第六章 堆排序
二叉堆数据结构是一种数组对象。
堆排序思想:首先建立一个最大堆函数,得到第一个节点是堆的最大值,将第一个节点与最后一个节点互换,将堆的大少减一,循环调用,即得到一个从大到小的序列。
堆排序代码:
w-s
Max-heapify(A,i) //子节点和父节点排序 O(lg(n))
{ l = left(i)
r = right(i)
if l <= heap-size[A] && A[l] > A[i]
then largest = l else largest = i
if r <= heap-size[A] && A[r] > A[largest]
then largest = r
if largest != i
then exchange A[i] <-> A[largest]
Max-heapify(A,largest) }
Build-max-heap(A) //建立完整的堆 O(n)
{
heap-size[A] = length[A]
for i = (length[A]/2) downto 1 //只需要将堆里面倒数第二排最后一个有子节点的点开始建立最大堆
do Max-heapify(A,i)
} /*堆排序算法*/
Heapsort(A) //对堆进行排序
{
Build-max-heap(A) O(n)
for i = length[A] downto 2
do exchange A[1] <-> A[i]
heap-size[A] = heap-size[A]-1
Max-heapify(A,1) O(nlg(n))
}
复杂度: T(n) = O(nlg(n))
优先队列(堆排序的一种应用):
/*
优先级队列支持操作:
Heap-maxinum(S)
Max-heap-Inser(S,x)
Heap-Extract-max(S) 去掉并返回S中具有最大关键字的元素
Heap-Increase-key(S,x,k) 将元素x的关键字的值增加到k,这里k不能小于x的原关键字
*/
// 堆里面最大值 w-s
Heap-maximum(A) O(1)
return A[1]
//插入x到堆里面
Max-heap-inset(A,key) O(lg(n))
{
heap-size[A] = heap-size[A]-1
A[heap-size[A]] = -∞
Heap-Increase-key(A,heap-size[A],key)
}
//去掉并返回S中最大关键字的元素
Heap-Extract-max(A) O(lg(n))
{
if heap-size[A] < 1
then error"heap underflow"
max = A[1]
A[1] = A[heap-size[A]]
heap-size[A] = heap-size[A]-1
Max-heapify(A,1)
return max
}
//将元素x的关键字加到k中
Heap-Increase-key(A,i,key) O(lg(n))
{
if key < A[i]
then error"new key is smaller than current key"
A[i] = key
while i > 1 && A[parent(i)] < A[i]
do exchange A[i] <-> A[parent(i)]
i = parent(i)
}
第七章 快速排序
快速排序的思想:分解:将数组划分两个子数组,取得一直在两个数组中间,并且此元素的下表即为数组排到第几位。
解决:通过递归调用快速排序,对数组进行排序。
合并:无需操作了,由分解知道,其实每个数已经排好序了。
//快速排序的解决式 w-s a-s b-s
Quick-sort(A,p,r) T(n)= T(n-1)+cn O(nlg(n)) O(nlg(n))
{
if p < r
then q = Partition(A,p,r)
Quick-sort(A,p,q-1)
Quick-sort(A,q+1,r)
}
//快速排序的分解
Partition(A,p,r)
{
x = A[r]
i = p-1
for j = p to r-1
do if A[j] <= x
then i = i+1
exchange A[i] <-> A[j]
exchange A[i+1] <-> A[r]
return i+1
}
快速排序的随机化版本
Randomized-partition(A,p,r)
{
i = random(p,r)//在A中随机抽取一个值i
exchange A[r] <-> A[i]
return partition(A,p,r)
}
Random-quicksort(A,p,r)
{
if p < r
then q = randomized-partition(A,p,r)
Random-quicksort(A,p,q-1)
Random-quicksort(A,q+1,r)
}
w-s : Ө(n^2)
期望的运行时间:Ө(nlg(n))
stooge 排序:
Stooge-sort(A,i,j)
{
if A[i] > A[j]
then exchange A[i] <-> A[j]
if i +1 >= j
then return
k = (j-i+1)/3 //round down
stooge-sort(A,i,j-k) //first two-thirds
stooge-sort(A,i+k,j) //last two-thirds
stooge-sort(A,i,j-k) //first two-thirds again
}
猜想:复杂度:T(n) = nlog3(n)
决策树模型(比较排序)
计数排序
思想:创建一个length(A)大小的数组B,和一个辅助数组C,辅助数组用来计数数组里面相同数的个数和位置,然后将辅助数组的书位置,即排好书序的下标赋给数组B的下表,将数组A对应的数赋给B的值。
Counting-sort(A,B,k) w-s
{
for i = 0 to k O(n)
do C[i] =0
for j = 1 to length[A] O(n)
do C[A[j]] = C[A[j]] +1
for i =1 to k O(n)
do C[i] = C[i] + C[i-1]
for j = length[A] downto 1 O(n)
do B[C[A[j]]] e= A[j]
C[A[j]] = C[A[j]] -1
}
复杂度:T(n) = O(n)
计数排序:
思想:首先将数进行分类,得出其位数,然后通过低位进行排序,直到最高位排序,最终得出排好的序列
Radix-sort(A,d)
{
for i = 1 to d
do use a stable sort to sort array A on digit i
}
T(n) = Ө(n)
桶排序
思想:创建一个length(A) 的数组,并且元素满足 0<= A[i] =< 1,通过十分位,百分位的数作为下标放入桶B里面,十分位相同,可通过创建链表的方式来比较百分位的位置,依次类推。
Bucket-sort(A) w-s
{
n = length[A]
for i = 1 to n O(n)
do inset A[i] into list B[nA[i]]
for i = 0 to n-1 O(n)
do sort list B[i] with insertion sort
concatenate the lists B[0] ,B[1].....B[n] together in order
}
T(n) = O(n)
第九章 中位数和顺序统计学
最大值和最小值
以期望线性时间做选择
如果我们不选最大值或最小值,而是选一个第i小的值。我们可以用一种分治算法——RAMDOMIZED_SELECT,它以快速排序为模型:把数组随机划分为两部分,A[p...q-1]的元素比A[q]小,A[q+1...r]的元素比A[q]大。与快速排序不同,如果i=q,则A[q]就是要找的第i小的元素,返回这个值;如果i < q,则说明第i小的元素在A[p...q-1]里;如果i > q,则说明第i小的元素在A[q+1...r]里。
RANDOMIZED_SELECT(A, p, r, i) {
1 if p == r
2 return A[p];
3 q = RANDOMIZED_PARTITION(A, p, r);
4 k = q-p+1;
5 if i == k
6 return A[q];
7 elseif i < k
8 return RANDOMIZED_SELECT(A, p, q-1, i);
9 else return RANDOMIZED_SELECT(A, q+1, r, i-k);
}
用代换法解上式,可以得到E(T(n))=O(n)。也就是说在平均情况下,任何顺序统计量(特别是中位数)都可以在线性时间内得到。
最坏情况线性时间的选择
首先我们需要稍微修改一下PARTITION算法(不是RANDOMIZED_PARTITION),它接收一个数组和一个值x,并把它划分为小于x和大于x的两部分(x为A中某个元素的值):
PARTITION_X(A, p, r, x) {
1 for i = 1 to n
2 if A[i] == x {
3 swap(A[i], A[n-1]);
4 break;
5 }
6 return PARTITION(A, p, r);
}
在修改划分算法后,我们通过以下步骤来实现在n个元素的数组中找第i小元素的SELECT:
SELECT(A, p, r, i) {
// 步骤1、2
1 count = ceiling(n/5);
2 for i = 1 to count-1
3 insertion sort A[(i-1)*5+1...i*5+1];
4 insertion sort A[(count-1)*5+1...n];
5 if count ==1
6 return A[floor(n/2)];
// 步骤3
7 create array B;
9 for i = 1 to count-1
10 B[i] = A[(i-1)*5+3];
11 B[count] = A[(count-1)*5 + floor((n - (count-1)*5)/2)];
12 x = SELECT(B, 1, count, floor(count/2));
// 步骤4
13 q = PARTITION_X(A, p, r, i);
14 k = q-p+1;
// 步骤5
15 if i == k
16 return x;
17 elseif i < k
18 return SELECT(A, p, q-1, i);
19 else
20 return SELECT(A, q+1, r, i-k);
}
SELECT算法在最坏情况下运行时间为O(n)