c语言删除最小的j个元素,最小-最大堆的插入和删除

一、定义

最小-最大堆:各层交替为最小层和最大层的堆。

最大层:该层上的节点大于等于以其为根节点的子树上的所有节点。

最小层:该层上的节点小于等于以其为根节点的子树上的所有节点。

本文中,我们令堆的层数从1开始,节点编号也从1开始,即根节点为第1层,编号为1。

最小最大对具有如下性质:

对于任一节点,从该节点到叶节点的任意一条路径,

(1)

处于最小层的节点关键字逐渐增大;

(2)

处于最大层的节点关键字逐渐减小;

(3)

所有处于最大层的节点关键字大于所有处于最小层的节点的关键字。

证明:

假设取这样一条路径,上面的节点依次为x1、x2、x3、…、xn。假设x1处于最小层,则,所有处于最小层的元素为x3、x5等,所有处于最大层的元素为x2、x4等。

(1) 由于x3处于以x1为根的树上,且x1处于最小层,因此x1小于x3,同理,x5处于以x3为根的树上,且x3处于最小层,因此x3小于x5等,依次类推,所有处于最小层的元素逐渐增大;

(2) 因为x4处于以x2为根的树上,且x2处于最大层,因此x2大于x4,依次类推,所有处于最大层的元素依次减小;

(3) 假设最小层最后一个节点是xs,最大层最后一个节点为xt,因为不可能连续两层或两层以上是最大层或最小层,因此xs与xt必然处于相邻的两层,且xs小于xt(假设xs是xt的父节点,因为xs是最小层,而xt处于以xs为根的子树上,因此,xs小于xt;假设xs是xt的子节点,因为xt是最大层,而xs处于以xt为根的子树上,因此,xs小于xt,因此,不管xs和xt谁是父节点,都有xs小于xt)。

由前面可知,xs是x1、x3、…、xs等节点的最大值,xt是x2、x4、…、xt等节点的最小值,前一序列的最大值小于后一序列的最小值,因此前一序列的所有值小于后一序列的所有值。

证明结束。

推论:假设最小-最大堆中有n个节点,其中最小层节点数为ms,最大层节点数为mt,则最小层节点必是n个节点中最小的ms个节点,最大层节点必是n个节点中最大的mt个节点。

二、最小-最大堆的插入

具体实现见下面,我们使用min_max_insert将item插入到堆heap中,插入前节点个数为*n。边界条件不进行详细解释。

首先将item存入heap[*n+1]处,检查该元素的父节点是最大层还是最小层。

1)父节点是最小层,且该元素小于父节点

该节点是最后一个节点且处于最大层,本次插入只增加最大层节点数,最小层节点数不变;

父节点是最小层的最后一个元素,是最小层节点中最大的节点;

假设插入前最小层节点数为ms,最大层节点数为mt,则插入后最小层节点数不变,最小层节点数为mt+1。由推论可知,最小层节点是所有节点中最小的前ms个节点,父节点属于这ms个节点中最大的节点,现在新插入的节点小于父节点,就意味着,新节点将代替父节点成为最小的前ms个节点之一,因此父节点属于最大层节点。

又由于父节点原先小于最小层,因此小于所有原先最大层的节点,现在转入最大层后,成为最大层节点中最小的节点,根据最小最大堆的性质可知,父节点必属于其所属路径上的最后一个节点。因此我们直接把父节点放入heap[*n+1]处即可。

对于新加入节点,我们不需要改动其它路径,只需沿着heap[*n+1]到根节点的这条路径,找出所有最小层节点,将其放在某位置,使得该路径上节点顺序满足最小最大堆性质(1)即可。

2)父节点是最小层,且该元素大于父节点

同理,新加入节点只是改变了最大层节点数,不会改变最小层节点数。

父节点是前ms个最小节点中最大的,新节点大于父节点,所以新节点不可能是前ms个最小节点,所以新节点属于最大层。

因此,我们只需找出heap[*n+1]到根节点的这条路径上所有最大层节点,将新节点放入某个位置,其它节点依次后移,使得这些节点满足最小最大堆的性质(2).

3)父节点是最大层,且该元素大于父节点

此时,新加入的节点处于最小层,所以新节点的加入改变的是最小层节点数,不会改变最大层节点数。

最大层节点是所有节点中最大的mt个节点,且父节点是则mt个节点中最小的节点,新节点大于父节点,就意味着父节点不可能再成为最大的前mt个节点,所以新节点代替父节点成为最大层节点。

由于父节点大于所有最小层节点,所以父节点应该是出于该路径上的最后一个位置,因此直接将父节点放入heap[*n+1]即可。

对于新节点,只需找出heap[*n+1]到根节点路径上的所有最大层节点,将新节点放入某位置,其它节点依次后移,使得该路径上所有最小层节点满足性质(1)即可。

4)父节点是最大层,且该元素小于父节点

最大层节点不变,仍是原先的mt个节点。

新节点属于最小层节点,将新节点加入heap[*n+1],找出heap[*n+1]到根节点路径上所有最小层节点,重新排序,满足性质(1)。

三、最小-最大堆的删除(最小元素)

基本思想:最小元素就是最小-最大堆根节点对应的元素,所以每次都是直接删除该节点。如果还有剩余节点,接着要做的就是将剩余节点重新组织成一个新的最小-最大堆。

最直接的方式就是对所有元素重新建堆,即把剩余元素逐个输入堆里,每输入一个元素调用一次函数min_max_insert,将已输入元素建成最小-最大堆,直到所有元素输入为止。但这么做显然效率是比较低的,原先的堆里所包含的信息被全部丢弃。直觉告诉我们,可以利用原先堆的信息,找到更简便的方法。

我们现在将剩余节点全部保留在原位置,缺了一个根节点,于是,我们很自然的想到要找一个节点放入根节点位置,我们找到剩余节点中最小的节点放入根节点。

因为最小最大堆的根节点是整个堆中最小的元素,所有首先在剩余节点中找到最小的节点,并将该节点放入根节点处;假设最小节点位置为k。

现在k位置空了下来。另外,现在堆节点数少了一个,最后一个节点需要删除,也就意味着该节点存放的元素将没有位置存放,用item表示,我们很自然地想到,将item放在k位置。那么这么做究竟是否可行呢?通过分析,直接放在有些情况下会违反最小-最大堆,下面我们具体分析:

(1)只剩一个节点,不需进行任何操作(该节点已被放入根节点);

(2)剩余两个或两个以上节点且k是最后一个节点,不需进行任何操作(该节点已被放入根节点);

(3)剩余两个或两个以上节点且k不是最后一个节点其它情况,则需要根据k可能出现的位置来进一步分析:

首先,k只可能出现在第二层或第三层(否则不可能是最小节点)。

如果出现在第二层,则有两种可能:左儿子、右儿子。

如果是左儿子,说明左儿子没有子节点,右儿子也没有子节点(完全二叉树),我们只需要将item直接放入k节点处即可;

如果是右儿子,则右儿子没有子节点,左儿子可能有子节点。不管左儿子有没有子节点,都只需将item放入k节点即可;

如果出现在第三层,不管最后一个节点是不是k节点的后代,甚至不管k是否有后代,将item放入k节点后,都只会影响从根节点出发到到叶节点结束,且经过k节点的路径,其它路径仍满足最小最大堆的性质。

我们由最小最大堆的性质和定义可知,检查一个堆是否为最小最大堆,只需检查其各条路径是否满足最小-最大堆性质即可。

违反最小-最大堆有下面几种情况:

item大于k的父节点:我们只需要item和父节点调换,此时其它路径仍不会违反,父节点仍是那些路径上的最大节点。接下来,只需把item(此时已变为原先k的父节点元素)放入k节点,但同样不能直接放入,插入方式与最开始将最后一个元素插入根节点一样。

因为此时k是第三层,其父节点是第二层,为最大层,所以父节点应大于k节点;父节点处于第二层,大于以其为根的堆中所有元素。item既然大于父节点,且即将成为这个堆中的元素之一,因此item是新堆中最大的元素,应将其放在父节点的位置,且不管后面如何排,都不会改变,也就是说,我们接下来只需考虑以k节点及k节点的兄弟为根的子树。另外,以k节点的兄弟为根的子树在父节点和item交换后并不违反最小-最大堆性质,因此,我们只需考虑以k为根的子树。问题就变成了将新的item插入以k为根的子树的根节点(即k节点)。这和最先将最后一个元素插入根节点完全相同。

item大于k的子节点:把item插入k节点,插入方式与最开始将最后一个元素插入根节点一样。

