Leetcode之树

本文详细介绍了二叉树的各种遍历方法(前序、中序、后序),包括递归和非递归实现,并展示了如何进行二叉树的镜像、深度计算、寻找第k大节点、最近公共祖先、平衡性和对称性的判断。此外,还涵盖了自上而下的不同层次遍历以及二叉搜索树的后序遍历序列验证。
摘要由CSDN通过智能技术生成

0 二叉树的遍历

前序法:访问根结点→左子树→右子树 口诀:根左右
在这里插入图片描述

class Solution:
    def preorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
        if not root:
            return []
        return [root.val] + self.preorderTraversal(root.left) + self.preorderTraversal(root.right)
class Solution:
    def preorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
        res = []
        if not root:
            return res
        def dfs(root):
            res.append(root.val)
            if root.left:
                dfs(root.left)
            if root.right:
                dfs(root.right)
        
        dfs(root)
        return res

中序法:访问左子树→根结点→右子树 口诀:左根右
在这里插入图片描述

后序法:访问左子树→右子树→ 根结点 口诀:左右根

在这里插入图片描述

# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None
#

res1,res2,res3 = [],[],[]
class Solution:
    def preorder(self, root: TreeNode):
        if root:
            res1.append(root.val)
            self.preorder(root.left)
            self.preorder(root.right)
        return res1
    
    def midorder(self, root: TreeNode):
        if root:
            self.midorder(root.left)
            res2.append(root.val)
            self.midorder(root.right)
        return res2
    
    def lastorder(self, root: TreeNode):
        if root:
            self.lastorder(root.left)
            self.lastorder(root.right)
            res3.append(root.val)
        return res3
    
    def threeOrders(self , root: TreeNode) -> List[List[int]]:
        ret = []
        ret.append(self.preorder(root))
        ret.append(self.midorder(root))
        ret.append(self.lastorder(root))
        return ret
        

1 二叉树镜像

在这里插入图片描述

struct TreeNode* mirrorTree(struct TreeNode* root){
    if(root == NULL)
        return root;
    
    struct TreeNode * tmp = root->right;
    root->right = root->left;
    root->left = tmp;

    mirrorTree(root->right);
    mirrorTree(root->left);
    return root;
}
class Solution:
    def mirrorTree(self, root: TreeNode) -> TreeNode:
        if not root: return root
        root.left, root.right = root.right, root.left
        
        self.mirrorTree(root.left)
        self.mirrorTree(root.right)
        return root

栈的思想

class Solution:
    def mirrorTree(self, root: TreeNode) -> TreeNode:
        if not root: return root
        stack = [root]
        while stack:
            node = stack.pop(0)
            if node.left: stack.append(node.left)
            if node.right: stack.append(node.right)
            node.left, node.right = node.right, node.left  # key operation
        return root

2 二叉树的深度

对于每个根节点来说,寻找其每个子节点的最大深度
先序遍历,递归实现

int maxDepth(struct TreeNode* root){
    if(root == NULL)
        return 0;
    else
        return fmax(maxDepth(root->left), maxDepth(root->right)) + 1;
}

在这里插入图片描述
层序遍历:

class Solution:
    def maxDepth(self, root: TreeNode) -> int:
        if not root:
            return 0
        queue,res = [root], 0
        while queue:
            t = []
            for node in queue:
                if node.left: t.append(node.left)
                if node.right: t.append(node.right)
            queue = t
            res += 1
        return res

3 二叉搜索树中第k大的节点

给定一棵二叉搜索树,请找出其中第k大的节点。

二叉搜索树:左子树不大于根节点、右子树不小于根节点
要点:二叉搜索树的中序遍历为 递增序列
思路:首先申请一个数组空间,保存二叉搜索树的中序遍历,输出第 k-1 位,这里注意我们在中序遍历的时候使用的是先右子树在左子树,这样得到的中序遍历是 递减序列,这样我们直接输出 k-1 即为第 k 大的节点了。

