【算法笔记】二分查找 二分答案 (超详细解析,一篇让你搞懂二分)

【算法笔记】二分查找 && 二分答案(超详细解析,一篇让你搞懂二分)

目录

  • 【算法笔记】二分查找 && 二分答案(超详细解析,一篇让你搞懂二分)
  • 前言
  • 一、什么是二分查找?为什么要用二分查找?
    • 1.1.基本概念
    • 1.2.为什么要用二分查找
    • 1.3.图解
    • 1.4.优缺点
  • 二、二分查找写法
    • 2.1.`while(l<r)`
    • 2.2.`while(l<=r)`
    • 2.2.代码细节解析
    • 2.3 例题
  • 三、二分答案
    • 3.1.什么是二分答案
    • 3.2.二分查找和二分答案有什么区别
    • 3.3.二分答案模板
    • 3.4.什么时候该使用二分答案
    • 3.5.怎样二分答案
    • 3.6.例题
  • 四、浮点数二分(实数二分)
    • 4.1.模板
    • 4.2 例题
  • 五、lower_bound 和 upper_bound
  • 六、练习题
  • 七、总结


前言

二分查找应该算是是很多人入门的第一个算法吧,无论是ACM还是蓝桥杯都是必学的算法,很多人都觉得其非常简单,但它真的那么简单吗? Knuth 大佬(发明 KMP 算法的那位)曾说过:

Although the basic idea of binary search is comparatively straightforward, the details can be surprisingly tricky…(思路很简单,细节是魔鬼)

本文将为大家详细讲解二分查找的原理和使用场景

并且,我们就是要深入细节,我将从while循环中该不该带=mid该不该+1等地方分析这些细节的差异及出现差异的原因,保证你能灵活准确的写出二分查找算法。


一、什么是二分查找?为什么要用二分查找?

1.1.基本概念

二分查找,也称为折半查找(Binary Search),是一种在有序数组中查找特定元素的搜索算法。它的基本思想是将目标值与数组中间的元素进行比较,如果目标值小于中间元素,则在数组的左半部分继续查找,否则在右半部分查找,不断缩小搜索范围,直到找到目标值或确定目标值不存在为止。二分查找的时间复杂度为O(logn),即查找效率较高。

1.2.为什么要用二分查找

想在数组中查找一个元素,最朴素的做法便是从头到尾遍历数组中的每个元素,该操作时间复杂度为O(n),当数组元素较多时会TLE,而二分查找只需O(logn)的时间复杂度便可完成对有序数组的查找,大大提高了算法的效率。
在这里插入图片描述

1.3.图解

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

1.4.优缺点

优点:

  • 比较次数少,查找速度快,效率高

缺点:

  • 必须要求待查表为有序表
  • 插入删除困难

二、二分查找写法

2.1.while(l<r)

尽量向左找目标

//在a数组中寻找第一个>=x的数,并返回其下标
int bsearch_1(int l, int r)
{while (l < r){int mid = l + r >> 1;//或写成(l+r)/2if (a[mid]>=x) r = mid;else l = mid + 1;}return l;//因为while循环当l==r时终止,return r也可以
}

尽量向右找目标

//在a数组中寻找最后一个<=x的数,并返回其下标
int bsearch_2(int l, int r)
{while (l < r){int mid = l + r + 1 >> 1;//或写成(l+r+1)/2if (a[mid]<=x) l = mid;else r = mid - 1;}return l;//因为while循环当l==r时终止,return r也可以
}

2.2.while(l<=r)

尽量向左找目标

//在a数组中寻找第一个>=x的数,并返回其下标
int bsearch_1(int l, int r)
{int res=0x3f3f3f3f//可以先将res设置为一个很大的数,如果while循环终止后res没变,便没找到while (l <= r){int mid = l + r >> 1;//或写成(l+r)/2if (a[mid]>=x){r = mid-1;res=mid;} else l = mid + 1;}return res;
}

尽量向右找目标

//在a数组中寻找最后一个<=x的数,并返回其下标
int bsearch_2(int l, int r)
{int res=0x3f3f3f3f//可以先将res设置为一个很大的数,如果while循环终止后res没变,便没找到while (l <= r){int mid = l + r >> 1;//或写成(l+r)/2if (a[mid]<=x){l = mid+1;res=mid;} else r = mid - 1;}return res;
}

2.2.代码细节解析

首先,区间是有单调性的,我们想查找目标值第一次出现的位置,如果查到一个值比目标值大,就把右半边丢掉,因为右半边肯定也比目标值大;同样,如果查到值比目标值小,那就丢掉左半边。

1.l+r>>1什么意思?
这个是位运算写法,将整数类型的二进制表示右移一位,等同于(l+r)/2注意,double类型不适用这一点

2. 循环条l<=rl<r 有什么区别?
本质上没什么区别,只是循环终止条件不同,个人建议用l<r,代码简便,清晰明了。

3.为什么当循环条件为l<=r时要用res来记录答案,为l<r时直接返回l或``r即可?
因为当循环条件为l<r时,在循环的过程中,可以发现当mid满足条件时l和r的更新方式都是l=midr=mid,而不是l=mid+1r=mid-1,在这个过程l/r已经替代res完成了记录并更新答案的过程,便不需要额外再用一个变量来存储答案,当循环终止时l==r,此时的l/r就在答案位置,直接输出l/r即可;而当循环条件为l<=r时,每次mid满足条件lr都会更新到mid的下一位置,所以此时就需要一个res变量存储满足条件的位置(mid)并不断更新,res最后一次更新的值便是答案,并且while(l<=r)终止时lr不相等,不方便通过lr返回答案

4.为什么当循环条件为l<rmid的表达式是(l+r+1)/2?什么时候需要加1什么时候不需要加?
至于什么时候需要加1,就记住就可以了,

while循环条件写的是l<r时,如果是l=mid,就需要加1,如果是r=mid就不需要。

至于为什么呢,大家都知道/是向下取整,那么可以想一个极端的时候,当l=r-1的时候,此时区间为[l,r], mid=(l+r)/2=(l+l+1)/2=l, l=mid,更新后区间变为[mid,r],也就是[l,r],和原来一模一样,然后就会陷入无尽的死循环;而如果+1的话,mid=(l+r+1)/2=(l+l+1+1)/2=r,区间变为[r,r]循环终止,避免了死循环。而正因为/是向下取整,所以当r=mid时不+1也会直接更新成[l,l],所以不用加。

5.为什么当循环条件为l<r 时会出现l=mid/r=mid的时候,什么时候l=mid+1/r=mid-1,什么时候l=mid/r=mid?
这个要看实际情况,可以参考我代码上的两种情况,我分别给大家详细解析一下

//在a数组中寻找第一个>=x的数
a={1,2,2,2,3,3,3,4,5}  x=3
初始状态: l=0  r=8  mid=(l+r)/2=4,答案区间[0,8]
此时a[mid]=a[4]=3>=3(符合条件),这个时候的mid也是满足答案的,
所以答案区间应该更新为[0,4]而不是[0,4-1],所以应该是r=mid而不是r=mid-1
其实我这个样例显而易见,如果r=mid-1,那么直接就WA了,你直接把正确答案给扔出区间了我们继续对[0,4]进行二分,此时mid=(0+4)/2=2;
此时的a[mid]=a[2]=2<x,此时的mid不满足答案,这个数和它之前的数就可以直接排除了,
那他就不应该在答案区间中继续二分,下一次我们应该从mid的下一个数开始找
即答案区间应更新为[2+1,4],所以应该是l=mid+1
这个时候如果你写的是l=mid,那么会直接喜提死循环
//在a数组中寻找最后一个<=x的数
和上面的其实一样,我再举个样例
a={0,1,1,1,2,2,3,4,5} x=2
初始状态: l=0  r=8  mid=(l+r)/2=4,答案区间[0,8]
此时a[mid]=a[4]=2<=2(符合条件),这个时候的mid也是满足答案的,
所以答案区间应该更新为[4,9]而不是[4+1,9],所以应该是l=mid而不是l=mid+1
然后这时候你可以顺势把上面写的(l+r)/2 改成 (l+r+1)/2  //不改一会就死循环
这又是一个极端的样例,如果l=mid+1,那么直接就WA了,你直接把正确答案给扔出区间了继续二分,[4,9],mid=(4+9+1)/2=7;
此时a[mid]=a[7]=4>x ,不满足答案,直接排除,从它的下一个数开始找
答案区间更新为[4,7-1],所以是r=mid-1
同样,如果你写的是l=mid,那么喜提死循环

如果题里是找第一个>x的数,就直接把>=改成> 就好了,灵活一点

2.3 例题

(一)P2249 【深基13.例1】查找
在这里插入图片描述

这题就是道二分查找的板题,相信大家都能写出来,直接奉上AC代码

#include<iostream>
using namespace std;
const int N=1000010;
int a[N],x,q,n;int main(){cin>>n>>q;for(int i=1;i<=n;i++) cin>>a[i];while(q--){cin>>x;int l=1,r=n; //左右边界 while(l<r) //因为是找第一次出现的位置,那就是尽量往左来,就用模板1 {int mid=l+r>>1;if(a[mid]>=x) r=mid; //判断条件,如果值大于等于目标值,说明在目标值右边,尽量往左来else l=mid+1;}if(a[l]!=x){ //如果找不到这个值 cout<<-1<<' ';continue;}cout<<l<<" ";}return 0;
} 

(二)P1102 A-B 数对

在这里插入图片描述

思路:给出了C,我们要找出A和B。我们可以遍历数组,即让每一个值先变成B,然后二分找对应的A首次出现位置,看是否能找到。
如果找到A,那就二分找最后出现的位置,继而,求出A的个数,即数对的个数。
注意:
int: 2的30次方 ;long long:2的60次方,因此要开long long
(不开long long 见祖宗…)

ACcode

#include<iostream>
#include<algorithm>
using namespace std;const int N=200010;
long long a[N],n,c,res,st;int main(){cin>>n>>c;for(int i=1;i<=n;i++) cin>>a[i];sort(a+1,a+1+n);	//先排序 for(int i=1;i<n;i++)	//遍历每一个B {int l=i+1,r=n;	//寻找A第一次出现的位置,使得A-B=C while(l<r) //因为是第一次出现,尽量往左找{int mid=l+r>>1;if(a[mid]-a[i]>=c) r=mid;	//判断:在目标值的右边,满足,往左来else l=mid+1;}if(a[l]-a[i]==c) st=l; //能找到C就继续 else continue;l=st-1,r=n;	//查找A最后出现的位置 while(l<r) //因为是最后一次出现,尽量往右找{int mid=l+r+1>>1;if(a[mid]<=a[st]) l=mid; //判断:在目标值的左边,满足,往右去else r=mid-1;}res+=l-st+1;	//最后出现的位置减首次出现的位置就是区间长度,即A的个数 }cout<<res;return 0;
} 

三、二分答案

3.1.什么是二分答案

对于一个问题,它的答案属于一个区间,当这个区间很大时,暴力超时。但重要的是——这个区间是对题目中的某个量有单调性的,此时,我们就会二分答案。每一次二分会做一次判断(check函数),看是否对应的那个量达到了需要的大小,如果满足check,就放弃右半区间(或左半区间),如果不满足,就放弃左半区间(或右半区间)。一直往复,直至到最终的答案。

这么解释可能比较抽象,大家可以回想一下高中做选择题的时候,如果不会做,这个时候你是不是会运用排除法,把四个选项挨个往里带,如果A B C D是递增的,有时候是不是你把C带进去不对的同时把D也同时排除了

举个例子
3+()=7
A.2 B.4 C.9 D.11 E.12 F.17 ...
不会就选C(bushi) ,我们先从C代,把C带进去都>7了,那C后面的选项一定都是错的,可以直接排除

这个过程其实就是一个简单的二分答案,排除答案的过程就是check的过程

3.2.二分查找和二分答案有什么区别

二分查找:在一个已知的有序数据集上进行二分地查找
二分答案:答案有一个区间,在这个区间中二分,直到找到最优答案

3.3.二分答案模板

尽量往左找答案

//求最小的最大值
int bsearch_1(int l, int r)
{while (l < r){int mid = l + r >> 1;if (check(mid)) r = mid;else l = mid + 1;}return l;
}

尽量往右找答案

//求最大的最小值
int bsearch_2(int l, int r)
{while (l < r){int mid = l + r + 1 >> 1;if (check(mid)) l = mid;else r = mid - 1;}return l;
}

你会发现二分答案其实只是把二分查找的条件改成了check,这个就是二分的通用模板,可以直接套板子应对绝大部分二分问题

3.4.什么时候该使用二分答案

1、答案在一个区间内(一般情况下,区间会很大,暴力超时)
2、直接搜索不好搜,但是容易判断一个答案可行不可行
3、该区间对题目具有单调性,即:在区间中的值越大或越小,题目中的某个量对应增加或减少。
4、关键词:求…最大值的最小值 、 求…最小值的最大值、求满足条件下的最小(大)值、 求最靠近一个值的值、 求最小的能满足条件的代价…

3.5.怎样二分答案

1.找具有单调性的答案区间(l= …,r=…)

如何理解答案的单调性呢?可以见下图,该图表示一个区间,从左到右要实现的代价逐渐增高,我们要在这个区间里找到一个代价最小的满足条件的答案。观察图可以发现,位于答案左边的区间不满足条件,位于答案右边的区间满足条件,像这种要么一边不满足,要么一边都满足的现象就是答案的单调性。

在这里插入图片描述
答案的单调性大多数情况下可以转化为一个函数,其单调性证明多种多样,如下:

移动石头的个数越多,答案越大(NOIP2015跳石头)。
前i天的条件一定比前 i + 1 天条件更容易(NOIP2012借教室)。
满足更少分配要求比满足更多的要求更容易(NOIP2010关押罪犯)。
满足更大最大值比满足更小最大值的要求更容易(NOIP2015运输计划)。
时间越长,越容易满足条件(NOIP2012疫情控制)。

2.设计check函数
这个具体题目具体分析,可以看例题感受一下,究其根本还是要多做题,无他,唯手熟尔

3.套模板
不用我多说了吧

3.6.例题

(一)P1873 [COCI 2011/2012 #5] EKO / 砍树
在这里插入图片描述
这道题目本质上就是要求一个最大高度的问题,使得砍到的树的总长度满足条件,拿样例 1 举例,展示出来就是这样的:

在这里插入图片描述
假设 ans 是满足条件的最大高度,那么在图片中展示就是这样的:
在这里插入图片描述
大于 ans 的高度,不满足条件,小于ans 的高度,都满足条件,这样的答案具有 单调性,所以考虑使用 二分答案 。
解题思路:
1.找有单调性的答案区间:
我们要求一个满足条件的最大高度,那么答案的范围就应该是从 0 到树的最大高度 height,也就是 [0,height]
2.设计check函数
可以看出来这题是尽量向右(向上)找答案,这道题我们可以用一个变量sum存储得到的总长度,如果 sum 大于等于 m 就直接返回true,否则返回false.
3.套模板
ACcode

#include<iostream>
using namespace std;
const int N = 1e6 + 10;
int n, m, a[N];bool check(int x){int sum = 0;for (int i = 1; i <= n;i++){if(a[i]>x)sum += a[i] - x;if(sum>=m)return true;}return false;
}int main(){cin >> n >> m;int height = 0;for (int i = 1; i <= n;i++){cin >> a[i];height = max(height, a[i]);//记录最大高度}int l = 0, r = height;while(l<r){int mid = l + r + 1 >> 1;if(check(mid))//判断是否达到要求的木材总量m,更新区间 l = mid;elser = mid - 1;}cout << l << endl;return 0;
}

(二)P1182 数列分段 Section II
在这里插入图片描述
解题思路:

这就是一道求最小的最大值的题

1.找有单调性的答案区间:
要求最大值最小,则该值必定大于数列中的最大值(最大值单独为一段的时候),最大值则为数列中所有数之和​。所以最大值的区间 left=数列中的最大值,right=数列的所有数之和。
2.设计check函数
需要两个变量,sum表示当前这段中的数字总和,cnt表示数列目前分了多少段,因为从a[0]一开始就是第一段,cnt最少是一段,所以初始赋值为1,具体见代码注释。
3.套模板
ACcode

#include<iostream>
using namespace std;
const int N = 1e5 + 10;
long long n, m, a[N], maxn, sum;bool check(long long x){long long sum = 0, cnt = 1;//sum表示当前这段中的数字总和,cnt表示数列目前分了多少段for (int i = 0; i < n;i++){if(sum+a[i]<=x)//判断a[i]是否可以放入当前这段sum += a[i];// //sum+a[i]不超过mid则可以继续连接else{sum = a[i];//否则a[i]自起一段,数列总段数cnt+1cnt++;}}if(cnt<=m)return true;return false;
}int main(){cin >> n >> m;for (int i = 0; i < n;i++){cin >> a[i];maxn = max(maxn, a[i]);//maxn表示数组中元素的最大值sum += a[i];//sum表示数列中所有元素之和}long long l = maxn, r = sum;while(l<r){long long mid = l + r >> 1;if(check(mid))//更新区间r = mid;elsel = mid + 1;}cout << l;return 0;
}

(三)P1824 进击的奶牛
在这里插入图片描述
解题思路

这就是一道求最大的最小值的题

1.找有单调性的答案区间:
l为1,就是紧挨着,ra[n-1]-a[0];
2.设计check函数
和上一道题差不多,具体看注释
3.套模板
ACcode

#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1e5 + 10;
long long n, m, a[N], maxn;bool check(long long x){int idx = 0, cnt = 1;//index为当前坐标,cnt为奶牛数量  for (int i = 1; i < n;i++){if(a[i]-a[idx]>=x){//判断a[i]和当前坐标之前的差值 idx = i;//条件成立,更新坐标,奶牛数量+1 cnt++;}}if(cnt>=m)return true;return false;
}int main(){cin >> n >> m;for (int i = 0; i < n;i++){cin >> a[i];}sort(a, a + n);//二分的题,一定不要忘了排序long long l = 1, r = a[n - 1] - a[0];//最小值为1,就是紧挨着 ,最大值为最后一个坐标减第一个坐标while(l<r){long long mid = l + r + 1 >> 1;if(check(mid))l = mid;elser = mid - 1;}cout << l;return 0;
}

四、浮点数二分(实数二分)

4.1.模板

思路和整数二分一样,区别是浮点型二分不需要注意边界问题(也就是不需要+1/-1)

	while(r-l>1e-5) //需要一个精度保证{double mid = (l+r)/2;if(check(mid)) l=mid; //或r=mid;else r=mid; //或l=mid;}

4.2 例题

(一)数的三次方根
题目描述

给定一个浮点数n,求它的三次方根。

输入格式
共一行,包含一个浮点数n。

输出格式
共一行,包含一个浮点数,表示问题的解。

注意,结果保留6位小数。

数据范围
−10000≤n≤10000

输入样例:

1000.00

输出样例:

10.000000

ACcode

#include<iostream>
using namespace std;
int main()
{double x;cin>>x;double l=-100,r=100;//根据题目范围 开三次方根 估计答案大概范围while(r-l>1e-8){double mid=l+r>>1;if(mid*mid*mid>=x)r=mid;elsel=mid;}printf("%.6lf\n",l);return 0;
}

五、lower_bound 和 upper_bound

这两个是C++的STL库封装的两个二分查找函数,使用时需要加上头文件

#include<algorithm>

使用方法和sort函数很像,可以用greater< type > 重载,也可以自定义比较函数,具体可以翻阅相关博客,在这里只简单介绍一下基本的使用方法
upper_bound

upper_bound(begin, end, value)
//在从小到大的排好序的数组中,在数组的 [begin, end) 区间中二分查找第一个大于value的数,找到返回该数字的地址,没找到则返回end。upper_bound(begin, end, value, greater<int>())
//在从大到小的排好序的数组中,在数组的 [begin, end) 区间中二分查找第一个小于value的数,找到返回该数字的地址,没找到则返回end。

lower_bound

lower_bound(begin, end, value)
//在从小到大的排好序的数组中,在数组的 [begin, end) 区间中二分查找第一个大于等于value的数,找到返回该数字的地址,没找到则返回end。lower_bound(begin, end, value, greater<int>())
//在从大到小的排好序的数组中,在数组的 [begin, end) 区间中二分查找第一个小于等于value的数,找到返回该数字的地址,没找到则返回end。

注意,返回的是地址,不是下标,也不是数值!!!

如果你直接输出,那么你会得到这样一串东西(地址)
在这里插入图片描述

接下来举个例子,讲解一下在实际中怎么用

#include <iostream>
#include <algorithm>
using namespace std;
int main(){int a[] = {1, 2, 3, 3, 3, 4, 4, 5, 6};int x = 3;//查找第一个>x的元素,并输出其下标cout << upper_bound(a, a + 9, x) - a;   //输出 5(下标)// 查找第一个>x的元素,并输出其数值auto i = upper_bound(a, a + 9, x);cout << *i;                             //输出4(数值)//查找第一个>=x的元素,并输出其下标cout << lower_bound(a, a + 9, x) - a;   //输出 2(下标)// 查找第一个>=x的元素,并输出其数值auto j = lower_bound(a, a + 9, x);cout << *j;                             //输出3(数值)
}

该函数的前两个参数及其重载方式和sort()函数差不多,如果不了解的话可以去学习一下sort()的相关语法,类比一下就懂了

六、练习题

相信大家已经正式入门二分了,接下来可以做几道题练习一下,题解后续更新。

P1678 烦恼的高考志愿
P1163 银行贷款
P8647 [蓝桥杯 2017 省 AB] 分巧克力
P1024 [NOIP2001 提高组] 一元三次方程求解
P2440 木材加工
P1577 切绳子
P2678 [NOIP2015 提高组] 跳石头
1460. 我在哪?
102. 最佳牛围栏


七、总结

二分模板一共有两个,分别适用于不同情况。
算法思路:假设目标值在闭区间[l, r]中, 每次将区间长度缩小一半,当l = r时,我们就找到了目标值。

当我们将区间[l, r]划分成[l, mid][mid + 1, r]时,其更新操作是r = mid或者l = mid + 1;,计算mid时不需要加1。

int bsearch_1(int l, int r)
{while (l < r){int mid = l + r >> 1;if (check(mid)) r = mid;else l = mid + 1;}return l;
}

当我们将区间[l, r]划分成[l, mid - 1][mid, r]时,其更新操作是r = mid - 1或者l = mid;,此时为了防止死循环,计算mid时需要加1。

int bsearch_2(int l, int r)
{while (l < r){int mid = l + r + 1 >> 1;if (check(mid)) l = mid;else r = mid - 1;}return l;
}

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

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

相关文章

vite+vue3快速构建项目+router、vuex、scss安装

安装 Vite npm install -g create-vite-app创建vue3项目 npm init vitelatestnpm i安装依赖 安装veux、router npm install vue-router vuex新建router/index.js&#xff08;自己创建home、login对应页面文件&#xff09; import { createRouter, createWebHistory } from…

python-游戏自动化(三)(实战-豆腐女孩)

前提准备 特别注意&#xff1a; 本节教程所演示的模拟器分辨率设置为 720x1080&#xff08;手机版&#xff09;&#xff0c;电脑分辨率设置大720x1080并且没有设置放大。 今天的课程开始之前我们来回顾一下昨天所学的知识内容&#xff0c;因为今天要学的内容和昨天内容…

828华为云征文 | 华为云Flexusx实例,高效部署Servas书签管理工具的优选平台

前言 华为云Flexus X实例&#xff0c;Servas书签管理工具部署的优选平台&#xff01;828节日特惠&#xff0c;让高效管理您的知识宝藏触手可及。Flexus X实例以其卓越的算力、灵活的资源配置和智能调优技术&#xff0c;为Servas提供了稳定、高效的运行环境。无论是快速访问、安…

链表相关OJ

目录 1、移除链表元素 &#xff08;1&#xff09;题目描述 &#xff08;2&#xff09;算法原理 2、链表的中间结点 &#xff08;1&#xff09;题目描述 &#xff08;2&#xff09;算法原理 3、链表中倒数第K个结点 &#xff08;1&#xff09;题目描述 &#xff0…

ssrf漏洞利用+CTF实例

引发ssrf漏洞的几个函数 file_get_contents() 把整个文件读入一个字符串中&#xff0c;获取本地或者远程文件内容fsockopen() 获得套接字信息curl_exec() 执行一个curl会话&#xff0c;由curl_init()初始化一个新的会话&#xff0c;返回一个curl句柄fopen() 打开文件或者URLre…

【C语言】内存函数详细讲解

文章目录 前言strerror的声明和使用字符串分类函数字符转换函数内存拷贝函数&#xff08;memcpy)memcpy的声明和使用memcpy函数的模拟实现 内存拷贝函数&#xff08;memmove&#xff09;memmove的声明和使用memmove模拟实现 内存比较函数&#xff08;memcmp&#xff09;memcmp的…

如何解决在idea中的hadoop日志错误

在idea中操作hadoop的时候&#xff0c;每次运行代码都会发现有个日志错误&#xff0c;虽然不影响程序运行&#xff0c;但是无法打印日志。这是缺少依赖&#xff0c;和windows上缺少log4j的文件 解决方案&#xff1a; 1、导入slf4j依赖 2、导入hadoop中的log4j文件 1、从hado…

Datawhale X 李宏毅苹果书 AI夏令营 《深度学习详解》第十九章 ChatGPT

19.1 ChatGPT 简介和功能 1、对话框可以输入任何东西 2、可以继续追问 19.2 对于 ChatGPT 的误解 1、第一个误解是 ChatGPT 的回答是罐头讯息 2、另外一个常见的误解是 ChatGPT 的答案是网络搜索的结果 3、那 ChatGPT 真正在做的事情是什么呢&#xff1f;一言以蔽之就是做…

开放式激光振镜运动控制器在Ubuntu+Qt下的文本标刻

开放式激光振镜运动控制器在UbuntuQt下的文本标刻 上节课程我们讲述了如何通过UbuntuQt进行振镜校正&#xff08;详情点击→开放式激光振镜运动控制器在UbuntuQt下的激光振镜校正&#xff09;&#xff0c;本节文本标刻是在振镜校正的前提下实现的。 在正式学习之前&#xff0…

F12抓包08:查看网站Cookie

课程大纲 1、查看Cookie 1. 应用界面查看&#xff1a;按F12进入浏览器的开发者模式 - “应用”&#xff08;Application&#xff09; - Cookie&#xff0c;可查看Cookie并进行增、删、改、查操作。 2. 控制台命令行查看&#xff1a;按F12进入浏览器的开发者模式 - “控制台”&…

2025年第八届计算机图形和虚拟国际会议(ICCGV 2025)即将召开!

2025年第八届计算机图形和虚拟国际会议&#xff08;ICCGV 2025&#xff09;将于2025年2月21-23日在中国成都举行。随着信息技术的飞速发展&#xff0c;计算机图形学与虚拟现实技术正以前所未有的速度重塑着我们的认知世界与交互体验。从沉浸式游戏到精准医疗模拟&#xff0c;从…

坐牢第三十八天(Qt)

1、使用Qt绘画事件处理画一个闹钟 widget.h #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QDebug> #include <QPaintEvent>//画画处理事件 #include <QPainter>//画画 #include <QTime> //时间类 #include <QTimer>…

电流互感器电压互感器

一&#xff0c;电流互感器 用途&#xff1a;对信号做精确采样和适当补偿功能&#xff0c;方便对5A以内的交流电进行信号采集。主要作用就是对电流进行测量和取样。 扩展&#xff1a;对应输出模拟交流信号可以调节&#xff0c;可根据电位器&#xff08;调节放大比例&#xff0…

Ali_Yun Port

Ali_Yun Port 云服务器端口

基于云原生向量数据库 PieCloudVector 的 RAG 实践

近年来&#xff0c;人工智能生成内容&#xff08;AIGC&#xff09;已然成为最热门的话题之一。工业界出现了各种内容生成工具&#xff0c;能够跨多种模态产生多样化的内容。这些主流的模型能够取得卓越表现&#xff0c;归功于创新的算法、模型规模的大幅扩展&#xff0c;以及海…

FlinkCDC 3.2.0 新增优点 Pattern Replacement in routing rules

新增优点&#xff1a;Pattern Replacement in routing rules flinkcdc 3.2.0版本相较于3.1.0版本&#xff0c;避免了多表多sink多次写 route 路由的麻烦&#xff0c;类似于统一前后缀的形式多表多sink&#xff0c;通过<>正则&#xff0c;大大减少了书写 官网&#xff1…

【干货分享】Ftrans安全数据交换系统 搭建跨网数据传输通道

安全数据交换系统是一种专门设计用于在不同的网络、系统或组织之间安全地传输数据的软件或硬件解决方案。这种系统通常包含多种安全特性&#xff0c;以确保数据在传输过程中的保密性、完整性和可用性。 安全数据交换系统可以解决哪些问题&#xff1f; 安全数据交换系统主要解…

图分类!!!

deepwalk 使用图中节点与节点的共现关系来学习节点的向量表示。那么关键的问题就是如何来描述节点与节点的共现关系&#xff0c;DeepWalk给出的方法是使用随机游走(RandomWalk)的方式在图中进行节点采样,RandomWalk是一种可重复访问已访问节点的深度优先遍历算法。给定当前访问…

CogView-3-Plus:深度解锁智谱AI的图像生成新力量

一、引言&#xff1a;AI助力创意与效率的全面提升 在如今这个瞬息万变的科技时代&#xff0c;AI大模型早就不是实验室里的“神秘武器”&#xff0c;它们已经实实在在地融入到我们的日常工作中了&#xff0c;尤其是在图像生成和内容创作这块儿&#xff0c;简直是效率神器。只要几…

微软 Power Apps MDA 模型驱动应用解决Image字段查询出来缩略图问题变原图方法(c#+Plugin方式)

微软 Power Apps MDA 模型驱动应用解决Image字段查询出来缩略图问题变原图方法&#xff08;c#Plugin方式&#xff09; 在某些特定的场景中&#xff0c;需要将Image字段中的图片取出来&#xff0c;一般来说直接查询这个字段可以直接取&#xff0c;取出来的就是一个Base64格式的图…