通过上面的分析,item插入以k的父节点为根的堆中后,堆的最大元素仍然是k的父节点,所以父节点不变,因此,k的兄弟节点所在的路径或子堆也未违反最小-最大堆性质,不需改变,所以我们就只考虑将item插入以k为根的堆中的根节点(即k节点)即可。

总结起来就是三种情况:

a)

k处于第二层:

b)

k处于第三层且item大于k的父节点;

c)

k处于第三层且item大于k的子节点;

结合边界条件,我们很容易进行实现。

四、C语言实现

#include

#include

#define MAX_SIZE 100

#define FALSE 0

#define TRUE

1

#define SWAP(x,y,t)

((t)=(x),(x)=(y),(y)=(t))

typedef struct{

int key;

}element;

element heap[MAX_SIZE];

void min_max_insert(element heap[], int *n,

element item);

int level(int n);

void verify_min(element heap[], int n);

void verify_max(element heap[], int n);

element delete_min(element heap[], int

*n);

int find_min(element heap[] ,int n1,int

n2);

void show(element heap[],int n);

int main(void)

{

int n=0;

int i,j;

element

rr[23]={{7},{3},{70},{20},{40},{80},{30},{5},{9},{17},{10},{100},

{15},{3},{45},{0},{50},{2},{30},{88},{20},{99},{12}};

for(i=0;i<23;i++)

min_max_insert(heap,&n, arr[i]);

show(heap,n);

element temp;

for(i=0;i<23;i++)

{

temp=delete_min(heap,&n);

printf("the deleted element is %d\n",temp.key);

}

system("PAUSE");

return 0;

}

element delete_min(element heap[], int

*n)

{

heap[0]=heap[1];//heap[0]保存要删除的元素

if((*n)==1)

{

(*n)--;

return heap[0];

}

int parent,last,i,k;

element temp,st;

temp=heap[(*n)--];

for(i=1,last=(*n)/2;i<=last;)

{

k=find_min(heap,i,*n);

if(temp.key<=heap[k].key)

break;

heap[i]=heap[k];

if(k<=2*i+1)

{

i=k;

break;

}

parent=k/2;

if(heap[parent].key

SWAP(heap[parent],temp,st);

i=k;

}

heap[i]=temp;

return heap[0];

}

void show(element heap[],int n)

{

int j;

printf("The heap is:\n");

for(j=1;j<=n;j++)

printf("%d

",heap[j].key);

printf("\n");

}

int find_min(element heap[] ,int n1,int

n2)

//在堆中寻找n1节点的后代中最小的节点,注意,并不是heap中所有位于n1后面的节点都是n1节点的后代

{

if(n1>=n2)

{

fprintf(stderr,"n1 must be smaller than n2");

exit(1);

}

int i,j,k;

k=2*n1;

for(i=1,j=pow(2,i)*n1;j<=n2;)

{

if(heap[j].key

k=j;

if(j==pow(2,i)*(n1+1)-1)

{

i++;

j=pow(2,i)*n1;

}

else

j++;

}

return k;

}

void min_max_insert(element heap[], int *n,

element item)

{

(*n)++;

if (*n==MAX_SIZE)

{

fprintf(stderr,"The heap is full.\n");

exit(1);

}

int parent=(*n)/2; //父节点下标

element temp;

if (parent==0) //说明

*n=1,即当前插入的item是heap中的第一个元素

heap[1]=item;

else

{

heap[*n]=item; //先把item放在最后一个位置

switch(level(parent))

{

case FALSE:

if(heap[*n].key

{

SWAP(heap[*n],heap[parent],temp);

verify_min(heap,parent);

break;

}

else

{

verify_max(heap,*n);

break;

}

case TRUE:

if(heap[*n].key>heap[parent].key)

{

SWAP(heap[*n],heap[parent],temp);

verify_max(heap,parent);

}

else

verify_min(heap,*n);

default:break;

}

}

}

int level(int n)

{

int i=log2(n);

if(i%2)

//max

return

1;

return

0;

}

void verify_min(element heap[], int n)

{

if (n>1)

{

int parent=(n/2);

parent=parent/2;

if(parent)

if(heap[n].key

{

element temp;

SWAP(heap[n],heap[parent],temp);

verify_min(heap,parent);

}

}

}

void verify_max(element heap[], int n)

{

if (n>1)

{

int parent=(n/2);

parent=parent/2;//父节点的父节点

if(parent)

if(heap[n].key>heap[parent].key)

{

element temp;

SWAP(heap[n],heap[parent],temp);

verify_min(heap,parent);

}

}

}

运行结果:

0

99 100

2

10

15

3

40

30

50 88

80

70

30

45

5

7

3 9

20

20

17

12

the

deleted element is 0

the

deleted element is 2

the

deleted element is 3

the

deleted element is 3

the

deleted element is 5

the

deleted element is 7

the

deleted element is 9

the

deleted element is 10

the

deleted element is 12

the

deleted element is 15

the

deleted element is 17

the

deleted element is 20

the

deleted element is 20

the

deleted element is 30

the

deleted element is 30

the

deleted element is 40

the

deleted element is 45

the

deleted element is 50

the

deleted element is 70

the

deleted element is 80

the

deleted element is 88

the

deleted element is 99

the

deleted element is 100

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/308290.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

.NET Core + Spring Cloud:API 网关

API 网关是系统的唯一入口&#xff0c;调用任何服务的请求都需要经过网关层&#xff0c;最终才可能到达目标服务&#xff0c;既然是必经之路&#xff0c;那我们可以在网关层进行一些通用的操作&#xff0c;如&#xff1a;认证、鉴权、限流、智能路由、缓存、日志、监控、超时、…

单片机C语言中空语句,单片机C语言中的空语句.doc

头文件如&#xff1a;//#include //包含库函数............//............_nop_(); //引用库函数敬礼。我一直都是借助仿真软件编。一点一点试时间。C语言最大的缺点就是实时性差,我在网上到看了一些关于延时的讨论,其中有篇文章51单片机 Keil C 延时程序的简单研究,…

VS Code 黑宝书背后的故事

自开售以来&#xff0c;《Visual Studio Code 权威指南》就受到了许多读者朋友的青睐。在京东和当当两大平台上&#xff0c;都分别取得了不错的绩&#xff1a;当当&#xff1a;计算机新书热卖榜第一名京东&#xff1a;科技IT新书榜第一名那么&#xff0c;热销背后&#xff0c;这…

c语言迪思卡尔算法,【论文】数字化的意匠_数字化设计与造型的认知学反思_谭峥.pdf...

D IG ITA L B UILD ING 数字建筑数字化的意匠—数字化设计与造型的认知学反思谭峥摘要/ 通过论述数字化设计的构成要件与理论界的思考&#xff0c; 数字化的设计、造型与工艺教育在西 基础。重呈一般是指对建成环境的物理状力图对数字化建构文化进行设计认知学角度的反思&#…

ASP.net Core MVC项目给js文件添加版本号

需求&#xff1a;使用ASP.net Core Mvc开发公司内部web系统&#xff0c;给视图中js(css,image也可以)文件添加版本号避免缓存问题。解决方法&#xff1a;利用Taghelper提供的标签&#xff08;asp-append-version&#xff09;可以实现<script src"~/Scripts/Biz/Village…

c语言网格搜索,基于C

引言教室作为学生长期使用的建筑类型&#xff0c;对光环境舒适度的需求尤为明显。相关研究表明&#xff0c;不仅照明会影响学习效率[1]&#xff0c;而且不当照明会引起使用者不适甚至损害视力[2]。随着多媒体教学设施的普及&#xff0c;幻灯片投影教学现已成为教师授课的主要形…

初识ABP vNext(1):开篇计划基础知识

点击上方蓝字"小黑在哪里"关注我吧审计(Audit)本地化(Localization)事件总线(Event Bus)多租户(multi-tenancy technology)DDD分层实体(Entity)值对象(Value Object)聚合根(Aggregate Root)仓储(Repository)应用服务(Application Services)数据传输对象(DTO)工作单元…

android 那几种动画,Android 动画实现几种方案

Android 动画实现几种方案在 Android 的 FrameWork 中&#xff0c;为我们提供三种动画的实现方式&#xff1a;逐帧(Frame)动画、视图/补间动画(View Animation)和属性动画(Property Animation)。由于&#xff0c;这三种动画的实现方式和针对面不一样&#xff0c;应用的范围也有…

[开源] .Net ORM FreeSql 1.8.0-preview 最新动态播报

FreeSql 是 .NET 开源生态下的 ORM 轮子&#xff0c;在一些人眼里属于重复造轮子&#xff1a;不看也罢。就像昨天有位朋友截图某培训直播发给我看&#xff0c;内容为&#xff1a;“FreeSQL&#xff08;个人产品&#xff09;&#xff0c;自己玩可以&#xff0c;不要商用。ORM框架…

android socket 服务端,Android socket 服务端

Android socket 服务端Android想在Android 上跑一个 socket 服务端。把在java工程里运行起来的代码直接放到android项目里来&#xff0c;开启线程&#xff0c;创建ServerSocket对象&#xff0c;创建对象的时候报错了。W/System.err( 3998): java.net.SocketException: socket f…

懂程序员的产品经理是什么样子?

这里是Z哥的个人公众号每周五11&#xff1a;45 按时送达当然了&#xff0c;也会时不时加个餐&#xff5e;我的第「155」篇原创敬上大家好&#xff0c;我是Z哥。在互联网行业&#xff0c;产品经理和程序员之间的关系很微妙。表面看上去水火不容&#xff0c;在一方的眼里看另外一…

Jdbc连接mysql的五种连接方式

一&#xff1a;五种连接方式 直接上码 package com.wyjedu.jdbc;import com.mysql.jdbc.Driver;import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.sql.Connection; import java.sql.DriverManager; import ja…

转 android anr 分析示例,[摘]Android ANR日志分析指南之实例解析

前文《[摘]Android ANR日志分析指南》也摘抄了如何分析&#xff0c;接下来通过实例解析。一、主线程被其他线程lock&#xff0c;导致死锁waiting on <0x1cd570> (a android.os.MessageQueue)DALVIK THREADS:"main" prio5 tid3 TIMED_WAIT| group"main&quo…

455. 分发饼干001(贪心算法+详解)

一&#xff1a;题目 假设你是一位很棒的家长&#xff0c;想要给你的孩子们一些小饼干。但是&#xff0c;每个孩子最多只能给一块饼干。 对每个孩子 i&#xff0c;都有一个胃口值 g[i]&#xff0c;这是能让孩子们满足胃口的饼干的最小尺寸&#xff1b;并且每块饼干 j&#xff…

android system window,Android之属性fitsSystemWindows

页面有输入框时都要考虑会不会被键盘挡住&#xff0c;于是fitsSystemWindows属性就派上了用场属性说明fitsSystemWindows属性可以让view根据系统窗口来调整自己的布局&#xff1b;简单点说就是我们在设置应用布局时是否考虑系统窗口布局&#xff0c;这里系统窗口包括系统状态栏…

初识ABP vNext(2):ABP启动模板

点击上方蓝字"小黑在哪里"关注我吧AbpHelper模块安装前言上一篇介绍了ABP的一些基础知识&#xff0c;本篇继续介绍ABP的启动模板。使用ABP CLI命令就可以得到这个启动模板&#xff0c;其中包含了一些基础功能模块&#xff0c;你可以基于这个模板来快速开发。开始首先…

linux多行变一列,多行转为一列

--创建表ifexists (select*from sysobjects where idOBJECT_ID([TempTable_Base]) and OBJECTPROPERTY(id,IsUserTable) 1)DROP TABLE[TempTable_Base]CREATE TABLE [TempTable_Base] ([id] [int] IDENTITY (1,1)NOTNULL,[guid] [varchar] (50)NULL,[code] [varchar] (50)N…

关于Dapper实现读写分离的个人思考

概念相关为了确保多线上环境数据库的稳定性和可用性&#xff0c;大部分情况下都使用了双机热备的技术。一般是一个主库一个从库或者多个从库的结构&#xff0c;从库的数据来自于主库的同步。在此基础上我们可以通过数据库反向代理工具或者使用程序的方式实现读写分离&#xff0…

135. 分发糖果002(贪心算法+思路+详解)

一&#xff1a;题目 老师想给孩子们分发糖果&#xff0c;有 N 个孩子站成了一条直线&#xff0c;老师会根据每个孩子的表现&#xff0c;预先给他们评分。 你需要按照以下要求&#xff0c;帮助老师给这些孩子分发糖果&#xff1a; 每个孩子至少分配到 1 个糖果。 评分更高的孩…

Enumerable 下又有新的扩展方法啦,快来一睹为快吧

一&#xff1a;背景1. 讲故事前段时间将公司的一个项目从 4.5 升级到了 framework 4.8 &#xff0c;编码的时候发现 Enumerable 中多了三个扩展方法&#xff1a; Append, Prepend, ToHashSet&#xff0c;想必玩过jquery的朋友一眼就能看出这三个方法的用途&#xff0c;这篇就和…