面试经典算法系列之二叉数6 -- 二叉树的右视图

面试经典算法21 - 二叉树的右视图

LeetCode.199
公众号:阿Q技术站

问题描述

给定一个二叉树的 根节点 root,想象自己站在它的右侧,按照从顶部到底部的顺序,返回从右侧所能看到的节点值。

示例 1:

输入: [1,2,3,null,5,null,4]
输出: [1,3,4]

示例 2:

输入: [1,null,3]
输出: [1,3]

示例 3:

输入: []
输出: []

提示:

  • 二叉树的节点个数的范围是 [0,100]
  • -100 <= Node.val <= 100

思路

递归
  1. 递归遍历二叉树:从根节点开始,按照根右左的顺序递归遍历整棵二叉树。这样可以保证每一层最先访问到的节点是最右侧的节点。
  2. 记录每层最右侧节点的值:在遍历过程中,使用一个 map 或类似的数据结构来记录每一层最右侧节点的值。如果当前层级已经在 map 中存在,则更新该层级的值为当前节点的值。
  3. 按层级顺序输出结果:最后,将 map 中按层级存储的节点值按顺序存入结果数组,并返回该数组作为最终的结果。
非递归
  1. 定义一个队列 queue 用于层序遍历二叉树,一个数组 result 用于存储每层最右侧节点的值。
  2. 将根节点 root 入队。
  3. 在队列不为空的情况下,循环遍历每一层的节点:
    • 记录当前队列的大小 size,表示当前层级的节点数。
    • 遍历当前层级的节点,如果是当前层级的最后一个节点,则将其值加入 result 数组中。
    • 将当前节点的左右子节点入队。
  4. 返回 result 数组,其中存储了每层最右侧节点的值。

图解

  1. 定义一个队列 queue 用于层序遍历二叉树,一个数组 result 用于存储每层最右侧节点的值。将根节点 root 入队。

  1. 如果队列不为空,遍历当前层级的节点,获取队头,并将其弹出队列;如果是当前层级的最后一个元素,将其加入结果数组。当前层级遍历结束。

  1. 将当前节点的左右节点依次加入到队列中。

  1. 开始遍历当前层级在队列中的元素,获取队头的元素,弹出元素,并将当前节点的左右子节点入队。

  1. 继续遍历,如果是当前层级的最后一个元素,加入结果集。并将当前节点的左右子节点入队。当前层级结束。

  1. 遍历当前层级的元素,获取队头元素并弹出。

  1. 继续遍历当前层级,如果是最后一个节点,将其加入结果集中。

  1. 队列为空,退出循环。

参考代码

C++
递归
#include <iostream>
#include <vector>
#include <map>
#include <queue>

using namespace std;

// 二叉树节点的定义
struct TreeNode {
    int val;
    TreeNode* left;
    TreeNode* right;
    TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};

void dfs(TreeNode* node, int level, map<int, int>& rightmost) {
    if (!node) return; // 如果节点为空,直接返回

    if (rightmost.find(level) == rightmost.end()) { // 如果当前层级不在map中,将当前节点的值存入map
        rightmost[level] = node->val;
    }

    // 递归遍历右子树和左子树,层级加一
    dfs(node->right, level + 1, rightmost);
    dfs(node->left, level + 1, rightmost);
}

vector<int> rightSideView(TreeNode* root) {
    vector<int> result; // 存储结果的数组
    map<int, int> rightmost; // 存储每层最右侧节点值的map
    dfs(root, 0, rightmost); // 调用递归函数开始遍历

    // 将map中的节点值按层级顺序存入结果数组
    for (auto& [level, val] : rightmost) {
        result.push_back(val);
    }

    return result;
}

// 创建二叉树
TreeNode* createTree(vector<int>& nodes, int index) {
    if (index >= nodes.size() || nodes[index] == -1) {
        return nullptr; // 如果节点为空,则返回nullptr
    }
    TreeNode* root = new TreeNode(nodes[index]); // 创建当前节点
    root->left = createTree(nodes, 2 * index + 1); // 创建左子树
    root->right = createTree(nodes, 2 * index + 2); // 创建右子树
    return root; // 返回当前节点
}

// 销毁二叉树
void destroyTree(TreeNode* root) {
    if (!root) return; // 如果根节点为空,直接返回
    destroyTree(root->left); // 递归销毁左子树
    destroyTree(root->right); // 递归销毁右子树
    delete root; // 删除当前节点
}

