c# 数据结构 树篇 入门树与二叉树的一切

        事先声明,本文不适合对数据结构完全不懂的小白 请至少学会链表再阅读

        c# 数据结构 链表篇 有关单链表的一切_c# 链表-CSDN博客

数据结构理论先导:《数据结构(C 语言描述)》也许是全站最良心最通俗易懂最好看的数据结构课(最迟每周五更新~~)_哔哩哔哩_bilibili

代码随想录:

关于二叉树,你该了解这些!| 二叉树理论基础一网打尽,二叉树的种类、二叉树的存储方式、二叉树节点定义、二叉树的遍历顺序_哔哩哔哩_bilibili

        

目录

0.树基础概念

1.二叉树基础概念 BT B:Binary 

​编辑

2.普通二叉树存储/遍历/缺点

 存储方式

顺序存储:

 链式存储:

遍历方式

顺序存储遍历(递归)

链式存储遍历(递归)

插入方式及其缺点

        链式存储插入

        缺点

3.二叉搜索树 BST S:Search

        链式构建及其增删查改

        测试用例以及全部代码

4.平衡二叉树(AVL) BBT:   B:Balanced

        构建和插入

        删除

5.二叉树的线索化     

5.1设计线索化结构

5.2 线索化构建树 

5.3 线索化后中序遍历方法举例

   关于红黑树和b+树 下一篇数据结构 树篇 将会做出彻底详解 


0.树基础概念

        0和1是基础概念和一些性质 可以看个大概 知道有这么回事就行了 后面用到再回来想想是不是这么回事即可 

 叶子节点也算是子树 

树的性质

 解释:

结点的度 = 该节点下最大子树个数,也就是看有几个孩子

比如:A结点 有三个子树 即为三个孩子 ,B有两个孩子,K无孩子

解释: 

还是这棵树 i =1开始 

树的度 = 最大结点的度,也就是有最多孩子结点的度

还是上面这棵树 最多孩子的度为A||D 所以 m = 3

第一层就是3的0次方 =1

第二层就是3的1次方 =3

第三层就是3的二次方 =9 (最大值)

1.二叉树基础概念 BT B:Binary 

 解释:

对于这个性质的解释:


 

2.普通二叉树存储/遍历/缺点

 存储方式

顺序存储:

        顺序存储需要注意的是让索引 i 从0

        

        特点是:依据其索引的特点 通过线性结构存储起来

        我用的是List(i=0开始)

public class Tree<T> : MonoBehaviour
{private List<T> bitTree = new List<T>();public void AddTree(T[] values){for(int i = 0; i < values.Length; i++){bitTree.Add(values[i]);}}
}
     Tree<char> tree = new Tree<char>();char[] values = { 'A', 'B', 'C', 'D' };tree.AddTree(values);

 链式存储:

特点为 =左右孩子指针 + 数据存储位

//节点暂时定义为char
public class TreeNode {public char Date;public TreeNode LeftNode;public TreeNode RightNode;public TreeNode(char Date, TreeNode LeftNode=null, TreeNode RightNode=null) {this.Date = Date;this.LeftNode =LeftNode;this.RightNode = RightNode;}

遍历方式

有三种分为前中后序

 前序特点:

就是先当前节点(如果从根节点开始),再左节点再右节点

中序特点:

先左,再当前,最后右

后序特点:

先左,再右,最后当前

顺序存储遍历(递归)

以前序为例 先构造树

再做递归

    //顺序遍历二叉树//前序public void Preorder(int index){if (index >= bitTree.Count)return;Debug.Log(bitTree[index]);int LeftNode = 2*index+1;int  RightNode = 2*index+2;Preorder(LeftNode);Preorder(RightNode);}//中序public void MiddleOrder(int index){if (index >= bitTree.Count)return;int LeftNode = 2 * index + 1;int RightNode = 2 * index + 2;MiddleOrder(LeftNode);Debug.Log(bitTree[index]);MiddleOrder(RightNode);}//后序public void AfterOrder(int index) {if (index >= bitTree.Count)return;int LeftNode = 2 * index + 1;int RightNode = 2 * index + 2;AfterOrder(LeftNode);AfterOrder(RightNode);Debug.Log(bitTree[index]);}

链式存储遍历(递归)

还是以前序遍历为例:

TreeNode rootNode = new TreeNode('A',new TreeNode('B',new TreeNode('D'), new TreeNode('E')),new TreeNode('C', new TreeNode('F')));rootNode.FirstRoot(rootNode);
Console.WriteLine();rootNode.MiddleRoot(rootNode);
Console.WriteLine();rootNode.AfterRoot(rootNode);
//节点暂时定义为char
public class TreeNode {public char Date;public TreeNode LeftNode;public TreeNode RightNode;public TreeNode(char Date, TreeNode LeftNode=null, TreeNode RightNode=null) {this.Date = Date;this.LeftNode =LeftNode;this.RightNode = RightNode;}//遍历方法 前public void FirstRoot(TreeNode treeNode){if (treeNode == null) return;Console.Write(treeNode.Date);FirstRoot(treeNode.LeftNode);FirstRoot(treeNode.RightNode);}//遍历方法 中public void MiddleRoot(TreeNode treeNode){if (treeNode == null) return;Console.Write(treeNode.Date);MiddleRoot(treeNode.LeftNode);MiddleRoot(treeNode.RightNode);}  //遍历方法 后public void AfterRoot(TreeNode treeNode){if (treeNode == null) return;AfterRoot(treeNode.LeftNode);AfterRoot(treeNode.RightNode);Console.Write(treeNode.Date);}
}

插入方式及其缺点

        链式存储插入

        我想给原来的二叉树的 2 6之间插入一个4


TreeNode root = new TreeNode(1);
root.LeftNode = new TreeNode(2);
root.RightNode = new TreeNode(3);
root.LeftNode.LeftNode = new TreeNode(6);
root.LeftNode.RightNode = new TreeNode(7);//节点暂时定义为char
public class TreeNode
{public int Date;public TreeNode LeftNode;public TreeNode RightNode;public TreeNode(int Date, TreeNode LeftNode = null, TreeNode RightNode = null){this.Date = Date;this.LeftNode = LeftNode;this.RightNode = RightNode;} 

我需要做的

// 创建新节点
TreeNode newNode = new TreeNode(4);// 在节点 2 的左子节点位置插入新节点 4
BinaryTree.InsertNode(root.LeftNode, true, newNode);
 public class BinaryTree{/// <summary>/// 值插入/// </summary>/// <param name="parent"></param>/// <param name="isLeft">是否是左子节点</param>/// <param name="newNode"></param>public static void InsertNode(TreeNode parent, bool isLeft, TreeNode newNode){if (isLeft){newNode.LeftNode = parent.LeftNode;parent.LeftNode = newNode;}else{newNode.RightNode = parent.RightNode;parent.RightNode = newNode;}}}

        缺点

        发现了没有和他娘的链表似的: 

        无法利用 “值的顺序” 进行高效操作  

        比如搜索某个值(如查找 4)时,只能从根节点开始遍历整个树(如广度优先或深度优先搜索),时间复杂度为 O(n),和链表无异

         插入逻辑不通用:插入节点 4 到节点 2 的左子节点,依赖于手动指定父子关系,没有统一的规则,如果树结构复杂,插入位置的选择会非常随意,导致树的高度不平衡(如退化成链表)

        于是有了下面这个玩意 二叉搜索树

3.二叉搜索树 BST S:Search

        数据结构合集 - 二叉搜索树(二叉排序树)(二叉查找树)_哔哩哔哩_bilibili

