一文带你搞懂什么是链表

鲜衣怒马少年时,不负韶华行且知

链表

线性表基本概念

链表属于线性表的一种。

而线性表有两种表示方式,一种是顺序表示,另一种是链式表示。

  • 顺序表示:

    • 概念:指的是用一组地址连续的存储单元一次存储线性表的数据元素。

    • 这个很好理解,主要凸显了这么几个字 地址连续 ,一次存储

    • 也就是说元素在计算机内"物理地址"上是相邻的,我们只需要知道第一个元素的地址,线性表上的任意一个元素都可以随机存取

    • 虽然随机存取很方便,但是缺点也油然而生,当我们需要增加或者删除元素的时候,就需要在末尾进行增加删除。如果我们在中间进行插入或者删除,那么插入或者删除的位置之后的元素的地址都需要改变(因为物理地址是连续的)。这样是非常麻烦的。

    • 于是链式表示(链表)变出现了

    •  

  • 链式表示:

    • 概念:是一组任意的存储单元存储线性表上的数据元素(这组存储单元可以是连续的,也可以是不连续的)

    • 这便解决了增加和删除麻烦的问题(因为可以不连续)。但是伴随着的就是存取不方便。

 

那么有的小伙伴该产生疑问了:

链表的数据元素的地址可以不是连续的,那么如何访问这些元素呢?

  • 在顺序表示的情况下,我们只需要知道第一个数据元素的地址,便可依次读出所有的数据元素

  • 但是在链式情况下,这种方法就不适用了。因此我们就需要两部分信息,一部分是数据元素本身,而另一部分是指向下一个元素的地址。由这两部分组成的一个整体,我们称之为结点。链表就是一个个结点构成的。而其中用来存储数据元素信息的域称为数据域,用来存储直接后继存储位置的域称为指针域

什么是单链表?循环链表?双向链表?

  • 单链表:每个结点中只包含一个指针域。

  • 双向链表:含有两个指针域,其一指向直接后继,另一指向直接前驱。

  • 循环链表:每个结点只包含一个指针域,但是表中最后一个结点的指针域指向头结点,整个链表形成一个环。

代码实现

代码实现是对链表整体上的一个实现,中间包含了链表的初始化,销毁链表,清空链表,返回链表长度,指定元素的位置,指定位置的元素,求某元素的前驱和后继等等操作。

直接复制代码粘贴即可运行,一步到位。

代码为原创,其中一些思路或者细节,如果发现错误或者逻辑问题的话,欢迎在评论区留言指正~

#include<iostream>
#include<stdio.h>
#include<stdlib.h>
using namespace std;

#define OK 1
#define error 0 

typedef int ElemType;
typedef int Status;

//声明节点
typedef struct Node{
	ElemType data;//数据域
	struct Node *next;	//指针域
}Node;

//初始化,创建一个空链表
Node* ListInitiate(){
	Node *head=(Node *)malloc(sizeof(Node));//生成头结点内存空间
	head->next=NULL;//头结点指向地址为空
	return head;//返回头结点
} 

//销毁链表
Status DestoryLinkList(Node *head){
	Node *p;//定义一个指针对链表进行遍历
	while(head!=NULL){
		p=head;//遍历
		head=head->next;
		free(p);//释放内存空间
	}
	return OK;
}

//清空链表
Status ClearList(Node *head){
	head->next=NULL;//直接将头结点指针指向空,即清空
	return OK;
}

//链表长度
Status Length(Node *head){
	Node *cur=head;//定义一个指针指向头结点地址,用来遍历
	int m=0;
	if(head->next==NULL){//如果链表为空,返回error
		return error;
	}
	//遍历 
	int sum=0;
	while(true){
		//到最后一个元素 
		if(cur->next==NULL){
			break;
		}
		cur=cur->next;
		sum++;
	}
	return sum;
}

//指定位置的元素值
Status GetPlace(Node *head,int i){
	//先求链表长度,考虑i的位置是否合法 
	int sum=0;
	Node *cur1=head;
	while(cur1->next!=NULL){
		cur1=cur1->next;
		sum++;
	}
	//判断i的位置是否合法 
	if(i>sum+1||i<1){
		return error;
	}
	Node *cur=head;
	int now=0;
    //从头结点开始遍历,定义一个指针进行遍历,头结点是第0个,没有数据。当遍历到第i个的时候,返回数据
	while(now!=i){
		cur=cur->next;
		now++;
	}
	return cur->data;
} 

