双向链表
1 双向链表的定义
在双链表中,由于每个结点既包含一个指向后继结点的指针,又包含一个指向前驱结点的指针,所以当访问过一个结点后既可以依次向后访问每一个结点,也可以依次向前访问每一个结点。因此与单链表相比,双链表中访问一个结点的前、后结点更方便。
typedef struct DNode {
ElemType data;
struct DNode* prior, * next;
}DNode, *DLinkList;
2 线性表基本运算在双链表中的实现
2.1 建立双链表
整体建立双链表有两种方法,即头插法和尾插法。
2.1.1 采用头插法建立双链表
void DList_HeadInsert(DLinkList& L, int n) {
L = (DLinkList)malloc(sizeof(DNode));
L->prior = NULL;
L->next = NULL;
for (int i = n; i > 0; i--) {
DLinkList s = (DLinkList)malloc(sizeof(DNode));
ElemType x;
cin >> x;
s->data = x;
s->next = L->next;
if (L->next != NULL) {
L->next->prior = s;
}
L->next = s;
s->prior = L;
}
}
2.1.2 采用尾插法建立双链表
void DList_TailInsert(DLinkList& L, int n) {
L = (DLinkList)malloc(sizeof(DNode));
L->prior = NULL;
L->next = NULL;
DNode* r = L;
for (int i = n; i > 0; i--) {
DLinkList s = (DLinkList)malloc(sizeof(DNode));
ElemType x;
cin >> x;
s->data = x;
r->next = s;
s->prior = r;
r = s;
}
r->next = NULL;
}
2.2 插入数据元素
在双链表L
中的第i个位置上插入值为e
的结点时采用类似单链表的插入过程,先查找到第i-1
个结点(由p
指向它),然后在p
所指结点之后插入一个新结点。
bool DListInsert(DLinkList& L, int i, ElemType e) {
DLinkList p = L;
if (i <= 0) {
return false;
}
int j = 0;
while (j < i - 1 && p != NULL) {
j++;
p = p->next;
}
if (p == NULL) {
return false;
}else{
DLinkList s = (DLinkList)malloc(sizeof(DNode));
s->data = e;
s->next = p->next;
if (p->next != NULL) {
p->next->prior = s;
}
s->prior = p;
p->next = s;
return true;
}
}
本算法的时间复杂度为O(n)
,其中n
为双链表中数据结点的个数。
2.3 删除数据元素
在双链表中删除第i
个结点时采用类似单链表的删除过程,先查找到第i-1
个结点p
,然后删除结点p
的后继结点。
bool DListDelete(DLinkList& L, int i, ElemType& e) {
DLinkList p = L;
if (i <= 0) {
return false;
}
int j = 0;
while (j < i - 1 && p != NULL) {
j++;
p = p->next;
}
if (p == NULL) {
return false;
} else {
DNode* q = p->next;
if (q == NULL) {
return false;
} else {
e = q->data;
p->next = q->next;
if (q->next != NULL) {
q->next->prior = p;
}
}
free(q);
return true;
}
}
本算法的时间复杂度为O(n)
,其中n
为双链表中数据结点的个数。
由于在双链表中通过一个结点既可以找到它的前驱结点,又可以找到它的后继结点,所以在双链表L
中实现插入和删除第i
个结点时可以先查找第i
个结点(由p
指向它),插入时在p
所指结点的前面插入一个新结点,删除时通过p
所指结点的前驱结点来删除它。
3 上机实验
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
typedef int ElemType;
typedef struct DNode {
ElemType data;
struct DNode* prior, * next;
}DNode, *DLinkList;
void DList_HeadInsert(DLinkList& L, int n) {
L = (DLinkList)malloc(sizeof(DNode));
L->prior = NULL;
L->next = NULL;
for (int i = n; i > 0; i--) {
DLinkList s = (DLinkList)malloc(sizeof(DNode));
ElemType x;
cin >> x;
s->data = x;
s->next = L->next;
if (L->next != NULL) {
L->next->prior = s;
}
L->next = s;
s->prior = L;
}
}
void DList_TailInsert(DLinkList& L, int n) {
L = (DLinkList)malloc(sizeof(DNode));
L->prior = NULL;
L->next = NULL;
DNode* r = L;
for (int i = n; i > 0; i--) {
DLinkList s = (DLinkList)malloc(sizeof(DNode));
ElemType x;
cin >> x;
s->data = x;
r->next = s;
s->prior = r;
r = s;
}
r->next = NULL;
}
bool DListInsert(DLinkList& L, int i, ElemType e) {
DLinkList p = L;
if (i <= 0) {
return false;
}
int j = 0;
while (j < i - 1 && p != NULL) {
j++;
p = p->next;
}
if (p == NULL) {
return false;
}else{
DLinkList s = (DLinkList)malloc(sizeof(DNode));
s->data = e;
s->next = p->next;
if (p->next != NULL) {
p->next->prior = s;
}
s->prior = p;
p->next = s;
return true;
}
}
bool DListDelete(DLinkList& L, int i, ElemType& e) {
DLinkList p = L;
if (i <= 0) {
return false;
}
int j = 0;
while (j < i - 1 && p != NULL) {
j++;
p = p->next;
}
if (p == NULL) {
return false;
} else {
DNode* q = p->next;
if (q == NULL) {
return false;
} else {
e = q->data;
p->next = q->next;
if (q->next != NULL) {
q->next->prior = p;
}
}
free(q);
return true;
}
}
void DisplayDList(DLinkList L) {
DLinkList p = L->next;
while (p != NULL) {
cout << p->data << " ";
p = p->next;
}
cout << endl;
}
int main() {
DLinkList L;
DList_HeadInsert(L, 6);
DisplayDList(L);
DListInsert(L, 2, 9999);
DisplayDList(L);
ElemType e;
cout<<DListDelete(L, 2, e)<<endl;
cout << e << endl;
DisplayDList(L);
cout << endl;
DList_TailInsert(L, 6);
DisplayDList(L);
DListInsert(L, 2, 8888);
DisplayDList(L);
cout << DListDelete(L, 2, e) << endl;
cout << e << endl;
DisplayDList(L);
return 0;
}