本文双链表介绍部分参考自博文数组、单链表和双链表介绍 以及 双向链表的C/C++/Java实现

  1 双链表介绍

  双向链表(双链表)是链表的一种。和单链表一样,双链表也是由节点组成,它的每个数据结点中都有两个指针,分别指向直接后继和直接前驱。所以,从双向链表中的任意一个结点开始,都可以很方便地访问它的前驱结点和后继结点。一般我们都构造双向循环链表。

  双链表的示意图如下:

  

  表头为空,表头的后继节点为"节点10"(数据为10的节点);"节点10"的后继节点是"节点20"(数据为10的节点),"节点20"的前继节点是"节点10";"节点20"的后继节点是"节点30","节点30"的前继节点是"节点20";...;末尾节点的后继节点是表头。

   1.1 双链表添加节点

  

  在"节点10"与"节点20"之间添加"节点15"
  添加之前:"节点10"的后继节点为"节点20","节点20" 的前继节点为"节点10"。
  添加之后:"节点10"的后继节点为"节点15","节点15" 的前继节点为"节点10"。"节点15"的后继节点为"节点20","节点20" 的前继节点为"节点15"。

  对于“添加节点”功能,在具体实现的时候,我们可以分为三种情况(个人觉得这样逻辑比较清晰,当然有些代码是可以合并的。):

  1)在链表第0位置添加。关键代码如下:

 NodePointer ptr = new Node(), prePtr, postPtr;
// assert(ptr != NULL);
ptr->data = val;
if (pos == ) // insert node right behind 'head'
{
postPtr = head->next;
head->next = ptr;
postPtr->pre = ptr; ptr->pre = head;
ptr->next = postPtr;
}

  2)在链表最后添加结点(即第len位置,其中len为链表原长度)。关键代码如下:

 ...
if (pos == ) // insert node right behind 'head'
{
...
}
else if (pos == len) // append node
{
prePtr = head->pre;
head->pre = ptr;
prePtr->next = ptr; ptr->pre = prePtr;
ptr->next = head;
}

  3)在链表的中间结点间添加结点。关键代码如下:

 ...
if (pos == ) // insert node right behind 'head'
{
...
}
else if (pos == len) // append node
{
...
}
else // others
{
prePtr = head->next;
int count = ;
while (prePtr != head && count < pos - )
{
prePtr = prePtr->next;
count++;
}
postPtr = prePtr->next; ptr->next = postPtr;
ptr->pre = prePtr;
prePtr->next = ptr;
postPtr->pre = ptr;
}

   1.2 双链表删除节点

  

  删除"节点30"
  删除之前:"节点20"的后继节点为"节点30","节点30" 的前继节点为"节点20"。"节点30"的后继节点为"节点40","节点40" 的前继节点为"节点30"。
  删除之后:"节点20"的后继节点为"节点40","节点40" 的前继节点为"节点20"。

  对于“添加节点”功能,在具体实现的时候,我们可以分为三种情况:

  1)删除链表第1个结点。关键代码如下:

 NodePointer ptr, prePtr, postPtr;
if (pos == ) // delete the first node
{
ptr = head->next;
postPtr = ptr->next; head->next = postPtr;
postPtr->pre = head;
delete ptr;
}

  2)删除链表最后一个结点。关键代码如下:

 ...
if (pos == ) // delete the first node
{
...
}
else if (pos == len - ) // delete the last one
{
ptr = head->pre;
prePtr = ptr->pre; prePtr->next = head;
head->pre = prePtr;
delete ptr;
}

  3)删除链表的中间结点。关键代码如下:

 ...
if (pos == ) // delete the first node
{
...
}
else if (pos == len - ) // delete the last one
{
...
}
else // others
{
ptr = head->next;
int count = ;
while (ptr != head && count < pos)
{
ptr = ptr->next;
count++;
}
prePtr = ptr->pre;
postPtr = ptr->next; prePtr->next = postPtr;
postPtr->pre = prePtr;
delete ptr;
}

  2. 代码实现

  在这里,我设计的类如下:

 #ifndef DOUBLELINKEDLIST
#define DOUBLELINKEDLIST #include <iostream>
#include <cassert>
using namespace std; typedef int ElementType;
class Node
{
public:
ElementType data;
Node * pre;
Node * next;
};
typedef Node * NodePointer; class LinkedList
{
public:
LinkedList();
virtual ~LinkedList();
LinkedList(const LinkedList& orig);
LinkedList& operator=(const LinkedList& orig);
bool isEmpty();
bool addNode(const int pos, const ElementType val);
bool deleteNode(const int pos);
void displayNodes();
NodePointer getNode(const int pos);
int getLenOfList(); private:
NodePointer head; }; #endif

  相应的实现代码为:

 // linkedlist.cpp
