堆是一種完全二叉樹,也叫二叉堆。
分別分爲兩種類型: 最大堆 以及 最小堆;
最大堆(大頂堆), 所有父節點都大於子節點
最小堆(小頂堆), 所有父子點都小於子節點
右爲 大頂堆,
左爲 小頂堆,
根節點叫堆頂 , 根節點一定是 整個堆中 最小/最大的。
堆排序利用這個 特點進行排序。
每次 它調整後, 最大的節點或最小的節點 總是排到第一位去,
那麼 可以讓最大 節點 存儲到它 最大的節點編號上, 這樣 最後一位就是保存了 所有節點中最大的節點了,依次的, 把第二大的浮去上,再放到 第二大的節點編號上,直到 最後 放到 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);
}
}