什么是链表?
链表由一系列节点(Node)组成,每个节点包含数据部分和指向列表中下一个节点的指针
为什么要学习、使用链表?
-
动态数据结构:链表可以在运行时地添加、删除和重新排列元素,而不需要像数组那样在添加或删除元素时可能需要进行大量的数据移动
-
插入和删除操作:在链表中进行插入和删除操作通常比数组要快,特别是当这些操作发生在列表的开始或中间位置时。在链表中,这些操作只需要修改指针的指向,而在数组中则可能需要移动大量的元素。
-
实现其他数据结构:链表是许多其他高级数据结构的基础,如邻接表、哈希表(解决冲突)、图的表示等。
-
解决特定问题:在某些特定的问题中,链表是解决问题的最佳或唯一的数据结构。例如,在解决约瑟夫环问题时,链表提供了一种直观且高效的方式来模拟和解决问题。
怎么实现链表?
一共有两种办法可以实现,个人推荐在实际应用的时候使用第一种。下面先讲单链表。
一、数组模拟
#include <bits/stdc++.h>
using namespace std;
const int N = 100010;
// head 表示头结点的下标
// e[i] 表示节点i的值
// ne[i] 表示节点i的next指针是多少
// idx 存储当前已经用到了哪个点
int head, e[N], ne[N], idx;
// 初始化
void init()
{
head = -1;
idx = 0;
}
// 将x插到头结点
void add_to_head(int x)
{
e[idx] = x, ne[idx] = head, head = idx ++ ;
}
// 将x插到下标是k的点后面
void add(int k, int x)
{
e[idx] = x, ne[idx] = ne[k], ne[k] = idx ++ ;
}
// 将下标是k的点后面的点删掉
void remove(int k)
{
ne[k] = ne[ne[k]];
}
int main()
{
int m;
cin >> m;
init();
while (m -- )
{
int k, x;
char ch;
cin >> ch;
if (ch == 'H')
{
cin >> x;
add_to_head(x);
}
else if (ch == 'D')
{
cin >> k;
if (!k) head = ne[head];
else remove(k - 1);
}
else
{
cin >> k >> x;
add(k - 1, x);
}
}
for (int i = head; i != -1; i = ne[i]) cout << e[i] << ' ';
cout << endl;
return 0;
}
二、结构体+指针
#include<bits/stdc++.h>
using namespace std;
inline int read(){
char w=getchar();
int fl=1,sum=0;
while(w>'9'||w<'0'){if(w=='-')fl=-1;w=getchar();}
while(w<='9'&&w>='0'){sum=(sum<<1)+(sum<<3)+(w^48);w=getchar();}
return fl*sum;
}
struct node{
int x;//链表节点的数据域
node *next;//下一个节点的地址
};
int n;
node *head,*tail,*temp;//链表的头节点,尾节点,临时节点
int main(){
cin>>n;
head=new node;
tail=new node;
temp=new node;//为三个节点申请空间
head->next=temp;//头节点的下一个指向临时节点
temp->next=tail;//临时节点的下一个指向尾节点
tail->next=NULL; //尾节点因为是最后一个节点了,所以它指向空
while(n--){//向链表添加节点
node *p=new node;//申请空间
p->x=read();//保存输入的数据
p->next=temp;//当前添加的节点的下一个指向临时节点
head->next=p;//head节点的下一个指向当前正在添加的节点
temp=p;//临时节点赋值为当前添加完的节点,在下一轮添加的时候用于追踪上一个节点
}
node *traversal=new node;//创建一个遍历链表的临时指针
traversal=head->next;//初始化为头节点后面的第一个节点,即最后一个添加进来的节点
while(true){
cout<<traversal->x<<" ";//输出数据
traversal=traversal->next;//将他的下一个赋值给他自己
if(traversal->next==tail){//没了,就跳出循环
break;
}
}
return 0;
}
关于插入节点,这里有一张图
怎么删除节点呢?
#include<bits/stdc++.h>
using namespace std;
inline int read(){
char w=getchar();
int fl=1,sum=0;
while(w>'9'||w<'0'){if(w=='-')fl=-1;w=getchar();}
while(w<='9'&&w>='0'){sum=(sum<<1)+(sum<<3)+(w^48);w=getchar();}
return fl*sum;
}
struct node{
int x,idx;//链表节点的数据域
node *next;//下一个节点的地址
};
int n,cishu,cnt;
node *head,*tail,*temp;//链表的头节点,尾节点,临时节点
int main(){
cin>>n;
head=new node;
tail=new node;
temp=new node;//为三个节点申请空间
head->next=temp;//头节点的下一个指向临时节点
temp->next=tail;//临时节点的下一个指向尾节点
tail->next=NULL; //尾节点因为是最后一个节点了,所以它指向空
while(n--){//向链表添加节点
node *p=new node;//申请空间
p->x=read();//保存输入的数据
p->idx=cnt;
p->next=temp;//当前添加的节点的下一个指向临时节点
head->next=p;//head节点的下一个指向当前正在添加的节点
temp=p;//临时节点赋值为当前添加完的节点,在下一轮添加的时候用于追踪上一个节点
cnt++;
}
cin>>cishu;
while(cishu--){
int x=read();//删除编号为x的节点
node *t;
t=head->next;
for(int i=1;i<=x;i++){
t=t->next;
if(t->next==tail){//没了
break;
}
}
//现在,t就刚好是要删除的节点的前一个节点
t->next=t->next->next;
}
node *traversal=new node;//创建一个遍历链表的临时指针
traversal=head->next;//初始化为头节点后面的第一个节点,即最后一个添加进来的节点
while(true){
cout<<traversal->x<<" ";//输出数据
traversal=traversal->next;//将他的下一个赋值给他自己
if(traversal->next==tail){//没了,就跳出循环
break;
}
}
return 0;
}