数据结构~二叉搜索树

文章目录

    • 一、二叉树搜索的概念
    • 二、二叉树搜索的结构
      • 二叉树搜索的性能分析
      • 二叉树搜索的插入
      • 二叉树搜索的查找
      • 二叉树搜索的删除
    • 三、二叉搜索树key和key/value使用场景
    • 四、二叉树搜索的练习
      • 将二叉搜索树就地转化为已排序的双向循环链表
      • 从前序与中序遍历序列构造二叉树
      • 二叉树的前序遍历(非递归)
    • 五、完整代码
      • 二叉树搜索的实现代码
      • key/value二叉树搜索代码实现
    • 六、总结

一、二叉树搜索的概念

二叉树搜索又称二叉排序树(Binary Search Tree,BST),它或者是一棵空树,或者是具有以下性质的⼆叉树:

  • 若它的左子树不为空,则左子树上所有结点的值都小于等于根结点的值
  • 若它的右子树不为空,则右子树上所有结点的值都大于等于根结点的值
  • 它的左右子树也分别为二叉搜索树
  • 二叉搜索树中可以支持插入相等的值,也可以不支持插入相等的值,具体看使用场景定义,后续学习map/set/multimap/multiset系列容器底层就是二叉搜索树,其中map/set不支持插入相等值,multimap/multiset支持插入相等值
    在这里插入图片描述

二、二叉树搜索的结构

二叉树搜索的性能分析

最优情况下,二叉搜索树为完全二叉树(或者接近完全⼆叉树),其高度为:O(log2N)
最差情况下,二叉搜索树退化为单支树(或者类似单支),其高度为:O(N/2)
所以综合而言⼆叉搜索树增删查改时间复杂度为:O(N)
那么这样的效率显然是无法满足需求的,后续讲解⼆叉搜索树的变形,平衡二叉搜索树AVL树和红黑树,才能适用于我们在内存中存储和搜索数据。另外需要说明的是,二分查找也可以实现O(logN) 级别的查找效率,但是二分查找有两大缺陷:

  1. 需要存储在支持下标随机访问的结构中,并且有序。
  2. 插入和删除数据效率很低,因为存储在下标随机访问的结构中,插⼊和删除数据⼀般需要挪动数据。这里也就体现出了平衡⼆叉搜索树的价值。
    在这里插入图片描述

二叉树搜索的插入

插入的具体过程如下:

  1. 树为空,则直接新增结点,赋值给root指针
  2. 树不空,按二叉搜索树性质,插入值比当前结点大往右走,插入值比当前结点小往左走,找到空位置,插入新结点。
  3. 如果支持插入相等的值,插入值跟当前结点相等的值可以往右走,也可以往左走,找到空位置,插入新结点。(要注意的是要保持逻辑⼀致性,插入相等的值不要⼀会往右走,⼀会往左走)
int a[] = {8, 3, 1, 10, 6, 4, 7, 14, 13};

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

二叉树搜索的查找

  1. 从根开始比较,查找x,x比根的值大则往右边走查找,x比根值小则往左边走查找。
  2. 最多查找高度次,走到到空,还没找到,这个值不存在。
  3. 如果不支持插入相等的值,找到x即可返回
  4. 如果支持插入相等的值,意味着有多个x存在,⼀般要求查找中序的第⼀个x。如下图,查找3,要找到1的右孩子的那个3返回
  5. 在这里插入图片描述

二叉树搜索的删除

首先查找元素是否在二叉搜索树中,如果不存在,则返回false。
如果查找元素存在则分以下四种情况分别处理:(假设要删除的结点为N)

  1. 要删除结点N左右孩子均为空
  2. 要删除的结点N左孩子位空,右孩子结点不为空
  3. 要删除的结点N右孩子位空,左孩子结点不为空
  4. 要删除的结点N左右孩子结点均不为空

