導航
1.瞭解什麼是堆
2.如何創建最小堆,最大堆
3.新增值在堆中如何進行
4.完整的堆排序,升序和降序(兩種方式)
————————————————————————————————————————
1.瞭解什麼是堆
樹型結構與圖型結構的差別:
看是否有迴路,無迴路的是樹型,有迴路的是圖型
滿二叉樹可以用數組進行存儲,層次遍歷順序存儲
設置其中父節點爲k,兒子節點(左結點,右節點) 其中左節點 爲2*k, 右節點爲 2 * k+1
高度爲log2^N
其中最典型的應用就是堆(也就是完全二叉樹)
完全二叉樹:
————————————————————————————————————————
2.如何將堆轉化爲最小堆,最大堆
有兩層的直接用父結點來比較
三層的應該有倒數一層的右邊節點開始依次往下
代碼如下:(創建最小堆)
//這裏用的是數組的方式存儲堆
void siftdown(int i) //輸入的是從i點往下找
{
int t,flag=0; //flag用來表示無需再進行往下查找
//從i位置開始,判斷是否有左孩子,並且沒有找到最佳位置
while(i*2<=n&&flag==0)
{
if(h[i]>h[i*2]) //比較父節點與左節點的大小
t = i*2;
else
t = i;
if(i*2+1<=n){ //判斷是否存在右孩子
if(h[t]>h[i*2+1]) //比較上一次父節點與左節點的最小值與右孩子大小
t = i*2+1;
}
if(t!=i){ //判斷得到最小值是否爲父節點,是的話flag標誌1,不是的話交換,繼續往下找
swap(t,i);
i=t;
}
else
flag = 1;
}
}
創建最大堆:只需要將父節點與左右孩子節點的判大小符號換一下就可以了
————————————————————————————————————————
3.新增值在堆中如何進行
先插入到堆中最後一個位置,在進行與父節點依次比對,找到最合適的位置
整個都用數組保存
代碼:
//這裏弄得是最小堆
void siftup(int i)
{
int i,flag=0; //flag等於0表示當前子節點比父節點小
if(i==1) return; //如果爲根節點直接返回
while(i!=1&&flag==0)
{
if(h[i]<h[i/2])
swap(i,i/2);
else
flag=1;
i=i/2; //讓子節點等於父節點的位置
}
}
————————————————————————————————————————
4.完整的堆排序,升序和降序
輸入提示:
14
99 5 36 7 22 17 46 12 2 19 25 28 1 92
升序:
#include <stdio.h>
int h[101]; //用來存放堆的數組
int n; //存放堆的個數
void swap(int x,int y)
{
int t;
t = h[x];
h[x] = h[y];
h[y] = t;
}
void siftdown(int i)
{
int t,flag=0; //flag用來表示無需再進行往下查找
//從i位置開始,判斷是否有左孩子,並且沒有找到最佳位置
while(i*2<=n&&flag==0)
{
if(h[i]>h[i*2]) //比較父節點與左節點的大小
t = i*2;
else
t = i;
if(i*2+1<=n){ //判斷是否存在右孩子
if(h[t]>h[i*2+1]) //比較上一次父節點與左節點的最小值與右孩子大小
t = i*2+1;
}
if(t!=i){ //判斷得到最小值是否爲父節點,是的話flag標誌1,不是的話交換,繼續往下找
swap(t,i);
i=t;
}
else
flag = 1;
}
}
void create()
{
int i;
for(i=n/2;i>=1;i--)
siftdown(i);
}
//用於返回最小值
int deletemax()
{
int t;
t=h[1];
h[1]=h[n];
n--; //刪除一位
siftdown(1); //進行最小堆
return t;
}
int main()
{
int i,num;
scanf("%d",&num);
for(i=1;i<=num;i++)
scanf("%d",&h[i]);
n = num;
//建立最小堆
create();
//刪除頂部元素,接着將最後一個元素放入第一個頂點中,輸出刪除的元素
for(i=1;i<=num;i++)
printf("%d ",deletemax());
return 0;
}
其中deletemax()函數作用是每次返回最小值,並且將最後一個值移入到根節點再次進行最小堆,這樣結束時數組中數據已不再是原來的。
若想用這樣的來求降序也是可以的,只需要建立最大堆就行了,其餘都一樣
另一種方式來求解升序和降序
//之前創建的是最大堆
void headsort()
{
while(n>1)
{
swap(1,n); //將第一個與最後一個進行交換,此時最後一個點爲最大值
n--; //下次建立最大堆不帶上最後一個值
siftdown(1); //進行最大堆
}
}
求解升序另一種方法完整:
#include <stdio.h>
int h[101]; //用來存放堆的數組
int n; //存放堆的個數
void swap(int x,int y)
{
int t;
t = h[x];
h[x] = h[y];
h[y] = t;
}
void siftdown(int i)
{
int t,flag=0; //flag用來表示無需再進行往下查找
//從i位置開始,判斷是否有左孩子,並且沒有找到最佳位置
while(i*2<=n&&flag==0)
{
if(h[i]<h[i*2]) //比較父節點與左節點的大小
t = i*2;
else
t = i;
if(i*2+1<=n){ //判斷是否存在右孩子
if(h[t]<h[i*2+1]) //比較上一次父節點與左節點的最小值與右孩子大小
t = i*2+1;
}
if(t!=i){ //判斷得到最小值是否爲父節點,是的話flag標誌1,不是的話交換,繼續往下找
swap(t,i);
i=t;
}
else
flag = 1;
}
}
//開始創建堆
void create()
{
int i;
for(i=n/2;i>=1;i--)
siftdown(i);
}
//之前創建的是最大堆
void headsort()
{
while(n>1)
{
swap(1,n); //將第一個與最後一個進行交換,此時最後一個點爲最大值
n--; //下次建立最大堆不帶上最後一個值
siftdown(1); //進行最大堆
}
}
int main()
{
int i,num;
scanf("%d",&num);
for(i=1;i<=num;i++)
scanf("%d",&h[i]);
n = num;
//建立最小堆
create();
headsort();
//刪除頂部元素,接着將最後一個元素放入第一個頂點中,輸出刪除的元素
for(i=1;i<=num;i++)
printf("%d ",h[i]);
return 0;
}