void func(struct TreeNode *root, int *count, int *res)
{
    if(root != NULL) {
        func(root->right, count, res);
        res[(*count)++] = root->val;
        func(root->left, count, res);
    }  
}
int kthLargest(struct TreeNode* root, int k){
    int *res = (int *)malloc(sizeof(int)*10000);
    int count = 0;
    func(root, &count, res);
    return res[k-1];
}

层序遍历,取出所有的元素,排序后输出

class Solution:
    def kthLargest(self, root: TreeNode, k: int) -> int:
        que = [root]
        t = []
        len_que = len(que)
        while que:
            for _ in range(len(que)):
                node = que.pop(0)
                t.append(node.val)
                if node.left: que.append(node.left)
                if node.right: que.append(node.right)
            len_que = len(que) 
        t.sort(reverse=True)  
        return t[k-1]

中序遍历

class Solution:
    def kthLargest(self, root: TreeNode, k: int) -> int:
        def dfs(root):
            if not root: return
            dfs(root.right)
            if self.k == 0: return
            self.k -= 1
            if self.k == 0: self.res = root.val
            dfs(root.left)
        self.k = k
        dfs(root)
        return self.res

4 二叉树的最近公共祖先

最近公共祖先的定义为:对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。

思路:
lowestCommonAncestor(root, p, q) 的功能就是找出以 root 为根节点的两个节点 p q 的最近公共祖先,所以递归体要分三种情况讨论:
如果 p q 分别是 root 的左右节点,那么 root 就是我们要找的最近的公共祖先
如果 p q 都是 root 的右节点,那么返回 lowestCommonAncestor(root->right, p, q)
如果 p q 都是 root 的左节点,那么返回 lowestCommonAncestor(root->left, p, q)

struct TreeNode* lowestCommonAncestor(struct TreeNode* root, struct TreeNode* p, struct TreeNode* q){
    if(root == NULL || root == p || root == q)
        return root;
    struct TreeNode * left = lowestCommonAncestor(root->left, p, q);
    struct TreeNode * right = lowestCommonAncestor(root->right, p, q);
    if(left != NULL && right != NULL)
        return root;
    if(left == NULL && right != NULL)
        return right;
    if(left != NULL && right == NULL)
        return left;
    else
        return NULL;
}

5 二叉搜索树的最近公共祖先

此题与第4题类似,但条件不同的是二叉树,此题为二叉搜索树,二叉搜索树有一个性质就是左子树的值小于根节点,右子树的值大于根节点

思路一:同第四题
思路二:
当 p q 都是右子树时,root = root->right
当 p, q 都在左子树时, root = root->left
否则,说明找到了 最近公共祖先 ,跳出

struct TreeNode* lowestCommonAncestor(struct TreeNode* root, struct TreeNode* p, struct TreeNode* q) {
    while(root != NULL)
    {
        if(root->val < q->val && root->val < p->val){
            root = root->right;
        }
        else if(root->val > q->val && root->val > p->val){
            root = root->left;
        } else {
            break;
        }
    }
    return root;
}

思路三: 递归,原理是一样的

truct TreeNode* lowestCommonAncestor(struct TreeNode* root, struct TreeNode* p, struct TreeNode* q) {
    while(root != NULL)
    {
        if(root->val < q->val && root->val < p->val){
            return lowestCommonAncestor(root->right, p, q);
        }
        else if(root->val > q->val && root->val > p->val){
            return lowestCommonAncestor(root->left, p, q);
        } else {
            break;
        }
    }
    return root;
}

6 平衡二叉树

平衡二叉树的定义是:任意节点的左右子树的深度不超过1
思路1:先序遍历求深度+判断深度

int max_dep(struct TreeNode* root)
{
    if(root == NULL)
        return 0;
    else
        return fmax(max_dep(root->left), max_dep(root->right)) + 1;
}

bool isBalanced(struct TreeNode* root){
    if(root == NULL)
        return true;
    else
        return (fabs(max_dep(root->right)-max_dep(root->left)) <= 1 
        && isBalanced(root->left) && isBalanced(root->right)); 
}
class Solution:
    def isBalanced(self, root: TreeNode) -> bool:
        def depth(root):
            if not root: return 0
            return max(depth(root.left), depth(root.right)) + 1
        if not root: return True
        return abs(depth(root.left)-depth(root.right)) <= 1 and self.isBalanced(root.left) and self.isBalanced(root.right)

