代码随想录——二叉树

news2024/10/6 4:07:33

二叉树遍历

基本介绍:

二叉树主要有两种遍历方式:

  1. 深度优先遍历:先往深走,遇到叶子节点再往回走。【前中后序遍历】
  2. 广度优先遍历:一层一层的去遍历。【层序遍历】

这两种遍历是图论中最基本的两种遍历方式

  • 深度优先遍历
    • 前序遍历(递归法,迭代法)
    • 中序遍历(递归法,迭代法)
    • 后序遍历(递归法,迭代法)
  • 广度优先遍历
    • 层次遍历(迭代法)

前中后序遍历,这里前中后,其实指的就是根节点的遍历顺序

前序遍历:根左右

中序遍历:左根右

后序遍历:左右根

看如下例子:

image-20221206215152376

栈其实就是递归的一种是实现结构,也就说前中后序遍历的逻辑其实都是可以借助栈使用非递归的方式来实现的。

二叉树定义:

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;
  	}
}

二叉树深度优先遍历(前中后序):

二叉树递归遍历:

递归算法的三个要素。每次写递归,都按照这三要素来写,可以保证大家写出正确的递归算法!

  1. 确定递归函数的参数和返回值: 确定哪些参数是递归的过程中需要处理的,那么就在递归函数里加上这个参数, 并且还要明确每次递归的返回值是什么进而确定递归函数的返回类型。
  2. 确定终止条件: 写完了递归算法, 运行的时候,经常会遇到栈溢出的错误,就是没写终止条件或者终止条件写的不对,操作系统也是用一个栈的结构来保存每一层递归的信息,如果递归没有终止,操作系统的内存栈必然就会溢出。
  3. 确定单层递归的逻辑: 确定每一层递归需要处理的信息。在这里也就会重复调用自己来实现递归的过程。

下面以前序遍历为例:

其他两种遍历修改递归代码顺序即可。

前序:

class Solution {

    List<Integer> ans = new ArrayList<>(); //返回值
    public List<Integer> preorderTraversal(TreeNode root) { //参数
        //递归出口
        if(root == null){
            return ans;
        }

        //下面就是递归逻辑
        //根
        ans.add(root.val);

        //左
        if(root.left != null) preorderTraversal(root.left);

        //右
        if(root.right != null) preorderTraversal(root.right);

        return ans;
    }
}

二叉树迭代遍历:

递归遍历需要借助栈这种数据结构了,以前序遍历为例,直接从根节点开始,一直向左节点遍历直到左节点为空将节点压入栈中,此时压栈顺序就是我们要的顺序根左,压栈的同时将结果放入List中,此时进行出栈回溯操作,当前节点置为栈顶节点的右孩子然后继续从当前节点继续上述压栈操作,这才实现真正意义上的根左右,具体代码如下:

前序迭代:

 public List<Integer> preorderTraversal(TreeNode root) {
        //迭代法
        List<Integer> ans = new ArrayList<>();
        if(root == null){
            return ans;
        }
        Stack<TreeNode> stack = new Stack<>();
       while(root != null || !stack.isEmpty()){
          while(root != null){
            //这个顺序就是我们要的
            ans.add(root.val); //根左
            //将根节点和左节点压栈
            stack.push(root);
            root = root.left;
         }
        //出栈回溯
        //继续将栈顶元素的右孩子的左孩子继续压栈
        TreeNode node = stack.pop();
        root = node.right; //右
       }

        return ans;
    }

中序迭代:

中序迭代相当于上述前序迭代,仅仅改变一行代码顺序即可,前序是入栈就是我们要的顺序根左,现在中序是左根,所以将出栈元素顺序放入结果即可,代码如下。

 public List<Integer> inorderTraversal(TreeNode root) {
        //迭代法
        List<Integer> ans = new ArrayList<>();
        if(root == null){
            return ans;
        }
        Stack<TreeNode> stack = new Stack<>();
       while(root != null || !stack.isEmpty()){
          while(root != null){
            //将根节点和左节点压栈
            stack.push(root);
            root = root.left;
         }
        //出栈回溯
        //继续将栈顶元素的右孩子的左孩子继续压栈
        TreeNode node = stack.pop();
        ans.add(node.val); //左根
        root = node.right; //右
       }

        return ans;
    }

后序迭代:

最后的后序遍历就不能单纯的改变代码顺序就能实现了,后序遍历顺序是左右根,我们可以先得到根右左的顺序,最后将结果反转就得到目标结果,代码如下:

 public List<Integer> postorderTraversal(TreeNode root) {
        //迭代法
        List<Integer> ans = new ArrayList<>();
        if(root == null){
            return ans;
        }
        Stack<TreeNode> stack = new Stack<>();
       while(root != null || !stack.isEmpty()){
          while(root != null){
            ans.add(root.val); //根右
            //将根节点和右节点压栈
            stack.push(root);
            root = root.right;
         }
        //出栈回溯
        //继续将栈顶元素的左孩子的右孩子继续压栈
        TreeNode node = stack.pop();
        root = node.left; //左
       }
     	//最后将结果反转即可
     	Collections.reverse(ans);
        return ans;
    }

二叉树广度优先遍历(层序):

层序遍历需要借助队列这种数据结构了,每层遍历的顺序满足队列先将进先出的特点。

我们需要一个队列以及需要一个变量记录每层节点的个数,每一层节点出队列时,需要将该节点的左右子孩子加入队列中。

实际效果如下:

具体代码如下:

public List<List<Integer>> levelOrder(TreeNode root) {
        List<List<Integer>> ans = new ArrayList<>();
        if (root == null) {
            return ans;
        }

        Queue<TreeNode> queue = new ArrayDeque<>();
        queue.offer(root);
        while (!queue.isEmpty()) {
            //记录每一层节点个数
            int size = queue.size();
            List<Integer> temp = new ArrayList<>();
            //一边将该层节点出队列,一边将节点的左右子节点加入队列,直到最后队列为空结束循环
            while (size > 0) {
                TreeNode node = queue.poll();
                temp.add(node.val);

                //将该节点的左右子节点放入队列
                if (node.left != null) queue.offer(node.left);
                if (node.right != null) queue.offer(node.right);
                size--;
            }
            ans.add(temp);
        }

        return ans;
    }

深度优先遍历需要用到栈这种数据结构,广度优先遍历借助队列这种数据结构。

104.二叉树的最大深度

力扣题目链接

给定一个二叉树,找出其最大深度。

二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。

说明: 叶子节点是指没有子节点的节点。

image-20221210163325742

返回它的最大深度 3 。

递归法

找出左右子树最大深度 + 1返回

递归三部曲

1、递归参数和返回值

参数就是树的根节点,返回值即树的高度,代码如下:

public int getDepth(TreeNode root)

2、递归出口

如果根节点为空,直接返回高度0

if(root == null) return 0;

3、递归逻辑

递归左子树、右子树得到左右子树高度,得到最大值 + 1返回

//左节点
int leftDepth = getDepth(root.left);
//右节点
int rightDepth = getDepth(root.right);

//根节点
return Math.max(leftDepth, rightDepth) + 1;

上述代码类似后序遍历。

完整递归代码

class Solution {
    public int maxDepth(TreeNode root) {
        return getDepth(root); 
    }

    public int getDepth(TreeNode root){
        if(root == null) return 0;
        //左子树
        int leftDepth = getDepth(root.left);
        //右子树
        int rightDepth = getDepth(root.right);

        //根节点
        return Math.max(leftDepth, rightDepth) + 1;
    }
}

迭代法——广度优先遍历

层序遍历记录树的深度

 public int maxDepth(TreeNode root) {
     if(root == null) return 0;
     int depth = 0;
     Queue<TreeNode> queue = new ArrayDeque<>();
     queue.offer(root);
     
     while(!queue.isEmpty()){
         //每一层节点个数
         int size = queue.size();
         depth++;
         
         for(int i = 0; i < size; i++){
             TreeNode node = queue.poll();
             
             if(node.left != null) queue.offer(node.left);
             if(node.right != null) queue.offer(node.right);
         }
     }
     
     return depth;
}

111.二叉树的最小深度

力扣题目链接

给定一个二叉树,找出其最小深度。

最小深度是从根节点到最近叶子节点的最短路径上的节点数量。

说明: 叶子节点是指没有子节点的节点。

