堆是一种完全二叉树,也叫二叉堆。
分别分为两种类型: 最大堆 以及 最小堆;
最大堆(大顶堆), 所有父节点都大于子节点
最小堆(小顶堆), 所有父子点都小于子节点
右为 大顶堆,
左为 小顶堆,
根节点叫堆顶 , 根节点一定是 整个堆中 最小/最大的。
堆排序利用这个 特点进行排序。
每次 它调整后, 最大的节点或最小的节点 总是排到第一位去,
那么 可以让最大 节点 存储到它 最大的节点编号上, 这样 最后一位就是保存了 所有节点中最大的节点了,依次的, 把第二大的浮去上,再放到 第二大的节点编号上,直到 最后 放到 2号 上。
构建大定堆的 思路:就是让所有的 非叶子节点(父节点) 与它的两个子节点
依次比较 ,保存最大。
从最后一个非叶子节点开始 依次比较保存 最大。
上图 最后一个非叶子 节点 编号 是4 它的值是 60, 它会和 编号8,9 进行比较 ,如果需要交换,需要 再去从交换过的 索引开始 对它 进行调整
然后再从 编号3 开始,它的值是80, 它会 和 编号 6,7进行 比较。
,如果需要交换,需要 再去从交换过的 索引开始 对它 进行调整
然后再从 编号2 开始 它的值是70,它会和编号4,5进行比较。
,如果需要交换,需要 再去从交换过的 索引开始 对它 进行调整
最后 从编号1开始,它的值是90,它会和编号2 和3 进行 比较。
完全二叉树中,它的左子节点 编号 是父节点编号的两倍。
即 leftIndex=2fatherIndex;
它的右节点是 左节点加上1,
即 rightIndex=2fatherIndex+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);
}
}