思路2:后序遍历 + 剪枝
后续遍历的顺序是 左右根
我们通过判断节点的左右子树的深度差,如果小于等于1,就返回子树的深度,否则直接返回 -1 表示当前二叉树不是平衡二叉树。

int func(struct TreeNode* root)
{
    if(root == NULL)
        return 0;
    int left = func(root->left);
    int right = func(root->right);
    if(left == -1 || right == -1)
        return -1;
    return fabs(left-right) < 2 ? fmax(left, right)+1 :-1;
}

bool isBalanced(struct TreeNode* root){
    if(root == NULL)
        return true;
    else
        return func(root) != -1;
}

7 对称二叉树

在这里插入图片描述

思路:对称二叉树有一个性质,那就是对称节点的值相等,以及
L.left.val = R.right.val
L.right.val = R.left.val

注意递归结束的边界:
左节点和右节点都为空 -> 到底了都长得一样 ->true
左节点为空的时候右节点不为空,或反之 -> 长得不一样-> false
左右节点值不相等 -> 长得不一样 -> false

int func(struct TreeNode* p, struct TreeNode* q)
{
    if(p == NULL && q == NULL)
        return true;
    else if (p == NULL || q == NULL || p->val != q->val)
        return false;
    else
        return func(p->left, q->right) && func(p->right, q->left);
}
bool isSymmetric(struct TreeNode* root){
    if(root == NULL)
        return true;
    else 
        return func(root->left, root->right);
}

层序遍历(注意弹出操作)

def isSymmetric(root):
    if not root: return True
    que=[root]
    len_que=len(que)
    while que:
        temp=[]
        for _ in range(len_que):
            node=que.pop(0) 			# key point
            if node: 
                temp.append(node.val)
                que.append(node.left)
                que.append(node.right)
            else:
                temp.append(None)
        if temp!=temp[::-1]: return False
        len_que=len(que)		# resize the length
    return True

8 自上而下打印二叉树 I

二叉树的 广度优先搜索(BFS)BFS 通常借助 队列 的先入先出特性来实现。
例如:
给定二叉树: [3,9,20,null,null,15,7],
在这里插入图片描述

返回:[3,9,20,15,7]

思路:首先构建一个队列和一个数组,节点依次入队出队,出队的节点的值将会被保存到返回数组中
拿上面的例子来说,先将节点3入队,出队保存到数组,因为节点3的左右子树均不为空,将左右子树9,20入队,9出队,因为9的左右子树为空,20出队,接着将15,7入队…

#define MAX_NUM 2000

int *levelOrder(struct TreeNode *root, int *returnSize)
{
    *returnSize = 0;
    if (root == NULL) {
        return NULL;
    }

    int *ret = (int *)malloc(sizeof(int) * MAX_NUM);
    if (ret == NULL) {
        return NULL;
    }

    struct TreeNode *queue[MAX_NUM];
    int head = 0;
    int tail = 0;
    queue[tail++] = root;

    while (head != tail) {
        int last = tail;
        while (head < last) {
            struct TreeNode *cur = queue[head++];
            ret[(*returnSize)++] = cur->val;
            if (cur->left != NULL) {
                queue[tail++] = cur->left;
            }
            if (cur->right != NULL) {
                queue[tail++] = cur->right;
            }
        }
    }
    return ret;
} 
class Solution:
    def levelOrder(self, root: TreeNode) -> List[int]:
        if not root: return []
        res, queue = [], collections.deque()
        queue.append(root)
        while queue:
            node = queue.popleft()
            res.append(node.val)
            if node.left: queue.append(node.left)
            if node.right: queue.append(node.right)
        return res

9 自上而下打印二叉树 II

例如:
给定二叉树: [3,9,20,null,null,15,7],
在这里插入图片描述
返回:
[ [3], [9,20], [15,7] ]

思路:此题的返回数组要求是一个二维数组,思想和上一题是一样的,每次按层输出一个数组,C语言操作起来的确是复杂的很…

