zoukankan      html  css  js  c++  java
  • 排序算法Java版,以及各自的复杂度,以及由堆排序产生的top K问题

    常用的排序算法包括:

    • 冒泡排序
      每次在无序队列里将相邻两个数依次进行比较,将小数调换到前面, 逐次比较,直至将最大的数移到最后。最将剩下的N-1个数继续比较,将次大数移至倒数第二。依此规律,直至比较结束。时间复杂度:O(n^2)
    • 选择排序
      每次在无序队列中“选择”出最大值,放到有序队列的最后,并从无序队列中去除该值(具体实现略有区别)。时间复杂度:O(n^2)
    • 直接插入排序
      始终定义第一个元素为有序的,将元素逐个插入到有序排列之中,其特点是要不断的 移动数据,空出一个适当的位置,把待插入的元素放到里面去。时间复杂度:O(n^2)
    • 希尔排序:
      希尔排序是对直接插入排序的改进,希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个文件恰被分成一组,算法便终止。
    • 快速排序
      递归地将数组一分为2,左边是所有比第一个数小的,右边是所有比第一个数大的。这其中涉及到如何移动交换的技巧问题。本题为挖坑填坑模式。时间复杂度:O(nlogn)
    • 堆排序
      对需要排序的数组首先建立堆,然后(如果最终是由小到大排序)依次交换根位置与最后一个节点位置的数值,之后调整堆,循环往复。时间复杂度:O(nlogn)。关于堆这一数据结构以及堆排序可以参考百度百科:堆排序
    • 归并排序:给定两个已经排好序(由小到大)的数组,归并排序的意思是,选取数组A中的第一个值,与另一个数组B中的第一个值比较,每找到一个较小的值就写入新的数组C,直到所有的值都大于此值时,将此值写入C,选取数组A的一个值作为对照值,重复上面的操作。时间复杂度:O(nlogn)

    关于:各种排序算法时间复杂度和空间复杂度表 

    关于各种排序算法的稳定性

     1) 稳定的:如果存在多个具有相同排序码的记录,经过排序后,这些记录的相对次序仍然保持不变,则这种排序算法称为稳定的。
        插入排序、冒泡排序、归并排序、分配排序(桶式、基数)都是稳定的排序算法。
     2) 不稳定的:否则称为不稳定的。
        直接选择排序、堆排序、shell排序、快速排序都是不稳定的排序算法。

    关于不同引用场景下使用不同的算法:

    (1) 若n较小(如n≤50),可采用直接插入或直接选择排序。
         当记录规模较小时,直接插入排序较好;否则因为直接选择移动的记录数少于直接插人,应选直接选择排序为宜。
    (2) 若文件初始状态基本有序(指正序),则应选用直接插人、冒泡或随机的快速排序为宜;
    (3) 若n较大,则应采用时间复杂度为O(nlgn)的排序方法:快速排序、堆排序或归并排序。
         快速排序是目前基于比较的内部排序中被认为是最好的方法,当待排序的关键字是随机分布时,快速排序的平均时间最短;
         堆排序所需的辅助空间少于快速排序,并且不会出现快速排序可能出现的最坏情况。这两种排序都是不稳定的。
         若要求排序稳定,则可选用归并排序。但本章介绍的从单个记录起进行两两归并的  排序算法并不值得提倡,通常可以将它和直接插入排序结合在一起使用。先利用直接插入排序求得较长的有序子文件,然后再两两归并之。因为直接插入排序是稳定 的,所以改进后的归并排序仍是稳定的。

    补充:视觉直观感受 7 种常用的排序算法

    代码1:冒泡排序,选择排序,直接插入排序,希尔排序,快速排序代码。

      1 /**
      2      * 冒泡排序: 每次在无序队列里将相邻两个数依次进行比较,将小数调换到前面, 逐次比较,直至将最大的数移到最后。
      3      * 最将剩下的N-1个数继续比较,将次大数移至倒数第二位。 依此规律,直至比较结束。
      4      */
      5     public static void bubbleSort(int[] a) {
      6         int n = a.length;
      7         for (int i = 0; i < n - 1; i++) {
      8             for (int j = 0; j < n - i - 1; j++) {
      9                 if (a[j] > a[j + 1]) {
     10                     int temp = a[j + 1];
     11                     a[j + 1] = a[j];
     12                     a[j] = temp;
     13                 }
     14             }
     15         }
     16     }
     17 
     18     /**
     19      * 选择排序:每次在无序队列中“选择”出最大值,放到有序队列的最后,并从无序队列 中去除该值(具体实现略有区别)。
     20      */
     21     public static void selectSort(int[] a) {
     22         int n = a.length;
     23         int max_index = 0;
     24         for (int i = n - 1; i >= 0; i--) {
     25             max_index = i;
     26             for (int j = 0; j <= i; j++) {
     27                 if (a[j] > a[max_index])
     28                     max_index = j;
     29             }
     30             if (max_index != i) {
     31                 int temp = a[i];
     32                 a[i] = a[max_index];
     33                 a[max_index] = temp;
     34             }
     35         }
     36     }
     37 
     38     /**
     39      * 插入排序:始终定义第一个元素为有序的,将元素逐个插入到有序排列之中,其特点是要不断的 移动数据,空出一个适当的位置,把待插入的元素放到里面去。
     40      */
     41     public static void insertSort(int[] a) {
     42         int n = a.length;
     43         int j = 0;
     44         for (int i = 1; i < n; i++) {
     45             j = i - 1;
     46             int temp = a[i];
     47             while (j >= 0 && a[j] > temp) {
     48                 a[j + 1] = a[j];
     49                 j--;
     50             }
     51             a[j + 1] = temp;
     52             for (int m : a) {
     53                 System.out.print(m + ",");
     54             }
     55             System.out.println();
     56             // for (int j = i; j > 0; j--) {
     57             // if (a[j] < a[j - 1]) {
     58             // int temp = a[j];
     59             // a[j] = a[j - 1];
     60             // a[j - 1] = temp;
     61             // }
     62             // }
     63         }
     64     }
     65 
     66     /**
     67      * 快速排序:递归地将数组一分为2,左边是所有比第一个数小的,右边是所有比第一个数大的
     68      */
     69     public static void quickSort(int[] a) {
     70         quickSort(a, 0, a.length - 1);
     71     }
     72 
     73     private static void quickSort(int[] a, int l, int r) {
     74         if (l < r) {
     75             // 取左边第一个值作为分界值
     76             int mid = a[l];
     77             int i = l, j = r;
     78             while (i < j) {
     79                 while (i < j && a[j] > mid)
     80                     j--;
     81                 if (i < j)
     82                     a[i++] = a[j];
     83                 while (i < j && a[i] <= mid)
     84                     i++;
     85                 if (i < j)
     86                     a[j--] = a[i];
     87             }
     88             a[i] = mid;
     89             quickSort(a, l, i - 1);
     90             quickSort(a, i + 1, r);
     91         }
     92     }
     93 
     94     /**
     95      * 希尔排序是对直接插入排序的改进,希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,
     96      * 当增量减至1时,整个文件恰被分成一组,算法便终止。
     97      * 
     98      * @param a
     99      */
    100     public static void shellSort(int[] a) {
    101         int d = a.length;
    102         while (true) {
    103             d = d / 2;
    104             for (int x = 0; x < d; x++) {// 分组
    105                 // 对每组进行直接插入排序
    106                 for (int i = x + d; i < a.length; i = i + d) {
    107                     int temp = a[i];// 记录要插入的值
    108                     int j;
    109                     // 将要插入的值插入到正确位置,这一过程中不断检测前面的数,如果相比较大,则同时移动一次
    110                     for (j = i - d; j >= 0 && a[j] > temp; j = j - d) {
    111                         a[j + d] = a[j];
    112                     }
    113                     a[j + d] = temp;
    114                 }
    115             }
    116             if (d == 1) {
    117                 break;
    118             }
    119         }
    120     }
    View Code

    代码2:推排序(由于java版比较长,所以另写一个)

      1 public class HeapSort {
      2     private static int[] sort = new int[] { 1, 0, 10, 20, 3, 5, 6, 4, 9, 8, 12, 17, 34, 11 };
      3 
      4     public static void main(String[] args) {
      5         System.out.println(sort.length);
      6         buildMaxHeapify(sort);
      7         heapSort(sort);
      8         print(sort);
      9     }
     10 
     11     private static void buildMaxHeapify(int[] data) {
     12         // 没有子节点的才需要创建最大堆,从最后一个的父节点开始
     13         int startIndex = getParentIndex(data.length - 1);
     14         // 从尾端开始创建最大堆,每次都是正确的堆
     15         for (int i = startIndex; i >= 0; i--) {
     16             maxHeapify(data, data.length, i);
     17         }
     18     }
     19 
     20     /**
     21      * 创建最大堆
     22      *
     23      * @paramdata
     24      * @paramheapSize需要创建最大堆的大小,一般在sort的时候用到,因为最多值放在末尾,末尾就不再归入最大堆了
     25      * @paramindex当前需要创建最大堆的位置
     26      */
     27     private static void maxHeapify(int[] data, int heapSize, int index) {
     28         // 当前点与左右子节点比较
     29         int left = getChildLeftIndex(index);
     30         int right = getChildRightIndex(index);
     31 
     32         int largest = index;
     33         if (left < heapSize && data[index] < data[left]) {
     34             largest = left;
     35         }
     36         if (right < heapSize && data[largest] < data[right]) {
     37             largest = right;
     38         }
     39         // 得到最大值后可能需要交换,如果交换了,其子节点可能就不是最大堆了,需要重新调整
     40         if (largest != index) {
     41             int temp = data[index];
     42             data[index] = data[largest];
     43             data[largest] = temp;
     44             maxHeapify(data, heapSize, largest);
     45         }
     46     }
     47 
     48     /**
     49      * 排序,最大值放在末尾,data虽然是最大堆,在排序后就成了递增的
     50      *
     51      * @paramdata
     52      */
     53     private static void heapSort(int[] data) {
     54         // 末尾与头交换,交换后调整最大堆
     55         for (int i = data.length - 1; i > 0; i--) {
     56             int temp = data[0];
     57             data[0] = data[i];
     58             data[i] = temp;
     59             maxHeapify(data, i, 0);
     60         }
     61     }
     62 
     63     /**
     64      * 父节点位置
     65      *
     66      * @paramcurrent
     67      * @return
     68      */
     69     private static int getParentIndex(int current) {
     70         return (current - 1) >> 1;
     71     }
     72 
     73     /**
     74      * 左子节点position注意括号,加法优先级更高
     75      *
     76      * @paramcurrent
     77      * @return
     78      */
     79     private static int getChildLeftIndex(int current) {
     80         return (current << 1) + 1;
     81     }
     82 
     83     /**
     84      * 右子节点position
     85      *
     86      * @paramcurrent
     87      * @return
     88      */
     89     private static int getChildRightIndex(int current) {
     90         return (current << 1) + 2;
     91     }
     92 
     93     private static void print(int[] data) {
     94         int pre = -2;
     95         for (int i = 0; i < data.length; i++) {
     96             if (pre < (int) getLog(i + 1)) {
     97                 pre = (int) getLog(i + 1);
     98                 System.out.println();
     99             }
    100             System.out.print(data[i] + "|");
    101         }
    102     }
    103 
    104     /**
    105      * 以2为底的对数
    106      *
    107      * @paramparam
    108      * @return
    109      */
    110     private static double getLog(double param) {
    111         return Math.log(param) / Math.log(2);
    112     }
    113 }
    View Code

    代码3:top-K问题

     1 /**
     2      * 使用最大堆进行top K的选取
     3      */
     4     public static int[] topK(int[] a, int k) {
     5         int[] res = new int[k];
     6         if (k > a.length) {
     7             System.err.println("k larger than length of array");
     8             return res;
     9         }
    10         for (int i = 0; i < a.length; i++) {
    11             if (a[i] > res[0]) {
    12                 res[0] = a[i];
    13                 for (int j = 0; j < k && 2 * j + 1 < k;) {
    14                     int tag1 = 0, tag2 = 0;
    15                     if (2 * j + 2 < k && res[2 * j + 1] > res[2 * j + 2]) {
    16                         if (res[j] > res[2 * j + 2]) {
    17                             int temp = res[2 * j + 2];
    18                             res[2 * j + 2] = res[j];
    19                             res[j] = temp;
    20                             j = 2 * j + 2;
    21                             tag1 = 1;
    22                         }
    23                     } else {
    24                         if (res[j] > res[2 * j + 1]) {
    25                             int temp = res[2 * j + 1];
    26                             res[2 * j + 1] = res[j];
    27                             res[j] = temp;
    28                             j = 2 * j + 1;
    29                             tag2 = 1;
    30                         }
    31                     }
    32                     if (tag1 == 0 && tag2 == 0)
    33                         break;
    34                 }
    35             }
    36         }
    37         return res;
    38     }
    View Code
  • 相关阅读:
    路由
    客户端如何调用
    第一个简单的DEMO
    Web API初印象
    项目开发中经常使用的缓存
    EF的Model First
    如何计算代码的运行性能
    使用Obsolete特性来标记方法过时或弃用
    nodeJs实现文件上传,下载,删除
    node 部署教程二
  • 原文地址:https://www.cnblogs.com/chen-kh/p/6686837.html
Copyright © 2011-2022 走看看