链表结构实现

通过一个含有自身结构的指针,我们可以实现随机分布的结构变量的遍历

对于以下结构

struct list
{
	char name[20];
	list * pn;
};

name成员含有结构中的某些信息,pn成员是指向另一个list的指针,这种结点(结构的实例)通过每个list的pn成员链接起来,能用于构造任意长的结构链,这样的结构链即被称为链表。链表中的每一个list结构变量称为结点

链表中的第一个list结点通常由一个指向list的指针引导,这个结构指针(称为链首指针)不是自身结构成员,但它指向第一个结点,而该结点的pn成员又指向下一个结点,以此类推,直至最后一个结点,最后一个结点的pn成员值为空(NULL)

 下面介绍链表的创建与遍历的代码实现

struct student
{
	int num;//结点包含的信息
	float score;//结点包含的信息
	student* next;//指向下一结点的指针
};

student* head;//链首指针

student* getnode()//创建结点
{
	int num;
	float score;
	cin>>num>>score;//输入结点信息
	if(!num)//终止创建 条件
	{
		return NULL;
	}
	student *p=new student;//创建
	p->num=num;//导入数据
	p->score=score;//导入数据
	p->next=0;
	return p;//返回该结点
}

void creat()
{
	head=getnode();//先传入头结点
	if((head)==0)
	{
		return ;
	}
	for(student *pe=head,*ps;ps=getnode();pe=ps)//同时创建pe、ps结构指针,pe用来指向上一个结点,ps用来指向下一个结点
	{
		pe->next=ps;//链接
	}
}

void showlist()//遍历该链表
{
	cout<<"here is the list\n";//注意这里不能直接用head去遍历,不然会丢失头结点
	for(student*p=head;p;p=p->next)//循环终止条件是p,即p是NULL的时候
	{
		cout<<p->num<<' '<<p->score<<endl;
	}
}
int main()
{
	cout<<fixed<<setprecision(1);
	creat();
	showlist();
	return 0;
}

 

 该图清晰地展现了链表创建的过程,可帮助理解链表

        下面介绍链表的基本操作

1.删除链表结点

我们的操作应该想上面这个图一样,这样就不会因删除而使链表链接中断

删除往往还包含查找子过程,因为首先要找到想要删除的结点(注意还包含找不到的处理)

例如:下面的代码实现了删除学号num的结点:

void deletenode(int target)
{
	if(!head)//链表为空
	{
		cout<<"List null"<<endl;
		return ;
	}
	student* pguard=head;
	if(pguard->num==target)//删除链首结点的情况单独讨论
	{
		head=head->next;
		delete pguard;
		cout<<target<<"The head of node has been deleted"<<endl;
		return ;
	}
    //从头结点的下一结点开始遍历
	for(student* p=pguard->next;p;pguard=p,p=p->next)//注意这里在循环结束之后先更新pguard再更新p
	{
		if(p->num==target)
		{
			pguard->next=p->next;//将待删除结点的上一结点与下一结点直接相连
			delete p;//记得delete
			cout<<target<<"The head of node has been deleted"<<endl;			
			return ;
		}
	}
	cout<<"NO find it!"<<endl;//遍历之后没找到
}

 注意上面删除链首结点的步骤为:

  1. p指向链首结点;
  2. head指向链首结点的下一结点;
  3. 删除p指向的结点

这里的顺序是很重要的,如果p不指向链首结点,则当head指向链首结点的下一结点后,原链首结点脱链,导致结点地址丢失,以致无法释放堆空间        如果先删除链首结点,head指针无法指向由链首结点导出的下一个结点地址

其次,当delete()函数找到要删除的结点时,删除的步骤为:

  1. p指向待删除的结点;
  2. pguard所指向结点的next成员指向待删结点的下一个结点;
  3. 删除p指向的结点

在找到待删结点时,pguard指向待删结点的下一个结点是很重要的。否则,待删结点的前一结点地址丢失,其next成员无法与待删结点的后一结点链接

2.插入链表结点 

请看图

         插入操作也包含一个插入位置查找的子过程,同样面临链表是空表或插入到链首的特殊情况(需要单独讨论)

        假设创建(调用create())链表的时候,结点是按照num的值由小到大顺序排列,则插入函数设计如下:

void insertnode(student& data)
{
	student *ps=new student;//链结点统一由堆空间结点构成
	*ps=data;
	if(!head||ps->num<head->num)//链表为空或者插在链首(二者操作一样)
	{
		ps->next=head;
		head=ps;
		delete ps;
	}
	student* pguard=head;
	for(student *p=pguard->next;p;pguard=p,p=p->next)//欲插在pguard与p中间
	{
		if(p->num>ps->num)
		{
			ps->next=p;
			pguard->next=ps;//结合上图理解	
			return ;
		}
	}
	pguard->next=ps;//直到链尾还未插入,则插在最后
	ps->next=NULL;
}

值得注意的是insertnode()函数的参数来自主函数实参的引用,数据属于栈空间,不能作为链表的结点,所以函数一上来就申请堆空间,复制函数参数到ps

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ZZZWWWFFF_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值