数据结构:二叉树详解

news2024/9/21 14:44:11

目录

概念(在做习题中常用的概念)

两种特殊的二叉树

二叉树的性质

二叉树的遍历(重点)

如上图:

二叉树的构建(代码表示一颗二叉树和一些操作二叉树的方法)

二叉树的oj习题讲解(重点)

1. 检查两棵树是否相同 力扣

2. 另一颗树的子树   力扣

3. 反转二叉树    力扣

4. 判断一颗二叉树是否为平衡二叉树( 分别在O(N*2)和O(N)的时间复杂度代码来写 )    力扣

5. 对称二叉树   力扣

6. 二叉树的构建及遍历(根据先序遍历创建一颗二叉树)   二叉树遍历_牛客题霸_牛客网

7. 二叉树的层序遍历   力扣

8. 二叉树中两个指定节点的最近公共祖先   力扣

9. 根据前序遍历和中序遍历构造一颗二叉树   力扣

10. 根据后序遍历和中序遍历构建一颗二叉树   力扣

11. 二叉树创建字符串   力扣

12. 二叉树的前序 非递归遍历   力扣

13. 二叉树的中序 非递归遍历   力扣

14. 二叉树的后序 非递归遍历   力扣

oj题代码详解:


前言

    二叉树的一些oj题是在面试中常考的,同时概念也是很重要的,在做二叉树习题中掌握概念以及一些推导公式才能提高做题效率。

概念(在做习题中常用的概念)

节点的度一个节点含有子树的个数
树的度在一棵树中所有节点度的最大值
叶子节点(终端节点)度为0的节点称为叶子节点
父亲节点(双亲节点)一个含有子节点的节点,这个节点称为其子节点的父亲节点
根节点一棵树中没有双亲节点的节点
节点的层从根节点开始,根节点就是第一层,根的子节点是第二层,一次类推
树的高度(深度)树种节点的最大层次

二叉树如何用代码表示(定义一个静态内部类)

static class TreeNode{
        public int val;
        public TreeNode left;
        public TreeNode right;
        public TreeNode(char val) {
            this.val = val;
        }
    }

    由于二叉树每个节点都有值域(value)、左子树(left)、右子树(right),所以将二叉树的节点定义成一个静态内部类,加上构造方法,就构成一颗二叉树中的完整的一个节点。

 两种特殊的二叉树

1. 满二叉树

     每层节点数都达到最大值,这颗二叉树就是满二叉树,或者一颗二叉树的层数为k,且节点总数是2^k - 1,此时这棵树就是满二叉树。

2. 完全二叉树

    完全二叉树是由满二叉树引出的,从根节点开始编号,顺序从上往下,每一行是从左往右,没有编号为空的节点,此时这棵树就是完全二叉树。

二叉树的性质

1. 根节点的层数为1,则一颗非空二叉树的第i层节点个数最多为:2 ^ (i -1)
2. 根节点的二叉树的深度为1,则深度为k的二叉树的最大节点数为:2^k - 1

3. 叶子节点个数为n0,度为2的节点个数为n2,则 n0 = n2 + 1

4.  n个节点的完全二叉树的深度为 log2 (n  + 1)

5. n个节点的完全二叉树,如果按层序遍历编号,对于i号节点有:

     (1)双亲节点编号:(i-1)/  2

     (2)左孩子节点编号:2*i + 1

     (3)右孩子节点编号:2 * i + 2

 二叉树的遍历(重点)

1. 前序遍历(先根遍历)根 ---> 左 ---> 右
2. 中序遍历左 ---> 根 ---> 右
3. 后序遍历左 ---> 右 ---> 根
4. 层序遍历按照顺序:从上往下,每一行从左往右依次编号

如上图:

    1. 先序遍历: 1 2 4 5 3 6 7

    2. 中序遍历: 4 2 5 1 6 3 7

    3. 后序遍历: 4 5 2 6 7 3 1

    4. 层序遍历: 1 2 3 4 5 6 7

二叉树的构建(代码表示一颗二叉树和一些操作二叉树的方法)

public class TextBinaryTree {
    static class TreeNode{
        public int val;
        public TreeNode left;
        public TreeNode right;
        public TreeNode(char val) {
            this.val = val;
        }
    }
    public TreeNode root;//定义二叉树的根节点
    //非正规创建一颗二叉树
    public TreeNode createTree() {
        TreeNode A = new TreeNode('A');
        TreeNode B = new TreeNode('B');
        TreeNode C = new TreeNode('C');
        TreeNode D = new TreeNode('D');
        TreeNode E = new TreeNode('E');
        TreeNode F = new TreeNode('F');
        TreeNode G = new TreeNode('G');
        TreeNode H = new TreeNode('H');
        A.left = B;
        A.right = C;
        B.left = D;
        B.right = E;
        E.right = H;
        C.left = F;
        C.right = G;
        this.root = A;
        return root;
    }
    //前序遍历
    public void preOrder(TreeNode root) {
        if (root == null) return;
        System.out.print(root.val + " ");
        preOrder(root.left);
        preOrder(root.right);
    }
    //中序遍历
    public void inOrder(TreeNode root) {
        if (root == null) return;
        inOrder(root.left);
        System.out.print(root.val + " ");
        inOrder(root.right);
    }
    //后序遍历
    public void postOrder(TreeNode root) {
        if (root == null) return;
        postOrder(root.left);
        postOrder(root.right);
        System.out.print(root.val + " ");
    }
    //前序遍历有返回值
    //此时这个list需要放在外边,否则每次递归都会产生一个新的list
    List<Integer> list = new ArrayList<>();
    public List<Integer> preorderTraversal(TreeNode root) {
        if (root == null) return list;
        list.add(root.val);
        preorderTraversal(root.left);
        preorderTraversal(root.right);
        return list;
    }
    //二叉树的中序遍历有返回值
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> list = new ArrayList<>();
        if (root == null) return list;
        List<Integer> leftTree = inorderTraversal(root.left);
        list.addAll(leftTree);
        list.add(root.val);
        List<Integer> rightTree = inorderTraversal(root.right);
        list.addAll(rightTree);
        return list;
    }
    //二叉树的后序遍历有返回值
    public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> list = new ArrayList<>();
        if (root == null) return list;
        List<Integer> leftTree = postorderTraversal(root.left);
        list.addAll(leftTree);
        List<Integer> rightTree = postorderTraversal(root.right);
        list.addAll(rightTree);
        list.add(root.val);
        return list;
    }
    //获取二叉树中的节点个数
    //子问题:左树的节点 + 右树的节点 + 1
    //时间复杂度:O(N)  空间复杂度:O(logN),如果是单分支的树,空间复杂度就是
    //O(N)
    public int size(TreeNode root) {
        if (root == null) return 0;
        return size(root.left) + size(root.right) + 1;
    }
    //遍历思路
    public int count = 0;
    public int size1(TreeNode root) {
        if (root == null) return 0;
        //只要根不为空就++,然后遍历左树和右树
        count++;
        size1(root.left);
        size1(root.right);
        return count;
    }
    //获取叶子节点的个数   子问题思路
    public int getLeafNodeCount(TreeNode root) {
        if (root == null) return 0;//注意:当root为空的时候是没有叶子节点的
        if (root.left == null && root.right == null)
            return 1;
        return getLeafNodeCount(root.left) +
                getLeafNodeCount(root.right);
    }
    //遍历思路
    public int leafCount;
    public void leafNodeCount(TreeNode root) {
        if (root == null) return;
        if (root.left == null && root.right == null)
            leafCount++;
        leafNodeCount(root.left);
        leafNodeCount(root.right);
    }
    //获取第k层的节点个数
    //第k层的节点个数就相当于第k-1层的孩子的节点个数
    public int getLevelNodeCount(TreeNode root, int k) {
        if (root == null) return 0;
        if (k == 1) return 1;
        int left = getLevelNodeCount(root.left, k-1);
        int right = getLevelNodeCount(root.right, k-1);
        return left + right;
    }
    //获取二叉树的高度
    //时间复杂度:O(n)(会遍历到每一个节点)
    public int getHeight(TreeNode root) {
        if (root == null) return 0;
        int leftHeight = getHeight(root.left);
        int rightHeight = getHeight(root.right);
        return Math.max(leftHeight,rightHeight) + 1;
    }
    //二叉树的层序遍历
    public void levelOrder1(TreeNode root) {
        if (root == null) return;
        Queue<TreeNode> queue = new LinkedList<>();
        queue.offer(root);
        while (!queue.isEmpty()) {
            TreeNode cur = queue.poll();
            System.out.print(cur.val + " ");
            if (cur.left != null) {
                queue.offer(cur.left);
            }
            if (cur.right != null) {
                queue.offer(cur.right);
            }
        }
    }
    //二叉树层序遍历有返回值
    public List<List<Integer>> levelOrder2(TreeNode root) {
        List<List<Integer>> list = new ArrayList<>();
        if (root == null) return list;
        Queue<TreeNode> queue = new LinkedList<>();
        queue.offer(root);
        while (!queue.isEmpty()) {
            int size = queue.size();
            List<Integer> ret = new ArrayList<>();
            while (size != 0) {
                TreeNode cur = queue.poll();
                ret.add(cur.val);
                size--;
                if (cur.left != null) {
                    queue.offer(cur.left);
                }
                if (cur.right != null) {
                    queue.offer(cur.right);
                }
            }
            list.add(ret);
        }
        return list;
    }
}

二叉树的oj习题讲解

1. 检查两棵树是否相同 力扣

思路:遍历两棵树的每一个节点,若一个空,一个不为空,不相同,两个节点都不为空,判断值是否相等,值不相等,也不是相同的树。

2. 另一颗树的子树   力扣

思路:先判断是不是相同的树,如果是相同的树也算是子树,如果不相同,判断是不是root的左子树,是不是root(根节点)的右子树。

3. 反转二叉树    力扣

思路:反转就是交换左右两个节点的位置,如果是根节点就不用交换,交换root.left 和 root.right 节点,之后递归去反转根节点的左子树的左子树,右子树的右子树。

4. 判断一颗二叉树是否为平衡二叉树( 分别在O(N*2)和O(N)的时间复杂度代码来写 )    力扣

 1.  思路:相邻的左右两个节点高度差如果超过1,就不是平衡,否则就是平衡树。(求高度的过程要写成一个方法)

 2. 思路:在求高度的过程中就判断高度差是否超过1,如果高度差超过1,此时就返回一个-1,如果求高度过程中返回了-1,之后的节点就不用求高度了,时间复杂度就是O(N)

5. 对称二叉树   力扣

思路:判断整棵树是否对称,需要判断这棵树的左子树和右子树是否是对称的,和判断是否为相同的树类似,只是判断的节点换成了左子树的左和右子树的右是否相同。

6. 二叉树的构建及遍历(根据先序遍历创建一颗二叉树)   二叉树遍历_牛客题霸_牛客网

思路:在遍历字符串的过程中,创建节点并且递归创建左子树和右子树,之后按中序遍历进行输出即可。

7. 二叉树的层序遍历   力扣

思路:层序遍历是有顺序的,此时需要用合适的数据结构:队列,先入队列根,之后在弹出节点的时候进行记录,左右不为空的情况就把左和右子节点入队列。

8. 二叉树中两个指定节点的最近公共祖先   力扣

思路:如果在根的两边(p在左子树,q在右子树),此时根节点就是,如果都在左边,就去遍历二叉树,找到一个节点就返回(先找到的节点就是公共祖先),如果都在右边也是同理。

