前言
最近在写数据结构的王道考研书,复习到了链表,写了一些练习题,发现了关于找出链表中的最小值节点有两种方法,而至于是单链表还是循环单链表,只不过是更改了循环的条件。
类似的,也可以得出“依次找出链表的最小值,打印并删除,释放空间”这种题目也是一样,不过是内外层循环的条件更改即可。
所以,觉得有必要进行总结。
1. 题目总汇一(一层循环)
- 试编写在带头结点的单链表L中删除一个最小值节点的高效算法(假设最小值节点是唯一的)
- 试编写在带头结点的单循环链表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. 题目总汇二(两层循环)
- 给定一个带头结点的单链表,设head为头指针,结点结构为(data,next),data是整型元素,next为指针,试写出算法:按递增次序输出单链表中各节点的数据元素,并释放结点所占的存储空间(不允许使用数组作为辅助空间)
- 设有一个带有头结点的单循环链表,其结点均为正整数。设计一个算法,反复找出表中结点值最小的节点并输出,然后将该节点删除,直到单链表为空为止,再删除表头结点。
上述两个题目,核心其实在于:
反复找出表中结点值最小的节点并输出
,而之前的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);
}