二叉堆的详解

1、堆的定义

堆是一种数据结构,一种叫做完全二叉树的数据结构。

什么是二叉树?

二叉树是一种特殊的树。二叉树的客店是每个结点最多有两个儿子,左边的叫左儿子,右边的叫右儿子。

二叉树的分类

二叉树中还有两种特殊的二叉树,叫做满二叉树和完全二叉树。

如果二叉树中每个内部结点都有两个儿子,这样的二叉树叫做满二叉树。满二叉树的严格的定义是一棵深度为h且有 2 h − 1 2^h-1 2h1 个结点的二叉树。

若一棵二叉树的高度为h,除第h层外,其他各层 ( 1   h − 1 ) (1~h-1) (1 h1的结点数都达到最大个数,第 h h h 层从右向左连续缺若干结点,则这个二叉树就是完全二叉树。

如何存储二叉树

首先将完全二叉树进行从上到下,从左到右编号。若一个父节结点编号为k,那么它左儿子的编号就是 2 ∗ k 2*k 2k ,右儿子的编号就是 2 ∗ k + 1 2*k+1 2k+1 。如果已知儿子(左儿子或右儿子)的编号为 x x x ,那么它的父结点的编号为 x / 2 x/2 x/2(c++语言中除法只取整数部分)。

堆的性质

1.堆中某个结点的值均不大于(或不小于)其父结点的值。
2.堆是一棵完全二叉树。
假设一棵完全二叉树所有父结点都比子节点要小,这样的完全二叉树成为最小堆,最大堆同理。

堆的操作

堆支持以下操作:

1.插入:向堆中插入一个新结点。在数组的末尾加入新值,并从下到上调整堆的父子关系,如果该结点大于其父结点,则交换它和它的父结点,使得它所在的子树满足堆的性质,时间复杂度 O ( l o g n ) O(log n) O(logn)

void sift_up(int i)
{ //从下到上调整堆的父子关系
    if (i == 1)
        return;
    while (i != 1)
    {
        if (h[i] < h[i / 2])
            swap(h[i], h[i / 2]); //<为从小到大排序  >为从大到小排序
        else
            break;
        i = i / 2;
    }
    //for(int i=1;i<=n;i++)cout<<h[i]<<" ";
    //cout<<endl;
}

2.弹出:删除堆顶元素。将堆底的元素覆盖堆顶的元素,结点数-1,再从上到下调整堆的父子关系。时间复杂度 O ( l o g n ) O(log n) O(logn)

void erase(int x){//弹出
	//cout<<h[x]<<" ";//排序时用
	h[x]=h[n];
	h[n]=0;
	n--;//结点数-1
}
void erase(int x)
{ //弹出
    //cout<<h[x]<<" ";//排序时用
    h[x] = h[n];
    h[n] = 0;
    n--; //结点数-1
}
erase(1);
sift_down(1);

3.删除:将堆中的某个编号为x的元素删除。将堆底的元素覆盖该元素,结点数-1,并从该元素交换前的位置自顶向下调整堆的父子关系。时间复杂度 O ( l o g n ) O(log n) O(logn)

erase(x);
sift_down(x);

4、创建堆的两种方法

1、一边输入,一边执行插入操作。

for (int i = 1; i <= n; i++)
{
    cin >> h[i];
    sift_up(i);
    //for(int j=1;j<=n;j++)cout<<h[j]<<" ";
    //cout<<endl;
}

2、在全部输入完后,紧接着从最后一个非叶结点 ( n / 2 ) (n/2) n/2 遍历到根节点 ( 1 ) (1) 1 ,将当前结点向下调整,时间复杂度为 O ( n ) O(n) O(n)

for (int i = 1; i <= n; i++)
{
    cin >> h[i];
    //sift_up(i);
    //for(int j=1;j<=n;j++)cout<<h[j]<<" ";
    //cout<<endl;
}
for (int i = n / 2; i >= 1; i--)
    sift_down(i); //注意这是sift_down,不是sift_up! ! !

堆的排序(重点来了! ! !)

1、将输入的序列构造成一个大顶堆,根据大顶堆的性质,当前堆的根节点(堆顶)就是序列中最大的元素;
2、重复执行 n n n弹出操作,在覆盖堆顶时先将堆顶输出(或存储);
3、时间复杂度为 O ( n l o g n ) O(nlogn) O(nlogn)

大根堆

#include <cstring>
#include <bits/stdc++.h>

template <typename item>
class largest_heap
{
private:
    item heap[10001];
    int len;

public:
    largest_heap();
    void push(item const &);
    void pop();
    item top();
    int size();
    bool empty();
};

template <typename item>
largest_heap<item>::largest_heap()
{
    len = 0;
    memset(heap, 0, sizeof(heap));
}

template <typename item>
void largest_heap<item>::push(item const &n)
{
    heap[++len] = n;
    int son = len, father = son / 2;
    while (heap[son] > heap[father] && father >= 1)
    {
        swap(heap[son], heap[father]);
        son = father, father = son / 2;
    }
}

template <typename item>
void largest_heap<item>::pop()
{
    swap(heap[1], heap[len]);
    heap[len--] = 0;
    int father = 1, son = 2;
    while (son <= len)
    {
        if (son < len && heap[son] < heap[son + 1])
            son++;
        if (heap[father] < heap[son])
        {
            swap(heap[father], heap[son]);
            father = son, son = father * 2;
        }
        else
            break;
    }
}

template <typename item>
item largest_heap<item>::top()
{
    return heap[1];
}

template <typename item>
int largest_heap<item>::size()
{
    return len;
}

template <typename item>
bool largest_heap<item>::empty()
{
    return len;
}

小根堆

#include <cstring>
#include <bits/stdc++.h>

template <typename item>
class smallest_heap
{
private:
    item heap[10001];
    int len;

public:
    smallest_heap();
    void push(item const &);
    void pop();
    item top();
    int size();
    bool empty();
};

template <typename item>
smallest_heap<item>::smallest_heap()
{
    len = 0;
    memset(heap, 0, sizeof(heap));
}

template <typename item>
void smallest_heap<item>::push(item const &n)
{
    heap[++len] = n;
    int son = len, father = son / 2;
    while (heap[son] < heap[father] && father >= 1)
    {
        swap(heap[son], heap[father]);
        son = father, father = son / 2;
    }
}

template <typename item>
void smallest_heap<item>::pop()
{
    swap(heap[1], heap[len]);
    heap[len--] = 0;
    int father = 1, son = 2;
    while (son <= len)
    {
        if (son < len && heap[son] > heap[son + 1])
            son++;
        if (heap[father] > heap[son])
        {
            swap(heap[father], heap[son]);
            father = son, son = father * 2;
        }
        else
            break;
    }
}

template <typename item>
item smallest_heap<item>::top()
{
    return heap[1];
}

template <typename item>
int smallest_heap<item>::size()
{
    return len;
}

template <typename item>
bool smallest_heap<item>::empty()
{
    return len;
}

A C   c o d e AC \ code AC code

#include <bits/stdc++.h>
using namespace std;
int n, a, b;
int h[1000005];
void erase(int x)
{
    cout << h[x] << " ";
    h[x] = h[n--];
}
void sift_up(int i)
{
    if (i == 1)
        return;
    while (i != 1)
    {
        if (h[i] < h[i / 2])
            swap(h[i], h[i / 2]); //<为从小到大排序  >为从大到小排序
        else
            break;
        i = i / 2;
    }
    //for(int i=1;i<=n;i++)cout<<h[i]<<" ";
    //cout<<endl;
}
void sift_down(int i)
{
    if (i > n)
        return;
    while (i * 2 <= n)
    {
        i = 2 * i;
        if (i + 1 <= n && h[i + 1] < h[i])
            i = i + 1; //<为从小到大排序  >为从大到小排序
        if (h[i / 2] > h[i])
            swap(h[i / 2], h[i]); //>为从小到大排序  <为从大到小排序 (这里不同!)《——
        else
            break;
    }
}
int main()
{
    cin >> n;
    for (int i = 1; i <= n; i++)
    {
        cin >> h[i];
        sift_up(i);
        //for(int j=1;j<=n;j++)cout<<h[j]<<" ";
        //cout<<endl;
    }
    //for(int i=n/2;i>=1;i--)sift_down(i);//注意这是sift_down,不是sift_up! ! !
    while (n > 1)
    {
        erase(1);
        sift_down(1);
    }
    //for(int i=1;i<=n;i++)cout<<h[i]<<" ";
    return 0;
}

——————QAQ

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值