对应以上四种情况的解决方案:

  1. 把N结点的父亲对应孩子指针指向空,直接删除N结点(情况1可以当成2或者3处理,效果是⼀样的)
  2. 把N结点的父亲对应孩子指针指向N的右孩子,直接删除N结点
  3. 把N结点的父亲对应孩子指针指向N的左孩子,直接删除N结
  4. 无法直接删除N结点,因为N的两个孩子无处安放,只能用替换法删除。找N左子树的值最大结点R(最右结点)或者N右子树的值最小结点R(最左结点)替代N因为这两个结点中任意⼀个,放到N的位置,都满足⼆叉搜索树的规则。替代N的意思就是N和R的两个结点的值交换,转而变成删除R结点,R结点符合情况2或情况3,可以直接删除。
    在这里插入图片描述
    在这里插入图片描述

在这里插入图片描述

三、二叉搜索树key和key/value使用场景

key搜索场景:
只有key作为关键码,结构中只需要存储key即可,关键码即为需要搜索到的值,搜索场景只需要判断key在不在。key的搜索场景实现的二叉树搜索树支持增删查,但是不支持修改,修改key破坏搜索树结构了。
场景1:小区无人值守车库,小区车库买了车位的业主车才能进小区,那么物业会把买了车位的业主的车牌号录入后台系统,车辆进入时扫描车牌在不在系统中,在则抬杆,不在则提示非本小区车辆,无法进入。
场景2:检查⼀篇英文章章单词拼写是否正确,将词库中所有单词放入⼆叉搜索树,读取文章中的单词,查找是否在⼆叉搜索树中,不在则波浪线标红提示。
在这里插入图片描述
key/value搜索场景:
每⼀个关键码key,都有与之对应的值value,value可以任意类型对象。树的结构中(结点)除了需要存储key还要存储对应的value,增/删/查还是以key为关键字走二叉搜索树的规则进行比较,可以快速查找到key对应的value。key/value的搜索场景实现的二叉树搜索树支持修改,但是不支持修改key,修改key破坏搜索树结构了,可以修改value。
场景1:简单中英互译字典,树的结构中(结点)存储key(英文)和vlaue(中文),搜索时输入英文,则同时查找到了英文对应的中文。
场景2:商场无人值守车库,入口进场时扫车牌,记录车牌和入场时间,出口离场时,扫描车牌,查找入场时间,用当前时间-入场时间计算出停车时长,计算出停车费用,缴费后抬杆,车辆离场。
场景3:统计⼀篇文章中单词出现的次数,读取⼀个单词,查找单词是否存在,不存在这个说明第⼀次出现,(单词,1),单词存在,则++单词对应的次数。
在这里插入图片描述

四、二叉树搜索的练习

将二叉搜索树就地转化为已排序的双向循环链表

将一个 二叉搜索树 就地转化为一个 已排序的双向循环链表 。
对于双向循环列表,你可以将左右孩子指针作为双向循环链表的前驱和后继指针,第一个节点的前驱是最后一个节点,最后一个节点的后继是第一个节点。
特别地,我们希望可以 就地 完成转换操作。当转化完成以后,树中节点的左指针需要指向前驱,树中节点的右指针需要指向后继。还需要返回链表中最小元素的指针。
在这里插入图片描述

思想:

  • 搜索二叉树走中序是有序的,本题目要求原地修改,也就是不能创建新的结点。
  • 思路1:中序遍历搜索⼆叉树,遍历顺序是有序的,将⼆叉树的结点指针放到⼀个vector中,再把前后结点的链接关系进⾏修改。这个思路最简单,但是需要消耗O(N)的空间复杂度。
  • 思路2:依旧中序遍历搜索二叉树,遍历顺序是有序的,遍历过程中修改左指针为前驱和右指针为后继指针。记录⼀个cur和prev,cur为当前中序遍历到的结点,prev为上⼀个中序遍历的结点,cur->left指向prev,cur->right⽆法指向中序下⼀个,因为不知道中序下⼀个是谁,但是prev->right指向cur;也就是说每个结点的左是在中遍历到当前结点时修改指向前驱的,但是当前结点的右,是在遍历到下⼀个结点时,修改指向后继的。

代码实现

class Solution {
public:void InOrderConvert(Node* cur, Node*& prev){if (cur == nullptr)return;// 中序遍历InOrderConvert(cur->left, prev);// 当前结点的左,指向前⼀个结点cur->left = prev;// 前⼀个结点的右,指向当前结点if (prev)prev->right = cur;prev = cur;InOrderConvert(cur->right, prev);}Node* treeToDoublyList(Node* root) {if (root == nullptr)return nullptr;Node* prev = nullptr;InOrderConvert(root, prev);// 从根开始往左⾛,找到第⼀个结点Node* head = root;while (head->left){head = head->left;}// head为第⼀个结点,prev是最后⼀个结点// 题⽬要求为循环链表,进⾏⼀些链接head->left = prev;prev->right = head;return head;}
};

从前序与中序遍历序列构造二叉树

给定两个整数数组 preorder 和 inorder ,其中 preorder 是二叉树的先序遍历, inorder 是同一棵树的中序遍历,请构造二叉树并返回其根节点。
在这里插入图片描述
思路:
先序遍历的顺序是根节点、左子树、右子树。这意味着先序遍历的第一个元素就是根节点的值。
中序遍历的顺序是左子树、根节点、右子树。通过在中序遍历中找到根节点的位置,可以确定左子树和右子树的元素范围。
可以根据先序遍历和中序遍历构建出二叉树。这个过程利用了先序遍历确定根节点和中序遍历划分左右子树的特点,通过递归的方式逐步构建出整个二叉

class Solution {
public:TreeNode* _buildTree(vector<int>& preorder, vector<int>& inorder, int&prei, int inbegin, int inend) {if (inbegin > inend)return nullptr;// 前序确定根TreeNode* root = new TreeNode(preorder[prei++]);// 根分割中序左右⼦区间int rooti = inbegin;while (rooti <= inend){if (inorder[rooti] == root->val)break;elserooti++;}// 递归左右⼦区间,递归构建左右⼦树// [inbegin, rooti-1] rooti [rooti+1, inend]root->left = _buildTree(preorder, inorder, prei, inbegin, rooti - 1);root->right = _buildTree(preorder, inorder, prei, rooti + 1, inend);return root;}TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {int i = 0;TreeNode* root = _buildTree(preorder, inorder, i, 0, inorder.size() - 1);return root;}
};

二叉树的前序遍历(非递归)

给你二叉树的根节点 root ,返回它节点值的 前序 遍历。
在这里插入图片描述
在这里插入图片描述
非递归迭代实现思想:
要迭代非递归实现⼆叉树前序遍历,首先还是要借助递归的类似的思想,只是需要把结点存在栈中,方便类似递归回退时取父路径结点。跟这里不同的是,这里把⼀棵⼆叉树分为两个部分:

  1. 先访问左路结点
  2. 再访问左路结点的右子树
    这里访问右子树要以循环从栈依次取出这些结点,循环子问题的思想访问左路结点的右子树。中序和后序跟前序的思路完全⼀致,只是前序先访问根还是后访问根的问题。后序稍微麻烦⼀些,因为后序遍历的顺序是左子树右子树根,当取到左路结点的右子树时,需要想办法标记判断右子树是否访问过了,如果访问过了,就直接访问根,如果访问过需要先访问右子树
class Solution {
public:vector<int> preorderTraversal(TreeNode* root) {stack<TreeNode*> s;vector<int> v;TreeNode* cur = root;while (cur || !s.empty()){// 访问⼀颗树的开始// 1、访问左路结点,左路结点⼊栈while (cur){v.push_back(cur->val);s.push(cur);cur = cur->left;}// 2、从栈中依次访问左路结点的右⼦树TreeNode* top = s.top();s.pop();// 循环⼦问题⽅式访问左路结点的右⼦树 --cur = top->right;}return v;}
};
class Solution {
public:vector<int> inorderTraversal(TreeNode* root) {stack<TreeNode*> st;TreeNode* cur = root;vector<int> v;while (cur || !st.empty()){// 访问⼀颗树的开始// 1、左路结点⼊栈while (cur){st.push(cur);cur = cur->left;}// 访问问左路结点 和 左路结点的右⼦树TreeNode* top = st.top();st.pop();v.push_back(top->val);// 循环⼦问题⽅式访问右⼦树cur = top->right;}return v;}
};
class Solution {
public:vector<int> postorderTraversal(TreeNode* root) {TreeNode* cur = root;stack<TreeNode*> s;vector<int> v;TreeNode* prev = nullptr;while (cur || !s.empty()){// 1、访问⼀颗树的开始while (cur){s.push(cur);cur = cur->left;}TreeNode* top = s.top();// top结点的右为空 或者 上⼀个访问结点等于他的右孩⼦// 那么说明(空)不⽤访问 或者 (不为空)右⼦树已经访问过了// 那么说明当前结点左右⼦树都访问过了,可以访问当前结点了if (top->right == nullptr || top->right == prev){s.pop();v.push_back(top->val);prev = top;}else{// 右⼦树不为空,且没有访问,循环⼦问题⽅式右⼦树cur = top->right;}}return v;}
};

五、完整代码

二叉树搜索的实现代码

// Binary Search Tree
template<class K>
class BSTree
{typedef BSTNode<K> Node;
public:bool Insert(const K& key){if (_root == nullptr){_root = new Node(key);return true;}Node* parent = nullptr;Node* cur = _root;while (cur){if (cur->_key < key){parent = cur;cur = cur->_right;}else if (cur->_key > key){parent = cur;cur = cur->_left;}else{return false;}}cur = new Node(key);if (parent->_key < key){parent->_right = cur;}else{parent->_left = cur;}return true;}bool Find(const K& key){Node* cur = _root;while (cur){if (cur->_key < key){cur = cur->_right;}else if (cur->_key > key){cur = cur->_left;}else{return true;}}return false;}bool Erase(const K& key){Node* parent = nullptr;Node* cur = _root;while (cur){if (cur->_key < key){parent = cur;cur = cur->_right;}else if (cur->_key > key){parent = cur;cur = cur->_left;}else{// 0-1个孩⼦的情况 // 删除情况1 2 3均可以直接删除,改变⽗亲对应孩⼦指针指向即可if (cur->_left == nullptr){if (parent == nullptr){_root = cur->_right;}else{if (parent->_left == cur)parent->_left = cur -> _right;elseparent->_right = cur -> _right;}delete cur;return true;}else if (cur->_right == nullptr){if (parent == nullptr){_root = cur->_left;}else{if (parent->_left == cur)parent->_left = cur -> _left;elseparent->_right = cur -> _left;}delete cur;return true;}else{// 2个孩⼦的情况 // 删除情况4,替换法删除 // 假设这⾥我们取右⼦树的最⼩结点作为替代结点去删除// 这⾥尤其要注意右⼦树的根就是最⼩情况的情况的处理,对应课件图中删除8的情况// ⼀定要把cur给rightMinP,否会报错。 Node* rightMinP = cur;Node* rightMin = cur->_right;while (rightMin->_left){rightMinP = rightMin;rightMin = rightMin->_left;}cur->_key = rightMin->_key;if (rightMinP->_left == rightMin)rightMinP->_left = rightMin -> _right;elserightMinP->_right = rightMin -> _right;delete rightMin;return true;}}}return false;}void InOrder(){_InOrder(_root);cout << endl;}
private:void _InOrder(Node* root){if (root == nullptr){return;}_InOrder(root->_left);cout << root->_key << " ";_InOrder(root->_right);}
private:Node* _root = nullptr;
};

key/value二叉树搜索代码实现

template<class K, class V>
struct BSTNode
{// pair<K, V> _kv;K _key;V _value;BSTNode<K, V>* _left;BSTNode<K, V>* _right;BSTNode(const K& key, const V& value):_key(key), _value(value), _left(nullptr), _right(nullptr){}
};
template<class K, class V>
class BSTree
{typedef BSTNode<K, V> Node;
public:BSTree() = default;BSTree(const BSTree<K, V>& t){_root = Copy(t._root);}BSTree<K, V>& operator=(BSTree<K, V> t){swap(_root, t._root);return *this;}~BSTree(){Destroy(_root);_root = nullptr;}bool Insert(const K& key, const V& value){if (_root == nullptr){_root = new Node(key, value);return true;}Node* parent = nullptr;Node* cur = _root;while (cur){if (cur->_key < key){parent = cur;cur = cur->_right;}else if (cur->_key > key){parent = cur;cur = cur->_left;}else{return false;}}cur = new Node(key, value);if (parent->_key < key){parent->_right = cur;}else{parent->_left = cur;}return true;}Node* Find(const K& key){Node* cur = _root;while (cur){if (cur->_key < key){cur = cur->_right;}else if (cur->_key > key){cur = cur->_left;}else{return cur;}}return nullptr;}bool Erase(const K& key){Node* parent = nullptr;Node* cur = _root;while (cur){if (cur->_key < key){parent = cur;cur = cur->_right;}else if (cur->_key > key){parent = cur;cur = cur->_left;}else{if (cur->_left == nullptr){if (parent == nullptr){_root = cur->_right;}else{if (parent->_left == cur)parent->_left = cur -> _right;elseparent->_right = cur -> _right;}delete cur;return true;}else if (cur->_right == nullptr){if (parent == nullptr){_root = cur->_left;}else{if (parent->_left == cur)parent->_left = cur -> _left;elseparent->_right = cur -> _left;}delete cur;return true;}else{Node* rightMinP = cur;Node* rightMin = cur->_right;while (rightMin->_left){rightMinP = rightMin;rightMin = rightMin->_left;}cur->_key = rightMin->_key;if (rightMinP->_left == rightMin)rightMinP->_left = rightMin -> _right;elserightMinP->_right = rightMin -> _right;delete rightMin;return true;}}}return false;}void InOrder(){_InOrder(_root);cout << endl;}
private:void _InOrder(Node* root){if (root == nullptr){return;}_InOrder(root->_left);cout << root->_key << ":" << root->_value << endl;_InOrder(root->_right);}void Destroy(Node* root){if (root == nullptr)return;Destroy(root->_left);Destroy(root->_right);delete root;}Node* Copy(Node* root){if (root == nullptr)return nullptr;Node* newRoot = new Node(root->_key, root->_value);newRoot->_left = Copy(root->_left);newRoot->_right = Copy(root->_right);return newRoot;}
private:Node* _root = nullptr;
};
void test1()
{BSTree<string, string> dict;//BSTree<string, string> copy = dict;dict.Insert("left", "左边");dict.Insert("right", "右边");dict.Insert("insert", "插⼊");dict.Insert("string", "字符串");string str;while (cin >> str){auto ret = dict.Find(str);if (ret){cout << "->" << ret->_value << endl;}else{cout << "无此单词,请重新输⼊" << endl;}}
}
void  test2()
{string arr[] = { "苹果", "西⽠", "苹果", "西⽠", "苹果", "苹果", "西⽠","苹果", "⾹蕉", "苹果", "⾹蕉" };BSTree<string, int> countTree;for (const auto& str : arr){// 先查找⽔果在不在搜索树中 // 1、不在,说明⽔果第⼀次出现,则插⼊<⽔果, 1> // 2、在,则查找到的结点中⽔果对应的次数++ //BSTreeNode<string, int>* ret = countTree.Find(str);auto ret = countTree.Find(str);if (ret == NULL){countTree.Insert(str, 1);}else{ret->_value++;}}countTree.InOrder();
}int main()
{test1();test2();return 0;
}

六、总结

本章节和之后的文章使用的语言从C变为C++
优点与缺点:

  • 优点:
    • 查找、插入、删除操作的时间复杂度在平均情况下为 ,效率较高,特别是对于经常进行动态插入和查找操作的数据集合非常适用。
    • 中序遍历可以方便地得到有序的数据序列,无需额外的排序操作。
  • 缺点:
    • 最坏情况下(例如树退化为链表),查找、插入、删除操作的时间复杂度会退化到 ,性能下降严重。
    • 二叉搜索树的平衡情况依赖于插入和删除操作的顺序,如果数据的插入顺序不合理,可能会导致树的不平衡,影响操作的效率。
  • 应用场景:
    • 二叉搜索树常用于实现动态集合的数据结构,例如在数据库索引、文件系统的目录结构、编译器的符号表等场景中广泛应用,能够快速地进行数据的查找、插入和删除操作。

二叉搜索树在 C++ 中是一种重要的数据结构,掌握其基本操作和遍历方式对于解决各种实际问题具有重要意义。

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

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

相关文章

1.3 MySql的用户管理

一、下载Mysql客户端 下载navicat:Navicat 中国 | 支持 MySQL、Redis、MariaDB、MongoDB、SQL Server、SQLite、Oracle 和 PostgreSQL 的数据库管理 二、安装Navicat 三、创建数据库 创建一个数据库的连接吧&#xff0c;因为这个界面儿是图形界面儿&#xff0c;所以我们创建…

RT_Thread内核源码分析(二)——链表和对象管理

实时操作系统基本上都是通过一些链表进行线程、信号、队列的管理&#xff0c;RT_Thread也不例外&#xff0c;本章主要讲解RT_Thread的链表结构和对象管理。 本章基于RT_Thread Nano V3.1.5版本分析 1、链表 RT_Thread使用的链表非常简单&#xff0c;链表节点只有节点指针&#…

深度学习(2):梯度下降

文章目录 梯度下降梯度是什么常见梯度下降算法 代码实现批量梯度下降 梯度下降 梯度是什么 类似y ax b这种单变量的函数来说&#xff0c;导数就是它的斜率&#xff0c;这种情况下可以说梯度就是导数。 但在多变量函数中&#xff0c;梯度是一个向量&#xff0c;其分量是各个…

frp内网穿透服务器+客户端详细配置

当我们拥有一台云服务器时&#xff0c;可以将局域网服务器的服务通过内网穿透发布到公网上。frp是一个主流的内网穿透软件&#xff0c;本文讲解frp内网穿透服务器客户端详细配置。 一、需要准备的内容&#xff1a; 腾讯云服务器&#xff1a;https://curl.qcloud.com/Sjy0zKjy 2…

安卓13修改设置设备型号和设备名称分析与更改-android13设置设备型号和设备名称更改

总纲 android13 rom 开发总纲说明 文章目录 1.前言2.问题分析3.代码分析4.代码修改5.编译6.彩蛋1.前言 用户要定制一些系统显示的设备型号和设备名称,这就需要我们分析设置里面的相关信息来找到对应的位置进行修改了。 2.问题分析 像这种信息要么是config.xml里面写死了,要…

开源 AI 智能名片与 S2B2C 商城小程序:嫁接权威实现信任与增长

摘要&#xff1a;本文探讨了嫁接权威在产品营销中的重要性&#xff0c;并结合开源 AI 智能名片与 S2B2C 商城小程序&#xff0c;阐述了如何通过与权威关联来建立客户信任&#xff0c;提升产品竞争力。强调了在当今商业环境中&#xff0c;巧妙运用嫁接权威的方法&#xff0c;能够…

Kafka系列之:安装使用kafka_exporter详细步骤

Kafka系列之:安装使用kafka_exporter详细步骤 一、kafka_exporter二、下载kafka_exporter三、理解Topic Metrics指标四、理解Consumer Groups Metrics指标五、启动kafka_exporter六、查看页面七、systemctl托管服务一、kafka_exporter kafka_exporter源码kafka_exporter下载页…

JVM的基本组成

一、JDK\JRE\JVM JDK: 全称 "Java Development Kit" &#xff0c;Java 开发工具包&#xff0c;提供 javac 编译器、jheap、jconsole 等监控工具;JRE: 全称"Java Runtime Environment"&#xff0c;Java 运行环境&#xff0c;提供Class Library 核心类库 JV…

典型的MVC设计模式:使用JSP和JavaBean相结合的方式来动态生成网页内容典型的MVC设计模式

先看代码与实现&#xff1a; 文件结构 triangle_area4.jsp <% page contentType"text/html;charsetUTF-8" pageEncoding"UTF-8" %> <html> <body> <%--<jsp:useBean>&#xff1a;用于在JSP中实例化JavaBean。在这里&#xff0c…

C++ —— 关于list

目录 链接 前言 1. 迭代器浅解 2. 接口 2.1 构造函数 2.2 push_back 2.3 emplace_back 2.4 insert 2.5 erase 2.6 reverse 2.7 sort 2.8 merge 2.9 unique 2.10 splice 链接 cplusplus.com/reference/list/list/?kwlisthttps://cplusplus.com/reference/list/list…

公安局软件管理平台建设方案和必要性,论文-———未来之窗行业应用跨平台架构

一、平台方略 由于csdn拦截关键信息&#xff0c;我发发布方案&#xff0c;请留意后面文章

云服务器(华为云)安装java环境。

这篇文章主要是介绍如何搭建华为云服务器中的java环境&#xff0c;也就是jdk的安装。 这里华为云服务器使用的是liunx系统。 uname -a Linux操作系统的版本信息。具体来说&#xff0c;它表明使用的是Ubuntu系统&#xff0c;内核版本是5.15.0&#xff0c;构建于2023年1月20日&a…

16年408-数据结构

第一题&#xff1a; 解析&#xff1a; 经过查表可知&#xff1a;a的链接地址是1010H&#xff0c;而1010H正是表中e所在的位置。 由题可知f存放的位置是1014H&#xff0c; 要将f链接在a和e的中间&#xff0c;则a后面要链接f&#xff0c;f后面要链接e&#xff0c;e的链接地址不变…

【HTTP】请求“报头”,Referer 和 Cookie

Referer 描述了当前这个页面是从哪里来的&#xff08;从哪个页面跳转过来的&#xff09; 浏览器中&#xff0c;直接输入 URL/点击收藏夹打开的网页&#xff0c;此时是没有 referer。当你在 sogou 页面进行搜索时&#xff0c;新进入的网页就会有 referer 有一个非常典型的用…

LeetCode 257. 二叉树的所有路径,dfs

LeetCode 257. 二叉树的所有路径 给定一个二叉树&#xff0c;返回所有从根节点到叶子节点的路径。 说明: 叶子节点是指没有子节点的节点。 目录 LeetCode 257. 二叉树的所有路径算法选择数据结构解题步骤算法流程算法代码算法分析易错点和注意事项相似题目 算法选择 深度优…

Excel 冻结多行多列

背景 版本&#xff1a;office 2021 专业版 无法像下图内某些版本一样&#xff0c;识别选中框选的多行多列。 如下选中后毫无反应&#xff0c;点击【视图】->【冻结窗口】->【冻结窗格】后自动设置为冻结第一列。 操作 如下&#xff0c;要把前两排冻结起来。 选择 C1&a…

2-102基于matlab的蒙特卡洛仿真

基于matlab的蒙特卡洛仿真&#xff0c;对64QAM和BPSK进行蒙特卡洛仿真&#xff0c;并绘出误码率曲线。程序已调通&#xff0c;可直接运行。 下载源程序请点链接&#xff1a; 2-102基于matlab的蒙特卡洛仿真

详解机器学习经典模型(原理及应用)——支持向量机

一、什么是支持向量机 支持向量机&#xff08;Support Vector Machine, SVM&#xff09;是一种强大的机器学习算法&#xff0c;可用于解决数据分类&#xff08;二分类&#xff09;和回归问题。在分类问题上&#xff0c;SVM的核心思想是在特征空间中找到一个最优的超平面&#x…

jupyter安装与使用——Ubuntu服务器

jupyter安装与使用——Ubuntu服务器 一、安装miniconda3/anaconda31. 下载miniconda32. 安装miniconda33. 切换到bin文件夹4. 输入pwd获取路径5. 打开用户环境编辑页面6. 重新加载用户环境变量7. 初始化conda8.验证是否安装成功9.conda配置 二、安装jupyter2.1 conda安装2.2 配…

视频汇聚/视频存储/安防视频监控EasyCVR平台RTMP推流显示离线是什么原因?

视频汇聚/视频存储/安防视频监控EasyCVR视频汇聚平台兼容性强、支持灵活拓展&#xff0c;平台可提供视频远程监控、录像、存储与回放、视频转码、视频快照、告警、云台控制、语音对讲、平台级联等视频能力。 EasyCVR安防监控视频综合管理平台采用先进的网络传输技术&#xff0…