返回它的最小深度 2 。

思路

二叉树的最小深度,即当一个节点没有左右孩子(叶子节点)时,此时的深度就是最小深度。

迭代法——广度优先遍历

 public int minDepth(TreeNode root) {
        //层序遍历
        if(root == null) return 0;

        Queue<TreeNode> queue = new ArrayDeque<>();
        queue.offer(root);
        int depth = 0;
        while(!queue.isEmpty()){
            int size = queue.size();
            depth++;

            for(int i = 0; i < size; i++){
                TreeNode node = queue.poll();
				//某个节点的左右子孩子都为空,此时的深度就是最小深度
                if(node.left == null && node.right == null){
                    return depth;
                }

                if(node.left != null) queue.offer(node.left);
                if(node.right != null) queue.offer(node.right);
            }
        }
        return depth;
        
    }

递归法见代码随想录

226.翻转二叉树

力扣题目链接

翻转一棵二叉树。

示例:

image-20221210163630640

思路

该题只需将每一个节点的左右孩子节点交换即可。

递归法

public TreeNode invertTree(TreeNode root){
    //递归出口
    if(root == null){
        return root;
    }
    //交换节点的左右孩子节点
    swap(root);
    
    //递归左子树
    invertTree(root.left);
    //递归右子树
    invertTree(root.right);
    return root;
}

/**
 * 交换该节点的左右孩子节点
 */
public void swap(TreeNode node){
    TreeNode temp = node.left;
    node.left = node.right;
    node.right = temp;
}

迭代法

借助栈/队列来遍历树,进而交换每个节点的左右孩子节点

借助队列

public TreeNode invertTree(TreeNode root){
		//迭代法
        //交换每个节点的左右孩子即可
        if(root == null) return root;
        Queue<TreeNode> queue = new ArrayDeque<>();
        queue.offer(root);

        while(!queue.isEmpty()){
            TreeNode node = queue.poll();
            swap(node);

            if(node.left != null) queue.offer(node.left);
            if(node.right != null) queue.offer(node.right);
        }
        return root;
}

借助栈

public TreeNode invertTree(TreeNode root){
		//迭代法
        //交换每个节点的左右孩子即可
        if(root == null) return root;
        Stack<TreeNode> stack = new Stack<>();
        stack.push(root);

        while(!stack.isEmpty()){
            TreeNode node = stack.pop();
            swap(node);

            if(node.left != null) stack.push(node.left);
            if(node.right != null) stack.push(node.right);
        }
        return root;
}

101. 对称二叉树

力扣题目链接

给定一个二叉树,检查它是否是镜像对称的。

image-20221210163721101

思路:

首先要明确,判断二叉树是否是对称二叉树,要比较的不是左右节点!

对于二叉树是否对称,要比较的是根节点的左子树与右子树是不是相互翻转的,理解这一点就知道了其实我们要比较的是两个树(这两个树是根节点的左右子树),所以在递归遍历的过程中,也是要同时遍历两棵树。【判断根节点的左右子树的内侧和外侧节点是否是都相同】

image-20221210164156898

递归法

1、确定递归函数的参数和返回值

因为我们要比较的是根节点的两个子树是否是相互翻转的,进而判断这个树是不是对称树,所以要比较的是两个树,参数自然也是左子树节点和右子树节点。

返回值自然是boolean类型。

代码如下:

boolean compare(TreeNode left, TreeNode right)

2、确定终止条件

要比较两个节点数值相不相同,首先要把两个节点为空的情况弄清楚!否则后面比较数值的时候就会操作空指针了。

