【leetcode热题】 二叉树的后序遍历

给你一棵二叉树的根节点 root ,返回其节点值的 后序遍历 

示例 1:

输入:root = [1,null,2,3]
输出:[3,2,1]

示例 2:

输入:root = []
输出:[]

示例 3:

输入:root = [1]
输出:[1]

解法一 递归

和之前的中序遍历和先序遍历没什么大的改变,只需要改变一下 list.add 的位置。

public List<Integer> postorderTraversal(TreeNode root) {
    List<Integer> list = new ArrayList<>();
    postorderTraversalHelper(root, list);
    return list;
}

private void postorderTraversalHelper(TreeNode root, List<Integer> list) {
    if (root == null) {
        return;
    }
    postorderTraversalHelper(root.left, list);
    postorderTraversalHelper(root.right, list);
    list.add(root.val);
}

解法二 栈

主要就是用栈要模拟递归的过程,区别于之前 94 题 中序遍历和 144 题 先序遍历,后序遍历的非递归形式会相对难一些。

原因就是,当遍历完某个根节点的左子树,回到根节点的时候,对于中序遍历和先序遍历可以把当前根节点从栈里弹出,然后转到右子树。举个例子,

     1
    / \
   2   3
  / \
 4   5

当遍历完 2,4,5 的时候,回到 1 之后我们就可以把 1 弹出,然后通过 1 到达右子树继续遍历。

而对于后序遍历,当我们到达 1 的时候并不能立刻把 1 弹出,因为遍历完右子树,我们还需要将这个根节点加入到 list 中。

所以我们就需要判断是从左子树到的根节点,还是右子树到的根节点。

如果是从左子树到的根节点,此时应该转到右子树。如果是从右子树到的根节点,那么就可以把当前节点弹出,并且加入到 list 中。

当然,如果是从左子树到的根节点,此时如果根节点的右子树为 null, 此时也可以把当前节点弹出,并且加入到 list 中。

基于上边的思想,可以写出一些不同的代码。

1. 思想一

可以先看一下中序遍历的实现。

public List<Integer> inorderTraversal(TreeNode root) {
    List<Integer> ans = new ArrayList<>();
    Stack<TreeNode> stack = new Stack<>();
    TreeNode cur = root;
    while (cur != null || !stack.isEmpty()) {
        //节点不为空一直压栈
        while (cur != null) {
            stack.push(cur);
            cur = cur.left; //考虑左子树
        }
        //节点为空,就出栈
        cur = stack.pop();
        //当前值加入
        ans.add(cur.val);
        //考虑右子树
        cur = cur.right;
    }
    return ans;
}

这里后序遍历的话,和中序遍历有些像。

开始的话,也是不停的往左子树走,然后直到为 null 。不同之处是,之前直接把节点 pop 并且加入到 list 中,然后直接转到右子树。

这里的话,我们应该把节点 peek 出来,然后判断一下当前根节点的右子树是否为空或者是否是从右子树回到的根节点。

判断是否是从右子树回到的根节点,这里我用了一个 set ,当从左子树到根节点的时候,我把根节点加入到 set 中,之后我们就可以判断当前节点在不在 set 中,如果在的话就表明当前是第二次回来,也就意味着是从右子树到的根节点。

public List<Integer> postorderTraversal(TreeNode root) {
    List<Integer> list = new ArrayList<>();
    Stack<TreeNode> stack = new Stack<>();
    Set<TreeNode> set = new HashSet<>();
    TreeNode cur = root;
    while (cur != null || !stack.isEmpty()) {
        while (cur != null && !set.contains(cur)) {
            stack.push(cur);
            cur = cur.left;
        }
        cur = stack.peek();
        //右子树为空或者第二次来到这里
        if (cur.right == null || set.contains(cur)) {
            list.add(cur.val);
            set.add(cur);
            stack.pop();//将当前节点弹出
            if (stack.isEmpty()) {
                return list;
            }
            //转到右子树,这种情况对应于右子树为空的情况
            cur = stack.peek();
            cur = cur.right;
        //从左子树过来,加到 set 中,转到右子树
        } else {
            set.add(cur);
            cur = cur.right;
        }
    }
    return list;
}

上边的代码把一些情况其实做了合并,并不是很好理解,下边分享一下 solution 里的一些简洁的解法。

2. 思想二

