鼓捣六十天算法——Day1

29 阅读2分钟

数组之二分查找

“工欲善其事,必先利其器”

算法与数据结构是每一位程序员的基本功,最近学得比较杂,脑海里思路比较混乱,所以想通过记录的方法来理清一个思绪,帮助自己更好地理解算法。

今日知识点

数组理论基础:

  • 存放相同类型的数据;
  • 对于数组来说,在内存上一定是连续的,包括二维数组;
  • 数组元素不能删除,只能覆盖;

今日题目

  1. 二分查找法

LeeCo 704: 给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target  ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。

先分析一波:二分查找法的原理比较简单,无非就是找到区间中点,然后找到所求值的那一半,再划分中点,不断进行下去。它真正的易错点在于它边界的划分,左端点要+1还是-1,右端点呢?

想要解决这个问题,首先是要判断区间定义,大概分为两种:左闭右闭和左闭右开[left, right] [left, right) 根据两种情况,有不同的写法

第一种情况:两侧都是闭区间

  • while (left <= right) 要使用 <= ,因为left == right是有意义的,所以使用 <=
  • if (nums[middle] > target) right 要赋值为 middle - 1,因为当前这个nums[middle]一定不是target,那么接下来要查找的左区间结束下标位置就是 middle - 1
class Solution {
public:
    int search(vector<int>& nums, int target) {
        int left = 0;
        int right = nums.size() - 1; // 定义target在左闭右闭的区间里,[left, right]
        while (left <= right) { //left==right,区间[left, right]依然有效,所以用 <=
            int middle = left + ((right - left) / 2);// 防止溢出 等同于(left + right)/2
            if (nums[middle] > target) {
                right = middle - 1; // target 在左区间,所以[left, middle - 1]
            } else if (nums[middle] < target) {
                left = middle + 1; // target 在右区间,所以[middle + 1, right]
            } else { // nums[middle] == target
                return middle; // 数组中找到目标值,直接返回下标
            }
        }
        // 未找到目标值
        return -1;
    }
};

第二种情况:左闭右开

  • while (left < right),这里使用 < ,因为left == right在区间[left, right)是没有意义的
  • if (nums[middle] > target) right 更新为 middle,因为当前nums[middle]不等于target,去左区间继续寻找,而寻找区间是左闭右开区间,所以right更新为middle,即:下一个查询区间不会去比较nums[middle]

对应的代码是

class Solution {
public:
    int search(vector<int>& nums, int target) {
        int left = 0;
        int right = nums.size(); // 定义target在左闭右开的区间里,即:[left, right)
        while (left < right) { // 因为left == right的时候,在[left, right)是无效的空间,所以使用 <
            int middle = left + ((right - left) >> 1);
            if (nums[middle] > target) {
                right = middle; // target 在左区间,在[left, middle)中
            } else if (nums[middle] < target) {
                left = middle + 1; // target 在右区间,在[middle + 1, right)中
            } else { // nums[middle] == target
                return middle; // 数组中找到目标值,直接返回下标
            }
        }
        // 未找到目标值
        return -1;
    }
};

除此之外,二分查找法还有额外的模版,这个模版也分为两种,分别是浮点数版和整数版

浮点数版大致原理如下:

double l = -1000, r = 1000;
while(r - l <= 1e-8)//这个地方有一个技巧就是,看保留几位小数,比他多两位即可 
{
double mid = r + l / 2; 
if(mid*mid*mid >= x)//判断条件 
r = mid ; else l = mid;
}
printf("%d", l);

整数版大致原理如下

int x; 
scanf("%d", &x); 
int l = 0, r = n-1; 
while(l >= r) return; 
while(l < r) 
{ 
//找左端点 
int mid = l + r >> 1; 
if(q[mid] >= x) 
r = mid; 
else l = mid + 1; 
} 
if(q[l] != x)
//按理说左侧端点就应该在迭代到最后的时候,为最小值就是x 
cout<<"-1 -1"<<endl; 
else { 
cout<<l<<' '; 
int l = 0, r= n - 1; 
//找右端点 
while(l < r)
{ 
int mid = r + l + 1>>1; 
if(q[mid] <= x) l = mid; 
else r= mid - 1; 
} 
cout<<l<<endl; 
}

参考

代码随想录