C++ 二叉树进阶:二叉搜索树

目录

二叉搜索树的概念

二叉搜索树的实现

基本结构

插入

1,当树是空树的时候

2,当树不为空的时候

3,纠正后的代码

查找

删除

1,左为空或右为空

 2,左右都不为空

3,删除的完整代码:

二叉搜索树的完整代码

BSTree.h

test.cpp

二叉搜索树的应用

Key 模型

Key-Value 模型

改造二叉搜索树为KV结构

BSTree.h

test.cpp

二叉搜索树的性能分析


二叉搜索树的概念

二叉搜索树又称二叉排序树,它或者是一棵空树,或者是具有以下性质的二叉树:

若它的左子树不为空,则左子树上所有节点的值都小于根节点的值

若它的右子树不为空,则右子树上所有节点的值都大于根节点的值

它的左右子树也分别为二叉搜索树

注意:二叉搜索树key值不能相同。

二叉搜索树中序遍历是有序的,因为二叉搜索树的定义决定了左子树节点值小于根节点值、右子树节点值大于等于根节点值(每一颗子树也满足),而中序遍历先左子树、再根节点、后右子树的方式使得遍历结果自然有序。 


二叉搜索树的实现

基本结构

二叉搜索树中的每个节点包含两个指针,分别指向左子树和右子树,以及一个存储关键值(key 值)的数据域。这种结构使得二叉搜索树能够以二叉树的形式组织数据,并通过比较节点的关键值来进行高效的查找、插入和删除操作。

二叉搜索树不能修改里面的key值,如果修改了就会破坏二叉搜索树的结构。

//节点的定义
template<class K>
struct BSTreeNode
{BSTreeNode<K>* _left;   //左节点BSTreeNode<K>* _right;  //右节点K _key;      //存储 key 值BSTreeNode(const K& key)  //构造函数完成初始化:_left(nullptr),_right(nullptr),_key(key){}
};template <class K>  //key 关键字,进行比较
class BSTree  //Binary Search Tree
{typedef BSTreeNode<K> Node;
private:Node* _root = nullptr;  //在类内进行成员初始化
};

插入

1,当树是空树的时候

直接定义一个节点把该节点给 _root。

2,当树不为空的时候

不是空树,去找这个需要插入的位置,插入一定是找一个空的位置,不可能替代某个位置。

如果插入时是相同的元素,则插入失败,因为二叉搜索树不允许出现相同的 key 值。

#pragma once
//节点的定义
template<class K>
struct BSTreeNode
{BSTreeNode<K>* _left;   //左节点BSTreeNode<K>* _right;  //右节点K _key;      //存储 key 值BSTreeNode(const K& key)  //构造函数完成初始化:_left(nullptr),_right(nullptr),_key(key){}
};template <class K>  //key 关键字,进行比较
class BSTree  //Binary Search Tree
{typedef BSTreeNode<K> Node;
public:bool Insert(const K& key){//1,根为空的时候if (_root == nullptr){_root = new Node(key);}//2,根不为空的时候Node* cur = _root;while (cur){if (key > cur->_key)   //插入的key比当前节点大就往右边走{cur = cur->_right;   }else if (key < cur->_key) //插入的key比当前节点小就往左边走{cur = cur->_left;}else                     {return false; //插入的key和当前节点相等,就插入失败}}cur = new Node(key);return true;}//中序遍历void _InOrder(Node* root) {if (root == nullptr)return;_InOrder(root->_left);cout << root->_key << " ";_InOrder(root->_right);}void InOrder() {_InOrder(_root);cout << endl;}
private:Node* _root = nullptr;  //在类内进行成员初始化
};
void Test() 
{BSTree<int> t;int a[] = { 5,3,4,1,7,8,2,6,0,9 };for (auto e : a){t.Insert(e);}t.InOrder();  
}

通过测试我们会发现,这里只有 5 插入成功了,也就是根节点插入成功,那么这段代码存在一定的问题,如何解决呢???

问题:在循环中,只是不断地更新 cur 指针,让它指向树中的不同节点,但没有记录下新节点应该连接的父节点。
    当找到空位置并创建新节点 cur = new Node(key) 后,新节点与树中的其他节点没有任何连接,导致新节点成为一个孤立的节点,没有真正插入到树中。

改进:

  1. 添加一个 parent 指针来记录新节点的父节点。在循环中,当更新 cur 指针时,也同时更新 parent 指针。
  2. 在找到插入位置后,根据 keyparent->_key 的大小关系,将新节点连接到父节点的左子树或右子树。

3,纠正后的代码

#pragma once
//节点的定义
template<class K>
struct BSTreeNode
{BSTreeNode<K>* _left;   //左节点BSTreeNode<K>* _right;  //右节点K _key;      //存储 key 值BSTreeNode(const K& key)  //构造函数完成初始化:_left(nullptr),_right(nullptr),_key(key){}
};template <class K>  //key 关键字,进行比较
class BSTree  //Binary Search Tree
{typedef BSTreeNode<K> Node;
public:bool Insert(const K& key){//当树是空树的时候if (_root == nullptr) {_root = new Node(key);return true;}//树不为空的时候Node* parent = nullptr;  //用一个节点来记录cur的父亲Node* cur = _root;while (cur){if (key > cur->_key) {parent = cur;   cur = cur->_right;	}else if (key < cur->_key) {parent = cur;cur = cur->_left;}else{return false;}}cur = new Node(key);if (key > parent->_key)  //判断到底是属于父亲的左树还是右树parent->_right = cur;elseparent->_left = cur;return true;}//中序遍历void _InOrder(Node* root) {if (root == nullptr)return;_InOrder(root->_left);cout << root->_key << " ";_InOrder(root->_right);}void InOrder() {_InOrder(_root);cout << endl;}
private:Node* _root = nullptr;  //在类内进行成员初始化
};
void Test() 
{BSTree<int> t;int a[] = { 5,3,4,1,7,8,2,6,0,9 };for (auto e : a){t.Insert(e);}t.InOrder();  
}

查找

查找和插入类似,如果比当前节点小就往左边找,如何比当前节点大就往右边找,不断更新cur,直到找返回 true,如果没有找到,返回 false。

	//查找bool Find(const K& key) {Node* cur = _root;while (cur){if (key > cur->_key) {cur = cur->_right;}else if (key < cur->_key) {cur = cur->_left;}else {return true;}}return false;}

删除

二叉搜索树重点在于删除操作,也比较简单。

在实现Erase的时候我们不能使用Find,因为我们还需用到它的父亲,所以这里还是使用双指针。

当我们试删除这些节点,我们可以发现可以存在这些情况:

1,删除2最好删,把2删除之后,还需要把2的右置成nullpt,不然就是野指针了。

2,  当前节点左为空,父亲指向我的右,当前节点右为空,父亲指向我的左边。

3,叶子结点也可以归类到这种左为空或者右为空,让父亲指向左/右。

3,当左右都不为空的时候不能直接删除,用替换法删除
可以找左子树的最大节点(最右节点)或者右子树的最小节点最左节点)替代它

综上所述:

① 左为空

② 右为空

③ 左右都不为空

1,左为空或右为空

我们不能单单只看cur的左右是否为空,然后直接用parent去指向cur的左右,而是我们需要去观察 cur 属于parent 左边还是右边,如果在左边就用 parent 的左边去指向cur的左或者右,如果在右边就用 parent 的右边去指向cur的左或者右。

右为空和左为空同理。

bool Erase(const K& key){Node* parent = nullptr;Node* cur = _root;while (cur){if (key > cur->_key){parent = cur;cur = cur->_right;}else if (key < cur->_key){parent = cur;cur = cur->_left;}else{//找到了//1,左边为空if (cur->_left == nullptr){if (parent->_right == cur)parent->_right = cur->_right;elseparent->_left = cur->_right;delete cur;}//2,右边为空else if (cur->_right == nullptr) {if (parent->_left = cur)parent->_left = cur->_left;elseparent->_right = cur->_left;delete cur;}else  //3,左右都不为空{}return true;}}return false;}

 2,左右都不为空

左右都不为空:找左树的最大节点,或者右树的最小节点,也就是左子树的最右节点,或者右子树的最左节点

bool Erase(const K& key){Node* parent = nullptr;Node* cur = _root;while (cur){if (key > cur->_key){parent = cur;cur = cur->_right;}else if (key < cur->_key){parent = cur;cur = cur->_left;}else{//找到了//1,左边为空if (cur->_left == nullptr){if (parent->_right == cur)parent->_right = cur->_right;elseparent->_left = cur->_right;delete cur;}//2,右边为空else if (cur->_right == nullptr) {if (parent->_left = cur)parent->_left = cur->_left;elseparent->_right = cur->_left;delete cur;}else  //3,左右都不为空{Node* rightMinParent = nullptr;Node* rightMin = cur->_right;while (rightMin->_left) {rightMinParent = rightMin;rightMin = rightMin->_left;}//替代cur->_key = rightMin->_key;//转换成删除rightMin rightMinParent->_left = rightMin->_right;delete rightMin;}return true;}}return false;}

假设我一上来就删除 7 这棵树存在问题

纠正后的代码:

Node* rightMinParent = cur;
Node* rightMin = cur->_right;
while (rightMin->_left) 
{rightMinParent = rightMin;rightMin = rightMin->_left;
}
//替代
cur->_key = rightMin->_key;
//转换成删除rightMin (rightMin是左为空,父亲指向它的右边)
if (rightMin == rightMinParent->_left) rightMinParent->_left = rightMin->_right;
elserightMinParent->_right = rightMin->_right;
delete rightMin;

如果把这棵树删空也会存在问题 

纠正后的代码: 

if (cur->_left == nullptr)
{if (cur == _root)  //当删除的是根节点的时候{_root = cur->_right;}else {if (parent->_right == cur)parent->_right = cur->_right;elseparent->_left = cur->_right;}delete cur;
}
else if (cur->_right == nullptr) 
{if (cur == _root)   //当删除的是根节点的时候{_root = cur->_left;  }else {if (parent->_left == cur)parent->_left = cur->_left;elseparent->_right = cur->_left;}delete cur;
}

3,删除的完整代码:

//删除
bool Erase(const K& key) 
{Node* parent = nullptr;Node* cur = _root;while (cur){if (key > cur->_key) {parent = cur;cur = cur->_right;}else if (key < cur->_key) {parent = cur;cur = cur->_left;}else {//找到了,开始删除// 1、左为空// 2、右为空// 3、左右都不为空if (cur->_left == nullptr) {if (cur == _root) {_root = cur->_right;}else {if (parent->_right == cur)parent->_right = cur->_right;elseparent->_left = cur->_right;}delete cur;}else if (cur->_right == nullptr) {if (cur == _root){_root = cur->_left;}else {if (parent->_left == cur)parent->_left = cur->_left;elseparent->_right = cur->_left;}delete cur;}else {Node* rightMinParent = cur;Node* rightMin = cur->_right;while (rightMin->_left) {rightMinParent = rightMin;rightMin = rightMin->_left;}//替代cur->_key = rightMin->_key;//转换成删除rightMin (rightMin是左为空,父亲指向它的右边)if (rightMin == rightMinParent->_left) rightMinParent->_left = rightMin->_right;else rightMinParent->_right = rightMin->_right;delete rightMin;}return true;}}return false;
}

二叉搜索树的完整代码

BSTree.h

#pragma once
template<class K>
struct BSTreeNode
{BSTreeNode<K>* _left;BSTreeNode<K>* _right;K _key;BSTreeNode(const K& key) :_left(nullptr),_right(nullptr),_key(key){}
};template <class K>  //key 关键字,进行比较
class BSTree  //Binary Search Tree
{typedef BSTreeNode<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 (key > cur->_key) {parent = cur;cur = cur->_right;	}else if (key < cur->_key) {parent = cur;cur = cur->_left;}else{return false;}}cur = new Node(key);if (key > parent->_key)parent->_right = cur;elseparent->_left = cur;return true;}//查找bool Find(const K& key) {Node* cur = _root;while (cur){if (key > cur->_key) {cur = cur->_right;}else if (key < cur->_key) {cur = cur->_left;}else {return true;}}return false;}//删除bool Erase(const K& key) {Node* parent = nullptr;Node* cur = _root;while (cur){if (key > cur->_key) {parent = cur;cur = cur->_right;}else if (key < cur->_key) {parent = cur;cur = cur->_left;}else {//找到了,开始删除// 1、左为空// 2、右为空// 3、左右都不为空if (cur->_left == nullptr) {if (cur == _root) {_root = cur->_right;}else {if (parent->_right == cur)parent->_right = cur->_right;elseparent->_left = cur->_right;}delete cur;}else if (cur->_right == nullptr) {if (cur == _root){_root = cur->_left;}else {if (parent->_left == cur)parent->_left = cur->_left;elseparent->_right = cur->_left;}delete cur;}else {Node* rightMinParent = cur;Node* rightMin = cur->_right;while (rightMin->_left) {rightMinParent = rightMin;rightMin = rightMin->_left;}//替代cur->_key = rightMin->_key;//转换成删除rightMin (rightMin是左为空,父亲指向它的右边)if (rightMin == rightMinParent->_left) rightMinParent->_left = rightMin->_right;else rightMinParent->_right = rightMin->_right;delete rightMin;}return true;}}return false;}//中序遍历void _InOrder(Node* root) {if (root == nullptr)return;_InOrder(root->_left);cout << root->_key << " ";_InOrder(root->_right);}void InOrder() {_InOrder(_root);cout << endl;}
private:Node* _root = nullptr;
};void TestBSTree() 
{BSTree<int> t;int a[] = { 5,3,4,1,7,8,2,6,0,9 };for (auto e : a) {t.Insert(e);}t.InOrder();//1.上来我就删除7,有问题t.Erase(7);t.InOrder();t.Erase(8);t.InOrder();//2.把这棵树删空,也会存在问题/*for (auto e : a) {t.Erase(e);}t.InOrder();*/叶子t.Erase(2);t.InOrder();左为空或者右为空t.Erase(8);t.Erase(1);t.InOrder();左右都不为空t.Erase(5);t.InOrder();
}

test.cpp

#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
using namespace std;
#include<string>
#include "BSTree.h"
int main()
{TestBSTree();return 0;
}

二叉搜索树的应用

Key 模型

  • 在 Key 模型中,数据主要围绕一个关键标识符(Key)来组织。通常这个 Key 是一个唯一的标识,用于快速检索数据。例如,在一个简单的学生信息系统中,学生的学号可以作为 Key。系统可以根据学号快速查找对应的学生信息,但是可能存储的信息相对比较单一,主要就是和这个 Key 直接相关的内容。
  • 它类似于一个索引,重点在于通过这个唯一的标识来定位某个特定的数据项。
  • 以上二叉搜索树的实现使用的就是key模型

Key-Value 模型

  • Key - Value 模型则是由一个 Key 和一个与之对应的 Value 组成的键值对。Key 仍然用于检索,但是 Value 可以是各种各样的数据结构,如字符串、数字、对象、数组等。比如在一个缓存系统中,Key 可以是一个 URL,Value 则是这个 URL 对应的网页内容。
  • 这种模型更强调数据的关联性,Key 和 Value 共同构成了一个完整的数据单元,Value 的内容可以非常丰富,并且 Key 和 Value 之间存在一种明确的对应关系。
  • Key 通常是设计为唯一的标识符,用于精确地定位和区分不同的键值对。而 Value 可以是相同的。例如,在一个记录用户购物偏好的系统中,Key 可以是用户的唯一标识(如用户 ID),Value 是用户喜欢的商品类别。多个用户(不同的 Key)可能都喜欢相同的商品类别(相同的 Value)。
  • 实际中 Key-Value模型应用广泛。

改造二叉搜索树为KV结构

BSTree.h
#pragma once
// Key-Value 模型
template<class K, class V>
struct BSTreeNode
{BSTreeNode<K, V>* _left;BSTreeNode<K, V>* _right;K _key;V _value;BSTreeNode(const K& key, const V& value):_left(nullptr), _right(nullptr), _key(key), _value(value){}
};template <class K, class V>  //key 关键字,进行比较
class BSTree  //Binary Search Tree
{typedef BSTreeNode<K, V> Node;
public://插入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 (key > cur->_key){parent = cur;cur = cur->_right;}else if (key < cur->_key){parent = cur;cur = cur->_left;}else{return false;}}cur = new Node(key, value);if (key > parent->_key)parent->_right = cur;elseparent->_left = cur;return true;}//查找Node* Find(const K& key){Node* cur = _root;while (cur){if (key > cur->_key){cur = cur->_right;}else if (key < cur->_key){cur = cur->_left;}else{return cur;}}return nullptr;}//删除bool Erase(const K& key){Node* parent = nullptr;Node* cur = _root;while (cur){if (key > cur->_key){parent = cur;cur = cur->_right;}else if (key < cur->_key){parent = cur;cur = cur->_left;}else{//找到了,开始删除// 1、左为空// 2、右为空// 3、左右都不为空if (cur->_left == nullptr){if (cur == _root){_root = cur->_right;}else{if (parent->_right == cur)parent->_right = cur->_right;elseparent->_left = cur->_right;}delete cur;}else if (cur->_right == nullptr){if (cur == _root){_root = cur->_left;}else{if (parent->_left == cur)parent->_left = cur->_left;elseparent->_right = cur->_left;}delete cur;}else{Node* rightMinParent = cur;Node* rightMin = cur->_right;while (rightMin->_left){rightMinParent = rightMin;rightMin = rightMin->_left;}//替代cur->_key = rightMin->_key;//转换成删除rightMin (rightMin是左为空,父亲指向它的右边)if (rightMin == rightMinParent->_left)rightMinParent->_left = rightMin->_right;elserightMinParent->_right = rightMin->_right;delete rightMin;}return true;}}return false;}//中序遍历void _InOrder(Node* root){if (root == nullptr)return;_InOrder(root->_left);cout << root->_key << ":" << root->_value << endl;_InOrder(root->_right);}void InOrder(){_InOrder(_root);cout << endl;}
private:Node* _root = nullptr;
};void TestBSTree()
{//输入单词,查找单词对应的中文翻译/*BSTree<string, string> dict;dict.Insert("sort", "排序");dict.Insert("string", "字符串");dict.Insert("tree", "树");dict.Insert("insert", "插入");string str;while (cin >> str){BSTreeNode<string, string>* ret = dict.Find(str);if (ret){cout << ret->_value << endl;}else{cout << "无此单词" << endl;}}*///以后很常用,统计水果的个数string strArr[] = { "西瓜","西瓜" ,"樱桃","苹果","香蕉","西瓜" ,"西瓜","哈密瓜" ,"西瓜" ,"西瓜" };BSTree<string, int> countTree;for (auto str : strArr){BSTreeNode<string, int>* ret = countTree.Find(str);if (ret == nullptr){countTree.Insert(str, 1);}else{ret->_value++;}}countTree.InOrder();
}
test.cpp
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
using namespace std;
#include<string>
#include "BSTree.h"
int main()
{TestBSTree();return 0;
}

二叉搜索树的性能分析

最好情况

  • 对于平衡的二叉搜索树,插入操作首先需要找到插入位置。因为树是平衡的,这个查找过程类似于查找操作,时间复杂度为 O(logN)。
  • 找到位置后,插入新节点的操作本身时间复杂度为O(1) (只需要修改指针来连接新节点)。所以,整体插入操作在最好情况下的时间复杂度为O(logN)。

最坏情况

  • 当二叉搜索树退化为链表时,插入操作需要先遍历链表找到合适的插入位置。例如,若按照从小到大的顺序插入节点,要插入一个新的最大值,需要遍历到链表的末尾。此时,插入操作的时间复杂度为 O(N)。

问题:如果退化成单支树,二叉搜索树的性能就失去了。那能否进行改进,不论按照什么次序插入关键码,二叉搜索树的性能都能达到最优?那么就有我们后续学习的AVL树和红黑树。

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

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

相关文章

蛋白质残基的距离计算以及径向基函数变换中的维度变化

1. 计算Ca距离的函数 def _dist(self, X, mask, eps=1E-6):mask_2D = torch.unsqueeze(mask,1) * torch.unsqueeze(mask,2)dX = torch.unsqueeze(X,1) - torch.unsqueeze(X,2)D = mask_2D * torch.sqrt(torch.sum(dX**2, 3) + eps)D_max, _ = torch.max(D, -1, keepdim=True)D…

visual studio设置修改文件字符集方法

该方法来自网文&#xff0c;特此记录备忘。 添加两个组件&#xff0c;分别是Force UTF-8,FileEncoding。 截图如下&#xff1a; 方法如下&#xff1a;vs中点击“扩展”->“管理扩展”&#xff0c;输入utf搜索&#xff0c;安装如下两个插件&#xff0c;然后重启vs&#xf…

MongoDB 安装教程(MAC版本)

1.官网地址 https://www.mongodb.com/ 下载社区版&#xff0c;并且解压即可 2.安装位置 没有固定位置&#xff0c;将解压后的文件拷贝到任意位置&#xff0c;这里将以 /usr/locall为例。 3.配置环境变量 ## 1.打开环境配置文件 open .bash_profile ## 2.添加环境配置&#…

input子系统的框架和重要数据结构详解

#1024程序员节 | 征文# 往期内容 I2C子系统专栏&#xff1a; 专栏地址&#xff1a;IIC子系统_憧憬一下的博客-CSDN博客具体芯片的IIC控制器驱动程序分析&#xff1a;i2c-imx.c-CSDN博客 – 末篇&#xff0c;有往期内容观看顺序 总线和设备树专栏&#xff1a; 专栏地址&#…

【人工智能】掌握深度学习中的时间序列预测:深入解析RNN与LSTM的工作原理与应用

深度学习中的循环神经网络&#xff08;RNN&#xff09;和长短时记忆网络&#xff08;LSTM&#xff09;在处理时间序列数据方面具有重要作用。它们能够通过记忆前序信息&#xff0c;捕捉序列数据中的长期依赖性&#xff0c;广泛应用于金融市场预测、自然语言处理、语音识别等领域…

RSocket vs WebSocket:Spring Boot 3.3 中的两大实时通信利器

RSocket vs WebSocket&#xff1a;Spring Boot 3.3 中的两大实时通信利器 随着现代互联网应用的不断发展&#xff0c;实时通信已经成为许多应用程序不可或缺的功能。无论是社交网络、在线游戏还是数据监控系统&#xff0c;实时通信都能提供快速、无缝的信息交换。而实现实时通…

高精度KEITHLEY2636A参数资料吉时利2636B数字源表

Keithley 2636A吉时利2636B数字源表&#xff0c;200V&#xff0c;1fA/1uV&#xff0c;2 通道&#xff0c;10A 脉冲 Keithley 2636A SourceMeter 是 Keithley 最新 IV 源测量仪器的一部分&#xff0c;可用作台式 IV 表征工具或多通道 IV 测试系统的构建块组件。对于台式使用&am…

STM32通信协议-I2C

目录 一&#xff0c;IC2的协议规则 I2C总线是PHILIPS公司开发的两线式串行总线&#xff0c;I2C总线主要解决了单片机一对多通信的问题 两根通信线&#xff1a;SCL,SDA&#xff0c;同步&#xff0c;半双工通信&#xff0c;支持数据应答机制&#xff0c;支持总线挂载多设备。 …

策略模式是一种行为设计模式

策略模式是一种行为设计模式&#xff0c;允许你定义一系列算法、封装每一个算法&#xff0c;并使它们可以相互替换。 策略模式使得算法的变化独立于使用算法的客户端。以下是一个用策略模式实现不同类型促销活动的 Java 示例。 步骤 定义促销策略接口&#xff1a;创建一个接…

Python异常检测- 单类支持向量机(One-Class SVM)

系列文章目录 Python异常检测- Isolation Forest&#xff08;孤立森林&#xff09; python异常检测 - 随机离群选择Stochastic Outlier Selection (SOS) python异常检测-局部异常因子&#xff08;LOF&#xff09;算法 Python异常检测- DBSCAN 文章目录 系列文章目录前言一、On…

图像高清化(论文复现)

图像高清化(论文复现) 本文所涉及所有资源均在传知代码平台可获取 文章目录 图像高清化(论文复现)概述算法原理核心逻辑效果演示使用方式概述 本文复现论文 “Zero-Shot” Super-Resolution using Deep Internal Learning[1] 提出的图像超分辨率方法。 图像超分辨率是指从低…

如何从模块内部运行 Pytest

在 Python 中&#xff0c;pytest 是一个强大的测试框架&#xff0c;用于编写和运行测试用例。通常我们会在命令行中运行 pytest&#xff0c;但是有时你可能希望从模块或脚本的内部运行 pytest&#xff0c;比如为了自动化测试或集成到某个工作流程中。 1、问题背景 当你从模块…

计算机视觉中的坐标变换

1.概述 高级驾驶辅助系统&#xff08;ADAS&#xff09;领域&#xff0c;存在多种常用的坐标系&#xff1a;LiDAR 坐标系、车辆坐标系、相机坐标系、图像坐标系等。因为和这些坐标系频繁打交道&#xff0c;本文对点的旋转与坐标系旋转等变换给出直观推导与说明。 2.坐标点平移…

Android Audio基础——音频混音结束处理(十一)

上一篇文章主要介绍了混音线程 MixerThread 的创建及混音流程的处理过程,这里我们看一下完成混音后的调用流程。 一、音频处理结束 在音频处理中,结束阶段通常分为两个主要阶段:sleep 和 standby。这两个阶段的目的是为了节省资源和优化性能。 源码位置:/frameworks/av/s…

06 go语言(golang) - 循环语句运算符

循环语句 在Go语言中&#xff0c;for 循环是实现循环的主要方式&#xff0c;它非常灵活且强大。 1. 基本的 for 循环 这种形式类似于 C/C 和 Java 中的 for 循环。它包括三个部分&#xff1a;初始化语句&#xff1b;条件表达式&#xff1b;后续操作&#xff08;通常是递增或…

多租户架构的全景分析(基本概念、实现策略、资源管理和隔离、数据安全与隔离、性能优化、扩展性与升级、案例研究)

文章目录 1. 多租户的基本概念2. 多租户的实现策略2.1 独立数据库模式2.2 共享数据库-独立Schema模式2.3 共享数据库-共享Schema模式 3. 资源管理和隔离4. 数据安全与隔离5. 性能优化6. 扩展性与升级7. 案例研究总结 多租户架构在云计算和SaaS应用中越来越流行&#xff0c;因为…

Netty无锁化设计之对象池实现

池化技术是比较常见的一种技术&#xff0c;在平时我们已经就接触很多了&#xff0c;比如线程池&#xff0c;数据库连接池等等。当我们要使用一个资源的时候从池中去获取&#xff0c;用完就放回池中以便其他线程可以使用&#xff0c;这样的目的就是为了减少资源开销&#xff0c;…

【编程语言】C++ 中 vector 初始化的多种方法详解

在 C 中&#xff0c;vector 是一个非常重要的数据结构&#xff0c;它提供了动态数组的功能&#xff0c;可以在运行时根据需要自动调整大小。vector 的初始化方法有多种&#xff0c;既可以简单地进行初始化&#xff0c;也可以根据特定的需求进行复杂的初始化。在这篇博客中&…

JMeter 动态参数赋值实践

目录 前言 单线程 用户参数 场景说明 实战结果 配置明细 单线程 CSV Data Set Config 场景说明 实践结果 配置明细 多线程循环单次执行 场景说明 实践结果 配置明细 单线程 控制器 用户自定义变量 用户参数 场景说明 实战结果 配置明细 多并发 多接口 …

AudioSegment 提高音频音量 - python 实现

一些采集的音频声音音量过小可以通过 AudioSegment 实现音量增强。 按照 python 库&#xff1a; pip install AudioSegment 代码具体实现&#xff1a; #-*-coding:utf-8-*- # date:2024-10 # Author: DataBall - XIAN # Function: 音频增加音量import os from pydub import …