节点为空的情况有:(注意我们比较的其实不是左孩子和右孩子,所以如下我称之为左节点右节点

  • 左节点为空,右节点不为空,不对称,return false
  • 左不为空,右为空,不对称 return false
  • 左右都为空,对称,返回true
  • 左右节点值不相等,返回false
  • 最后左右节点的值相等,继续递归判断对应内侧节点、各外侧节点是否都相等

代码如下:

        if(left == null && right != null){ // 左空右不空
            return false;
        }else if(left != null && right == null){ // 左不空右空 
            return false;
        }else if(left == null && right == null){ //左右都空
            return true;
        }else if(left.val != right.val){ // 左右值不相等
            return false;
        }

最后是else if不是else,就是因为还有上述最后一种情况左右节点的值都相等还需要继续递归。

3、单层递归的逻辑

此时才进入单层递归的逻辑,单层递归的逻辑就是处理 左右节点都不为空,且数值相同的情况。

  • 比较二叉树外侧是否对称:传入的是左节点的左孩子,右节点的右孩子。
  • 比较内测是否对称,传入左节点的右孩子,右节点的左孩子。
  • 如果左右都对称就返回true ,有一侧不对称就返回false 。

代码如下:

        //左右值相等 继续递归 左子树外侧和右子树外侧、左子树内侧以及右子树内侧
        boolean outside = compare(left.left, right.right);
        boolean inside = compare(left.right, right.left);

        //两棵树的内外侧都相等返回true
        return inside && outside;

最后完整递归代码如下:

class Solution {
    public boolean isSymmetric(TreeNode root) {
        return compare(root.left, root.right); 
    }

    //递归,判断根节点的左右子树的外侧和内测是否都相等
    public boolean compare(TreeNode left, TreeNode right){
        if(left == null && right != null){ // 左空右不空
            return false;
        }else if(left != null && right == null){ // 左不空右空 
            return false;
        }else if(left == null && right == null){ //左右都空
            return true;
        }else if(left.val != right.val){ // 左右值不相等
            return false;
        }

        //左右值相等 继续递归 左子树外侧和右子树外侧、左子树内侧以及右子树内侧
        boolean outside = compare(left.left, right.right);
        boolean inside = compare(left.right, right.left);

        //两棵树的内外侧都相等返回true
        return inside && outside;
    }
}

迭代法

使用队列

我们可以借助队列来判断左右子树,左子树->左右,右子树->右左,对于节点元素是否相等来判断该树是否是对称二叉树。

代码如下:

如下的条件判断和递归的逻辑是一样的。

class Solution {
    public boolean isSymmetric(TreeNode root) {
        Deque<TreeNode> queue = new LinkedList<>();
        //将左右子树根节点放入队列中
        queue.offer(root.left);
        queue.offer(root.right);

        while(!queue.isEmpty()){
            //取出左右子树对应顺序的节点
            TreeNode left = queue.pop();
            TreeNode right = queue.pop();

            //左右两节点都为空,继续判断
            if(left == null && right == null){
                continue; // !!!为什么是continue?而不是直接返回false?
            }

            if(left == null && right != null){
                return false;
            }else if(left != null && right == null){
                return false;
            }else if(left.val != right.val){
                return false;    
            }

            queue.offer(left.left);//左子树外侧
            queue.offer(right.right);//右子树外侧
            queue.offer(left.right);//左子树内侧
            queue.offer(right.left);//右子树内侧
        }
        return true;
    }
}
    //左右两节点都为空,继续判断
    if(left == null && right == null){
        continue; // !!!为什么是continue?而不是直接返回false?
    }

如上一段代码,是为什么呢?看如下例子:

image-20221210171720978

当判断左右子树对应的第一个外侧节点时,此时对应两个节点都为null,如果直接返回true判断有误,所以应该跳过该节点继续判断后续节点,所以应该是continue而不是直接返回true

使用栈

细心的话,其实可以发现,这个迭代法,其实是把左右两个子树要比较的元素顺序放进一个容器,然后成对成对的取出来进行比较,那么其实使用栈也是可以的。

只要把队列原封不动的改成栈就可以了,代码如下。

class Solution {
    public boolean isSymmetric(TreeNode root) {
        Stack<TreeNode> stack = new Stack<>();
        //先将左右子树对应根节点放入栈中
        stack.push(root.left);
        stack.push(root.right);

        while(!stack.isEmpty()){
            //取出左右子树对应顺序的节点
            TreeNode left = stack.pop();
            TreeNode right = stack.pop();

            //左右两节点都为空,继续判断
            if(left == null && right == null){
                continue;
            }

            if(left == null && right != null){
                return false;
            }else if(left != null && right == null){
                return false;
            }else if(left.val != right.val){
                return false;    
            }

            stack.push(left.left);//左子树外侧
            stack.push(right.right);//右子树外侧
            stack.push(left.right);//左子树内侧
            stack.push(right.left);//右子树内侧
        }
        return true;
    }
}

257. 二叉树的所有路径

力扣题目链接

给定一个二叉树,返回所有从根节点到叶子节点的路径。

说明: 叶子节点是指没有子节点的节点。

示例:

image-20221213201838980

思路

前序遍历递归+回溯

如下图:

image-20221213220037530

class Solution {

    List<String> ans = new ArrayList<>();
    List<Integer> list = new ArrayList();    
    public List<String> binaryTreePaths(TreeNode root) {    
        if(root == null){
            return ans;
        }

        list.add(root.val);
        if(root.left != null){
            binaryTreePaths(root.left);
            //回溯
            list.remove(list.size() - 1);
        }
         
        if(root.right != null) {
            binaryTreePaths(root.right);
            //回溯 
            list.remove(list.size() - 1);
        }
        
        //叶子节点 
        if(root.left == null && root.right == null){
          ans.add(getPath(list));
        }
        return ans;
    }

    public String getPath(List<Integer> list){
        StringBuilder sb = new StringBuilder();
        for(int i = 0; i < list.size(); i++){
                if(i == list.size() - 1){
                    sb.append(list.get(i));
                }else
                sb.append(list.get(i)).append("->");

        }

        return sb.toString();
    }
}

或者

class Solution {
    
    public List<String> binaryTreePaths(TreeNode root) {  
        List<String> ans = new ArrayList<>();  
        if(root == null){
            return ans;
        }

        dfs(root, "", ans);
        return ans;
    }

   /**
    * 深度优先遍历
    */ 
   public void dfs(TreeNode node, String path, List<String> ans){
       if(node == null){
           return;
       }
       path += node.val;

      //根节点
      if(node.left == null && node.right == null){
          ans.add(path);
      } else{
          //不是根节点继续递归
          path += "->";
          dfs(node.left, path, ans);
          dfs(node.right, path, ans);
      }
   }
}

106.从中序与后序遍历序列构造二叉树

力扣题目链接

根据一棵树的中序遍历与后序遍历构造二叉树。

注意: 你可以假设树中没有重复的元素。

例如,给出

中序遍历 inorder = [9,3,15,20,7] ;后序遍历 postorder = [9,15,7,20,3]

返回如下的二叉树:

106. 从中序与后序遍历序列构造二叉树1

代码及实现步骤:

public class LC106 {

    /**
     * 哈希表快速查找,元素索引 用于查找中序数组中根节点索引下标
     */
    Map<Integer, Integer> map;

    /*
    整体步骤:
    1、第一步根据后序数组每次都能得到树的根节点
    2、得到根节点后,在中序数组中找到根节点下标索引,切割中序数组,该索引左边就是左子树节点,右边就是右子树节点
    3、然后根据左子树长度,可以从后序数组也切割出对应左子树节点和右子树节点
    4、在根据中序、后序数组对应左右子树节点递归得到对应左右孩子节点
    5、没有必要每次都创建新的数组再做处理,可以直接给出前后索引范围在原数组上直接切割处理,可以节省不少空间
     */

    public TreeNode buildTree(int[] inorder, int[] postorder) {
        map = new HashMap<>();
        for (int i = 0; i < inorder.length; i++) {
            map.put(inorder[i], i);
        }

        return buildNode(inorder, 0, inorder.length, postorder, 0, postorder.length);
    }

    public TreeNode buildNode(int[] inorder, int inBegin, int inEnd, int[] postorder, int postBegin, int postEnd) {
        // 参数下标遵循左闭右开原则
        if (inBegin >= inEnd || postBegin >= postEnd) {
            // 不满足条件,数组中没有元素直接返回
            return null;
        }
        // 后序数组最后一个元素是根节点元素
        // 从中序数组中得到根节点下标索引
        int rootIndex = map.get(postorder[postEnd - 1]);
        // 创建节点
        TreeNode root = new TreeNode(postorder[postEnd - 1]);

        // 保存中序左子树个数,用来确定后序数列的个数
        int lenOfLeft = rootIndex - inBegin;
        // 递归构建左右子树 中序左数组&后序左数组
        root.left = buildNode(inorder, inBegin, rootIndex, postorder, postBegin, postBegin + lenOfLeft);
        // 中序右数组&后序右数组
        root.right = buildNode(inorder, rootIndex + 1, inEnd, postorder, postBegin + lenOfLeft, postEnd - 1);
        return root;
    }
}

二叉树其余题目见代码随想录

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

录制电脑内部声音,2个方法,轻松解决

在我们日常的学习、娱乐和工作中&#xff0c;我们经常会遇到需要使用电脑录屏的情况。在电脑录屏的时候&#xff0c;怎么录制电脑内部声音&#xff1f;今天小编分享2个方法&#xff0c;教你如何轻松解决这个问题&#xff0c;一起来看看吧。 录制电脑内部声音方法1&#xff1a;Q…

Python基于PyTorch实现BP神经网络ANN分类模型项目实战

说明&#xff1a;这是一个机器学习实战项目&#xff08;附带数据代码文档视频讲解&#xff09;&#xff0c;如需数据代码文档视频讲解可以直接到文章最后获取。 1.项目背景 在人工神经网络的发展历史上&#xff0c;感知机(Multilayer Perceptron&#xff0c;MLP)网络曾对人工神…

什么是真正的骨传导耳机,五款真骨传导耳机推荐

市面上真假骨传导耳机不能辨别&#xff1f;真骨传导耳机是没有喇叭传播声音的&#xff0c;通过振子传播声音&#xff0c;我们在区分耳机是不是真骨传导耳机时&#xff0c;可以看看机身有没有喇叭音孔&#xff0c;有音孔的就不是利用骨传导传播声音的方式&#xff0c;下面就给大…

新的AI技术展望

“科学史是克服我们自身认知局限的不懈动力。”——约翰克拉考尔 这些是关于未来人工智能的话语。现在你可能在想&#xff0c;人工智能将如何影响我们&#xff0c;我们将如何处理它&#xff1f; 不用担心; 我有一个答案。AI&#xff08;人工智能&#xff09;已经在很多方面影…

easyexcel案例之类型转换,合并单元格,批注,下拉框,导入校验

文章目录easyexcel案例之类型转换&#xff0c;合并单元格&#xff0c;批注&#xff0c;下拉框&#xff0c;导入校验一、依赖二、导出1.类型转换导出2.自定义文件标题3.合并单元格导出注解方式通过 registerWriteHandler 方法注册进去自定义合并规则进行合并4.批注和下拉框导出批…

vulnhub DC系列 DC-2

总结:cewl和wpscan的使用&#xff0c;rbash逃逸&#xff0c;suid提权 下载地址 DC-2.zip (Size: 847 MB)Download: http://www.five86.com/downloads/DC-2.zipDownload (Mirror): https://download.vulnhub.com/dc/DC-2.zip使用方法:解压后&#xff0c;使用vm直接打开ova文件 漏…

MergeTree原理之二级索引

二级索引 除了一级索引之外&#xff0c;MergeTree同样支持二级索引&#xff0c;二级索引又称跳数索引&#xff0c;由数据的聚合信息构建而成。根据索引类型的不同&#xff0c;其聚合信息的内容也不同&#xff0c;当然跳数索引的作用和一级索引是一样的&#xff0c;也是为了查询…

【SpringCloud】什么是微服务?什么是SpringCloud?

【SpringCloud】什么是微服务&#xff1f;什么是SpringCloud&#xff1f; 一、什么是微服务&#xff1f; 1. 微服务架构的演变历程 单体架构 单体架构优缺点 2. 分布式架构 分布式架构优缺点 存在问题 3. 微服务 微服务的架构特征 微服务的优缺点 二、SpringClo…

头部3D建模新应用:护目镜类产品定制,省时高效好选择

自从越来越多人开始了运动健身&#xff0c;不少运动爱好者已经从小白用户升级为高级运动玩家。随着大家对运动装备的要求也越来越高&#xff0c;不少爱好者开始选购一些轻量化的私人订制装备。例如&#xff0c;高度符合用户头部外型的游泳眼镜、骑行护目镜等等。 游泳眼镜是为了…

用ACLS去控制访问文件

ACLs可以针对多个用户以及群组&#xff0c;其他人做出权限控制。文件系统需要挂载被ACL支持。XFS文件系统支持ACL。EXt4在7版本中默认激活ACL.但在更早的版本需要使用acl选项去挂载申请。 上图第十个字符.代表已经有了acl.表示已经设置ALC。文件的owner可以对文件设置ACL. get…

【结构型】组合模式(Composite)

目录组合模式(Composite)适用场景组合模式实例代码&#xff08;Java&#xff09;组合模式(Composite) 将对象组合成树型结构以表示“部分-整体”的层次结构。Composite使得用户对单个对象和组合对象的使用具有一致性。 适用场景 想表示对象的部分-整体层次结构。希望用户忽略…

Java判断null的几种方式

组内code review时&#xff0c;有同学提到字符串判断空值的写法&#xff0c;如下两种&#xff0c;&#xff08;1&#xff09;null在后&#xff0c;Test public void testDemo1() {String str null;if (str null) {System.out.println("null在后");return;} }&#…

计算机的人机交互

1、 计算机的人机交互发展历史 计算机在刚开始出现的时候&#xff0c;因为占地广、造价高、耗电高的原因&#xff0c;一般都是给军队、政府使用的&#xff0c;并不是给个人使用的。随着计算机的发展&#xff0c;体积越来越小&#xff0c;出现了微型机&#xff0c;才使得计算机…

C# 数据库 ADO.NET概述

一 数据库 1 数据库&#xff08;Database&#xff09; 2 数据库管理系统&#xff08;DBMS&#xff09; 如Oracle,MS SQL Server 3 数据库系统的优点 共享性、独立性、完整性、冗余数据少。 4 管理功能 数据定义/操纵/完整/完全/并发 二 常用的数据库管理系统 1 微软的…

剑指offer----C语言版----第二天

目录 1. 二维数组中的查找 1.1 题目描述 1.1 思路一 1.2 思路二 1.3 思路三&#xff08;最优解&#xff09; 1. 二维数组中的查找 原题链接&#xff1a;剑指 Offer 04. 二维数组中的查找 - 力扣&#xff08;LeetCode&#xff09;https://leetcode.cn/problems/er-wei-shu-…

WinServer 2012 域控组策略 用户发布软件部署

本例演示安装 Notepad 这款软件 因为域中发布软件只支持 msi 格式&#xff0c;所以要把 exe 转成 msi 格式&#xff0c;可以用这个软件 https://www.advancedinstaller.com/ 1、转换格式 &#xff08;1&#xff09;选择 MSI from EXE &#xff08;2&#xff09;定义项目名…

PLC实现十字路口交通灯的控制课程设计毕业设计

微信公众号&#xff1a;创享日记 对话框发送&#xff1a;plc十字路口 获取完整源码源程序文件 要求&#xff1a; 1、信号灯受启动及停止按钮的控制&#xff0c;当按下启动按钮时&#xff0c;信号灯系统开始工作&#xff0c;并周而复始地循环工作&#xff0c;当按下停止按钮时&…

【再学Tensorflow2】TensorFlow2的核心概念

TensorFlow2的核心概念Tensorflow中的张量常量张量变量张量Tensorflow中的计算图计算图介绍静态计算图动态计算图Autograph张量Tensor、图Graph、操作Operation、会话Session模型Model与层LayerTensorflow中的自动微分机制利用梯度磁带求导数利用梯度磁带和优化器求最小值参考资…

ArcGIS基础实验操作100例--实验13 数字化面图形的技巧

本实验专栏来自于汤国安教授《地理信息系统基础实验操作100例》一书 实验平台&#xff1a;ArcGIS 10.6 实验数据&#xff1a;请访问实验1&#xff08;传送门&#xff09; 基础编辑篇--实验13 数字化面图形的技巧 目录 一、实验背景 二、实验数据 三、实验步骤 &#xff08…

Android 虚拟机 模拟器 设置梯子代理 for Mac

最近需要Android13的环境&#xff0c;想着刷机或者弄个模拟器。 本着效率优先的原则&#xff0c;现在本地机器上搞个Android模拟器。 打开AndroidStudio&#xff0c;在tool菜单 选择avd&#xff0c;安卓虚拟设备 根据本地电脑选择x86架构&#xff0c;MAC os 10.12&#xff0c…