[算法沉淀记录] 分治法应用 —— 二分搜索(Binary Search)

分治法应用 —— 二分搜索

算法基本思想

二分搜索(Binary Search)是一种在有序数组中查找特定元素的高效算法。它每次将搜索区间减半,从而快速地缩小搜索范围。二分搜索的基本思想是:首先将待查关键字与数组中间位置的关键字比较,由于数组是有序的,所以一次比较就可以确定待查关键字是在中间位置的左边还是右边,然后只在相应的区域内继续搜索,直到找到为止,或者确定找不到为止。

算法步骤

二分搜索的算法步骤如下:

  1. 初始化:设置两个指针,low指向数组的起始位置,high指向数组的最后一个位置。
  2. 查找中间元素:计算中间位置mid = (low + high) / 2
  3. 比较中间元素
    • 如果中间元素等于目标值,则搜索成功,返回中间位置的索引。
    • 如果中间元素小于目标值,则将low设置为mid + 1,表示目标值在中间位置的右侧。
    • 如果中间元素大于目标值,则将high设置为mid - 1,表示目标值在中间位置的左侧。
  4. 重复步骤2和3:直到low大于high,表示搜索区间为空,目标值不存在于数组中。

伪代码描述

function binarySearch(arr, target):low = 0high = length(arr) - 1while low <= high:mid = (low + high) / 2if arr[mid] == target:return midelif arr[mid] < target:low = mid + 1else:high = mid - 1return -1  // 目标值不存在于数组中

二分搜索算法是一种高效的查找算法,特别适用于有序数组。下面是二分搜索算法的一些优缺点:

优点

  1. 高效的时间复杂度:二分搜索算法的时间复杂度为O(log n),这意味着即使在很大的数据集中,它也能快速地找到目标元素。
  2. 较少的比较次数:相比线性搜索,二分搜索每次迭代都将搜索区间减半,因此需要比较的次数大大减少。
  3. 空间复杂度低:二分搜索算法只需要常数级别的额外空间来存储几个变量,因此它的空间复杂度为O(1)。
  4. 适用于静态或有序数组:对于静态数据集或已经排序的数据,二分搜索是一个理想的选择。

缺点

  1. 数据必须有序:二分搜索算法的前提是数据必须是有序的。如果数据未排序,那么首先需要进行排序,这会增加额外的复杂度和时间成本。
  2. 不适合数据频繁变动:如果数据集合经常变化,那么每次变化后可能都需要重新排序,这会导致维护成本增加。
  3. 内存访问模式不友好:二分搜索可能导致非顺序的内存访问模式,这可能会影响缓存性能,尤其是在大数据集上。
  4. 不适合小数据集:对于小数据集,二分搜索的优势不明显,甚至可能不如线性搜索高效,因为二分搜索的常数因子和递归开销在小数据集上更为显著。
  5. 实现复杂度:虽然二分搜索的基本思想简单,但实现时需要注意很多细节,如整数溢出、循环终止条件等,这些都可能导致算法出错。
  6. 不适合所有查找问题:二分搜索适用于精确查找,但如果需要查找的是近似值或者需要查找多个满足条件的元素,可能需要其他更合适的算法。

二分搜索算法在有序数组中查找特定元素时非常高效,但其高效性依赖于数据已排序的前提。在实际应用中,需要根据数据的特点和问题的需求来选择合适的查找算法。

应用场景

