二分查找 之初值设置的区别分析

本文由于测试是选取的测试集不具有一般性,得到的结论是错的(后续抽时间更正实验),但讨论的过程,也许可以给你带来些思考(建议直接看结论)。

主要思路在于探讨所要查找的数位于原数组元素之间或者上下限之外(如原数组[1, 3, 5, 6], 要找0, 4, 10)时 l,r指针的情况。

left = 0, r = v. size() - 1, while(l <= r)时
void test() {
	vector<int> v{1, 3, 5, 7, 9, 11, 13, 15, 17};
    vector<int> vl, vr, vm;
    int find_val;
    //输入要查询的值,看二分搜索下标的情况
    while(cin >> find_val) {
        int l = 0, r = v.size() - 1, mid;
        while(l <= r) {
            	vl.push_back(l); // 前面加tab的三行,用于后面打印
            	vr.push_back(r);
            mid = l + ((r-l)>>1);
            	vm.push_back(mid);
            if(v[mid] == find_val) break;
            if(v[mid] > find_val) r = mid-1;
            else l = mid+1;
        }

        //分别输出mid, l, r的运行过程中所有下标
        //要找的值
        cout << "f: " << find_val;

        //l下标
        cout << endl << "l: ";
        for(int i : vl)
            cout << i << " ";
        cout << l;

        //r下标
        cout << endl << "r: ";
        for(int i : vr)
            cout << i << " ";
        cout << r;
        
        //mid下标
        cout << endl << "m: ";
        for(int i : vm)
            cout << i << " ";
        cout << mid << endl;
		
		vl.clear(), vr.clear(), vm.clear();
    }
}

几组测试数据:

//left = 0, r = v.size() - 1, while(l <= r)
//v为要找的值,mid为下标中值,l为左下标,r为右下标
//最后一列下标值为出循环后的各下标值
//v[] = {1, 3, 5, 7, 9, 11, 13, 15, 17} 待查询数组长度为9,下标范围(0 ~ 8)

-1
f: -1
l: 0 0 0 0
r: 8 3 0 -1
m: 4 1 0 0
1
f: 1
l: 0 0 0 0
r: 8 3 0 0
m: 4 1 0 0
5
f: 5
l: 0 0 2 2
r: 8 3 3 3
m: 4 1 2 2
6
f: 6
l: 0 0 2 3 3
r: 8 3 3 3 2
m: 4 1 2 3 3
7
f: 7
l: 0 0 2 3 3
r: 8 3 3 3 3
m: 4 1 2 3 3
19
f: 19
l: 0 5 7 8 9
r: 8 8 8 8 8
m: 4 6 7 8 8
小结:

编码方式:left = 0, r = v.size() - 1, while(l <= r)

找到时:

find_val = 1(0), 7(3) 【圆括号内为下标值,用于观察下标奇偶性】

find_val的下标值:l = r = mid

find_val = 5(2)

find_val的下标值:l = mid ≠ r

所以找到 find_val 时,正确表示下标的是 l

未找到时:

当要find_val = -1小于所有的数时,r体现出未找到,且 l > r

当find_val = 19大于所有数时,l体现出未找到,且 l > r

当find_val = 6在所有数的之间却不是其中一个时,l > r

所以未找到 find_val 时,总有 l > r

换个角度:

从while(l <= r)代码块的出口这个角度来分析较简单,执行完这个代码块后,只有两种可能,要么是 l > r,要么是从mid处break: mid = (l+r)/2,r = l 或 l+1;

left = 0, r = v. size() , while(l < r)时

只需对上面代码做微调

void test() {
	vector<int> v{1, 3, 5, 7, 9, 11, 13, 15, 17};
    vector<int> vl, vr, vm;
    int find_val;
    //输入要查询的值,看二分搜索下标的情况
    while(cin >> find_val) {
        int l = 0, r = v.size(), mid;
        while(l < r) {
            	vl.push_back(l); // 前面加tab的三行用于后面打印
            	vr.push_back(r);
            mid = l + ((r-l)>>1);
            	vm.push_back(mid);
            if(v[mid] == find_val) break;
            if(v[mid] > find_val) r = mid-1;
            else l = mid+1;
        }

        //分别输出mid, l, r的运行过程中所有下标
        //要找的值
        cout << "f: " << find_val;

        //l下标
        cout << endl << "l: ";
        for(int i : vl)
            cout << i << " ";
        cout << l;

        //r下标
        cout << endl << "r: ";
        for(int i : vr)
            cout << i << " ";
        cout << r;
        
        //mid下标
        cout << endl << "m: ";
        for(int i : vm)
            cout << i << " ";
        cout << mid << endl;
    }
}

几组测试数据:

//left = 0, r = v.size(), while(l < r)
//v为要找的值,mid为下标中值,l为左下标,r为右下标
//最后一列下标值为出循环后的各下标值
//v[] = {1, 3, 5, 7, 9, 11, 13, 15, 17} 待查询数组长度为9,下标范围(0 ~ 8)

-1
f: -1
l: 0 0 0
r: 9 3 0
m: 4 1 1
1
f: 1
l: 0 0 0
r: 9 3 0
m: 4 1 1
5
f: 5
l: 0 0 2 2
r: 9 3 3 3
m: 4 1 2 2
6
f: 6
l: 0 0 2 3
r: 9 3 3 3
m: 4 1 2 2
7
f: 7
l: 0 0 2 3
r: 9 3 3 3
m: 4 1 2 2
9
f: 9
l: 0 0
r: 9 9
m: 4 4
19
f: 19
l: 0 5 8 9
r: 9 9 9 9
m: 4 7 8 8
小结:

编码方式:left = 0, r = v.size(), while(l < r)

找到时:

find_val = 1(0), 5(2) 【圆括号内为下标值,用于观察下标奇偶性】

find_val的下标值:l = mid ≠ r

find_val = 7 (3)

find_val的下标值:l = r ≠ mid

find_val = 9(4)

find_val的下标值:mid ≠ l ≠ r
所以找到 find_val 时,正确表示下标的是 l 或 mid

未找到时:

当要find_val = -1 小于所有的数时,l = r, v[l] != find_val

当find_val = 19 大于所有数时,l = r, v[l] != find_val

当find_val = 6 在所有数的之间却不是其中一个时,l = r, v[l] != find_val

所以未找到 find_val 时,总有 l = r 且 v[l] != find_val

换个角度:

从while(l < r)代码块的出口这个角度来分析较简单,执行完这个代码块后,只有两种可能,要么是 l = r,要么是从mid处break: l <= r;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值