        特点 左子树Data小于根Data   右子树Data 大于根Data

操作最好时间复杂度最坏时间复杂度
查找(Search)O(log n)O(n)
插入(Insert)O(log n)O(n)
删除(Delete)O(log n)O(n)
空间复杂度O(n){O (n)(存储所有节点,与树的形态无关)}

        存储方式还是有链式和顺序 我倾向于链式 因为比较简单和清晰 所以拿此举例

        链式构建及其增删查改

            构建

public class TreeNode { public int value; public TreeNode Left; public TreeNode Right;public TreeNode(int value, TreeNode left = null, TreeNode right = null) {this.value = value;this.Left = left;this.Right = right;}
}public class BinarySearchTree
{public TreeNode root;public BinarySearchTree(){root = null;}

        增加 

    //增加节点public void Insert(int value) { InsertTrueMethod(root, value);}/// <summary>/// 递归查找插入位置/// </summary>/// <param name="node">插入的节点</param>/// <param name="val">插入的值</param>private TreeNode InsertTrueMethod(TreeNode node, int value){if (node == null){return new TreeNode(value);}//如果插入值 小于 节点值,则在左子树中插入if (value < node.value){node.Left = InsertTrueMethod(node.Left, value);}//如果插入值 大于 节点值,则在右子树中插入else if (value > node.value){node.Right = InsertTrueMethod(node.Right, value);}//如果找到了相同的值,则不插入return node;}

        删除

    private TreeNode DeleteTrueMethod(TreeNode node, int value){//如果树为空,则直接返回空if (node == null){return null;}//如果删除值 小于 节点值,则在左子树找if (value < node.value){node.Left = DeleteTrueMethod(node.Left, value);}//如果删除值 大于 节点值,则在右子树else if (value > node.value){node.Right = DeleteTrueMethod(node.Right, value);}//如果找到了相同的值,则删除该节点else{//如果该节点没有子节点,则直接删除该节点if (node.Left == null && node.Right == null){node = null;}//如果该节点只有一个子节点,则直接用该节点的子节点替换该节点else if (node.Left == null){node = node.Right;}else if (node.Right == null){node = node.Left;}// 如果该节点有两个子节点,此时有两种常见的替换策略:// 可以选择该节点右子树中的最小节点,也可以选择左子树中的最大节点。// 这里我们采用选择右子树中最小节点的策略。选择右子树最小节点(或者左子树最大节点)的目的是// 为了保证替换后仍然满足二叉搜索树的性质:左子树所有节点值小于根节点值,右子树所有节点值大于根节点值。// 步骤为:先找到右子树的最小节点,用其值替换当前要删除节点的值,// 然后再递归地从右子树中删除这个最小节点。else{TreeNode minNode = GetMinNode(node.Right);node.value = minNode.value;node.Right = DeleteTrueMethod(node.Right, minNode.value);}}return node;}//获取节点方法private TreeNode GetMinNode(TreeNode node){while (node.Left != null){node = node.Left;}return node;}

        测试用例以及全部代码

        注释进行了简单的修改

using System;
BinarySearchTree bst = new BinarySearchTree();// 测试用例 1:删除叶子节点
int[] values1 = { 50, 30, 20, 40, 70, 60, 80 };
foreach (var val in values1) bst.Insert(val);
//Console.WriteLine("删除前(用例1):");
//bst.InOrderTraversal(bst.root); // 输出:20 30 40 50 60 70 80 
//bst.Delete(20);
//Console.WriteLine("删除后(用例1-叶子节点):");
//bst.InOrderTraversal(bst.root); // 预期:30 40 50 60 70 80 测试用例 2:删除单子节点
//BinarySearchTree bst2 = new BinarySearchTree();
//int[] values2 = { 50, 30, 40 };
//foreach (var val in values2) bst2.Insert(val);
//bst2.Delete(30);
//Console.WriteLine("删除后(用例2-单子节点):");
//bst2.InOrderTraversal(bst2.root); // 预期:40 50 测试用例 3:删除双子节点
BinarySearchTree bst3 = new BinarySearchTree();
int[] values3 = { 50, 30, 20, 40, 70, 60, 80 };
foreach (var val in values3) bst3.Insert(val);
bst3.Delete(50);
Console.WriteLine("删除后(用例3-双子节点):");
bst3.InOrderTraversal(bst3.root); // 预期:20 30 40 60 70 80 
public class TreeNode
{public int value;public TreeNode Left;public TreeNode Right;public TreeNode(int value, TreeNode left = null, TreeNode right = null){this.value = value;this.Left = left;this.Right = right;}
}public class BinarySearchTree
{public TreeNode root;public BinarySearchTree(){root = null;}// 增加节点public void Insert(int value){root = InsertTrueMethod(root, value); }private TreeNode InsertTrueMethod(TreeNode node, int value){if (node == null)return new TreeNode(value);if (value < node.value)node.Left = InsertTrueMethod(node.Left, value);else if (value > node.value)node.Right = InsertTrueMethod(node.Right, value);return node;}public void Delete(int value){root = DeleteTrueMethod(root, value);}private TreeNode DeleteTrueMethod(TreeNode node, int value){//先找到待删除节点if (node == null) return null;if (value < node.value)node.Left = DeleteTrueMethod(node.Left, value);else if (value > node.value)node.Right = DeleteTrueMethod(node.Right, value);else {//找到以后分为三种情况//1. 叶子节点if (node.Left == null && node.Right == null)node = null;//2. 单子节点else if (node.Left == null)node = node.Right;else if (node.Right == null)node = node.Left;else{//3. 双子节点 找到右子树最小节点(或者左子树最大节点 替换待删除节点 然后递归删除右子树最小节点, 或者左子树最大节点)TreeNode minNode = GetRightMinNode(node.Right);node.value = minNode.value;node.Right = DeleteTrueMethod(node.Right, minNode.value);}}return node;}private TreeNode GetRightMinNode(TreeNode node){while (node.Left != null) node = node.Left;return node;}public void InOrderTraversal(TreeNode node){if (node == null) return;InOrderTraversal(node.Left);Console.Write(node.value + " "); InOrderTraversal(node.Right);if (node == root) Console.WriteLine(); }
}

        但是还是有一个问题 如果数据本来就有序 那构建二叉搜索树会成为一条线

         

        所以为了解决这个问题 有了下面这个平衡二叉树

4.平衡二叉树(AVL) BBT:   B:Balanced

        建议直接看视频 平衡二叉树(AVL树)_哔哩哔哩_bilibili

        这个东西是比较抽象的 而且情况也比较多 下图来自 b站 @帕拉迪克

        平衡因子:Balanced Factor

        上代码

        构建和插入

using System;//测试结果
AVLTree tree = new AVLTree();
int[] values = { 5,3,4};foreach (int value in values) {tree.Insert(value);
}tree.InOrderTraversal(tree.root);
// 定义 AVL 树节点类
public class TreeNode
{public int Value;       public TreeNode Left;     public TreeNode Right;    public int Height;        public TreeNode(int value){Value = value;Height = 1; // 新节点初始高度为1}
}public class AVLTree
{public TreeNode root;//获取传入节点的高度 private int GetHight(TreeNode node) { return node == null? 0 : node.Height;}//计算平衡因子 让传入节点的左子树 - 右子树高度 private int ComputeBalanceFactor(TreeNode node) { if(node == null ) return 0;int BalanceFactor = GetHight(node.Left) - GetHight(node.Right);return BalanceFactor;}//右旋 右旋父节点,然后将冲突的右放在旋转后的父节点的左子树private TreeNode RightRotate(TreeNode father) { //找到左子树节点和新插入节点TreeNode leftChild = father.Left;TreeNode conflict = leftChild.Right;//旋转leftChild.Right =  father; //原父节点弄到左子树的右边father.Left = conflict; //冲突的弄到原父节点的左边//更新高度 先更新一下原来的父节点的高度 再更新一下现在的父节点的高度father.Height = Math.Max(GetHight(father.Left), GetHight(father.Right)) + 1;leftChild.Height = Math.Max(GetHight(leftChild.Left), GetHight(leftChild.Right)) + 1;//返回旋转后的父节点return leftChild;}//左旋 左旋父节点,然后将冲突的左放在旋转后的父节点的右子树private TreeNode LeftRotate(TreeNode father){//找到右子树节点和新插入节点TreeNode rightChild = father.Right;TreeNode conflict = rightChild.Left;//旋转rightChild.Left = father; //原父节点弄到右子树的左边father.Right = conflict; //冲突的弄到原父节点的右边//更新高度 先更新一下原来的父节点的高度 再更新一下现在的父节点的高度father.Height = Math.Max(GetHight(father.Left), GetHight(father.Right)) + 1;rightChild.Height = Math.Max(GetHight(rightChild.Left), GetHight(rightChild.Right)) + 1;//返回旋转后的父节点return rightChild;}public void Insert(int value){root = InsertTrueMethod(root, value);}private TreeNode InsertTrueMethod(TreeNode root, int value){//BST插入 空创建 左右递归插入 重复值不插入if (root == null)return new TreeNode(value);if (value < root.Value)root.Left = InsertTrueMethod(root.Left, value); //递归+更新+平衡else if (value > root.Value)root.Right = InsertTrueMethod(root.Right, value);//递归+更新+平衡else return root;//更新高度root.Height = Math.Max(GetHight(root.Left), GetHight(root.Right)) + 1;//计算平衡因子int balanceFactor = ComputeBalanceFactor(root);//根据平衡因子判断怎么转//LL型 右旋  value < root.Left.Value:验证新节点确实插入在左子树的左侧if (balanceFactor > 1 && value < root.Left.Value)return RightRotate(root);//RR型 左旋if (balanceFactor < -1 && value > root.Right.Value)return LeftRotate(root);//LR型 先左旋再右旋if (balanceFactor > 1 && value > root.Left.Value){root.Left = LeftRotate(root.Left);return RightRotate(root);}//RL型 先右旋再左旋if (balanceFactor < -1 && value < root.Right.Value){root.Right = RightRotate(root.Right);return LeftRotate(root);}//不用旋返回根节点return root;}//中序遍历public void InOrderTraversal(TreeNode root){if (root == null)return;InOrderTraversal(root.Left);Console.WriteLine(root.Value);InOrderTraversal(root.Right);}
}

        删除

    // 删除指定值的节点public void Delete(int value){root = DeleteNode(root, value);}private TreeNode DeleteNode(TreeNode root, int value){// 如果根节点为空,直接返回 nullif (root == null)return root;// 如果要删除的值小于当前节点的值,递归删除左子树中的节点if (value < root.Value)root.Left = DeleteNode(root.Left, value);// 如果要删除的值大于当前节点的值,递归删除右子树中的节点else if (value > root.Value)root.Right = DeleteNode(root.Right, value);// 找到要删除的节点else{// 情况 1: 节点没有子节点或只有一个子节点if (root.Left == null || root.Right == null){TreeNode temp = root.Left ?? root.Right;// 如果没有子节点,直接删除该节点if (temp == null){root = null;}else{// 用子节点替换当前节点root = temp;}}// 情况 2: 节点有两个子节点else{// 找到右子树中的最小节点TreeNode temp = MinValueNode(root.Right);// 用最小节点的值替换当前节点的值root.Value = temp.Value;// 递归删除右子树中的最小节点root.Right = DeleteNode(root.Right, temp.Value);}}// 如果删除后树为空,直接返回 nullif (root == null)return root;// 更新节点高度root.Height = Math.Max(GetHight(root.Left), GetHight(root.Right)) + 1;// 计算平衡因子int balanceFactor = ComputeBalanceFactor(root);// LL 型失衡,进行右旋操作if (balanceFactor > 1 && ComputeBalanceFactor(root.Left) >= 0)return RightRotate(root);// LR 型失衡,先对左子树进行左旋,再对根节点进行右旋if (balanceFactor > 1 && ComputeBalanceFactor(root.Left) < 0){root.Left = LeftRotate(root.Left);return RightRotate(root);}// RR 型失衡,进行左旋操作if (balanceFactor < -1 && ComputeBalanceFactor(root.Right) <= 0)return LeftRotate(root);// RL 型失衡,先对右子树进行右旋,再对根节点进行左旋if (balanceFactor < -1 && ComputeBalanceFactor(root.Right) > 0){root.Right = RightRotate(root.Right);return LeftRotate(root);}return root;}// 找到以给定节点为根的子树中的最小节点private TreeNode MinValueNode(TreeNode node){TreeNode current = node;// 不断向左遍历,直到找到最左边的节点while (current.Left != null)current = current.Left;return current;}

        看起来有点难度实际上熟悉以后你会发现其编写都是套路 多读几遍就好了

        递归递归递归,你会发现二叉树一直在做这个动作 那么有没有一种方式脱离它呢?

        有的兄弟 有的 :线索化二叉树

5.二叉树的线索化     

        线索化二叉树的设计思想是:

        将空的指针(Left 或 Right)利用起来,不再指向子树(因为子树为空),而是指向该节点在中序遍历中的前驱或后继节点

        这样做可以在后续遍历中,不依赖递归或栈,直接通过线索找到前后节点

        画图,逻辑思想

        下为代码解释

5.1设计线索化结构

        其中LeftIsThread 和 RightIsThread 是指当前Left和Right是否为空

public class ThreadedNode<T>
{public T Data { get; set; }public ThreadedNode<T> Left { get; set; }public ThreadedNode<T> Right { get; set; }// 标志位:false表示指向子节点,true表示线索public bool LeftIsThread { get; set; }public bool RightIsThread { get; set; }public ThreadedNode(T data){this.Data = data;this.Left = null;this.Right = null;this.LeftIsThread = false;this.RightIsThread = false;}
}

5.2 线索化构建树 

public class ThreadedBinaryTree<T>
{private ThreadedNode<T> _root;private ThreadedNode<T> _pre; // 记录前驱节点// 线索化核心逻辑(中序)private void ThreadNodes(ThreadedNode<T> node){if (node == null) return;// 递归线索化左子树ThreadNodes(node.Left);// 处理当前节点的前驱if (node.Left == null){node.Left = _pre;node.LeftIsThread = true; // 标记为线索}// 处理前驱节点的后继if (_pre != null && _pre.Right == null){_pre.Right = node;_pre.RightIsThread = true;}_pre = node; // 更新前驱// 递归线索化右子树ThreadNodes(node.Right);}public void BuildThreadedTree(ThreadedNode<T> root){_root = root;_pre = null;ThreadNodes(root);}}

5.3 线索化后中序遍历方法举例

// 中序遍历(利用线索)public void InOrderTraversal(){ThreadedNode<T> current = _root;while (current != null){// 找到最左节点while (!current.LeftIsThread){current = current.Left;}Console.Write(current.Data + " ");// 根据后继线索遍历while (current.RightIsThread){current = current.Right;Console.Write(current.Data + " ");}current = current.Right;}

   关于红黑树和b+树 下一篇数据结构 树篇 将会做出彻底详解 

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

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

相关文章

《Cookie Cutter》中2000多张精灵表与10000个2D光源的管理之道

一个小团队如何在多个平台上以优秀的效果展示手绘动画&#xff1f;Subcult Joint 工作室给出了答案。他们用六年时间开发出了游戏《Cookie Cutter》。游戏中使用了数千个使用传统动画技术制作的高分辨率资产&#xff0c;而且这些资产都在 Unity 中进行了优化。由于工作室需要在…

什么是实景VR?实景VR应用场景

实景VR&#xff0c;即基于真实场景的虚拟现实技术&#xff0c;是利用计算机技术生成三维环境&#xff0c;以模拟并再现真实世界场景的技术。 用户通过佩戴VR设备&#xff08;如VR头盔、手柄等&#xff09;或通过电脑设备&#xff0c;可以沉浸在一个高度仿真的虚拟环境中&#…

内核性能测试(60s不丢包性能)

以xGAP-200-SE7K-L&#xff08;双口10G&#xff09;在飞腾D2000上为例&#xff08;单通道最高性能约2.8Gbps) 单口测试 0口&#xff1a; tcp&#xff1a; taskset -c 4 iperf -c 1.1.1.1 -i 1 -t 60 -p 60001 taskset -c 4 iperf -s -i 1 -p 60001 udp&#xff1a; taskse…

58. 区间和

题目链接&#xff1a; 58. 区间和 题目描述&#xff1a; 给定一个整数数组 Array&#xff0c;请计算该数组在每个指定区间内元素的总和。 输入描述 第一行输入为整数数组 Array 的长度 n&#xff0c;接下来 n 行&#xff0c;每行一个整数&#xff0c;表示数组的元素。随后…

C#进阶(2)stack(栈)

前言 我们前面介绍了ArrayList,今天就介绍另一种数据结构——栈。 这是栈的基本形式,博主简单画了一下,你看个意思就行,很明显,这种数据有一种特征:先进后出。因为先进来的数据会在下面,下面是密闭的,所以只能取后面进来的。 C#为我们封好了这种数据结构,我们不用担…

汽车工厂数字孪生实时监控技术从数据采集到三维驱动实现

在工业智能制造推动下&#xff0c;数字孪生技术正成为制造业数字化转型的核心驱动力。今天详细介绍数字孪生实时监控技术在汽车工厂中的应用&#xff0c;重点解析从数据采集到三维驱动实现的全流程技术架构&#xff0c;并展示其在提升生产效率、降低成本和优化决策方面的显著价…

git|gitee仓库同步到github

参考&#xff1a;一次提交更新两个仓库&#xff0c;Get 更优雅的 GitHub/Gitee 仓库镜像同步 文章目录 进入需要使用镜像功能的仓库&#xff0c;进入「管理」找到「仓库镜像管理」选项&#xff0c;点击「添加镜像」按钮绑定github绑定成功后再次点击添加镜像如何申请 GitHub 私…

原生小程序+springboot+vue+协同过滤算法的音乐推荐系统(源码+论文+讲解+安装+部署+调试)

感兴趣的可以先收藏起来&#xff0c;还有大家在毕设选题&#xff0c;项目以及论文编写等相关问题都可以给我留言咨询&#xff0c;我会一一回复&#xff0c;希望帮助更多的人。 系统背景 在数字音乐产业迅猛发展的当下&#xff0c;Spotify、QQ 音乐、网易云音乐等音乐平台的曲…

RustDesk

配置中继服务器 https://rustdesk.com/docs/zh-cn/self-host/windows/ 服务器端 下载Windows版本 rustdesk-server-windows-x86_64.zip&#xff0c;安装路径为&#xff1a;C:\Program Files\RustDeskServer\bin。执行 hbbr.exe 和 hbbs.exe 两个应用程序。这两个应用提供了两…

django中用 InforSuite RDS 替代memcache

在 Django 项目中&#xff0c;InforSuite RDS&#xff08;关系型数据库服务&#xff09;无法直接替代 Memcached&#xff0c;因为两者的设计目标和功能定位完全不同&#xff1a; 特性MemcachedInforSuite RDS核心用途高性能内存缓存&#xff0c;临时存储键值对数据持久化关系型…

leetcode 57. Insert Interval

题目描述 代码&#xff1a;由于intervals已经按照左端点排序&#xff0c;并且intervals中的区间全部不重叠&#xff0c;那么可以断定intervals中所有区间的右端点也已经是有序的。先二分查找intervals中第一个其右端点>newInterval左端点的区间。然后按照类似于56. Merge In…

去年开发一款鸿蒙Next Os的window工具箱

持拖载多个鸿蒙应用 批量签名安装 运行 http://dl.lozn.top/lozn/HarmonySignAndFileManagerTool_2024-11-26.zip 同类型安卓工具箱以及其他软件下载地址汇总 http://dl.lozn.top/lozn/ 怎么个玩法呢&#xff0c;比如要启动某app, 拖载识别到包名 点启动他能主动读取包名 然后…

Trivy:让你时刻掌控的开源安全扫描器

深入了解 Trivy:全面的安全扫描工具 在如今互联网快速发展的时代,软件的安全性显得尤为重要。随着应用程序的复杂性增加,其可能带来的安全漏洞也在不断增多。如何快速、准确地发现这些潜在威胁是每个开发者和运维人员心中的课题。今天,我们将为大家介绍一个开源的安全扫描…

网址为 http://xxx:xxxx/的网页可能暂时无法连接,或者它已永久性地移动到了新网址

这是由于浏览器默认的非安全端口所导致的&#xff0c;所谓非安全端口&#xff0c;就是浏览器出于安全问题&#xff0c;会禁止一些网络浏览向外的端口。 避免使用6000,6666这样的端口 6000-7000有很多都不行&#xff0c;所以尽量避免使用这个区间 还有在云服务器中&#xff0c…

Jenkins 执行器(Executor)如何调整限制?

目录 现象原因解决 现象 Jenkins 构建时&#xff0c;提示如下&#xff1a; 此刻的心情正如上图中的小老头&#xff0c;火冒三丈&#xff0c;但是不要急&#xff0c;因为每一次错误&#xff0c;都是系统中某个环节在说‘我撑不住了’。 原因 其实是上图的提示表示 Jenkins 当…

运维实施31-NFS服务

NFS概述 NFS&#xff08;Network File System&#xff09;网络文件系统&#xff0c;主要用于Linux系统上实现文件共享的一种协议&#xff0c;其客户端主要是Linux。 没有用户认证机制&#xff0c;且数据在网络上传送的时候是明文传送&#xff0c;一般只能在局域网中使用支持多…

蓝牙协议架构与调试工具详解(含 BLE、HCI 命令、调试命令)

本文介绍蓝牙协议从物理层到应用层的完整通信流程&#xff0c;并详解了 Linux 下主流蓝牙调试工具的使用方法&#xff0c;适用于嵌入式蓝牙驱动开发、BLE调试、通信协议分析等场景。 &#x1f527; 1. 蓝牙架构概览 ✅ 芯片架构 单模芯片&#xff1a;仅支持 BLE 或 Classic 蓝…

激光雷达定位算法在FPGA中的实现——section3 Matlab实现和校验

1、校验section2的计算方法是否正确 以section1里面的图示 举个例子: 1.1 手动计算 可以计算出4*4方阵C相关参数: 可以计算出余子式矩阵C_1相关参数:

数据结构(2)线性表-顺序表

知道一个算法的好坏怎么去判断以后&#xff0c;就该正式的去学习一些常见的数据结构&#xff0c;当然&#xff0c;这里的数据结构仅仅是初阶&#xff0c;不会挨个一个一个学完&#xff0c;后期慢慢来。 一、数据结构总论 一般按照逻辑结构和存储结构来分类&#xff0c;在初阶…

性能测试详解

&#x1f345; 点击文末小卡片&#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 一、什么是性能测试 先看下百度百科对它的定义 性能测试是通过自动化的测试工具模拟多种正常、峰值以及异常负载条件来对系统的各项性能指标进行测试 我们可以认为…