• 算法与数据结构(二):排序


    趁着需要面试百度的机会,复习了常用的排序方法。

    1、快速排序

    平均时间复杂度:O(nlogn),最差情况时间复杂度:O(n^2),即序列以有序的情况

    空间复杂度:O(1),不需要开辟额外的空间

    实现细节:递归的分而治之,

    在每一个递归中,将最后一个数作为比较数mid,pa指示小于的部分,pb指示大于等于mid的部分。

    当pa=pb时,比较结束。将序列分割为小于mid、等于mid和大于mid三段,分别递归

    2、计数排序

    时间复杂度:O(n)

    空间复杂度:O(码本)

    实现细节:在知道元素取值范围的情况下使用

    3、选择排序

    时间复杂度:O(n^2)

    空间复杂度:O(1)

    不稳定:序列(7) 2 5 9 3 4 [7] 1第一轮遍历就将(7)放到了最后

    实现细节:每一轮循环遍历后面n-i个元素,找出最小值与第i个元素交换

    4、冒泡排序

    时间复杂度:O(n^2)

    空间复杂度:O(1)

    实现细节:每一轮循环从i开始,两两比较x和x+1

    优化:若某一轮循环没有发生交换,则表明自元素i后的序列有序,对于局部有序序列可以有O(n)的性能

    5、归并排序

    时间复杂度:O(nlogn)

    空间复杂度:O(n)

    实现细节:首先递归的将序列等距离划分,向上返回时归并段两两比较,写入辅助数组,然后复制到对应的归并段位置,保证向上返回的归并段段内有序。

    6、堆排序

    一种树形选择排序,是对直接选择排序的改进。

    时间复杂度:O(nlogn),其中O(n)为建堆的时间复杂度,O(nlogn)为每次调整堆顶元素的时间复杂度

    空间复杂度:O(1)

    应用场景:适合大数据排序场合。TOPK

    实现细节:

    堆排序包括两个基本步骤:建立最小(大)堆和排序。其中的核心操作HeapAdjust(Heap H, int s, int m)。

    HeapAdjust输入参数为序列中的元素s和序列长度m。

    初始条件:s元素左右子树皆为堆

    递归的:比较s与左右子树根节点的大小,若s是最小值,则不发生交换,s保持了堆的特性,退出;若发生交换,则左/右子树的堆特性无法保持,递归的HeapAdjust发生交换的子树。

    退出:原来以s为根节点的子树保持了堆的特性

    建堆:从最后一个根节点length/2开始调整[length/2….0]这些根节点的堆特性。

    有序输出:循环的将头结点与最后一个结点交换,然后调用HeapAdjust保持自根节点的堆特性。

         

    堆排序源码:

    HeapAdjust操作

    1. public static void sift(int[] input, int s, int length) {
    2.    int left = 2 * s + 1;
    3.    int min = left;
    4.      
    5.    if (s > length / 2 - 1) return; //判断向下递归的退出条件
    6.      
    7.    //寻找根、左子树根、右子树根三者间的最小值
    8.    if (left + 1 < length && input[left] > input[left + 1])
    9.       min = left + 1;
    10.      
    11.    if (input[s] <= input[min]) {
    12.       return;
    13.    } else {
    14.       //交换,破坏了子树堆的特性,递归的调整
    15.       int tmp = input[s];
    16.       input[s] = input[min];
    17.       input[min] = tmp;
    18.      
    19.       sift(input, min, length);
    20.    }
    21. }

    主过程:

    1. public static void heapsort(int[] input) {
    2.    //建堆
    3.    for (int i = input.length / 2 - 1; i >= 0; i--) {
    4.       sift(input, i, input.length);
    5.    }
    6.    //输出堆头,调整堆头
    7.    for (int i = input.length - 1; i >= 0; i--) {
    8.       int tmp = input[0];
    9.       input[0] = input[i];
    10.       input[i] = tmp;
    11.       sift(input, 0, i);
    12.    }
    13. }

    7、寻找最大的K个数

    方法一:利用堆排序的特性

    时间复杂度:O(nlogK)

    首先建立一个K元素的最小堆,即根节点小于左右子树节点;

    接着遍历数组元素,若x>root,则root<-x,调整堆性质O(logK)

    整个过程为O(n*logK)

    方法二:线性期望时间O(n)

        采用randomized-select方法,从数组中随机选取一个数x,将数组划分为小于x的Sa和大于x的Sb的两个部分,i为x的坐标

        若i=K,则x就是第K个数,同时可以输出前k个数

        若i > K,则randomized-select(Sa,K)

        若i < K,则randomized-select(Sb, K-i)

    但是在最坏情况下,具有O(n^2)的时间复杂度,也就是每次划分极不均匀,选到的随机数不是最大,就是最小,每次划分为O(n),共要划分n次

         

         

         

         

       

  • 相关阅读:
    Oracle
    注解
    java 算法实现
    ConcurrentHashMap
    hashMap 1.8
    hashmap 1.7
    MySQL优化
    Mysql面试题
    tmux
    mysql 复制表结构、表数据的方法
  • 原文地址:https://www.cnblogs.com/zjgtan/p/3377680.html
Copyright © 2020-2023  润新知