//链表已存在元素的位序 
Status GetData(Node *head,int i){
    //定义一个指针进行遍历
	Node *cur=head;
	int now=0;
    //如果当前结点的数据不符合条件,并且结点不为空,一直遍历,直到符合条件或者结点为空
	while(cur->data!=i&&cur!=NULL){
		cur=cur->next;
		now++;
	}
    //如果为空,返回error
	if(cur==NULL){
		return error;
	}
    //如果不为空,说明符合条件,直接返回数据
	return now;
}

//求输入元素的直接前驱
Status GetPre(Node *head,int i){
    //定义结点进行遍历
	Node *cur=head;
    //如果头结点下一个结点的数据域为指定元素,直接返回错,因为头结点不存数据
	if(head->next->data==i){
		return error;
	}
    //如果下一个结点的数据不等于指定数据,并且下一个元素不为空,就进行遍历。直到等于指定元素或者为空,停止。
	while(cur->data!=i&&cur!=NULL){
		cur=cur->next;
	}
    //为空
	if(cur==NULL){
		return error;
	}
    //否则返回数据
	return cur->data;
}

//求输入元素的直接后驱
Status GetNext(Node *head,int i){
	Node *cur=head;
    //如果当前所指结点数据匹配成功或者当前结点为空,或者下一个结点为空0,结束循环
	while(cur->data!=i&&cur!=NULL&&cur->next!=NULL){
		cur=cur->next;
	}
    //如果当前结点为空或者下一个结点为空,返回error
	if(cur==NULL||cur->next==NULL){
		return error;
	}
	return cur->next->data;
}

//在第i个位置插入元素
Status addNode(Node *head,int i,int e){
	//先求链表长度,考虑i的位置是否合法 
	int sum=0;
	Node *cur1=head;
	while(cur1->next!=NULL){
		cur1=cur1->next;
		sum++;
	}
	//判断i的位置是否合法 
	if(i>sum||i<1){
		return error;
	}
	else{
		//辅助遍历 
	Node *cur=head;
	int now=0;
	while(now!=i-1){
		//第i-1个位置结束循环 ,因为要在第i个位置插入元素 
		cur=cur->next;
		now++;
	}
	Node *e1=(Node*)malloc(sizeof(Node));
	e1->data=e;
	e1->next=cur->next;
	cur->next=e1;
	return OK;
	}
	
	
}

//删除第i个位置的元素
Status deleteNode(Node *head,int i){
	//链表为空,不能删除节点 
	if(head->next==NULL){
		return error;
	}
	//先求链表长度,考虑i的位置是否合法 
	int sum=0;
	Node *cur1=head;
	while(cur1->next!=NULL){
		cur1=cur1->next;
		sum++;
	}
	//判断i的位置是否合法 
	if(i>sum||i<1){
		return error;
	}
	
	else{
		Node *cur=head;
		int now=0;
		while(true){
		//第i-1个位置结束遍历,因为要把第i个删掉
		if(now==i-1){
			break;
		}
		cur=cur->next;
		now++;
	}
        //第i-1个位置的下一个为第i+1的地址
	cur->next=cur->next->next;
	return OK;
	}
	
}

//输出有的链表元素
Status ScanNode(Node *head){
	Node *cur=head->next;
	while(cur!=NULL){
		
		cout<<cur->data<<endl;
		cur=cur->next;
		}
	return OK;
}

//初始化并用头插法或尾插法插入元素
Status hahaha(Node *head,int e){
	//先求链表长度,考虑i的位置是否合法 
	int sum=0;
	Node *cur1=head;
	while(cur1->next!=NULL){
		cur1=cur1->next;
		sum++;
	}
		//辅助遍历 
	Node *cur=head;
	int now=0;
	while(now!=sum){
		//第i-1个位置结束循环 ,因为要在第i个位置插入元素 
		cur=cur->next;
		now++;
	}
	Node *e1=(Node*)malloc(sizeof(Node));
	e1->data=e;
	e1->next=cur->next;
	cur->next=e1;
	return OK;
	
	
	
}

//实现单链表的逆序输出
Status Nixu(Node *head){
	int sum=0;
	Node *cur1=head;
	while(cur1->next!=NULL){
		cur1=cur1->next;
		sum++;
	}
	//将链表中的数据存放在一个数组中,并且逆序输出即可
	int a[sum];
	for(int m=0;m<sum;m++){
		a[m]=head->next->data;
		head=head->next;
	}
	for(int n=sum-1;n>=0;n--){
		cout<<a[n];
	}
}

int main(){
	ElemType i;
	ElemType e;
	int jieguo;
	Node *head;
	Node *head1;
	bool flag=false;
	bool fg=false;
	bool flag1=false;
	cout<<"********************************************"<<endl;
	cout<<"*---------1.初始化一个链表-----------------*"<<endl; 
	cout<<"*---------2.销毁链表-----------------------*"<<endl;
	cout<<"*---------3.清空链表-----------------------*"<<endl;
	cout<<"*---------4.求链表长度 --------------------*"<<endl;
	cout<<"*---------5.指定位置的元素值---------------*"<<endl;
	cout<<"*---------6.链表已存在元素的位序-----------*"<<endl;
	cout<<"*---------7.求输入元素的直接前驱-----------*"<<endl;
	cout<<"*---------8.求输入元素的直接后继-----------*"<<endl;
	cout<<"*---------9.在第i个位置插入一个元素--------*"<<endl;	
	cout<<"*---------10.删除第i个位置的元素-----------*"<<endl;
	cout<<"*---------11.输出链表----------------------*"<<endl;
	cout<<"*---------12.初始化并用尾插法插入元素------*"<<endl;	
	cout<<"*---------13.退出该程序--------------------*"<<endl;
	cout<<"*---------14.实现单链表的逆序存放----------*"<<endl;
	cout<<"********************************************"<<endl;
	
	int m=0;
	cout<<endl;
	while(!fg){
		cout<<"请输入所需操作:"<<endl;
	    scanf("%d",&m);
	switch(m){
		case 1:
			head=ListInitiate();
			flag=true;
			break;
		case 2:
			if(flag!=true){
				cout<<"请先初始化链表"<<endl;
			}else{
				jieguo=DestoryLinkList(head);
				cout<<jieguo<<endl;
				
			}
			break;
		case 3:
			if(flag!=true){
				cout<<"请先初始化链表"<<endl;
			}else{
				jieguo=ClearList(head);
				cout<<jieguo<<endl;
				
			}
			break;
		case 4:
			if(flag!=true){
				cout<<"请先初始化链表"<<endl;
			}else{
				jieguo=Length(head);
				cout<<"链表长度为";
				cout<<jieguo<<endl;
				
			}
			break;
		case 5:
			if(flag!=true){
				cout<<"请先初始化链表"<<endl;
			}else{
				cout<<"请先输入指定位置"<<endl;
				cin>>i;
				jieguo=GetPlace(head,i);
				cout<<jieguo<<endl;
				
			}
			break;
		case 6:
			if(flag!=true){
				cout<<"请先初始化链表"<<endl;
			}else{
				cout<<"请输入指定元素"<<endl;
				cin>>i;
				jieguo=GetData(head,i);
				cout<<jieguo<<endl;
				
			}
			break;
		case 7:
			if(flag!=true){
				cout<<"请先初始化链表"<<endl;
			}else{ 
				cout<<"请输入指定元素"<<endl;
				cin>>i;
				jieguo=GetPre(head,i);
				cout<<jieguo<<endl;
				
			}
			break;
		case 8:
			if(flag!=true){
				cout<<"请先初始化链表"<<endl;
			}else{
				cout<<"请输入指定元素"<<endl;
				cin>>i;
				jieguo=GetNext(head,i);
				cout<<jieguo<<endl;
				
			}
			break;
		case 9:
			if(flag!=true){
				cout<<"请先初始化链表"<<endl;
			}else{
				
			cout<<"请输入元素插入的位置"<<endl;
			cin>>i;
			cout<<"请输入需要插入的元素"<<endl;
			cin>>e;
			jieguo=addNode(head,i,e);
			cout<<jieguo<<endl;
			
			}
			break;
		case 10:
			if(flag!=true){
				cout<<"请先初始化链表"<<endl;
			}else{
			cout<<"请输入想要删除位置"<<endl;
			cin>>i;
			jieguo=deleteNode(head,i);
			cout<<jieguo<<endl;
			
			}
			break;	
		case 11:
			if(flag!=true){
				cout<<"请先初始化链表"<<endl;
			}else{
			ScanNode(head);
			
			}
			break;
		case 12:
			head1=ListInitiate();
			cout<<"您现在可以插入元素,如果输入000即可结束输入"<<endl;
			while(true){
				cout<<"请输入元素"<<endl;
				cin>>i;
				if(i==000){
					break;
				}
				hahaha(head1,i);
			}
			ScanNode(head1);
			flag1=true;
			break;
		case 13:
			fg=true;
		case 14:
			if(flag1!=true){
				cout<<"请先进行操作12创造一个链表"<<endl;
			}else{
				
			Nixu(head1);
			break;
			}
		}
	}
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

henghengzhao_

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

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

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

打赏作者

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

抵扣说明:

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

余额充值