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

版权声明:本文为博主原创学习笔记,如需转载请注明来源。 https://blog.csdn.net/SHU15121856/article/details/82588247

面试题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;
}

猜你喜欢

转载自blog.csdn.net/SHU15121856/article/details/82588247
今日推荐