二分搜索算法由于其高效性,在许多开发场景中都有应用。以下是一些常见的应用场景:

  1. 搜索算法
    • 在有序数组或数据结构中查找特定的元素。
    • 在有序列表中查找某个值的索引或位置。
  2. 数值计算
    • 在计算机科学和工程中,寻找某个方程的根或满足特定条件的数值解。
    • 在动态规划中寻找最优解的边界条件。
  3. 游戏开发
    • 在游戏中计算玩家的排名或分数位于哪个百分比。
    • 在游戏中动态调整难度级别,根据玩家的表现找到合适的挑战水平。
  4. 数据分析和统计
    • 在大量有序数据中查找中位数、百分位数或其他统计指标。
    • 在时间序列数据中查找特定时间点的数据值。
  5. 排序算法的优化
    • 在归并排序中,用于合并两个已排序的子数组。
    • 在快速排序中,用于选择合适的枢轴元素。
  6. 算法竞赛和面试题
    • 在编程竞赛和算法面试中,二分搜索是常见的问题解决工具。
    • 用于解决各种变种的二分搜索问题,如查找最接近的元素、查找元素的边界等。
  7. 资源分配和调度
    • 在资源分配问题中,如云计算中的负载均衡,找到合适的资源分配点。
    • 在任务调度中,根据任务的优先级和截止时间找到最优的调度顺序。
  8. 缓存和数据库索引
    • 在数据库中,利用二分搜索在索引中快速定位记录。
    • 在缓存淘汰策略中,如LRU缓存,快速查找和更新缓存条目。
  9. 网络协议
    • 在网络协议中,如TCP拥塞控制,用于动态调整发送速率。
  10. 人工智能和机器学习
    • 在机器学习算法中,如支持向量机(SVM),用于寻找最优的超平面。
      二分搜索算法的这些应用场景都利用了其在有序数据中快速查找特定元素的特性。在实际开发中,根据具体情况选择合适的二分搜索变种,可以大大提高程序的效率和性能。

时间复杂度分析

最坏情况

最坏情况下,目标元素不在数组中,或者目标元素是数组的最后一个元素。在这种情况下,每次迭代都会将搜索区间减半,直到区间为空。设数组长度为n,那么最坏情况下二分搜索需要进行(log2(n))次迭代。因此,最坏情况下的时间复杂度是O(log n)。

最佳情况

最佳情况下,目标元素恰好在数组的中间位置。在这种情况下,只需要一次迭代就可以找到目标元素。因此,最佳情况下的时间复杂度是(O(1))。

平均情况

在平均情况下,目标元素可能在数组的任何位置。每次迭代都会将搜索区间减半,直到找到目标元素。假设目标元素出现在数组中的任何位置的概率是相等的,那么平均情况下的时间复杂度也是(O(log(n)))。

空间复杂度分析

二分搜索算法只需要常数级别的额外空间来存储几个变量,如low、high和mid等。这些变量的空间需求不随输入数组的大小而变化,因此二分搜索的空间复杂度是(O(1))。

证明

时间复杂度证明

我们可以使用数学归纳法来证明二分搜索的时间复杂度。
基础情况
当n=1时,只需要一次比较,时间复杂度为O(1),符合O(log n)。
归纳假设
假设当数组长度为k时,二分搜索的时间复杂度为O(log k)。
归纳步骤
当数组长度为k+1时,第一次迭代将数组分为两半,每半的长度为k/2(向上取整)。根据归纳假设,对每一半进行二分搜索的时间复杂度是O(log(k/2))。因此,总的时间复杂度是2 * O(log(k/2)),这可以简化为O(log k + log 2) = O(log k + 1) = O(log k)。由于log k是k的增长速度的较小的常数倍,所以O(log k)也符合O(log(k+1))。
由数学归纳法可知,二分搜索的时间复杂度是O(log n)。

空间复杂度证明

二分搜索算法使用的额外空间包括几个整型变量和循环中的临时空间。这些空间的需求不随输入数组的大小而变化,因此空间复杂度是常数级别的,即O(1)。
综上所述,二分搜索算法的时间复杂度在最坏情况下是O(log n),在最佳情况下是O(1),在平均情况下也是O(log n)。空间复杂度是O(1)。这些结论都是基于对算法迭代过程的数学分析和归纳证明得出的。

代码实现

Python 实现

def binary_search(arr, target):low = 0high = len(arr) - 1while low <= high:mid = low + (high - low) // 2if arr[mid] == target:return midelif arr[mid] < target:low = mid + 1else:high = mid - 1return -1

C++ 模板实现

// Define a template binary_search_function:
template <typename Iterator, typename T>
Iterator binarySearch(Iterator begin, Iterator end, const T &key)
{const Iterator NotFound = end;while (begin < end){const Iterator Middle = begin + (distance(begin, end) / 2);if (*Middle == key){return Middle;}else if (*Middle > key){end = Middle;}else{begin = Middle + 1;}}return NotFound;
}

另一个版本:

int binarySearch(const vector<T>& arr, T target) {int low = 0;int high = arr.size() - 1;while (low <= high) {int mid = low + (high - low) / 2;  // 防止溢出if (arr[mid] == target) {return mid;} else if (arr[mid] < target) {low = mid + 1;} else {high = mid - 1;}}return -1;  // 目标值不存在于数组中
}

扩展阅读

二分搜索算法的时间复杂度已经是O(log n),这是一个非常高效的算法。但是,在一些特殊情况下,还有几种常见的优化方法和变种:

优化时间复杂度

  1. 避免重复计算
    • 在计算中点时,可以直接使用low + (high - low) / 2而不是(low + high) / 2,这样可以避免整数溢出的问题。
  2. 尾递归优化
    • 如果二分搜索是通过递归来实现的,那么可以通过尾递归优化来减少栈空间的使用。
  3. 循环不变量
    • 使用循环不变量来维护搜索区间,这样可以减少不必要的比较和分支判断。

一个疑虑:既然有二分搜索,那么为什么没有多分搜索?

多分搜索这个概念并不是没有,但从理论和实践的角度来看,二分搜索已经是效率非常高的搜索算法,它能够在对数时间复杂度内找到目标元素。多分搜索,即每次将搜索区间分成多于两部分的搜索,理论上也是可能的,但在实际应用中并不常见,原因如下:

  1. 效率提升有限:二分搜索每次将搜索区间减半,已经是一个非常快的搜索方式。多分搜索可能每次将区间分成更多部分,但并不一定能显著提升搜索效率。在大多数情况下,二分搜索的O(log n)时间复杂度已经足够高效。
  2. 实现复杂性:二分搜索的实现相对简单,只需要维护两个指针即可。而多分搜索的实现会更加复杂,需要维护多个子区间的指针和状态,这会增加代码的复杂度和出错的可能性。
  3. 缓存不友好:多分搜索可能导致非顺序的内存访问模式,这可能会影响缓存性能。相比之下,二分搜索的顺序访问模式更加符合现代计算机系统的缓存机制。
  4. 适用场景有限:二分搜索适用于有序数组或能够转化为有序的搜索问题。多分搜索可能在某些特定的问题上有用,但这些场景相对罕见,且往往可以通过其他更有效的算法来解决。
  5. 递归开销:如果多分搜索采用递归实现,那么每次分割都会产生额外的递归调用开销。这可能会导致递归深度增加,从而增加栈空间的使用。
  6. 概率分布问题:在多分搜索中,如何合理地分割搜索区间是一个问题。理想的分割应该是基于数据的概率分布,但这样的信息通常不可用,因此很难设计出一个普遍适用的多分搜索算法。
    综上所述,虽然多分搜索在理论上是可能的,但在实际应用中,二分搜索已经能够满足大多数场景的需求,且具有更好的普适性、简单性和效率。因此,多分搜索并没有成为主流的搜索算法。

完整的项目代码

Python 代码

def binary_search(arr, target):low = 0high = len(arr) - 1while low <= high:mid = low + (high - low) // 2if arr[mid] == target:return midelif arr[mid] < target:low = mid + 1else:high = mid - 1return -1arr = [1, 3, 5, 7, 9, 11]
target = 7
result = binary_search(arr, target)if result != -1:print(f"Element found at index {result}")
else:print("Element not found in the array")

C++ 代码

#include <list>
#include <array>
#include <algorithm>
#include <functional>
#include <iostream>
#include <iterator>
#include <numeric>
#include <vector>using namespace std;class Person
{
public:Person() = default;~Person() = default;Person(string name, int age, int score){this->name = name;this->age = age;this->socre = score;}// Override the operator> for other function to use.bool operator>(const Person &other) const{// Compare the socre of two Person objects.return this->socre > other.socre;}// Override the operator< for other function to use.bool operator<(const Person &other) const{// Compare the socre of two Person objects.return this->socre < other.socre;}// Override the operator== for other function to use.bool operator==(const Person &other) const{// Compare the socre, age and name of two Person objects.return this->socre == other.socre &&this->age == other.age &&this->name == other.name;}// Override the operator!= for other function to use.bool operator!=(const Person &other) const{// Compare the socre, age and name of two Person objects.return this->socre != other.socre ||this->age != other.age ||this->name != other.name;}// Override the operator<= for other fnction to use.bool operator<=(const Person &other) const{// Compare the socre, age and name of two Person objects.return this->socre <= other.socre &&this->age <= other.age &&this->name <= other.name;}// Override the operator>= for other function to use.bool operator>=(const Person &other) const{// Compare the socre, age and name of two Person objects.return this->socre >= other.socre &&this->age >= other.age &&this->name >= other.name;}// Now there are some get parameters function for this calss:const string &getName() const { return this->name; }int getAge() const { return this->age; }int getScore() const { return this->socre; }private:string name;int age;int socre;
};// Return whether modulus of elem1 is less than modulus of elem2
bool mod_lesser(int elem1, int elem2)
{// If elem1 is negative, make it positiveif (elem1 < 0)elem1 = -elem1;// If elem2 is negative, make it positiveif (elem2 < 0)elem2 = -elem2;// Return whether elem1 is less than elem2return elem1 < elem2;
}// This is a absolute value comparison function.
template <typename T>
bool compareAbsoluteValue(T elem1, T elem2)
{return abs(elem1) < abs(elem2);
}/*** @brief This case if copy from the link :* @link https://learn.microsoft.com/zh-cn/cpp/standard-library/algorithm-functions?view=msvc-170#binary_search @endlink*/
void MicosoftSTLCase()
{using namespace std;// create a list of intslist<int> List1;// insert values into the listList1.push_back(50);List1.push_back(10);List1.push_back(30);List1.push_back(20);List1.push_back(25);List1.push_back(5);// sort the listList1.sort();// output the sorted listcout << "List1 = ( ";for (auto Iter : List1)cout << Iter << " ";cout << ")" << endl;// default binary search for 10if (binary_search(List1.begin(), List1.end(), 10))cout << "There is an element in list List1 with a value equal to 10."<< endl;elsecout << "There is no element in list List1 with a value equal to 10."<< endl;// a binary_search under the binary predicate greaterList1.sort(greater<int>());if (binary_search(List1.begin(), List1.end(), 10, greater<int>()))cout << "There is an element in list List1 with a value greater than 10 "<< "under greater than." << endl;elsecout << "No element in list List1 with a value greater than 10 "<< "under greater than." << endl;// a binary_search under the user-defined binary predicate mod_lesser// create a vector of intsvector<int> v1;// insert values into the vectorfor (auto i = -2; i <= 4; ++i){v1.push_back(i);}// sort the vectorsort(v1.begin(), v1.end(), mod_lesser);// output the sorted vectorcout << "Ordered using mod_lesser, vector v1 = ( ";for (auto Iter : v1)cout << Iter << " ";cout << ")" << endl;// binary search for -3if (binary_search(v1.begin(), v1.end(), -3, mod_lesser))cout << "There is an element with a value equivalent to -3 "<< "under mod_lesser." << endl;elsecout << "There is not an element with a value equivalent to -3 "<< "under mod_lesser." << endl;// The extra section uses a custom absolute value comparison function flow.// create a vector of doublesvector<double> v2;for (auto i = -2.5; i <= 4.5; i += 0.5){v2.push_back(i);}// sort the vectorsort(v2.begin(), v2.end(), compareAbsoluteValue<double>);// output the sorted vectorcout << "Ordered using compareAbsoluteValue<double>, vector v2 = ( ";for (auto Iter : v2)cout << Iter << " ";cout << ")" << endl;// binary search for 0.0if (binary_search(v2.begin(), v2.end(), 0.0, compareAbsoluteValue<double>))cout << "There is an element with a value equivalent to 0.0 "<< "under compareAbsoluteValue<double>." << endl;elsecout << "There is not an element with a value equivalent to 0.0 "<< "under compareAbsoluteValue<double>." << endl;
}/*** @brief This case if copy from the link :* @link https://en.cppreference.com/w/cpp/algorithm/binary_search @endlink*/
void cppRefCase()
{vector<int> haystack{1, 3, 4, 5, 9};vector<int> needles{1, 2, 3};for (const auto needle : needles){cout << "Searching for " << needle << '\n';if (binary_search(haystack.begin(), haystack.end(), needle))cout << "Found " << needle << '\n';elsecout << "No dice!\n";}
}bool myfunction(int i, int j) { return (i < j); }/*** @brief This case if copy from the link :* @link https://cplusplus.com/reference/algorithm/binary_search/ @endlink*/
void cppPlusCase()
{int myints[] = {1, 2, 3, 4, 5, 4, 3, 2, 1};vector<int> v(myints, myints + 9); // 1 2 3 4 5 4 3 2 1// using default comparison:sort(v.begin(), v.end());cout << "looking for a 3... ";if (binary_search(v.begin(), v.end(), 3))cout << "found!\n";elsecout << "not found.\n";// using myfunction as comp:sort(v.begin(), v.end(), myfunction);cout << "looking for a 6... ";if (binary_search(v.begin(), v.end(), 6, myfunction))cout << "found!\n";elsecout << "not found.\n";
}// Define a template binary_search_function:
template <typename Iterator, typename T>
Iterator binarySearch(Iterator begin, Iterator end, const T &key)
{const Iterator NotFound = end;while (begin < end){const Iterator Middle = begin + (distance(begin, end) / 2);if (*Middle == key){return Middle;}else if (*Middle > key){end = Middle;}else{begin = Middle + 1;}}return NotFound;
}void templateSearchCase()
{vector<int> haystack{1, 3, 4, 5, 9};vector<int> needles{1, 2, 3};const auto &hayStackEnd = haystack.end();for (const auto needle : needles){cout << "Searching for " << needle << '\n';if (binarySearch<vector<int>::iterator, int>(haystack.begin(), hayStackEnd, needle) != hayStackEnd)cout << "Found " << needle << '\n';elsecout << "No dice!\n";}vector<double> dHaystack{1.1, 1.3, 1.4, 1.5, 1.9, 2.0, 2.2, 2.3, 3.0, 3.14};vector<double> dNeedles{1.1, 1.3, 1.5, 1.9, 2.0, 3.0, 3.14, 4.0};const auto &dHayStackEnd = dHaystack.end();for (const auto needle : dNeedles){cout << "Searching for " << needle << '\n';if (binarySearch<vector<double>::iterator, double>(dHaystack.begin(), dHayStackEnd, needle) != dHayStackEnd)cout << "Found " << needle << '\n';elsecout << "No dice!\n";}vector<Person> pHaystack{Person{"Jonah", 20, 75}, Person{"Albert", 45, 88}, Person{"Brenda", 60, 96}, Person{"Christina", 18, 110}, Person{"David", 19, 95}};vector<Person> pNeedles{Person{"Albert", 45, 88}, Person{"Christina", 18, 110}, Person{"David", 88, 95}, Person{"Erica", 50, 91}};const auto &pHayStackEnd = pHaystack.end();for (const auto &needle : pNeedles){cout << "Searching for " << needle.getName() << '\n';if (binarySearch<vector<Person>::iterator, Person>(pHaystack.begin(), pHaystack.end(), needle) != pHaystack.end())cout << "Found " << needle.getName() << '\t' << needle.getAge() << '\t' << needle.getScore() << '\n';elsecout << "No dice!\n";}
}template <typename T>
int binarySearchVec(const vector<T> &arr, T target)
{int low = 0;int high = arr.size() - 1;while (low <= high){int mid = low + (high - low) / 2; // 防止溢出if (arr[mid] == target){return mid;}else if (arr[mid] < target){low = mid + 1;}else{high = mid - 1;}}return -1;
}int main(int argc, char *argv[])
{MicosoftSTLCase();cppRefCase();cppPlusCase();templateSearchCase();return 0;
}

个人格言

追寻与内心共鸣的生活,未来会逐渐揭晓答案。

Pursue the life that resonates with your heart, and the future will gradually reveal the answer.

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

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

相关文章

【C++】STL简介 | STL六大组件 | string类 | string类对象操作

目录 1. 什么是STL 2. STL的版本 3. STL的六大组件 4. STL的缺陷 5. 引出string类 6. 标准库中的string类 6.1 string类简介 6.2 string类对象的构造 6.3. string类对象的容量 6.4. string类对象的遍历 6.5. string类对象的修改 6.6. string类非成员函数 6.7. vs…

使用滤镜属性将网页从彩色变黑白

在某些情况下&#xff0c;例如为了表达哀悼或纪念&#xff0c; 许多网站会将页面颜色从彩色调整为黑白灰色。我到网上查找答案&#xff0c;发现有些是通过javascript或jQuery实现的&#xff0c;我试了一下居然无效。 后来找到一个方法&#xff0c;是仅用一行CSS代码就能搞定的&…

基于CNN-LSTM-Attention的时间序列回归预测matlab仿真

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 4.1卷积神经网络&#xff08;CNN&#xff09;在时间序列中的应用 4.2 长短时记忆网络&#xff08;LSTM&#xff09;处理序列依赖关系 4.3 注意力机制&#xff08;Attention&#xff09; 5…

MySQL 学习记录 2

原文&#xff1a;https://blog.iyatt.com/?p13818 13 存储引擎 查看一下前面创建的一张表的创建语句&#xff0c;当时并没有显式指定引擎&#xff0c;MySQL 自动指定的 InnoDB&#xff0c;即默认引擎是这个。 创建表的时候要显式指定引擎可以参考这个语句 查看当前 MySQL …

【牛客】SQL130 试卷发布当天作答人数和平均分

描述 现有用户信息表user_info&#xff08;uid用户ID&#xff0c;nick_name昵称, achievement成就值, level等级, job职业方向, register_time注册时间&#xff09;&#xff0c;示例数据如下&#xff1a; iduidnick_nameachievementleveljobregister_time11001牛客1号31007算…

rke方式安装k8s集群

一、新机环境准备 1.1主机名设置 hostnamectl set-hostname XXX1.2 主机名与ip地址解析 vim /etc/hosts 192.168.0.140 rke 192.168.0.147 master1 192.168.0.152 node1 192.168.0.153 node21.3安装docker tar -xf docker-20.10.24.tgz cp ${SHELL_FOLDER}/docker/*…

【java】19:内部类(3)

成员内部类&#xff1a; 1.可以直接访问外部类的所有成员&#xff0c;包含私有的 class Outer01{//外部类 private int n1 10; public String name "张三"; class Innter01{ public void say0(){ System.out.println("Outer01 的n1 " n1 " outer…

JWT基于Cookie的会话保持,并解决CSRF问题的方案

使用JWT进行浏览器接口请求&#xff0c;在使用Cookie进行会话保持传递Token时&#xff0c;可能会存在 CSRF 漏洞问题&#xff0c;同时也要避免在产生XSS漏洞时泄漏Token问题&#xff0c;如下图在尽可能避免CSRF和保护Token方面设计了方案。 要点解释如下&#xff1a; 将JWT存入…

Snagit 2024:让你的屏幕活动瞬间变得生动有力 mac/win版

Snagit 2024 屏幕录制与截图软件是一款功能强大的工具&#xff0c;专为现代用户设计&#xff0c;以满足他们在工作、学习和娱乐中对屏幕内容捕捉和分享的需求。这款软件结合了屏幕录制和截图功能&#xff0c;为用户提供了一种高效、便捷的方式来捕捉屏幕上的精彩瞬间。 Snagit…

xxl-job--01--简介

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 1.xxl-job1. 1 发展历史1.2 XXL-JOB的系统架构1.3 xxl-job与其他框架对比 2. XXL-JOB的使用2.1 准备工作- 配置调度中心XXL-JOB的数据表 2.2 配置执行器1 引入依赖包…

vue cesium加载点与定位到指定位置

vue cesium定位到指定位置 window.viewer.camera.flyTo({destination: Cesium.Cartesian3.fromDegrees(point.longDeg, point.latDeg, 6500000), orientation: {heading: 6.2079384332084935, roll: 0.00031509431759868534, pitch: -1.535}, duration: 3})vue cesium加载点 …

【蛀牙】如何选择牙刷,牙膏和牙杯(含其他日常牙具:牙线,漱口水,冲牙器)

程序员生活指南之 【蛀牙】如何选择牙刷&#xff0c;牙膏和牙杯&#xff08;含其他日常牙具&#xff1a;牙线&#xff0c;漱口水&#xff0c;冲牙器&#xff09; 文章目录 一、如何选择牙刷&#xff0c;牙膏和牙杯1、如何选择牙刷2、如何选择牙膏3、如何选择牙杯 二、日常牙具&…

为什么HashMap的键值可以为null,而ConcurrentHashMap不行?

写在开头 今天在写《HashMap很美好&#xff0c;但线程不安全怎么办&#xff1f;ConcurrentHashMap告诉你答案&#xff01;》这篇文章的时候&#xff0c;漏了一个知识点&#xff0c;知道晚上吃饭的时候才凸显想到&#xff0c;关于ConcurrentHashMap在存储Key与Value的时候&…

【Java】面向对象之多态超级详解!!

文章目录 前言一、多态1.1 多态的概念1.2 多态的实现条件1.3 重写1.3.1方法重写的规则1.3.2重写和重载的区别 1.4 向上转型和向下转型1.4.1向上转型1.4.2向下转型 1.5 多态的优缺点1.5.1 使用多态的好处1.5.2 使用多态的缺陷 结语 前言 为了深入了解JAVA的面向对象的特性&…

基于yolov5的电瓶车和自行车检测系统,可进行图像目标检测,也可进行视屏和摄像检测(pytorch框架)【python源码+UI界面+功能源码详解】

功能演示&#xff1a; 基于yolov5的电瓶车和自行车检测系统_哔哩哔哩_bilibili &#xff08;一&#xff09;简介 基于yolov5的电瓶车和自行车检测系统是在pytorch框架下实现的&#xff0c;这是一个完整的项目&#xff0c;包括代码&#xff0c;数据集&#xff0c;训练好的模型…

协程库项目—日志模块

日志模块程序结构图 sylarLog ├── LogLevel&#xff08;日志级别封装类&#xff09; │ ├── 提供“从日志级别枚举值转换到字符串”、“从字符串转换相应的日志级别枚举值”等方法 ├── LogEvent&#xff08;日志事件类&#xff09; │ ├── 封装日志事件的属性…

Unity(第二十一部)动画的基础了解(感觉不了解其实也行)

1、动画组件老的是Animations 动画视频Play Automatically 是否自动播放Animate Physics 驱动方式&#xff0c;勾选后是物理驱动Culling Type 剔除方式 默认总是动画化就会一直执行下去&#xff0c;第二个是基于渲染播放&#xff08;离开镜头后不执行&#xff09;&#xff0c; …

高性能服务系列【二】CPU和内存

现代计算机的系统架构十分复杂。在服务器中&#xff0c;双路处理器已经十分常见。最近Arm处理器实现双路共384核心&#xff0c;要知道目前Linux内核最高只支持256核&#xff0c;这就有点尴尬。 多路处理器将越来越普遍&#xff0c;对性能的影响和传统架构有不小的差别&#xf…

MySQL中json类型的字段

有些很复杂的信息&#xff0c;我们一般会用扩展字段传一个json串&#xff0c;字段一般用text类型存在数据库。mysql5.7以后支持json类型的字段&#xff0c;还可以进行sql查询与修改json内的某个字段的能力。 1.json字段定义 ip_info json DEFAULT NULL COMMENT ip信息, 2.按…

GO基本类型一些记录

基本类型一些记录 1.直接粘贴文本进println(" ")2.中文字符串长度别用len( )3.byte本质是uint8 1.直接粘贴文本进println(" ") GoLand会自动补充转义符 2.中文字符串长度别用len( ) 用相应编码库的方法&#xff0c;一般utf8即可 utf8.RuneCountInStrin…