奇思妙想:利用野指针和lower_bound()/upper_bound()函数实现整数二分
众所周知,c++的STL中提供了三个二分查找函数,binary_search(),lower_bound(),upper_bound(),功能分别是找某值是否在数组中出现,找到数组中第一个大于等于某值的元素,找到数组中第一个大于某值的元素。
这三个函数使用十分灵活,可以通过自定义结构体,比较函数,重载大于小于号来实现各种用法,但是它们有一个共同的局限性,就是必须在数组上使用,而不能在整数上使用。
比如我要二分找一个数的向下取整的平方根,范围1e9,肯定不行,1e9的数组都开不了。
大佬的解决方法当然是手写二分辣,但是整数二分是以条件众多,难记难写著称的。所以今天开了一下脑洞,利用野指针实现整数二分。
首先我们观察lower_bound()和upper_bound()调用比较函数时的行为。
struct Example{ int index; int num; }exm[105]; bool cmp(const Example &x,const Example &y){ printf("%d %d\n",x.index,y.index); return x.num<y.num; } int main(){ for(int i=1;i<=100;i++){ exm[i].index=i; exm[i].num=i; } Example t; t.index=-1;t.num=50; lower_bound(exm+1,exm+101,t,cmp); upper_bound(exm+1,exm+101,t,cmp); } //51 -1 //26 -1 //39 -1 //45 -1 //48 -1 //50 -1 //49 -1 //-1 51 //-1 26 //-1 39 //-1 45 //-1 48 //-1 50
看出来了吧,lower_bound()在调用比较函数时,把数组元素传给比较函数的第一个参数,把待查找元素传给第二个参数
upper_bound()则相反。
自定义的比较函数对于这两个二分查找函数而言是个黑箱,只被关心返回值是几。
那么我们可以让比较函数只关心两个参数的地址,用这个地址瞎搞,不去真的访问这两个地址里面的值,比如刚才给出的例子,找平方根,可以这样写:
const int *p=0; bool cmp(const int &x,const int &y){ return (&x-p)<(&y-p)*(&y-p); } int main(){ int k; scanf("%d",&k); printf("%d",upper_bound(p+1,p+k+1,p[k],cmp)-1-p); } //91749281 //9578 //注意溢出
此方法的局限性:
1,指针长度有限制且无法自己定义。
#include<iostream> #include<algorithm> using namespace std; int main(){ printf("the size of int* is %d\n",sizeof(int*)); printf("the size of long long is %d\n",sizeof(long long)); } //the size of int* is 8 //the size of long long is 8
但是这个貌似没太大问题,因为所有指针,不管它指向什么,都是8位的,和long long一样长,足够了。
2,只能用于正数
指针运算没有负数,对于此种情况只能用设虚拟零点的方法解决。
3,存在非法访问的风险
目前笔者未找到任何证据能够证明二分查找函数本身不去访问指针指向的内容,有可能导致未知的非法访问。