#define MAXSIZE 2000
int** levelOrder(struct TreeNode* root, int* returnSize, int** returnColumnSizes)
{
    if (root == NULL) {
        *returnSize = 0;
        return NULL;
    }

    struct TreeNode *queue[MAXSIZE] = {0};
    int head = 0;
    int tail = 0;
    int **ret = (int**)malloc(sizeof(int*) * MAXSIZE);
    if (ret == NULL) {
        return NULL;
    }
    memset(ret, 0, sizeof(int*) * MAXSIZE);

    *returnColumnSizes = (int*)malloc(sizeof(int*) * MAXSIZE);
    if (*returnColumnSizes == NULL) {
        return NULL;
    }
    memset(*returnColumnSizes, 0, sizeof(int*) * MAXSIZE);

    queue[tail++] = root;   //push
    *returnSize = 0;
    while (head < tail) {
        int size = (tail - head + MAXSIZE) % MAXSIZE;
        (*returnColumnSizes)[*returnSize] = size;
        ret[*returnSize] = (int*)malloc(sizeof(int) * size);
        for (int i = 0; i < size; i++) {
            struct TreeNode *temp = queue[head++];  //pop
            ret[*returnSize][i] = temp->val;
            if (temp->left != NULL) {
                queue[tail++] = temp->left;
            }
            if (temp->right != NULL) {
                queue[tail++] = temp->right;
            }
        }
        (*returnSize)++;
    }
    return ret;
}
class Solution(object):
    def levelOrder(self, root):
        que = [root]
        len_que = len(que)
        res = []
        if not root: return res
        while que:
            tmp = []
            for _ in range(len_que):
                node = que.pop(0)
                tmp.append(node.val)
                if node.left: que.append(node.left) 
                if node.right: que.append(node.right)
            len_que = len(que) 
            res.append(tmp)   
        return res

10 自上而下打印二叉树 III

要求在输出的时候,每偶数行逆序
思路:核心思想和前面两题是一样的,在实现上,只需要在第二题的基础上,判断是否是偶数行,如果是就逆序。

#define MAXSIZE 2000
int** levelOrder(struct TreeNode* root, int* returnSize, int** returnColumnSizes)
{
    if (root == NULL) {
        *returnSize = 0;
        return NULL;
    }

    struct TreeNode *queue[MAXSIZE] = {0};
    int head = 0;
    int tail = 0;
    int **ret = (int**)malloc(sizeof(int*) * MAXSIZE);
    if (ret == NULL) {
        return NULL;
    }
    memset(ret, 0, sizeof(int*) * MAXSIZE);

    *returnColumnSizes = (int*)malloc(sizeof(int*) * MAXSIZE);
    if (*returnColumnSizes == NULL) {
        return NULL;
    }
    memset(*returnColumnSizes, 0, sizeof(int*) * MAXSIZE);

    queue[tail++] = root;   //push
    *returnSize = 0;
    while (head < tail) {
        int size = (tail - head + MAXSIZE) % MAXSIZE;
        (*returnColumnSizes)[*returnSize] = size;
        ret[*returnSize] = (int*)malloc(sizeof(int) * size);
        for (int i = 0; i < size; i++) {
            struct TreeNode *temp = queue[head++];  //pop
            ret[*returnSize][i] = temp->val;
            if (temp->left != NULL) {
                queue[tail++] = temp->left;
            }
            if (temp->right != NULL) {
                queue[tail++] = temp->right;
            }
        }
        if((*returnSize) % 2 != 0){
            for(int i = 0; i < size/2; i++){
                int temp;
                temp = ret[*returnSize][i];
                ret[*returnSize][i] = ret[*returnSize][size-1-i];
                ret[*returnSize][size-1-i] =  temp;
            }                        
        }
        (*returnSize)++;
    }
    return ret;
}
class Solution:
    def levelOrder(self, root: TreeNode) -> List[List[int]]:
        tmp, queue = [], collections.deque()
        if not root: return tmp
        queue.append(root)
        len_que = len(queue)
        k = 2
        while queue:
            res = []
            for _ in range(len_que):
                node = queue.popleft()
                res.append(node.val)
                if node.left: queue.append(node.left)
                if node.right: queue.append(node.right)
            tmp.append(res) if k%2==0 else tmp.append(res[::-1])
            k += 1
            len_que = len(queue)
        return tmp

