二分查找
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. 寻找比目标字母大的最小字母
代码实现:
cppint 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. 两个数组间的距离值
代码实现:
方法一:暴力法
cppint 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; }
方法二:二分法
cppvoid 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. 准时到达的列车最小时速
代码实现:
cppdouble 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指数 ||
代码实现:
cppint 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. 每个小孩最多能分到多少糖果
代码实现:
cppint 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. 正方形中的最多点数
代码实现:
cppint 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. 分割数组的最大值
代码实现:
cppbool 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; }