单链表的定义
什么是单链表
- 顺序表:可随机存取,存储密度高;但是要大片连续空间,改变容量不方便
- 单链表:不要求大片连续空间;但是不可随机存取,要耗费一个空间放指针
用代码定义一个单链表——链表由节点组成
每个结点可以定义为:
struct LNode{ //定义单链表结点类型
ElemType data;(数据域) //每个结点存放一个数据元素
struct LNode *next;(指针域) //指针指向下一个结点
};
增加一个新的结点:在内存中申请一个结点所需空间,并用指针P指向这个结点
struct LNode *P = (struct LNode *)malloc(sizeof(struct LNode));
typedef关键字——数据类型重命名
t
y
p
e
d
e
f
<
数
据
类
型
>
<
别
名
>
typedef<数据类型><别名>
typedef<数据类型><别名>
typedef struct LNode;
LNode *P = (LNode *)malloc(sizeof(LNode));
typedef struct LNode{ //定义单链表结点类型
ElemType data;//(数据域) //每个结点存放一个数据元素
struct LNode *next;//(指针域) //指针指向下一个结点
}LNode,*LinkList;
-
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; while(p!=null && j<i){ p=p->next; j++; } return p; }
强调这是一个单链表 ——使用LinkList
强调这是一个结点 ——使用LNode *
两种实现
不带头结点
#include<stdio.h>
typedef int ElemType;
typedef struct LNode{ //定义单链表结点类型
ElemType data; //每个结点存放一个数据元素
struct LNode *next; //指针指向下一个结点
}LNode,*LinkList;
//初始化一个空的单链表
bool InitList(LinkList &L){
L = NULL; //空表,暂时还没有任何结点,防止脏数据
return true;
}
int main(){
//这里的没有创建一个结点
LinkList L; //申明一个指向单链表的指针
//初始化一个空表
printf("%d",L);//打印链表的初始地址
InitList(L);
//……后续代码
return 0;
}
带头结点
#include<stdio.h>
typedef int ElemType;
typedef struct LNode{ //定义单链表结点类型
ElemType 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;
}
int main(){
//这里的没有创建一个结点
LinkList L; //申明一个指向单链表的指针
//初始化一个空表
printf("%d",L);//打印链表的初始地址
InitList(L);
//……后续代码
return 0;
}
不带头节点,写代码更麻烦,对第一个数据结点和后续数据结点的处理需要用不同的代码逻辑,对空表和非空表的处理需要用不懂的代码逻辑。
单链表插入和删除
带头结点
插入(按位序插入)
ListInsert(&L,i,e):在表中的第i个位置上插入指定元素e;
#include<stdio.h>
#include<stdlib.h>
typedef int ElemType;
typedef struct LNode{ //定义单链表结点类型
ElemType 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 ListInsert(LinkList &L,int i,ElemType e){
if(i<1)
return false;
LNode *p; //指针p指向当前扫描到的节点
int j=0; //当前p指向的是第几个结点
p=L; //L指向头结点,头结点是第0个结点(不存数据)
while(p!=NULL && j<i-1){
p=p->next;
j++;
}
if(p==NULL){
return false;//i值不合法
}
LNode *s = (LNode *)malloc(sizeof(LNode));
s->data = e;
//下面的顺序很重要
s->next = p->next;
p->next = s;
return true;
}
删除(按位序删除)
ListDele(&L,i,&e):删除表L中第i个位置的元素,并用e返回删除元素的值。
不带头结点
bool ListDelete(LinkList &L,int i,ElemType &e){
if(i<1)
return false;
LNode *p; //指针p指向当前扫描到的节点
int j=0; //当前p指向的是第几个结点
p=L; //L指向头结点,头结点是第0个结点(不存数据)
while(p!=NULL && j<i-1){
p=p->next;
j++;
}
if(p==NULL){
return false;//i值不合法
}
if(p->next == NULL){//第i-1个结点之后已无其他结点
return false;
}
LNode *q=p->next; //令q指向被删除结点
e = q->data; //用e返回元素的值
p->next = q->next; //将*q结点从链中断开
free(q); //释放结点的存储空间
return true; //删除成功
}
删除指定结点
bool DeleteNode(LNode *p){
if(p==NULL)
return false;
LNode *q=p->next; //令q指向*p的后继结点
p->data = p->next->data;//和后继结点先还数据域
p->next = q->next; //将*q结点从链中“断开”
free(q); //释放后继结点的存储空间
return true;
}
插入(按位序插入)
#include<stdio.h>
#include<stdlib.h>
typedef int ElemType;
typedef struct LNode{ //定义单链表结点类型
ElemType 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 ListInsert(LinkList &L,int i,ElemType e){
if(i<1)
return false;
if(i==1){//插入第一个结点的操作与其他结点操作不同
LNode *s = (LNode *)malloc(sizeof(LNode));
s->data = e;
s->next = L;
L=s; //头指针指向新结点
return true;
}
LNode *p; //指针p指向当前扫描到的节点
int j=1; //当前p指向的是第几个结点
p=L; //L指向头结点,头结点是第0个结点(不存数据)
while(p!=NULL && j<i-1){
p=p->next;
j++;
}
if(p==NULL){
return false;//i值不合法
}
LNode *s = (LNode *)malloc(sizeof(LNode));
s->data = e;
//下面的顺序很重要
s->next = p->next;
p->next = s;
return true;
}
插入(按指定结点后面插入)
#include<stdio.h>
#include<stdlib.h>
typedef int ElemType;
typedef struct LNode{ //定义单链表结点类型
ElemType 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 ListInsert(LinkList &L,int i,ElemType e){
if(i<1)
return false;
if(i==1){//插入第一个结点的操作与其他结点操作不同
LNode *s = (LNode *)malloc(sizeof(LNode));
s->data = e;
s->next = L;
L=s; //头指针指向新结点
return true;
}
LNode *p; //指针p指向当前扫描到的节点
int j=1; //当前p指向的是第几个结点
p=L; //L指向头结点,头结点是第0个结点(不存数据)
while(p!=NULL && j<i-1){
p=p->next;
j++;
}
return InsertNextNode(p,e);
}
//后插操作:在指定P结点之后插入元素e
bool InsertNextNode(LNode *p,ElemType e){
if(p==NULL){
return false;
}
LNode *s = (LNode *)malloc(sizeof(LNode));
if(s==NULL){//内存分配失败
return false;
}
s->data = e; //用结点s保存数据元素e
s->next = p->next;
p->next = s;
return true;
}
插入(按指定结点前面插入)
bool InsertPrioNode(LNode *p,ElemType 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;
}
注:新声明一个结点,先插入结点的后面,再把结点的值赋值给新节点,再把插入的值给原来的节点就实现插入到前面了(先连到后面再换值)