#include "linkedlist.h" LinkedList::LinkedList()
{
head = new Node();
//assert(head != NULL);
head->next = head;
head->pre = head;
} LinkedList::~LinkedList()
{
NodePointer ptr = head->next, postPtr;
while (ptr != head)
{
postPtr = ptr;
ptr = ptr->next;
delete postPtr;
}
delete head;
} LinkedList::LinkedList(const LinkedList& orig)
{
// head 初始化这一块不能缺,因为当新声明一个新对象并调用该拷贝构造函数时,
// 新对象的head并不会被初始化,也就没有调用默认构造函数。要不会出错,因为
// 在其他地方有用到了类似ptr = head->next的语句,如在函数getLenOfList()
// 中,当新对象没有被初始化时,调用该函数就会发生“内存访问错误”。
head = new Node();
//assert(head != NULL);
head->next = head;
head->pre = head; NodePointer ptr = orig.head->next;
int i = ;
while (ptr != orig.head)
{
addNode(i, ptr->data);
ptr = ptr->next;
i++;
} } LinkedList& LinkedList::operator=(const LinkedList& orig)
{
NodePointer ptr = orig.head->next;
int i = ;
while (ptr != orig.head)
{
addNode(i, ptr->data);
ptr = ptr->next;
i++;
} return *this;
} bool LinkedList::isEmpty()
{
return head->next == head && head->pre == head;
} bool LinkedList::addNode(const int pos, const ElementType val)
{
bool isSuccess = true;
int len = getLenOfList();
// assert(0 <= pos <= len);
if (pos < || pos > len)
{
cerr << "The node at position " << pos << " you want to add is less than zero or larger than "
<< "the length of list ." << endl;
isSuccess = false;
throw out_of_range("out_of_range");
}
else
{
NodePointer ptr = new Node(), prePtr, postPtr;
// assert(ptr != NULL);
ptr->data = val;
if (pos == ) // insert node right behind 'head'
{
postPtr = head->next;
head->next = ptr;
postPtr->pre = ptr; ptr->pre = head;
ptr->next = postPtr;
}
else if (pos == len) // append node
{
prePtr = head->pre;
head->pre = ptr;
prePtr->next = ptr; ptr->pre = prePtr;
ptr->next = head;
}
else // others
{
prePtr = head->next;
int count = ;
while (prePtr != head && count < pos - )
{
prePtr = prePtr->next;
count++;
}
postPtr = prePtr->next; ptr->next = postPtr;
ptr->pre = prePtr;
prePtr->next = ptr;
postPtr->pre = ptr;
}
}
return isSuccess;
} bool LinkedList::deleteNode(const int pos)
{
bool isSuccess = true;
int len = getLenOfList();
// assert(0 <= pos <= len);
if (len == )
{
cerr << "There is no elements in the list." << endl;
isSuccess = false;
}
else
{
if (pos < || pos > len - )
{
cerr << "The node at position " << pos << " you want to delete is out of range." << endl;
isSuccess = false;
throw out_of_range("out_of_range");
}
else
{
NodePointer ptr, prePtr, postPtr;
if (pos == ) // delete the first node
{
ptr = head->next;
postPtr = ptr->next; head->next = postPtr;
postPtr->pre = head;
delete ptr;
}
else if (pos == len - ) // delete the last one
{
ptr = head->pre;
prePtr = ptr->pre; prePtr->next = head;
head->pre = prePtr;
delete ptr;
}
else // others
{
ptr = head->next;
int count = ;
while (ptr != head && count < pos)
{
ptr = ptr->next;
count++;
}
prePtr = ptr->pre;
postPtr = ptr->next; prePtr->next = postPtr;
postPtr->pre = prePtr;
delete ptr;
}
}
} return isSuccess;
} void LinkedList::displayNodes()
{
NodePointer ptr = head->next;
while (ptr != head)
{
cout << ptr->data << endl;
ptr = ptr->next;
}
} NodePointer LinkedList::getNode(const int pos)
{
int len = getLenOfList();
if (len == )
{
cerr << "There is no element in the list." << endl;
return NULL;
}
else
{
// assert(0 <= pos <= len);
if (pos < || pos > len - )
{
cerr << "The item at position " << pos << " you want to get is less than zero or "
<< "larger than the length of list." << endl;
throw out_of_range("out_of_range");
// return NULL;
}
else
{
NodePointer ptr = head->next;
int count = ;
while (ptr != head && count < pos)
{
ptr = ptr->next;
count++;
}
return ptr;
}
}
} int LinkedList::getLenOfList()
{
NodePointer ptr = head->next;
int len = ;
while (ptr != head)
{
ptr = ptr->next;
len++;
} return len; }