11 二叉搜索树的后序遍历序列

判断给定序列是否是二叉搜索树的后续遍历序列
思路:首先题目给的是二叉搜索树的条件,我们知道二叉搜索树的性质是,根节点、左子树、右子树的大小关系为 left < root < right,同时,后续遍历序列是 左右根 那么得到的序列的最后一个元素必然是根节点,那么我们从根节点往前找,前一小搓是左子树,他的值肯定是小于根节点的,我们就从前

bool verifyPostorder(int* postorder, int postorderSize){
    if(postorderSize == 0 || postorderSize == 1)
        return true;
    int i,j;
    for(i = 0; i < postorderSize - 1; i++){
        if(postorder[i] > postorder[postorderSize-1])
            break;
    }
    for(j = i; j < postorderSize - 1; j++){
        if(postorder[j] < postorder[postorderSize-1])
            return false;
    }   
    return verifyPostorder(postorder, i) && verifyPostorder(&postorder[i], postorderSize-1-i);
}
class Solution:
    def verifyPostorder(self, postorder: [int]) -> bool:
        def recur(i, j):
            if i >= j: return True
            p = i
            while postorder[p] < postorder[j]: p += 1
            m = p
            while postorder[p] > postorder[j]: p += 1
            return p == j and recur(i, m - 1) and recur(m, j - 1)

        return recur(0, len(postorder) - 1)
class Solution:
    def verifyPostorder(self, postorder: List[int]) -> bool:
        if not postorder: return True
        index, n = 0, len(postorder)
        while index < n and postorder[index] < postorder[-1]:
            index += 1
        for i in range(index, n-1):
            if postorder[i] < postorder[-1]:
                return False
        return self.verifyPostorder(postorder[:index]) and self.verifyPostorder(postorder[index:n-1])

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

给你二叉树的根节点 root 和一个整数目标和 targetSum ,找出所有 从根节点到叶子节点 路径总和等于给定目标和的路径。

思路:考虑深度优先遍历,先序遍历按照根、左、右的顺序,遍历所有节点。
定义一个 dfs 函数,主要做如下工作:
路径更新:将当前节点值 root->val 加入路径 path
目标值更新:target - = root-val
路径记录:当 root 为叶节点(无子树)且路径和等于目标值时,将此路径 path 加入 res 中
先序遍历:递归左/右子节点
路径恢复:向上回溯前,需要将当前节点从路径 path 中删除

int** ret;
int retSize;
int* retColSize;
int* path;
int pathSize;

void dfs(struct TreeNode* root, int target) {
    if (root == NULL) {
        return;
    }
    path[pathSize++] = root->val;
    target -= root->val;
    if (root->left == NULL && root->right == NULL && target == 0) {
        int* tmp = malloc(sizeof(int) * pathSize);
        memcpy(tmp, path, sizeof(int) * pathSize);
        ret[retSize] = tmp;
        retColSize[retSize++] = pathSize;
    }
    dfs(root->left, target);
    dfs(root->right, target);
    pathSize--;
}

int** pathSum(struct TreeNode* root, int target, int* returnSize, int** returnColumnSizes) {
    ret = malloc(sizeof(int*) * 2001);
    retColSize = malloc(sizeof(int) * 2001);
    path = malloc(sizeof(int) * 2001);
    retSize = pathSize = 0;
    dfs(root, target);
    *returnColumnSizes = retColSize;
    *returnSize = retSize;
    return ret;
}

class Solution:
    def pathSum(self, root: TreeNode, sum: int) -> List[List[int]]:
        res, path = [], []
        def recur(root, tar):
            if not root: return
            path.append(root.val)
            tar -= root.val
            if tar == 0 and not root.left and not root.right:
                res.append(list(path))
            recur(root.left, tar)
            recur(root.right, tar)
            path.pop()

        recur(root, sum)
        return res
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值