数据结构——用Java实现二分搜索树

目录

一、树

二、二分搜索树

1.二叉树

2.二分搜索树

三、代码实现

1.树的构建

2.获取树中结点的个数

3.添加元素

4.查找元素

(1)查找元素是否存在

(2)查找最小元素

(3)查找最大元素

5.二分搜索树的遍历

(1)前序遍历:

(2)中序遍历:

(3)后序遍历:

(4)层序遍历:

6.删除操作

(1)删除最小元素

(2)删除最大元素

(3)删除任意元素

(4)删除根节点


一、树

        树结构本身是一种天然的组织结构

        是一个高效的查询内容的结构

二、二分搜索树

1.二叉树

特点:1.只有唯一的一个根节点

           2.每个结点最多有两个孩子

           3.每个结点最多有一个父亲

           4.二叉树具有天然的递归结构(左右子树也是二叉树)

           5.叶子结点出现在二叉树的最底层,除叶子结点之外的其它结点都有两个孩子结点。

2.二分搜索树

是特殊的二叉树

每个节点都大于左子树的所有结点,都小于右子树的所有结点

注意:存储的元素必须具有可比性

因为二分搜索树也是二叉树,也具有天然的递归结构,所以许多方法都可以使用递归的思想去实现

三、代码实现

1.树的构建

需要的元素有:根节点,结点,频率(如果添加的元素有重复元素),结点的值,索引,结点个数

//树的结点private static class Node<T> {private final T ele;//结点的值private int frequence;//频率private Node<T> left, right;//分别指向左右孩子的索引public Node(T ele) {this.ele = ele;this.left = this.right = null;}}//树对应的属性private Node<T> root;//树的根节点private int size;//结点的个数//构建树public BinearySeachTree() {this.root = null;this.size = 0;}

在给元素添加泛型后,就不能直接比较,所以在开始就继承Comparable来实现元素的比较

public class BinearySeachTree<T extends Comparable<T>>{}

2.获取树中结点的个数

//获取树中结点的个数public int getSize() {return this.size;}

3.添加元素

将元素添加到二分搜索树的过程中,要注意将大的元素放在结点的右边,小的元素放在左边

再添加元素时,需要找到对应的位置,则可以使用递归的思想。

如果添加的值小于结点的值,则查找结点左孩子,如果还是小于结点,则继续查找

//向树中添加结点public void add(T ele) {//更新根结点this.root = addDG(this.root, ele);}//语义:向以root为根的二分搜索树中添加元素eleprivate Node<T> addDG(Node<T> root, T ele) {//递归终止条件if (root == null) {this.size++;return new Node<T>(ele);}//递归操作if (root.ele.compareTo(ele) > 0) {root.left = addDG(root.left, ele);} else if (root.ele.compareTo(ele) < 0) {root.right = addDG(root.right, ele);} else {//更新频率root.frequence++;}return root;}

4.查找元素

(1)查找元素是否存在

        查找元素是否在二叉树中,查找每一个结点,如果查找元素比当前节点小,就在左子树里重新查找,如果查找元素比当前节点大,就在右子树里重新查找

 //查询的方法public boolean search(T ele) {return searchDG(this.root, ele);}//语义:从以root为根的二分搜索树中查找元素eleprivate boolean searchDG(Node<T> root, T ele) {//递归终止的条件if (root == null) {return false;}//递归操作if (root.ele.compareTo(ele) == 0) {return true;} else if (root.ele.compareTo(ele) > 0) {return searchDG(root.left, ele);} else {return searchDG(root.right, ele);}}

(2)查找最小元素

        二分搜索树中最左边的元素

 //找树中的最小元素public T getMinValue() {if (this.isEmpty()) {return null;}Optional<Node<T>> optional = getMinNode();return optional.get().ele;}//直接查找
private Optional<Node<T>> getMinNode() {if (this.root == null) {return Optional.empty();}//一直向左查找Node<T> node = this.root;while (node.left != null) {node = node.left;}return Optional.of(node);}//利用递归方法查找//语义:在以Node为根结点的树中查找最小结点private Optional<Node<T>> getMinNode(Node<T> node) {if (node.left == null) {return Optional.of(node);}return getMinNode(node.left);}

(3)查找最大元素

        二分搜索树中最右边的元素

//找树中的最大元素public T getMaxValue() {if (this.isEmpty()) {return null;}Optional<Node<T>> optional = getMaxNode(this.root);return optional.get().ele;}//语义:在以Node为根结点的树中查找最大结点private Optional<Node<T>> getMaxNode(Node<T> node) {if (node.right == null) {return Optional.of(node);}return getMaxNode(node.right);}

5.二分搜索树的遍历

树的遍历有四种:前序遍历;中序遍历;后序遍历;层序遍历

(1)前序遍历:

首先打印根节点,然后遍历左子树,最后是右子树

【28,16,13,22,30,29,42】

//前序遍历public void preTravel() {List<AbstractMap.SimpleEntry<T, Integer>> list = new ArrayList<>();preTravelDG(this.root, list);String str = list.stream().map(item -> "[" + item.getKey() + ":" + item.getValue() + "]").collect(Collectors.joining("-"));System.out.println(str);}//前序遍历以root为根的树,讲解稿保存在list中private void preTravelDG(Node<T> root, List<AbstractMap.SimpleEntry<T, Integer>> list) {//递归终止条件if (root == null) {return;}//递归操作list.add(new AbstractMap.SimpleEntry<>(root.ele, root.frequence));//遍历左子树preTravelDG(root.left, list);//遍历右子树preTravelDG(root.right, list);}

(2)中序遍历:

先遍历左子树,在打印中间结点,最后遍历右子树

【13,16,22,28,29,30,42】

//中序遍历public void midTravel() {List<AbstractMap.SimpleEntry<T, Integer>> list = new ArrayList<>();midTravelDG(this.root, list);String str = list.stream().map(item -> item.toString()).collect(Collectors.joining("-"));System.out.println(str);}//中序遍历以root为根的树,讲解稿保存在list中private void midTravelDG(Node<T> root, List<AbstractMap.SimpleEntry<T, Integer>> list) {//递归终止条件if (root == null) {return;}//递归操作//遍历左子树preTravelDG(root.left, list);list.add(new AbstractMap.SimpleEntry<>(root.ele, root.frequence));//遍历右子树preTravelDG(root.right, list);}

(3)后序遍历:

先遍历左子树,在遍历右子树,最后在打印中间结点

【13,22,16,29,42,30,28】

 //后序遍历public void sufTravel() {List<AbstractMap.SimpleEntry<T, Integer>> list = new ArrayList<>();sufTravelDG(this.root, list);String str = list.stream().map(item -> item.toString()).collect(Collectors.joining("-"));System.out.println(str);}//后序遍历以root为根的树,讲解稿保存在list中private void sufTravelDG(Node<T> root, List<AbstractMap.SimpleEntry<T, Integer>> list) {//递归终止条件if (root == null) {return;}//递归操作//遍历左子树preTravelDG(root.left, list);//遍历右子树preTravelDG(root.right, list);list.add(new AbstractMap.SimpleEntry<>(root.ele, root.frequence));}

可以看到,先中后序遍历的代码区别只是在递归最后将元素添加到list的位置不同而已

(4)层序遍历:

一层一层的打印

【28,16,30,13,22,29,42】

//层序遍历public void levelTravel() {//判断树是否为空if (this.isEmpty()) {return;}Queue<AbstractMap.SimpleEntry<Node<T>, Integer>> queue = new LinkedList<>();//1.先将根结点入队queue.add(new AbstractMap.SimpleEntry<>(this.root, 1));//2.遍历队列while (!queue.isEmpty()) {//2-1.出队AbstractMap.SimpleEntry<Node<T>, Integer> pair = queue.poll();//结点Node<T> node = pair.getKey();//层int level = pair.getValue();;System.out.println("[val:" + node.ele + ",level:" + level + "]");//2-2.判断左右子树是否为空if (node.left != null) {queue.add(new AbstractMap.SimpleEntry<>(node.left, level + 1));}if (node.right != null) {queue.add(new AbstractMap.SimpleEntry<>(node.right, level + 1));}}}

6.删除操作

删除的操作中,需要注意删除后二分搜索树也会因此改变,所以要分情况讨论

(1)删除最小元素

删除最小元素并不需要改变树,只需要失去关联关系即可

 //从树中删除最小的结点public T removeMinNode() {T result = getMinValue();if (result == null) {return null;}//更新根结点this.root = removeMinNode(this.root);return result;}//语义:从以Node为根的二分搜索树中删除元素最小的结点private Node<T> removeMinNode(Node<T> node) {//递归终止条件if (node.left == null) {//删除操作//1.记录右子树Node<T> rightTree = node.right;//失去关联关系node.right = null;//3.跟新sizethis.size--;return rightTree;}//递归操作node.left = removeMinNode(node.left);return node;}

(2)删除最大元素

跟删除最小元素一样,只需要失去关联关系即可

//从树中删除最大的结点public T removeMaxNode() {T result = getMaxValue();if (result == null) {return null;}//更新根结点this.root = removeMaxNode(this.root);return result;}//语义:从以Node为根的二分搜索树中删除元素最大的结点private Node<T> removeMaxNode(Node<T> node) {//递归终止条件if (node.right == null) {//删除操作//1.记录左子树Node<T> leftTree = node.left;//失去关联关系node.left = null;//3.跟新sizethis.size--;return leftTree;}//递归操作node.right = removeMaxNode(node.right);return node;}

(3)删除任意元素

在删除任意元素中,需要考虑删除结点有没有左右子树

//语义:从以Node为根的二分搜索树中删除值为ele的结点private Node<T> remove(Node<T> node, T ele) {//递归终止的条件//没有找到if (node == null) {return null;}//找到了if (node.ele.compareTo(ele) == 0) {this.size--;//Node就是要删除的结点if (node.left == null) {Node<T>rightNode=node.right;node.right=null;return rightNode;} else if (node.right == null) {Node<T>leftNode=node.left;node.left=null;return leftNode;} else {Node<T> suffixNode = getMinNode(node.right).get();suffixNode.right=removeMinNode(node.right);suffixNode.left=node.left;this.size++;//失去关联关系node.left=node.right=null;return suffixNode;}}//递归操作if (node.ele.compareTo(ele) > 0) {node.left = remove(node.left, ele);} else {node.right = remove(node.right, ele);}return node;}

(4)删除根节点

直接删除关联关系即可

 //删除根节点public void removeRoot(){if(this.root==null){return;}remove(this.root.ele);}

四、完整代码

package com.algo.lesson.lesson04;import java.util.*;
import java.util.stream.Collectors;//二分搜索树
/*
保存到结点中的元素值必须具有可比性*/
public class BinearySeachTree<T extends Comparable<T>> {//树的结点private static class Node<T> {private final T ele;//结点的值private int frequence;//频率private Node<T> left, right;//分别指向左右孩子的索引public Node(T ele) {this.ele = ele;this.left = this.right = null;}}//树对应的属性private Node<T> root;//树的根节点private int size;//结点的个数//构建树public BinearySeachTree() {this.root = null;this.size = 0;}//获取树中结点的个数public int getSize() {return this.size;}//向树中添加结点public void add(T ele) {//更新根结点this.root = addDG(this.root, ele);}//语义:向以root为根的二分搜索树中添加元素eleprivate Node<T> addDG(Node<T> root, T ele) {//递归终止条件if (root == null) {this.size++;return new Node<T>(ele);}//递归操作if (root.ele.compareTo(ele) > 0) {root.left = addDG(root.left, ele);} else if (root.ele.compareTo(ele) < 0) {root.right = addDG(root.right, ele);} else {//更新频率root.frequence++;}return root;}//查询的方法public boolean search(T ele) {return searchDG(this.root, ele);}//语义:从以root为根的二分搜索树中查找元素eleprivate boolean searchDG(Node<T> root, T ele) {//递归终止的条件if (root == null) {return false;}//递归操作if (root.ele.compareTo(ele) == 0) {return true;} else if (root.ele.compareTo(ele) > 0) {return searchDG(root.left, ele);} else {return searchDG(root.right, ele);}}//二分搜索树的遍历//前序遍历public void preTravel() {List<AbstractMap.SimpleEntry<T, Integer>> list = new ArrayList<>();preTravelDG(this.root, list);String str = list.stream().map(item -> "[" + item.getKey() + ":" + item.getValue() + "]").collect(Collectors.joining("-"));System.out.println(str);}//中序遍历public void midTravel() {List<AbstractMap.SimpleEntry<T, Integer>> list = new ArrayList<>();midTravelDG(this.root, list);String str = list.stream().map(item -> item.toString()).collect(Collectors.joining("-"));System.out.println(str);}//后序遍历public void sufTravel() {List<AbstractMap.SimpleEntry<T, Integer>> list = new ArrayList<>();sufTravelDG(this.root, list);String str = list.stream().map(item -> item.toString()).collect(Collectors.joining("-"));System.out.println(str);}//前序遍历以root为根的树,讲解稿保存在list中private void preTravelDG(Node<T> root, List<AbstractMap.SimpleEntry<T, Integer>> list) {//递归终止条件if (root == null) {return;}//递归操作list.add(new AbstractMap.SimpleEntry<>(root.ele, root.frequence));//遍历左子树preTravelDG(root.left, list);//遍历右子树preTravelDG(root.right, list);}//中序遍历以root为根的树,讲解稿保存在list中private void midTravelDG(Node<T> root, List<AbstractMap.SimpleEntry<T, Integer>> list) {//递归终止条件if (root == null) {return;}//递归操作//遍历左子树preTravelDG(root.left, list);list.add(new AbstractMap.SimpleEntry<>(root.ele, root.frequence));//遍历右子树preTravelDG(root.right, list);}//后序遍历以root为根的树,讲解稿保存在list中private void sufTravelDG(Node<T> root, List<AbstractMap.SimpleEntry<T, Integer>> list) {//递归终止条件if (root == null) {return;}//递归操作//遍历左子树preTravelDG(root.left, list);//遍历右子树preTravelDG(root.right, list);list.add(new AbstractMap.SimpleEntry<>(root.ele, root.frequence));}//判断树是否为空public boolean isEmpty() {return this.size == 0;}//层序遍历public void levelTravel() {//判断树是否为空if (this.isEmpty()) {return;}Queue<AbstractMap.SimpleEntry<Node<T>, Integer>> queue = new LinkedList<>();//1.先将根结点入队queue.add(new AbstractMap.SimpleEntry<>(this.root, 1));//2.遍历队列while (!queue.isEmpty()) {//2-1.出队AbstractMap.SimpleEntry<Node<T>, Integer> pair = queue.poll();//结点Node<T> node = pair.getKey();//层int level = pair.getValue();;System.out.println("[val:" + node.ele + ",level:" + level + "]");//2-2.判断左右子树是否为空if (node.left != null) {queue.add(new AbstractMap.SimpleEntry<>(node.left, level + 1));}if (node.right != null) {queue.add(new AbstractMap.SimpleEntry<>(node.right, level + 1));}}}//找树中的最小元素public T getMinValue() {if (this.isEmpty()) {return null;}Optional<Node<T>> optional = getMinNode();return optional.get().ele;}//找树中的最大元素public T getMaxValue() {if (this.isEmpty()) {return null;}Optional<Node<T>> optional = getMaxNode(this.root);return optional.get().ele;}private Optional<Node<T>> getMinNode() {if (this.root == null) {return Optional.empty();}//一直向左查找Node<T> node = this.root;while (node.left != null) {node = node.left;}return Optional.of(node);}//递归//语义:在以Node为根结点的树中查找最小结点private Optional<Node<T>> getMinNode(Node<T> node) {if (node.left == null) {return Optional.of(node);}return getMinNode(node.left);}//语义:在以Node为根结点的树中查找最大结点private Optional<Node<T>> getMaxNode(Node<T> node) {if (node.right == null) {return Optional.of(node);}return getMaxNode(node.right);}//删除操作//从树中删除最小的结点public T removeMinNode() {T result = getMinValue();if (result == null) {return null;}//更新根结点this.root = removeMinNode(this.root);return result;}//语义:从以Node为根的二分搜索树中删除元素最小的结点private Node<T> removeMinNode(Node<T> node) {//递归终止条件if (node.left == null) {//删除操作//1.记录右子树Node<T> rightTree = node.right;//失去关联关系node.right = null;//3.跟新sizethis.size--;return rightTree;}//递归操作node.left = removeMinNode(node.left);return node;}//删除操作//从树中删除最大的结点public T removeMaxNode() {T result = getMaxValue();if (result == null) {return null;}//更新根结点this.root = removeMaxNode(this.root);return result;}//语义:从以Node为根的二分搜索树中删除元素最大的结点private Node<T> removeMaxNode(Node<T> node) {//递归终止条件if (node.right == null) {//删除操作//1.记录左子树Node<T> leftTree = node.left;//失去关联关系node.left = null;//3.跟新sizethis.size--;return leftTree;}//递归操作node.right = removeMaxNode(node.right);return node;}//删除任意结点public void remove(T ele) {//根据值查找结点this.root = remove(this.root, ele);}//语义:从以Node为根的二分搜索树中删除值为ele的结点private Node<T> remove(Node<T> node, T ele) {//递归终止的条件//没有找到if (node == null) {return null;}//找到了if (node.ele.compareTo(ele) == 0) {this.size--;//Node就是要删除的结点if (node.left == null) {Node<T>rightNode=node.right;node.right=null;return rightNode;} else if (node.right == null) {Node<T>leftNode=node.left;node.left=null;return leftNode;} else {Node<T> suffixNode = getMinNode(node.right).get();suffixNode.right=removeMinNode(node.right);suffixNode.left=node.left;this.size++;//失去关联关系node.left=node.right=null;return suffixNode;}}//递归操作if (node.ele.compareTo(ele) > 0) {node.left = remove(node.left, ele);} else {node.right = remove(node.right, ele);}return node;}//删除根节点public void removeRoot(){if(this.root==null){return;}remove(this.root.ele);}}

五、例题

1.700. 二叉搜索树中的搜索

​​​​

class Solution {public TreeNode searchBST(TreeNode root, int val) {if(root==null){return null;}if(val==root.val){return root;}return searchBST(val<root.val?root.left:root.right,val);}
}

2.力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

class Solution {public TreeNode insertIntoBST(TreeNode root, int val) {
//更新根结点return root = addDG(root, val);}//语义:向以root为根的二分搜索树中添加元素eleprivate TreeNode addDG(TreeNode root, int val) {//递归终止条件if (root == null) {return new TreeNode(val);}//递归操作if (root.val>val) {root.left = addDG(root.left, val);} else if (root.val<val) {root.right = addDG(root.right, val);}return root;}
}

3.力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

class Solution {public List<List<Integer>> levelOrder(TreeNode root) {List<List<Integer>> list = new ArrayList<List<Integer>>();if (root == null) {return list;}Queue<TreeNode> queue = new LinkedList<TreeNode>();queue.offer(root);while (!queue.isEmpty()) {List<Integer> level = new ArrayList<Integer>();int temp = queue.size();for (int i = 1; i <= temp; i++) {TreeNode node = queue.poll();level.add(node.val);if (node.left != null) {queue.offer(node.left);}if (node.right != null) {queue.offer(node.right);}}list.add(level);}return list;}
}

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

256:vue+openlayers利用高德逆地理编码,点击地图,弹出某点坐标和地址信息

第256个 点击查看专栏目录 本示例的目的是介绍演示如何在vue+openlayers中利用高德逆地理编码,点击地图,弹出某点坐标和地址信息。这里要仔细阅读高德地图的逆编码API,同时要注意的是,这种转换在中国很好用,到了欧美国家就不好使了。 直接复制下面的 vue+openlayers源代码…

【JaveWeb教程】(30)SpringBootWeb案例之《智能学习辅助系统》的详细实现步骤与代码示例(3)员工管理的实现

目录 SpringBootWeb案例033. 员工管理3.1 分页查询3.1.1 基础分页3.1.1.1 需求分析3.1.1.2 接口文档3.1.1.3 思路分析3.1.1.4 功能开发3.1.1.5 功能测试3.1.1.6 前后端联调 3.1.2 分页插件3.1.2.1 介绍3.1.2.2 代码实现3.1.2.3 测试 3.2 分页查询(带条件)3.2.1 需求3.2.2 思路分…

vue3---inputRef.value.focus()报错Cannot read properties of null (reading ‘focus‘)

问题描述&#xff1a;点击编辑按钮&#xff0c;出现el-input框&#xff08;el-input显示隐藏通过v-if控制&#xff09; <el-input ref"inputRef" v-if"isEdit" v-model"modelName" blur"isEdit false" /> <el-button text …

大模型学习与实践笔记(十三)

将训练好的模型权重上传到 OpenXLab 方式1&#xff1a; 先将Adapter 模型权重通过scp 传到本地&#xff0c;然后网页上传 步骤1. scp 到本地 命令为&#xff1a; scp -o StrictHostKeyCheckingno -r -P *** rootssh.intern-ai.org.cn:/root/data/ e/opencv/ 步骤2&#…

为什么时序逻辑电路会落后一拍?

1、时序逻辑电路落后一拍&#xff1f; FPGA初学者可能经常听到一句话&#xff1a;“时序逻辑电路&#xff0c;或者说用 < 输出的电路会延迟&#xff08;落后&#xff09;一个时钟周期。”但在仿真过程中经常会发现不符合这一“定律”的现象–明明是在仿真时序逻辑&#xff…

C#winform上位机开发学习笔记13-串口助手显示系统时间功能添加

1.功能描述 在上位机中显示系统的实时时间 2.代码部分 步骤1&#xff1a;添加文本框控件并设置参数 #此处注意将BackColor颜色修改为非Control&#xff0c;即可正常显示ForeColor颜色&#xff0c;否则该颜色不变&#xff0c;原因暂且不明。 步骤2&#xff1a;添加timer控件…

GC26E31S国产芯片可替代AM26LV31E/TI,适用于马达编码等产品上

众所周知AM26LV31E是一款具有三态输出的四路差分线路驱动器。该驱动器具有15kV ESD&#xff08;HBM和IEC61000-4-2&#xff0c;气隙放电&#xff09;和8kV ESD&#xff08;IEC61000-4-2&#xff0c;接触放电&#xff09;保护。该器件旨在满足TIA /EIA-422-B和ITU建议V.11驱动器…

Dify学习笔记-知识库(六)

1、知识库 大多数语言模型采用较为陈旧的训练数据&#xff0c;并且对每次请求的上下文有长度限制。例如 GPT-3.5 是基于 2021 年的语料进行训练的&#xff0c;且有每次约 4K Token 的限制。这意味着开发者如果想让 AI 应用基于最新的、私有的上下文对话&#xff0c;必须使用类…

SpringCloudConfig+SpringCloudBus+Actuator+Git实现Eureka关键配置属性热更新(全程不重启服务)

文章目录 前言1.痛点2.解决方案3.具体实现3.1搭建热配置服务3.2编写配置文件3.3搭建版本控制仓库3.4Eureka-Client引入以下依赖3.5Eureka-Client微服务编写以下配置bootstrap.yml提前加载3.6分别编写测试Controller3.7测试效果3.8下线场景压测 4.SpringCloudBus优化5.写到最后 …

springboot-mybatis项目

一、后端开发环境搭建 1、File->New->Projet 2选择 Spring Initializr &#xff0c;然后选择默认的 url 点击next 3勾选Spring Web、SQL模板&#xff0c;next 4点击finish&#xff0c;搭建完成 二 数据库 1 新建数据库 2 执行sql建表 SET NAMES utf8mb4; SET FOREIGN…

爬虫js逆向分析——x平台(实现)

爬虫js逆向分析——x平台&#xff08;实现&#xff09; &#xff08;仅供学习&#xff0c;本案例只是分析流程没有账号&#xff09;网址&#xff1a;https://xuexi.chinabett.com/ 1.分析请求包格式 打开控制台&#xff0c;并勾选保存日志&#xff0c;然后点击登录看发送了什…

外贸邮件群发软件有哪些?邮件群发的系统?

外贸邮件群发软件哪个比较好&#xff1f;外贸开发信软件推荐&#xff1f; 对于许多外贸企业来说&#xff0c;邮件营销是一种非常有效的推广方式。那么&#xff0c;外贸邮件群发软件就成为了必备的工具。蜂邮EDM将为你揭秘几款主流的外贸邮件群发软件&#xff0c;助你更好地开展…

linux安装docker(入门一)

环境&#xff1a;centos 7(linux) 网站 官网: https://docs.docker.com/ Docker Hub 网站: https://hub.docker.com/ 容器官方概述 一句话概括容器&#xff1a;容器就是将软件打包成标准化单元&#xff0c;以用于开发、交付和部署。 容器镜像是轻量的、可执行的独立软件包 &…

shell脚本——条件语句与循环语句

目录 一、条件语句 1、test命令测试条件表达式 2、整数数值比较 3、字符串比较 4、逻辑测试&#xff08;短路运算&#xff09; 5、双中括号 二、if语句 1、 分支结构 1.1 单分支结果 1.2 双分支 1.3 多分支 2、case 一、条件语句 条件测试&#xff1a;判断某需求是…

【原神游戏开发日志3】登录和注册有何区别?

版权声明&#xff1a; ● 本文为“优梦创客”原创文章&#xff0c;您可以自由转载&#xff0c;但必须加入完整的版权声明 ● 文章内容不得删减、修改、演绎 ● 本文视频版本&#xff1a;见文末 ● 相关学习资源&#xff1a;见文末 前言 ● 这是我们原神游戏开发日记的第三期 ●…

OpenHarmony—TypeScript到ArkTS约束说明

对象的属性名必须是合法的标识符 规则&#xff1a;arkts-identifiers-as-prop-names 级别&#xff1a;错误 在ArkTS中&#xff0c;对象的属性名不能为数字或字符串。通过属性名访问类的属性&#xff0c;通过数值索引访问数组元素。 TypeScript var x { name: x, 2: 3 };c…

计算机网络 第5章(运输层)

系列文章目录 计算机网络 第1章&#xff08;概述&#xff09; 计算机网络 第2章&#xff08;物理层&#xff09; 计算机网络 第3章&#xff08;数据链路层&#xff09; 计算机网络 第4章&#xff08;网络层&#xff09; 计算机网络 第5章&#xff08;运输层&#xff09; 计算机…

Windows10上使Git Bash支持rsync命令操作步骤

rsync命令是linux上常用的工具之一&#xff0c;用于远程以及本地系统中拷贝/同步文件和文件夹。 Windows Git Bash默认并不支持rsync&#xff0c;如下图所示&#xff1a; 使Git Bash支持rsync命令操作步骤&#xff1a; 1.从https://repo.msys2.org/msys/x86_64/ 下…

Docker镜像的创建

基于现有镜像创建 先用现有镜像创建启动容器 docker run 再进入容器进行内容更新 docker exec -it 最后提交成新的镜像 docker commit 然后将修改后的容器提交为新的镜像&#xff0c;需要使用该容器的 ID 号创建新镜像 进入容器可查看相关性能 基于本地模板创…

测试C#调用OpenCvSharp和ViewFaceCore从摄像头中识别人脸

学习了基于OpenCvSharp获取摄像头数据&#xff0c;同时学习了基于ViewFaceCore的人脸识别用法&#xff0c;将这两者结合即是从摄像头中识别人脸。本文测试测试C#调用OpenCvSharp和ViewFaceCore从摄像头中识别人脸&#xff0c;并进行人脸红框标记。   新建Winform项目&#xf…