总结:删除单链表/单循环链表中的最小值节点的两种解法(并延伸至:依次找出链表的最小值,打印并删除,释放空间)

前言

最近在写数据结构的王道考研书,复习到了链表,写了一些练习题,发现了关于找出链表中的最小值节点有两种方法,而至于是单链表还是循环单链表,只不过是更改了循环的条件。

类似的,也可以得出“依次找出链表的最小值,打印并删除,释放空间”这种题目也是一样,不过是内外层循环的条件更改即可。

所以,觉得有必要进行总结。

1. 题目总汇一(一层循环)

  1. 试编写在带头结点的单链表L中删除一个最小值节点的高效算法(假设最小值节点是唯一的)
  2. 试编写在带头结点的单循环链表L中删除一个最小值节点的高效算法(假设最小值节点是唯一的)

上面这两个题目,只是单链表的类型不同,其他一致。由题目可以看出,只需要找一次最小值,所以一层while循环即可。

方法一

(先以普通单链表为例)

算法思想:用p从头至尾扫描单链表,pre指向p节点的前驱,用minp保存值最小的结点指针(初始为p),minpre指向minp结点的前驱(初始为pre)。一边扫描,一边比较,若p->data小于minp->data,则将p、pre分别赋值给minp、minpre。当p扫描完毕时,minp指向最小值结点,minpre指向最小值节点的前驱结点,再将minp所指节点删除即可。

代码如下:

LinkList Delete_min(LinkList &L){
	// L是带头结点的单链表
	LNode *pre = L,*p = pre->next;// p为工作指针,pre指向其前驱 
	LNode *minpre = pre,*minp = minpre->next; // 保存最小值结点以及其前驱
	while(p!=NULL){
		if(p->data<minp->data){
			minp = p;  //找到比之前找到的最小值结点更小的结点 
			minpre = pre;
		}
		pre = p; // 继续扫描下一个结点 
		p = p->next;
	} 
	minpre->next = minp->next;// 删除最小值结点 
	free(minp);
	return L; 
}

那么如果把单链表换成循环单链表呢?

其实,单链表和循环单链表区别仅在于:单链表的最后一个节点的next指向NULL,而循环单链表的最后一个节点的next指向头结点L。

因此,只需要把循环条件改变即可,其他不变。

在这里插入图片描述

方法二

算法思路:设置*p,*pre,p不断向后移动。pre是p的前驱,用来记住最小值结点的前驱。每一次的比较都是p->next和pre->next进行比较,当p->next 小于pre->next,就令pre = p(当找到了最小值结点,那么pre就是其前驱)。

代码如下:

LinkList Delete_min(LinkList &L){
	// L是带头结点的单链表
	LNode *pre = L,*p = pre->next;// p为工作指针,pre指向其前驱 
	LNode *u;
	while(p->next!=NULL){ // 注意这里的条件变成了 p->next!=NULL
		if(p->next->data<pre->next->data){
			pre = p; // 记住当前最小值的前驱 (p->next是最小值结点) 
		}
		p = p->next;
	} 
	u = pre->next;// 这是最小值结点
	pre->next = u->next;// 删除最小值结点
	free(u);// 释放空间 
	return L; 
}

换成循环单链表,仍然只需要更改循环条件。(单链表的最后一个节点的next指向NULL,而循环单链表的最后一个节点的next指向头结点L。)

在这里插入图片描述

2. 题目总汇二(两层循环)

  1. 给定一个带头结点的单链表,设head为头指针,结点结构为(data,next),data是整型元素,next为指针,试写出算法:按递增次序输出单链表中各节点的数据元素,并释放结点所占的存储空间(不允许使用数组作为辅助空间)
  2. 设有一个带有头结点的单循环链表,其结点均为正整数。设计一个算法,反复找出表中结点值最小的节点并输出,然后将该节点删除,直到单链表为空为止,再删除表头结点。

上述两个题目,核心其实在于:反复找出表中结点值最小的节点并输出,而之前的1,2题是只要找一次就可以了。其实仔细想一下,要反复找出,其实就是需要两层循环的。

而内层循环不就是 题目总汇一 中的代码吗?因为内层循环就是遍历链表找出最小值,只是在延伸拓展的题目里面,每当找出一次最小值并删除,链表就会改变,然后要再找一次最小值并删除,又会改变,所以需要外层循环来控制:直到链表为空时,才不会对链表进行遍历

方法一
void Delete_min(LinkList &L){
	// L是带头结点的单链表
	LNode *p,*pre,*minp,*minpre;
	while(L->next!=NULL){
		p = L->next; pre = L;
		minp = p;	minpre = pre;
		
		while(p!=NULL){
		if(p->data<minp->data){
			minp = p;  //找到比之前找到的最小值结点更小的结点 
			minpre = pre;
		}
		pre = p; // 继续扫描下一个结点 
		p = p->next;
		} 
		
		print(minp->data)// 输出最小值结点元素 
		minpre->next = minp->next;// 删除最小值结点 
		free(minp);
	}
		free(L);
}

纠正:下图中,函数返回值是void。
在这里插入图片描述

方法二

同理,只是将 L 换成 head 即可。

void Delete_min(LinkList &head){
	// L是带头结点的单链表
	LNode *pre,*p ,*u;
		while(head->next!=NULL){
			
			pre = head;
			p = pre->next;
			while(p->next!=NULL){ // 注意这里的条件变成了 p->next!=NULL
				if(p->next->data<pre->next->data){
					pre = p; // 记住当前最小值的前驱 (p->next是最小值结点) 
				}
			p = p->next;
			} 
			print(pre->next->data);// 输出最小值结点的数据 
			u = pre->next;// 这是最小值结点
			pre->next = u->next;// 删除最小值结点
			free(u);// 释放空间 
		}
	free(head);
}

在这里插入图片描述

  • 15
    点赞
  • 76
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值