堆排序是一种树形选择排序,是对直接选择排序的有效改进。html
具备n个元素的序列(h1,h2,...,hn),当且仅当知足(hi>=h2i,hi>=2i+1)或(hi<=h2i,hi<=2i+1) (i=1,2,...,n/2)时称之为堆。在这里只讨论知足前者条件的堆。由堆的定义能够看出,堆顶元素(即第一个元素)必为最大项(大顶堆)。彻底二 叉树能够很直观地表示堆的结构。堆顶为根,其它为左子树、右子树。java
初始时把要排序的数的序列看做是一棵顺序存储的二叉树,调整它们的存储序,使之成为一个堆,这时堆的根节点的数最大。而后将根节点与堆的最后一个节点交换。而后对前面(n-1)个数从新调整使之成为堆。依此类推,直到只有两个节点的堆,并对它们做交换,最后获得有n个节点的有序序列。从算法描述来看,堆排序须要两个过程,一是创建堆,二是堆顶与堆的最后一个元素交换位置。因此堆排序有两个函数组成。一是建堆的渗透函数,二是反复调用渗透函数实现排序的函数。算法
/** * 创建堆 * @param a 待建的数组 * @param lastIndex 须要创建的数组的最后一个元素(控制须要创建堆的长度) */ private void buildMaxHeap(int[] a, int lastIndex){ // 从lastIndex处节点(最后一个节点)的父节点开始 for(int i = (lastIndex-1)/2; i >=0 ; i--){ // k保存正在判断的节点 int k=i; // 若是当前的节点的子节点存在 while (k*2+1<=lastIndex){ // biggerIndex 为最大值的索引,先将其赋值为左子节点 int biggerIndex = k*2+1; // 若是存在右子节点,则须要比较其大小 if(biggerIndex < lastIndex){ // biggerIndex始终为最大的子节点。 if(a[biggerIndex + 1] > a[biggerIndex]){ biggerIndex++; } } // 若是k节点的值小于其较大的子节点的值,则须要交换他们 if(a[biggerIndex] > a[k]){ swap(a, biggerIndex, k); // 交换后的左子节点,、 // 有可能小于他本身的子节点, // 因此须要从新进行比较排序, // 保证最小值在下面的节点 k = biggerIndex; } else { break; } } } } /** * 交换 */ private void swap(int[] a, int i, int j){ int temp = a[i]; a[i] = a[j]; a[j] = temp; } public void heapSort(int[] a){ for(int i=0; i<a.length-1; i++){ buildMaxHeap(a, a.length-1-i); swap(a, 0, a.length-1-i); } } @Test public void heapTest(){ int[] a={7,5,3,2,9,10,8,4,6,1}; heapSort(a); System.out.println(Arrays.toString(a)); } 复制代码
归并排序(MERGE-SORT)是利用归并的思想实现的排序方法,该算法采用经典的分治(divide-and-conquer)策略(分治法将问题分(divide)成一些小的问题而后递归求解,而治(conquer)的阶段则将分的阶段获得的各答案"修补"在一块儿,即分而治之)。shell
/** * 归并排序 * 简介:将两个(或两个以上)有序表合并成一个新的有序表 即把待排序序列分为若干个子序列,每一个子序列是有序的。而后再把有序子序列合并为总体有序序列 * 时间复杂度为O(nlogn) * 稳定排序方式 * @param a 待排序数组 */ private void mergeSort(int[] a, int low, int high){ if(low < high){ int mid = (low + high) >>> 1; mergeSort(a, low, mid); mergeSort(a,mid+1, high); merge(a, low, mid, high); } } /** * 将数组中low到high位置的数进行排序 * @param a 待排序数组 * @param low 待排的开始位置 * @param mid 待排中间位置 * @param high 待排结束位置 */ private void merge(int[] a, int low, int mid, int high){ int[] temp = new int[high -low + 1]; int i = low; // 左指针 int j = mid + 1; // 右指针 int k = 0; // 将较小的数移到新数组中 while (i <= mid && j <= high){ if(a[i] < a[j]){ temp[k++] = a[i++]; } else { temp[k++] = a[j++]; } } // 把左边剩余的移到数组中 while (i <= mid){ temp[k++] = a[i++]; } // 把右边剩余的移到数组中 while (j <= high){ temp[k++] = a[j++]; } // 把新数组中的数覆盖原数组 for(int k2 = 0; k2 <temp.length; k2++){ a[k2+low] = temp[k2]; } } @Test public void mergeTest(){ int[] a={7,5,3,2,9,10,8,4,6,1}; mergeSort(a, 0, a.length-1); System.out.println(Arrays.toString(a)); } 复制代码
把记录按步长进行分组,对每组记录采用直接插入的方法进行排序。随着步长的缩小,所分红的组包含的记录就愈来愈多,当步长的值减少到1时,整个数据合成一组,构成一组有序的记录,则完成排序。数组
private void shellSort(int[] a){ for(int increment = a.length/2; increment > 0; increment /= 2){ // 分组 for(int i=increment; i < a.length; i++){ // 组内排序 for(int j=i; j >= increment; j--){ if(a[j] < a[j-increment]){ int temp = a[j]; a[j] = a[j-increment]; a[j-increment] = temp; } else { // 由于当一次循环以上时,由于前面已经排好序, // 因此直接与最近的一个increment增量比较便可。 // 符合继续比较,不符合直接跳过 break; } } } } } 复制代码
减小交换次数,提升效率bash
/** * 哨兵希尔排序,就是将位置为j的元素取出来放到一个变量, * 最后将这个值放到合适的位置 * @param a 待排序数组a */ private void shellSort1(int[] a){ int j=0; int temp = 0; for(int increment = a.length >>> 1; increment >0; increment = increment >>> 1){ for (int i=increment; i < a.length; i++){ temp = a[i]; for(j=i; j>=increment; j-=increment){ // temp 是a[j-increment] 交换后的值, // if里面只是将a[j]的值变换了, // 而a[j-increment]中没有变, // 所以来减小变换次数 if(temp < a[j-increment]){ a[j] = a[j-increment]; } else { // 由于当一次循环以上时,由于前面已经排好序, // 因此直接与最近的一个increment增量比较便可。 // 符合继续比较,不符合直接跳过 break; } } a[j] = temp; } } } 复制代码
参考:
必须知道的八大种排序算法【java实现】(二) 选择排序,插入排序,希尔算法【详解】
内部排序(二)希尔排序的两种实现
数据结构常见的八大排序算法(详细整理) 图解排序算法(四)之归并排序markdown
有些注解是本身的理解,注解及代码若有不恰之处,欢迎各位留言指正。数据结构