一、链表介绍
注:节点和结点只是说法、习惯的不同,本质上没有区别。
单、双链表基本运算实现的核心算法是插入和删除操作,插入(删除)操作有前插(删)和后插(删)的区别,也有按值插入(删除)和按位序插入(删除)的区别,难点在于如何去找到合适插入(删除)的合适位置。
一般而言,前插操作需要找到待插入元素(按值)或待插入位序(按位序)的前驱;后插操作需要找到待插入元素所在的位置或待插入的位序。
1.类别
带头节点 | 不带头节点 | ||
单链表 | 循环 | 带头结点的单循环链表 | 不带头结点的单循环链表 |
不循环 | 带头结点的单链表 | 不带头结点的单链表 | |
双链表 | 循环 | 带头结点的双循环链表 | 不带头结点的双循环链表 |
不循环 | 带头结点的双链表 | 不带头结点的双链表 |
注:本文实现的是带头结点的双链表
2.带头结点的双链表图示
二、涉及的基本运算总览
- 前插操作:往双链表D中第i位(前)插入值为e的元素(按位序)
- 前插操作:往双链表D中值为x的元素前面插入值为e的元素(按值)
- 删除操作:删除双链表D中位序为i的元素(按位序)
- 删除操作:删除双链表中值为x的元素(按值)
- 后插操作:往双链表D的第i位后面插入一个值为e的元素(按位序)
- 后插操作:往双链表D的第i位后面插入一个值为e的元素(按值)
三、具体代码实现
需要的头文件
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
#include<assert.h>
#define Elemtype int
1.定义存储结构
//1.定义存储结构
typedef struct LNode{
Elemtype data; //数据域
struct LNode *pre, *next; //指针域
}DLnode, *Double_link;
2.基本运算的代码实现
//2.初始化双链表
bool Init_Double_link(Double_link* D){
(*D) = (Double_link)malloc(sizeof(DLnode));
(*D)->data = 0;
(*D)->pre = NULL;
(*D)->next = NULL;
return true;
}
//3.求双链表D的长度(元素个数)
int Count_length(Double_link D){
int length = 0;
while (D->next != NULL){
length++;
D = D->next;
}
return length;
}
//4.(1)前插操作:往双链表D中第i位(前)插入值为e的元素(按位序)
bool Insert_Dlist_e(Double_link* D, const int i, Elemtype e){
//检查D是否存在
assert(D);
//判断插入位置i的合法性
if (i < 1 || i > (Count_length(*D) + 1)) return false;
//新建节点并初始化
Double_link S = (Double_link)malloc(sizeof(DLnode));
S->data = e;
S->next = NULL;
S->pre = NULL;
Double_link P = (*D);
//单独处理双链表为空情况
if (P->next == NULL){
P->next = S;
S->pre = P;
}
else{
//找到第i-1位
for (int j = 1; j < i; j++){
P = P->next;
}
//此时P指向第i-1位
//处理正常情况(包括在末尾插入元素的情况),至少存在一个节点的情况
if (i != Count_length(*D) + 1){
P->next->pre = S;
S->next = P->next;
}
P->next = S;
S->pre = P;
}
return true;
}
//4.(2)前插操作:往双链表D中值为x的元素前面插入值为e的元素(按值)
bool Insert_Dlist_x(Double_link* D, Elemtype x, Elemtype e){
assert(*D);
Double_link P = (*D);
//创建新节点
Double_link S = (Double_link)malloc(sizeof(DLnode));
S->data = e;
S->pre = NULL;
S->next = NULL;
//单独处理双链表为空情况
if (P->next == NULL){
P->next = S;
S->pre = P;
}
else{
//找到值为x的前驱
while (P->next != NULL && P->next->data != x){
P = P->next;
}
if (!(P->next)) return false;
if (P->next){
P->next->pre = S;
S->next = P->next;
}
P->next = S;
S->pre = P;
}
return true;
}
//5.打印输出双链表D中的全部元素
void Print_Double_link(Double_link D){
Double_link P = D->next;
while (P != NULL){
printf("%d ", P->data);
P = P->next;
}
printf("\n");
}
//6.(1)按位序删除:删除双链表D中位序为i的元素
bool Delete_i(Double_link* D, const int i){
//判断i的合法性
if (i<1 || i>Count_length(*D)) return false;
int j = 0;
Double_link P = (*D), T;
//找到第i-1位
for (j = 1; j < i; j++){
P = P->next;
}
T = P->next; //指向第i位元素
if (T->next != NULL){
T->next->pre = P;
}
P->next = T->next;
free(T);
return true;
}
//6.(2)按值删除:删除双链表中值为x的元素
bool Delete_x(Double_link* D, Elemtype x){
Double_link P = (*D),T;
//找到值为x的前驱节点
while (P->next != NULL && P->next->data != x){
P = P->next;
}
//此时P指向 1:值为x的节点的前驱;2:最后一个元素(即、链表中不存在值为x的元素)
if (!(P->next)) return false;
T = P->next;//指向值为x的节点(元素)
if (T->next){ // T->next != NULL
T->next->pre = P;
}//这个if条件是处理当最后一个元素值为x时的情形(T->next==NULL)
P->next = T->next;
free(T);
return true;
}
//7.(1)后插操作:往双链表D的第i位后面插入一个值为e的元素(按位序)
bool Insert_nex_i(Double_link* D, const int i, Elemtype e){
assert(*D);
//判断i的合法位置
int len = Count_length(*D);
if (i<1 || i>len) return false;
//链表为空则终止
if (len == 0) return false;
//创建新节点
Double_link S = (Double_link)malloc(sizeof(DLnode));
S->data = e;
S->next = NULL;
S->pre = NULL;
Double_link P = (*D);
//找到第i位
for (int j = 0; j < i; j++){
P = P->next;
}
S->next = P->next;
if (P->next){
P->next->pre = S;
}
P->next = S;
S->pre = P;
return true;
}
//7.(2)后插操作:往双链表D的第i位后面插入一个值为e的元素(按值)
bool Insert_nex_x(Double_link* D, Elemtype x, Elemtype e){
assert(*D);
//创建新节点
Double_link S = (Double_link)malloc(sizeof(DLnode));
S->data = e;
S->next = NULL;
S->pre = NULL;
Double_link P = (*D)->next;
if (!P) return false;
//找到值为x的元素
while (P != NULL && P->data != x){
P = P->next;
}
S->next = P->next;
if (P->next){
P->next->pre = S;
}
P->next = S;
S->pre = P;
return true;
}
3.main函数
//带头节点的双链表的实现
int main(){
//1.声明变量
Double_link D;
//2.初始化双链表
Init_Double_link(&D);
//3.求双链表长度
printf("%d \n", Count_length(D));
//4.(1)前插操作(按位序)
Insert_Dlist_e(&D, 1, 4);//头插
printf("%d \n", Count_length(D));
Insert_Dlist_e(&D, 2, 14);//尾插
printf("%d \n", Count_length(D));
Insert_Dlist_e(&D, 3, 24);//尾插
printf("%d \n", Count_length(D));
Insert_Dlist_e(&D, 2, 34);
printf("%d \n", Count_length(D));
Insert_Dlist_e(&D, 3, 44);
printf("%d \n", Count_length(D)); //5
Print_Double_link(D);//4 34 44 14 24
//4.(2)前插操作(按值)
if (Insert_Dlist_x(&D, 14, 55)){
printf("%d \n", Count_length(D)); //6
}
//5.打印操作
Print_Double_link(D);//4 34 44 55 14 24
//6.删除操作
//(1)按位删除
Delete_i(&D,3);
printf("%d \n", Count_length(D)); //5
Print_Double_link(D); //4 34 55 14 24
//(2)按值删除
if (Delete_x(&D, 4)){
printf("%d \n", Count_length(D)); //4
Print_Double_link(D); //34 55 14 24
}
//7.(1)后插操作(按位序)
Insert_nex_i(&D, 3, 23);
Print_Double_link(D); //34 55 14 23 24
//7.(2)后插操作(按值)
Insert_nex_x(&D, 34,32);
Print_Double_link(D); //34 32 55 14 23 24
return 0;
}
4.运行结果
0
1
2
3
4
5
4 34 44 14 24
6
4 34 44 55 14 24
5
4 34 55 14 24
4
34 55 14 24
34 55 14 23 24
34 32 55 14 23 24
请按任意键继续. . .
作者:QuestoQ
欢迎大家交流指正。