堆的插入和删除

堆的定义

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

  优先队列的底层数据结构就是堆,实现和堆基本一样

 

 

 

 

由于堆存储在下标从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;
}

 

posted @ 2020-05-14 23:08  知道了呀~  阅读(3926)  评论(0编辑  收藏  举报