如图4.8所示,表中的各元素称作“结点”。双向链表的结点是结构体,由数据本体(这里是整数key)、指向前一元素的指针prev以及指向后一元素的指针next组成。这些结构体通过指针连接成一个链,就形成了双向链表。
program4.3 C++双向链表的结点
strcut Node {
int key;
Node *prev, *next;
};
另外,在表头设置一个特殊的结点可以简化链表的实现。我们将这个结点称为“头结点”。头结点中虽然不包含实际数据,但他可以让我们更轻松的对链表做更改。比如加入头结点之后,我们将更容易实现删除元素的操作。
init函数用于初始化链表。如图4.9所示,它会生成一个NIL结点作为链表的头结点,然后让prev和next都指向这个结点,从而创造一个空表。
program4.4 初始化双向链表
Node *nil;
void init() {
nil = (Node *)malloc(sizeof(Node));
nil->next = nil;
nil->prev = nil;
}
头结点是添加元素的起点。这里的malloc是C语言标准库中的函数,用于动态申请指定大小的空间。另外,“->”是通过指针变量访问成员的运算符,称为箭头运算符。
insert函数用于生成包含所输入键值的结点,并将该结点添加到标的开头。如图4.10所示这个函数会以头结点为起点分四步改变指针所指的方向。
program4.5 往双向表中插入元素
void insert(int key) {
Node *x = (Node *)malloc(sizeof(Node));
x->key = key;
//在头结点后添加元素
x->next = nil->next;
nil->next->prev = x;
nil->next = x;
x->prev = nil;
}
listSearch函数用于搜索元素,它可以在链表中寻找包含指定键值的结点,并返回其指针。假设cur为指向当前位置结点的指针,只要从头结点的next所指的结点,即链表开头的元素开始逐个执行cur = cur -> next,即可逐一访问每个结点。
program4.6 在双向链表中搜索元素
Node* listSearch(int key) {
Node *cur = nil->next; //从头结点后面的元素开始访问
while( cur != nil && cur->key != key) {
cur = cur->next;
}
retrun cur;
}
在访问过程中发现key或者指针回到头结点NIL时结束搜索,并返回此时cur的值。
deleteNode函数会通过如图4.11所示的步骤改变指针所指的位置,从而删除指定的结点t。在C++中,我们必须手动释放已删除结点的内存。这里的free时C语言标准库中的函数,用于释放已不需要的内存空间。
program4.7 从双向链表中删除元素
void deleteNode(Node *t) {
if( t == nil ) return;
t->prev->next = t->next;
t->next->prev = t->prev;
free(t);
}
void deleteFirst() {
deleteNode( nil->next );
}
void deleteLast() {
deleteNode( nil->prev );
}
void deleteKey(int key) {
//删除已搜索到的结点
deleteNode( listSearch(key));
}
例题;
代码:
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <iostream>
using namespace std;
struct Node {
int key;
Node *next,*prev;
};
Node *nil;
void init() { //初始化
nil = (Node *)malloc(sizeof(Node));
nil->next = nil;
nil->prev = nil;
}
Node* listSearch(int key) { //找出含有键值key的结点
Node *cur = nil->next;
while( cur != nil && cur->key != key) {
cur = cur->next;
}
return cur;
}
void insert(int key) { //在头节点后插入某个元素
Node *x = (Node *)malloc(sizeof(Node));
x->key = key;
x->next = nil->next;
nil->next->prev = x;
nil->next = x;
x->prev = nil;
}
void deleteNode(Node *t) { //删除结点 被下面调用函数 调用
if( t == nil) return;
t->prev->next = t->next;
t->next->prev = t->prev;
free(t); //释放已经不需要的内存空间
}
void deleteFirst() { //删除头结点next所指向的结点
deleteNode(nil->next);
}
void deleteLast() { //删除头结点prev所指向的结点
deleteNode(nil->prev);
}
void deleteKey(int key) {
deleteNode(listSearch(key)); //删掉搜索到的结点
}
void printList() { //打印链表
Node *cur = nil->next;
int isf = 0;
while(1) {
if( cur == nil ) break;
if(isf++ > 0) printf(" ");
printf("%d",cur->key);
cur = cur->next;
}
printf("\n");
}
int main()
{
int n,key,i;
char com[20];
cin>>n;
init();
int size = 0,np = 0,nd = 0;
for( i=0; i<n; i++ ) {
scanf("%s%d",com,&key);
if( com[0] == 'i') { insert(key); np++; size++;}
else if( com[0] == 'd') {
if( strlen(com) > 6) {
if( com[6] == 'F') deleteFirst();
else if( com[6] == 'L') deleteLast();
} else {
deleteKey(key); nd++;
}
size--;
}
}
printList();
return 0;
}
/*
7
insert 5
insert 2
insert 3
insert 1
delete 3
insert 6
delete 5
*/