数据结构之链表——学习笔记(2)(包含循环链表的基本知识与运用)

例题:n个学生的成绩

代码部分如下:

#include<iostream>
#include<algorithm>
using namespace std;
int n;
struct Node{
    int v;
	Node *next;	
}*head,*tail,a[1001];
int main(){
	cin>>n;
	head=tail=NULL;
	for(int i=1;i<=n;i++){
	cin>>a[i].v;
	if(head==NULL)
	head=tail=&a[i];
    else{
	tail->next=&a[i];
	tail=&a[i];
	}
	}
    Node *x=head,*y=x->next;
    while(y){
    	Node*z=y->next;
    	y->next=x;
    	x=y;y=z;
	}
	swap(head,tail);
	tail->next=NULL;
    for(Node *p=head;p;p=p->next)
	cout<<p->v<<endl;
	return 0;
}

对于这一段代码,我们根据题目要求先定义了学生人数然后用结构体来存储分数并定义一个next指针指向下个单位的结构体,并随后定义了头部,尾部以及最大容量加一的结构体数组a,主函数的输入部分都不需要太多的赘述,我想要强调的是在第一个大的for循环结束之后,我们就要考虑如何达到题目的要求,其实也就是需要将输入的链表的原本箭头换一个方向,也就是说一开始的第一个元素指向第二个元素应该变成第二个指向第一个,交换原来的头部和尾部中间的元素指向也都交换一个方向,所以我们引入了指针变量x,y,z当y非空的时候,我们就不断更新链表,来达到链表反向的操作,最后将原本的头部和尾部进行交换,然后将尾部的下一个设置为空,并利用for循环挨着输出新的链表中的元素,就完成了本题。

接下来是双向链表的基础知识讲解:

 实际上它和单向链表是非常的相似的,只是对于链表中的元素,我们不仅仅要记录它的下一个元素是什么,还需要记录它的上一个元素,接下来我会介绍双向链表的插入操作:

 

但是我们需要思考一个问题,就是如何在头部和尾部进行插入节点的操作。

1.如何在head的前面插入节点now: 首先我们将now的下一个(next)设置成head,然后将head的上一个(prev)设置成now,最后将head令为now即可。

2.如何在尾部的后面添加节点now:首先将tail的下一个设置成为now,然后将now的上一个设置成tail,最后设tail为now。

还有双向链表的删除操作:

 

 这时候我们同样需要思考当需要删除的now是head或者tail的时候,应该怎么去处理:

1.如果要删除头部,那么我们首先令q为head的next,然后令q的prev为NULL,并令q为head。

2.如果要删除尾部,那么我们直接令q为tail的prev,然后令q的next为NULL,并令q为tail。

3.如果删除now后链表为空,我们直接令head=tail=NULL即可。

接下来简单介绍一种特殊的链表:循环链表,下面是它的基本概念:

 实际上循环链表里面是没有头,尾的概念的,只是我们需要head和tail来引入节点,当引入完成之后,那么每一个节点都是完全一样的,所以说呢这个时候就没有头和尾这个说法了,因为这个时候链表是一个圈,这个圈上面有一个个节点上面分别含有一个个的元素。

不妨来看一道例题方便进行理解:
约瑟夫问题:

 代码实现如下:

#include<iostream>
using namespace std;
int n,m;
struct Node{
  int v;
  Node *next;
  Node *prev;	
}a[101];
int main(){
   cin>>n>>m;
   for(int i=1;i<=n;i++){
   	a[i].v=i;
   	if(i==n)
   	   a[i].next=&a[1];
   	else
   	   a[i].next=&a[i+1];
   	if(i==1)
   	   a[i].prev=&a[n];
   	else 
   	   a[i].prev=&a[i-1];
   }
   Node *p=&a[n];
   int x=0;
   while(1){
   	p=p->next;
   	++x;
   	if(x==m){
   		cout<<p->v<<' ';
   		if(p->next==p)
   		break;
   		else{
   			Node *l=p->prev,*r=p->next;
   			l->next=r;
   			r->prev=l;
   			p->prev=p->next=NULL;
   			x=0;
   			p=l;
		   }
	   }
   }
   return 0; 
}

题目难度或许并不大,只是这里有几个细节或许需要强调一下:由于我们是使用循环链表的做法这时候我们并没有使用head或者tail,而是只定义了结构体数组,同时利用for循环读入每一个的时候,记得记录最后读入的下一个节点是第一个读入,第一个读入的上一个节点是最后一个读入,另外我们用while循环,当剩余人数不为1的时候,不断进行while循环,如果出列之和链表变成空直接break,否则就引入指针变量l,r来对应这时候即将出列的节点p对应的人的编号,同时更新l的下一个是r,r的上一个是l,并将出列的人对应的节点删除,同时为了进行下一个while操作,p需令成l。

以上便是这一次的全部内容。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

残念亦需沉淀

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

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

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

打赏作者

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

抵扣说明:

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

余额充值