春蚕到死丝方尽,蜡炬成灰泪始干
——无题
这里介绍两个比较难的算法:
1、堆排序
2、分治并归排序
先说堆。
这里请大家先自行了解完全二叉树的数据结构。
堆是完全二叉树。大顶堆是在堆中,任意双亲值都大于(或等于)其孩子值,就称其为大顶堆。
堆排序的步骤:
1、把数组想象成一个堆。数组的index+1就是其对应在堆中的序号
2、调堆中各值的顺序,得到大顶堆
3、将堆首位值与堆末尾值交换,最大值排序完毕
4、将堆得大小减1,重复步骤2和步骤3,直到堆中只剩下一个元素。排序完毕
上代码:
1 public class HeapSort { 2 3 public static void heapSort(int[] arr){ 4 //建立完全二叉树,从最后一个双亲开始调整双亲值,直到根,调整完毕后大顶堆建立完成 5 for (int i = arr.length >> 1; i > 0; i --) { 6 heapAdjust(arr, i, arr.length);//调用堆要从1到length才符合堆的定义 7 } 8 //堆顶和堆低交换,获取最大值,然后调整大顶堆 9 for (int i = arr.length - 1; i > 0; i--) { 10 arr[i] = arr[i]^arr[0]; 11 arr[0] = arr[i]^arr[0]; 12 arr[i] = arr[i]^arr[0]; 13 heapAdjust(arr, 1, i);//因为堆计数要从1开始,所以size = endIndex + 1 14 } 15 } 16 //调整大顶堆.先找左子,然后和右子比,取值大的,在和双亲自己比,自己比儿子大,break,否则交换.注意:根要从1开始才能找到左子 17 public static void heapAdjust(int[] arr, int parents, int size){ 18 int j;//孩子们的标记是j,索引全部-1 19 int i = parents;//双亲是i,索引全部-1 20 while (i << 1 <= size) { 21 j = i << 1;//左子 22 if (j + 1 <= size) {//有右子 23 if (arr[j - 1] < arr[j + 1 -1]) 24 j ++; 25 } 26 if (arr[i - 1] > arr[j - 1]) 27 break; 28 arr[i-1] = arr[i-1]^arr[j-1]; 29 arr[j-1] = arr[i-1]^arr[j-1]; 30 arr[i-1] = arr[i-1]^arr[j-1]; 31 i = j;//儿子变为父亲,这里不知道是左子还是右子,所以不能直接通过for循环的迭代步骤<<i调整i值,如果是右子的话就错了(右子<<1+1) 32 } 33 } 34 }
再说分治并归排序
这里先要了解什么是递归
1 public class MergingTest { 2 3 public static void main(String[] args) { 4 mSort(0, 3); 5 } 6 private static void mSort(int left, int right) { 7 int m = (left + right)/2; 8 if (left == right) { 9 System.out.println(left); 10 return; 11 } 12 mSort(left, m); 13 mSort(m+1, right); 14 } 15 }
这几行代码是并归算法的核心。运行代码将输出0123456789,虽然看上去很简单,但是如果真能明白,说明你已经完全理解递归的思想了,写出并归算法也就不在话下了。
为什么会输出0123呢?
代码执行的走向:1→2→4→2→5→2→1→3→6→3→7→3→1→return
能领悟这个东西就好办了,上代码:
1 public class MergingSort { 2 3 public static void mergingSort(int[] arr) { 4 int[] temp = new int[arr.length];
//temp是相当于一张牌,通过left,m,right在逻辑上分成两个数组,进行分治排序,arr是原数组,对数组排序不传数组怎么行?! 5 mSort(arr, temp, 0, arr.length-1); 6 } 7 private static void mSort(int[] arr, int[] temp, int left, int right) {
//这里的分组逻辑没有使用到temp和arr,而是把它们作为参数传入merge方法 8 int m = (left+right)/ 2; 9 if (left == right) { 10 return; 11 } 12 mSort(arr, temp, left, m); 13 mSort(arr, temp, m+1, right); 14 merge(arr,temp,left,m,right); 15 } 16 private static void merge(int[]arr, int[] temp, int left, int m, int right) {
//分治排序极其简单,已知两个有序数组,要把他们合并成一个有序数组,用什么方法都不用说,大家一想就知道了。 17 for (int c = 0;c < arr.length;c++){ 18 temp[c] = arr[c]; 19 } 20 for (int p = 0; p < temp.length; p++) { 21 System.out.println("temp["+p+"] = " + temp[p]); 22 } 23 int i = left; 24 int j = m + 1; 25 int k = left; 26 while (i<=m && j<=right) {
//使用分支结构,把每摞牌最小的那个挑出来给arr 27 if (temp[i] < temp[j]) { 28 arr[k++] = temp[i++]; 29 } 30 if (temp[j] < temp[i]) { 31 arr[k++] = temp[j++]; 32 } 33 }
//使用分支结构,把剩下的那摞牌都塞给arr 34 if (i > m) { 35 while (j <= right) { 36 arr[k++] = temp[j++]; 37 } 38 } 39 if (j > right) { 40 while (i <= m) { 41 arr[k++] = temp[i++]; 42 } 43 } 44 } 45 46 }