【复杂链表】复杂链表的复制

面试题35:复杂链表的复制

书上有图,不再画了。

请实现函数ComplexListNode* Clone(ComplexListNode* pHead),复制一个复杂链表。在复杂链表中,每个结点除了有一个m_pNext指针指向下一个结点外,还有一个m_pSibling 指向链表中的任意结点或者nullptr。

复杂链表比普通的单链表多了一个Sibling域,可以指向任意的一个结点。如何将Sibling域高效地拷贝是复杂链表复制的难点。

在原链表上,每个结点后面夹上拷贝它的结点,形成一个合并链表,因为拷贝结点总是跟在原结点后面,所以Sibling域也就是原结点Sibling域的下一结点。

ComplexList.h
#pragma once

//复杂链表的结点
struct ComplexListNode {
	//普通链表就有的值和next域 
	int                 m_nValue;
	ComplexListNode*    m_pNext;
	//复杂链表还多一个指向任意结点的域 
	ComplexListNode*    m_pSibling;
};

//建立指定Value的新结点 
ComplexListNode* CreateNode(int nValue); 
//建立结点关系 
void BuildNodes(ComplexListNode* pNode, ComplexListNode* pNext, ComplexListNode* pSibling);
//输出复杂链表 
void PrintList(ComplexListNode* pHead);
ComplexList.cpp
#include <cstdio>
#include "ComplexList.h"

//建立指定Value的结点:创建结点,设定值,返回 
ComplexListNode* CreateNode(int nValue) {
	ComplexListNode* pNode = new ComplexListNode();

	pNode->m_nValue = nValue;
	pNode->m_pNext = nullptr;
	pNode->m_pSibling = nullptr;

	return pNode;
}

//建立结点关系 
void BuildNodes(ComplexListNode* pNode, ComplexListNode* pNext, ComplexListNode* pSibling) {
	if(pNode != nullptr) {//检查要设置关系的结点非空 
		pNode->m_pNext = pNext;
		pNode->m_pSibling = pSibling;
	}
}

//输出复杂链表 
void PrintList(ComplexListNode* pHead) {
	ComplexListNode* pNode = pHead;
	while(pNode != nullptr) {
		printf("The value of this node is: %d.\n", pNode->m_nValue);
		//在输出时把它独有的域也输出,next域不用输出 
		if(pNode->m_pSibling != nullptr) 
			printf("The value of its sibling is: %d.\n", pNode->m_pSibling->m_nValue);
		else
			printf("This node does not have a sibling.\n");
		printf("\n");
		//因为输出链表时候还是从前向后按next这条路走 
		pNode = pNode->m_pNext;
	}
}
复制复杂链表
#include<bits/stdc++.h>
#include "../Utilities/ComplexList.h"
using namespace std;

void CloneNodes(ComplexListNode* pHead);
void ConnectSiblingNodes(ComplexListNode* pHead);
ComplexListNode* ReconnectNodes(ComplexListNode* pHead);

//[总的函数]复制一个复杂链表,传入原型链表头,返回复制后链表的头
ComplexListNode* Clone(ComplexListNode* pHead) {
	CloneNodes(pHead);//第一遍O(n)扫描,做克隆合并
	ConnectSiblingNodes(pHead);//第二版O(n)扫描,给出Sibling域
	//为什么不能一遍扫描?因为第一遍扫的时候后面的结点还没克隆创好
	//如果Sibling域指向后面的结点,那没法给出Sibling域

	return ReconnectNodes(pHead);//第三遍O(n)扫描,拆开搞成2个链表
}

//[克隆合并]对复杂链表的每个结点克隆,将其夹在原结点之next、原next之前
void CloneNodes(ComplexListNode* pHead) {
	ComplexListNode* pNode = pHead;//从头结点开始
	//沿着next走
	while(pNode != nullptr) {
		//复制当前结点
		ComplexListNode* pCloned = new ComplexListNode();
		pCloned->m_nValue = pNode->m_nValue;
		pCloned->m_pNext = pNode->m_pNext;//注意其next也指向当前的next
		pCloned->m_pSibling = nullptr;//唯有这个特殊指针暂时置空
		//将原结点的next指向新克隆的结点,所以克隆的结点在next链上是被夹杂其中
		pNode->m_pNext = pCloned;
		//沿着next走
		pNode = pCloned->m_pNext;
	}
}

//连接Sibling域,传入[克隆合并]后的链表(next链已经变成两倍长了)
void ConnectSiblingNodes(ComplexListNode* pHead) {
	ComplexListNode* pNode = pHead;//从头结点开始
	//一直沿着next链走
	while(pNode != nullptr) {
		//克隆的结点总是在原结点的后面一个
		ComplexListNode* pCloned = pNode->m_pNext;
		//现在要克隆其Sibling域,只要原结点的Sibling域非空
		if(pNode->m_pSibling != nullptr) {
			//那么Sibling域所指的那个结点的后一个结点也就是其克隆结点
			//所以本结点的克隆结点的Sibling域将指向原结点的Sibling域的next
			pCloned->m_pSibling = pNode->m_pSibling->m_pNext;
		}
		//一直沿着next链走
		pNode = pCloned->m_pNext;
	}
}

//将这个大链表拆开,把之前的原链表建回来
//并把新的克隆的链表也维护好,然后返回之
ComplexListNode* ReconnectNodes(ComplexListNode* pHead) {
	ComplexListNode* pNode = pHead;//原链表上的游标指针,从[合并链表]头开始
	ComplexListNode* pClonedHead = nullptr;//克隆链表的头
	ComplexListNode* pClonedNode = nullptr;//克隆链表上的游标指针

	if(pNode != nullptr) {//这里是防止空指针异常
		//克隆链表头和其上的游标都初始化为[合并链表]的第二个结点
		pClonedHead = pClonedNode = pNode->m_pNext;
		//原链表的next,是[合并链表]上其克隆结点的next
		pNode->m_pNext = pClonedNode->m_pNext;
		//原链表指针向后走
		pNode = pNode->m_pNext;
	}

	//至此,[原链表指针]比[克隆链表指针]领先一个身位
	//即这个[原链表指针]其实是对应当前[克隆链表指针]所指元素的下一个元素
	//并且[原链表指针]和后面一个身位的[克隆链表指针]都挂在[合并链表上]

	//只要[原链表指针]没到空,也即没有遍历完
	while(pNode != nullptr) {
		//[克隆链表指针]的next也就是它前面的[原链表指针]的next
		pClonedNode->m_pNext = pNode->m_pNext;
		//[克隆链表指针]向后走
		pClonedNode = pClonedNode->m_pNext;

		//现在,[克隆链表指针]又和[原链表指针]指向的元素相对应了
		//并且[克隆链表指针]还挂在[合并链表上]
		//[原链表指针]要维护next域(并向下走),要依赖这个[克隆链表指针]

		//[原链表指针]的next域就是[克隆链表指针]的下一个
		pNode->m_pNext = pClonedNode->m_pNext;
		//[原链表指针]向下走
		pNode = pNode->m_pNext;
	}

	return pClonedHead;//最终返回[克隆链表头]
}

//          -----------------
//         \|/              |
//  1-------2-------3-------4-------5
//  |       |      /|\             /|\
//  --------+--------               |
//          -------------------------
int main() {
	ComplexListNode* pNode1 = CreateNode(1);
	ComplexListNode* pNode2 = CreateNode(2);
	ComplexListNode* pNode3 = CreateNode(3);
	ComplexListNode* pNode4 = CreateNode(4);
	ComplexListNode* pNode5 = CreateNode(5);

	BuildNodes(pNode1, pNode2, pNode3);
	BuildNodes(pNode2, pNode3, pNode5);
	BuildNodes(pNode3, pNode4, nullptr);
	BuildNodes(pNode4, pNode5, pNode2);
	
	ComplexListNode* newNode=Clone(pNode1);
	
	PrintList(newNode);

	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值