您的位置:首页 > 理论基础 > 数据结构算法

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

2013-10-19 11:38 127 查看
趁着需要面试百度的机会,复习了常用的排序方法。

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操作

public
static
void sift(int[] input, int s, int length) {

   int left = 2 * s + 1;

   int min = left;

   
 
   if (s > length / 2 - 1) return; //判断向下递归的退出条件

   
 
   //寻找根、左子树根、右子树根三者间的最小值

   if (left + 1 < length && input[left] > input[left + 1])

      min = left + 1;

   
 
   if (input[s] <= input[min]) {

      return;

   } else {

      //交换,破坏了子树堆的特性,递归的调整

      int tmp = input[s];

      input[s] = input[min];

      input[min] = tmp;

   
 
      sift(input, min, length);

   }

}

主过程:

public
static
void heapsort(int[] input) {

   //建堆

   for (int i = input.length / 2 - 1; i >= 0; i--) {

      sift(input, i, input.length);

   }

   //输出堆头,调整堆头

   for (int i = input.length - 1; i >= 0; i--) {

      int tmp = input[0];

      input[0] = input[i];

      input[i] = tmp;

      sift(input, 0, i);

   }

}

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次

   
 

   
 

   
 

   
 

   
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: