双链表-带头结点
- 1.头文件及类型定义
- 2.双链表结点类型定义
- 3.函数声明
- 4.基本操作
- 4.1 初始化双链表
- 4.2 判空
- 4.3 查找操作
- 4.4 插入操作
- 4.4.1指定结点插入
- 4.4.1.1后插
- 4.4.1.1.1 给结点
- 4.4.1.1.2 给元素值
- 4.4.1.2 前插
- 4.4.2 按位插入
- 4.5 删除操作
- 4.6 创建双链表
- 4.7 销毁双链表
- 4.8 求表长
- 4.9 遍历
- 4.10 main函数
- 5.小结
1.头文件及类型定义
#include<stdio.h>
#include<stdlib.h>
#define ElemType int
2.双链表结点类型定义
typedef struct DNode {
ElemType data;
struct DNode* prior, * next;
}DNode, * DLinkList;
3.函数声明
DLinkList InitDLinkList(DLinkList& L);
bool Empty(DLinkList L);
DNode* GetElem(DLinkList L, int i);
DNode* LocateElem(DLinkList L, ElemType e);
bool InsertNextDNode1(DNode* p, DNode* s);
bool InsertNextDNode2(DNode* p, ElemType e);
bool InsertPriorDNode(DNode* p, DNode* s);
bool InsertDLinkList(DLinkList& L, int i, ElemType e);
bool DeleteNextDNode(DNode* p);
DLinkList List_HeadInsert(DLinkList& L);
void DestoryList(DLinkList& L);
int Length(DLinkList L);
void PrintDLinkList(DLinkList L);
4.基本操作
4.1 初始化双链表
DLinkList InitDLinkList(DLinkList& L) {
L = (DNode*)malloc(sizeof(DNode));
if (L == NULL)
return NULL;
L->prior = NULL;
L->next = NULL;
return L;
}
4.2 判空
bool Empty(DLinkList L) {
return (L->next == Null);
}
4.3 查找操作
4.3.1 按位查找
DNode* GetElem(DLinkList L, int i) {
if (i < 0)
return NULL;
int j = 0;
DNode* p = L;
while (p != NULL && j < i) {
p = p->next;
j++;
}
return p;
}
4.3.2 按值查找
DNode* LocateElem(DLinkList L, ElemType e) {
DNode* p = L->next;
while (p->data != e) {
p = p->next;
}
return p;
}
4.4 插入操作
4.4.1指定结点插入
4.4.1.1后插
4.4.1.1.1 给结点
bool InsertNextDNode1(DNode* p, DNode* s) {
if (p == NULL || s == NULL)
return false;
DNode* q = p->next;
s->next = q;
if (p->next != NULL)
q->prior = s;
s->prior = p;
p->next = s;
return true;
}
4.4.1.1.2 给元素值
bool InsertNextDNode2(DNode* p, ElemType e) {
if (p == NULL)
return false;
DNode* q = p->next;
DNode* s = (DNode*)malloc(sizeof(DNode));
if (s == NULL)
return false;
s->data = e;
s->next = q;
if(q!=NULL)
q->prior = s;
s->prior = p;
p->next = s;
return true;
}
4.4.1.2 前插
bool InsertPriorDNode(DNode* p, DNode* s) {
DNode* q = p->prior;
return InsertNextDNode1(q, s);
}
4.4.2 按位插入
bool InsertDLinkList(DLinkList& L, int i, ElemType e) {
DNode* p = GetElem(L, i-1);
return InsertNextDNode2(p, e);
}
4.5 删除操作
bool DeleteNextDNode(DNode* p) {
if (p == NULL)
return false;
DNode* q = p->next;
if (q == NULL)
return false;
p->next = q->next;
if (q->next != NULL)
q->next->prior = p;
free(q);
return true;
}
4.6 创建双链表
DLinkList List_HeadInsert(DLinkList& L) {
L = InitDLinkList(L);
ElemType x;
int i = 1;
printf("开始创建双链表!\n请输入%d个元素:", i);
scanf("%d", &x);
while (x != 0) {
if (InsertNextDNode2(L, x))
printf("成功插入第%d个元素:%d\n", i, x);
else
printf("插入第%d个元素失败!\n", i);
printf("请输入%d个元素:", ++i);
scanf("%d", &x);
}
printf("双链表创建完成!\n");
return L;
}
4.7 销毁双链表
void DestoryList(DLinkList& L) {
printf("开始销毁双链表!\n");
while (L->next != NULL)
DeleteNextDNode(L);
free(L);
L = NULL;
}
4.8 求表长
int Length(DLinkList L) {
int len = 0;
DNode* p = L->next;
while (p != NULL) {
p = p->next;
len++;
}
return len;
}
4.9 遍历
void PrintDLinkList(DLinkList L) {
DNode* p = L->next;
printf("遍历双链表:\n");
while (p != NULL) {
printf("%d\t", p->data);
p = p->next;
}
printf("\n");
}
4.10 main函数
int main() {
DLinkList L;
int i;
L = List_HeadInsert(L);
printf("当前表长为:%d\n", Length(L));
PrintDLinkList(L);
ElemType e1;
printf("请输入您所要插入的位序i和元素值e:");
scanf("%d%d", &i, &e1);
if (InsertDLinkList(L, i, e1))
printf("您已成功插入元素值:%d\n", e1);
else
printf("位序i不合法,插入元素值%d失败!\n", e1);
PrintDLinkList(L);
printf("请输入您要查找的位序i:");
scanf("%d", &i);
DNode* p1 = GetElem(L, i);
printf("位序为%d的值为:%d\n", i, p1->data);
ElemType e2;
printf("请输入您要查找的元素值e:");
scanf("%d", &e2);
DNode* p2 = LocateElem(L, e2);
DNode* p3 = (DNode*)malloc(sizeof(DNode));
p3->data = p2->data;
if (InsertPriorDNode(p1, p3)) {
printf("前插成功!\n");
DeleteNextDNode(p2->prior);
}
else
printf("前插失败!\n");
PrintDLinkList(L);
ElemType e3 = p1->next->data;
if (DeleteNextDNode(p1))
printf("此结点后继结点删除成功,值为:%d\n", e3);
else
printf("此结点后继结点删除失败!");
PrintDLinkList(L);
DestoryList(L);
if (L == NULL)
printf("双链表已销毁!\n");
else
printf("双链表销毁失败!\n");
return 0;
}
5.小结
- 说明
本文仅用带头结点的双链表进行测试,不带头结点的双链表可参考不带头结点的单链表,基本操作大多相似。
- 要注意的点
在双链表中的后插操作和指定结点的后继结点的删除操作中,要特别注意边界情况的处理:若插入结点或被删除结点是最后一个结点,其后继结点为NULL,故前驱指针不可赋值(因为就不存在),需要进行判断。
当然,在引入循环双链表之后这个问题可以避免(因为不存在后继结点不存在的情况)。