基于树的存储数据结构demo

一.简介

由于之前博主尝试Java重构redis,在redis中的的字典数据结构底层也是采用数组实现,字典中存在两个hash表,一个是用于存储数据,另一个被用于rehash扩容为前者两倍。但是我注意到了在redis的数据结构中,并没有像Java集合类中的hashmap一样的树化机制,即当链表数大于8,且通过线性搜索法查找后续hash槽,发现占用了64个了,就会将拉链上的链表转化为红黑树,涉及到自平衡等等操作。是比较麻烦的,于是博主尝试使用二叉搜索树来实现一个基础的树的存储数据的数据结构。

二.准备代码

先定义两个接口,里面是我们准备实现的一些必须的方法,

IMember 接口

这个接口的实现类是该存储数据结构中的每个对象。

package partA;/**** an objects of a class implementing this interface holds a* database of member information* DO NOT CHANGE THIS INTERFACE* You must create a class that implements this interface**/public interface IMemberDB {/*** Empties the database.* @pre true*/public void clearDB();/*** Determines whether a member's name exists as a key inside the database* @pre name is not null and not empty string* @param name the member name (key) to locate* @return true if the name exists as a key in the database*/public boolean containsName(String name);/*** Returns a Member object mapped to the supplied name.* @pre name not null and not empty string* @param name The Member name (key) to locate* @return the Member object mapped to the key name if the nameexists as key in the database, otherwise null*/public Member get(String name);/*** Returns the number of members in the database* @pre true* @return number of members in the database.*/public int size();/*** Determines if the database is empty or not.* @pre true* @return true iff the database is empty*/public boolean isEmpty();/*** Inserts a Member object into the database, with the key of the supplied* member's name.* Note: If the name already exists as a key, then the original entry* is overwritten.* This method must return the previous associated value* if one exists, otherwise null** @pre member not null and member name not empty string*/public Member put(Member member);/*** Removes and returns a member from the database, with the key* the supplied name.* @param name The name (key) to remove.* @pre name not null and name not empty string* @return the removed member object mapped to the name, or null if* the name does not exist.*/public Member remove(String name);/*** Prints the names and affiliations of all the members in the database in* alphabetic order.* @pre true*/public void displayDB();
}

IMemberDB 接口

该接口就是定义一些 crud方法

package partA;/**** an objects of a class implementing this interface holds a* database of member information* DO NOT CHANGE THIS INTERFACE* You must create a class that implements this interface**/public interface IMemberDB {/*** Empties the database.* @pre true*/public void clearDB();/*** Determines whether a member's name exists as a key inside the database* @pre name is not null and not empty string* @param name the member name (key) to locate* @return true if the name exists as a key in the database*/public boolean containsName(String name);/*** Returns a Member object mapped to the supplied name.* @pre name not null and not empty string* @param name The Member name (key) to locate* @return the Member object mapped to the key name if the nameexists as key in the database, otherwise null*/public Member get(String name);/*** Returns the number of members in the database* @pre true* @return number of members in the database.*/public int size();/*** Determines if the database is empty or not.* @pre true* @return true iff the database is empty*/public boolean isEmpty();/*** Inserts a Member object into the database, with the key of the supplied* member's name.* Note: If the name already exists as a key, then the original entry* is overwritten.* This method must return the previous associated value* if one exists, otherwise null** @pre member not null and member name not empty string*/public Member put(Member member);/*** Removes and returns a member from the database, with the key* the supplied name.* @param name The name (key) to remove.* @pre name not null and name not empty string* @return the removed member object mapped to the name, or null if* the name does not exist.*/public Member remove(String name);/*** Prints the names and affiliations of all the members in the database in* alphabetic order.* @pre true*/public void displayDB();
}

三.实现类

package partA;import java.util.Objects;public class Member implements IMember{String fullName;String affiliation;public Member(String name, String affiliation){this.fullName = name;this.affiliation = affiliation;}public String getName() {return fullName;}public String getAffiliation() {return affiliation;}public void setAffiliation(String affiliation) {this.affiliation = affiliation;}@Overridepublic boolean equals(Object o) {if (this == o) return true;if (o == null || getClass() != o.getClass()) return false;Member member = (Member) o;return Objects.equals(fullName, member.fullName) && Objects.equals(affiliation, member.affiliation);}@Overridepublic int hashCode() {return Objects.hash(fullName, affiliation);}
}
package partA;import java.util.ArrayList;
import java.util.List;
public class MemberBST implements IMemberDB{private Node root;private static class Node {Member member;String name;Node left, right;Node(Member member) {this.member = member;this.name = member.getName();left = right = null;}}// constructorpublic MemberBST() {System.out.println("Binary Search Tree");this.root = null;}@Overridepublic void clearDB() {root = null;}@Overridepublic boolean containsName(String name) {return recursionTree(root, name);}// if two string is same,compare() method will return 0private boolean recursionTree(Node node, String name) {if (node == null) return false;if (node.name.compareTo(name) == 0) return true;return recursionTree(node.left, name) || recursionTree(node.right, name);}@Overridepublic Member get(String name) {List<String> sequence = new ArrayList<>();Member result = recursionGetter(root, name, sequence);if (result != null) {System.out.println("the sequence of nodes of the tree visited: " + sequence);}return result;}private Member recursionGetter(Node node, String name, List<String> sequence) {if (node == null){return null;}sequence.add(node.member.getName()); // Add the node to the sequenceif (node.name.compareTo(name) == 0) {System.out.println("the Member name: " + node.member.getName());return node.member;}// recursionMember leftResult = recursionGetter(node.left, name, sequence);if (leftResult != null) {return leftResult;}return recursionGetter(node.right, name, sequence);}@Overridepublic int size() {int cnt = 0;return calculator(root, cnt);}private int calculator(Node node, int cnt) {if (node == null) return cnt;cnt++;cnt = calculator(node.left, cnt);cnt = calculator(node.right, cnt);return cnt;}@Overridepublic boolean isEmpty() {return root == null;}@Overridepublic Member put(Member member) {if ((member == null) || member.getName().isEmpty()) {throw new IllegalArgumentException("member and " +"member's name can not be null");}// exist?Member meb = get(member.getName());if (meb != null) {meb.setAffiliation(member.affiliation);System.out.println("the Member name: " + meb.getName());return meb;}if (isEmpty()) {root = new Node(member);System.out.println("Visited node: " + root.member.getName());} else {recursionAdder(root, member);}return member;}private void recursionAdder(Node node, Member member) {int gap = member.getName().compareTo(node.name);if (gap < 0) {// The new member's name comes before the current node's name, go leftif (node.left == null) {node.left = new Node(member);System.out.println("Visited node: " + node.member.getName());} else {recursionAdder(node.left, member);}} else if (gap > 0) {// The new member's name comes after the current node's name, go rightif (node.right == null) {node.right = new Node(member);System.out.println("Visited node: " + node.member.getName());} else {recursionAdder(node.right, member);}}}@Overridepublic Member remove(String name) {if (name == null || name.isEmpty()) {throw new IllegalArgumentException("Name cannot be null or an empty string.");}MemberWrapper removedMemberWrapper = new MemberWrapper();root = removeRecursive(root, name, removedMemberWrapper);System.out.println("Removed member: " + removedMemberWrapper.member.getName());return removedMemberWrapper.member;}private Node removeRecursive(Node node, String name, MemberWrapper removedMemberWrapper) {if (node == null) {return null;}int cmp = name.compareTo(node.name);if (cmp < 0) {System.out.println("Visited node: " + node.member.getName());node.left = removeRecursive(node.left, name, removedMemberWrapper);} else if (cmp > 0) {System.out.println("Visited node: " + node.member.getName());node.right = removeRecursive(node.right, name, removedMemberWrapper);} else {// Found the node to removeSystem.out.println("Removing node: " + node.member.getName());removedMemberWrapper.member = node.member;if (node.left == null) {return node.right;} else if (node.right == null) {return node.left;}// Node has two children, find the inorder successornode.member = findMin(node.right).member;node.name = findMin(node.right).name;node.right = removeRecursive(node.right, node.name, null);}return node;}private Node findMin(Node node) {while (node.left != null) {System.out.println("Visited node: " + node.member.getName());node = node.left;}return node;}@Overridepublic void displayDB() {disRecDB(root);}private void disRecDB(Node node) {if (node == null) return;String name = node.name;String affiliation = node.member.affiliation;System.out.println("fullName:\t" + name + "\t" + affiliation);disRecDB(node.left);disRecDB(node.right);}
}

包装类:用于值传递

package partA;public class MemberWrapper {Member member;
}

四.解释及测试类

APIs

First we need to build the inner class of the node and define the root node.

private Node root;private static class Node {Member member;String name;Node left, right;Node(Member member) {this.member = member;this.name = member.getName();left = right = null;}
}

And then in the constructor, it says "Binary Search Tree"

// constructorpublic MemberBST() {System.out.println("Binary Search Tree");this.root = null;}

Next we will implement all the apis defined in the MemberBST class inheritance interface:

  1. clearDB()
public void clearDB() {root = null;}

Emptying a tree simply requires setting its root node to 0, and the jvm's gc automatically frees up memory.

  1. containsName()
public boolean containsName(String name) {return recursionTree(root, name);}// if two string is same,compare() method will return 0private boolean recursionTree(Node node, String name) {if (node == null) return false;if (node.name.compareTo(name) == 0) return true;return recursionTree(node.left, name) || recursionTree(node.right, name);}

This is done using a simple depth-first traversal in the middle order traversal.

  1. get()
@Overridepublic Member get(String name) {List<String> sequence = new ArrayList<>();Member result = recursionGetter(root, name, sequence);if (result != null) {System.out.println("the sequence of nodes of the tree visited: " + sequence);}return result;}private Member recursionGetter(Node node, String name, List<String> sequence) {if (node == null){return null;}sequence.add(node.member.getName()); // Add the node to the sequenceif (node.name.compareTo(name) == 0) {System.out.println("the Member name: " + node.member.getName());return node.member;}// recursionMember leftResult = recursionGetter(node.left, name, sequence);if (leftResult != null) {return leftResult;}return recursionGetter(node.right, name, sequence);}

Since I need to return the order of access in task3, I create a variable-length array to store the locations of the accessed nodes and add them to the dynamic array whenever a node is accessed.

We also use recursion to implement the depth-first algorithm's mid-order pass to get the member of the specified name. Comparing two strings we use the compareTo() method, which converts the string to a char array and then determines whether the two strings are equal by adding the asc code. If 0 is equal, 1 means that the former is greater, and 2 means that the latter is greater.

  1. size()
public int size() {int cnt = 0;return calculator(root, cnt);}private int calculator(Node node, int cnt) {if (node == null) return cnt;cnt++;cnt = calculator(node.left, cnt);cnt = calculator(node.right, cnt);return cnt;}

dfs is also implemented using recursion. By defining a cnt counter to record the number of nodes and objects.

isEmpty()
@Overridepublic boolean isEmpty() {return root == null;}
Very simple no introduction.put()
public Member put(Member member) {if ((member == null) || member.getName().isEmpty()) {throw new IllegalArgumentException("member and " +"member's name can not be null");}// exist?Member meb = get(member.getName());if (meb != null) {meb.setAffiliation(member.affiliation);System.out.println("the Member name: " + meb.getName());return meb;}if (isEmpty()) {root = new Node(member);System.out.println("Visited node: " + root.member.getName());} else {recursionAdder(root, member);}return member;}private void recursionAdder(Node node, Member member) {int gap = member.getName().compareTo(node.name);if (gap < 0) {// The new member's name comes before the current node's name, go leftif (node.left == null) {node.left = new Node(member);System.out.println("Visited node: " + node.member.getName());} else {recursionAdder(node.left, member);}} else if (gap > 0) {// The new member's name comes after the current node's name, go rightif (node.right == null) {node.right = new Node(member);System.out.println("Visited node: " + node.member.getName());} else {recursionAdder(node.right, member);}}}
First we need to determine if a member of that name exists in the tree. Now add setAffiliation() to the Member class. Because we need to modify the operation, we need to get the member object so we can use the GET method here. If it exists, call setAffiliation() and modify the Affiliation corresponding to name.If it is empty, create a new node to store the new member.If it does not exist, we call the auxiliary method recursionAdder() to determine whether a member is placed in the left or right subtree by compartTo's gap.desplyDB()
public void displayDB() {disRecDB(root);}private void disRecDB(Node node) {if (node == null) return;String name = node.name;String affiliation = node.member.affiliation;System.out.println("fullName:\t" + name + "\t" + affiliation);disRecDB(node.left);disRecDB(node.right);}
Recursive depth traversal, just print.remove()
public Member remove(String name) {if (name == null || name.isEmpty()) {throw new IllegalArgumentException("Name cannot be null or an empty string.");}MemberWrapper removedMemberWrapper = new MemberWrapper();root = removeRecursive(root, name, removedMemberWrapper);System.out.println("Removed member: " + removedMemberWrapper.member.getName());return removedMemberWrapper.member;}private Node removeRecursive(Node node, String name, MemberWrapper removedMemberWrapper) {if (node == null) {return null;}int cmp = name.compareTo(node.name);if (cmp < 0) {System.out.println("Visited node: " + node.member.getName());node.left = removeRecursive(node.left, name, removedMemberWrapper);} else if (cmp > 0) {System.out.println("Visited node: " + node.member.getName());node.right = removeRecursive(node.right, name, removedMemberWrapper);} else {// Found the node to removeSystem.out.println("Removing node: " + node.member.getName());removedMemberWrapper.member = node.member;if (node.left == null) {return node.right;} else if (node.right == null) {return node.left;}// Node has two children, find the inorder successornode.member = findMin(node.right).member;node.name = findMin(node.right).name;node.right = removeRecursive(node.right, node.name, null);}return node;}private Node findMin(Node node) {while (node.left != null) {System.out.println("Visited node: " + node.member.getName());node = node.left;}return node;}

To implement value passing, we first create a wrapper class to hold the deleted member:

public class MemberWrapper {Member member;}

The member with the specified name is then recursively found and deleted. If the current node is empty, it indicates that the subtree has been traversed and the target member is not found, and null is returned. If the node has no right subtree, simply return its left subtree as the new subtree (or null if there is no left subtree). Compare the name of the current node member with the name of the target, and decide whether the left subtree or the right subtree recursion based on the comparison results. If the node has no left subtree, simply return its right subtree as the new subtree (or null if there is no right subtree), for a node with two children, find the smallest node in the right subtree (via the findMin method), replace the current node with this node, and then recursively remove the smallest node.

Testcode

Download Junit for unit testing using Maven.

(In Java, we generally do not use assertion types directly in our code, we use more if conditions, and assertion types are required to be used in test classes as much as possible.)

/*Insertion structure:Alice/    \Adam    Bob/   \Barbara  Charlie/     \null    null*/private MemberBST memberBST;private Member member1;private Member member2;private Member member3;private Member member4;@Beforepublic void setUp() {memberBST = new MemberBST();member1 = new Member("Alice", "Company A");member2 = new Member("Bob", "Company B");member3 = new Member("Charlie", "Company C");member4 = new Member("Adam", "Company D");memberBST.put(member1);memberBST.put(member2);memberBST.put(member3);memberBST.put(member4);}

First, insert the test data. See comments for the structure.

@Overridepublic boolean equals(Object o) {if (this == o) return true;if (o == null || getClass() != o.getClass()) return false;Member member = (Member) o;return Objects.equals(fullName, member.fullName) && Objects.equals(affiliation, member.affiliation);}@Overridepublic int hashCode() {return Objects.hash(fullName, affiliation);}

Note that we need to override the equals and hashcode methods in the Member class because we need a time value comparison rather than a memory address comparison.

The unit test code is as follows:

 

 

 

 

 

 

 

 

 

 

Then we can allow the main use case to be given in actual code:

Display:

Apparently it's allowed.

Analyse

  1. put
    1. Average case: O(log n), because each insertion attempts to place a new node in the proper place in the tree so that the tree is balanced.
    2. Best case: O(1), when the tree is empty or a new node is always inserted as the last node (this can happen with ordered inserts, but is uncommon).
    3. Worst case: O(n), if the tree is extremely unbalanced (degenerates into a linked list), each insertion may require traversing the entire tree.
  2. get & containsName
    1. Average case: O(log n), similar to insertion because the search follows a path from root to leaf.
    2. Best case: O(1), if the target node happens to be the root node.
    3. Worst case: O(n), when the tree degenerates into a linked list.
  3. Remove
    1. Average case: O(log n), the node needs to be found, and then three cases (no child, one child, two children) are processed, most of the operations are concentrated in a small part of the tree.
    2. Best case: O(1), delete the leaf node.
    3. Worst case: O(n), also when the tree is reduced to a linked list, it takes linear time to find the node.
  4. size & displayDB

The recursive implementation results in a time complexity of O(n) because each node needs to be traversed

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

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

相关文章

【MySQL】库的操作和表的操作

库的操作和表的操作 一、库的操作1、创建数据库(create)2、字符集和校验规则&#xff08;1&#xff09;查看系统默认字符集以及校验规则&#xff08;2&#xff09;查看数据库支持的字符集&#xff08;3&#xff09;查看数据库支持的字符集校验规则&#xff08;4&#xff09;校验…

存储+调优:存储-IP-SAN

存储调优&#xff1a;存储-IP-SAN 数据一致性问题 硬盘&#xff08;本地&#xff0c;远程同步rsync&#xff09; 存储设备&#xff08;网络&#xff09; 网络存储 不同接口的磁盘 1.速率 2.支持连接更多设备 3.支持热拔插 存储设备什么互联 千…

ARTS Week 29

Algorithm 本周的算法题为 2413. 最小偶倍数 给你一个正整数 n &#xff0c;返回 2 和 n 的最小公倍数&#xff08;正整数&#xff09;。 示例 1&#xff1a;输入&#xff1a;n 5输出&#xff1a;10解释&#xff1a;5 和 2 的最小公倍数是 10 。 实现代码如下&#xff1a; con…

由于找不到mfc140u.dll,无法继续执行代码如何解决

在计算机使用过程中&#xff0c;我们经常会遇到一些错误提示&#xff0c;其中之一就是电脑找不到mfc140u.dll文件。这个问题可能会导致程序无法正常运行或系统崩溃。为了解决这个问题&#xff0c;本文将介绍5种修复方法&#xff0c;帮助大家快速恢复电脑的正常运行。 一&#x…

如何理解kmp的套娃式算法啊?

概念 KMP算法&#xff0c;全称Knuth Morris Pratt算法 。文章大部分内容出自《数据结构与算法之美》 核心思想 假设主串是a&#xff0c;模式串是b 在模式串与主串匹配的过程中&#xff0c;当遇到不可匹配的字符的时候&#xff0c;对已经对比过的字符&#xff0c;是否能找到…

【kubernetes】多 master 高可用集群架构部署

目录 前言 一、环境部署 二、master02 节点部署 1、拷贝相关文件 2、修改配置文件 3、启动各服务并设置开机自启 4、 查看node节点状态 三、负载均衡部署 1、部署 nginx 服务 1.1 编译安装 nginx 1.2 修改 nginx 配置文件 2、部署 keepalived 服务 2.1 yum安装 ke…

通过管理系统完成商品属性维护

文章目录 1.数据库表设计1.商品属性表 2.renren-generator生成CRUD1.基本配置检查1.generator.properties2.application.yml 2.启动RenrenGeneratorApplication.java生成CRUD1.启动后访问localhost:812.生成商品属性表的crud 3.将crud代码集成到项目中1.解压&#xff0c;找到ma…

python科研数据可视化之折线图

例如 &#xff1a; 下面的配色表画出的图很好看。选择喜欢的颜色&#xff0c;找到代码中颜色部分进行修改即可。 代码部分已经有详细的注释&#xff0c;就不一一解释了。另外&#xff0c;如果想要坐标轴从设定的值开始就把下面代码中的范围xlim&#xff0c;ylim进行注释。 imp…

设计模式12——外观模式

写文章的初心主要是用来帮助自己快速的回忆这个模式该怎么用&#xff0c;主要是下面的UML图可以起到大作用&#xff0c;在你学习过一遍以后可能会遗忘&#xff0c;忘记了不要紧&#xff0c;只要看一眼UML图就能想起来了。同时也请大家多多指教。 外观模式&#xff08;Facade&a…

javaSwing购物系统项目(文档+视频+源码)

摘要 由Java swing实现的一款简单的购物程序&#xff0c;数据库采用的是mysql&#xff0c;该项目非常简单&#xff0c;实现了管理员对商品类型和商品的管理及用户注册登录后浏览商品、加入购物车、购买商品等功能&#xff0c;旨在学习Java 图形界面开发 系统实现 我们先来管理…

CF451E: Devu and Flowers(容斥原理 + 考虑反面 + golang组合模版)

题目截图 题目翻译 题目分析 正难则反&#xff0c;考虑所有不符合的例子 由于n很小&#xff0c;所以可以状态压缩二进制遍历完全部不符合例子的组合 对于不符合的例子&#xff0c;假设其中第i个不符合&#xff0c;那么就消耗掉fi 1个球 以此类推&#xff0c;减剩下s2个球 这时…

一剪梅-答赠云安客刘自果

当众网友看了笔者“边吸氧边动鼠标”的短视频之后&#xff0c;纷纷发来微信问候。其中我的远房亲戚&#xff0c;那个正在潜心写作数十万字的长篇纪实文学《川江向东流》的66岁贤弟刘自果&#xff08;号云安客&#xff0c;亦称自果居士&#xff09;&#xff0c;发来微信鼓励我&a…

【Vue】性能优化

使用 key 对于通过循环生成的列表&#xff0c;应给每个列表项一个稳定且唯一的 key&#xff0c;这有利于在列表变动时&#xff0c;尽量少的删除和新增元素。 使用冻结的对象 冻结的对象&#xff08;Object.freeze(obj)&#xff09;不会被响应化&#xff0c;不可变。 使用函…

【Linux】网络层——IP协议

欢迎来到Cefler的博客&#x1f601; &#x1f54c;博客主页&#xff1a;折纸花满衣 &#x1f3e0;个人专栏&#xff1a;Linux 目录 &#x1f449;&#x1f3fb;IP协议基本概念&#x1f449;&#x1f3fb;IP的协议头格式&#x1f449;&#x1f3fb;IP协议的网段划分五类IP地址子…

mysql数据库主从复制,搭建从库

1 期望效果 假设我们现在有两个服务器&#xff0c;两个服务器都有数据库&#xff0c;然后我们命名一个叫主数据库&#xff08;Master&#xff09;&#xff0c;一个叫从数据库&#xff08;Slave&#xff09; 数据备份和容灾&#xff1a;通过主从复制&#xff0c;可以将主数据库…

618有什么宠物空气净化器推荐?希喂FreAir Lite宠物空气净化器真实体验

一、宠物空气净化器的必要性 掉毛季又来了&#xff0c;猫咪的毛发满天飞&#xff0c;怎么办&#xff1f;我家里的猫咪一到换毛季就掉满地的毛发&#xff0c;尤其喜欢在家里奔跑打闹&#xff0c;结果整个房间都是毛。为了减少家里空气中的浮毛&#xff0c;你都做过哪些努力呢&a…

关于「公 告」根据中华人民共和国法律,Bing 在中国内地暂停 “搜索自动建议” 功能 30 天

当我看见我们大家都这样我可放心了&#xff0c;我打开电脑搜索图片就发生了。 当我看见我们大家都这样我可放心了&#xff0c;坐等攻城狮修复。

关闭以及启动ubuntu图形界面

关闭以及启动ubuntu图形界面 文章目录 关闭以及启动ubuntu图形界面1. 关闭图形界面2. 打开图形界面 如果你误杀了Xorg进程&#xff0c;需要重新启动图形界面&#xff0c;可以按照以下步骤操作&#xff1a; 1. 关闭图形界面 查看当前启动的图形界面&#xff1a; 使用下面命令…

LeetCode刷题之HOT100之比特位计数

今天把仙剑三看完了&#xff0c;茂茂割肉让人无法释怀&#xff0c;眼泪止不住的流。长卿和紫萱的分离似乎也意味着重逢&#xff0c;这就是他们的宿命吧。怅然若失的感觉席卷全身&#xff0c;哎&#xff0c;做题吧。 1、题目描述 2、逻辑分析 题目要求将整数从0到此元素&#…

【GO基础】1. Go语言环境搭建

Go语言环境搭建 Go的三种安装方式Go标准包安装Windows 安装验证是否安装成功 4.Go的第一个程序 Hello World.go Go的三种安装方式 Go有多种安装方式&#xff0c;可以选择自己适合的。这里介绍三种最常见的安装方式&#xff1a; Go源码安装&#xff1a;这是一种标准的软件安装…