《五月集训》(第十三天)——双向链表

本文主要介绍了如何设计浏览器历史记录的双向链表数据结构,并通过实例解析了430.扁平化多级双向链表、剑指OfferII028.展平多级双向链表以及剑指Offer36.二叉搜索树与双向链表的解题思路和源码分析,涉及链表操作和二叉树的中序遍历。
摘要由CSDN通过智能技术生成

前言

        欢迎大家积极在评论区留言发表自己的看法,知无不言,言无不尽,养成每天刷题的习惯,也可以自己发布优质的解题报告,供社区一同鉴赏,吸引一波自己的核心粉丝。
        今天是五月集训第十三天:双向链表🔥🔥🔥🔥

一、打卡啦

2022.5.13.10:47:第一题就复盘了俩小时,虽然时间很久但是搞清楚一道题那种开心感是真的不错的,后面的几题对现在我来说还有点难,还得慢慢复盘了。可能感觉后面的训练难度会越来越难,希望自己能尽量做出一题或者两题。😢
2022.5.13.13:29:今天的第二第三题利用了午休时间过辣,芜湖~😄
2022.5.24.19:29:24号的题目太难了回过来填坑了😄

二、练习题目

        1472. 设计浏览器历史记录
        430. 扁平化多级双向链表
        剑指 Offer II 028. 展平多级双向链表
        剑指 Offer 36. 二叉搜索树与双向链表

三、算法思路

  • 1、1472. 设计浏览器历史记录:😄花了2小时搞明白了这一题,模拟题的帮助下懂了双向链表的妙用,链表最头疼要搞清楚指针指向移动,写的时候经常会指到null的地方去。
  • 2、430. 扁平化多级双向链表:😄题目的理解不难,而且也知道用啥双向链表加dfs,就是怎么去翻译成代码的问题了。
      题目意思如下:now->childFirst->···->childLast->(now->next) 代码思路也是这么写的。
  • 3、剑指 Offer II 028. 展平多级双向链表:😄今天买一送一题。
  • 4、剑指 Offer 36. 二叉搜索树与双向链表:二叉搜索树转链表,利用二叉搜索树的特性,中序遍历就是一个顺序排列来做。

四、源码剖析

// 1472. 设计浏览器历史记录
class BrowserHistory {
    struct Node{
        string val;
        Node* prev;
        Node* next;
    }; //(1)
public:
    Node *List, *Current; 
    BrowserHistory(string homepage) {
        List = new Node();
        List->prev = List->next = nullptr;
        List->val = homepage;
        Current = List;
    } //(2)
    
    void visit(string url) {
        Node* Next = Current->next;
        if(Next == nullptr) {
            Current->next = new Node();
            Current->next->next = nullptr;
            Current->next->prev = Current;
        } 
        else {
            Node* tmp = Next->next;
            Next->next = nullptr;

            // 释放内存空间
            while (tmp)
            {
                Node* node = tmp->next;
                delete tmp;
                tmp = node;
            }
        }
        Current->next->val = url;
        Current = Current->next;
    } //(3)
    
    string back(int steps) {
        string str = Current->val;
        Node* pre;
        while (steps-- && Current)
        {
            pre = Current;
            Current = Current->prev;
            if(Current) str = Current->val;
        }
        if(!Current) {
            Current = pre;
        }
        return str;
    }
    
    string forward(int steps) {
        string str = Current->val;
        Node* pre;
        while (steps-- && Current)
        {
            pre = Current;
            Current = Current->next;
            if(Current) str = Current->val;
        }
        if(!Current) {
            Current = pre;
        }
        return str;
    } //(4)
};
  • 1、创建双向链表的数据结构。有值,前指针,后指针;
  • 2、链表的初始化定义头结点和当前结点;
  • 3、visit:要分情况讨论,如果当前结点没有进行前进或者后退操作,直接当前结点的next进行链接,否则的话先将当前结点之后的记录全部删除掉,进行内存回收,再对当前结点的next进行操作;
  • 4、backforward是类似的,不断找steps步内的next或者prev,但是这也有个条件Current要存在并且我们能访问,如果Current不是空指针的话,我们更新Current去它的next或者prev,同时要返回的网页string更新为当前结点的值,否则如果Current根本不存在你也移动不了,老老实实回到上一个地方吧。
// 430. 扁平化多级双向链表
class Solution {
public:
    Node* dfs(Node* head) {
        Node *now = head;
        Node *last = nullptr; //(2)
        while(now)
        {
            Node *clast;
            if(now->child) {
                clast = dfs(now->child); //(3)
                Node* next = now->next;
                now->next = now->child;
                now->child = nullptr;
                now->next->prev = now; 
                if(next) {
                    next->prev = clast;
                }
                clast->next = next; //(4)
            }
            if(now->next == nullptr) {
                last = now;
            } //(5)
            now = now->next;
        }
        return last;
    }

    Node* flatten(Node* head) {
        if(!head) {
            return nullptr;
        }
        Node* last = dfs(head); //(1)
        last->next = nullptr;
        return head;
    }
};
  • 1、递归调用的入口,返回一个值;
  • 2、定义一个当前结点,和最终返回的结点;
  • 3、判断是否有子结点,有的话继续递归子结点;
  • 4、这一块是这么个过程now->childFirst->···->childLast->(now->next)
      (1) 定义一个next用来先把当前的now->next存下来;
      (2) now–>next我要指向孩子,now->next的前指针指回now,形成完整的双向链表;
      (3) 之前next不是原来的now->next嘛,如果不空接到孩子结点的后面去,否则的话孩子结点的末尾就是结束,在孩子结点末尾加null;
  • 5、如果now->next已经是空了,那last就到now这就结束了,也不需要考虑是不是有孩子结点,有孩子前面递归就把它处理了。
// 剑指 Offer II 028. 展平多级双向链表
class Solution {
public:
    Node* dfs(Node* head) {
        Node *now = head;
        Node *last = nullptr;
        while(now)
        {
            Node *clast;
            if(now->child) {
                clast = dfs(now->child);
                Node* next = now->next;
                now->next = now->child;
                now->child = nullptr;
                now->next->prev = now;
                if(next) {
                    next->prev = clast;
                }
                clast->next = next;
            }
            if(now->next == nullptr) {
                if(now->child) {
                    last = clast;
                } else {
                    last = now;
                }
            }
            now = now->next;
        }
        return last;
    }

    Node* flatten(Node* head) {
        if(!head) {
            return nullptr;
        }
        Node* last = dfs(head);
        last->next = nullptr;
        return head;
    }
};
  • 1、同上一题。
// 剑指 Offer 36. 二叉搜索树与双向链表
class Solution {
    void dfs(Node *root, Node** minnode, Node** maxnode) {
        if(root == nullptr) {
            *minnode = nullptr;
            *maxnode = nullptr;
        } //(2)
        Node *lminnode, *lmaxnode, *rminnode, *rmaxnode;
        if(root->left) {
            dfs(root->left, &lminnode, &lmaxnode);
            lmaxnode->right = root;
            root->left = lmaxnode;
            *minnode = lminnode; //(3)
        } else {
            *minnode = root;
        }

        if(root->right) {
            dfs(root->right, &rminnode, &rmaxnode);
            rminnode->left = root;
            root->right = rminnode;
            *maxnode = rmaxnode; //(4)
        } else {
            *maxnode = root;
        }
    }

public:
    Node* treeToDoublyList(Node* root) {
        if(root == nullptr) {
            return nullptr;
        }
        Node *minnode, *maxnode; //(1)
        dfs(root, &minnode, &maxnode);
        minnode->left = maxnode;
        maxnode->right = minnode;
        return minnode;
    }
};
  • 1、构成双向链表,先定义一个头指针和尾指针,准备将链表循环到一起;
  • 2、处理下结点为空的情况;
  • 3、根据平衡二叉树的特性,左子树<根<右子树,即中序遍历一遍会发现是一个递增的序列,所以找到左子树的最小结点和最大结点,最小结点肯定也是整棵平衡二叉树的最小结点,左边最大的结点一定会比根小,所以左边最大结点的尾指针指向根节点,根节点的头指针指向左边最大结点。
  • 4、根据平衡二叉树的特性,左子树<根<右子树,即中序遍历一遍会发现是一个递增的序列,所以找到右子树的最小结点和最大结点,最大结点肯定也是整棵平衡二叉树的最大结点,右边最小的结点一定会比根大,所以右边最小结点的头指针指向根节点,根节点的尾指针指向右边最小结点;
  • 4、在主函数中还要把首尾指针串在一起形成一个双向链表。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值