链表的简单入门

链表的简单入门

寒假刚刚把链表的题目刷完,感觉对链表有了一点认识,分享给大家,希望大家看了我的博文后都能有所进步,有所收获,另外有误的地方欢迎大家来指正。

链表是一种常见的数据结构。当使用数组存放数据时,要先指定数组中包含元素的个数,即数组的长度。当数据超过了数组的大小时,便不能够将内容完全保存。如果数据远小于数组的长度,用数组申请空间的方式就非常浪费空间。
所以就希望有一种储存方式,其元素的个数不受限定当添加新的元素时,储存的个数也会随之改变,这种存储方式就是链表。
下图为结构体的示意图
在这里插入图片描述

  • 链表的建立
    要建立一个链表,首先要有一个头指针,头指针中一般只存放着一个地址(当然头指针中也可以存放数据,在接触循环链表时会用到),该地址为一个变量的地址,也就是说头指针指向一个变量,这个变量称为元素。链表中每一个元素包括数据部分和指针部分。数据部分用来存放元素所包含的数据,而指针部分用来指向下一个元素。最后一个元素的指针指向NULL,表示指向的地址为空。

  • 有关函数
    如malloc函数
    void *malloc(unsigned int size)
    该函数的功能是动态的分配一块size大小的内存空间,错误时返回NULL。
    void *calloc(unsigned int size)
    该函数的功能是动态的分配n个长度为size的内存空间,错误时返回NULL。
    void free(void *ptr)
    该函数的功能是能使用由指针ptr指向的内存区,使部分内存区能够被其他变量使用,ptr是最近一次调用calloc或malloc函数返回的值。free函数没有返回值。
    下面由一个题来讲解函数的使用方法,以及链表的建立,和链表的输出

                                   顺序建立链表
    

SDUT程序二题目

#include <iostream>
#include <malloc.h>

using namespace std;

struct node
{
	int data;
    node *next;
};

struct node * create(int n){
	node *head=(struct node*)malloc(sizeof(struct node)) ;  //前面一个括号是强制类型转换,后面一个括号是要分配的空间大小
	//我个人比较喜欢c++的申请内存空间建立节点的方式   node *head= new node; 
	node *tail=(struct node*)malloc(sizeof(struct node));
	head->next=NULL;   //让新建的头指针指向空 
	tail=head;         //建立一个辅助指针便于访问新建立元素前一个元素的位置 
	for(int i=0;i<n;i++){
		node* p=(struct node*)malloc(sizeof(struct node));
		scanf("%d",&p->data);
		p->next=NULL;     //新建立节点指向空(顺序建立链表) 
		tail->next=p;     //让新建立的元素与前一个元素相连 
		tail=p;           //辅助指针的移动 
	}
	return head;
}

void show(node *head)
{
	node *p=head->next;
	while(p!=NULL){
		if(p->next==NULL) printf("%d\n",p->data);  //此处注意空格 搞不好会Pe 
		else  printf("%d ",p->data);
		p=p->next;
	}
}

int main()
{
	int n;
	cin>>n;
	node * head=create(n);
	show(head);
	return 0; 
 } 

逆序建立链表与顺序建立链表相似,逆序建立链表的新元素在head的后边插入,顺序建立链表新元素放在旧元素之后。 再给出一段代码注意区别。

struct node *create(int n)
{
	node *head=new node;
	node tail;
	head->next=NULL;
	tail=head;
	for(int i=0;i<n;i++){
		node* p=new node;
		scanf("%d",&p->data);
		p->next=tail->next;  //注意此处tail=head不移动
		tail->next=p;   
	}
	return head;
 } 

链表的建立和输出基本的东西就这些。

  • 链表中元素的删除
    链表可以对其中的一个元素进行删除操作,具体的步骤如下:
    1 让要删除元素前一个元素指向要删除元素的后一个元素
    2 把要删除的元素释放(free)掉
    注意:因为要借助被删除元素的前一个元素,所以需要一个辅助指针
    下面根据一个题目来进一步认识链表元素的删除

                         链表指定元素的删除
    

SDUT程序二题目

#include <iostream>
#include <malloc.h>
using namespace std;

struct node {
	int data ;
	node *next;
}; 

node *create(int n){
	node *head=new node;
	node *tail;
	head->next=NULL;
	tail=head;
	for(int i=0;i<n;i++){
		node *p=new node;
		scanf("%d",&p->data);
		p->next=NULL;
		tail->next=p;
		tail=p;
	}
	return head;
}

void show(node *head)
{
	node *p=head->next;
	while(p!=NULL){
		if(p->next==NULL) printf("%d\n",p->data);
		else printf("%d ",p->data);
		p=p->next;
	}
}

node *cut(int n,int m,node *head){
	node *pre,*p,*q;
	pre=head;
	q=head->next;
	while(q!=NULL)
	{
		if(q->data==m)
		{
			pre->next=q->next;  //此处注意辅助指针的位置向后移动了一位,所以下一步只需要移动另一个指针
			free(q);   //释放空间显得严谨,同时防止未知错误的发生
			q=q->next;
			n--;
		}
		else{
		    pre=pre->next;
		    q=q->next;
		}
	}
	cout<<n<<endl;
	return head;
	
}
int main()
{
	int n;
	cin>>n;
	node *head;
	head=create(n);
	cout<<n<<endl; 
	show(head);
	int m;
	cin>>m;
	head=cut(n,m,head);
	show(head);
	return 0;
}
  • 链表元素的插入
    理解了链表以后,链表元素的插入就变的轻松起来
    链表元素的插入步骤如下:
    1 断开原来的联系
    2 前一个元素指向新元素
    3 新元素指向后一个元素
    下面给出一道题目来进一步认识链表元素的插入:

                              师--链表的节点插入
    

