堆的插入和刪除


堆的定義

  堆就是一棵可以自我平衡的完全二叉樹

  優先隊列的底層數據結構就是堆,實現和堆基本一樣

 

 

 

 

由於堆存儲在下標從0開始計數的數組中,因此,在堆中給定下標為i的結點時:

如果 i = 0,結點 i 是根結點,無父結點;否則結點 i 的父結點為結點 [(i - 2) / 2]

如果 2i + 1 > n - 1,則結點 i 無左子女;否則結點 i 的左子女為結點 2i + 1

如果 2i + 2 > n - 1,則結點 i 無右結點;否則結點 i 的右子女為結點 2i + 2

 

堆特性

 

1、每個父節點都大於等於其所有后代結點。

2、堆的根節點含有堆中最大(或者最小)的對象。

3、堆中以任意節點為根節點的子樹仍然是堆。

 

 

堆的分類

最大堆(大根堆,大頂堆):根節點對象是最大對象的堆

最小堆(小根堆,小頂堆):根節點對象是最小對象的堆

 

 

堆的插入操作(以最小堆為例,將一個數組變成堆)

堆的插入操作是自底向上進行的,每次從堆的最后一個結點開始插入(將插入的值放入完全二叉樹的最后一個結點),為了維持堆的性質,還要從插入結點開始依次往前遞歸,去維持堆的三個特性

  ①我們現在有一個長度為n的數組a,里邊的元素都沒有順序,把這個數組變成最小堆

  ②然后我們新建一個完全二叉樹b,按數組下標0到n-1的順序依次放入完全二叉樹,也就是說現在b[0]=a[0],b[1]=a[1]......。

  ③取插入結點的父節點,比較父節點的值和插入結點的值,將較小的值交換到父節點位置。

  ④再以父節點為當前結點,重復上一步操作,知道遇到父節點比插入結點值小,就可以結束遞歸0

 

//建堆時間復雜度O(n)
int
Heap[MAX_SIZE]; int Cur = 0; void Insert(int val) //插入 { Heap[++Cur] = val; //新元素加入堆 int Temp_Cur = Cur; //加入元素后維持堆的性質,一直遞歸到符合堆性質的結點 while (Temp_Cur > 1) { //找父節點 int Root = Temp_Cur / 2; //父節點比子節點大-->小根堆 if (Heap[Root] > val) swap(Heap[Root], Heap[Temp_Cur]); //交換 //找到符合性質的堆就退出 else break; //更新當前結點位置 Temp_Cur = Root; } }

 

 

 

堆的刪除操作

堆的刪除操作是只能刪除堆頂的元素,刪除堆頂元素之后,把堆的最后一個元素放到堆頂,然后不斷向下維護堆的特性1、2、3

 

//刪除堆頂,接着維護堆的性質的時間復雜度為O(lgn)
int
Pop() //刪除 { if (Cur == 0) //堆空 return -99999999; //保存堆頂 int Temp_Top = Heap[1]; //將末尾結點提到樹根 Heap[1] = Heap[Cur]; int Root = 1; //從堆頂往下維護堆的特性,一直處理到葉子結點-->小根堆 while (2 * Root < Cur) { //左兒子 int L_Child = Root * 2; //右兒子 int R_Child = Root * 2 + 1; //只有左節點或者左節點比右節點的值小 if (R_Child >= Cur || Heap[L_Child] < Heap[R_Child]) { //讓左結點和根節點比較,把較小值交換到父節點位置上 if (Heap[Root] > Heap[L_Child]) { swap(Heap[Root], Heap[L_Child]); //更新位置 Root = L_Child; } else break; } //左結點值比右結點值大,比較父節點和右節點 else { if (Heap[Root] > Heap[R_Child]) { swap(Heap[Root], Heap[R_Child]); Root = R_Child; } else break; } } Cur--; //返回刪除的堆頂元素 return Temp_Top; }

 

堆的排序操作

//堆排序
//排序的空間復雜度為O(1)
int temp[MAX_SIZE]; int k = 0; void quick_sort(int n) { for (int i = 1; i <= n; i++) { temp[k++] = Pop(); } }

 

完整代碼

#include<iostream>
#include<string>
#define MAX_SIZE 100005
using namespace std;

int Heap[MAX_SIZE];
int Cur = 0;

void Insert(int val)     //插入
{
    Heap[++Cur] = val;     //新元素加入堆
    int Temp_Cur = Cur;
    //加入元素后維持堆的性質,一直遞歸到符合堆性質的結點
    while (Temp_Cur > 1)  
    {
        //找父節點
        int Root = Temp_Cur / 2;   
        //父節點比子節點大-->小根堆
        if (Heap[Root] > val)    
            swap(Heap[Root], Heap[Temp_Cur]);  //交換
        //找到符合性質的堆就退出
        else
            break;  
        //更新當前結點位置
        Temp_Cur = Root;     
    }
}

int Pop()    //刪除
{
    if (Cur == 0)  //堆空
        return -99999999;
    //保存堆頂
    int Temp_Top = Heap[1];   
    //將末尾結點提到樹根
    Heap[1] = Heap[Cur];   
    int Root = 1;
    //從堆頂往下維護堆的特性,一直處理到葉子結點-->小根堆
    while (2 * Root < Cur)   
    {
        //左兒子
        int L_Child = Root * 2;   
        //右兒子
        int R_Child = Root * 2 + 1;   
        //只有左節點或者左節點比右節點的值小
        if (R_Child >= Cur || Heap[L_Child] < Heap[R_Child])  
        {
            //讓左結點和根節點比較,把較小值交換到父節點位置上
            if (Heap[Root] > Heap[L_Child])  
            {
                swap(Heap[Root], Heap[L_Child]);  
                //更新位置
                Root = L_Child;  
            }
            else
                break;
        }
        //左結點值比右結點值大,比較父節點和右節點
        else
        {
            if (Heap[Root] > Heap[R_Child])
            {
                swap(Heap[Root], Heap[R_Child]);
                Root = R_Child;
            }
            else
                break;
        }
    }
    Cur--;
    //返回刪除的堆頂元素
    return Temp_Top;  
}
//堆排序
int temp[MAX_SIZE];
int k = 0;
void quick_sort(int n)
{
    for (int i = 1; i <= n; i++)
    {
        temp[k++] = Pop();
    }
    
}
int main()
{
    int n, x;
    cin >> n;
    for (int i = 0; i < n; i++) {
        cin >> x;
        Insert(x);
    }
    quick_sort(n);
    for (int i = 0; i <k; i++) {
        cout << temp[i] << ' ';
    }
    cout << endl;
    system("pause");
    return 0;
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM