赞
踩
首先要理解什么是链表:链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。
链表和数组的区别:采用顺序存储结构时称为为数组,采用链式存储结构时称为为链表。但二者同为线性表。
简单的说:
数组通过下标将每一个元素值联系在一起,并且用一组地址连续的存储单元(物理位置相邻)来存储表中的元素值链表通过指针把每一个元素的值联系在一起,不要求逻辑上(链表逻辑)相邻的两个元素在物理位置上也相邻,那么就需要链表元素需要两个数据,一个是储存该元素的值,一个是储存链表下一个元素的地址,两者统称为一个节点,接下来每一个元素都用对应的节点表示,阐明两个存储数据
下面这张图是单链表的结构,其中红色代表元素值,蓝色代表下一个元素地址,每次遍历都是从下一个元素的地址指向下一个元素
只能从左往右一条指针遍历的链表
这里我们用数组模拟单链表更好的理解
首先链表里面需要两个数据,一个是值,一个是下一个元素的地址
一般的 : 我们用e[]数组储存链表里面的值,用ne[]数组储存链表每个元素下一个元素的地址
const int N = 1e6+10;
int e[N],ne[N];
让我们来思考一个问题,如果当链表为空怎么办呢?说明链表里面没有元素,所以我们规定链表两端还有不会变的一个头节点和一个空节点,头节点本身没有数值,但是它会指向下一个元素的地址,当链表为空时指向空节点地址(规定为-1),用idx来表示第几个节点
const int N = 1e6+10;
int e[N],ne[N],idx=1; //因为有一个头节点,所以idx为一
ne[0]=-1;
那么一个单链表的雏形构建完毕,我们接下来应该对链表进行操作,实现链表的基本功能
void to_insert(int k,int x)
{
e[idx]=x,ne[idx]=ne[k],ne[k]=idx++;
//注意顺序不能翻!!!
}
我们将idx节点的元素先赋值,就是e[idx]=x.
再将idx节点下一个元素的地址赋值(这个值是k节点的下一个元素的地址,即ne[k])所以是ne[idx]=ne[k].
最后在把k节点的下一个元素的地址指向idx,所以是ne[k]=idx.
示例代码
#include<bits/stdc++.h> using namespace std; const int N = 1e6+10; int e[N],ne[N],idx; void init() {idx=1;ne[0]=-1;} void to_insert(int k,int x) {e[idx]=x;ne[idx]=ne[k];ne[k]=idx++;} int main() { init(); int t; cout<<"请输入你要访问的次数: "; cin >> t; while(t--) { int k,x; cout<<"请输入你要在单链表第几号节点插入数字: "; cin>>k; cout<<"插入的数字是: "; cin>>x; to_insert(k,x); cout<<"该单链表变为: "; for(int i = ne[0];i != -1;i = ne[i]) cout<<e[i]<<" "; cout<<endl<<endl; } return 0; }
结果如下:
void remove(int k)
{
ne[k]=ne[k]];//直接让k<指向的地址>指向k+1<指向的地址>,即把k+1节点跳过
}
单链表基本上以及完成,让我们带上一个简单的例题看看最终的代码:
实现一个单链表,链表初始为空,支持三种操作:
输入格式
第一行包含整数 MM,表示操作次数
接下来 M 行,每行包含一个操作命令,操作命令可能为以下几种:
H x
,表示向链表头插入一个数 x。D k
,表示删除第 k个插入的数后面的数(当 k 为 0 时,表示删除头结点)I k x
,表示在第 k 个插入的数后面插入一个数 x(此操作中 k 均大于 0)输出格式
共一行,将整个链表从头到尾输出
输入样例:
10
H 9
I 1 1
D 1
D 0
H 6
I 3 6
I 4 5
I 4 5
I 3 4
D 6
输出样例:
6 4 6 5
#include<bits/stdc++.h> using namespace std; const int N = 1e6+10; int e[N],ne[N],idx; void init() {idx=1;ne[0]=-1;} void to_insert(int k,int x) {e[idx]=x;ne[idx]=ne[k];ne[k]=idx++;} void remove(int k) {ne[k]=ne[ne[k]];} int main() { init(); int t; cin>>t; while(t--) { char c; int x,k; cin>>c; if(c=='H') { cin>>x; to_insert(0,x); } if(c=='D') { cin >> k; remove(k); } if(c=='I') { cin >> k >> x; to_insert(k,x); } } for(int i = ne[0];i != -1;i = ne[i]) cout<<e[i]<<" "; cout<<endl; return 0; }
一个元素有三个数据,一个是元素的值,一个是下一个元素的地址,还有一个是上一个元素的地址,像这样的链表就是双链表
总体上同样和单链表类似,用数组模拟。
一般的 : 我们用e[]数组储存链表里面的值,用r[]数组储存链表每个元素右一个元素的地址,用l[]数组储存链表每个元素左一个元素的地址。这里我们同样默认链表两端有两个不变的数0,1来防止空链表的情况,这时k=2.
const int N = 1e6+10;
int e[N],l[N],r[N],idx;
void init()
{
r[0]=1;l[1]=0;idx=2;
}
void to_insert(int k,int x)
{
e[idx]=x,r[idx]=r[k],l[idx]=k,l[r[k]]=idx,r[k]=idx++;
}
如果是左侧插入,只需让k节点左侧插入变为k-1节点右侧插入,就是l[k]进入子函数
样例代码
#include<bits/stdc++.h> using namespace std; const int N = 1e6+10; int e[N],l[N],r[N],idx; void init() {idx=2;r[0]=-1;l[-1]=0;} void to_insert(int k,int x) { e[idx]=x; r[idx]=r[k]; l[idx]=k; l[r[k]]=idx; r[k]=idx++; } int main() { init(); int t; cout<<"请输入你要访问的次数: "; cin >> t; cout<<endl; while(t--) { int k,x; char p; cout<<"请输入你要在双链表第几号节点插入数字: "; cin>>k; cout<<"请输入插入方向:"; cin>>p; cout<<"插入的数字是: "; cin>>x; if(p=='r') to_insert(k,x); else to_insert(l[k],x); cout<<"该单链表变为: "; for(int i = r[0];i != -1;i = r[i]) cout<<e[i]<<" "; cout<<endl<<endl; } return 0; }
结果如下:
在这里插入图片描述
void remove(int k)
{
l[r[k]]=l[k];r[l[k]]=r[k];
}
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。