循环单链表逐个删除最小结点,直到表为空,在删除表头结点

《2014年 数据结构联考复习指导》第二章,第二大题,第19小题:

题目:设带有一个头结点的循环单链表,其结点均为正整数。设计一个算法,反复找出单链表中结点值最小的结点并输出,然后将该结点从中删除,直到单链表空为止,再删除表头结点。


我这里主要是给出代码的解释,,和如何在编程环境下看到效果。(下面纯属个人理解,如有错误,恳请指正)

代码如下:

void Del_All(LinkList &L)
{
	LNode *p,*pre,*minp,*minpre;
	while(L->next!=L)
	{
		p=L->next;//p指向当前的结点
		pre=L;//pre指向当前结点的前一个结点
		minp=p;//minp为要删除的最小值的结点
		minpre=pre;//minpre为要删除结点的前一个结点
		while(p!=L)
		{
			if(p->data<minp->data)
			{
				minp=p;
				minpre=pre;
			}
			pre=p;
			p=p->next;
		}
		
		printf("%d ",minp->data);
		minpre->next=minp->next;//从链表中删除结点
		free(minp);//销毁结点
	}
	free(L);
	
}


由于之前写好了,本想在后面补充添加效果的,但是不小心将之前分析给删除了。这里简单的讲解一下:

1、我们学习数据结构时一般对于循环单链表,都是头指针指向尾结点的。但是在咱们上面的算法是采用头指针指向头结点的。即代码中的L指向链表的头结点。为什么代码会定义头指针指向头结点而不是尾结点呢?主要是因为,如果我们定义头指针指向尾结点,由于尾结点包含有数据,即假如当在某一次遍历中尾结点包含的数据是最小值,那么我们就会将尾结点删除,然后修改头指针指向新的尾结点,这样就无缘无故的增加的算法的复杂度,如果我们我们定义头指针指向头结点则不存在这样的问题,我们不需要处理头结点中的数据,因为我们一般认为头结点是没有数据的结点。

2、上述代码传递的是函数的引用,其实对于上述代码传递引用和非引用没有本质的区别,因为我们处理的是指针所指向的数据,而不是指针变量本身。

3、那我们会想如何在链表定义为头指针指向尾结点的工程中,利用上述的算法呢?我们或许会想,直接把L->next传递给上述函数不就可以了吗?事实并非如此,如果我们传递L->next 给上述函数,由于是引用类型,我们先举例链表为:5 2 10 4 8 1,我们第一次遍历找到最小值为1,那么我们要删除它,而这个点是外围(调用该函数的地方)L头指针指向的对象,那么就会销毁L所指向的这个对象,那么当我们再次调用while(L->next!=NULL),此时的外围的L为悬垂指针,而我们传入的引用&L->next就无法解析,即函数内部L是一个无效的指针,则会报错。对于这个算法,我们直接传递非引用类型就可以了,此时传递给为一个副本。这个过程也告诉了我们,如果我们传递一个引用类型,该类型为另外一个对象的子对象,就如上面的,我们传入&L->next(前面加上&只是表示引用,并非取地址),L->next为由L引申出来的子对象,如果我们在函数处理过程中将L销毁,那么L->next就是一个无定义的对象,那么后面的对其操作就会报错。


代码如下(头文包含的头文件代码都是严蔚敏老师的数据解构书中的代码,随处可以下载到):

//为了显示效果,我们程序并没有销毁头结点。如果销毁了,我们就不能通过链表的相关操作进行访问了,而只能得到指针的值了。
#include"c1.h"
typedef int ElemType;
#include"c2-2.h"
#include"bo2-4.hpp"

void Del_All(LinkList &L2)
{
	LNode *L=L2->next;
	LNode *p,*pre,*minp,*minpre;
	while(L->next!=L)
	{
		p=L->next;
		pre=L;
		minp=p;
		minpre=pre;
		while(p!=L)
		{
			if(p->data<minp->data)
			{
				minp=p;
				minpre=pre;
			}
			pre=p;
			p=p->next;
		}
		
		printf("%d ",minp->data);
		minpre->next=minp->next;
		free(minp);
	}
	//free(L);
	L2=L;
	
}

void visit(ElemType c)
{
	printf("%d ",c);
}

void main()
{
	LinkList L;
	Status s;
	s=InitList_CL(L); // 初始化单循环链表L
	
	const int NUM=6;
	const int val[NUM]={5,2,10,4,8,1};
	
	for(int i=0;i<NUM;i++)
	{
		ListInsert_CL(L,i+1,val[i]);
	}
	
	cout<<"链表如下:"<<endl;
	ListTraverse_CL(L,visit);
	cout<<"链表的长度:"<< ListLength_CL(L)<<endl;
	cout<<"每次从链表中删除最小的,并将其逐个打印出来: "<<endl;
	Del_All(L);
	s=ListEmpty_CL(L);
	cout<<endl;
	cout<<"删除后链表的长度: "<< ListLength_CL(L)<<endl;
	
	
	
}


执行效果:







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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值