linkedlist.cpp

  Boost单元测试代码为:

 #define BOOST_TEST_MODULE LinkedList_Test_Module

 #include "stdafx.h"
#include "..\DoubleLinkedList\linkedlist.h" struct LinkedList_Fixture
{
public:
LinkedList_Fixture()
{
testLinkedList = new LinkedList();
}
~LinkedList_Fixture()
{
delete testLinkedList;
} LinkedList * testLinkedList;
}; BOOST_FIXTURE_TEST_SUITE(LinkedList_Test_Suite, LinkedList_Fixture) BOOST_AUTO_TEST_CASE(LinkedList_Normal_Test)
{
// isEmpty --------------------------------------------
BOOST_REQUIRE(testLinkedList->isEmpty() == true); // getLenOfList ---------------------------------------
BOOST_REQUIRE(testLinkedList->getLenOfList() == ); // addNode & getNode ---------------------------------
BOOST_REQUIRE(testLinkedList->addNode(, ) == true);
BOOST_REQUIRE((testLinkedList->getNode())->data == );
BOOST_REQUIRE((testLinkedList->getNode())->pre != NULL);
BOOST_REQUIRE((testLinkedList->getNode())->next != NULL);
BOOST_REQUIRE(testLinkedList->isEmpty() == false);
BOOST_REQUIRE(testLinkedList->getLenOfList() == ); BOOST_REQUIRE(testLinkedList->addNode(, ) == true);
BOOST_REQUIRE((testLinkedList->getNode())->data == );
BOOST_REQUIRE((testLinkedList->getNode())->pre != NULL);
BOOST_REQUIRE((testLinkedList->getNode())->next != NULL);
BOOST_REQUIRE(testLinkedList->getLenOfList() == ); BOOST_REQUIRE(testLinkedList->addNode(, ) == true);
BOOST_REQUIRE((testLinkedList->getNode())->data == );
BOOST_REQUIRE((testLinkedList->getNode())->pre != NULL);
BOOST_REQUIRE((testLinkedList->getNode())->next != NULL);
BOOST_REQUIRE(testLinkedList->getLenOfList() == ); // deleteNode -----------------------------------------
BOOST_REQUIRE(testLinkedList->deleteNode() == true);
BOOST_REQUIRE((testLinkedList->getNode())->data == );
BOOST_REQUIRE((testLinkedList->getNode())->pre != NULL);
BOOST_REQUIRE((testLinkedList->getNode())->next != NULL);
BOOST_REQUIRE(testLinkedList->getLenOfList() == ); BOOST_REQUIRE(testLinkedList->deleteNode() == true);
BOOST_REQUIRE((testLinkedList->getNode())->data == );
BOOST_REQUIRE((testLinkedList->getNode())->pre != NULL);
BOOST_REQUIRE((testLinkedList->getNode())->next != NULL);
BOOST_REQUIRE(testLinkedList->getLenOfList() == ); BOOST_REQUIRE(testLinkedList->deleteNode() == true);
BOOST_REQUIRE(testLinkedList->getLenOfList() == ); } BOOST_AUTO_TEST_CASE(LinkedList_Abnormal_Test)
{
// initialize ------------------------------------------
BOOST_REQUIRE(testLinkedList->addNode(, ) == true);
BOOST_REQUIRE(testLinkedList->addNode(, ) == true);
BOOST_REQUIRE(testLinkedList->addNode(, ) == true); // addNode -------------------------------------------
BOOST_REQUIRE_THROW(testLinkedList->addNode(-, ), out_of_range);
BOOST_REQUIRE_THROW(testLinkedList->addNode(, ), out_of_range); // deleteNode ----------------------------------------
BOOST_REQUIRE_THROW(testLinkedList->deleteNode(-), out_of_range);
BOOST_REQUIRE_THROW(testLinkedList->deleteNode(), out_of_range); // getNode --------------------------------------------
BOOST_REQUIRE_THROW(testLinkedList->getNode(-), out_of_range);
BOOST_REQUIRE_THROW(testLinkedList->getNode(), out_of_range);
} BOOST_AUTO_TEST_CASE(LinkedList_CopyConstructor_Test)
{
// initialize ------------------------------------------
BOOST_REQUIRE(testLinkedList->addNode(, ) == true);
BOOST_REQUIRE(testLinkedList->addNode(, ) == true);
BOOST_REQUIRE(testLinkedList->addNode(, ) == true); // check copy constructor ------------------------------
LinkedList * testLinkedList2 = new LinkedList(*testLinkedList);
BOOST_REQUIRE(testLinkedList2->isEmpty() == false);
BOOST_REQUIRE(testLinkedList2->getLenOfList() == );
BOOST_REQUIRE((testLinkedList2->getNode())->data == );
BOOST_REQUIRE((testLinkedList2->getNode())->data == );
BOOST_REQUIRE((testLinkedList2->getNode())->data == ); BOOST_REQUIRE(testLinkedList2->deleteNode() == true);
BOOST_REQUIRE((testLinkedList2->getNode())->data == );
BOOST_REQUIRE(testLinkedList2->getLenOfList() == ); BOOST_REQUIRE(testLinkedList2->deleteNode() == true);
BOOST_REQUIRE((testLinkedList2->getNode())->data == );
BOOST_REQUIRE(testLinkedList2->getLenOfList() == ); BOOST_REQUIRE(testLinkedList2->deleteNode() == true);
BOOST_REQUIRE(testLinkedList2->getLenOfList() == );
} BOOST_AUTO_TEST_CASE(LinkedList_EqualOperator_Test)
{
// initialize ------------------------------------------
BOOST_REQUIRE(testLinkedList->addNode(, ) == true);
BOOST_REQUIRE(testLinkedList->addNode(, ) == true);
BOOST_REQUIRE(testLinkedList->addNode(, ) == true); // check copy constructor ------------------------------
LinkedList * testLinkedList2 = new LinkedList();
*testLinkedList2 = *testLinkedList;
BOOST_REQUIRE(testLinkedList2->isEmpty() == false);
BOOST_REQUIRE(testLinkedList2->getLenOfList() == );
BOOST_REQUIRE((testLinkedList2->getNode())->data == );
BOOST_REQUIRE((testLinkedList2->getNode())->data == );
BOOST_REQUIRE((testLinkedList2->getNode())->data == ); BOOST_REQUIRE(testLinkedList2->deleteNode() == true);
BOOST_REQUIRE((testLinkedList2->getNode())->data == );
BOOST_REQUIRE(testLinkedList2->getLenOfList() == ); BOOST_REQUIRE(testLinkedList2->deleteNode() == true);
BOOST_REQUIRE((testLinkedList2->getNode())->data == );
BOOST_REQUIRE(testLinkedList2->getLenOfList() == ); BOOST_REQUIRE(testLinkedList2->deleteNode() == true);
BOOST_REQUIRE(testLinkedList2->getLenOfList() == );
} BOOST_AUTO_TEST_SUITE_END()