int main() {
    vector<int> nodes = {1, 2, 3, -1, 5, -1, 4}; // 定义二叉树的层序遍历序列
    TreeNode* root = createTree(nodes, 0); // 创建二叉树
    vector<int> result = rightSideView(root); // 进行右视图遍历
    for (int val : result) { // 输出遍历结果
        cout << val << " ";
    }
    cout << endl;

    destroyTree(root); // 销毁二叉树

    return 0;
}
非递归
#include <iostream>
#include <vector>
#include <queue>

using namespace std;

// 二叉树节点的定义
struct TreeNode {
    int val;
    TreeNode* left;
    TreeNode* right;
    TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};

vector<int> rightSideView(TreeNode* root) {
    vector<int> result; // 存储结果的数组
    if (!root) {
        return result; // 如果根节点为空,直接返回空数组
    }

    queue<TreeNode*> q; // 辅助队列,用于层级遍历
    q.push(root); // 将根节点入队

    while (!q.empty()) {
        int level_size = q.size(); // 当前层级的节点数
        for (int i = 0; i < level_size; ++i) {
            TreeNode* node = q.front(); // 获取队头节点
            q.pop(); // 弹出队头节点

            // 如果是当前层级的最后一个节点,将其值加入结果数组
            if (i == level_size - 1) {
                result.push_back(node->val);
            }

            // 将当前节点的左右子节点入队
            if (node->left) {
                q.push(node->left);
            }
            if (node->right) {
                q.push(node->right);
            }
        }
    }

    return result; // 返回结果数组
}

// 创建二叉树
TreeNode* createTree(vector<int>& nodes, int index) {
    if (index >= nodes.size() || nodes[index] == -1) {
        return nullptr; // 如果节点为空,则返回nullptr
    }
    TreeNode* root = new TreeNode(nodes[index]); // 创建当前节点
    root->left = createTree(nodes, 2 * index + 1); // 创建左子树
    root->right = createTree(nodes, 2 * index + 2); // 创建右子树
    return root; // 返回当前节点
}

// 销毁二叉树
void destroyTree(TreeNode* root) {
    if (!root) return; // 如果根节点为空,直接返回
    destroyTree(root->left); // 递归销毁左子树
    destroyTree(root->right); // 递归销毁右子树
    delete root; // 删除当前节点
}

int main() {
    vector<int> nodes = {1, 2, 3, -1, 5, -1, 4}; // 二叉树的层序遍历序列
    TreeNode* root = createTree(nodes, 0); // 创建二叉树
    vector<int> result = rightSideView(root); // 获取从右侧所能看到的节点值
    for (int val : result) { // 输出结果
        cout << val << " ";
    }
    cout << endl;

    destroyTree(root); // 销毁二叉树

    return 0;
}
Java
import java.util.*;

// 二叉树节点的定义
class TreeNode {
    int val;
    TreeNode left;
    TreeNode right;
    TreeNode(int x) { val = x; }
}

public class RightSideView {
    public List<Integer> rightSideView(TreeNode root) {
        List<Integer> result = new ArrayList<>(); // 存储结果的列表
        if (root == null) {
            return result; // 如果根节点为空,直接返回空列表
        }

        Queue<TreeNode> queue = new LinkedList<>(); // 辅助队列,用于层级遍历
        queue.add(root); // 将根节点入队

        while (!queue.isEmpty()) {
            int levelSize = queue.size(); // 当前层级的节点数
            for (int i = 0; i < levelSize; ++i) {
                TreeNode node = queue.poll(); // 获取队头节点

                // 如果是当前层级的最后一个节点,将其值加入结果列表
                if (i == levelSize - 1) {
                    result.add(node.val);
                }

                // 将当前节点的左右子节点入队
                if (node.left != null) {
                    queue.add(node.left);
                }
                if (node.right != null) {
                    queue.add(node.right);
                }
            }
        }

        return result; // 返回结果列表
    }

    // 创建二叉树
    public TreeNode createTree(Integer[] nodes, int index) {
        if (index >= nodes.length || nodes[index] == null) {
            return null; // 如果节点为空,则返回null
        }
        TreeNode root = new TreeNode(nodes[index]); // 创建当前节点
        root.left = createTree(nodes, 2 * index + 1); // 创建左子树
        root.right = createTree(nodes, 2 * index + 2); // 创建右子树
        return root; // 返回当前节点
    }

