前言:本文以升序排序为例讲解了几种排序算法的java实现,建议读者先了解各种排序算法的原理先,然后结合原理来读取本文的代码,效果更佳。
由于多次使用到交换数组中的两个元素的值,故先实现交换函数:
//交换数组中两个元素
private static void swap(int[] arr, int a, int b) {
int t = arr[a];
arr[a] = arr[b];
arr[b] = t;
}
一、冒泡排序
/**
* 冒泡排序
* @param arr 待排序数组
*/
private static void bubbleSort(int[] arr) {
int len = arr.length;
boolean flag = true;//用于剪枝:记录该轮是否有元素交换,若无:可直接结束
for (int i=1;flag && i<len;i++) {//冒泡排序趟数,len个数最多进行len-1趟
flag = false;//每轮开始设置标志位false
for (int j=0;j<len-i;j++) {
if(arr[j] > arr[j+1]) {//若当前元素比后一个元素大,则交换两元素,并设置标志位true
swap(arr, j, j+1);
flag = true;
}
}
}
}
测试方法:
public static void main(String[] args) {
int[] arr = {11,44,23,67,88,65,9,12,13,16,13,9,53,68};
System.out.println("原始数组:");
System.out.println(Arrays.toString(arr));
bubbleSort(arr);
System.out.println("冒泡排序后:");
System.out.println(Arrays.toString(arr));
}
输出结果:
二、快速排序
?
/**
* 快速排序
* @param arr 待排序数组
* @param low 数组中待排序最低位置
* @param high 数组中待排序最高位置
*/
private static void quickSort(int[] arr, int low, int high) {
if (low >= high) return;//递归终止条件
int l = low, r = high, temp = arr[low];//设置变量记录low、high和要确定位置的元素值
while (l < r) {
while (l<r && arr[r]>=temp) r--;//当前位置合理且值大于等于temp直接跳过
while (l<r && arr[l]<=temp) l++;//同上
if (l < r) {//若l和r未相遇,则交换对应位置的两个值
swap(arr, l, r);
}
}
//将本轮要确定的值放入最终位置
arr[low] = arr[l];
arr[l] = temp;
//继续递归
quickSort(arr, low, l-1);
quickSort(arr, l+1, high);
}
测试方法:
public static void main(String[] args) {
int[] arr = {11,44,23,67,88,65,9,12,13,16,13,9,53,68};
System.out.println("原始数组:");
System.out.println(Arrays.toString(arr));
// bubbleSort(arr);
// System.out.println("冒泡排序后:");
quickSort(arr,0,arr.length-1);
System.out.println("快速排序后:");
System.out.println(Arrays.toString(arr));
}
输出结果:
三、堆排序
堆排序中要经常调整堆,所以我们先来实现这个调整堆的方法,代码如下:
/**
* 用于调整堆的方法
* @param arr 待排序数组(待调整数组)
* @param cur 当前需要调整的数据的下标
* @param max 参与调节(比较)的最大范围+1
*/
private static void adjustHeap (int[] arr, int cur, int max) {
int temp = arr[cur];//先将要调整的数据存入临时变量
for (int i=2*cur+1;i<max;i=i*2+1) {//获取当前结点的左子结点
//如果当前结点存在右子结点且值大于左子结点,则将索引指向右子结点
if (i+1<max && arr[i+1]>arr[i]) i++;
if (arr[i] > temp) {//判断当前子结点值是否大于自己的值
arr[cur] = arr[i];//使当前值等于大于自己的子结点值
cur = i;//将当前值索引指向与自己交换的子结点的位置
} else break;//若不需要交换,提前结束
}
arr[cur] = temp;//最后将要调整的数据放入当前所指向的位置
}
?堆排序方法的代码如下:
/**
* 堆排序
* @param arr 待排序的数组
*/
private static void heapSort(int[] arr) {
int len = arr.length;
//从第一个非叶子结点自底向上依次进行调整,最后得到一个大顶堆
for (int i=len/2-1;i>=0;i--) {
adjustHeap(arr, i, len);
}
//len个元素进行len-1次交换和维护后得到一个升序的数组
for (int i=1;i<len;i++) {
swap(arr, 0, len-i);//交换对顶元素和最后一个元素
//调整堆顶元素,使其依然是大顶堆(最后i个元素不参与调整)
adjustHeap(arr, 0, len-i);
}
}
测试方法:
public static void main(String[] args) {
int[] arr = {11,44,23,67,88,65,9,12,13,16,13,9,53,68};
System.out.println("原始数组:");
System.out.println(Arrays.toString(arr));
// bubbleSort(arr);
// System.out.println("冒泡排序后:");
// quickSort(arr,0,arr.length-1);
// System.out.println("快速排序后:");
heapSort(arr);
System.out.println("堆排序后:");
System.out.println(Arrays.toString(arr));
}
输出结果:
?
四、归并排序
归并排序需要用到一个合并两个有序子序列到一个有序子序列中的方法,代码如下:
/**
* 合并两个有序子序列到一个有序子序列(临时数组)中
* @param arr 待排序数组
* @param low 将要排序的最低下标
* @param mid 两个子子序列的分界值
* @param high 将要排序的最高下标
*/
private static void merger(int[] arr, int low, int mid, int high){
//创建一个临时数组,用于存放两个子序列排序后的结果
int[] temp = new int[high-low+1];
//分别设置索引指向两个已经排好序的子序列的起始位置,并设置tI指向temp中的位置
int lowI = low, highI = mid+1, tI = 0;
//若两个索引都未达到上边界值,则循环比较
while (lowI<=mid && highI<=high) {
//将两个子序列中较小的值放入临时数组,并将对应索引加一
if (arr[lowI] < arr[highI]) {
temp[tI++] = arr[lowI++];
} else {
temp[tI++] = arr[highI++];
}
}
//判断哪个子序列还没有遍历完,然后遍历该子序列
if (lowI <= mid) {
while (lowI <= mid) {
temp[tI++] = arr[lowI++];
}
} else {
while (highI <= high) {
temp[tI++] = arr[highI++];
}
}
//将临时数组中有序的元素更新到arr的相应位置中
for (int i=0;i<tI;i++) {
arr[low+i] = temp[i];
}
}
归并排序的总体方法代码:
/**
* 归并排序
* @param arr 待排序数组
* @param low 待排序序列的最小下标
* @param high 待排序序列的最大下标
*/
private static void mergeSort(int[] arr, int low, int high) {
if (low < high) {//判断是否满足条件
int mid = low + (high-low)/2;//计算中间值
mergeSort(arr, low, mid);//递归对左边序列进行归并排序
mergeSort(arr, mid+1, high);//递归对右边序列进行归并排序
merger(arr, low, mid, high);//合并两个子序列
}
}
测试方法:
public static void main(String[] args) {
int[] arr = {11,44,23,67,88,65,9,12,13,16,13,9,53,68};
System.out.println("原始数组:");
System.out.println(Arrays.toString(arr));
// bubbleSort(arr);
// System.out.println("冒泡排序后:");
// quickSort(arr,0,arr.length-1);
// System.out.println("快速排序后:");
// heapSort(arr);
// System.out.println("堆排序后:");
mergeSort(arr, 0, arr.length-1);
System.out.println("归并排序后:");
System.out.println(Arrays.toString(arr));
}
输出结果:
?五、全部代码
public class TestClass {
public static void main(String[] args) {
int[] arr = {11,44,23,67,88,65,9,12,13,16,13,9,53,68};
System.out.println("原始数组:");
System.out.println(Arrays.toString(arr));
// bubbleSort(arr);
// System.out.println("冒泡排序后:");
// quickSort(arr,0,arr.length-1);
// System.out.println("快速排序后:");
// heapSort(arr);
// System.out.println("堆排序后:");
mergeSort(arr, 0, arr.length-1);
System.out.println("归并排序后:");
System.out.println(Arrays.toString(arr));
}
/**
* 归并排序
* @param arr 待排序数组
* @param low 待排序序列的最小下标
* @param high 待排序序列的最大下标
*/
private static void mergeSort(int[] arr, int low, int high) {
if (low < high) {//判断是否满足条件
int mid = low + (high-low)/2;//计算中间值
mergeSort(arr, low, mid);//递归对左边序列进行归并排序
mergeSort(arr, mid+1, high);//递归对右边序列进行归并排序
merger(arr, low, mid, high);//合并两个子序列
}
}
/**
* 合并两个有序子序列到一个有序子序列(临时数组)中
* @param arr 待排序数组
* @param low 将要排序的最低下标
* @param mid 两个子子序列的分界值
* @param high 将要排序的最高下标
*/
private static void merger(int[] arr, int low, int mid, int high){
//创建一个临时数组,用于存放两个子序列排序后的结果
int[] temp = new int[high-low+1];
//分别设置索引指向两个已经排好序的子序列的起始位置,并设置tI指向temp中的位置
int lowI = low, highI = mid+1, tI = 0;
//若两个索引都未达到上边界值,则循环比较
while (lowI<=mid && highI<=high) {
//将两个子序列中较小的值放入临时数组,并将对应索引加一
if (arr[lowI] < arr[highI]) {
temp[tI++] = arr[lowI++];
} else {
temp[tI++] = arr[highI++];
}
}
//判断哪个子序列还没有遍历完,然后遍历该子序列
if (lowI <= mid) {
while (lowI <= mid) {
temp[tI++] = arr[lowI++];
}
} else {
while (highI <= high) {
temp[tI++] = arr[highI++];
}
}
//将临时数组中有序的元素更新到arr的相应位置中
for (int i=0;i<tI;i++) {
arr[low+i] = temp[i];
}
}
/**
* 堆排序
* @param arr 待排序的数组
*/
private static void heapSort(int[] arr) {
int len = arr.length;
//从第一个非叶子结点自底向上依次进行调整,最后得到一个大顶堆
for (int i=len/2-1;i>=0;i--) {
adjustHeap(arr, i, len);
}
//len个元素进行len-1次交换和维护后得到一个升序的数组
for (int i=1;i<len;i++) {
swap(arr, 0, len-i);//交换对顶元素和最后一个元素
//调整堆顶元素,使其依然是大顶堆(最后i个元素不参与调整)
adjustHeap(arr, 0, len-i);
}
}
/**
* 用于调整堆的方法
* @param arr 待排序数组(待调整数组)
* @param cur 当前需要调整的数据的下标
* @param max 参与调节(比较)的最大范围+1
*/
private static void adjustHeap (int[] arr, int cur, int max) {
int temp = arr[cur];//先将要调整的数据存入临时变量
for (int i=2*cur+1;i<max;i=i*2+1) {//获取当前结点的左子结点
//如果当前结点存在右子结点且值大于左子结点,则将索引指向右子结点
if (i+1<max && arr[i+1]>arr[i]) i++;
if (arr[i] > temp) {//判断当前子结点值是否大于自己的值
arr[cur] = arr[i];//使当前值等于大于自己的子结点值
cur = i;//将当前值索引指向与自己交换的子结点的位置
} else break;//若不需要交换,提前结束
}
arr[cur] = temp;//最后将要调整的数据放入当前所指向的位置
}
/**
* 快速排序
* @param arr 待排序数组
* @param low 数组中待排序最低位置
* @param high 数组中待排序最高位置
*/
private static void quickSort(int[] arr, int low, int high) {
if (low >= high) return;//递归终止条件
int l = low, r = high, temp = arr[low];//设置变量记录low、high和要确定位置的元素值
while (l < r) {
while (l<r && arr[r]>=temp) r--;//当前位置合理且值大于等于temp直接跳过
while (l<r && arr[l]<=temp) l++;//同上
if (l < r) {//若l和r未相遇,则交换对应位置的两个值
swap(arr, l, r);
}
}
//将本轮要确定的值放入最终位置
arr[low] = arr[l];
arr[l] = temp;
//继续递归
quickSort(arr, low, l-1);
quickSort(arr, l+1, high);
}
/**
* 冒泡排序
* @param arr 待排序数组
*/
private static void bubbleSort(int[] arr) {
int len = arr.length;
boolean flag = true;//用于剪枝:记录该轮是否有元素交换,若无:可直接结束
for (int i=1;flag && i<len;i++) {//冒泡排序趟数,len个数最多进行len-1趟
flag = false;//每轮开始设置标志位false
for (int j=0;j<len-i;j++) {
if(arr[j] > arr[j+1]) {//若当前元素比后一个元素大,则交换两元素,并设置标志位true
swap(arr, j, j+1);
flag = true;
}
}
}
System.out.println("冒泡排序后:");
}
//交换数组中两个元素
private static void swap(int[] arr, int a, int b) {
int t = arr[a];
arr[a] = arr[b];
arr[b] = t;
}
}
六、总结
学习一个算法应该先理解并掌握其原理,然后动手写代码去实现该算法。由于最近比较忙,所以只编写了这几种排序算法的java代码,并没有详细的对原理进行解释。鄙人争取空闲下来后能补上这些排序算法的原理解读。最后,由于能力有限,本文若有不当之处,欢迎大家批评并指出。
|