BoostUnitTest.cpp

  本篇博文的代码均托管到Taocode : http://code.taobao.org/p/datastructureandalgorithm/src/.

"《算法导论》之‘线性表’":双向循环链表的更多相关文章

  1. JavaScript 数据结构与算法之美 - 线性表(数组、栈、队列、链表)

    前言 基础知识就像是一座大楼的地基,它决定了我们的技术高度. 我们应该多掌握一些可移值的技术或者再过十几年应该都不会过时的技术,数据结构与算法就是其中之一. 栈.队列.链表.堆 是数据结构与算法中的基 ...

  2. 已知长度为n的线性表采用顺序结构,写一算法删除该线性表中所有值为item的元素

    /** * @author:(LiberHome) * @date:Created in 2019/2/27 23:34 * @description: * @version:$ */ /*已知长度为 ...

  3. 数据结构与算法系列2 线性表 使用java实现动态数组+ArrayList源码详解

    数据结构与算法系列2 线性表 使用java实现动态数组+ArrayList源码详解 对数组有不了解的可以先看看我的另一篇文章,那篇文章对数组有很多详细的解析,而本篇文章则着重讲动态数组,另一篇文章链接 ...

  4. ACM金牌选手算法讲解《线性表》

    哈喽,大家好,我是编程熊,双非逆袭选手,字节跳动.旷视科技前员工,ACM亚洲区域赛金牌,保研985研究生,分享算法与数据结构.计算机学习经验,帮助大家进大厂~ 公众号:『编程熊』 文章首发于: ACM ...

  5. 数据结构(java版)学习笔记(四)——线性表之循环链表

    单向循环链表 PS:有阴影的结点是头结点 概念: 最后一个结点的链域值不为NULL,而是指向头结点 特点: 从表中的任意结点出发,都可以找到表中其他结点 循环条件 p==h 双向链表 概念 链表中的每 ...

  6. 数据结构与算法系列2 线性表 链表的分类+使用java实现链表+链表源码详解

    数据结构与算法系列2.2 线性表 什么是链表? 链表是一种物理存储单元上非连续,非顺序的存储结构,数据元素的逻辑顺序是通过链表的链接次序实现的一系列节点组成,节点可以在运行时动态生成,每个节点包括两个 ...

  7. C语言 线性表 双向链式结构 实现

    一个双向链式结构实现的线性表 duList (GCC编译). /** * @brief 线性表双向链表结构 * @author wid * @date 2013-10-28 * * @note 若代码 ...

  8. 数据结构(1) 第一天 算法时间复杂度、线性表介绍、动态数组搭建(仿Vector)、单向链表搭建、企业链表思路

    01 数据结构基本概念_大O表示法 无论n是多少都执行三个具体步骤 执行了12步 O(12)=>O(1) O(n) log 2 N = log c N / log c N (相当于两个对数进行了 ...

  9. javascript实现数据结构与算法系列:线性表的静态单链表存储结构

    有时可借用一维数组来描述线性链表,这就是线性表的静态单链表存储结构. 在静态链表中,数组的一个分量表示一个结点,同时用游标(cur)代替指针指示结点在数组中的相对位置.数组的第0分量可看成头结点,其指 ...

  10. 数据结构导论 四 线性表的顺序存储VS链式存储

    前几章已经介绍到了顺序存储.链式存储 顺序存储:初始化.插入.删除.定位 链式存储:初始化.插入.删除.定位 顺序存储:初始化 strudt student{ int ID://ID char nam ...

随机推荐

  1. Ubuntu下装QQ2014(http://my.oschina.net/oscfox/blog/315951)

    QQ登陆界面: QQ登陆之后: 1.首先我们需要下载一个 deb的 Wine QQ安装包 qq2014官方下载:http://www.longene.org/download/WineQQ2013SP ...

  2. T-SQL动态查询(3)——静态SQL

    接上文:T-SQL动态查询(2)--关键字查询   本文讲述关于静态SQL的一些知识和基础技巧. 简介: 什么是静态SQL?静态SQL是和动态SQL相对而言的,其实我们没必要过于纠结精确定义,只要大概 ...

  3. 5、Android Service测试

    如果你在应用中使用了Service,你应该来测试这个Service来确保它正常工作.你可以创建仪表测试来验证Service的行为是否正确:比如,service保存和返回有效的数值并正常的处理数据. A ...

  4. leetcode 3 Longest Substring Without Repeating Characters最长无重复子串

    Given a string, find the length of the longest substring without repeating characters. For example, ...

  5. Android简易实战教程--第十四话《模仿金山助手创建桌面Widget小部件》

    打开谷歌api,对widget小部件做如下说明: App Widgets are miniature application views that can be embedded in otherap ...

  6. Objc中为何某些类的属性要设置为copy而不是strong?

    大熊猫猪·侯佩原创或翻译作品.欢迎转载,转载请注明出处. 如果觉得写的不好请多提意见,如果觉得不错请多多支持点赞.谢谢! hopy ;) 不知道大家是否注意,我们再使用一些第三方类的时候大多数情况下对 ...

  7. C++对象模型的那些事儿之四:拷贝构造函数

    前言 对于一个没有实例化的空类,编译器不会给它默认生成任何函数,当实例化一个空类后,编译器会根据需要生成相应的函数.这类函数包括一下几个: 构造函数 拷贝构造函数 析构函数 赋值运算符 在上一篇博文C ...

  8. Mybatis源码之Statement处理器RoutingStatementHandler(三)

    RoutingStatementHandler类似路由器,在其构造函数中会根据Mapper文件中设置的StatementType来选择使用SimpleStatementHandler.Prepared ...

  9. flex 强制转换类型失败无法将object转换为XXX

    错误描述 flex在加载module时报出如题所示的错误, 实际表现 问题就出现在这 我取消这个错误提示框 再次在前台查询数据 就一切ok 问题就出现在这一句 var zoufangModel:ZfR ...

  10. 关于会话、进程、请求的几个常用SQL

    1.检查自己的SID SELECT sid FROM v$session WHERE sid = (SELECT sid FROM v$mystat WHERE rownum = 1); 2. 几个I ...