数据结构之复杂链表的复制
1. 什么是复杂链表?
所谓复杂链表,指的是个链表的每个节点,有一个指向next指针指向下一个节点,还有一个random指针指向这个链表中的一个随机节点或者NULL。
什么意思呢?我们来看图说话
如图,每个结点都用next指针指向下一个结点的同时,还用random指针指向任意的结点,注意这里的random指针的指向是随心所欲的,可以指向任何结点(包括NULL)。
2. 怎样复制?
那么我们怎么样对这样的链表进行复制呢?很多人的第一反应是先将每个结点的random指针所指向的信息记录下来,然后再根据记录下的信息进行复制。
这就会有一个问题:既然是复制链表,那么复制得来的链表的每个结点都是新开辟的,有着自己的空间——这个空间一定不是原链表相应结点的空间。我们怎么找到random指针所指向的新空间的地址呢?有一种方法:开辟一个N*N的数组,用来存放原链表每个结点的之间的关系。然而这种方法的时间和空间开销是巨大的。
这里提供一种对复杂链表复制的方案。一共分为三步:
1. 给原链表每个结点后插入值相同的新结点
2. 给新插入结点的随机指针域赋值
3. 将新结点从原来表上重新拆下来
第一步:给原链表每个结点后插入值相同的新结点:
第二步:给新插入结点的随机指针域赋值。以值为1的结点为例,它的结点记为p,由p结点复制过来的结点记为pC。关键代码为:pC->random = p->random->next; 需要注意的一点就是当一个结点的random指针指向NULL时,由该结点复制得来的结点的random指针直接置为NULL。
第三步:将新结点从原来表上重新拆下来。以值为1的结点为例,它的结点记为p,由p结点复制过来的结点记为pC。关键代码为:p->next = pC->next; pC->next = p->next->next;
最终效果为:
假设链表长度为n,那么总的时间复杂度为O(n),空间复杂度为O(1)。
typedef struct PNode{
DataType data;
struct PNode *next;
struct PNode *random;
}*PNode, Node;
PNode AddNode(DataType data) //增加结点
{
PNode p = (PNode)malloc(sizeof(Node));
if (p != NULL)
{
p->data = data;
p->next = NULL;
p->random = NULL;
return p;
}
assert(p);
return NULL;
}
PNode CopyComplexList(PNode pHead) // 复杂链表复制
{
PNode pHead2 = NULL;
PNode pNext = NULL; //原链表的当前结点
PNode pCur = NULL; //原链表当前结点的下一个结点
if (pHead == NULL)
{
return NULL;
}
//给原链表每个结点后插入值相同的新结点
pCur = pHead;
pNext = pHead->next;
while(pNext)
{
pCur->next = AddNode(pCur->data); //在当前结点后插入值相同的新结点
pCur->next->next = pNext; //修改新结点的next指针,使其指向原链表当前结点的下一个结点
pCur = pNext; //移动pCur指针
pNext = pNext->next; //移动pNext指针
}
pCur->next = AddNode(pCur->data); //对于最后一个结点,其next为NULL。需单独处理
pCur->next->next = pNext;
//给新插入结点的随机指针域赋值
pCur = pHead;
pNext = pHead->next;
while(pNext->next) //复制原链表中前n-1个结点的random指针指向
{
if (pCur->random == NULL)
{
pNext->random = NULL;
}
else
{
pNext->random = pCur->random->next;
pCur = pNext->next;
pNext = pCur->next;
}
}
if (pCur->random == NULL) //对最后一个结点,特殊处理
pNext->random = NULL;
else
pNext->random = pCur->random->next;
//将新结点从原来表上重新拆下来
pCur = pHead;
pNext = pHead->next;
pHead2 = pHead->next;
while(pNext->next) //将原链表中的前n-1个结点拆下来
{
pCur->next = pNext->next;
pNext->next = pCur->next->next;
pCur = pCur->next;
pNext = pNext->next;
}
pCur->next = pNext->next; //最后一个结点特殊处理
return pHead2;
}