数据结构——二分算法

二分查找

1. 在排序数组中查找元素的第一个和最后一个位置


代码实现:

cpp 复制代码
/**
 * Note: The returned array must be malloced, assume caller calls free().
 */

int binarySearch(int *nums, int numsSize, int target) {
    int l = 0, r = numsSize - 1;         
    while (l <= r) {
        int mid = (l + r) >> 1;
        if (nums[mid] == target) {            
            return mid;
        } else if (nums[mid] > target) {      
            r = mid - 1;
        } else if (nums[mid] < target) {      
            l = mid + 1;
        }
    }
    return -1;
}

int* searchRange(int *nums, int numsSize, int target, int *returnSize) {
    int *res = malloc(sizeof(int) * 2);
    memset(res, -1, sizeof(int) * 2);
    *returnSize = 2;
    if (nums == NULL || numsSize < 1) {
        return res;
    }
    int ind = binarySearch(nums, numsSize, target);
    if (ind == -1) {
        return res;
    }
    int i, j;
    for (i = ind - 1; i >= 0; i--) {
        if (nums[i] != target) {
            break;
        }
    }
    res[0] = i + 1;
    for (j = ind + 1; j < numsSize; j++) {
        if (nums[j] != target) {
            break;
        }
    }
    res[1] = j - 1;
    return res;
}

2. 搜索插入位置

代码实现:

cpp 复制代码
/*
    函数功能:返回有n个元素的数组arr中,找等于key或者第一个比key大的数的下标
*/
int binary_search_v1(int *arr, int n, int key) {
    int head = 0, tail = n - 1, mid; // 左闭右闭
    while (head <= tail) {
        mid = (head + tail) >> 1;
        if (key > arr[mid]) {
            head = mid + 1;
        } else if (key < arr[mid]) {
            tail = mid - 1;
        } else if (key == arr[mid]) {
            return mid;
        }
    }
    return head;
}

int binary_search_v2(int *arr, int n, int key) {
    int head = 0, tail = n, mid; // 左闭右开
    while (head < tail) {
        mid = (head + tail) >> 1;
        if (key > arr[mid]) {
            head = mid + 1;
        } else if (key < arr[mid]) {
            tail = mid;
        } else if (key == arr[mid]) {
            return mid;
        }
    }
    return head;
}
int binary_search_v3(int *arr, int l, int n, int key) {
    int head = l, tail = n; // 左闭右开
    int mid = (head + tail) >> 1;
    if (head >= tail) {
        return head;
    }
    if (key > arr[mid]) {
        head = mid + 1;
    } else if (key < arr[mid]) {
        tail = mid;
    } else if (key == arr[mid]) {
        return mid;
    }
    return binary_search_v3(arr, head, tail, key); 
}

int searchInsert(int *nums, int numsSize, int target) {
    return binary_search_v1(nums, numsSize, target);
    // return binary_search_v2(nums, numsSize, target);
    // return binary_search_v3(nums, 0, numsSize, target);
}

3. 二分查找

代码实现:

cpp 复制代码
/*
最基本的二分查找
    前提:待查找序列是有序的
    时间复杂度:O(logn) 以2为底的对数
*/
int binary_search_v1(int *arr, int n, int key) {
    int head = 0, tail = n - 1, mid; // 左闭右闭
    while (head <= tail) {
        mid = (head + tail) >> 1; // 右移
        if (arr[mid] == key) { // 找到了
            return mid;
        }
        if (arr[mid] < key) { // 未找到
            head = mid + 1;
        } else {
            tail = mid - 1;
        }
    }
    return -1;  // 没找到,返回-1
}

int binary_search_v2(int *arr, int n, int key) {
    int head = 0, tail = n, mid; // 左闭右开
    while (head < tail) {
        mid = (head + tail) >> 1; // 右移
        if (arr[mid] == key) { // 找到了
            return mid;
        }
        if (arr[mid] < key) { // 未找到
            head = mid + 1;
        } else {
            tail = mid;
        }
    }
    return -1;  // 没找到,返回-1
}

