【剑指Offer】个人学习笔记_36_二叉搜索树与双向链表

目录

    • 题目:
        • [剑指 Offer 36. 二叉搜索树与双向链表](https://leetcode-cn.com/problems/er-cha-sou-suo-shu-yu-shuang-xiang-lian-biao-lcof/)
        • 题目分析
    • 初始解答:
    • 学习他人:
      • 方法一:
      • 方法二:
      • 方法三:
    • 上周面试题
    • 总结

刷题日期:19:0434 星期一2021年4月19日

个人刷题记录,代码收集,来源皆为leetcode

再次经过考虑,既然面试答题用什么答都无所谓

那为什么不用自己相对来说最熟悉,语言最上层的Python呢

其他的该学的基础学会就好,用Python来做剑吧

题目:

剑指 Offer 36. 二叉搜索树与双向链表

难度中等231

输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的循环双向链表。要求不能创建任何新的节点,只能调整树中节点指针的指向。

为了让您更好地理解问题,以下面的二叉搜索树为例:

img

我们希望将这个二叉搜索树转化为双向循环链表。链表中的每个节点都有一个前驱和后继指针。对于双向循环链表,第一个节点的前驱是最后一个节点,最后一个节点的后继是第一个节点。

下图展示了上面的二叉搜索树转化成的链表。“head” 表示指向链表中有最小元素的节点。

img

特别地,我们希望可以就地完成转换操作。当转化完成以后,树中节点的左指针需要指向前驱,树中节点的右指针需要指向后继。还需要返回链表中的第一个节点的指针。

题目分析

然后发现完了,现在对Python的链表和树定义又丈二和尚摸不着头脑了,没事,慢慢来。

二叉查找树(Binary Search Tree),(又:二叉搜索树,二叉排序树)它或者是一棵空树,或者是具有下列性质的二叉树: 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值; 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值; 它的左、右子树也分别为二叉排序树。二叉搜索树作为一种经典的数据结构,它既有链表的快速插入与删除操作的特点,又有数组快速查找的优势,所以应用十分广泛。

首先肯定得把树给中序遍历过一遍,这样才能实现从小到大的顺序。

常规思路肯定得用到递归。

其实要逼迫着自己向K神那样去思考,才能把题做的得心应手吧,不过那样的习惯除了培养,也离不开大量的刷题,反思,自己现在的状态还差的挺远的。

首先第一个节点得留着,因为不仅要让head来引用,而且还要在尾指针处构成循环。操作还得就地完成,说明这个操作是在递归子程序的内部实现。

每一个递归,输入的是前一个节点,构建的是双向的循环,进入的是下一个节点。

返回的,感觉没有真正理解到递归,暂时想不出来。

初始解答:

有点复杂,还是学习别人的思路。参考了方法一

"""
# Definition for a Node.
class Node:
    def __init__(self, val, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right
"""
class Solution:  
    def treeToDoublyList(self, root: 'Node') -> 'Node':
        # lambda语句中,冒号前是参数,可以有多个,用逗号隔开,
        # 冒号右边的返回值。lambda语句构建的其实是一个函数对象
        a, f = [], lambda r: r and (f(r.left) or a.append(r) or f(r.right))
        f(root)
        n = len(a)
        # enumerate() 函数用于将一个可遍历的数据对象(如列表、元组或字符串)组合为一个索引序列,
        # 同时列出数据和数据下标,一般用在 for 循环当中。
        for i, r in enumerate(a):
            r.left, r.right = a[i - 1], a[i + 1 - n] #向前,向后建立指针连接
        return n and a[0] or None #返回a中的第一个

要补的东西还有很多,以及py就是简洁。执行结果:通过

显示详情

执行用时:44 ms, 在所有 Python3 提交中击败了74.48%的用户

内存消耗:16 MB, 在所有 Python3 提交中击败了48.56%的用户

学习K神的解法

"""
# Definition for a Node.
class Node:
    def __init__(self, val, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right
"""
class Solution:
    def treeToDoublyList(self, root: 'Node') -> 'Node':
        def dfs(cur):
            if not cur: return #为空返回的写法
            dfs(cur.left) #左
            if self.pre: #修改前索引
                self.pre.right , cur.left = cur, self.pre #构建双向链表
            else: #记录头节点
                self.head = cur
            self.pre = cur #保存cur
            dfs(cur.right) #右

        if not root: return #根节点为空,返回空
        self.pre = None #往前不指向吗
        dfs(root)  #子程序运行
        self.head.left, self.pre.right = self.pre, self.head #构建循环
        return self.head #返回结果

空间效率更高了,执行结果: 通过

显示详情

执行用时:56 ms, 在所有 Python3 提交中击败了12.25%的用户

内存消耗:15.7 MB, 在所有 Python3 提交中击败了90.97%的用户

学习他人:

方法一:

typingMonkeyL5 2020-02-12

class Solution:
    def treeToDoublyList(self, root: 'Node') -> 'Node':
        a, f = [], lambda r: r and (f(r.left) or a.append(r) or f(r.right))
        f(root)
        n = len(a)
        for i, r in enumerate(a):
            r.left, r.right = a[i - 1], a[i + 1 - n]
        return n and a[0] or None

方法二:

K神 本文解法基于性质:二叉搜索树的中序遍历为 递增序列 。将 二叉搜索树 转换成一个 “排序的循环双向链表” ,其中包含三个要素:

排序链表: 节点应从小到大排序,因此应使用 中序遍历 “从小到大”访问树的节点。

双向链表: 在构建相邻节点的引用关系时,设前驱节点 pre 和当前节点 cur ,不仅应构建 pre.right = cur ,也应构建 cur.left = pre 。

循环链表: 设链表头节点 head 和尾节点 tail ,则应构建 head.left = tail 和 tail.right = head 。

作者:jyd
链接:https://leetcode-cn.com/problems/er-cha-sou-suo-shu-yu-shuang-xiang-lian-biao-lcof/solution/mian-shi-ti-36-er-cha-sou-suo-shu-yu-shuang-xian-5/
来源:力扣(LeetCode)

class Solution:
    def treeToDoublyList(self, root: 'Node') -> 'Node':
        def dfs(cur):
            if not cur: return
            dfs(cur.left) # 递归左子树
            if self.pre: # 修改节点引用
                self.pre.right, cur.left = cur, self.pre
            else: # 记录头节点
                self.head = cur
            self.pre = cur # 保存 cur
            dfs(cur.right) # 递归右子树
        
        if not root: return
        self.pre = None
        dfs(root)
        self.head.left, self.pre.right = self.pre, self.head
        return self.head

方法三:

oldking (编辑过)2021-04-05

python3
class Solution:
    def treeToDoublyList(self, root: 'Node') -> 'Node':
        if root == None: return None
        p = root
        stack = []
        def fun(p):
            if p == None:
                return 
            fun(p.left)
            stack.append(p)
            fun(p.right)
        fun(p)
        n = len(stack)
        for i in range(0,n):
            stack[i].left = stack[i-1]
            stack[i].right = stack[(i+1)%n]
        return stack[0]

上周面试题

/*题目:给定数组,删除其中心元素
中心元素:奇数则1,偶则2个任意一个。
该值等于整个数组的平均值*/

class Solution {
     
    public int findRepeatNumber(int[] nums int num) {
     
        //1.对数组进行排序
        nums = nums.sort();
        // //2.1 遍历得到中心元素 or 计算得到这个均值
        // int sum = 0, reverage;
        // for (int i = 0; i < nums.length() - 1; i++) {
     
        //     sum += nums(i); //总和
        // }
        // reverage = sum/i; //均值
        
        //2.找到均值下标
        int order = 0;
        if (num % 2 == 0) order = num/2; //为偶数的情况
        if (num % 2 == 1) order = num/2; //为奇数的情况

        //3. 循环将改元素后面的数据前移一位
        for (int i = order; i < nums.length() - 1; i++) {
     
            //前移
            nums[i] = nums[i + 1];
        }
        // 去掉最后一位
        nums[nums.length() - 1] = null;
        return nums; //处理后的数组
        
    }
}

肯定是跑不通的,在网上也没遇到相似的题目,主要还是自己连一种排序都不会默写,而且连面试官提示的思路都没有底气实现,这也是导致不通过的主要原因,还是基础的东西都没打好,不过本来就不是打算现在就上场的,不要气馁,等到7月份攒够了实力,再上就是了。

总结

以上就是本题的内容和学习过程了,上一周因为参加了华为的机试,综测,专业面(挂了),所以也没有规律做题更新,专业面虽然选了自己本专业的自动化,但是面试官在问了我50min项目后,还是让我手撕了,当时脑子都不清楚,别说做题了,表现实在一般。

正好借这次经历鞭策一下自己,再接再厉,该走出舒适圈了。

周末把简历也升级了一下,考虑了更多的个人履历,项目,科研经历等等,实实在在的两页了。

欢迎讨论,共同进步。

你可能感兴趣的:(Coding,链表,指针,数据结构,算法)