    // 销毁二叉树
    public void destroyTree(TreeNode root) {
        if (root == null) return; // 如果根节点为空,直接返回
        destroyTree(root.left); // 递归销毁左子树
        destroyTree(root.right); // 递归销毁右子树
    }

    public static void main(String[] args) {
        Integer[] nodes = {1, 2, 3, null, 5, null, 4}; // 二叉树的层序遍历序列
        RightSideView solution = new RightSideView();
        TreeNode root = solution.createTree(nodes, 0); // 创建二叉树
        List<Integer> result = solution.rightSideView(root); // 获取从右侧所能看到的节点值
        for (int val : result) { // 输出结果
            System.out.print(val + " ");
        }
        System.out.println();

        solution.destroyTree(root); // 销毁二叉树
    }
}
Python
from typing import List
from collections import deque

# 二叉树节点的定义
class TreeNode:
    def __init__(self, val=0, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right

def rightSideView(root: TreeNode) -> List[int]:
    result = [] # 存储结果的列表
    if not root:
        return result # 如果根节点为空,直接返回空列表

    queue = deque([root]) # 辅助队列,用于层级遍历
    while queue:
        level_size = len(queue) # 当前层级的节点数
        for i in range(level_size):
            node = queue.popleft() # 获取队头节点

            # 如果是当前层级的最后一个节点,将其值加入结果列表
            if i == level_size - 1:
                result.append(node.val)

            # 将当前节点的左右子节点入队
            if node.left:
                queue.append(node.left)
            if node.right:
                queue.append(node.right)

    return result # 返回结果列表

# 创建二叉树
def createTree(nodes: List[int], index: int) -> TreeNode:
    if index >= len(nodes) or nodes[index] is None:
        return None # 如果节点为空,则返回None
    root = TreeNode(nodes[index]) # 创建当前节点
    root.left = createTree(nodes, 2 * index + 1) # 创建左子树
    root.right = createTree(nodes, 2 * index + 2) # 创建右子树
    return root # 返回当前节点

# 销毁二叉树
def destroyTree(root: TreeNode) -> None:
    if not root:
        return # 如果根节点为空,直接返回
    destroyTree(root.left) # 递归销毁左子树
    destroyTree(root.right) # 递归销毁右子树

# 测试代码
if __name__ == "__main__":
    nodes = [1, 2, 3, None, 5, None, 4] # 二叉树的层序遍历序列
    root = createTree(nodes, 0) # 创建二叉树
    result = rightSideView(root) # 获取从右侧所能看到的节点值
    print(result) # 输出结果
    destroyTree(root) # 销毁二叉树
  • 32
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 算法6-1:二叉链表存储的二叉树的结构定义 二叉链表存储的二叉树是一种常见的二叉树存储方式,其结构定义如下: typedef struct BiTNode{ TElemType data; // 数据域 struct BiTNode *lchild, *rchild; // 左孩子指针 }BiTNode, *BiTree; 其中,TElemType为二叉树节点的数据类型,可以根据实际情况进行定义。 算法6-2:二叉链表存储的二叉树的创建 二叉链表存储的二叉树的创建可以通过递归方式实现,具体算法如下: void CreateBiTree(BiTree *T){ TElemType ch; scanf("%c", &ch); // 输入节点的值 if(ch == '#'){ // 如果输入的是#,表示该节点为空 *T = NULL; }else{ *T = (BiTree)malloc(sizeof(BiTNode)); // 创建新节点 (*T)->data = ch; // 节点赋值 CreateBiTree(&((*T)->lchild)); // 递归创建左子树 CreateBiTree(&((*T)->rchild)); // 递归创建子树 } } 其中,#表示空节点,输入#时,该节点为空。 算法6-3:二叉链表存储的二叉树的遍历 二叉链表存储的二叉树的遍历可以通过递归方式实现,具体算法如下: void PreOrderTraverse(BiTree T){ // 先序遍历 if(T != NULL){ printf("%c ", T->data); // 访问根节点 PreOrderTraverse(T->lchild); // 遍历左子树 PreOrderTraverse(T->rchild); // 遍历子树 } } void InOrderTraverse(BiTree T){ // 中序遍历 if(T != NULL){ InOrderTraverse(T->lchild); // 遍历左子树 printf("%c ", T->data); // 访问根节点 InOrderTraverse(T->rchild); // 遍历子树 } } void PostOrderTraverse(BiTree T){ // 后序遍历 if(T != NULL){ PostOrderTraverse(T->lchild); // 遍历左子树 PostOrderTraverse(T->rchild); // 遍历子树 printf("%c ", T->data); // 访问根节点 } } 其中,先序遍历是先访问根节点,再遍历左子树和子树;中序遍历是先遍历左子树,再访问根节点和子树;后序遍历是先遍历左子树和子树,再访问根节点。 算法6-4:二叉链表存储的二叉树的销毁 二叉链表存储的二叉树的销毁可以通过递归方式实现,具体算法如下: void DestroyBiTree(BiTree *T){ if(*T != NULL){ DestroyBiTree(&((*T)->lchild)); // 销毁左子树 DestroyBiTree(&((*T)->rchild)); // 销毁子树 free(*T); // 释放节点空间 *T = NULL; // 节点指针置空 } } 其中,递归销毁左子树和子树,最后释放节点空间并将节点指针置空。 ### 回答2: 二叉链表存储的二叉树是一种常见的二叉树存储结构,它是由一个节点数组和一个指向根节点的指针组成的。每个节点都包含一个值、一个左孩子指针和一个孩子指针。这种存储结构可以非常方便地进行二叉树的遍历和修改操作,是二叉树基本操作的重要实现方式。 算法6-1是建立二叉链表存储结构的算法,它通过前序遍历和中序遍历来构造一棵二叉树。具体而言,算法6-1会先遍历前序序列找到根节点,并在中序序列中找到根节点的位置,然后递归地建立左子树和子树。该算法时间复杂度为O(n^2),其中n为二叉树的节点数。算法6-1的缺点是在处理大规模的二叉树时效率较低。 算法6-2是先序遍历的递归算法,它通过递归实现先序遍历。具体而言,先访问根节点,然后递归访问左子树和子树。该算法时间复杂度为O(n),其中n为二叉树的节点数。 算法6-3是中序遍历的递归算法,它通过递归实现中序遍历。具体而言,先递归访问左子树,然后访问根节点,最后递归访问子树。该算法的时间复杂度为O(n),其中n为二叉树的节点数。 算法6-4是后序遍历的递归算法,它通过递归实现后序遍历。具体而言,先递归访问左子树和子树,最后访问根节点。该算法的时间复杂度为O(n),其中n为二叉树的节点数。 总之,二叉链表存储的二叉树是一种非常方便实用的数据结构,可以方便地进行各种遍历和修改操作。同时,基于递归实现的三种遍历算法也非常简洁高效,应用广泛。 ### 回答3: 算法6-1至6-4描述了二叉链表存储的二叉树。二叉链表存储的二叉树是一种基于线性链式结构存储的二叉树,它通过指针关系将每个结点的左子树联系起来。下面我们将分别对每个算法进行详细解释。 算法6-1:二叉链表存储结构定义。该算法定义了二叉树的结构,主要是通过指针关系分别指向左子树和父节点。这样的结构便于对二叉树的操作和遍历。 算法6-2:二叉链表存储的建立。该算法通过输入有序序列,依次插入二叉树结点,先从根结点开始比较大小,插入到左子树中。当插入到空节点时,创建新的结点,通过指针关系连接起来。递归地进行插入操作,直到序列中的所有元素插入完毕。 算法6-3:二叉链表存储的遍历。该算法通过对二叉树的先序、中序和后序遍历进行递归实现。先序遍历需要先访问根节点,然后再对左子树进行遍历;中序遍历需要先访问左子树,再访问根节点,最后再访问子树;后序遍历需要先访问左子树,最后访问根节点。 算法6-4:二叉链表的基本操作。该算法主要包括插入、删除、查找和修改等操作。其中,插入和删除操作需要先定位到相应的结点,然后通过指针关系进行连接或删除。查找操作需要按照二叉树的规律进行查找,找到目标结点后返回其对应的指针。修改操作类似于删除操作,先找到需要修改的结点,然后进行相应的修改操作。 综上所述,二叉链表存储的二叉树是一种便于操作和遍历的数据结构,它通过指针关系将每个结点的左子树联系起来。该结构的建立、遍历和操作都可以通过递归实现,不仅提高了程序的可读性,还方便了程序员的开发。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值