// 递归版本
int binary_search_v3(int *arr, int i, int n, int key) {
    int head = i, tail = n - 1, mid; // 左闭右闭
    // 失败出口
    if (head > tail) {
        return -1;
    }
    mid = (head + tail) >> 1;
    if (arr[mid] == key) { // 找到了
        return mid; // 返回待查找数的下标
    }
    if (arr[mid] < key) { // 未找到 
        head = mid + 1;
    } else {
        tail = mid - 1;
    }
    return binary_search_v3(arr, head, tail + 1, key);
}

int search(int *nums, int numsSize, int target) {
    return binary_search_v1(nums, numsSize, target);
    // return binary_search_v2(nums, numsSize, target);
    // return binary_search_v3(nums, 0, numsSize, target);
}

4. 寻找比目标字母大的最小字母


代码实现:

cpp 复制代码
int binary_search(char *arr, int n, char key) {
    int head = 0, tail = n - 1, mid; // 左闭右闭
    while (head <= tail) {
        mid = (head + tail) >> 1; // 右移
        if (arr[mid] == key) { // 找到了
            return mid;
        }
        if (arr[mid] < key) { // 未找到
            head = mid + 1;
        } else {
            tail = mid - 1;
        }
    }
    return -1;  // 没找到,返回-1
}

char nextGreatestLetter(char *letters, int lettersSize, char target) {
    for (char i = target + 1; i <= 'z'; i++) {
        int ind = binary_search(letters, lettersSize, i);
        if (ind != -1) {
            return letters[ind];
        }
    }
    return letters[0];
}

5. 正整数和负整数的最大计数


代码实现:

cpp 复制代码
/*
    使用二分查找找到第一个大于等于 1 的元素的下标 i 以及
    第一个大于等于 0 的元素的下标 j
    那么正整数的个数 a=n−i,负整数的个数 b=j,返回 a 和 b 中的较大值即可
*/

int binary_search(int *arr, int n, int key) {
    int head = 0, tail = n - 1, mid; // 左闭右闭
    while (head <= tail) {
        mid = (head + tail) >> 1;
        if (key <= arr[mid]) {
            tail = mid - 1;
        } else {
            head = mid + 1;
        }
    }
    return head;
}

int maximumCount(int *nums, int numsSize) {
    int i = binary_search(nums, numsSize, 1);
    int j = binary_search(nums, numsSize, 0);
    return fmax(numsSize - i, j);
}

6. 两个数组间的距离值


代码实现:

方法一:暴力法

cpp 复制代码
int findTheDistanceValue(int *arr1, int arr1Size, int *arr2, int arr2Size, int d) {
    int ans = 0;
    for (int i = 0; i < arr1Size; i++) {
        for (int j = 0; j < arr2Size; j++) {
            // 不符合条件,退出
            if (abs(arr1[i] - arr2[j]) <= d) {
                break;
            } else if (j  == arr2Size - 1) { // 当j走到最后一个的时候,才算符合,结果加1
                ans++;
            }
        }
    }
    return ans;
}

方法二:二分法

cpp 复制代码
void sort(int *a, int n) {
    int flag = 1;
    for (int i = n - 1; flag && i > 0; i--) {
        flag = 0;
        for (int j = 0; j < i; j++) {
            if (a[j] > a[j + 1]) {
                flag = 1;
                int temp = a[j];
                a[j] = a[j + 1];
                a[j + 1] = temp;
            }
        }
    }
}

int findTheDistanceValue(int *arr1, int arr1Size, int *arr2, int arr2Size, int d) {
    int ans = 0;
    sort(arr2, arr2Size);
    for (int i = 0; i < arr1Size; i++) {
        int key = arr1[i];
        int left = 0, right = arr2Size - 1;
        while (left <= right) {
            int mid = (right + left) >> 1;
            if (arr2[mid] >= key - d && arr2[mid] <= key + d) {
                break;
            } else if (arr2[mid] < key - d) {
                left = mid + 1;
            } else if (arr2[mid] > key + d) {
                right = mid - 1;
            }
        }
        if (left > right) {
            ans++;
        }
    }
    return ans;
}

7. 咒语和药水的成功对数


代码实现:

8. 和有限的最长子序列

9. 比较字符串最小字母出现频次

二分答案:求最小

1. 使结果不超过阈值的最小除数


代码实现:

cpp 复制代码
#define max(a,b) (((a) > (b)) ? (a) : (b))

int smallestDivisor(int *nums, int numsSize, int threshold) {
    int low = 1, high = 0;
    for (int i = 0; i < numsSize; i++) {
        high = max(high, nums[i]);
    }
    while (low < high) {
        int mid = (high + low) >> 1;
        int sum = 0;
        for (int i = 0; i < numsSize; i++) {
            sum += (nums[i] + mid - 1) / mid; // 向上取整
        }
        if (sum > threshold) {
            low = mid + 1;
        } else  {
            high = mid;
        }
    }
    return low;
}

2. 完成旅途的最少时间


代码实现:

cpp 复制代码
// t时间内,一共走了多少趟
long long isall_go(int *time, int n, long long t) {
    long long sum = 0;
    for (int i = 0; i < n; i++) {
        sum += (t / time[i]);
    }
    return sum;
}

long long minimumTime(int *time, int timeSize, int totalTrips) {
    long long min_time = INT_MAX;
    for (int i = 0; i < timeSize; i++) {
        if (time[i] < min_time) {
            min_time = time[i];
        }
    }
    // 一趟最长时间的公交车完成总共趟数的最大时间
    long long left = min_time;    
    long long right =(min_time * totalTrips);

    while (left <= right) {
        long long mid = (right + left) / 2;
        long long cur_trip = isall_go(time, timeSize, mid);
        if (cur_trip < totalTrips) {
            left = mid + 1;
        } else {
            right = mid - 1;
        }
    }
    return left;
}

3. 准时到达的列车最小时速


代码实现:

cpp 复制代码
double getTime(int* dist, int distSize, int speed) {
    double time = 0;
    for (int i = 0; i < distSize - 1; i++) {
        time += dist[i] / speed;
        time += (dist[i] % speed == 0 ? 0 : 1);
    }
    time += (double)dist[distSize - 1] / speed;
    return time;
}

int minSpeedOnTime(int* dist, int distSize, double hour){
    int left = 1;
    int right = 1e7;
    int maxspeed = right;
    while (left <= right) {
        int mid = (right + left) / 2;
        double time = getTime(dist, distSize, mid);
        if (time > hour) {
            left = mid + 1;
        } else if (time <= hour) {
            right = mid - 1;
        }
    }
    if (left > maxspeed) {
        return -1;
    }
    return left;
}

二分答案:求最大

😭 1. H指数 ||


代码实现:

cpp 复制代码
int hIndex(int *citations, int citationsSize) {
    int left = 0, right = citationsSize - 1;
    while (left <= right) {
        int mid = (right + left) / 2;
        if (citations[mid] >= citationsSize - mid) {
            right = mid - 1;
        } else {
            left = mid + 1;
        }
    }
    return citationsSize - left;
}

2. 每个小孩最多能分到多少糖果


代码实现:

cpp 复制代码
int maximumCandies(int *candies, int candiesSize, long long k) {
    long long sum = 0; 
    for (int i = 0; i < candiesSize; i++) {
        sum += candies[i];
    }
    if (sum < k) { // 特殊例子 总数不够孩子分
        return 0;
    }
    if (sum == k) { // 数量刚好够孩子分
        return 1;
    }

    long long max = 0; // 用于存放满足条件的最大值
    long long  head = 1;
    long long  tail = sum / k;
    long long  mid;
    while (head <= tail) {
        mid = (head + tail) / 2;
        long long count = 0;
        // 检查是否满足条件
        for (int i = 0; i < candiesSize; i++) {
            count += ((long long)candies[i]) / mid;
        }
        if (count >= k) {
            // k个孩子能均分mid
            max = mid;
            head = mid + 1;
        } else {
            // k个孩子不能均分mid
            tail = mid - 1;
        }
    }
    return max;
}

3. 找出出现至少三次的最长特殊子字符串 ||

二分间接值:二分的不是答案,而是一个和答案有关的值(间接值)

1. 正方形中的最多点数




代码实现:

cpp 复制代码
int func(int mid, int n, int **points, char *s) {
    int ret = 0;
    bool vis[26] = {0};
    for (int i = 0; i < n; i++) {
        if (abs(points[i][0]) <= mid && abs(points[i][1]) <= mid) {
            int c = s[i] - 'a';
            if (vis[c]) {
                return -1;
            }
            ret++;
            vis[c] = true;
        }
    }
    return ret;
}
int maxPointsInsideSquare(int **points, int pointsSize, int *pointsColSize, char *s) {
    int n = pointsSize;
    int head = 0, tail = 1e9;
    while (head <= tail) {
        int mid = (head + tail) / 2;
        if (func(mid, n, points, s) >= 0) {
            head = mid + 1;
        } else {
            tail = mid - 1;
        }
    }
    return func(head - 1, n, points, s);
}

2. 销售价值减少的颜色球



代码实现:

cpp 复制代码
#define MAX(a, b) ((a > b) ? (a) : (b))

int MOD = 1e9 + 7;

bool areaSummation(int *list, int listSize, int price, int orders) {
    long long sum = 0;
    for (int i = 0; i < listSize; i++) {
        sum += MAX(list[i] - price, 0);
    }
    if (sum >= orders) {
        return true;
    }
    return false;
}

int maxProfit(int *inventory, int inventorySize, int orders) {
    long long res = 0;
    int len = inventorySize;
    int left = 0, right = 0;

    for (int i = 0; i < len; i++) {
        right = MAX(right, inventory[i]);
    }
    while (left < right) {
        int mid = (right + left) / 2;
        if (areaSummation(inventory, len, mid, orders)) {
            left = mid + 1;
        } else {
            right = mid;
        }
    }
    for (int i = 0; i < len; i++) {
        if (inventory[i] > left) {
            res += (inventory[i] + left + 1L) * (inventory[i] - left + 0L) / 2;
            orders -= inventory[i] - left;
        }
    }
    res += (long)left * orders;

    return res %= MOD;
}

最小化最大值:本质是二分答案求最小

1. 分割数组的最大值


代码实现:

cpp 复制代码
bool check(int *nums, int numsSize, int m, int x) {
    long long sum = 0;
    int cnt = 1;
    for (int i = 0; i < numsSize; i++) {
        if (sum + nums[i] > x) {
            cnt++;
            sum = nums[i];
        } else {
            sum += nums[i];
        }
    }
    return cnt <= m;
}

int splitArray(int *nums, int numsSize, int m) {
    long long left = 0, right = 0;
    for (int i = 0; i < numsSize; i++) {
        right += nums[i];
        if (left < nums[i]) {
            left = nums[i];
        }
    }
    while (left < right) {
        long long mid = (left + right) >> 1;
        if (check(nums, numsSize, m, mid)) {
            right = mid;
        } else {
            left = mid + 1;
        }
    }
    return left;
}

最大化最小值:本质是二分答案求最大

1. 礼盒的最大甜蜜度

相关推荐
ヾ慈城3 小时前
【数据结构 - 二叉树】
c语言·数据结构·算法·链表
闲仁人3 小时前
数据结构预科
数据结构
卡戎-caryon3 小时前
【项目实践】贪吃蛇
c语言·数据结构·算法
计算机平台作业答案讲解3 小时前
QT实现GIF动图显示(小白版,可直接copy使用)
服务器·开发语言·数据结构·数据库·c++·qt·动态规划
冲鸭嘟嘟可3 小时前
【数据结构】使用C语言 从零实现一个栈的数据结构
c语言·数据结构·算法
tq025 小时前
Java数据结构-树的面试题
数据结构·面试·职场和发展
Jules_wwy5 小时前
查找——数据结构与算法 总结7
数据结构·查找
bigbigli_大李5 小时前
C++基础21 二维数组及相关问题详解
数据结构·c++·算法
卡戎-caryon7 小时前
【数据结构】06.栈&&队列
c语言·数据结构·算法·链表
2401_858286118 小时前
12.【C语言】创建函数
c语言·开发语言·数据结构