一、排序算法 - 图1

Java和Python对于对象的排序都是归并排序,因为是稳定的。

冒泡排序 时间复杂度为O(n^2)

  1. public static void sort(int arr[]) {
  2. for(int i = 0; i < arr.length - 1; i++) {
  3. for(int j = 0; j < arr.length - 1 - i; j++) {
  4. int temp = 0;
  5. if(arr[j] > arr[j+1]) {
  6. temp = arr[j];
  7. arr[j] = arr[j+1];
  8. arr[j+1] = temp;
  9. }
  10. }
  11. }
  12. }
  13. // 优化,设置一个临时变量来标记该数组是否已经有序,如果有序就直接进入下一次遍历。
  14. public static void sort(int arr[]) {
  15. for(int i = 0; i < arr.length - 1; i++ ) {
  16. boolean isSort = true;
  17. for(int j = 0; j < arr.length - 1 - i; j++ ){
  18. int temp = 0;
  19. if(arr[j] > arr[j + 1]){
  20. temp = arr[j];
  21. arr[j] = arr[j + 1];
  22. arr[j + 1] = temp;
  23. isSort = false;
  24. }
  25. }
  26. if(isSort){
  27. break;
  28. }
  29. }
  30. }

选择排序 时间复杂度为O(n^2)

  1. public static void sort(int arr[]) {
  2. for (int i = 0; i < arr.length; i++ ) {
  3. int min = i; //最⼩小元素的下标
  4. for(int j = i + 1; j < arr.length; j++ ) {
  5. if(arr[j] < arr[min]) {
  6. min = j; //找最⼩小值下标
  7. }
  8. }
  9. //交换位置
  10. int temp = arr[i];
  11. arr[i] = arr[min];
  12. arr[min] = temp;
  13. }

插入排序 时间复杂度为O(n^2)

  1. public static void sort(int[] arr) {
  2. int n = arr.length;
  3. for(int i = 1; i < n; ++i) {
  4. int value = arr[i];
  5. int j = 0;
  6. for(j = i -1; j >= 0; j--) {
  7. if(arr[j] > value) {
  8. arr[j+1] = arr[j];
  9. } else {
  10. break;
  11. }
  12. }
  13. arr[j+1] = value;
  14. }
  15. }

希尔排序 时间复杂度(n),按照间隔排,间隔里面按照插入排序排

  1. public static void sort(int[] arr) {
  2. int length = arr.length;
  3. int gap = 1;
  4. while (gap < length) {
  5. gap = gap * 3 + 1;
  6. }
  7. while (gap > 0) {
  8. for (int i = gap; i < length; i++) {
  9. int tmp = arr[i];
  10. int j = i - gap;
  11. while (j >= 0 && arr[j] > tmp) {
  12. arr[j + gap] = arr[j];
  13. j -= gap;
  14. }
  15. arr[j + gap] = tmp;
  16. }
  17. gap = gap / 3;
  18. }
  19. }

归并排序 时间复杂度为 O(nlogn)

  1. public static void sort(int[] arr) {
  2. int[] tempArr = new int[arr.length];
  3. sort(arr, tempArr, 0, arr.length - 1);
  4. }
  5. /**
  6. * 归并排序
  7. * @param arr 排序数组
  8. * @param tempArr 临时存储数组
  9. * @param startIndex 排序起始位置
  10. * @param endIndex 排序终⽌止位置
  11. */
  12. private static void sort(int[] arr, int[] tempArr, int startIndex, int endIndex) {
  13. if (endIndex <= startIndex) {
  14. return;
  15. }
  16. int middleIndex = startIndex + (endIndex - startIndex) / 2;
  17. sort(arr, tempArr, startIndex, middleIndex);
  18. sort(arr, tempArr, middleIndex + 1, endIndex);
  19. merge(arr, tempArr, startIndex, middleIndex, endIndex);
  20. }
  21. /**
  22. * 归并
  23. * @param arr 排序数组
  24. * @param tempArr 临时存储数组
  25. * @param startIndex 归并起始位置 * @param middleIndex 归并中间位置 * @param endIndex 归并终⽌止位置
  26. */
  27. private static void merge(int[] arr, int[] tempArr, int startIndex, int middleIndex, int endIndex) {
  28. // 复制要合并的数据
  29. for(int s = startIndex; s <= endIndex; s++) {
  30. tempArr[s] = arr[s];
  31. }
  32. // 左边⾸首位下标
  33. int left = startIndex;
  34. // 右边⾸首位下标
  35. int right = middleIndex + 1;
  36. for (int k = startIndex; k <= endIndex; k++) {
  37. if (left > middleIndex) {
  38. // 如果左边的⾸首位下标⼤大于中部下标,证明左边的数据已经排完了了。
  39. arr[k] = tempArr[right++];
  40. } else if (right > endIndex) {
  41. // 如果右边的⾸首位下标⼤大于了了数组⻓长度,证明右边的数据已经排完了了。
  42. arr[k] = tempArr [left++];
  43. } else if (tempArr[right] < tempArr[left]) {
  44. // 将右边的⾸首位排⼊入,然后右边的下标指针+1
  45. arr[k] = tempArr[right++];
  46. } else {
  47. // 将左边的⾸首位排⼊入,然后左边的下标指针+1
  48. arr[k] = tempArr[left++];
  49. }
  50. }
  51. }

快速排序 时间复杂度为O(nlogn)

  1. private static void quickSort(int[] arr, int left, int right) {
  2. if (left < right) {
  3. int i, j, x;
  4. i = left;
  5. j = right;
  6. x = arr[i];
  7. while (i < j) {
  8. // 从右向左找到第一个小于x的数
  9. while (i < j && arr[j] > x) j--;
  10. if (i < j)
  11. arr[i++] = arr[j];
  12. // 从左向右找到第一个大于x的数
  13. while (i < j && arr[i] < x) i++;
  14. if (i < j)
  15. arr[j--] = arr[i];
  16. }
  17. arr[i] = x;
  18. quickSort(arr, left,i - 1);
  19. quickSort(arr,i + 1, right);
  20. }
  21. }

桶排序,不常用,复杂,

主要应用为计数排序和基数排序

计数排序,求众数

非比较思想、量大数小

  1. private static int getMax(int[] a) {
  2. int max;
  3. max = a[0];
  4. for (int i = 1; i < a.length; i++)
  5. if (a[i] > max)
  6. max = a[i];
  7. return max;
  8. }
  9. public static int[] sort(int[] a, int max) {
  10. int[] count = new int[++max];
  11. int[] result = new int[a.length];
  12. if (a == null || max < 1)
  13. return null;
  14. // 1. 计数
  15. for(int i = 0; i < a.length; i++)
  16. count[a[i]]++;
  17. // 2. 排序,稳定
  18. for (int i = 1; i < count.length; i++) {
  19. count[i] = count[i] + count[i-1];
  20. }
  21. for (int i = a.length - 1; i >= 0; i--) {
  22. result[--count[a[i]]] = a[i];
  23. }
  24. return result;
  25. }

基数排序

非比较思想、桶思想的一种、多关键字排序

堆排序,最大堆通常被用来进行”升序”排序,而最小堆通常被用来进行”降序”排序

  1. public static void maxHeapDown(int[] a, int start, int end) {
  2. int c = start; // 当前(current)节点的位置
  3. int l = 2 * c + 1; // 左(left)孩子的位置
  4. int tmp = a[c]; // 当前(current)节点的大小
  5. for (; l <= end; c = l,l = 2 * l + 1) {
  6. // "l"是左孩子,"l+1"是右孩子
  7. if (l < end && a[l] < a[l+1])
  8. l++; // 左右两孩子中选择较大者,即m_heap[l+1]
  9. if (tmp >= a[l])
  10. break; // 调整结束
  11. else { // 交换值
  12. a[c] = a[l];
  13. a[l]= tmp;
  14. }
  15. }
  16. }
  17. public static void heapSortAsc(int[] a, int n) {
  18. int i,tmp;
  19. // 从(n/2-1) --> 0逐次遍历。遍历之后,得到的数组实际上是一个(最大)二叉堆。
  20. for (i = n / 2 - 1; i >= 0; I--)
  21. maxHeapDown(a, i, n-1);
  22. // 从最后一个元素开始对序列进行调整,不断的缩小调整的范围直到第一个元素
  23. for (i = n - 1; i > 0; I--) {
  24. // 交换a[0]和a[i]。交换后,a[i]是a[0...i]中最大的。
  25. tmp = a[0];
  26. a[0] = a[i];
  27. a[i] = tmp;
  28. // 调整a[0...i-1],使得a[0...i-1]仍然是一个最大堆。
  29. // 即,保证a[i-1]是a[0...i-1]中的最大值。
  30. maxHeapDown(a, 0, i-1);
  31. }
  32. }