//2.堆排序
namespace htx_heapsort
{
//2.堆排序
//<1>源码 <2>伪代码 <3>时间复杂度分析
//--------<1>源码---------最大堆--------------------
//如果数组的元素不是基本数据类型,而是对象,那么对应的类需要重载
//堆排序:从小到大 1->2
//建堆
template<class T>
void z_build_max_heap(T *A,const int aLength)
{
int maxIndex = aLength - 1;
//int a = maxIndex/2; 则a之后的元素都是二叉树里的没有字节点的节点(最终子节点)
//因为z_max_heapify要用到 2*i来定位子节点,所以如果这里的i的值大于a的话,就会读取到数组之外的数据
//这个时候,再进行父子节点值交换的时候会溢出
for(int i = maxIndex/2; i >= 0; i--)
z_max_heapify(A,i,maxIndex);
}
//堆维护:使大值为小值的父节点
template<class T>
void z_max_heapify(T *A,const int i,const int maxIndex)
{//A:数组首地址 i:当前父节点下标 maxIndex:最大下标
//因为实际的数组下标是从0开始的,而不是1,而left和right的计算是以1为基准的
//所以我们要将先将i+1,然后将得到的结果-1,就得到了以0为基准的left和right
int L = 2 * (i + 1) - 1;//left = 2*i
int R = 2 * (i + 1);//right = 2*i + 1
int largest = i;
if(L <= maxIndex && A[L] > A[i])
largest = L;
if(R <= maxIndex && A[R] > A[largest])
largest = R;
if(largest != i)
{
//保证父节点大于等于子节点
htx_quicksort::z_exchange(A[i],A[largest]);
z_max_heapify(A,largest,maxIndex);
}
}
template<class T>
void headsort12(T *A,const int aLength)
{
//1.建堆
z_build_max_heap(A,aLength);
int maxIndex = aLength - 1;
for(int i = maxIndex; i >= 1; i--)
{
//将最大值(根节点值)与最后的一个元素交换,形成后大前小的顺序
htx_quicksort::z_exchange(A[0],A[i]);
int maxIndex_ = i - 1;//最大下标
z_max_heapify(A,0,maxIndex_);
}
}
//--------<1>源码---------最小堆--------------------
//建堆
template<class T>
void z_build_min_heap(T *A,const int aLength)
{
int maxIndex = aLength - 1;
for(int i = maxIndex/2; i >= 0; i--)
z_min_heapify(A,i,maxIndex);
}
//堆维护:使大值为小值的父节点
template<class T>
void z_min_heapify(T *A,const int i,const int maxIndex)
{
int L = 2 * (i + 1) - 1;//left = 2*i
int R = 2 * (i + 1);//right = 2*i + 1
int largest = i;
if(L <= maxIndex && A[L] <= A[i])
largest = L;
if(R <= maxIndex && A[R] <= A[largest])
largest = R;
if(largest != i)
{
//保证父节点小于等于子节点
htx_quicksort::z_exchange(A[i],A[largest]);
z_min_heapify(A,largest,maxIndex);
}
}
template<class T>
void headsort21(T *A,const int aLength)
{
//1.建堆
z_build_min_heap(A,aLength);
int maxIndex = aLength - 1;
for(int i = maxIndex; i >= 1; i--)
{
//将最大值(根节点值)与最后的一个元素交换,形成后小前大的顺序
htx_quicksort::z_exchange(A[0],A[i]);
int maxIndex_ = i - 1;//最大下标
z_min_heapify(A,0,maxIndex_);
}
}
/*
简例
int A[6]={6,2,2,9,10,1};
htx_heapsort::headsort21(A,6);
for(int i=0;i<6;i++)
printf("%d ",A[i]);
*/
/*
--------<2>伪代码---------最大堆--------------------
定位父、子节点
PARENT(i)
return i/2 //向下取整,下同
LEFT(i)
return 2i
RIGHT(i)
return 2i+1
堆维护
MAX_HEAPIFY(A,i)
l = LEFT(i)
r = RIGHT(i)
if l <= A.heap-size and A[l]>A[i]
largest = l
else largest = i
if r<=A.heap-size and A[r]>A[largest]
largest = r
if largest != i
exchange A[i] with A[largest]
MAX_HEAPIFY(A,largest)
建堆
BUILD-MAX-HEAP(A)
A.heap-size = A.length
for i = A.length/2 downto 1
MAX-HEAPIFY(A,i)
堆排序
HEAPSORT(A)
BUILD-MAX-HEAP(A)
for i = A.length downto 2
exchange A[1] with A[i]
A.heap-size = A.heap-size - 1
MAX-HEAPIFY(A,1)
--------<4>时间复杂度分析-----------------------------
HEAPSORT(A)
BUILD-MAX-HEAP(A) ------- O(n)
for i = A.length downto 2 |
exchange A[1] with A[i] |
A.heap-size = A.heap-size - 1 | O(nlog(2)n)
MAX-HEAPIFY(A,1) |
所以O(n)= n + nlog(2)n = nlog(2)n
详析:
/
1.MAX_HEAPIFY 执行一次的最大代价是该节点所在高度h
n
在高度h上的节点数:┌ -------- ┐ (向上取整)
2^(h+1)
BUILD-MAX-HEAP 的总代价:各高度上的所有节点的代价之和
h n h h
∑ ┌ -------- ┐O(h) = O ( n ∑ ----- ) (常数项被忽略,┌ ┐表示向上取整)
h=0 2^(h+1) h=0 2^h
∞ h
极限:∑ ----- = 2
h=0 2^h
所以,总代价为 O(n * 2)= O(n)
//
2.再看这段代码的执行代价:
for i = A.length downto 2
exchange A[1] with A[i]
A.heap-size = A.heap-size - 1
MAX-HEAPIFY(A,1)
而 MAX-HEAPIFY 的执行代价为 O(h)
由 for i = A.length downto 2 可知,执行次数为 O(n)
所以,总代价为 O(n * h)
而O(h)= O(log(2)n)
所以 总代价为 O(nlog(2)n)
关于O(log(2)n)的由来,看下面:
现在要找到 n 与 h 的关系:
假设有二叉树(数值随便给,不要较真)
16
14 10
8 7 9
图1
------------------
16
14 10
8
图2
------------------
16
14 10
8 7 9 3
2
图3
对于从上到下第h层而言(根节点为第0层),在满的情况下有(2^h)个节点,
在总共有n个节点的二叉树上
假设二叉树如 图2所示,最底层只有一个节点,
那么对于有 x 个节点有:
1 + 2^1 + 2^2 + 2^3 + ... + 2^m + 1 = x (此处 m = h -1 第 h 层只有一个节点)
等比数列求和:1 + 2*(1-2^m)/(1-2) + 1 = 2^(m+1) = x
则log(2)x = m + 1 而 h = m + 1
所以 h = log(2)x
再设 z = x + 2^(m+1)
则 h + 1 = log(2)z
所以,对于任何值n ,只要满足 x <= n < z
对于 h <= log(2)n < h + 1 必定成立
所以对于一个堆二叉树而言,它的高度就是:
h = └ log(2)n ┘ (└ ┘表示向下取整)
所以 MAX-HEAPIFY 的执行代价为 O(n)=log(2)n
*/
}
堆排序:源码(C++)--伪代码--时间复杂度解析
最新推荐文章于 2024-03-12 18:09:32 发布