Leetcode题解 - 树、DFS部分简单题目代码+思路(700、671、653、96

2020-10-18 05:33发布

700. 二叉搜索树中的搜索 - 树

给定二叉搜索树(BST)的根节点和一个值。 你需要在BST中找到节点值等于给定值的节点。 返回以该节点为根的子树。 如果节点不存在,则返回 NULL。

思路:
二叉搜索树的特点为左比根小,右比根大。那么目标结点就有三种可能:
1. 和根一样大,那么直接返回根即可。
2. 比根的值小,那么应该再去次左子树中搜索。
3. 比根的值大,那么应该再次去右子树中搜索。
可以看到这就是一个递归的思路。

class Solution:
    def searchBST(self, root: TreeNode, val: int) -> TreeNode:
        if not root:
            return 
        if val == root.val:
            return root
        # 比根的值小就去左子树
        if val < root.val:
            lres = self.searchBST(root.left, val)
            if lres: return lres
        # 否则就去右子树
        if val > root.val:
            rres = self.searchBST(root.right, val)
            if rres: return rres
        return None

671. 二叉树中第二小的节点 - 树

给定一个非空特殊的二叉树,每个节点都是正数,并且每个节点的子节点数量只能为 2 或 0。如果一个节点有两个子节点的话,那么这个节点的值不大于它的子节点的值。

给出这样的一个二叉树,你需要输出所有节点中的第二小的值。如果第二小的值不存在的话,输出 -1 。

思路:
(并没有想到很巧妙的方法emmm)
直接BFS遍历树,将大于根结点的值都保存起来(因为题目条件中说明了子节点的值都是大于or等于父节点的,那么根一定是最小的),如果这些值存在那么最小的一个一定为第二小的,否则就是不存在。

#         self.right = None
class Solution:
    def findSecondMinimumValue(self, root: TreeNode) -> int:
        if not root:
            return -1
        Q = [root]
        res = []
        while len(Q) != 0:
            node = Q.pop(0)
            if node.val > root.val:
                res.append(node.val)
            if node.left:
                Q.append(node.left)
            if node.right:
                Q.append(node.right)
        if len(res) == 0:
            return -1
        return min(res)

*653. 两数之和 IV - 输入 BST - 树 -> 双指针 *

给定一个二叉搜索树和一个目标结果,如果 BST 中存在两个元素且它们的和等于给定的目标结果,则返回 true。

思路:
(看了题解才想出来...两个数字和的问题似乎经常和双指针有关)
因为是二叉搜索树,所以中序遍历的结果就是一个递增序列。双指针法的前提就是递增序列,然后用一个指针指向头,一个指针指向尾,不断移动两个指针判断是否符合条件。

 class Solution:
    def findTarget(self, root: TreeNode, k: int) -> bool:
        inRes = []
        # 中序遍历
        def Inorder(root):
            if not root:
                return 
            Inorder(root.left)
            inRes.append(root.val)
            Inorder(root.right)
        Inorder(root)
        # 双指针
        head = 0; tail = len(inRes) - 1
        while head < tail:
            res = inRes[head] + inRes[tail]
            # 如果大于目标值,就让尾指针左移
            if res > k:
                tail -= 1
            # 如果小于目标值,就让头指针右移
            if res < k:
                head += 1
            if res == k:
                return True
        return False

965. 单值二叉树 - 树

如果二叉树每个节点都具有相同的值,那么该二叉树就是单值二叉树。

只有给定的树是单值二叉树时,才返回 true;否则返回 false。

思路:
遍历一遍树即可,这里采用的是层次遍历,但凡出现和根结点的值不同的值就绝对不是单值。

class Solution:
    def isUnivalTree(self, root: TreeNode) -> bool:
        if not root:
            return False
        Q = [root]
        check = root.val
        while len(Q) != 0:
            node = Q.pop(0)
            if node.val != check:
                return False
            if node.left:
                Q.append(node.left)
            if node.right:
                Q.append(node.right)
        return True

547. 朋友圈 - DFS -> 统计连通块的数量

班上有 N 名学生。其中有些人是朋友,有些则不是。他们的友谊具有是传递性。如果已知 A 是 B 的朋友,B 是 C 的朋友,那么我们可以认为 A 也是 C 的朋友。所谓的朋友圈,是指所有朋友的集合。

给定一个 N * N 的矩阵 M,表示班级中学生之间的朋友关系。如果M[i][j] = 1,表示已知第 i 个和 j 个学生互为朋友关系,否则为不知道。你必须输出所有学生中的已知的朋友圈总数。

思路:
直接套DFS模板即可,相当于判断一共做了几次DFS。
这个题的本质就是在求图中有几个连通块。

class Solution:
    def findCircleNum(self, M: List[List[int]]) -> int:
        res = 0
        vis = set()
        # 每一次的DFS都会把属于同一个朋友圈的人加上vis中
        def DFS(node):
            vis.add(node)
            for i in range(len(M[node])):
                if M[node][i] == 1 and i not in vis:
                    DFS(i)
        for i in range(len(M)):
        	# 剩下的没有被加入vis中才会再次进行DFS
            if i not in vis:
                DFS(i)
                res += 1
        return res

DFS - 求出所有的组合

473. 火柴拼正方形 - 含重复

输入为小女孩拥有火柴的数目,每根火柴用其长度表示。输出即为是否能用所有的火柴拼成正方形。

输入: [1,1,2,2,2]
输出: true
解释: 能拼成一个边长为2的正方形,每边两根火柴。

输入: [3,3,3,3,4]
输出: false
解释: 不能用所有火柴拼成一个正方形。

思路:
对于每一根火柴来说,它都有四个边可以去(不同的火柴可以去相同的边),这样只要求解出所有火柴的每一种摆放组合就可以得知是否能够称为正方形

"""
DFS遍历所有可能性
本题剪枝很重要,不然会很容易超时。
"""
class Solution:
    def makesquare(self, nums) -> bool:
        # 如果数组和不是4的倍数,一定不能拼成正方形
        sumRes = sum(nums)
        if len(nums) < 4 or sum(nums) % 4 != 0:
            return False
        # 若有一个数大于数组和/4,也一定不能拼成正方形
        check = sumRes // 4
        nums.sort(reverse=True)
        if nums[0] > check:
            return False
        # 遍历每根火柴loc为该跟火柴的下标,square为正方形的各个边
        def DFS(loc, square):
            if len(set(square)) == 1 and 0 not in square and loc == len(nums):
                return True
            if loc > len(nums) -1:
                return
            # 每根火柴有四个地方可以放置
            for i in range(4):
                if square[i] + nums[loc] <= check:
                    square[i] += nums[loc]
                    # 让下一个位置的火柴去放
                    if DFS(loc + 1, square):
                        return True
                    # 表示不应该放在这个位置
                    square[i] -= nums[loc]
            return False
        return DFS(0, [0, 0, 0, 0])
46. 全排列 - 不含重复

给定一个没有重复数字的序列,返回其所有可能的全排列。

思路:
就是DFS的套路,(一共有n个数字,n个位置)每个位置都有n个数字可以选择,在一次排列中一个数字只能属于一个位置。

 class Solution:
    def permute(self, nums):
        vis = set()
        r = []
        def DFS(loc, res):
            if loc == len(nums):
                r.append(res.copy())
            # 每个位置n个数字
            for i in nums:
                if i not in vis:
                    vis.add(i)
                    res[loc] = i
                    DFS(loc + 1, res)
                    vis.remove(i)
        DFS(0, [0] * len(nums))
        return sorted(r)
标签: