单链表的缺陷
单链表要想高效的访问链表中的元素,需要从头结点开始,如果需要逆向访问链表中的元素,这时候效率非常低下
双向链表
在单链表的结点中增加一个指针pre
,用于指向当前结点的前驱结点,通过next指针域可以高效访问后继结点,通过pre指针域可以高效访问前驱结点,不管结点处于哪个位置都可以高效的访问前驱和后继结点
双向链表的继承层次结构
双向链表和单链表在内部实现上完全不同,所以不应该是继承关系,而应该是兄弟关系
双向链表的定义
在单链表的基础上新增加一个前驱指针域,
双向链表的插入操作
新结点的next指针指向next结点
current结点的next指针指向node结点,current结点与next结点断了联系
node结点的pre指针指向current结点,next结点的pre指针指向node
bool insert(int i, T& e)
{
bool ret = (i >= 0) && (i <= m_length);
if (ret)
{
Node* node = create();
if (node != NULL)
{
//找到插入的位置
Node* current = position(i);
Node* next = current->next;
node->value = e;
node->next = next; // 第1步
current->next = node;//第2步
//插入的位置为头结点
if (current != position(0))
{
node->pre = current;
}
else
{
node->pre = NULL;
}
//如果插入的位置是尾结点
if (next != NULL)
{
next->pre = node;
}
m_length++;
}
else
{
cout << "no memery to malloc" << endl;
}
}
双向链表的删除
第一步:找到待删除结点的前一个结点,即current结点
第二步:将current结点的next指针指向待删除结点的下一个结点,即next结点
第三步:将next结点的pre指针指向current结点
bool remove(int i)
{
bool ret = (i >= 0) && (i <= m_length);
if (ret)
{
//找到删除位置的前一个元素
Node* current = position(i);
Node* toDel = current->next;
Node* next = toDel->next;
if (m_current == toDel)
{
m_current = next;//游标指向下一个结点
}
current->next = next;
//删除的位置为尾结点
if (next != NULL)
{
next->pre = current;
}
destroy(toDel);
m_length--;
}
完整代码
#ifndef __DUAL_LINK_LIST_
#define __DUAL_LINK_LIST_
#include <iostream>
using namespace std;
template<class T,int N>
class DualLinkList
{
public:
struct Node
{
T value;
Node* next;
Node* pre;
};
mutable struct
{
char reverse[sizeof(T)];
Node* next;
Node* pre;
} m_header;
int m_length;
int m_step;
Node* m_current;
virtual Node* create()
{
return new Node();
}
void destroy(Node* pn)
{
delete pn;
}
public:
DualLinkList()
{
m_header.next = NULL;
m_header.pre = NULL;
m_length = 0;
m_step = 0;
m_current = NULL;
}
Node* position(int i)const
{
Node* pre = reinterpret_cast<Node*>(&m_header);
for (int pos = 0; pos < i; pos++)
{
pre = pre->next;
}
return pre;
}
int find(const T& e)const
{
Node* pre = reinterpret_cast<Node*>(&m_header);
int i = 0;
while (e != pre->next->value)
{
pre = pre->next;
i++;
}
return i;
}
bool end()
{
return m_current == NULL;
}
bool move(int i, int step = 1)
{
bool ret = (i >= 0) && (i <= m_length);
if (ret)
{
m_current = position(i)->next;
m_step = step;
}
return ret;
}
T current()
{
if (!end())
{
return m_current->value;
}
else
{
cout << "current end()" << endl;
return -1;//不知道写啥值
}
}
bool next()
{
int i = 0;
while (!end() && i < m_step)
{
m_current = m_current->next;
i++;
}
return i == m_step;
}
bool insert(int i, T& e)
{
bool ret = (i >= 0) && (i <= m_length);
if (ret)
{
Node* node = create();
if (node != NULL)
{
//找到插入的位置
Node* current = position(i);
Node* next = current->next;
node->value = e;
node->next = next; // 第1步
current->next = node;//第2步
//插入的位置为头结点
if (current != position(0))
{
node->pre = current;
}
else
{
node->pre = NULL;
}
//如果插入的位置是尾结点
if (next != NULL)
{
next->pre = node;
}
m_length++;
}
else
{
cout << "no memery to malloc" << endl;
}
}
return ret;
}
bool remove(int i)
{
bool ret = (i >= 0) && (i <= m_length);
if (ret)
{
//找到删除位置的前一个元素
Node* current = position(i);
Node* toDel = current->next;
Node* next = toDel->next;
if (m_current == toDel)
{
m_current = next;//游标指向下一个结点
}
current->next = next;
//删除的位置为尾结点
if (next != NULL)
{
next->pre = current;
}
destroy(toDel);
m_length--;
}
return ret;
}
bool set(int i, T& e)
{
bool ret = (i >= 0) && (i <= m_length);
if (ret)
{
Node* pre = reinterpret_cast<Node*>(&m_header);
pre = position(i);
pre->next->value = e;
}
return ret;
}
T get(int i) const
{
bool ret = (i >= 0) && (i <= m_length);
T e;
if (ret)
{
e = position(i)->next->value;
}
return e;
}
int length() const
{
return m_length;
}
void clear()
{
while (m_length > 0)
{
remove(0);
}
}
//找到当前结点的前一个结点,m_step为每一次移动的步数
virtual bool pre()
{
int i = 0;
while( (i < m_step) && !end())
{
m_current = m_current->pre;
i++;
}
return (i == m_step);
}
~DualLinkList()//O(n)
{
clear();
}
};
#endif
测试代码1-遍历链表元素
#include "DualLinkList.h"
#include <iostream>
int main()
{
DualLinkList<int, 5> sl;
for (int i = 0; i < 5; i++)
{
sl.insert(0, i);
}
//
for (int i = 0; i < sl.length(); i++)
{
cout << sl.get(i) << endl;
}
cout << "begin()" << endl;
//先定位到尾结点,通过pre指针定位元素
for (sl.move(sl.length()-1); !sl.end(); sl.pre())
{
cout << sl.current() << endl;
}
cout << "end()" << endl;
return 0;
}
第一个for循环时间复杂度是O(n*n),是非线性的,第二个for循环的时间复杂度是O(n)。
结果:
测试代码2-删除链表值中数据元素为5的结点
#include "DualLinkList.h"
#include <iostream>
int main()
{
DualLinkList<int, 5> sl;
int j = 5;
for (int i = 0; i < 5; i++)
{
sl.insert(0, i);
sl.insert(0, j);
}
for (sl.move(0);!sl.end(); sl.next())
{
cout << sl.current() << endl;
}
sl.move(sl.length() - 1);//定位到最后一个元素
cout << "begin()" << endl;
while (!sl.end())
{
if (sl.current() == 5)
{
cout << sl.current() << endl;
sl.remove(sl.find(sl.current()));
}
else
{
sl.pre(); //定位到前驱结点
}
}
for (sl.move(0); !sl.end(); sl.next())
{
cout << sl.current() << endl;
}
cout << "end()" << endl;
return 0;
}
结果:
小结
- 双向链表的诞生是为了弥补单链表的缺陷而设计
- 双向链表的游标可以直接访问当前结点的前驱和后继结点
- 双向链表是线性标概念的最终实现