1月4日学习——双指针—二分

1月5日更新

蝴蝶忍可爱捏


提示:本文主要记录下自己学习算法时候的想法捏😋
训练的第一天,在家摆烂一个月天天看番打游戏捏,一个多月了鼠鼠我啊,回来学习了捏😊


一、尺取法(双指针)

算法介绍(双指针与二分查找)

双指针算法优化了二分查找的算法,二分查找的时间复杂度为 O ( log ⁡ 2 n ) O(\log_2n) O(log2n)

  • 二分查找的时间复杂度的解释:在一个序列中,每次进行一次比较都要去掉一半的数组元素,假设数组内有 n n n个元素( n n n 2 2 2的幂),第一次比较剩下了 n / 2 n/2 n/2个元素,第二次比较剩下 ( n / 2 ) / 2 (n/2)/2 (n/2)/2个元素,以此类推,经过第k次比较之后,需要查找的元素就剩下了 n / 2 k n/2^k n/2k个,当 k = log ⁡ 2 k k=\log_2k k=log2k时,需要查找的元素只剩下一个了,就只需要在比较一次,因此在最最糟糕的情况下,也只需要 log ⁡ 2 n + 1 \log_2n+1 log2n+1次查找捏👍
  • 双指针算法的时间复杂度解释:两个指针,一个指向头部,一个指向尾部,向中间靠拢,直到找到我们需要的元素,所以最糟糕的情况下,设数组有 n n n个元素,这俩指针只能沿着一个方向,把这个数组扫一遍(最糟糕的情况下)所以时间复杂度为 O ( n ) O(n) O(n)

下面是一个经典的双指针算法例子

//比如说在一个数组a中我们要朝朝两个数使他们的和为target
void find(int a[] , int n , int target){
	sort(a,a+n);
	int i = 0 , j = n - 1;
	while(i < j ){
		if(a[i] + a[j] > target) j--;//这说明太大了,所以代表大元素的尾指针后退一步
		else if(a[i] + a[j] < target) i++;//同理
		else cout<<a[i]<<' '<<a[j];

使用双指针创造滑动区间寻找区间和

void findsum(int *a , int len , int target){
	int i , j = 0;//初始化指针
	int sum = a[0];//初始化和为头指针指向的下标元素
	while(j < len){
		if(sum >= target){
			if(sum == target) print("%d %d\n",i,j);//找到一个合适的区间,输出下标
			sum -=a[i];//头指针后移,先减掉头指针后移之前指向的元素
			i++;
			if(i > j){
			//如果i超越了j(通常是第一个元素就大于target值所以头指针需要后移导致超越了尾指针)
				sum = a[i];//重置这个和为最新的头指针所指向的元素(准确的说直到找到一个合适的数据,否则头指针和尾指针都是并一块走的)
				j++;//尾指针后移
			}else{
				j++;
				sum+=a[j];//尾指针后移,加上新的元素

利用双指针算法进行数组去重

void delsame(int *a,int len){//本算法有缺陷,因为我暂时没有想到一个普世的处理办法,所以只能对每个数组的最后一个元素进行一个特殊判断,但我也觉得是一个双指针算法;
	int i ,j ,k;
	int cmp[10010];//这个数组保存去重之后的数组
	for(i = 0 , j = 0 , k = 0 ;i < n ; i++){//i指针用于遍历数组,j指针用于指向需要保存的元素,k指针是新数组cmp的下标
		if(a[i] != a[j]){//如果不相同 就把j指针的值保存到cmp数组里
			cmp[k++] = a[j];
			j = i;//因为此时i与j的值不相同所以直接把i赋值给j
		}
		if( i == n - 1 && cmp[k] != a[i])//判断最后扫描的值之前是否有保存过,如果保存了,就不需要录进去了,没保存我们就录入
			cmp[k++] = a[i];
		}

二、二分法(折半)

整数二分(细节很重要,牵扯到了一些比较底层的东西)

int bin_search(int *a ,int left , int right , int target){//查找后继
	int i = left , int j = right;
	while(i < j){
		int mid = i + (j - i)/2;
		if(a[mid] >= target) j = mid;
		else left = mid + 1;
//以上是半开半闭写法,如果喜欢全闭区间只需要修改j和返回的mid值即可
int bin_search1(int *a,int left , int right , int target){//查找前驱
	int i = left , int right = right ;
	while(i < j){
		int mid = i + ( j - i + 1) /2;
		if(a[mid] <= target) i = mid;
		else j = mid - 1; 
public static int FindPoinner(int b[], int len ,int target) {
		int i = 0 , j = len;
		while(i < j) {
			int mid = i + (j - i + 1)/2;//向下取整,这个是右中位数
			if(b[mid] <= target) {
				i = mid ;
			}else {
				j = mid - 1;
			}
		}
		return i ;
	}
	public static int FindEnd(int b[],int len ,int target) {
		int i = 0 , j = len;
		while(i < j) {
			int mid = i + (j - i)/2;//向上取整,这个是左中位数
			if(b[mid] >= target) {
				j = mid;
			}else {
				i = mid + 1;
			}
		}
		return i ;
	}
  • m i d mid mid的计算是向下取整,更加靠近左边,所以是左中位数,也就是说在 a [ m i d ] > = t a r g e t a[mid]>=target a[mid]>=target的情况下最后返回的 m i d mid mid值是第一个等于或第一个大于 t a r g e t target target的下标,也成为 t a r g e t target target后继
  • 尽量不要使用 m i d = ( i + j ) / 2 mid=(i+j)/2 mid=(i+j)/2这种写法,因为这是向零取整,虽然在C++语言里头不用担心但是在Java,Python这种有负数下标存在的语言里会导致死循环
  • 同样的如果要使用右中位数,就需要 m i d mid mid向上取整, m i d = i + ( j − i + 1 ) / 2 mid=i+(j-i+1)/2 mid=i+(ji+1)/2那么这样算法返回的值就是最后一个等于或者最后一个大于 t a r g e t target target的下标,也成为 t a r g e t target target前驱
  • 注意:在查找前驱时,如果要查找的数为有序序列的最大值,则会出现越界一位的现象

STL中的lower_bound()与upper_bound()

  • 二者功能是一样的,如果只是简单的查找x或者x附近的数,那么用 S T L STL STL就行
//查找第一个大于x的元素的位置
pos = upper_bound(a,a+n,x) - a;
//查找第一个等于或者大于x的元素
lower_bound();
//以此为例可以进行很多变式写法

实数二分

  • 没有取整的问题所以代码相对二分简单
const double eps = 1e-7
while(right - left > eps){
	double mid = left + (right - left)/2;
	if(check(mid)) right = mid;
	else left = mid;

重点在于仔细设计精度eps,太小会超时,太大会不准确

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Dou_Huanmin

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值