9. 根据前序遍历和中序遍历构造一颗二叉树   力扣

思路:先在前序遍历中找到根,然后拿着根在中序遍历 中找到根的位置,根的左边就是左子树,根的右边就是右子树,(记录左子树和右子树的下标)然后根据左子树和右子树的下标递归创建左树和右树。

10. 根据后序遍历和中序遍历构建一颗二叉树   力扣

思路:在后序遍历中找到根的位置,然后再在中序遍历中找到根的位置,划分左子树,右子树,记录左子树和右子树的下标,根据下标递归创建左树和右树。

11. 二叉树创建字符串   力扣

思路:考虑几种情况;左子树为空的前提下,右子树为空或者不为空,左子树不为空的前提下,右子树为空或者不为空。

12. 二叉树的前序 非递归遍历   力扣

思路:用栈这个数据结构,定义一个cur节点,当cur.left不为空的时候,就一直往左走(同时入栈走过的节点),一直到cur为空了,此时弹出栈顶节点,打印弹出的节点,同时记录这个节点,然后让cur走到弹出节点的右边,继续遍历,直到栈空了,此时也就遍历完成了。

13. 二叉树的中序 非递归遍历   力扣

思路:和前序遍历类似,只是打印的时机不同了。

14. 二叉树的后序 非递归遍历   力扣

思路:和前中序遍历类似,只是需要注意两点:(1)需要判断根以下的右子树是否为空(两种情况代码不一样)

(2)在记录的节点的右子节点打印之后就不要再次进行打印了。

oj题代码详解:

//二叉树的构建及遍历:读入一颗先序遍历的字符串,创建一颗二叉树
    //然后按中序遍历输出二叉树的节点
    //虽然只是一个前序遍历的字符串,但是也是可以创建出一颗二叉树的(有顺序的前序遍历)
    /*此处的字符串下标是不会越界的,因为给的前序遍历本来就是一个合法的字符串,如果多给了
    * 或者是少给了,此时在创建右树的时候就会发生越界,因为字符串已经遍历完了,但是可能树
    * 还没有建完,所以还会递归,但是现在是合法的字符串,此时递归的时候有回退的次数*/
    public static void main(String[] args) {
        //在main方法中将i置零也是可以的,
        Scanner scan = new Scanner(System.in);
        //将字符串整行都读进来
        String str = scan.nextLine();
        TreeNode root = createTree(str);
        inorder(root);
    }
    public static int i = 0;
    public static TreeNode createTree(String str) {
        TreeNode root = null;
        char ch = str.charAt(i);
        if (ch != '#') {
            root = new TreeNode(ch);
            i++;
            root.left = createTree(str);
            root.right = createTree(str);
        }else {
            i++;
        }
        return root;
    }
    public static void inorder(TreeNode root) {
        if (root == null) return;
        inorder(root.left);
        System.out.println(root.val + " ");
        inorder(root.right);
    }
    //二叉树的最近公共祖先
    /*1.在根的两边 2.要么都在根的左边或者右边(其中一个就是公共祖先)*/
    public TreeNode lowestCommonAncestor(
            TreeNode root, TreeNode p, TreeNode q) {
        if (root == null) return null;
        if (root == p || root == q) return root;
        TreeNode left = lowestCommonAncestor(root.left, p, q);
        TreeNode right = lowestCommonAncestor(root.right, p, q);
        if (left != null && right != null) return root;
        /*这个地方是找到就返回了,如:在左边先找到了一个节点,此时p和q都在
        * root的左边,但是找到p就结束了,不会再去找另一个节点了,所以返回的
        * 节点就是上边的节点,就是最近的公共祖先*/
        else if (left != null) return left;
        else if (right != null) return right;
        else return null;
    }
    //如果每个节点有了父亲节点的地址,此时就变成了求相交节点
    //但是二叉树没有父亲节点,所以用一个数据结构:栈
    /*把找p和q的路径入到两个栈中,然后元素多的先出差值个元素,之后再一起出
    * 如果元素相等此时这个元素就是最近公共祖先*/

    //找到从根节点到指定节点node路径上所有的节点,然后入栈
    public boolean getPath(TreeNode root,
                           TreeNode node, Deque<TreeNode> stack) {
        if (root == null || node == null) return false;
        stack.push(root);
        if (root == node) return true;
        boolean ret1 = getPath(root.left,node,stack);
        boolean ret2 = getPath(root.right,node,stack);
        if (ret1) return true;
        if (ret2) return true;
        //左边和右边都没找到这个node,此时就回退,让这个元素出栈
        stack.pop();
        return false;//说明这条路径走不通,走到头了,也没找到这个节点
    }
    public TreeNode lowestCommonAncestor2(TreeNode root,
                                          TreeNode p, TreeNode q) {
        //1.两个栈中先存储好数据
        Deque<TreeNode> stack1 = new LinkedList<>();
        getPath(root,p,stack1);
        Deque<TreeNode> stack2 = new LinkedList<>();
        getPath(root,q,stack2);
        //2.判断栈的大小,出差值个元素
        int size1 = stack1.size();
        int size2 = stack2.size();
        //让size1是最大的
        int ret = Math.abs(size1 - size2);
        while (ret != 0) {
            if (size1 > size2) {
                stack1.pop();
            }else {
                stack2.pop();
            }
            ret--;
        }
        //栈中元素个数相等,此时一块出元素
        while (!stack1.isEmpty() && !stack2.isEmpty()) {
            if (stack1.peek() != stack2.peek()) {
                stack1.pop();
                stack2.pop();
            }else {
                return stack1.pop();
            }
        }
        return null;
    }
    //根据前序遍历和中序遍历构造一颗二叉树
    /*1.根据前序遍历找到根   2.在中序遍历中找到根的位置*/
    //public int i = 0;
    public TreeNode buildTree(int[] preorder, int[] inorder) {
        TreeNode root = buildTreeChild(preorder, inorder, 0,
                inorder.length - 1);
        return root;
    }
    public TreeNode buildTreeChild(int[] preorder, int[] inorder,
                                   int begin, int end) {
        if (begin > end) return null;//如果下标越界,此时说明没有节点了
        //TreeNode root = new TreeNode(preorder[i]);
        //找到当前根在中序遍历中的位置
        int rootIndex = findIndex(inorder,begin, end, preorder[i]);
        i++;
        root.left = buildTreeChild(preorder,inorder,begin,rootIndex-1);
        root.right = buildTreeChild(preorder, inorder, rootIndex+1,end);
        return root;
    }

    private int findIndex(int[] inorder, int begin, int end, int key) {
        for (int j = begin; j <= end; j++) {
            if (key == inorder[j]) return j;
        }
        return -1;
    }
    //根据中序遍历和后序遍历来创建一颗二叉树
    /*1.先从后续遍历中找到根,之后在中序遍历中找到根的位置*/
    /*然后i--,要注意,先创建的是右树,然后创建左树*/
    public int a;
    public TreeNode buildTree3(int[] inorder, int[] postorder) {
        a = postorder.length - 1;
        TreeNode root = buildChildTree(inorder,postorder,
                 0, inorder.length - 1);
        return root;
    }
    public TreeNode buildChildTree(int[] inorder,int[] postorder,
                                   int begin, int end) {
        //TreeNode root = new TreeNode(postorder[a]);
        int rootIndex = findIndex(inorder, begin, end, postorder[a]);
        a--;
        root.right = buildChildTree(inorder,postorder,rootIndex + 1,end);
        root.left = buildChildTree(inorder, postorder, begin, rootIndex - 1);
        return root;
    }
    //根据二叉树创建字符串
    /*要以前序遍历的方式来创建这个字符串*/
    public String tree2str(TreeNode root) {
        if (root == null) return null;
        StringBuilder builder = new StringBuilder();
        tree2strChild(root, builder);
        /*1.左边为空,右边为空  2.左边不为空右边为空  3.左边为空右边不为空*/
        return builder.toString();
    }
    public void tree2strChild(TreeNode root, StringBuilder builder) {
        if (root == null) return;
        builder.append(root.val);
        //左子树为空或者不为空
        if (root.left != null) {
            builder.append("(");
            tree2strChild(root.left,builder);
            builder.append(")");
        }else {
            //左边为空的情况下考虑右边
            if (root.right != null) {
                builder.append("()");
            }else {
                return;
            }
        }
        //右子树为空或者不为空
        if (root.right != null) {
            builder.append("(");
            tree2strChild(root.right,builder);
            builder.append(")");
        }else {
            return;
        }
    }
    //判断一棵树是否为完全二叉树
    //用合适的数据结构: 队列
    /*每次从队列中取出一个节点,不为空:把左子树和右子树带进来,
    * 为空:开始判断队列中剩余的元素*/
    public boolean isCompleteTree(TreeNode root) {
        if (root == null) return true;
        Queue<TreeNode> queue = new LinkedList<>();
        queue.offer(root);
        while (!queue.isEmpty()) {
            TreeNode cur = queue.poll();
            if (cur != null) {
                queue.offer(cur.left);
                queue.offer(cur.right);
            }else {
                //如果cur为空说明左和右都是空的了,此时就去判断队列中的元素是否都是空的
                break;
            }
        }
        while (!queue.isEmpty()) {
            TreeNode tmp = queue.poll();
            if (tmp != null) return false;
        }
        return true;
    }
    //非递归实现前序遍历
    public List<Integer> preorderTraversalNor(TreeNode root) {
        List<Integer> list = new ArrayList<>();
        TreeNode cur = root;
        Stack<TreeNode> stack = new Stack<>();
        while (cur != null || !stack.isEmpty()) {
            while (cur != null) {
                stack.push(cur);
                list.add(cur.val);
                cur = cur.left;
            }
            TreeNode top = stack.pop();
            cur = top.right;
        }
        return list;
    }
    //非递归实现中序遍历
    public List<Integer> inorderTraversalNor(TreeNode root) {
        List<Integer> list = new ArrayList<>();
        TreeNode cur = root;
        Stack<TreeNode> stack = new Stack<>();
        while (cur != null || !stack.isEmpty()) {
            while (cur != null) {
                stack.push(cur);
                cur = cur.left;
            }
            TreeNode top = stack.pop();
            list.add(top.val);
            cur = top.right;
        }
        return list;
    }
    //非递归实现后序遍历
    public List<Integer> postorderTraversalNor(TreeNode root) {
        List<Integer> list = new ArrayList<>();
        TreeNode cur = root;
        Stack<TreeNode> stack = new Stack<>();
        TreeNode prev = null;
        while (cur != null || !stack.isEmpty()) {
            while (cur != null) {
                stack.push(cur);
                cur = cur.left;
            }
            TreeNode top = stack.peek();
            //此时需要注意:当打印之后top.right之后就不要再次打印了
            //所以prev记录的是 是否打印过top.right这个节点
            if (top.right == null || prev == top.right) {
                stack.pop();
                list.add(top.val);
                prev = top;
            }else {
                cur = top.right;
            }
        }
        return list;
    }

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

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

相关文章

代码审计-Java项目Filter过滤器CNVD分析XSS跨站框架安全

文章目录 Demo-Filter-过滤器引用Demo-ST2框架-组件安全CNVD-Jeesns-XSS跨站绕过CNVD-悟空CRM-Fastjson组件 Demo-Filter-过滤器引用 Filter&#xff1a;Javaweb三大组件之一(另外两个是Servlet、Listener) 概念&#xff1a;Web中的过滤器&#xff0c;当访问服务器的资源时&am…

编程语言的优劣评选标准与未来发展趋势——探索最佳编程语言选择

编程语言的优劣评选标准与未来发展趋势——探索最佳编程语言选择 评判标准不同编程语言的优点与缺点分析对编程语言未来发展的猜测和未来趋势 &#x1f495; &#x1f495; &#x1f495; 博主个人主页&#xff1a; 汴京城下君–野生程序员&#x1f495; &#x1f495; &#x…

编程输出三位数的水仙花数

目录 题目 分析思路 代码 题目 编程输出三位数的水仙花数 标准的 水仙花数 就是三位数&#xff0c;即将三位数的个位&#xff1b;十位&#xff1b;百位取出来&#xff0c;分别三次方相加&#xff0c;若个位&#xff1b;十位&#xff1b;百位三次方相加与原来的三位数相等&a…

模拟电路系列文章-频率响应的描述

目录 概要 整体架构流程 技术名词解释 技术细节 1.为什么受频率的影响 2.频率响应 小结 概要 提示&#xff1a;这里可以添加技术概要 电容和电感是储能元件&#xff0c;对不同频率的交流信号&#xff0c;它们具有不同的感抗或者容抗。虽然它们不消耗功率&#xff0c;但同电阻一…

【PHP】文件写入和读取详解

一&#xff0e;实现文件读取和写入的基本思路&#xff1a; 1&#xff0e;通过fopen方法打开文件&#xff1a;$fp fopen(/*参数&#xff0c;参数*/)&#xff0c;fp为Resource类型 2&#xff0e;进行文件读取或者文件写入操作&#xff08;这里使用的函数以1中返回的$fp作为参数…

Python网络爬虫基础进阶到实战教程

文章目录 认识网络爬虫HTML页面组成Requests模块get请求与实战效果图代码解析 Post请求与实战代码解析 发送JSON格式的POST请求使用代理服务器发送POST请求发送带文件的POST请求 Xpath解析XPath语法的规则集&#xff1a;XPath解析的代码案例及其详细讲解&#xff1a;使用XPath解…

macOS Sonoma 14 beta 2 (23A5276g) ISO、IPSW、PKG 下载

macOS Sonoma 14 beta 2 (23A5276g) ISO、IPSW、PKG 下载 本站下载的 macOS 软件包&#xff0c;既可以拖拽到 Applications&#xff08;应用程序&#xff09;下直接安装&#xff0c;也可以制作启动 U 盘安装&#xff0c;或者在虚拟机中启动安装。另外也支持在 Windows 和 Linu…

【致敬未来的攻城狮计划】打卡2:keil 程序下载尝试

下载 终于考完试了&#xff0c;然而攻城狮的截止期限也快到了QAQ&#xff0c;得尽快水&#xff08;划掉&#xff09;写几篇文章了&#xff01; 先争取可以成功下载一个空的程序。 先对上一篇文章下载 DFP 也就是 keil MDK Software Packs 做一个补充。我们要下载的是 RA_DFP…

[进阶]TCP通信综合案例:群聊

代码演示如下&#xff1a; 客户端&#xff1a; public class Client {public static void main(String[] args) throws Exception{System.out.println("客户端开启&#xff01;");//1.创建Socket对象&#xff0c;并同时请求与服务端程序的连接。Socket socket new…

网线制作实验

计算机网络综合实训 网线制作 所在院系 计算机与信息工程学院 学科专业名称 计算机科学与技术 导师及职称 柯宗武 教授 提交时间 2023.4.30 计算机网络综合实训报告 一、实验目的 1、掌握三类UTP线缆的制作和测试方法 2、了解三类UTP线缆的作用及布线方式 二、实验内容 1、局…

第二章 进程的描述与控制

目录 一、进程的概念、组成、特征 1.1 进程的概念 1.1.1 程序 1.1.2 进程 1.2 进程的组成 1.3 进程控制块PCB 1.4 进程的特征 二、进程的状态与转换 2.1 进程的状态 2.2 创建态、就绪态 2.3 运行态 2.4 阻塞态 2.5 终止态 2.6 进程状态的转换 2.7 进程的组织 三…

【C++篇】C++字符串:标准库string类

友情链接&#xff1a;C/C系列系统学习目录 知识总结顺序参考C Primer Plus&#xff08;第六版&#xff09;和谭浩强老师的C程序设计&#xff08;第五版&#xff09;等&#xff0c;内容以书中为标准&#xff0c;同时参考其它各类书籍以及优质文章&#xff0c;以至减少知识点上的…

python3.9 安装 pyspider

安装pyspider pip install pyspider 直接报错 Please specify --curl-dir/path/to/built/libcurl 于是从PythonLibs官网 中获取依赖并自行下载到本地 下载与3.9对应的 执行安装 pip install D:\下载\ad3-2.2.1-cp39-cp39-win32.whl 安装成功之后 执行 pip install p…

1748_Perl中使用通配符处理文件

全部学习汇总&#xff1a; GreyZhang/perl_basic: some perl basic learning notes. (github.com) 使用通配符处理文件一般是用于文件的拷贝、删除以及对文件逐个分析等功能。在Perl中遇到的相应的功能基本上也是如此。通配符匹配处理文件&#xff08;文件名&#xff09;需要使…

前端JavaScript入门-day01

(创作不易&#xff0c;感谢有你&#xff0c;你的支持&#xff0c;就是我前行的最大动力&#xff0c;如果看完对你有帮助&#xff0c;请留下您的足迹&#xff09; 目录 JavaScript 介绍 1 JavaScript 是什么 1. JavaScript &#xff08;是什么&#xff1f;&#xff09; 2. 作…

H. Don‘t Blame Me(DP)

Problem - 1829H - Codeforces 很遗憾&#xff0c;这道题目的出题人没有想到一个有趣的故事&#xff0c;所以他只是让你解决以下问题。 给定由n个正整数组成的数组a&#xff0c;计算具有子序列中元素的按位AND在其二进制表示中恰好有k个设置位的非空子序列的数量。答案可能很大…

Winwebmail搭建邮件服务器

配置环境 角色系统IP地址DNS邮件服务器WindowServer2016192.168.1.1/24192.168.1.1客户端1Window10192.168.1.10/24192.168.1.1客户端2Window10192.168.1.20/24192.168.1.1 Winwebmail介绍 WinWebMail是安全高速的全功能邮件服务器&#xff0c;融合强大的功能与轻松的管理为…

macOS Ventura 13.4.1 (22F82|22F2083) 正式版发布,ISO、IPSW、PKG 下载

macOS Ventura 13.4.1 (22F82|22F2083) 正式版发布&#xff0c;ISO、IPSW、PKG 下载 本站下载的 macOS Ventura 软件包&#xff0c;既可以拖拽到 Applications&#xff08;应用程序&#xff09;下直接安装&#xff0c;也可以制作启动 U 盘安装&#xff0c;或者在虚拟机中启动安…

C++ 指针常量与常量指针

指针常量与常量指针的翻译就好比机器学习中Regularization翻译成正则化一样&#xff0c;讲真&#xff0c;正则化这个说法确实要比规则化显得高端些&#xff0c;但是到了理解的时候&#xff0c;还是要按照规则化理解。 指针常量与常量指针这个概念是一样的&#xff0c;英文里没…

【STM32】软件I2C(支持多字节)

I2C简介 I2C总线是一种串行、半双工的总线&#xff0c;主要用于近距离、低速的芯片之间的通信。I2C总线有两根双向的信号线&#xff0c;一根数据线SDA用于收发数据&#xff0c;一根时钟线SCL用于通信双方时钟的同步。 在一个i2c通讯总线中&#xff0c;可连接多个i2c通讯设备&a…