上边的解法在判断当前是从左子树到的根节点还是右子树到的根节点用了 set ,这里 还有一个更直接的方法,通过记录上一次遍历的节点。

如果当前节点的右节点和上一次遍历的节点相同,那就表明当前是从右节点过来的了。

public List<Integer> postorderTraversal(TreeNode root) {
    List<Integer> list = new ArrayList<>();
    Stack<TreeNode> stack = new Stack<>();
    TreeNode cur = root;
    TreeNode last = null;
    while (cur != null || !stack.isEmpty()) {
        if (cur != null) {
            stack.push(cur);
            cur = cur.left;
        } else {
            TreeNode temp = stack.peek();
            //是否变到右子树
            if (temp.right != null && temp.right != last) {
                cur = temp.right;
            } else {
                list.add(temp.val);
                last = temp;
                stack.pop();
            }
        }
    }
    return list;
}

3. 思想三

在 这里 看到另一种想法,还是基于上边分析的入口点,不过解决方案真的是太优雅了。

先看一下 144 题 前序遍历的代码。

我们还可以将左右子树分别压栈,然后每次从栈里取元素。需要注意的是,因为我们应该先访问左子树,而栈的话是先进后出,所以我们压栈先压右子树。

public List<Integer> preorderTraversal(TreeNode root) {
    List<Integer> list = new ArrayList<>();
    if (root == null) {
        return list;
    }
    Stack<TreeNode> stack = new Stack<>();
    stack.push(root);
    while (!stack.isEmpty()) {
        TreeNode cur = stack.pop();
        if (cur == null) {
            continue;
        }
        list.add(cur.val);
        stack.push(cur.right);
        stack.push(cur.left);
    }
    return list;
}

后序遍历遇到的问题就是到根节点的时候不能直接 pop ,因为后边还需要回来。

上边的作者,提出只需要把每个节点 push 两次,然后判断当前 pop 节点和栈顶节点是否相同。

相同的话,就意味着是从左子树到的根节点。

不同的话,就意味着是从右子树到的根节点,此时就可以把节点加入到 list 中。

public List<Integer> postorderTraversal(TreeNode root) {
    List<Integer> list = new ArrayList<>();
    if (root == null) {
        return list;
    }
    Stack<TreeNode> stack = new Stack<>();
    stack.push(root);
    stack.push(root);
    while (!stack.isEmpty()) {
        TreeNode cur = stack.pop();
        if (cur == null) {
            continue;
        }
        if (!stack.isEmpty() && cur == stack.peek()) {
            stack.push(cur.right);
            stack.push(cur.right);
            stack.push(cur.left);
            stack.push(cur.left);
        } else {
            list.add(cur.val);
        }
    }
    return list;
}

解法三 转换问题

首先我们知道前序遍历的非递归形式会比后序遍历好理解些,那么我们能实现后序遍历 -> 前序遍历的转换吗?

后序遍历的顺序是 左 -> 右 -> 根

前序遍历的顺序是 根 -> 左 -> 右,左右其实是等价的,所以我们也可以轻松的写出 根 -> 右 -> 左 的代码。

然后把 根 -> 右 -> 左 逆序,就是 左 -> 右 -> 根,也就是后序遍历了。

让我们改一下之前 144 题 先序遍历的代码。

改之前的代码。

public List<Integer> preorderTraversal(TreeNode root) {
    List<Integer> list = new ArrayList<>();
    Stack<TreeNode> stack = new Stack<>();
    TreeNode cur = root;
    while (cur != null || !stack.isEmpty()) {
        if (cur != null) {
            list.add(cur.val);
            stack.push(cur);
            cur = cur.left; //考虑左子树
        }else {
            //节点为空,就出栈
            cur = stack.pop();
            //考虑右子树
            cur = cur.right;
        }
    }
    return list;
}

然后我们只需要把上边的 left 改成 rightright 改成 left 就可以了。最后倒置即可。

public List<Integer> postorderTraversal2(TreeNode root) {
    List<Integer> list = new ArrayList<>();
    Stack<TreeNode> stack = new Stack<>();
    TreeNode cur = root;
    while (cur != null || !stack.isEmpty()) {
        if (cur != null) {
            list.add(cur.val);
            stack.push(cur);
            cur = cur.right; // 考虑左子树
        } else {
            // 节点为空,就出栈
            cur = stack.pop();
            // 考虑右子树
            cur = cur.left;
        }
    }
    Collections.reverse(list);
    return list;
}

同样的,之前先序遍历的 Morris Traversal ,不需要额外空间的解法。

public List<Integer> preorderTraversal(TreeNode root) {
    List<Integer> list = new ArrayList<>();
    TreeNode cur = root;
    while (cur != null) {
        //情况 1
        if (cur.left == null) {
            list.add(cur.val);
            cur = cur.right;
        } else {
            //找左子树最右边的节点
            TreeNode pre = cur.left;
            while (pre.right != null && pre.right != cur) {
                pre = pre.right;
            }
            //情况 2.1
            if (pre.right == null) {
                list.add(cur.val);
                pre.right = cur;
                cur = cur.left;
            }
            //情况 2.2
            if (pre.right == cur) {
                pre.right = null; //这里可以恢复为 null
                cur = cur.right;
            }
        }
    }
    return list;
}

同样的处理,把上边的 left 改成 rightright 改成 left 就可以了。最后倒置即可。

public List<Integer> postorderTraversal(TreeNode root) {
    List<Integer> list = new ArrayList<>();
    TreeNode cur = root;
    while (cur != null) {
        if (cur.right == null) {
            list.add(cur.val);
            cur = cur.left;
        } else {
            TreeNode pre = cur.right;
            while (pre.left != null && pre.left != cur) {
                pre = pre.left;
            }
            if (pre.left == null) {
                list.add(cur.val);
                pre.left = cur;
                cur = cur.right;
            }
            if (pre.left == cur) {
                pre.left = null; // 这里可以恢复为 null
                cur = cur.left;
            }
        }
    }
    Collections.reverse(list);

    return list;
}

上边的话由于我们用的是 ArrayList ,所以倒置的话其实是比较麻烦的,可能需要更多的时间或空间。

所以我们可以用 LinkedList , 这样倒置链表就只需要遍历一遍,也不需要额外的空间了。

更近一步,我们在调用 list.add 的时候,其实可以直接 list.addFirst ,每次都插入到链表头,这样做的话,最后也不需要逆转链表了。

解法四 Morris Traversal

上边已经成功改写 Morris Traversal 了,但是是一种取巧的方式,通过变形的前序遍历做的。同学介绍了另一种写法,这里也分享下。

94 题 中序遍历中对 Morris 遍历有详细的介绍,我先贴过来。

我们知道,左子树最后遍历的节点一定是一个叶子节点,它的左右孩子都是 null,我们把它右孩子指向当前根节点,这样的话我们就不需要额外空间了。这样做,遍历完当前左子树,就可以回到根节点了。

当然如果当前根节点左子树为空,那么我们只需要保存根节点的值,然后考虑右子树即可。

所以总体思想就是:记当前遍历的节点为 cur

1、cur.left 为 null,保存 cur 的值,更新 cur = cur.right

2、cur.left 不为 null,找到 cur.left 这颗子树最右边的节点记做 last

2.1 last.right 为 null,那么将 last.right = cur,更新 cur = cur.left

2.2 last.right 不为 null,说明之前已经访问过,第二次来到这里,表明当前子树遍历完成,保存 cur 的值,更新 cur = cur.right

结合图示:

如上图,cur 指向根节点。 当前属于 2.1 的情况,cur.left 不为 nullcur 的左子树最右边的节点的右孩子为 null,那么我们把最右边的节点的右孩子指向 cur

接着,更新 cur = cur.left

如上图,当前属于 2.1 的情况,cur.left 不为 null,cur 的左子树最右边的节点的右孩子为 null,那么我们把最右边的节点的右孩子指向 cur

更新 cur = cur.left

如上图,当前属于情况 1,cur.left 为 null,保存 cur 的值,更新 cur = cur.right

如上图,当前属于 2.2 的情况,cur.left 不为 nullcur 的左子树最右边的节点的右孩子已经指向 cur,保存 cur 的值,更新 cur = cur.right

如上图,当前属于情况 1,cur.left 为 null,保存 cur 的值,更新 cur = cur.right

如上图,当前属于 2.2 的情况,cur.left 不为 nullcur 的左子树最右边的节点的右孩子已经指向 cur,保存 cur 的值,更新 cur = cur.right

当前属于情况 1,cur.left 为 null,保存 cur 的值,更新 cur = cur.right

cur 指向 null,结束遍历。

根据这个关系,写代码

记当前遍历的节点为 cur

1、cur.left 为 null,保存 cur 的值,更新 cur = cur.right

2、cur.left 不为 null,找到 cur.left 这颗子树最右边的节点记做 last

2.1 last.right 为 null,那么将 last.right = cur,更新 cur = cur.left

2.2 last.right 不为 null,说明之前已经访问过,第二次来到这里,表明当前子树遍历完成,保存 cur 的值,更新 cur = cur.right

public List<Integer> inorderTraversal(TreeNode root) {
    List<Integer> ans = new ArrayList<>();
    TreeNode cur = root;
    while (cur != null) {
        //情况 1
        if (cur.left == null) {
            ans.add(cur.val);
            cur = cur.right;
        } else {
            //找左子树最右边的节点
            TreeNode pre = cur.left;
            while (pre.right != null && pre.right != cur) {
                pre = pre.right;
            }
            //情况 2.1
            if (pre.right == null) {
                pre.right = cur;
                cur = cur.left;
            }
            //情况 2.2
            if (pre.right == cur) {
                pre.right = null; //这里可以恢复为 null
                ans.add(cur.val);
                cur = cur.right;
            }
        }
    }
    return ans;
}

根据上边的关系,我们会发现除了叶子节点只访问一次,其他节点都会访问两次,结合下图。

当第二次访问某个节点的时候,我们只需要将它的左节点,以及左节点的右节点,左节点的右节点的右节点... 逆序添加到 list 中即可。比如上边的例子。

上边的遍历顺序其实就是按照深度优先的方式。

先访问 15, 7, 3, 1 然后往回走
3 第二次访问,将它的左节点逆序加入到 list 中
list = [1]

继续访问 2, 然后往回走
7 第二次访问,将它的左节点,左节点的右节点逆序加入到 list 中
list = [1 2 3]

继续访问 6 4, 然后往回走
6 第二次访问, 将它的左节点逆序加入到 list 中
list = [1 2 3 4]

继续访问 5, 然后往回走
15 第二次访问, 将它的左节点, 左节点的右节点, 左节点的右节点的右节点逆序加入到 list 中
list = [1 2 3 4 5 6 7]

然后访问 14 10 8, 然后往回走
10 第二次访问,将它的左节点逆序加入到 list 中
list = [1 2 3 4 5 6 7 8]

继续访问 9, 然后往回走
14 第二次访问,将它的左节点,左节点的右节点逆序加入到 list 中
list = [1 2 3 4 5 6 7 8 9 10]

继续访问 13 11, 然后往回走
13 第二次访问, 将它的左节点逆序加入到 list 中
list = [1 2 3 4 5 6 7 8 9 10 11]

继续遍历 12,结束遍历

然后单独把根节点,以及根节点的右节点,右节点的右节点,右节点的右节点的右节点逆序加入到 list 中
list = [1 2 3 4 5 6 7 8 9 10 11 12 13 14 15]

得到 list 就刚好是后序遍历

如下图,问题就转换成了 9 组单链表的逆序问题。

当遇到第二次访问的节点,我们将单链表逆序,然后加入到 list 并且还原即可。单链表逆序已经在 第 2 题 讨论过了,直接拿过来用,只需要把 node.next 改为 node.right 。

public List<Integer> postorderTraversal(TreeNode root) {
    List<Integer> list = new ArrayList<>();
    TreeNode cur = root;
    while (cur != null) {
        // 情况 1
        if (cur.left == null) {
            cur = cur.right;
        } else {
            // 找左子树最右边的节点
            TreeNode pre = cur.left;
            while (pre.right != null && pre.right != cur) {
                pre = pre.right;
            }
            // 情况 2.1
            if (pre.right == null) {
                pre.right = cur;
                cur = cur.left;
            }
            // 情况 2.2,第二次遍历节点
            if (pre.right == cur) {
                pre.right = null; // 这里可以恢复为 null
                //逆序
                TreeNode head = reversList(cur.left);
                //加入到 list 中,并且把逆序还原
                reversList(head, list);
                cur = cur.right;
            }
        }
    }
    TreeNode head = reversList(root);
    reversList(head, list);
    return list;
}

private TreeNode reversList(TreeNode head) {
    if (head == null) {
        return null;
    }
    TreeNode tail = head;
    head = head.right;

    tail.right = null;

    while (head != null) {
        TreeNode temp = head.right;
        head.right = tail;
        tail = head;
        head = temp;
    }

    return tail;
}

private TreeNode reversList(TreeNode head, List<Integer> list) {
    if (head == null) {
        return null;
    }
    TreeNode tail = head;
    head = head.right;
    list.add(tail.val);
    tail.right = null;

    while (head != null) {
        TreeNode temp = head.right;
        head.right = tail;
        tail = head;
        list.add(tail.val);
        head = temp;
    }
    return tail;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/445320.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

微信小程序uniapp+django+python的酒店民宿预订系统ea9i3

Android的民宿预订系统设计的目的是为用户提供民宿客房、公告信息等方面的平台。 与PC端应用程序相比&#xff0c;Android的民宿预订系统的设计主要面向于民宿&#xff0c;旨在为管理员和用户、商家提供一个Android的民宿预订系统。用户可以通过Android及时查看民宿客房等。 An…

FreeRTOS操作系统学习——同步互斥与通信

同步&#xff08;Synchronization&#xff09; 同步是一种机制&#xff0c;用于确保多个任务能够按照特定的顺序协调执行或共享数据。当一个任务需要等待其他任务完成某个操作或满足某个条件时&#xff0c;同步机制可以帮助任务进行协调和等待。 在FreeRTOS中&#xff0c;常见…

力扣--动态规划5.最长回文子串

class Solution { public:string longestPalindrome(string s) {// 获取输入字符串的长度int n s.size();// 如果字符串长度为1&#xff0c;直接返回原字符串&#xff0c;因为任何单个字符都是回文串if (n 1)return s;// 创建一个二维数组dp&#xff0c;用于记录子串是否为回…

【Python】成功解决ModuleNotFoundError: No module named ‘seaborn’

【Python】成功解决ModuleNotFoundError: No module named ‘seaborn’ &#x1f308; 个人主页&#xff1a;高斯小哥 &#x1f525; 高质量专栏&#xff1a;Matplotlib之旅&#xff1a;零基础精通数据可视化、Python基础【高质量合集】、PyTorch零基础入门教程&#x1f448; …

掌握项目进度管理:确保项目按时交付的关键策略

项目进度管理是确保项目按时、按预算完成的重要环节。在项目执行过程中&#xff0c;项目进度可能会受到各种因素的影响&#xff0c;如资源不足、任务延误、需求变更等。因此&#xff0c;掌握项目进度管理的关键策略对于项目经理来说至关重要。本文将介绍一些实用的项目进度管理…

011-keep-alive详解

keep-alive详解 1、简介2、keep-alive的使用效果未使用keep-alive的效果图使用keep-alive的效果图include和exclude指定是否缓存某些组件使用keep-alive的钩子函数执行顺序问题 3、keep-alive的应用场景举例4、总结 1、简介 keep-alive 是 Vue 的内置组件&#xff0c;当它包裹…

什么是自动化测试?什么情况下使用?

什么是自动化测试? 自动化测试是指把以人为驱动的测试行为转化为机器执行的过程。实际上自动化测试往往通过一些测试工具或框架&#xff0c;编写自动化测试脚本&#xff0c;来模拟手工测试过程。比如说&#xff0c;在项目迭代过程中&#xff0c;持续的回归测试是一项非常枯燥…

解决阿里云服务器开启frp服务端,内网服务器开启frp客户端却连接不上的问题

解决方法&#xff1a; 把阿里云自带的Alibabxxxxxxxlinux系统 换成centos 7系统&#xff01;&#xff01;&#xff01;&#xff01; 说一下我的过程和问题&#xff1a;由于我们内网的服务器在校外是不能连接的&#xff0c;因此我弄了个阿里云服务器做内网穿透&#xff0c;所谓…

【C++】二叉树进阶之二叉搜索树

> 作者简介&#xff1a;დ旧言~&#xff0c;目前大二&#xff0c;现在学习Java&#xff0c;c&#xff0c;c&#xff0c;Python等 > 座右铭&#xff1a;松树千年终是朽&#xff0c;槿花一日自为荣。 > 目标&#xff1a;熟练掌握二叉搜索树&#xff0c;能自己模拟实现二…

使用 Docker 部署 Next Terminal 轻量级堡垒机

1&#xff09;Next Terminal 介绍 官网&#xff1a;https://next-terminal.typesafe.cn/ GitHub&#xff1a;https://github.com/dushixiang/next-terminal 想必经常玩服务器的都了解过 堡垒机&#xff0c;类似于跳板机&#xff0c;但与跳板机的侧重点不同。堡垒机的主要功能是…

20240310-1-Java后端开发知识体系

Java 基础 知识体系 Questions 1. HashMap 1.8与1.7的区别 1.71.8底层结构数组链表数组链表/红黑树插入方式头插法尾插法计算hash值4次位运算5次异或运算1次位运算1次异或运算扩容、插入先扩容再插入先插入再扩容扩容后位置计算重新hash原位置或原位置旧容量 (1) 扩容因子…

排查线上JVM CPU飙升使用率高和线程死锁问题

一、排查CPU飙升使用率高问题 在开始前新建一个 SpringBoot 项目构建CPU使用率高的场景&#xff1a; RestController public class JvmThread1Controller {ThreadPoolExecutor executor new ThreadPoolExecutor(10,15,2,TimeUnit.SECONDS,new LinkedBlockingDeque<>(5…

【网络原理】使用Java基于TCP搭建简单客户端与服务器通信

目录 &#x1f384;API介绍&#x1f338;ServerSocket API&#x1f338;Socket API &#x1f340;TCP中的长短连接&#x1f333;建立TCP回显客户端与服务器&#x1f338;TCP搭建服务器&#x1f338;TCP搭建客户端 ⭕总结 TCP服务器与客户端的搭建需要借助以下API &#x1f384;…

springcloud-alibaba Sentinel入门

Releases alibaba/Sentinel GitHubSentinel下载官方 在cmd 里面运行 启动命令 java -jar sentinel-dashboard-1.8.6.jar 启动成功前提 java环境 &#xff0c;已经注册到服务注册中心&#xff0c;8080端口没有被占用 启动后访问地址为 qhttp://localhost:8080http://lo…

Clion开发STM32之printf的缓冲区验证方式

前言 clion开发stm32时&#xff0c;涉及到printf函数的重写&#xff0c;一般我们重写__io_putchar函数经发现printf函数在没有加换行符时&#xff0c;数据不会立刻通过串口发送数据&#xff0c;而是等到1024字节之后发送数据 测试 主程序 现象 debug调试 不加换行的情况 总…

Windows系统安装Tomcat并结合内网穿透实现公网访问本地网页

文章目录 前言1.本地Tomcat网页搭建1.1 Tomcat安装1.2 配置环境变量1.3 环境配置1.4 Tomcat运行测试1.5 Cpolar安装和注册 2.本地网页发布2.1.Cpolar云端设置2.2 Cpolar本地设置 3.公网访问测试4.结语 前言 Tomcat作为一个拥有强大功能的轻量级服务器&#xff0c;由于其可以实…

万界星空科技MES系统中的车间管理的作用

在了解mes生产管理系统的作用包括哪些方面之前&#xff0c;我们先来了解一下作为生产管理信息化的关键部分&#xff0c;车间管理系统包含哪几个部分&#xff1a;一、mes系统中的车间管理通常包含以下部分&#xff1a; 1、设备管理&#xff1a;用于监控车间内的设备状态&#xf…

网络安全: Kali Linux 进行 MSFvenom 程序利用

目录 一、实验 1.环境 2. Kali Linux 进行 MSFvenom 程序利用 3. 创建计划任务自动运行 MSFvenom 程序 二、问题 1.在线加密解密 2.MSF 运行失败 3.MobaXterm 连接Ubuntu 失败 一、实验 1.环境 &#xff08;1&#xff09;主机 表1 主机 系统版本IP备注Kali Linux20…

大数乘法算法

例题&#xff1a;6-10 阶乘计算升级版&#xff08;pta--基础编程题目集&#xff09; 此方法的原理就是把数字的每一位拆分出来&#xff0c;存到数组中&#xff0c;在求阶乘时&#xff0c;每一次乘法都分解为数组的每一位乘这个数&#xff0c;例如&#xff1a; 4的阶乘&#x…

Unity--自动版面(Grid Layout Group)

Unity--自动版面&#xff08;Grid Layout Group&#xff09; Grid Layout Group 网格布局组组件将其子布局元素放置在网格中。 Padding&#xff1a;&#xff08;填充&#xff09; 布局组边缘内的填充。与其他布局组不同&#xff0c;“网格布局组”将忽略其所包含布局元素的最…