当前位置:   article > 正文

【堆】数据结构|详细解析|完美诠释|堆排序|模拟堆_堆数据结构

堆数据结构

一,堆的性质与特点

1.堆的数据结构

堆我们认为他是一种完全二叉树的数据结构,我们在使用这种数据结构的时候会使用一些二叉树种的性质,这些性质需要我们着重的理解和应用

二叉树的父子关系
设一个结点为u : 2 * u 为他的左子节点 , 2 * u+ 1 为他的右子结点
设一个结点为u : u / 2 为他的父亲结点

在这里插入图片描述

2.堆的存储结构

虽然数据结构中堆是一种非线性的数据结构,但是他的存储结构是一种绝对的线性存储结构,我们它通过一个指针cnt来完成这个堆结构的数据输入和输出操作.

3.堆的特点

堆是的h[1] 永远都是最小值,因为堆的压入压出操作

二,堆的操作和代码实现

1.堆的基础操作

堆两个基础操作组成,down ,up 操作,这两个操作涉及堆数据结构的 基本的构建和绝大多数操作。我们首先从down操作开始,down操作就是每次和他的儿子结点进行比较,如果他们比父亲大的话就进行交换操作,当这两个数值比他父亲结点小的时候,选用他们中比较小的一个进行交换.


void down (int u)
{
	int t = u ;
	if(2 * u  <= cnt && h[2 * u] < h[t])	t = 2 * u;
	if(2 * u + 1 <= cnt && h[2 * u + 1] < h [t]) t = 2 *  u + 1 ;
	
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

当完成这一步交换判断之后,我们首先判断一下他是否要进行交换,如果要进行交换操作,我们就进行交换操作,并且在交换完成之后完成剩下的再次down操作。

if(u ! = t)
{
	swap(h[t] , h[u] );
	down(t) ;
}
  • 1
  • 2
  • 3
  • 4
  • 5

总结一下这个操作

void down(int u)
{
	int t = u ;
	if( 2 * u <= cnt && h[ 2 * u] < h[t])	t = 2 * u;
	if(2 * u + 1 <= cnt && h[2 * u + 1] < h[t] ) t = 2 * u + 1;
	if(t != u )
	{
		swap(h[t],h[u]);
		down(t) ;
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

当完成这个简单的down操作之后我们再来看看简单的up操作,up操作实际上就是看看父亲结点比他的大小关系,当父亲大的时候,进行交换并且再次up,下面我们看代码实现

void up (int u )
{
	while(u / 2 > 0 && h[u / 2] > h[u])
	{
		swap(h[u / 2],h[u]);
		u = u / 2;
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

2.堆的操作

2.1堆的初始化操作

我们使用updown来完成初始化操作,首先我们知道堆是一个完全二叉树,使用这个完全二叉树的性质可以知道,堆的最小值是在最上面的,当我们用down完成这个操作的时候,我们是从下向上的完成这个操作,并且最后一行不用进行这次的操作
在这里插入图片描述当我们使用up操作的时候是不好控制的,一般情况下我们不使用up操作进行初始化

2.2堆的排序(集成操作)

堆每次都遍历一次h[1] 这个数,并且把他弹出,弹出之后我们再进行把末尾的元素赋值给h[1] 再进行down(1) 操作.

#include <iostream>
using namespace std ;

const int N = 100010 ;

int h[N] , cnt ;

void down (int u )
{
	int t = u ;
	if(2 * u <= cnt && h[2 * u] < h[t]) t = 2 * u;
	if(2 * u  + 1 <= cnt && h[2 * u + 1 ] < h[t]) t = 2 * u + 1;
	if(t != u)
	{
		swap(h[u], h [t]);
		down(t);
	}

}

int main ()
{
	int n ,m;
	cin >> n >> m;
	cnt = n ;
	for(int i = 1 ; i <= n ; i ++)
		cin >> h[i] ;
	for(int i = n / 2 ; i ; i --)
		down(i) ;
	for(int i = 0 ; i < m ; i ++ )
	{
		int x = h[1];
		h[1] = h[ cnt -- ];
		down(1) ;
		cout << x << " ";
	}
	return 0 ;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38

上述操作已经是比较完美了,但是,还是有一些能改进的地方,这个堆虽然能快速的反应最小值的情况但是,对于第k个插入的值我们还是没有办法,当然我们可以统过后面的堆模拟来改造一下swap_1这个函数就可以了。

3.堆的进阶模拟

在堆的进阶操作中,由于我们要进行堆的第k次运算的相关的操作,所以我们开了两个指针——相互指针ph[N]hp[N]
在这里插入图片描述首先我们在updown中要为了,查找这个相关值得操作更改一下swap函数要完成一些相关的更改

void swap_1(int u ,int v)
{
	swap(h[u],h[v]);
	swap(hp[u],hp[v]);
	swap(ph[hp[u]],ph[hp[v]]);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

剩下操作的更改就是在初始化赋值的时候,我们要把两个指针都完成他们的赋值操作,ph[N]第几个插入的指向的位置,m++,cnt++很好的就能完成这个赋值操作,同样的hp[N]就是一个完整的取反操作

#include <iostream>
using namespace std ;

const int N = 100010 ;

int h[N] , cnt ;
int hp[N], ph[N];

void swap_1(int u ,int v)
{
	swap(h[u],h[v]);
	swap(hp[u],hp[v]);
	swap(ph[hp[u]],ph[hp[v]]);
}
void down (int u )
{
	int t = u ;
	if(2 * u <= cnt && h[2 * u] < h[t]) t = 2 * u;
	if(2 * u  + 1 <= cnt && h[2 * u + 1 ] < h[t]) t = 2 * u + 1;
	if(t != u)
	{
		swap_1(u, t);
		down(t);
	}

}
void up (int  u)
{
	while (u / 2 and h[u / 2 ] > h[u])
	{
		swap_1(u,u / 2);
		u = u / 2;
	}
}



int main ()
{
	int n ;
	cin >> n ;
	int m = 1 ;
	while ( n -- )
	{
	    string str ;
	    cin >> str ;
	    if(str == "I")
	    //I 操作是一个标准的插入操作;
	    {
	        int x ;
	        cin >> x ;
	        h[++ cnt ] = x ;
	        //基础的的赋值操作
	        ph[m] = cnt ; hp[cnt] = m ++ ;
	        
	        //常规的:ph[N].hp[N[赋值操作;
	        up(cnt);
	    }
	    else if(str == "PM")
	    //返回最小值
	        cout << h[1] <<endl;
	    else if(str == "DM")
	    //删除最小值
            {
                swap_1(1,cnt);
                cnt -- ;
                //删除
                down(1) ;
                //规整操作;
            }
        else if(str == "D")
        //删除第k个插入的数
            {
                int k ;
                cin >> k;
                k = ph[k] ;
                swap_1(cnt -- , k);
                down (k) ;
                up(k) ;
            }
        else 
        {
        //更换第k个插入的数值为x 
            int k , x;
            cin >> k >> x;
            k = ph[k];
            h[k] = x;
            up(k);
            down(k);
        }
	}
	return 0 ;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/AllinToyou/article/detail/622731
推荐阅读
相关标签
  

闽ICP备14008679号