博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
【线性表6】线性表的链式实现:双向链表
阅读量:5132 次
发布时间:2019-06-13

本文共 5816 字,大约阅读时间需要 19 分钟。

简介

在单链表中,我们只能向一个方向(向后)访问结点:即从索引小的到索引大的方向,而不能逆向访问,例如通过第3个结点的指针,无法直接得到第2个结点的地址。这是因为单链表的每一个结点只保存了下一个结点的地址。
而双向链表中,结点有2个指针域:prior 和 next。next同样保存下一个结点的指针,而结点新增的成员prior则用来保存前一个结点的地址。
 

 

因此,双向链表在访问元素操作上比单链表快。为什么?例如,链表总长度为100,在单链表中,要访问索引为第98的结点(倒数第二个),我们必须从索引为0的结点开始向后迭代寻。而在双向链表中,我们先通过逻辑判断:索引为98的结点,是更靠近首结点(索引为0),还是更靠近尾结点(索引为99)?这里当然是更靠近尾结点,则从尾结点开始向前迭代。

 

对于双向链表,他的缺点也很明显:

1、比单链表更占空间,因为每个结点多了一个指针域。

2、操作复杂。

但这不影响它的使用,从实际应用的角度看,他的优点大于他的缺点。例如在Java集合框架中,LinkedList就是一个双向链表。

 

结点的定义

struct DLNode    //双向链表的结点定义{    int element;      //数据域    DLNode* prior     //指针域:保存前一个结点的指针    DLNode* next;     //指针域:保存后一个结点的指针        ListNode(int e=0,DLNode* pri=0,DLNode* nxt = 0):element(e),prior(pri),next(nxt)    {    }    };

 

 

 

代码实现

初始化状态

 

 

#include
#include
#include
using namespace std;struct DLNode //双向链表的结点定义{ int element; //数据域 DLNode* prior; //指针域:保存前一个结点的指针 DLNode* next; //指针域:保存后一个结点的指针 DLNode(int e=0,DLNode* pri=0,DLNode* nxt = 0):element(e),prior(pri),next(nxt) { } };class DLinkList{private: DLNode headNode; //头结点 DLNode * pLastNode; //尾结点指针 int size; //长度 //获取双向链表中,索引为index 的结点的地址。 //如果不存在此结点,则返回NULL DLNode* _getNodeAt(int index) const { DLNode*p; int i; if(index < 0 || index >=size ) return 0; //返回NULL if( index < size /2) //从前向后寻找更快到达 { for(i=0,p=headNode.next ; i
next){ // NOOP } return p; } else { //从后向前 寻找更快到达 for(i=size-1,p = pLastNode; i>index ;i--,p=p->prior){ // NOOP } return p; } }public: DLinkList():headNode(),pLastNode(&headNode),size(0) { /* * * ************************* * * * * * * * prior * x * next * * * (NULL) * *(NULL) * * ************************* * / \ headNode * / * / * / * pLastNode = &headNode * * size = 0 * ********/ } ~DLinkList() { clear(); } //追加一个元素到表末尾 void append(int e) { DLNode * new_node = new DLNode(e,pLastNode,0); //新结点向前连接 pLastNode->next = new_node; //最后一个结点向后连接新的尾结点 pLastNode = new_node; //更新最后一个结点指针 size++; } bool insert(int index,int e) { DLNode*new_node; DLNode*p = _getNodeAt(index-1); //获取待插入位置的前一个结点地址 if(p==0) return false; new_node = new DLNode(e,p,p->next); if(p->next ==0) pLastNode = new_node; //如果新结点是插入为最后一个结点,则更新pLastNode的值 else p->next->prior = new_node; p->next = new_node; size++; return true; } bool remove(int index) { DLNode*del_node; DLNode*p = _getNodeAt(index-1); //获取待删除结点的前一个结点地址 if(p==0 || p->next ==0) return false; del_node = p->next; p->next = p->next->next; if(p->next->next == 0){ //说明被删除的是最后一个结点 pLastNode = p; }else{ p->next->next->prior = p; } delete del_node; size--; return true; } size_t length ()const { return size; } bool isEmpty()const { return size == 0; } int indexOf(int e) const { DLNode *p = headNode.next; int i=0; while(p!=0){ if(p->element == e) return i; i++; p=p->next; } return -1; } /* * 功能:删除所有的数据结点,清空表 */ void clear() { DLNode*p = headNode.next; DLNode*t; while(p!=0){ t = p; p=p->next; delete t; } //回归初始状态 headNode.next = 0; pLastNode = &headNode; size = 0; } void show()const { DLNode*p = headNode.next; cout<<'['; while(p!=0) { if(p!=headNode.next) cout<<','; cout<
element; p = p->next; } cout<<']'; } int operator[](int index) const { DLNode*p = _getNodeAt(index); if(p==0) throw std::out_of_range(0); return p->element; } int& operator[](int index) { DLNode*p = _getNodeAt(index); if(p==0) throw std::out_of_range(0); return p->element; } };int main(){ DLinkList list; for(int i=0;i<10;++i) list.append(i); list.show(); cout<<'\n'; list.remove(3); list.remove(3); list[0] = 101; list.show(); cout<<'\n'; return 0;}

 

 

总结

单链表 vs 双向链表:双向链表由于结点多了一个指针域,因此空间占用量比单链表高,但是结点的访问时间却可以加快了,因为我们可以选择最近的迭代查找方向,从尾结点向前,或者从头结点向后。这是一个用空间换时间的例子。实际开发中,双向链表更加通用,强大。

DLNode* _getNodeAt(int index) const{        DLNode*p;        int i;        if(index < 0 || index >=size ) return 0;   //索引不合法,返回NULL                if( index  < size /2) //从前向后寻找更快到达        {            for(i=0,p=headNode.next    ;  i
next){ // NOOP } return p; } else { //从后向前 寻找更快到达 for(i=size-1,p = pLastNode; i>index ;i--,p=p->prior){ // NOOP } return p; } }

 

 

 

 

 

 

转载于:https://www.cnblogs.com/lulipro/p/7425170.html

你可能感兴趣的文章
Linux 查看系统所有用户
查看>>
实验三——for 语句及分支结构else-if
查看>>
国家哲学社会科学文献中心
查看>>
把一些表单属性封装一个JSON
查看>>
module
查看>>
Linux平台下卸载MySQL的方法
查看>>
ubuntu下安装Docker
查看>>
CF1152 F. Neko Rules the Catniverse (dp)
查看>>
全国行政区域数据库
查看>>
8_19 比赛总结 [暑假集训]
查看>>
GET与POST的区别
查看>>
花容月貌
查看>>
Spring Roo 之 Mysql 连接
查看>>
Spring_Mybatis整合实现CRUD操作
查看>>
Android学习第六天---seekbar
查看>>
C++ lambda
查看>>
div中显示页面
查看>>
C向C++改造
查看>>
[洛谷P5367]【模板】康托展开
查看>>
Redis部分数据结构方法小结
查看>>