一、排序 基于比较的排序时间复杂度目前做不到nlogn;时间为nlogn,空间为n以下的排序,做不到稳定性。
一般来说,都是用快排。算法的每一步实际上都需要一个固定时间量,被称为常量。我们平时考虑时间复杂度的时候并不考虑常量的影响,但有时候常量的影响不可忽略,比如在这个问题上。但是大多数时候考虑复杂度的时候,可能还是不需要考虑常量的影响的,嗯,我觉得是……由于算法的每一步都有一个常量,而快排的常量比归排小 ,因此虽然两者的复杂度相同,但是快排更快一些。那第二个问题就来了,归排的复杂度一直都是O(nlog n),而快排的平均复杂度才是O(n log n),最坏情况下快排的复杂度可以达到O(n^2),为什不怕快排的时候是最坏的情况呢?主要是因为绝大多数情况下,快排遇到的都是平均情况,也就是最佳情况,只有极个别的时候会是最坏情况,因此往往不考虑这种糟糕的情况。
归并排序可以做到空间复杂度为O(1)吗,可以!但是就不再稳定了。而且实现非常难,不如用堆排序。
“原地归并排序”方法会让时间复杂度变成O(n^2)也不行,不如插入排序。
“01 stable sort”论文的方法可以实现快排的稳定性,但是会让空间复杂度变成O(n)水平,不如直接用归并。
所有的改进都不重要,因为目前没有找到时间复杂度为O(N*logN),额外空间复杂度O(1),又稳定的排序。
有一道题目,是奇数放在数组左边,偶数放在数组右边,还要求原始的相对次序不变,碰到这个问题,可以怼面试官。 按照经典快排,这个问题跟左边小于等于一个数和右边大于一个数是等效的,但是无法做到稳定性,你可以直接让面试官教下你(左神都说超级难实现)。
工程上充分利用O(N*logN)和O(N^2)的优势,如快排递归到样本量小于六十的时候,用插入排序更快。即进行综合排序。
工程上利用稳定性优势,如果是基础类型数据的排序,如 年龄,分数,用快排 。如果是用户自己定义的类型,职工(年龄、工时等属性)用归并 。
而C++和java等语言的排序,底层实现都非常多代码,用到了很多排序,充分利用各种排序的优势。
1、选择排序
选择排序: 选择排序( Selection sort)是一种简单直观的排序算法。它的工作原理是每一趟从待排序的数据元素中选出最小(或最大)的一个元素,顺序放在已排好序的数列的最后,直到全部待排序的数据元素排完。
选择排序算法通过选择和交换来实现排序,其排序流程如下:
(1)首先从原始数组中选择最小的1个数据,将其和位于第1个位置的数据交换。
(2)接着从剩下的n-1个数据中选择次小的1个元素,将其和第2个位置的数据交换
(3)然后,这样不断重复,直到最后两个数据完成交换。最后,便完成了对原始数组的从小到大的排序。
假如给定初始数据:(118,101,105,127,112)
一次排序:101,118,105,127,112
二次排序:101,105,118,127,112
三次排序:101,105,112,127,118
四次排序:101,105,112,118,127
(绿色为交换的数据)
每一趟在n-i+1(i=1,2,…,n-1)个记录中选取关键字最小的记录作为有序序列中第i个记录。具体来说,假设长度为n的数组arr,要按照从小到大排序,那么先从n个数字中找到最小值min1,如果最小值min1的位置不在数组的最左端(也就是min1不等于arr[0]),则将最小值min1和arr[0]交换,接着在剩下的n-1个数字中找到最小值min2,如果最小值min2不等于arr[1],则交换这两个数字,依次类推,直到数组arr有序排列。算法的时间复杂度为O(n^2)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 package class1;public class Program01SelectionSort { public static void SelectionSort (int [] arr) { if (arr == null || arr.length < 2 ) return ; for (int i = 0 ; i < arr.length - 1 ; i++) { int minIndex = i; for (int j = i + 1 ; j < arr.length; j++) { minIndex = arr[j] < arr[minIndex] ? j : minIndex; } if (minIndex != i) { swap(arr, i, minIndex); } for (int k : arr) { System.out.print(k + " " ); } System.out.println(); } } private static void swap (int [] arr, int i, int j) { int temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } public static void main (String[] args) { int [] arr = {1 , 4 , 6 , 3 , 0 , 2 , 5 , 9 , 8 , 7 }; SelectionSort(arr); } }
2、冒泡排序
1、冒泡排序基本思想: 通过对待排序序列从前向后(从下标较小的元素开始),依次对相邻两个元素的值进行两两比较,若发现逆序则交换,使值较大的元素逐渐从前移向后部,就如果水底下的气泡一样逐渐向上冒。
小结: 设总的元素个数为n,那么由上边的排序过程可以看出:
(1)总计需要进行(n-1)轮排序,也就是(n-1)次大循环
(2)每轮排序比较的次数逐轮减少
(3)如果发现在某趟排序中,没有发生一次交换, 可以提前结束冒泡排序。(详见下边优化部分)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 package class1;public class Program02BubbleSort { public static void BubbleSort (int [] arr) { if (arr == null || arr.length < 2 ) return ; for (int i = 0 ; i < arr.length - 1 ; i++) { for (int j = 0 ; j < arr.length - 1 ; j++) { if (arr[j] > arr[j + 1 ]) { swap(arr, j, j + 1 ); } } for (int k : arr) { System.out.print(k + " " ); } System.out.println(); } } private static void swap (int [] arr, int i, int j) { arr[i] = arr[i] ^ arr[j]; arr[j] = arr[i] ^ arr[j]; arr[i] = arr[i] ^ arr[j]; } public static void main (String[] args) { int [] arr = {1 , 4 , 6 , 3 , 0 , 2 , 5 , 9 , 8 , 7 }; BubbleSort(arr); } }
“异或” ^(异或)的使用: 在以后用的不多,了解一下即可。
计算规则:如果两边相同,结果为false,如果两边不同,结果为true
可以理解为无进位相加 。
性质:
0^N=N N^N=0
B^A = A^B (B^A) ^C = B^ (A^C)
同一批数运算的结果,与顺序无关,结果固定。 下列代码实现顺序交换:(arr[i]=甲,arr[j]=乙) arr[i] = arr[i] ^ arr[j]; arr[j] = arr[i] ^ arr[j]; arr[i] = arr[i] ^ arr[j]; 原理(关键arr[i]和arr[j]所指向的内存中的内容不能是同一个,即地址不能相同,但值可以) arr[i]=甲^乙,arr[j]=乙;即arr[i]=甲 ^乙,arr[j]=乙 arr[i]=甲^乙, arr[j]=乙^ (甲^乙)= (乙^ 乙)^甲=甲; 即arr[i]=甲^乙, arr[j]=甲 arr[i]=(甲^乙 )^甲= (甲^ 甲)^乙=乙, arr[j]=甲;即arr[i]==乙, arr[j]=甲
代码示例: 1 2 3 4 5 System.out.println(true ^ true ); System.out.println(false ^ false ); System.out.println(true ^ false ); System.out.println(false ^ true );
题目: 1、一个数组内,有一种数出现了奇数次,其余数出现了偶数次,求该数。
2、一个数组内,有两种数出现了奇数次,其余数出现了偶数次,求该两种数。
解: 1、对于只有一个数出现奇数次的数组,我们可以利用异或的性质(同为0异为1),将所有的数相异或,最后就只剩下出现奇数次的数。
1 2 3 4 5 6 7 public static void printOddTimesNum1 (int [] arr) { int eor = 0 ; for (int cur : arr){ eor ^= cur; } System.out.println(eor); }
2、但是现在有两个出现了奇数次,要是还按上面的方法,最后的结果将是这两个数的异或结果,我们又分不开。怎么办呢? 我们已经知道了只有一个数出现奇数次的解决方法。能不能考虑一下分治的思想呢,就是将这个数组分成两个子数组,然后保持每一个子数组只有一个奇数次数。可是应该怎么分这两个数组呢,以什么依据来分呢? 假设这两个出现奇数次的数分别为a,b。那么它俩肯定至少有一个bit位是不同的吧,如果全都相同那这俩数就完全相等了。我们如果能找到一个这样的bit位,然后按照这一位是0还是1将这个数组分开,那就得到了上面所说的子数组,每个子数组里仅有一个数出现了奇数次。 详细解释一下:假如a和b的第9位是最低的不相同的位,首先我们要找到这个第九位,令x = a^b,则x的第9位肯定是1,我们用 x & -x得到x的最低位的1,并将其余位都变为0。然后对数组中的元素挨个与这个数(x & -x)相“与”,如果结果为0,说明该数该位上为0,否则说明该数该位上为1,根据结果就可以将其分成两个子数组。首先a和b因为该位上的数字不同一定不在一个组里,其次对于其他的数都出现了偶数次,那么要么全被分到这个子数组要么全被分到那个子数组里,因此每个子数组中其他的数仍然是偶数次。这样问题就完美解决了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public static void printOddTimesNum2 (int [] arr) { int eor = 0 ,onlyOne = 0 ; for (int cur : arr){ eor ^= cur; } int rightOne = eor & (~eor + 1 ); for (int cur : arr){ if ((cur & rightOne) == 0 ){ onlyOne ^= cur; } } System.out.println(onlyOne + " " + (eor ^ onlyOne)); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 package class1;public class Program03XOR { public static void printOddTimesNum1 (int [] arr) { int eor = 0 ; for (int cur : arr) { eor ^= cur; } System.out.println(eor); } public static void printOddTimesNum2 (int [] arr) { int eor = 0 , onlyOne = 0 ; for (int cur : arr) { eor ^= cur; } int rightOne = eor & (~eor + 1 ); for (int cur : arr) { if ((cur & rightOne) == 0 ) { onlyOne ^= cur; } } System.out.println(onlyOne + " " + (eor ^ onlyOne)); } public static void main (String[] args) { printOddTimesNum1(new int []{1 , 1 , 1 , 1 , 5 }); printOddTimesNum2(new int []{1 , 2 , 1 , 4 , 1 , 1 }); } }
int rightOne = eor & (~eor + 1);的含义如下图:
3、插入排序
1.左边第一个元素可作为有序子数组; 2.从第二个元素开始,依次向前比较,大于该元素的则向右移一位,直到比该元素小的元素,插入其后; 3.依次向后推进,直至整个数组成为有序数组
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 package class1;import utils.PrintIntArr;public class Program04InsertionSort { public static void InsertionSort (int [] arr) { if (arr == null || arr.length < 2 ) return ; for (int i = 1 ; i < arr.length; i++) { for (int j = i; j > 0 ; j--) { if (arr[j] < arr[j - 1 ]) { swap(arr, j, j - 1 ); } } PrintIntArr.PrintIntArrays(arr); } } private static void swap (int [] arr, int i, int j) { arr[i] = arr[i] ^ arr[j]; arr[j] = arr[i] ^ arr[j]; arr[i] = arr[i] ^ arr[j]; } public static void main (String[] args) { int [] arr = {1 , 4 , 6 , 3 , 0 , 2 , 5 , 9 , 8 , 7 }; InsertionSort(arr); } }
二分查找
二分查找的前提条件是有序数列,普通查找则不需要。 查找到返回该元素的下标,否则返回-1。 普通查找的时间复杂度为O(N), 二分查找的时间复杂度为O(logN)。 N/2/2···/2=1,2^m=N(m为折半查找的次数),那么m=log(N),二分查找的时间复杂度就为O(logN)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 package class1;public class Program05BinarySearch { public static int binarySearch (int [] arr, int key) { int head = 0 , tail = arr.length - 1 ; while (head <= tail) { int mid = (head + tail) / 2 ; if (arr[mid] == key) { return mid; } else if (arr[mid] < key) { head = mid + 1 ; } else { tail = mid - 1 ; } } return -1 ; } public static int findLeft (int [] arr, int key) { int head = 0 , tail = arr.length - 1 ; while (head < tail) { int mid = (head + tail) / 2 ; if (arr[mid] == key) { tail = mid; } else if (arr[mid] < key) { head = mid + 1 ; } else { tail = mid; } } return tail; } public static int findRight (int [] arr, int key) { int head = 0 , tail = arr.length - 1 ; while (head < tail) { int mid = (head + tail) / 2 + 1 ; if (arr[mid] == key) { head = mid; } else if (arr[mid] < key) { head = mid; } else { tail = mid - 1 ; } } return head; } public static int findLocalMinimum (int [] arr) { if (arr[0 ] < arr[1 ]) { return 0 ; } if (arr[arr.length - 1 ] < arr[arr.length - 2 ]) { return arr.length - 1 ; } int left = 1 , right = arr.length - 2 ; while (left < right) { int mid = (left + right) / 2 ; if (arr[mid] > arr[mid + 1 ]) { left = mid + 1 ; } else if (arr[mid] > arr[mid - 1 ]) { right = mid - 1 ; } else { return mid; } } return -1 ; } public static void main (String[] args) { int [] arr = {4 , 3 , 2 , 4 , 0 , 5 , 6 , 7 , 8 , 9 }; int binarySearch = binarySearch(arr, 9 ); System.out.println(binarySearch == -1 ? "not found" : "found" + " " + binarySearch); int [] arrNew = {1 , 1 , 2 , 2 , 2 , 2 , 3 , 3 , 4 , 5 }; int left = findLeft(arrNew, 2 ); System.out.println(left == -1 ? "not found" : "found" + " " + left); int right = findRight(arrNew, 2 ); System.out.println(right == -1 ? "not found" : "found" + " " + right); int localMinimum = findLocalMinimum(arr); System.out.println(localMinimum == -1 ? "not found" : "found" + " " + localMinimum); } }
4、归并排序 算法简介 分: 1.一直分两组,分别对两个数组进行排序(根据上层对下层在一组的数据通过临时数组排序,再将有序数组挪回上层数组中)。 2.循环第一步,直到划分出来的“小组”只包含一个元素,只有一个元素的数组默认为已经排好序。 合:(合并时,站在上层合并下层(使组内有序)) 1.将两个有序的数组合并到一个大的数组中。 2.从最小的只包含一个元素的数组开始两两合并。此时,合并好的数组也是有序的。最后把小组合成一个组。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 package class2;import utils.PrintIntArr;public class Program06MergeSort { public static void mergeSort (int [] arr) { if (arr == null || arr.length < 2 ) return ; sort(arr, 0 , arr.length - 1 ); } private static void sort (int [] arr, int left, int right) { if (left == right) return ; int mid = left + ((right - left) >> 1 ); sort(arr, left, mid); sort(arr, mid + 1 , right); merge(arr, left, mid, right); } private static void merge (int [] arr, int left, int mid, int right) { int [] processArr = new int [right - left + 1 ]; int p = left, q = mid + 1 , i = 0 ; while (p <= mid && q <= right) { if (arr[p] < arr[q]) { processArr[i++] = arr[p++]; } else { processArr[i++] = arr[q++]; } } while (p <= mid) { processArr[i++] = arr[p++]; } while (q <= right) { processArr[i++] = arr[q++]; } for (i = 0 ; i < processArr.length; i++) { arr[left + i] = processArr[i]; } } public static void main (String[] args) { int [] arr = {4 , 3 , 2 , 1 , 0 , 5 , 6 , 7 , 8 , 9 }; mergeSort(arr); PrintIntArr.PrintIntArrays(arr); } }
最小和 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 package class2;import utils.PrintIntArr;public class Program07SmallSum { public static int process (int [] data, int L, int R) { if (L == R) { return 0 ; } int mid = L + ((R - L) >> 1 ); int left = process(data, L, mid); int right = process(data, mid + 1 , R); int mergeVal = merge(data, L, mid, R); return left + right + mergeVal; } private static int merge (int [] data, int L, int M, int R) { int [] help = new int [R - L + 1 ]; int i = 0 ; int p1 = L; int p2 = M + 1 ; int result = 0 ; while (p1 <= M && p2 <= R) { if (data[p1] < data[p2]) { help[i++] = data[p1]; result += (R - p2 + 1 ) * data[p1]; p1++; } else { help[i++] = data[p2++]; } } while (p1 <= M) { help[i++] = data[p1++]; } while (p2 <= R) { help[i++] = data[p2++]; } for (int j = 0 ; j < help.length; j++) { data[L + j] = help[j]; } return result; } public static void mergeSort2 (int [] data) { if (data == null || data.length < 2 ) { return ; } int N = data.length; int mergeSize = 1 ; while (mergeSize < N) { int L = 0 ; while (L < N) { int M = L + mergeSize - 1 ; if (M >= N) { break ; } int R = Math.min(N - 1 , M + mergeSize); merge(data, L, M, R); L = R + 1 ; } if (mergeSize > N / 2 ) { break ; } mergeSize <<= 1 ; } } public static void main (String[] args) { int [] data = {1 , 2 , 4 , 3 , 5 }; int result = process(data, 0 , data.length - 1 ); System.out.println(result); System.out.println("----------------------------------" ); PrintIntArr.PrintIntArrays(data); } }
5、快速排序 算法简介 1.选择一个元素赋值给中间元素temp,默认选择左边第一个元素,这样左边第一个元素的位置就空出来了;
先从最右边的元素开始依次跟temp比较大小,大于等于temp元素的原地不动,遇到小于temp元素的则终止循环,把该元素赋值到左侧空出来的位置,同时左侧索引值自增,这是该元素原来的位置就空出;
然后左侧元素开始依次跟temp比较大小,小于等于temp元素的原地不动,遇到大雨temp元素的则终止循环,把该元素赋值到右侧空出来的位置,同时右侧索引值自减;
依次循环2,3步,直至左侧索引等于右侧索引,则完成一轮循环,把哨兵赋值到该索引的位置。
在分别递归地对temp左右两侧的子数组进行1234步,直至递归子数组只有一个元素则排序完成
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 package class2;import utils.PrintIntArr;public class Program08QuickSort { public static void quickSort (int [] arr) { if (arr == null || arr.length < 2 ) return ; quickSort(arr, 0 , arr.length - 1 ); } public static void quickSort (int [] arr, int left, int right) { if (left < right) { swap(arr, left + (int ) (Math.random() * (right - left + 1 )), right); int [] p = partition(arr, left, right); quickSort(arr, left, p[0 ] - 1 ); quickSort(arr, p[1 ] + 1 , right); } } public static int [] partition(int [] arr, int left, int right) { int less = left - 1 ; int more = right; while (left < more) { if (arr[left] < arr[right]) { swap(arr, ++less, left++); } else if (arr[left] > arr[right]) { swap(arr, --more, left); } else { left++; } } swap(arr, more, right); return new int []{less + 1 , more}; } private static void swap (int [] arr, int i, int j) { int temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } public static void main (String[] args) { int [] arr = {4 , 3 , 2 , 1 , 0 , 5 , 6 , 7 , 8 , 9 }; quickSort(arr); PrintIntArr.PrintIntArrays(arr); } }
6、堆排序
堆排序(Heapsort)是指利用堆这种数据结构所设计的一种排序算法。堆积是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。堆排序可以说是一种利用堆的概念来排序的选择排序。分为两种方法: 大顶堆:每个节点的值都大于或等于其子节点的值,在堆排序算法中用于升序排列; 小顶堆:每个节点的值都小于或等于其子节点的值,在堆排序算法中用于降序排列;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 package class3;import utils.PrintIntArr;public class Program09HeapSort { public static void HeapSort (int [] arr) { if (arr == null || arr.length < 2 ) return ; for (int i = arr.length - 1 ; i >= 0 ; i--) { heapify(arr, i, arr.length); } int heapSize = arr.length; PrintIntArr.swap(arr, 0 , --heapSize); while (heapSize > 0 ) { heapify(arr, 0 , heapSize); PrintIntArr.swap(arr, 0 , --heapSize); } } private static void heapInsert (int [] arr, int index) { while (arr[index] > arr[(index - 1 ) / 2 ]) { PrintIntArr.swap(arr, index, (index - 1 ) / 2 ); index = (index - 1 ) / 2 ; } } private static void heapify (int [] arr, int index, int heapSize) { int leftChild = 2 * index + 1 ; while (leftChild < heapSize) { int largerChild = leftChild + 1 < heapSize && arr[leftChild + 1 ] > arr[leftChild] ? leftChild + 1 : leftChild; largerChild = arr[largerChild] > arr[index] ? largerChild : index; if (largerChild == index) break ; PrintIntArr.swap(arr, largerChild, index); index = largerChild; leftChild = 2 * index + 1 ; } } public static void main (String[] args) { int [] arr = {4 , 3 , 2 , 1 , 0 , 5 , 6 , 7 , 8 , 9 }; HeapSort(arr); PrintIntArr.PrintIntArrays(arr); } }
应用 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 package class3;import utils.PrintIntArr;import java.util.PriorityQueue;public class Program10SortArrayDistanceLessK { public static void sortArrayDistanceLessK (int [] arr, int k) { PriorityQueue<Integer> heap = new PriorityQueue <Integer>(); int index = 0 ; for (; index <= Math.min(arr.length - 1 , k - 1 ); index++) { heap.add(arr[index]); } int i = 0 ; for (; index < arr.length; i++, index++) { heap.add(arr[index]); arr[i] = heap.poll(); } while (!heap.isEmpty()) { arr[i++] = heap.poll(); } } public static void main (String[] args) { int [] arr = {1 , 4 , 6 , 3 , 0 , 2 , 5 , 9 , 8 , 7 }; sortArrayDistanceLessK(arr, 4 ); PrintIntArr.PrintIntArrays(arr); } }
7、基数排序
相比其它排序,主要是利用比较和交换,而基数排序则是利用分配和收集两种基本操作。基数 排序是一种按记录关键字的各位值逐步进行排序的方法。此种排序一般适用于记录的关键字为整数类型的情况。所有对于字符串和文字排序不适合。
实现:将所有待比较数值(自然数)统一为同样的数位长度,数位较短的数前面补零。然后,从最低位开始,依次进行一次排序。这样从最低位排序一直到最高位排序完成以后, 数列就变成一个有序序列。 基数排序的两种方式: 高位优先,又称为最有效键(MSD),它的比较方向是由右至左; 低位优先,又称为最无效键(LSD),它的比较方向是由左至右;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 package class3;import utils.PrintIntArr;public class Program12RadixSort { public static void radixSort (int [] arr) { if (arr == null || arr.length < 2 ) { return ; } radixSort(arr, 0 , arr.length - 1 , maxbits(arr)); } public static int maxbits (int [] arr) { int max = Integer.MIN_VALUE; for (int i = 0 ; i < arr.length; i++) { max = Math.max(max, arr[i]); } int res = 0 ; while (max != 0 ) { res++; max /= 10 ; } return res; } public static void radixSort (int [] arr, int L, int R, int digit) { final int radix = 10 ; int i = 0 , j = 0 ; int [] help = new int [R - L + 1 ]; for (int d = 1 ; d <= digit; d++) { int [] count = new int [radix]; for (i = L; i <= R; i++) { j = getDigit(arr[i], d); count[j]++; } for (i = 1 ; i < radix; i++) { count[i] = count[i] + count[i - 1 ]; } for (i = R; i >= L; i--) { j = getDigit(arr[i], d); help[count[j] - 1 ] = arr[i]; count[j]--; } for (i = L, j = 0 ; i <= R; i++, j++) { arr[i] = help[j]; } } } public static int getDigit (int x, int d) { return ((x / ((int ) Math.pow(10 , d - 1 ))) % 10 ); } public static void main (String[] args) { int [] arr = {1 , 4 , 6 , 3 , 0 , 2 , 5 , 9 , 8 , 7 }; radixSort(arr); PrintIntArr.PrintIntArrays(arr); } }