文章目录
- 一、单链表
- 1、单链表定义
- 2、初始化单链表
- 2.1、不带头结点的单链表
- 2.2、带头结点的单链表
- 3、单链表基本操作
- 3.1、按位序插入(带头结点)
- 3.2、按位序插入(不带头结点)
- 3.3、指定结点的后插操作
- 3.4、指定结点的前插操作
- 3.5、按位序删除(带头结点)
- 3.6、指定结点的删除
- 3.7、按位查找
- 3.8、按值查找
- 4、单链表的建立
- 4.1、尾插法
- 4.2、头插法
- 二、双链表
- 1、双链表的初始化(带头结点)
- 2、插入
- 3、删除
- 4、遍历
- 三、循环链表
- 3.2、循环双链表
- 四、静态链表
- 4.1、基本操作
- 顺序表和链表的比较
一、单链表
1、单链表定义
typedef重命名数据类型
第二种声明方式可读性更强
LinkList和LNode*的不同用法
#include<stdio.h>
//定义单链表结点类型
typedef struct LNode{
int data;
struct LNode *next;
}LNode,*LinkList;
//查找单链表中的某一个位置的数
LNode* GetElem(LinkList L,int i){
int j=1;
LNode *p=L->next;
if(i==0)
return L;
if(i<1)
return NULL;
if(p!=NULL&&j<i){
p=p->next;
j++;
}
return p;
}
2、初始化单链表
2.1、不带头结点的单链表
//定义单链表结点类型
typedef struct LNode{
int data;
struct LNode *next;
}LNode,*LinkList;
//初始化一个空的单链表
bool InitList(LinkList &L){
L=NULL;
return true;
}
//判断单链表是否为空
bool Empty(LinkList L){
return(L==NULL);
}
2.2、带头结点的单链表
//定义单链表结点类型
typedef struct LNode{
int data;
struct LNode *next;
}LNode,*LinkList;
//初始化一个空的单链表
bool InitList(LinkList &L){
L=(LNode*)malloc(sizeof(LNode));
if(L==NULL)
return false;
L->next=NULL;
return true;
}
//判断单链表是否为空
bool Empty(LinkList L){
return(L->next==NULL);
}
int main(){
LinkList L;
InitList(L);
return 0;
}
大多数情况使用带头结点的方式
3、单链表基本操作
3.1、按位序插入(带头结点)
带头结点插入位置1时
//插入操作
bool ListInsert(LinkList &L,int i,int e){
if(i<1)
return false;
LNode *p;
p=L;
int j=0;
//找到i-1位置
while(p!=NULL&&j<i-1){
p=p->next;
j++;
}
//开拓新区域
LNode *s=(LNode*)malloc(sizeof(LNode));
s->data=e;
s->next=p->next;
p->next=s;
return true;
}
3.2、按位序插入(不带头结点)
bool ListInsert(LinkList &L,int i,int e){
if(i<1)
return false;
if(i==1){//不同的地方
LNode*s=(LNode*)malloc(sizeof(LNode));
s->data=e;
s->next=L;//因为L没有next所以直接等于
L=s; //头指针指向新结点
return true;
}
LNode *p;
p=L;
int j=0;
//找到i-1位置
while(p!=NULL&&j<i-1){
p=p->next;
j++;
}
//开拓新区域
LNode *s=(LNode*)malloc(sizeof(LNode));
s->data=e;
s->next=p->next;
p->next=s;
return true;
}
3.3、指定结点的后插操作
bool InsertNextNode(LNode *p,int e){
if(p==NULL)
return false;
//开拓新区域
LNode *s=(LNode*)malloc(sizeof(LNode));
if(s==NULL)
return false;
s->data=e;
s->next=p->next;
p->next=s;
return true;
}
3.4、指定结点的前插操作
前面区域无法访问,只能把头结点传入
将p的值赋值给s,再将插入的值赋值给p
bool InsertPriorNode(LNode *p,int e){
if(p==NULL)
return false;
//开拓新区域
LNode *s=(LNode*)malloc(sizeof(LNode));
if(s==NULL)
return false;
s->next=p->next;
p->next=s;
s->data=p->data;
p->data=e;
return true;
}
3.5、按位序删除(带头结点)
bool ListDelete(LinkList &L,int i,int &e){
if(i<1)
return false;
LNode *p;
p=L;
int j=0;
while(p!=NULL&&j<i-1){
p=p->next;
j++;
}
if(p==NULL)
return false;
if(p->next==NULL)
return false;
LNode *q=p->next;//q指向被删除的结点
e=q->data;//保存删除的值
p->next=q->next;//断开q
free(q);//销毁q
return true;
}
3.6、指定结点的删除
如果删除最后一个元素,就会出错
3.7、按位查找
LNode *GetElem(LinkList L,int i){
if(i<1)
return false;
LNode *p;
p=L;
int j=0;
while(p!=NULL&&j<i-1){
p=p->next;
j++;
}
return p;
}
3.8、按值查找
4、单链表的建立
4.1、尾插法
LinkList List_TailInsert(LinkList &L){
int x;
L=(LNode*)malloc(sizeof(LNode));
L->next=NULL;
LNode *s,*r=L;
scanf("%d",&x);
while(x!=9999){
s=(LNode*)malloc(sizeof(LNode));
r->next=s;
s->data=x;
r=s;
scanf("%d",&x);
}
r->next=NULL;
return L;
}
4.2、头插法
链表的逆置
LinkList List_TailInsert(LinkList &L){
LNode *s;
int x;
L=(LNode*)malloc(sizeof(LNode));
L->next=NULL;
scanf("%d",&x);
while(x!=9999){
s=(LNode*)malloc(sizeof(LNode));
s->next=L->next;
L->next=s;
scanf("%d",&x);
}
return L;
}
二、双链表
1、双链表的初始化(带头结点)
bool InitDLinkList(DLinkList &L){
L=(DNode*)malloc(sizeof(DNode));
L->next=NULL;
L->prior=NULL;
return true;
}
2、插入
bool InsertNextDNode(DNode *p,DNode *s){
s->next=p->next;
if(p->next!=NULL)
p->next->prior=s;
s->prior=p;
p->next=s;
}
3、删除
4、遍历
三、循环链表
3.2、循环双链表
双链表,在尾部插入数据时,双链表会在第二行p->next->prior=s出错,因为p->next为空,但是循环后就不为空了
四、静态链表
#include<stdio.h>
#define MaxSize 10
//常规
struct Node{
int data;
int next;
};
//课本
typedef struct{
int data;
int next;
}SLinkList[MaxSize];
int main(){
struct Node x;
printf("%d\n",sizeof(x));
//常规
struct Node a[MaxSize];
printf("%d\n",sizeof(a));
SLinkList b;
printf("%d\n",sizeof(b));
}
4.1、基本操作
优点:
增、删操作不需要大量移动元素
缺点:
不能随机存取,只能从头结点开始依次往后查找;容量固定不可变
适用场景:
①不支持指针的低级语言;
②数据元素数量固定不变的场景(如操作系统的文件分配表FAT)
顺序表和链表的比较
存储结构
表长难以预估、经常要增加/删除元素——链表
表长可预估、查询(搜索)操作较多——顺序表