程序员那些必须掌握的排序算法(下)

快速排序(Quicksort)是对冒泡排序的一种改进。
快速排序由C. A. R. Hoare在1960年提出。它的基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
演示:

在这里插入图片描述


代码如下:

public static void quickSort(int[] arr, int left, int right) { int l = left;// 左下标 int r = right;// 右下标 int pivot = arr[(left + right) / 2];// 找到中间的值 // 将比pivot小的值放在其左边,比pivot大的值放在其右边 while (l < r) { // 在pivot左边寻找,直至找到大于等于pivot的值才退出 while (arr[l] < pivot) { l += 1;// 将l右移一位 } // 在pivot右边寻找,直至找到小于等于pivot的值才退出 while (arr[r] > pivot) { r -= 1;// 将r左移一位 } if (l >= r) { // 左右下标重合,寻找完毕,退出循环 break; } // 交换元素 int temp = arr[l]; arr[l] = arr[r]; arr[r] = temp; //倘若发现值相等的情况,则没有比较的必要,直接移动下标即可 // 如果交换完后,发现arr[l]==pivot,此时应将r左移一位 if (arr[l] == pivot) { r -= 1; } // 如果交换完后,发现arr[r]==pivot,此时应将l右移一位 if (arr[r] == pivot) { l += 1; } } // 如果l==r,要把这两个下标错开,否则会出现无限递归,导致栈溢出的情况 if (l == r) { l += 1; r -= 1; } // 向左递归 if (left < r) { quickSort(arr, left, r); } // 向右递归 if (right > l) { quickSort(arr, l, right); } }

测试代码:

public static void main(String[] args) { int[] arr = { 3, 44, 38, 5, 47, 15, 36, 26, 27, 2, 46, 4, 19, 50, 48 }; quickSort(arr, 0, arr.length - 1); System.out.println(Arrays.toString(arr)); }

运行结果:

[2, 3, 4, 5, 15, 19, 26, 27, 36, 44, 38, 46, 47, 48, 50]

快速排序的实现原理很简单,就是将原数组分成两部分,然后以中间值为标准,比它小的就放其左边,比它大的就放其右边,然后在左右两边又以相同的方式继续排序。
所以在代码实现过程中,首先要创建两个移动的变量,一个从最左边开始往右移动,一个从最右边开始往左移动,通过这两个变量来遍历左右两部分的元素。当发现左边有大于中间数的元素,右边有小于中间数的元素,此时就进行交换。当两个变量重合也就是相等的时候遍历结束,然后左右两部分作递归处理。

2.归并排序

归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。
演示:

在这里插入图片描述


归并排序使用了一种分治思想,分治思想的意思就是'分而治之",就是把一个复杂的问题分成两个或更多的相同或相似的子问题,再把子问题分成更小的子问题……直到最后子问题可以简单地直接求解。
通过这个动图来看的话,相信很多人都一脸懵,没关系,我们通过静态图来分析一下:

在这里插入图片描述


假设现在有一个待排序的序列,[5,2,4,7,1,3,2,2],那么我们就需要将该序列进行分治,先将其分成两份:[5,2,4,7]和[1,3,2,2],再将这两份分别分成两份:[5,2]和[4,7];[1,3]和[2,2],最后将这四部分再次分别分为两份,最后就将整个序列分为了八份。需要注意的是,在分的过程中,不需要遵循任何规则,关键在于归并,归并的过程中便实现了元素的排序。
代码如下:

public static void mergeSort(int[] arr, int left, int right, int[] temp) { // 分解 if (left < right) { int mid = (left + right) / 2;// 中间索引 // 向左递归进行分解 mergeSort(arr, left, mid, temp); // 向右递归进行分解 mergeSort(arr, mid + 1, right, temp);// mid + 1,中间位置的后一个位置才是右边序列的开始位置 // 每分解一轮便合并一轮 merge(arr, left, right, mid, temp); } } /** * 合并的方法 * * @param arr 待排序的数组 * @param left 左边有序序列的初始索引 * @param right 中间索引 * @param mid 右边有序序列的初始索引 * @param temp 做中转的数组 */ public static void merge(int[] arr, int left, int right, int mid, int[] temp) { int i = left; // 初始化i,左边有序序列的初始索引 int j = mid + 1;// 初始化j,右边有序序列的初始索引(右边有序序列的初始位置即为中间位置的后一个位置) int t = 0;// 指向temp数组的当前索引,初始为0 // 先把左右两边的数据(已经有序)按规则填充到temp数组 // 直到左右两边的有序序列,有一边处理完成为止 while (i <= mid && j <= right) { // 如果左边有序序列的当前元素小于或等于右边有序序列的当前元素,就将左边的元素填充到temp数组中 if (arr[i] <= arr[j]) { temp[t] = arr[i]; t++;// 索引后移 i++;// i后移 } else { // 反之,将右边有序序列的当前元素填充到temp数组中 temp[t] = arr[j]; t++;// 索引后移 j++;// j后移 } } // 把有剩余数据的一边的元素填充到temp中 while (i <= mid) { // 此时说明左边序列还有剩余元素 // 全部填充到temp数组 temp[t] = arr[i]; t++; i++; } while (j <= right) { // 此时说明左边序列还有剩余元素 // 全部填充到temp数组 temp[t] = arr[j]; t++; j++; } // 将temp数组的元素复制到原数组 t = 0; int tempLeft = left; while (tempLeft <= right) { arr[tempLeft] = temp[t]; t++; tempLeft++; } }

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zzdfgf.html