二分法

二分法

1、二分法查找,也称为折半法,是一种在有序数组中查找特定元素的搜索算法。
2、二分法查找的思路如下:
  (1)首先,从数组的中间元素开始搜索,如果该元素正好是目标元素,则搜索过程结束,否则执行下一步。
  (2)如果目标元素大于/小于中间元素,则在数组大于/小于中间元素的那一半区域查找,然后重复步骤(1)的操作。
  (3)如果某一步数组为空,则表示找不到目标元素。
二分法查找的时间复杂度O(logn)。
3、 二分查找是有局限性的:
  (1)二分查找依赖的是顺序表结构,简单点说就是数组。
  解释:主要原因是二分查找算法需要按照下标随机访问元素。
  (2)二分查找针对的是有序数据。
  (3)数据量太小不适合二分查找。
  (4)数据量太大也不适合二分查找。
  解释:二分查找的底层需要依赖数组这种数据结构,而数组为了支持随机访问的特性,要求内存空间连续,对内存的空间要求比较苛刻。

3.5二分查找的递归解法

upload successful

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include<bits/stdc++.h>
using namespace std;

//二分查找的非递归形式 
int binSearch(int nums[],int k){
    int i = 0;
    //j是数组最后一个下标的位置,如int a[5],j最大为4(因为没有a[5]这个值  
    int j = sizeof(nums) / sizeof(nums[0])-1;
    int mid;
    while(i<=j){
        mid = i+((j-i)>>1);//防止溢出,而且使用位运算更加高效//mid = ((i+j)>>>1);
        if(nums[mid]==k){
            return mid;
        }else if(nums[mid]>k){
            j = mid-1;
        }else{
            i = mid+1;
        }
    }
    return -1;
}
//二分查找的递归形式,其中k,i,j参数是必要的,不然的话怎么调用传值 
int binSearch_1(int nums[],int k,int i,int j){
	//其中nums是有序数组,k为要查找的值,i为要查找的数组的起始下标,j为终点下标 
	if(i>j) return -1;
	int mid =i+((j-i)>>1);
	if(nums[mid]==k){
		return mid;
	}else if(nums[mid]>k) {
		return binSearch_1(nums,k,i,mid-1);
	}else {
		return binSearch_1(nums,k,mid+1,j);
	}
} 
int main(){
	int a[]={1,2,3,4,5};
	cout<<binSearch_1(a,5,0,4); //结果为4,即数组下表的最后一个,a[4] 
}

4、实例:
(1)求f(x)=x^3-x-1在(1,1.5)内的一个实根,使误差不超过0.005。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <iostream>
#include<math.h>
using namespace std;
float Oper(float x)
{
    return x*x*x-x-1;
}
int main()
{
    float a,b,c,x;
    cin>>a>>b>>c;
    while(fabs(b-a)>=c){
        x=(a+b)/2;
        if(Oper(a)*Oper(x)<0){
            b=x;
        }
        else{
            a=x;
        }
    }
    cout << "满足要求的值是:" <<x<< endl;
    return 0;
}

(2)
求最小的i,使得a[i] = key,若不存在,则返回-1(lowerbound函数);
求最大的i的下一个元素的下标(c++中的upperbound函数),使得a[i] = key,若不存在,则返回-1;
求最大的i,使得a[i] = key,若不存在,则返回-1;
求最小的i,使得a[i] > key,若不存在,则返回-1;
求最大的i,使得a[i] < key,若不存在,则返回-1;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
#include <bits/stdc++.h>

using namespace std;
const int maxn = 100 + 10;

int cmp(const void *a, const void *b) {
    return *(int *) a - *(int *) b;
}

//普通的二分查找
int bs(int *arr,int L,int R,int target){
    while( L <= R){
        int mid = (L) + (R-L)/2;
        if(arr[mid] == target)
            return mid;
        if(arr[mid] > target)
            R = mid - 1;
        else
            L = mid + 1;
    }
    return -1; // not find
}

//求最小的i,使得a[i] = target,若不存在,则返回-1
//返回 如果有重复的 下界(比如1,2,2,2,3,4)查找2,返回1
int firstEqual(int arr[], int L, int R, int target) {
    while (L < R) {
        int mid = L + (R - L) / 2;
        if (arr[mid] < target)
            L = mid + 1;
        else
            R = mid;
    }
    if (arr[L] == target)
        return L;
    return -1;
}

//求最大的i的下一个元素的下标(c++中的upperbound函数),使得a[i] = target,若不存在,则返回-1
int lastEqualNext(int arr[], int L, int R, int target) {
    while (L < R) {
        int m = L + (R - L) / 2;
        if (arr[m] <= target) 
            L = m + 1;
        else
            R = m;
    }
    if (arr[L - 1] == target)
        return L;
    return -1;
}

//求最大的i,使得a[i] = target,若不存在,则返回-1
int lastEqual(int arr[], int L, int R, int target) {
    while (L < R) {
        int mid = L + ((R + 1 - L) >> 1);//向上取整
        if (arr[mid] <= target)
            L = mid;
        else
            R = mid - 1;
    }
    if (arr[L] == target)
        return L;
    return -1;
}

//求最小的i,使得a[i] > target,若不存在,则返回-1
int firstLarge(int arr[], int L, int R, int target) {
    while (L < R) {
        int m = L + ((R - L) >> 1);//向下取整
        if (arr[m] <= target)
            L = m + 1;
        else
            R = m;
    }
    if (arr[R] > target)
        return R;
    return -1;
}

//求最大的i,使得a[i] < target,若不存在,则返回-1
int lastSmall(int arr[], int L, int R, int target) {
    while (L < R) {
        int m = L + ((R + 1 - L) >> 1);//向上取整
        if (arr[m] < target)
            L = m;
        else
            R = m - 1;
    }
    if (arr[L] < target)
        return L;
    return -1;
}

int main() {
    //freopen("in.txt", "r", stdin);
    int n, a[maxn], v;
    scanf("%d", &n);
    for (int i = 0; i < n; i++)scanf("%d", &a[i]); //1 3 2 9 4 1 3 7 2 2
    scanf("%d", &v);   //input the number you need find
    qsort(a, n, sizeof(a[0]), cmp);                // 1 1 2 2 2 3 3 4 7 9
    printf("after sorted : \n");
    for (int i = 0; i < n; i++)printf("%d ", a[i]);

    printf("\n-------------test----------------");
    
    printf("\n%d\n", firstEqual(a, 0, n, v));  //output 2  第一个
    printf("%d\n", lastEqualNext(a, 0, n, v)); //output 4 + 1,最后一个的下一个
    printf("%d\n", lastEqual(a, 0, n, v));     //output 4  最后一个
    printf("%d\n", firstLarge(a, 0, n, v));    //output 5(第一个3大于2)
    printf("%d\n", lastSmall(a, 0, n, v));     //output 1(不是0)
    return 0;
}
/*
测试数据:
10
1 3 2 9 4 1 3 7 2 2
2
*/

upload successful

(5)二分套二分

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
/*
 *  二分套二分
 *  数组A同数组B组合乘积,二分查找第K大
 */
typedef long long ll;

const int MAXN = 5e4 + 10;

ll N, K;

ll A[MAXN];
ll B[MAXN];

//  查找小于x的元素个数
ll check(ll x)
{
    ll j = N, ans = 0;
    for (int i = 1; i <= N; i++)
    {
        for (; j > 0;)
        {
            if (A[i] * B[j] > x)
            {
                j--;
            }
            else
            {
                break;
            }
        }
        ans += j;
    }
    return ans;
}

int main(int argc, const char * argv[])
{
    cin >> N >> K;

    for (int i = 1; i <= N; i++)
    {
        scanf("%lld %lld", A + i, B + i);
    }
    sort(A + 1, A + N + 1);
    sort(B + 1, B + N + 1);

    ll ans = 0;
    ll key = N * N - K + 1;
    ll low = A[1] * B[1];   //  初始最小值
    ll high = A[N] * B[N];  //  初始最大值

    while (high - low > 1)
    {
        ll mid = (low + high) >> 1;
        if (check(mid) >= key)
        {
            ans = mid;
            high = mid;
        }
        else
        {
            low = mid;
        }
    }

    cout << ans << '\n';
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值