堆排序:源码(C++)--伪代码--时间复杂度解析

//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

	*/


}

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值