一、初始操作
尾插法创建:
#include <stdio.h>
#include <stdlib.h>
// 定义双向链表节点结构体
typedef struct DLnode {
int data;
struct DLnode *prev;
struct DLnode *next;
} DLnode, *DLinklist;
// 创建并初始化双向链表
void Create(DLinklist *L) {
*L = (DLinklist)malloc(sizeof(DLnode)); // 创建头节点
if (*L == NULL) {
printf("内存分配失败!\n");
return;
}
(*L)->prev = NULL; // 头节点的前驱为NULL
(*L)->next = NULL; // 初始时头节点没有后继
DLnode *p = *L; // p用于遍历链表
int num, n;
printf("请输入要创建的个数:");
scanf("%d", &num);
for (int i = 1; i <= num; i++) {
DLnode *s = (DLinklist)malloc(sizeof(DLnode)); // 创建新节点
if (s == NULL) {
printf("内存分配失败!\n");
return; // 提前退出,防止后续操作
}
printf("请输入第%d个元素的值:", i);
scanf("%d", &n);
s->data = n;
s->prev = p; // 新节点的前驱指向当前最后一个节点
p->next = s; // 当前最后一个节点的后继指向新节点
p = s; // 移动p到新节点
}
printf("创建成功!\n");
}
int main() {
DLinklist L = NULL;
Create(&L);
// 这里可以添加代码来遍历链表或进行其他操作
return 0;
}
二、双向链表的插入
整体代码:
// 插入函数
int Insert(DLinklist *L, int n, int data) {
DLinklist s, p;
// 检查链表是否为空或位置无效
if (*L == NULL || n < 1) {
printf("链表为空或位置错误!");
return -1;
}
// 尝试找到第 n-1 个节点
p = Find(*L, n - 1);
if (p == NULL) {
// 如果找不到,则在链表末尾插入(假设 n 大于当前链表长度)
s = (DLinklist)malloc(sizeof(DLnode));
if (s == NULL) {
printf("内存分配失败!");
return -1;
}
s->data = data;
s->prev = (*L)->prev; // 链表末尾的 prev 指向头节点的前一个位置(通常是 NULL)
if ((*L)->prev != NULL) { // 如果链表有多个节点
(*L)->prev->next = s;
}
s->next = *L;
(*L)->prev = s; // 更新头节点的 prev 指针
if (*L == *L->next) { // 如果是单节点链表
*L = s; // 新节点成为头节点
}
} else {
// 如果找到了第 n-1 个节点,则在其后插入新节点
s = (DLinklist)malloc(sizeof(DLnode));
if (s == NULL) {
printf("内存分配失败!");
return -1;
}
s->data = data;
s->prev = p;
s->next = p->next;
if (p->next != NULL) {
p->next->prev = s;
}
p->next = s;
if (p == (*L)->prev) { // 如果插入到了链表末尾
(*L)->prev = s;
}
}
printf("插入成功!");
return 1;
}
对于这个插入操作的执行顺序个人当初想的与各个教材的有所不同;
教材参考代码:
①:s->prev=p->prev;
②: p->prev->next=s;
③: s->next=p;
④:p->prev=s;
个人觉得对于一个新节点首先要确定它本身的前后指针,应该先进行1,3操作改变新节点的前后指针.
然后在进行2操作最后进行4操作;但是这两个顺序是不能改变的。
原因:4操作是将p的prev指针指向s,如果先进行4则前驱结点的next值将无法操作!
三、双向链表的删除
整体代码:
//双向链表的删除
void Delate(DLinklist &L){
DLinklist s;
int num;
printf("请输入要删除结点的位置:");
scanf("%d",&num);
if(num!=NULL){
s=Find(L,num) ; //函数找到删除节点的序号
s->prev->next=s->next;
s->next->prev=s->prev;
free(s);
printf("删除成功!");
}
}
代码:
①:p->prev->next=p->next;
②:p->next->prev=p->prev;
此顺序可以交换。
四、总结
双向链表由于有一个前驱指针,所以方便了许多单链表的操作,例如:插入和删除,不需要申请过多的指针来进行前驱结点的遍历,但是额外增加了空间上的负担,有利有弊,酌情处理。