代码随想录算法训练营,9月9日 | 二叉树递归遍历,迭代遍历,层序遍历
二叉树理论基础
1.二叉树的种类:满二叉树:深度为k,有2^k-1个节点的二叉树;完全二叉树:除最后一层外的所有层全满,而且最后一层的结点集中在最左边,中间不能空
2.二叉搜索树:若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;它的左、右子树也分别为二叉搜索树
3.平衡二叉搜索树:在二叉搜索树的基础上。左右子树的高度差绝对值不超过1。(题外话:HashMap/HashSet: 哈希表实现,平均时间复杂度 O(1);TreeMap/TreeSet: 红黑树实现,时间复杂度 O(log n);LinkedHashMap/LinkedHashSet: 哈希表 + 双向链表,保持插入顺序;ConcurrentHashMap: 分段锁/红黑树 + 哈希表,适用于并发场景。)
4.二叉树的存储方式:二叉树可以链式存储,也可以顺序存储(数组:i,左2i+1,右2i+2)。
5.二叉树的遍历方式:深度优先遍历:前序遍历(中左右)、中序遍历(左中右)、后序遍历(左右中);广度优先遍历:层次遍历(迭代法)
6.二叉树的定义:
public class TreeNode {
int val;
TreeNode left;
TreeNode right;
TreeNode() {}
TreeNode(int val) { this.val = val; }
TreeNode(int val, TreeNode left, TreeNode right) {
this.val = val;
this.left = left;
this.right = right;
}
}
二叉树的前序遍历
题目链接:144.二叉树的前序遍历
文档讲解︰代码随想录(programmercarl.com)
日期:2024-09-09
想法:1.递归:递归三要素:确定递归函数的参数和返回值,确定终止条件,确定单层递归的逻辑;2.迭代:递归的实现就是:每一次递归调用都会把函数的局部变量、参数值和返回地址等压入调用栈中,所以用栈来做迭代法,入栈根结点,每次取“中“位置的,再入栈右左(出栈是才能是左右)。
Java代码如下:
//递归
class Solution {
public void preOrder(TreeNode root, List<Integer> list){
if(root == null){
return;
}
list.add(root.val);
preOrder(root.left, list);
preOrder(root.right, list);
}
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> list = new ArrayList<Integer>();
preOrder(root, list);
return list;
}
}
//迭代
class Solution {
public List<Integer> preorderTraversal(TreeNode root) {
Deque<TreeNode> st = new LinkedList<>();
List<Integer> list = new ArrayList<>();
if(root == null){
return list;
}
st.push(root);
while(!st.isEmpty()){
TreeNode node = st.pop();
list.add(node.val);
if(node.right != null){
st.push(node.right);
}
if(node.left != null){
st.push(node.left);
}
}
return list;
}
}
总结:前序中左右,递归确定三要素,递归函数的参数和返回,递归中止条件,单层逻辑;迭代用栈来完成,想象怎么进,出的时候才能顺序正确。
二叉树的后序遍历
题目链接:145.二叉树的后序遍历
文档讲解︰代码随想录(programmercarl.com)
日期:2024-09-09
Java代码如下:
//递归
class Solution {
public void postOrder(TreeNode root, List<Integer> list){
if(root == null){
return;
}
postOrder(root.left, list);
postOrder(root.right, list);
list.add(root.val);
}
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> list = new ArrayList<Integer>();
postOrder(root, list);
return list;
}
}
//迭代
class Solution {
public List<Integer> postorderTraversal(TreeNode root) {
Deque<TreeNode> st = new ArrayDeque<>();
List<Integer> list = new ArrayList<>();
if(root == null){
return list;
}
st.push(root);
while(!st.isEmpty()){
TreeNode node = st.pop();
list.add(node.val);
if(node.left != null){
st.push(node.left);
}
if(node.right != null){
st.push(node.right);
}
}
Collections.reverse(list);
return list;
}
}
总结:后序左右中(中右左反着),跟前序很类似。
二叉树的中序遍历
题目链接:94.二叉树的中序遍历
文档讲解︰代码随想录(programmercarl.com)
日期:2024-09-09
想法:中序遍历是左中右,先访问的是二叉树顶部的节点,然后一层一层向下访问,直到到达树左面的最底部,再开始处理节点,需要借用指针的遍历来帮助访问节点,栈则用来处理节点上的元素
Java代码如下:
//递归
class Solution {
public void inOrder(TreeNode root, List<Integer> list){
if(root == null){
return;
}
inOrder(root.left, list);
list.add(root.val);
inOrder(root.right, list);
}
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> list = new ArrayList<Integer>();
inOrder(root, list);
return list;
}
}
//迭代
class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> result = new ArrayList<>();
if (root == null){
return result;
}
Deque<TreeNode> st = new ArrayDeque<>();
TreeNode cur = root;
while (cur != null || !st.isEmpty()){
if (cur != null){
st.push(cur);
cur = cur.left;
}else{
cur = st.pop();
result.add(cur.val);
cur = cur.right;
}
}
return result;
}
}
总结:中序,左中右,迭代法与前后序不一样,得先左边走到底,再中,再右。
二叉树的层序遍历
题目链接:102.二叉树的层序遍历
107.二叉树的层次遍历II
199.二叉树的右视图
637.二叉树的层平均值
429.N叉树的层序遍历
515.在每个树行中找最大值
116.填充每个节点的下一个右侧节点指针
117.填充每个节点的下一个右侧节点指针II
104.二叉树的最大深度
111.二叉树的最小深度
文档讲解︰代码随想录(programmercarl.com)
日期:2024-09-09
Java代码如下:
//二叉树的层序遍历
class Solution {
public List<List<Integer>> res = new ArrayList<List<Integer>>();
public List<List<Integer>> levelOrder(TreeNode root) {
check(root);
return res;
}
public void check(TreeNode node) {
if (node == null) return;
Queue<TreeNode> que = new LinkedList<TreeNode>();
que.offer(node);
while (!que.isEmpty()) {
List<Integer> itemList = new ArrayList<Integer>();
int len = que.size();
while (len > 0) {
TreeNode tmpNode = que.poll();
itemList.add(tmpNode.val);
if (tmpNode.left != null) que.offer(tmpNode.left);
if (tmpNode.right != null) que.offer(tmpNode.right);
len--;
}
res.add(itemList);
}
}
}
//二叉树的层次遍历II
class Solution {
public List<List<Integer>> levelOrderBottom(TreeNode root) {
List<List<Integer>> list = new ArrayList<>();
Deque<TreeNode> que = new LinkedList<>();
if (root == null) {
return list;
}
que.offerLast(root);
while (!que.isEmpty()) {
List<Integer> levelList = new ArrayList<>();
int levelSize = que.size();
for (int i = 0; i < levelSize; i++) {
TreeNode peek = que.peekFirst();
levelList.add(que.pollFirst().val);
if (peek.left != null) {
que.offerLast(peek.left);
}
if (peek.right != null) {
que.offerLast(peek.right);
}
}
list.add(levelList);
}
List<List<Integer>> result = new ArrayList<>();
for (int i = list.size() - 1; i >= 0; i-- ) {
result.add(list.get(i));
}
return result;
}
}
//二叉树的右视图
class Solution {
public List<Integer> rightSideView(TreeNode root) {
List<Integer> list = new ArrayList<>();
Deque<TreeNode> que = new LinkedList<>();
if (root == null) {
return list;
}
que.offerLast(root);
while (!que.isEmpty()) {
int levelSize = que.size();
for (int i = 0; i < levelSize; i++) {
TreeNode poll = que.pollFirst();
if (poll.left != null) {
que.addLast(poll.left);
}
if (poll.right != null) {
que.addLast(poll.right);
}
if (i == levelSize - 1) {
list.add(poll.val);
}
}
}
return list;
}
}
//二叉树的层平均值
class Solution {
public List<Double> averageOfLevels(TreeNode root) {
List<Double> list = new ArrayList<>();
Deque<TreeNode> que = new LinkedList<>();
if (root == null) {
return list;
}
que.offerLast(root);
while (!que.isEmpty()) {
int levelSize = que.size();
double levelSum = 0.0;
for (int i = 0; i < levelSize; i++) {
TreeNode poll = que.pollFirst();
levelSum += poll.val;
if (poll.left != null) {
que.addLast(poll.left);
}
if (poll.right != null) {
que.addLast(poll.right);
}
}
list.add(levelSum / levelSize);
}
return list;
}
}
//N 叉树的层序遍历
class Solution {
public List<List<Integer>> levelOrder(Node root) {
List<List<Integer>> list = new ArrayList<>();
Deque<Node> que = new LinkedList<>();
if (root == null) {
return list;
}
que.offerLast(root);
while (!que.isEmpty()) {
int levelSize = que.size();
List<Integer> levelList = new ArrayList<>();
for (int i = 0; i < levelSize; i++) {
Node poll = que.pollFirst();
levelList.add(poll.val);
List<Node> children = poll.children;
if (children == null || children.size() == 0) {
continue;
}
for (Node child : children) {
if (child != null) {
que.offerLast(child);
}
}
}
list.add(levelList);
}
return list;
}
}
//在每个树行中找最大值
class Solution {
public List<Integer> largestValues(TreeNode root) {
if(root == null){
return Collections.emptyList();
}
List<Integer> result = new ArrayList();
Queue<TreeNode> queue = new LinkedList();
queue.offer(root);
while(!queue.isEmpty()){
int max = Integer.MIN_VALUE;
for(int i = queue.size(); i > 0; i--){
TreeNode node = queue.poll();
max = Math.max(max, node.val);
if(node.left != null) queue.offer(node.left);
if(node.right != null) queue.offer(node.right);
}
result.add(max);
}
return result;
}
}
//填充每个节点的下一个右侧节点指针
class Solution {
public Node connect(Node root) {
Queue<Node> tmpQueue = new LinkedList<Node>();
if (root != null) tmpQueue.add(root);
while (tmpQueue.size() != 0){
int size = tmpQueue.size();
Node cur = tmpQueue.poll();
if (cur.left != null) tmpQueue.add(cur.left);
if (cur.right != null) tmpQueue.add(cur.right);
for (int index = 1; index < size; index++){
Node next = tmpQueue.poll();
if (next.left != null) tmpQueue.add(next.left);
if (next.right != null) tmpQueue.add(next.right);
cur.next = next;
cur = next;
}
}
return root;
}
}
//填充每个节点的下一个右侧节点指针II
class Solution {
public Node connect(Node root) {
Queue<Node> queue = new LinkedList<>();
if (root != null) {
queue.add(root);
}
while (!queue.isEmpty()) {
int size = queue.size();
Node node = null;
Node nodePre = null;
for (int i = 0; i < size; i++) {
if (i == 0) {
nodePre = queue.poll();
node = nodePre;
} else {
node = queue.poll();
nodePre.next = node;
nodePre = nodePre.next;
}
if (node.left != null) {
queue.add(node.left);
}
if (node.right != null) {
queue.add(node.right);
}
}
nodePre.next = null;
}
return root;
}
}
class Solution {
public int maxDepth(TreeNode root) {
if (root == null) return 0;
Queue<TreeNode> que = new LinkedList<>();
que.offer(root);
int depth = 0;
while (!que.isEmpty())
{
int len = que.size();
while (len > 0)
{
TreeNode node = que.poll();
if (node.left != null) que.offer(node.left);
if (node.right != null) que.offer(node.right);
len--;
}
depth++;
}
return depth;
}
}
//二叉树的最小深度
class Solution {
public int minDepth(TreeNode root) {
if (root == null) {
return 0;
}
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
int depth = 0;
while (!queue.isEmpty()){
int size = queue.size();
depth++;
TreeNode cur = null;
for (int i = 0; i < size; i++) {
cur = queue.poll();
if (cur.left == null && cur.right == null){
return depth;
}
if (cur.left != null) queue.offer(cur.left);
if (cur.right != null) queue.offer(cur.right);
}
}
return depth;
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构