前言
什么是链表?
线性表的链式存储结构称为链表(Linked List)。链表是一种常见的线性数据结构,用于组织和存储一系列元素,这些元素以节点(Node)的形式连接在一起。每个节点包括两个主要部分:用于存储数据的数据域(Data Field)和指向节点的指针域(Next Pointer)。链表可以有不同的变种,包括单链表、双链表和循环链表等。
双链表和单链表的优缺点
1. 单链表
1.1 定义
代码语言:javascript复制typedef struct LNode//单链表定义
{
int data;
struct LNode *next;
}LNode;
1.2 常用操作
1.2.1 创建
尾插法创建:(额...头插和尾插会一个就行,因为尾插法创建的顺序和输入数组一样所以习惯了)
代码语言:javascript复制void CreateListB(LNode **L,int a[],int n) //尾插法创建单链表
{
LNode *r,*s;
*L=(LNode *)malloc(sizeof(LNode));
r=*L;
for(int i=0;i<n;i ){
s=(LNode *)malloc(sizeof(LNode));
s->data=a[i];
r->next=s;
r=s;
}
r->next=NULL;
}
1.2.2 输出
代码语言:javascript复制void DispList(LNode *L) //输出单链表
{
LNode *p=L;//p指向头节点
while (p!=NULL)
{
printf("%d ",p->next->data);
p=p->next;
}
printf("n");
}
1.2.3 插入
代码语言:javascript复制void InsertList(LNode **L,int i,int e)//位置i插入元素e
{
LNode *p,*s,*q;
p=*L;//p指向头结点
int j=0;
while (p!=NULL && j<i-1)//p定位到第i-1节点
{
p=p->next;
j ;
}
s=(LNode *)malloc(sizeof(LNode));
s->data=e;
q=p->next;
s->next=q;
p->next=s;
}
1.2.4 删除
代码语言:javascript复制void DelList(LNode **L,int i)//删除第i位置元素
{
LNode *p=*L;//p指向头结点
int j=0;
while (p!=NULL && j<i-1)//p定位第i-1节点
{
p=p->next;
j ;
}
p->next=p->next->next;
}
1.2.5 销毁
代码语言:javascript复制void DestoryList(LNode **L)//销毁链表
{
LNode *p=*L,*p2=(*L)->next;//p指向头结点,p2指向首节点
while (p!=NULL)
{
free(p);
p=p2;
p2=p2->next;
}
}
1.3 完整代码
代码语言:javascript复制#include <iostream>
using namespace std;
struct LNode{
int data;
LNode *next;
};
//创建链表:尾插法
void CreateLNode(LNode **L,int a[],int n){
LNode *s,*r;
*L=(LNode *)malloc(sizeof(LNode));
r=*L;
for(int i=0;i<n;i ){
s=(LNode *)malloc(sizeof(LNode));
s->data=a[i];
r->next=s;
r=s;
}
r->next=NULL;
}
//输出链表
void DispLNode(LNode **L){
LNode *p=*L;
while(p->next!=NULL){
cout<<p->next->data<<" ";
p=p->next;
}
cout<<endl;
}
//插入
void InsertLNode(LNode **L,int n,int e){
LNode *p=*L;
for(int i=0;i<n-1 && p!=NULL;){
p=p->next;
i ;
}
if(p==NULL) cout<<"error!"<<endl;
else{
LNode *q,*s=(LNode *)malloc(sizeof(LNode));
s->data=e;
q=p->next;
s->next=q;
p->next=s;
}
}
//删除
void DelLNode(LNode **L,int n){
LNode *p=*L;
for(int i=0;i<n-1 && p!=NULL;){
p=p->next;
i ;
}
if(p==NULL) cout<<"error!"<<endl;
else{
LNode *q=p->next;
p->next=q->next;
}
}
//查找
void Sreach(LNode **L,int e){
LNode *p=*L;
int count=0,flag=0;
while(p->next!=NULL){
if(p->next->data==e){
flag=1;
break;
}
else p=p->next;
count ;
}
if(flag!=0){
cout<<"查找成功,"<<e<<"位于第"<<count<<"位"<<endl;
}
else cout<<"查找失败"<<endl;
}
//销毁
void DesLNode(LNode **L){
LNode *p=*L,*q=(*L)->next;
while(p!=NULL){
free(p);
p=q;
q=q->next;
}
}
//判断是否为空串
void NullLNode(LNode **L){
LNode *p=*L;
if(p->next== NULL) cout<<"空串"<<endl;
else cout<<"不是空串"<<endl;
}
int main(){
LNode *L;
int a[]={0,1,2,3,4,5,6,7,8,9,10};
int n=sizeof(a)/sizeof(a[0]);
CreateLNode(&L,a,n);
DispLNode(&L);
NullLNode(&L);
//插入
InsertLNode(&L,2,5);
DispLNode(&L);
//删除
DelLNode(&L,2);
DispLNode(&L);
//查找
Sreach(&L,5);
//销毁
DesLNode(&L);
//NullLNode(&L);
return 0;
}
2. 双链表
2.1 定义
代码语言:javascript复制typedef struct LNode//单链表定义
{
int data;
struct LNode *next;
}LNode;
2.2 常用操作
2.2.1 创建
尾插法创建:
代码语言:javascript复制void CreateDListB(DNode **L,int a[],int n) //尾插法创建双链表
{
DNode *r,*s;
*L=(DNode *)malloc(sizeof(DNode));
r=*L;
for(int i=0;i<n;i ){
s=(DNode *)malloc(sizeof(DNode));
s->data=a[i];s->prior=r;//就这里不一样而已
r->next=s;
r=s;
}
r->next=NULL;
}
2.2.2 输出
和单链表一样。
2.2.3 插入
代码语言:javascript复制void InsertDList(DNode **L,int i,int e)//位置i插入元素e
{
DNode *p,*s,*q;
p=*L;
int j=0;
while (p!=NULL && j<i-1)
{
p=p->next;
j ;
}
s=(DNode *)malloc(sizeof(DNode));
s->data=e;
q=p->next;
s->next=q;q->prior=s;//就这里和单链表不一样
p->next=s;s->prior=p;//就这里和单链表不一样
}
2.2.4 删除
和单链表一样。
2.2.5 销毁
和单链表一样。
2.3 完整代码
代码语言:javascript复制#include <stdio.h>
#include <stdlib.h>
typedef struct DNode
{
int data;
struct DNode *prior;
struct DNode *next;
}DNode;
void CreateDListB(DNode **L,int a[],int n); //尾插法创建双链表
void DispDList(DNode *L); //输出双链表
void InsertDList(DNode **L,int i,int e);//位置i插入元素e
void DelDList(DNode **L,int i);//删除第i位置元素
void DestoryDList(DNode **L);//销毁链表
int main()
{
DNode *L;
int a[10]={0,1,2,3,4,5,6,7,8,9};
CreateDListB(&L,a,10);
InsertDList(&L,1,2);
DelDList(&L,1);
DispDList(L);
DestoryDList(&L);
DispDList(L);
return 0;
}
void CreateDListB(DNode **L,int a[],int n) //尾插法创建双链表
{
DNode *r,*s;
*L=(DNode *)malloc(sizeof(DNode));
r=*L;
for(int i=0;i<n;i ){
s=(DNode *)malloc(sizeof(DNode));
s->data=a[i];s->prior=r;
r->next=s;
r=s;
}
r->next=NULL;
}
void DispDList(DNode *L) //输出双链表
{
DNode *p=L;//p指向头节点
while (p!=NULL)
{
printf("%d ",p->next->data);
p=p->next;
}
printf("n");
}
void InsertDList(DNode **L,int i,int e)//位置i插入元素e
{
DNode *p,*s,*q;
p=*L;
int j=0;
while (p!=NULL && j<i-1)
{
p=p->next;
j ;
}
s=(DNode *)malloc(sizeof(DNode));
s->data=e;
q=p->next;
s->next=q;q->prior=s;//就这里不一样
p->next=s;s->prior=p;//就这里不一样
}
void DelDList(DNode **L,int i)//删除第i位置元素
{
DNode *p=*L;//p指向头结点
int j=0;
while (p!=NULL && j<i-1)//p定位第i-1节点
{
p=p->next;
j ;
}
p->next=p->next->next;
}
void DestoryDList(DNode **L)//销毁链表
{
DNode *p=*L,*p2=(*L)->next;//p指向头结点,p2指向首节点
while (p!=NULL)
{
free(p);
p=p2;
p2=p2->next;
}
}
3. 循环链表
3.1 定义
循环链表是一种链表数据结构,其特点是链表的尾节点指向链表中的头节点,形成一个循环。包括循环单链表和循环双链表。 循环单链表:每个节点包含一个next指针,并且尾节点的next指针指向头节点。 循环双链表:每个节点包含next指针和piror指针。尾节点的next指针指向头节点,头节点的piror指针指向尾节点。
3.2 扫描链表结束的临界条件
p!=L //while语句内,正常为p!=NULL
3.3 环形链表
环形链路: 环形链路指的是在链表中存在一个或多个节点形成了循环,其中一个节点指向先前的节点,导致链表永无止境地继续下去。是链表中的问题或异常情况 解决:判断链表是否为环形链表,通常可以使用两个指针(快慢指针)的方法,也称为弗洛伊德环检测算法。以下是判断环形链表的一般步骤:
- 使用两个指针,一个称为"慢指针",另一个称为"快指针",同时从链表的起始节点出发。
- 在每一步中,慢指针前进一步,快指针前进两步。如果链表中存在环,快指针最终会进入环中,并在某个时刻与慢指针相遇。如果链表不包含环,快指针将在某个时刻到达链表的末尾并未曾与慢指针相遇。
- 如果快慢指针相遇,那么链表包含环,可以判断为环形链表。
致读者
非知之难,行之为难;非行之难,终之斯难