复杂链表即就是在普通单链表的基础之上添加了一个可以指向链表中任意节点或者NULL的_random指针。
节点定义:
template <class T>
struct ComplexListNode{
ComplexListNode(const T& d)
:_data(d),_next(NULL),_random(NULL)
{}
T _data;
ComplexListNode<T>* _next;
ComplexListNode<T>* _random;
};
图1是一个含有5各节点的复杂链表。图中实箭头表示_next指针,虚箭头表示 _random指针.(NULL指针省略没有画出)
构造函数和析构函数比较容易实现,这里不详细介绍直接贴出实现:
构造函数:
ComplexList()
:_head(NULL),_tail(NULL)
{}
析构函数:
~ComplexList(){
if (_head){
ComplexListNode<T>* cur = _head;
while (cur->_next != NULL){ //逐个删除节点
ComplexListNode<T>* del = cur;
cur = cur->_next;
delete del;
}
delete cur; //删除_tail节点
_head = _tail = NULL;
}
}
在实现其拷贝构造函数时,比较容易想到的做法就是讲整个拷贝过程分为两步:第一步就是复制原始链表的每一个节点,并用_next连接起来;第二步设置每一个节点的_random指针。假设原始链表中的节点N的_random指针指向S;则在复制后的的链表中设置N‘节点的_random指针时,需要从原始链表中定位到S节点所在的链表中的位置,需要添加一个计数器来统计其相对于头节点的位置。在确定了原始链表中S的位置,然后需要从头遍历赋值节点,找到相对应的S’;然后让_random指针指向S‘。这样一来虽然问题解决了。
对于一个有n个节点的链表来说,由于定位每个节点的_random都需要从链表的头节点开始遍历经过O(n)步定位到。因此这种方法的时间复杂度为O(n^2)。
分析上面的这种做法,大量的时间都发正在_random的定位上面。做进一步的优化。
这种方法主要分为三个步骤:
第一步:复制链表;
第二部:设置_random指针;
第三部:拆分。
下面详细介绍每一步的具体做法。
第一步、依然是复制链表,但是试讲复制的节点N’直接连接到N后面
实现代码:
void DoubleComplexList(ComplexList<T>& cp){
ComplexListNode<T>* cur = cp._head;
while (cur){
ComplexListNode<T>* NewNode =
new ComplexListNode<T>(cur->_data); //创建新节点;
NewNode->_next = cur->_next;
cur->_next = NewNode; //连接到基节点后;
cur = cur->_next->_next;
}
}
第二步、设定每一个节点的_random指针;假设基链表上的N节点的_random指针指向S,那么复制出来的N‘是N的_naxt指向的节点;同样S’是S的_next指向的。如下图:
实现代码:
void CopyRandom(ComplexList<T>& cp){
if (cp._head){
ComplexListNode<T>* cur = cp._head;
while (cur){
if (cur->_random != NULL){ //设置每一个节点的_random指针
cur->_next->_random = cur->_random->_next;
}
cur = cur->_next->_next;
}
}
}
第三步、将第一步复制出来的链表拆分成为两个链表;用指针cur将基链表连接起来,使用cur1链表将复制链表连接起来。效果如下图所示:
实现代码:
void CopyComplexList(ComplexList<T>& cp){
ComplexListNode<T>* cur = cp._head; //控制cp链表的当前节点
_head = cur->_next;
cur->_next = cur->_next->_next; //cur指向第二个节点,并且跳
cur = cur->_next; //过新链表的头节点
ComplexListNode<T>* cur2 = _head; //控制新链表的当前结节点
while (cur != cp._tail){
cur2->_next = cur->_next; //连接新链表
cur2 = cur2->_next; //cur指向新节点
cur->_next = cur->_next->_next; //连接cp链表
cur = cur->_next; //移动至下一节点
}
cur2->_next = cp._tail->_next;
cp._tail->_next = NULL;
_tail = cur2->_next;
_tail->_next = NULL;
}
将上面三个步骤合起来就实现了这个算法:
ComplexList(ComplexList<T>& cp){
DoubleComplexList(cp); //复制节点,子节点连接在父节点之后;
CopyRandom(cp); //复制_random;
CopyComplexList(cp); //拆分cp链表,获得新链表;
}
完整的复杂链表类的实现代码:
template <class T>
struct ComplexListNode{
ComplexListNode(const T& d)
:_data(d),_next(NULL),_random(NULL)
{}
T _data;
ComplexListNode<T>* _next;
ComplexListNode<T>* _random;
};
template <class T>
class ComplexList{
public:
ComplexList()
:_head(NULL),_tail(NULL)
{}
ComplexList(ComplexList<T>& cp){
DoubleComplexList(cp); //复制节点,子节点连接在父节点之后;
CopyRandom(cp); //复制_random;
CopyComplexList(cp); //拆分cp链表,获得新链表;
}
~ComplexList(){
if (_head){
ComplexListNode<T>* cur = _head;
while (cur->_next != NULL){
ComplexListNode<T>* del = cur;
cur = cur->_next;
delete del;
}
delete cur;
_head = _tail = NULL;
}
}
void PushBack(const T& d){
if (_head == NULL){
_head = _tail = new ComplexListNode<T>(d);
}
else{
_tail->_next = new ComplexListNode<T>(d);
_tail = _tail->_next;
}
}
void PopBack(){
if (_head != _tail){
ComplexListNode<T>* cur = _head;
while (cur->_next != _tail){
cur = cur->_next;
}
delete _tail;
_tail = cur;
_tail->_next = NULL;
}
else if (_head == _tail){
delete _head;
_head = _tail = NULL;
}
}
ComplexListNode<T>* GetComplexListNode(const T& d){
if (_head){
ComplexListNode<T>* cur = _head;
{
while (cur->_data != d){
cur = cur->_next;
}
}
return cur;
}
}
protected:
void DoubleComplexList(ComplexList<T>& cp){
ComplexListNode<T>* cur = cp._head;
while (cur){
ComplexListNode<T>* NewNode = new ComplexListNode<T>(cur->_data);
NewNode->_next = cur->_next;
cur->_next = NewNode;
cur = cur->_next->_next;
}
}
void CopyRandom(ComplexList<T>& cp){
if (cp._head){
ComplexListNode<T>* cur = cp._head;
while (cur){
if (cur->_random != NULL){
cur->_next->_random = cur->_random->_next;
}
cur = cur->_next->_next;
}
}
}
void CopyComplexList(ComplexList<T>& cp){
ComplexListNode<T>* cur = cp._head; //控制cp链表的当前节点
_head = cur->_next;
cur->_next = cur->_next->_next; //cur指向第二个节点,并且跳过新链表的头节点
cur = cur->_next;
ComplexListNode<T>* cur2 = _head; //控制新链表的当前结节点
while (cur != cp._tail){
cur2->_next = cur->_next; //连接新链表
cur2 = cur2->_next; //cur指向新节点
cur->_next = cur->_next->_next; //连接cp链表
cur = cur->_next; //移动至下一节点
}
cur2->_next = cp._tail->_next;
cp._tail->_next = NULL;
_tail = cur2->_next;
_tail->_next = NULL;
}
protected:
ComplexListNode<T>* _head;
ComplexListNode<T>* _tail;
};
测试用例:
void test(){
ComplexList<int> a;
a.PushBack(1);
a.PushBack(2);
a.PushBack(3);
a.PushBack(4);
a.PushBack(5);
a.PushBack(6);
a.PushBack(7);
ComplexListNode<int>* r1 = a.GetComplexListNode(2);
ComplexListNode<int>* r2 = a.GetComplexListNode(5);
r1->_random = r2; //创建复杂链表
ComplexList<int> s(a); //测试拷贝构造函数功能正确性
}
基链表结果:
复制链表的结果: