C++ : 剑指offer(21-35)

C++ : 剑指offer(21-40)


21、调整数组顺序使奇数位于偶数前面

题目描述
输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。

class Solution {
public:
    void reOrderArray(vector<int> &array){
        vector<int> list;
        for(int i=0;i<array.size();++i){
            if(array[i]%2==1){
                list.push_back(array[i]);
            }
        }
        for(int i=0;i<array.size();++i){
            if(array[i]%2==0){
                list.push_back(array[i]);
            }
        }
        for(int i=0;i<array.size();++i){
            array[i] = list[i];
        }
    }
};

思路:由于需要考虑稳定性,动用了额外内存存储数据,不算是好方法;暂时未发现更好方法;


22、链表中倒数第k个节点

题目描述
输入一个链表,输出该链表中倒数第k个结点。

class Solution {
public:
    ListNode* FindKthToTail(ListNode* pListHead, unsigned int k) {
        if(pListHead==nullptr||k==0) return nullptr;
        ListNode* LeftNode = pListHead;
        ListNode* RightNode = pListHead;
        for(int i=0;i<k-1;++i){
            if(RightNode->next!=nullptr){
                RightNode = RightNode->next;
            }
            else{
                return nullptr;
            }
        }
        while(RightNode->next!=nullptr){
            RightNode = RightNode->next;
            LeftNode = LeftNode->next;
        }
        return LeftNode;
    }
};

思路:这种题可以只遍历一次解决问题,常见方法是设置两个指针,一个在第一个节点,一个在第k个节点,然后整体向后移动;计算复杂度O(n);需要注意鲁棒性问题:k值大于链表长度,k为0时等等;

扩展:
1、输出链表中间节点:同样定义两个指针,指向头节点,然后每次指针1移动一个节点,指针2移动两个节点直至尾,返回指针1;
2、如何判断链表中含有环:定义两个头节点指针,一个一次移动一个节点,一个移动两个节点,如果快节点追上了慢节点,则说明有环;如果快节点走到头了,则说明没有环;


23、链表中环的入口节点

题目描述
给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,输出null。

class Solution {
public:
    ListNode* EntryNodeOfLoop(ListNode* pHead)
    {   // 第一步,查看链表中有无环,利用一快一慢两个指针
        if(pHead==nullptr) return nullptr;
        ListNode* pFast = pHead;
        ListNode* pSlow = pHead;
        while(pFast->next!=nullptr&&pFast->next->next!=nullptr){
            pFast = pFast->next->next;
            pSlow = pSlow->next;
            if(pFast==pSlow){
                int num = FindNumOfLoop(pFast); // 获取环中节点个数
                return FindEntryHelper(pHead,num); // 获取入口节点
            }
        }
        return nullptr;
    }
    int FindNumOfLoop(ListNode* pNode){ 
        // 获取环中节点个数,利用环中节点的循环计数遍历
        int num = 1;
        ListNode* pNodeStay = pNode;
        while(pNode->next!=pNodeStay){
            pNode = pNode->next;
            ++num;
        }
        return num;
    }
    ListNode* FindEntryHelper(ListNode* pHead,int num){
        // 获取入口节点,利用两个相同指针,一个在头,一个在头后num个节点位置
        // 同步移动,一旦相等,就是入口
        ListNode* pLeft = pHead;
        ListNode* pRight = pHead;
        for(int i=0;i<num;++i){
            pRight = pRight->next;
        }
        while(pLeft!=pRight){
            pLeft = pLeft->next;
            pRight = pRight->next;
        }
        return pLeft;
    }
};

思路:该问题分三步走:首先需要判断是否有环,利用一快一慢两个指针遍历;然后需要确定环的入口,定义两个同步指针,一个在头,一个在头后n个节点处,共同移动至相等即可;n表示环内的节点个数,计算节点个数传入确定入口节点的函数,由于最开始一快一慢两个节点相遇时肯定在环内,则在环内计数遍历直至回到该点;


24、翻转链表

题目描述
输入一个链表,反转链表后,输出新链表的表头。

class Solution {
public:
    ListNode* ReverseList(ListNode* pHead) {
        if(pHead==nullptr) return nullptr; // 若链表为空
        if(pHead->next==nullptr) return pHead;  // 若链表为单节点
        ListNode* LeftNode = pHead; // 从头节点依次创建三个指针 
        ListNode* MidNode = pHead->next;  
        ListNode* RightNode = pHead->next->next; 
        LeftNode->next = nullptr;  // 先处理头节点
        while(RightNode!=nullptr){  // 如果有三个或更多节点
            MidNode->next = LeftNode;
            LeftNode = MidNode;
            MidNode = RightNode;
            RightNode = RightNode->next;
        }
        MidNode->next = LeftNode;// 两个节点时或者多个节点的结尾工作
        return MidNode;
    }
};

思路:算法题应当首先考虑不使用额外内存空间的方法解决,链表反转中同样不要借助额外空间,定义三个指针,进行递进处理。注意在链表长度为012时候的特殊处理;


25、合并两个排序的链表

题目描述
输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。

class Solution {
public:
    ListNode* Merge(ListNode* pHead1, ListNode* pHead2)
    {
        if(pHead1==nullptr) return pHead2;  
        if(pHead2==nullptr) return pHead1;
        ListNode* pHead = nullptr;  
        if(pHead1->val<=pHead2->val){ 
            pHead = pHead1;
            pHead->next = Merge(pHead1->next,pHead2);
        }
        else{
            pHead = pHead2;
            pHead->next = Merge(pHead1,pHead2->next);
        }
        return pHead;
    }
};

一个非递归的版本:

class Solution {
public:
    ListNode* Merge(ListNode* pHead1, ListNode* pHead2)
    {
        if(pHead1==nullptr) return pHead2;
        if(pHead2==nullptr) return pHead1;
        if(pHead1==pHead2){  // 若两个参数是同一条链表
            while(pHead2!=nullptr){
                auto pNode = new ListNode(pHead2->val);
                pNode->next = pHead2->next;
                pHead2->next = pNode;
                pHead2 = pHead2->next->next;
            }
            return pHead1;
        }
        ListNode* pHead;
        if(pHead1->val<=pHead2->val){
            pHead = pHead1; pHead1 = pHead1->next;
        }else{
            pHead = pHead2; pHead2 = pHead2->next;
        }
        ListNode* pNode = pHead;
        while(pHead1!=nullptr&&pHead2!=nullptr){
            if(pHead1->val<=pHead2->val){
                pNode->next = pHead1;
                pHead1 = pHead1->next;
                pNode = pNode->next;
            }else{
                pNode->next = pHead2;
                pHead2 = pHead2->next;
                pNode = pNode->next;
            }
        }
        if(pHead1==nullptr) pNode->next = pHead2;
        else pNode->next = pHead1;
        return pHead;
    }
};

思路:相比而言,递归版本的该问题显得异常简单,即每次比较两个链表的头节点,将较小的一方链接到下一个,最后返回头节点即可;


26、树的子结构

题目描述
输入两棵二叉树A,B,判断B是不是A的子结构。(ps:我们约定空树不是任意一个树的子结构)

class Solution {
public:
    bool HasSubtree(TreeNode* pRoot1, TreeNode* pRoot2)
    {
        if(pRoot1==nullptr || pRoot2==nullptr) return false;
        // 用或,当前已经是子结构的情况下不需要再遍历了
        return isSubtree(pRoot1,pRoot2)
            || HasSubtree(pRoot1->left,pRoot2)
            || HasSubtree(pRoot1->right,pRoot2);
    }
    bool isSubtree(TreeNode* tree,TreeNode* Subtree){
        if(Subtree==nullptr) return true;
        if(tree==nullptr) return false;
        // 用与,当前节点相等之后还需要确认其左右子树是否也相同
        return (tree->val == Subtree->val)
            && isSubtree(tree->left,Subtree->left)
            && isSubtree(tree->right,Subtree->right);
    }
};

思路:首先判断B是不是A的子结构,首先要在A中遍历寻找B的根节点,若相同,则第二步要在两个相等的根节点下遍历,判断B是否为其子结构;该题需要熟练掌握二叉树的递归遍历思想以及在递归遍历思想下灵活穿插其他语句。
该题思路可以用于在二叉树中寻找子节点、判断二叉树是否相等等问题中;


27、二叉树的镜像

题目描述
操作给定的二叉树,将其变换为源二叉树的镜像。

class Solution {
public: // 此处只能使用前序遍历或后序遍历,不能用中序遍历
    void Mirror(TreeNode *pRoot){
        if(pRoot){ // 镜像即交换每个节点的左右节点即可
            Mirror(pRoot->left);
            Mirror(pRoot->right);
            if(pRoot->left!=nullptr||pRoot->right!=nullptr){
                TreeNode* TempNode = pRoot->left;
                pRoot->left = pRoot->right;
                pRoot->right = TempNode;
            }
        }
    }
};

思路:求镜像的过程就是把每个节点的左右节点进行交换,使用递归遍历处理最为简便,注意这里使用前序遍历处理和后序遍历处理都可以,但不能是中序遍历,因为中序遍历中,先处理根节点的左子树,左子树镜像后处理根节点,左子树变成右子树,接着再处理根节点的右子树是无效的。


28、对称的二叉树

题目描述
请实现一个函数,用来判断一颗二叉树是不是对称的。注意,如果一个二叉树同此二叉树的镜像是同样的,定义其为对称的。

class Solution {
public:
    bool isSymmetrical(TreeNode* pRoot){
        // 函数重载,扩展一个指针,一个先遍历左子节点,一个先遍历右子节点
        return isSymmetrical(pRoot,pRoot);  
    }
    bool isSymmetrical(TreeNode* pRoot1,TreeNode* pRoot2){
        if(pRoot1==nullptr&&pRoot2==nullptr) 
            return true;  // 考虑两个叶子节点都为nullptr的情况,也为相等
        if((pRoot1==nullptr&&pRoot2!=nullptr)||(pRoot1!=nullptr&&pRoot2==nullptr)) 
            return false;  // 若一个为空一个不为空,不相等
        // 用与,表示所有节点(包括nullptr)都为true时,最后结果为true;
        return (pRoot1->val==pRoot2->val)  // 当两个节点都不为空时,判断值是否相等并递归
            && isSymmetrical(pRoot1->left,pRoot2->right)
            && isSymmetrical(pRoot1->right,pRoot2->left);
    }
};

思路:最简单的判断方式:一个对称的二叉树,对其遍历左->右子节点和遍历右->左子节点的结果应该相同,递归终止条件为两个节点都为nullptr;(需要判断两者遍历节点每个都是否相同,整体结构是否对称,包括有的节点的左右nullptr的子节点也要判断是否相等;)


29、顺时针打印矩阵

题目描述
输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下4 X 4矩阵: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 则依次打印出数字1,2,3,4,8,12,16,15,14,13,9,5,6,7,11,10.

vector<int> printMatrix(vector<vector<int> > matrix) {
    vector<int> result;
    if(matrix.empty()||matrix[0].empty()) return result;
    int size_i = matrix.size();
    int size_j = matrix[0].size();
    int index = 0, i = 0, j = 0;
    while(size_i-index>0&&size_j-index>0){  // 判断如果还有内圈没有打印
        if(size_i-index==1){    // 当剩余内圈行数为1时,直接打印内圈一行
            for(j=index;j<matrix[0].size()-index;++j){
                result.push_back(matrix[index][j]);
            }
        }
        else if(size_j-index==1){   // 当剩余内圈列数为1时,直接打印一列
            for(i=index;i<matrix.size()-index;++i){
                result.push_back(matrix[i][index]);
            }
        }
        else{  // 当剩余内圈还是一个矩阵时,循环打印一圈
            while(j<size_j){   // 从左到右打印一行
                result.push_back(matrix[i][j]);
                ++j;
            }--j;++i;

            while(i<size_i){   // 从上到下打印一行
                result.push_back(matrix[i][j]);
                ++i;
            }--i;--j;

            while(j>=index){  // 从有右到左打印一行
                result.push_back(matrix[i][j]);
                --j;
            }++j;--i;

            while(i>index){  // 从下到上打印一行
                result.push_back(matrix[i][j]);
                --i;
            }
        }
        ++index; i = index; j = index; // 往内部更新一圈 index表示已经打印了几圈
        --size_i;  // 内圈大小减小
        --size_j;  // 内圈大小减小
    }
    return result;
}

思路:上述题解没有啥特殊技巧,列出打印规律并按照规律写循环代码一圈一圈循环打印,注意判断剩余矩阵为空、为行向量、为列向量的情况;


30、包含min函数的栈

题目描述
定义栈的数据结构,请在该类型中实现一个能够得到栈中所含最小元素的min函数(时间复杂度应为O(1))。

class Solution {
    stack<int> sk1;
    stack<int> sk2;
public:
    void push(int value) {
        if(sk1.empty()&&sk2.empty()){
            sk1.push(value);
            sk2.push(value);
        }
        else if(value<sk2.top()){
            sk2.push(value);
        }
        else{
            sk2.push(sk2.top());
        }
        sk1.push(value);
    }
    void pop() {
        if(!sk1.empty()&&!sk2.empty()){
            sk1.pop();
            sk2.pop();
        }
    }
    int top() {
        if(!sk1.empty()){
            return sk1.top();
        }
    }
    int min() {
        if(!sk2.empty()){
            return sk2.top();
        }
    }
};

思路:该题需要明确使用辅助栈,用来存放每个栈状态下的最小值(这个过程通过画图推演很容易可以发现规律),同时要注意栈在各种情况下的操作的合法性,保证程序鲁棒);


31、栈的压入、弹出序列

题目描述
输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈序列对应的一个弹出序列,但4,3,5,1,2就不可能是该压栈序列的弹出序列。(注意:这两个序列的长度是相等的)

class Solution {
public:
    bool IsPopOrder(vector<int> pushV,vector<int> popV) {
        if(pushV.size()==0&&popV.size()==0) return true;
        stack<int> sk;  // 辅助栈
        auto iter_push = pushV.begin(); // 定义迭代器
        auto iter_pop = popV.begin(); // 定义迭代器
        sk.push(*iter_push); // 现将第一个数入栈
        while(iter_pop!=popV.end()){ // 按照弹出序列进行循环
            while(*iter_pop!=sk.top()){ // 如果当前要弹出的节点不等于栈顶元素
                ++iter_push; // 依次将入栈序列压入栈,直至等于当前要弹出节点
                if(iter_push==pushV.end()){ 
                    // 入栈序列已循环完毕但弹出序列还在循环中,故不匹配
                    return false;
                }
                sk.push(*iter_push); // 入栈序列依次入栈
            }
            sk.pop(); // 弹出当前相等的节点
            ++iter_pop; // 弹出序列加1,继续检查下一个要弹出节点
        }
        return true; // 全部循环完毕,都匹配,返回true
    }
};

思路:这也是一道比较复杂的题,主要难点在于找到思路后的代码编写过程(不好想);思路是利用辅助栈,按照给定的出栈序列模拟入栈出栈过程(可以在纸上模拟),当整个出栈序列全部模拟完毕,则匹配,其中如果当前出栈序列节点不等于辅助栈顶元素,则继续向后寻找该元素并依次入栈,如果一直找到结尾都没发现相等节点,则说明给定的出栈序列有问题,不匹配;


32、从上往下打印二叉树

题目描述
从上往下打印出二叉树的每个节点,同层节点从左至右打印。

class Solution {
public:
    vector<int> PrintFromTopToBottom(TreeNode* root) {
        vector<int> result; // 存放结果返回的vector
        if(root==nullptr) return result; 
        deque<TreeNode*> de; // 辅助双端队列deque
        de.push_back(root); // 将头节点压入队列
        while(!de.empty()){ // 若daque不为空则循环
            TreeNode* it = de.back(); // 当前队尾节点
            if(it->left!=nullptr) // 如果有左子节点则压入
                de.push_front(it->left);
            if(it->right!=nullptr) // 如果有右子节点则压入
                de.push_front(it->right);
            result.push_back(it->val); // 打印当前节点
            de.pop_back(); // 弹出当前节点
        }
        return result;
    }
};

思路:该题为二叉树的广度优先遍历(BFS),不同于深度优先遍历DFS,该方法需要借助双端队列实现,其遍历过程就是每次把当前节点压入队列,如果该节点还有子节点,则依次再把子节点压入队列,从队列头到队列尾就是遍历顺序(画图演练);


32(2)、把二叉树打印成多行

题目描述
从上到下按层打印二叉树,同一层结点从左至右输出。每一层输出一行。

class Solution {
public:
    vector<vector<int> > Print(TreeNode* pRoot) {
        vector<vector<int> > result; // 结果矩阵
        if(pRoot==nullptr) return result;
        vector<int> result_row; // 暂时保存每行结果的vector
        deque<TreeNode*> de; // 广度优先遍历的辅助双端队列
        de.push_front(pRoot); // 队列中压入根节点
        // 定义(下一行该打印的节点数)和(该行还应打印的节点数)
        int nums_of_row = 0, nums_of_print = 1;
        while(!de.empty()){ // 双端队列不为空(还未打印完毕)
            TreeNode* Node = de.back(); // 打印当前节点
            if(Node->left!=nullptr){ // 判断当前节点是否还有子节点
                de.push_front(Node->left);
                ++nums_of_row; // 如果有,则下一行中需要打印的节点数+1
            }
            if(Node->right!=nullptr){
                de.push_front(Node->right);
                ++nums_of_row;
            }
            result_row.push_back(Node->val); // 结果暂存到当前行结果
            --nums_of_print; // 打印一次,当前行还应打印数目-1
            de.pop_back(); // 打印后的节点移除队列
            // 如果当前行以处理完毕(换行)
            if(nums_of_print<=0){ // 无剩余该打印节点,换行
                result.push_back(result_row); // 行向量保存到结果矩阵
                result_row.clear(); // 暂存行向量清零
                nums_of_print = nums_of_row; // 将下一行该打印的数目赋给当前行
                nums_of_row = 0; // 准备进入新一行的打印,将下一行该打印数目置零并重新递增
            }
        }
        return result;
    }
};

思路:该题是建立在二叉树的广度优先搜索遍历算法基础上的,难度较大;首先需要明确出按层遍历的程序架构,然后设计分层打印的边界条件;一开始的想法是定义每一层的总节点数(2的幂)、每一行的节点数和空节点数,然后判断,但写起来非常复杂,很难完成;最关键地在于设计出两个变量:下一行应该打印的节点数和当前行还未打印的节点数;处理当前节点时,需要将其子节点(下一行)压入栈,故通过当前行的处理就可以轻松得知下一行应该打印的节点数。这种按行打印的变量设置应该记住;


32(3)、按之字形打印二叉树

题目描述
请实现一个函数按照之字形打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右至左的顺序打印,第三行按照从左到右的顺序打印,其他行以此类推。

class Solution {
public:
    vector<vector<int> > Print(TreeNode* pRoot) {
        vector<vector<int>> result;
        if(pRoot==nullptr) return result;
        stack<TreeNode*> sk1; // 辅助栈1
        stack<TreeNode*> sk2; // 辅助栈2
        sk1.push(pRoot); 
        while(!sk1.empty()||!sk2.empty()){ 
            // 循环完所有节点的条件
            // 该循环每次循环可以实现打印一行数据
            if(sk2.empty()){ 
                // 如果栈2为空,则实行从左往右打印(sk1出栈)
                // 左节点->右节点顺序存储下一层的节点(sk2入栈)
                result.push_back(Print_row(sk1,sk2,1));
            }
            else if(sk1.empty()){
                // 如果栈1为空,则实行从右往左打印(sk2出栈)
                // 右节点->左节点顺序存储下一层节点(sk1入栈)
                result.push_back(Print_row(sk2,sk1,0));
            }
        }
        return result;
    }
    vector<int> Print_row(stack<TreeNode*>& sk1,stack<TreeNode*>& sk2,int LeftToRight){
        // 函数中的sk1永远是要打印的栈,sk2永远是空栈
        vector<int> result_row;  // 每一行结果暂存vector
        while(!sk1.empty()){ // 每行循环打印的边界条件
            TreeNode* Node = sk1.top();
            if(LeftToRight==1){ // 如果是左右子节点模式
                if(Node->left!=nullptr){
                    sk2.push(Node->left); 
                }
                if(Node->right!=nullptr){
                    sk2.push(Node->right);
                }
            }
            else if(LeftToRight==0){ // 如果是右左子节点模式
                if(Node->right!=nullptr){
                    sk2.push(Node->right);
                }
                if(Node->left!=nullptr){
                    sk2.push(Node->left); 
                }
            }
            result_row.push_back(Node->val);
            sk1.pop();
        }
        return result_row;
    }
};

思路:该题不同于平常的按行打印二叉树,由于顺序相反,可以发现该打印过程可以用两个栈来模拟,思路不难但编程难度较大,必须思考清楚如何交替操作两个栈名(可以设下标num=1,每次交替num=1-num)以及每次是先压入左子树还是压入右子树;另外该方法不需要像上两题那样设定下一层需要打印的节点数目和该层剩余应打印数目,因为每个栈一次存储一行节点,栈为空即为边界条件;


33、二叉搜索树的后序遍历序列

题目描述:
输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。如果是则输出Yes,否则输出No。假设输入的数组的任意两个数字都互不相同。

class Solution {
public:
    bool VerifySquenceOfBST(vector<int> sequence) {
        if(sequence.empty()) return false; // 若为空,返回false
        return VerifySquence(sequence);
    }
    bool VerifySquence(vector<int>& sequence){
        if(sequence.size()<=2) return true; // 递归终止条件
        auto iter = sequence.begin(); // 从头遍历的迭代器
        int root = sequence.back(); // 获取根节点的值
        while(*iter<root){
            ++iter;
        }
        auto mid_iter = iter; // mid_iter为序列中第一个位与右子树的节点
        while(iter!=sequence.end()){
            if(*iter<root) return false; // 若右子树中有小于根节点的值
            ++iter;
        }
        vector<int> left_tree(sequence.begin(),mid_iter);
        vector<int> right_tree(mid_iter,sequence.end()-1);
        return VerifySquence(left_tree) // 判断左子树
            && VerifySquence(right_tree);  // 判断右子树
    }
};

思路:该题需要首先明确二叉搜索树的各项性质,然后针对后序遍历的规律(按照左子树节点,右子树节点、根节点顺序排列),然后利用递归层层判定。


34、二叉树中和为某一值的路径

题目描述
输入一颗二叉树的根节点和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。(注意: 在返回值的list中,数组长度大的数组靠前)

class Solution {
public:
    vector<vector<int> > FindPath(TreeNode* root,int expectNumber) {
        vector<vector<int> > result; // 总结果路径矩阵
        vector<int> result_row; // 保存当前路径的向量
        if(root==nullptr) return result; 
        return FindEachPath(root,expectNumber,result,result_row);
    }
    vector<vector<int> > FindEachPath(TreeNode* root,const int expectNumber,vector<vector<int> >& result,vector<int>& result_row){
        if(root==nullptr) return result; // 如为空直接返回,避免路径再次弹出节点
        if(root){
            result_row.push_back(root->val);
            if(root->left==nullptr&&root->right==nullptr){ // 为叶子节点,要判断
                int sum = 0;
                for(int i : result_row){ // 统计路径和
                    sum = sum + i;
                }
                if(sum==expectNumber){ // 如果符合,保存结果
                    result.push_back(result_row);
                }
            } // 开始遍历过程
            result = FindEachPath(root->left,expectNumber,result,result_row);
            result = FindEachPath(root->right,expectNumber,result,result_row);
            result_row.pop_back();  // 在路径中弹出当前节点
            return result;
        }
    }
};

思路:该题首先应确定出还是借助递归遍历解决问题,首先设定最终结果矩阵和保存当前路径的向量,当每次递归函数结束返回时,就相当于当前节点回溯至其父节点,故每次返回前要在路径向量中pop_back一次,遍历过程中如果遇到叶子节点,则判断路径向量之和符不符合给定值,符合则输出;注意递归函数设计以及递归结束条件;另外非常重要的是,必须明确使用前序遍历方法,这样才能逐步保存从上到下的当前路径;


35、复杂链表的复制

题目描述
输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的head。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)

class Solution {
public:
    RandomListNode* Clone(RandomListNode* pHead)
    {
        if(pHead==nullptr) return nullptr;
        CloneNext(pHead); // 在每个节点后面复制一个自身节点
        CloneRandom(pHead); // 每个复制出的节点random指针赋值
        return SplitList(pHead); // 分离两个链表
    }// 在每个节点后面复制一个自身节点
    void CloneNext(RandomListNode* pNode){
        while(pNode!=nullptr){
            RandomListNode* pNew = new RandomListNode(pNode->label);
            pNew->next = pNode->next;
            pNode->next = pNew;
            pNode = pNew->next;
        }
    }// 每个复制出的节点random指针赋值
    void CloneRandom(RandomListNode* pNode){
        while(pNode!=nullptr){
            if(pNode->random!=nullptr){
                pNode->next->random = pNode->random->next;
            }
            pNode = pNode->next->next;
        }
    }// 分离两个链表
    RandomListNode* SplitList(RandomListNode* pNode){
        RandomListNode* pNewHead = pNode->next;
        while(pNode->next->next!=nullptr){
            RandomListNode* pNewNode = pNode->next;
            pNode->next = pNode->next->next;
            pNode = pNewNode;
        }
        pNode->next = nullptr;
        return pNewHead;
    }
};

思路:复杂链表复制方法很多,但要考虑代码是否容易写,和计算复杂度的问题,在链表自身中复制一份,然后分离是比较方便的方法,具体分三步:第一步在链表每个节点后面复制一个自身节点,链表长度变成两倍;第二步在依次对每个新建节点的复杂指针赋值;第三部从长链表中把两条链表分离出来;


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值