如图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 */