基于C#的堆排序

堆是一种完全二叉树,也叫二叉堆。
分别分为两种类型: 最大堆 以及 最小堆;

最大堆(大顶堆), 所有父节点都大于子节点
最小堆(小顶堆), 所有父子点都小于子节点

右为 大顶堆,
左为 小顶堆,

根节点叫堆顶 , 根节点一定是 整个堆中 最小/最大的。
基于C#的堆排序_第1张图片

堆排序利用这个 特点进行排序。
每次 它调整后, 最大的节点或最小的节点 总是排到第一位去,
那么 可以让最大 节点 存储到它 最大的节点编号上, 这样 最后一位就是保存了 所有节点中最大的节点了,依次的, 把第二大的浮去上,再放到 第二大的节点编号上,直到 最后 放到 2号 上。

基于C#的堆排序_第2张图片

构建大定堆的 思路:就是让所有的 非叶子节点(父节点) 与它的两个子节点
依次比较 ,保存最大。
从最后一个非叶子节点开始 依次比较保存 最大。

上图 最后一个非叶子 节点 编号 是4 它的值是 60, 它会和 编号8,9 进行比较 ,如果需要交换,需要 再去从交换过的 索引开始 对它 进行调整

然后再从 编号3 开始,它的值是80, 它会 和 编号 6,7进行 比较。
,如果需要交换,需要 再去从交换过的 索引开始 对它 进行调整

然后再从 编号2 开始 它的值是70,它会和编号4,5进行比较。
,如果需要交换,需要 再去从交换过的 索引开始 对它 进行调整

最后 从编号1开始,它的值是90,它会和编号2 和3 进行 比较。

完全二叉树中,它的左子节点 编号 是父节点编号的两倍。
即 leftIndex=2fatherIndex;
它的右节点是 左节点加上1,
即 rightIndex=2
fatherIndex+1
逆推 可以得
fatherIndex=leftIndex/2;
fatherIndex=(rightIndex-1)/2;

最后一个非叶子节点编号,就是最后一个节点的 父节点,
完全二叉树中 最后一个 节点 一定是 右子节点。
所以 非叶子节点编号公式 为
(length-1)/2

知道这个 就可以进行调整了。

//从 调整num编号的元素, 仅仅是调整一个 
 void HeapAdjust(int numToAdjust,int[] nums,int maxLimit)
 {
	    int i=numToAdjust;
		int tempMaxNum=i; //保存 最大
		while(true)
		{
			int left= i*2;
			int right= i*2+1;
	
			//节点 存在  且是它的值是最大的 
			if(left<maxLimit&& nums[left-1]>nums[tempMaxIndex-1])
			{
			 	tempMaxNum=left;
			}
			if(rightt<maxLimit&&nums[right-1]>nums[tempMax-1])
			{
			 	tempMaxNum=right;
			}
			//需要交换
			if(tempMaxNum!=i)
			{
				int temp=nums[temMaxNum-1];
				nums[temMaxNum-1]=nums[i];
				nums[i]=temp;
			 	i=tempMaxNum;//再从交换过的节点 进行调整
			}else//不需要调整退出
			{
				break; 
			}
		}
 }
void BuildHeap(int[] nums)
{
	//从最后一个非叶子节点开始
	for(int i=nums.Lenght/2;i>0;i--)
	{
		HeapAdjust(i,nums,nums.Length);
	}
}

void HeapSort(int[] nums)
{
	BuildHeap(nums);//构建 大定堆  
	for(int i=nums.Length-1,i>1;i--)
	{
		int temp=nums[i];
		nums[i]= nums[0];
		nums[0]=temp;
		//首尾交换  再进行调整堆 最后一个节点不需要 交换了  也就不用+1了  
		//因为 编号 比索引多1
		 HeapAdjust(1,nums,i); 
	}
}

你可能感兴趣的:(S1_数据结构学习)