SDUT程序二题目

#include <iostream>

using namespace std;

struct node {
	int data;
	node *pnext;
};

void Insert(node* head, int m,int data1)
{
	node *p=head;
     while(m--&&p->pnext!=NULL)
     {
     	p=p->pnext;
	 }
	 node *ptr=new node;
	 ptr->data=data1;
	 ptr->pnext=p->pnext;   //体现了节点的插入
	 p->pnext=ptr;
}

void show(node *head)
{
	node *pe=head->pnext;
	while(pe!=NULL)
	{
		printf("%d ",pe->data);
		pe=pe->pnext;
	}
}

int main()
{
	int m,x;
	int n; 
	while(cin>>n){
		node *head=new node;
		head->pnext=NULL;
	    for(int i=1;i<=n;i++){
	    	scanf("%d%d",&m,&x);
	    	Insert(head,m,x);
		}show(head);
	}
	
	return 0;
}
  • 双向链表
    双向链表与链表相似,只不过双向链表有两个指着front和next分别指向前一个元素和后一个元素,但要注意边界时front或next指向空的情况,使用双向链表让链表的删除操作变得更简单,不用辅助指针就可以实现。
    给出一个题目来了解双向链表:

                                 双向链表
    

SDUT程序二题目

#include <iostream>

using namespace std;

struct node{
	int data;
	node* front,*back;
};

node *creat(int n)
{
	node* head=new node;
	node * tail;
	head->front=NULL;   //注意此处的初始化
	head->back=NULL;
	tail=head;
	for(int i=0;i<n;i++){
		node * p=new node;
		scanf("%d",&p->data);
		p->back=NULL;
		tail->back=p;
		p->front=tail;
		tail=p;
	}
	return head;
}

void search(int x,node *head){
	 node *p=head->back;
        while(p->data!=x)
            p=p->back;
        if(p->front==head)
            printf("%d\n",p->back->data);
        else if(p->back==NULL)
            printf("%d\n",p->front->data);
        else
            printf("%d %d\n",p->front->data,p->back->data);
}

int main(){
	int n,m;
	int x;
	cin>>n>>m;
	node* head=creat(n);
	for(int i=0;i<m;i++)
	{
	cin>>x;
		 search(x,head);
		}
	return 0;
}
  • 循环链表
    循环链表和单向链表相似,不同点是循环链表的最后一个元素指向head,还有一个注意点是循环链表的头指针到底要不要储存元素,两者都可,只不过不储存数据的时候需要另外判断好几个地方,所以头指针储存数据就显得方便,我个人喜欢用*我懒* 。
    下面两个题目可以更好的理解循环链表

                                 约瑟夫问题
    

SDUT程序二题目

#include <iostream>
#include <malloc.h>

using namespace std;

struct node {
	int data;
	node *next;
}; 

node * create(int n)
{
	node *head=new node ;
	node *tail;
	head->data=1;
	head->next=NULL;
	tail=head;
	for(int i=2;i<=n;i++)
	{
		node *p=new node;
		p->data=i;
		p->next=NULL;
		tail->next=p;
		tail=p;
	 } 
	 tail->next=head;
	 return head;
 } 
 
int main()
{
	int n,m;
	cin>>n>>m;
	node *head=create(n);
	node *q,*p;
	q=head;
	while(q->next!=head)
	q=q->next;
	int con=0;
	while(q->next!=q)
	{
		p=q->next;
		con++;
		if(con==m)
		{
			q->next=p->next;
			free(p);
			con=0;
		}
		else q=p;
		
	}
	printf("%d",q->data);
	return 0;
}
                          不敢死队问题

SDUT程序二题目

#include <iostream>
#include <malloc.h>
using namespace std;

struct node{
	int data;
	node *next;
};

node * create(int n)
{
	node *head=new node;
	node *tail;
	head->next=NULL;
	head->data=1;
	tail=head;
	for(int i=2;i<=n;i++)
	{
		node *p=new node;
		p->data=i;
		p->next=NULL;
		tail->next=p;
		tail=p;
	}
	tail->next=head;
	return head;
}

int main()
{
	int n;

	while(~scanf("%d",&n)&&n){
		node *head;
	    node *p,*q;
	    head=create(n);
		q=head;
		while(q->next!=head)
		q=q->next;
		int con=0,ans=0;
		while(q->next!=q)
		{
			p=q->next;
			con++;
			if(con==5){
				if(p->data==1)
				break;
				q->next=p->next;
				free(p);
				con=0;
				ans++;
			}
			else q=p;
		}
		printf("%d\n",ans+1);
	}
	return 0;
}
  • 最后来说一下数组与链表的优缺点:

1 数组是顺序排列的,访问第一个地址的索引和访问最后一个地址的索引,消耗的时间基本所差无几,很快;而链表地址却不是相连的,所以效率不如数组。
2 数组的插入和删除很麻烦,所以引入了链表;

  • 结语
    这是本人写的第一篇博文,有很多不足的地方,随着以后发博文的数量增多相信我的博文能够更加的完善,如果能够帮助到大家,我会感到非常的荣幸。
    还有剩余的链表的题目,我会再后续发博文。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值