当前位置: 首页 > article >正文

力扣hot100——二分查找

理论总结

可以看一看carl的总结,简短并且通透。

B站视频:二分法

力扣题目链接

二分查找

要考虑两个问题

  • 是while(left<=right)还是while(left<right)
  • 是right=mid+1还是right=mid

要根据区间的情况进行选择

1.左闭右闭区间

用while(left<=right)和right=mid+1

class Solution {
public:
    int search(vector<int>& nums, int target) {
        int left = 0, right = nums.size() - 1;
        while (left <= right) {
            int mid = left + ((right - left) >> 1);//防止溢出
            if (nums[mid] > target) {
                right = mid - 1;

            } else if (nums[mid] < target) {
                left = mid + 1;
            } else
                return mid;
        }
        return -1;
    }
};

2.左闭右开区间

用while(left<right)和right=mid

class Solution {
public:
    int search(vector<int>& nums, int target) {
        int left = 0, right = nums.size();
        while (left < right) {
            int mid = (left + right) / 2;
            if (nums[mid] > target) {
                right = mid ;

            } else if (nums[mid] < target) {
                left = mid + 1;
            } else
                return mid;
        }
        return -1;
    }
};

63.搜索插入位置

题目描述

35. 搜索插入位置

给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。

请必须使用时间复杂度为 O(log n) 的算法。

示例 1:

输入: nums = [1,3,5,6], target = 5
输出: 2

示例 2:

输入: nums = [1,3,5,6], target = 2
输出: 1

示例 3:

输入: nums = [1,3,5,6], target = 7
输出: 4

提示:

  • 1 <= nums.length <= 10^4
  • -10^4 <= nums[i] <= 10^4
  • nums无重复元素升序 排列数组
  • -10^4 <= target <= 10^4

思路:二分法

  1. 二分查找
    • 计算中间位置 mid
    • 如果 nums[mid] 大于目标值,则目标值在左半部分。
    • 如果 nums[mid] 等于目标值,则返回 mid
    • 如果 nums[mid] 小于目标值,则目标值在右半部分。

code

#include <stdio.h>

/**
 * 搜索插入位置函数
 * @param nums: 有序数组
 * @param numsSize: 数组的大小
 * @param target: 目标值
 * @return: 目标值的索引或应该被插入的位置
 */
int searchInsert(int* nums, int numsSize, int target) {
    int l = 0, r = numsSize - 1; // 初始化左右指针
    while (l <= r) {
        int mid = (l + r) >> 1; // 计算中间位置
        if (nums[mid] > target) {
            r = mid - 1; // 目标值在左半部分
        } else if (nums[mid] == target) {
            return mid; // 找到目标值,返回索引
        } else {
            l = mid + 1; // 目标值在右半部分
        }
    }
    // 如果目标值不存在,返回它应该被插入的位置
    return l;
}

64.搜索二维矩阵

题目描述

74. 搜索二维矩阵

给你一个满足下述两条属性的 m x n 整数矩阵:

  • 每行中的整数从左到右按非严格递增顺序排列。
  • 每行的第一个整数大于前一行的最后一个整数。

给你一个整数 target ,如果 target 在矩阵中,返回 true ;否则,返回 false

示例 1:

img

输入:matrix = [[1,3,5,7],[10,11,16,20],[23,30,34,60]], target = 3
输出:true

示例 2:

img

输入:matrix = [[1,3,5,7],[10,11,16,20],[23,30,34,60]], target = 13
输出:false

提示:

  • m == matrix.length
  • n == matrix[i].length
  • 1 <= m, n <= 100
  • -10^4 <= matrix[i][j], target <= 10^4

思路:两次二分

使用 两次二分查找

  1. 第一次二分查找确定目标值可能位于哪一行。
  2. 第二次二分查找在目标行中查找目标值。

code

#include <stdio.h>
#include <stdbool.h>

/**
 * 搜索二维矩阵函数
 * @param matrix: 二维矩阵
 * @param matrixSize: 矩阵的行数
 * @param matrixColSize: 矩阵的列数数组
 * @param target: 目标值
 * @return: 目标值是否存在
 */
bool searchMatrix(int** matrix, int matrixSize, int* matrixColSize, int target) {
    int n = matrixColSize[0]; // 矩阵的列数
    int col = 0; // 目标值可能位于的行
    int l = 0, r = matrixSize - 1;

    // 第一次二分查找:确定目标值可能位于哪一行
    while (l <= r) {
        int mid = (l + r) >> 1; // 计算中间位置
        if (matrix[mid][n - 1] < target) {
            col = mid + 1; // 目标值在下一行
            l = mid + 1;
        } else if (matrix[mid][n - 1] > target) {
            r = mid - 1; // 目标值在当前行或上一行
        } else {
            return true; // 找到目标值
        }
    }

    // 如果目标值可能位于的行超出矩阵范围,返回 false
    if (col == matrixSize) return false;

    // 第二次二分查找:在目标行中查找目标值
    l = 0, r = n - 1;
    while (l <= r) {
        int mid = (l + r) >> 1; // 计算中间位置
        if (matrix[col][mid] > target) {
            r = mid - 1; // 目标值在左半部分
        } else if (matrix[col][mid] < target) {
            l = mid + 1; // 目标值在右半部分
        } else {
            return true; // 找到目标值
        }
    }

    return false; // 未找到目标值
}

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

题目描述

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

给你一个按照非递减顺序排列的整数数组 nums,和一个目标值 target。请你找出给定目标值在数组中的开始位置和结束位置。

如果数组中不存在目标值 target,返回 [-1, -1]

你必须设计并实现时间复杂度为 O(log n) 的算法解决此问题。

示例 1:

输入:nums = [5,7,7,8,8,10], target = 8
输出:[3,4]

示例 2:

输入:nums = [5,7,7,8,8,10], target = 6
输出:[-1,-1]

示例 3:

输入:nums = [], target = 0
输出:[-1,-1]

提示:

  • 0 <= nums.length <= 10^5
  • -10^9 <= nums[i] <= 10^9
  • nums 是一个非递减数组
  • -10^9 <= target <= 10^9

思路:二分

  1. 使用 二分查找 来高效地查找目标值。
  2. 找到目标值后,向左右扩展以确定起始和结束位置。

code

#include <stdio.h>
#include <stdlib.h>

/**
 * 查找目标值的起始和结束位置
 * @param nums: 非递减顺序排列的数组
 * @param numsSize: 数组的大小
 * @param target: 目标值
 * @param returnSize: 返回结果数组的大小
 * @return: 返回目标值的起始和结束位置
 */
int* searchRange(int* nums, int numsSize, int target, int* returnSize) {
    // 初始化结果数组
    int* ans = malloc(sizeof(int) * 2);
    ans[0] = -1;
    ans[1] = -1;
    *returnSize = 2;

    // 如果数组为空,直接返回结果
    if (numsSize == 0) {
        return ans;
    }

    int l = 0, r = numsSize - 1; // 初始化左右指针

    // 二分查找
    while (l <= r) {
        int mid = (l + r) >> 1; // 计算中间位置
        if (nums[mid] < target) {
            l = mid + 1; // 目标值在右半部分
        } else if (nums[mid] > target) {
            r = mid - 1; // 目标值在左半部分
        } else {
            // 找到目标值,向左右扩展以确定起始和结束位置
            ans[0] = mid;
            ans[1] = mid;
            while (ans[0] > 0 && nums[ans[0]] == nums[ans[0] - 1]) ans[0]--;
            while (ans[1] + 1 < numsSize && nums[ans[1]] == nums[ans[1] + 1]) ans[1]++;
            break;
        }
    }

    return ans; // 返回结果
}

66.搜索旋转排序数组

题目描述

33. 搜索旋转排序数组

整数数组 nums 按升序排列,数组中的值 互不相同

在传递给函数之前,nums 在预先未知的某个下标 k0 <= k < nums.length)上进行了 旋转,使数组变为 [nums[k], nums[k+1], ..., nums[n-1], nums[0], nums[1], ..., nums[k-1]](下标 从 0 开始 计数)。例如, [0,1,2,4,5,6,7] 在下标 3 处经旋转后可能变为 [4,5,6,7,0,1,2]

给你 旋转后 的数组 nums 和一个整数 target ,如果 nums 中存在这个目标值 target ,则返回它的下标,否则返回 -1

你必须设计一个时间复杂度为 O(log n) 的算法解决此问题。

示例 1:

输入:nums = [4,5,6,7,0,1,2], target = 0
输出:4

示例 2:

输入:nums = [4,5,6,7,0,1,2], target = 3
输出:-1

示例 3:

输入:nums = [1], target = 0
输出:-1

提示:

  • 1 <= nums.length <= 5000
  • -10^4 <= nums[i] <= 10^4
  • nums 中的每个值都 独一无二
  • 题目数据保证 nums 在预先未知的某个下标上进行了旋转
  • -10^4 <= target <= 10^4

思路:二分法

二分查找

  • 计算中间位置 mid
  • 如果 nums[mid] 等于目标值,则返回 mid
  • 判断左半部分是否有序:
    • 如果 nums[left] <= nums[mid],则左半部分有序。
    • 如果目标值在左半部分的范围内,则在左半部分继续查找。
    • 否则在右半部分继续查找。
  • 如果左半部分无序,则右半部分有序:
    • 如果目标值在右半部分的范围内,则在右半部分继续查找。
    • 否则在左半部分继续查找。

code

#include <stdio.h>

/**
 * 搜索旋转排序数组函数
 * @param nums: 旋转后的有序数组
 * @param numsSize: 数组的大小
 * @param target: 目标值
 * @return: 目标值的索引或 -1
 */
int search(int* nums, int numsSize, int target) {
    int left = 0, right = numsSize - 1; // 初始化左右指针

    // 二分查找
    while (left <= right) {
        int mid = left + (right - left) / 2; // 计算中间位置

        // 如果找到目标值,返回索引
        if (nums[mid] == target) {
            return mid;
        }

        // 判断左半部分是否有序
        if (nums[left] <= nums[mid]) {
            // 如果目标值在左半部分的范围内,则在左半部分继续查找
            if (nums[left] <= target && target < nums[mid]) {
                right = mid - 1;
            } else {
                left = mid + 1; // 否则在右半部分继续查找
            }
        } else {
            // 如果目标值在右半部分的范围内,则在右半部分继续查找
            if (nums[mid] < target && target <= nums[right]) {
                left = mid + 1;
            } else {
                right = mid - 1; // 否则在左半部分继续查找
            }
        }
    }

    return -1; // 未找到目标值
}

67.寻找旋转排序数组中的最小值

题目描述

153. 寻找旋转排序数组中的最小值

已知一个长度为 n 的数组,预先按照升序排列,经由 1n旋转 后,得到输入数组。例如,原数组 nums = [0,1,2,4,5,6,7] 在变化后可能得到:

  • 若旋转 4 次,则可以得到 [4,5,6,7,0,1,2]
  • 若旋转 7 次,则可以得到 [0,1,2,4,5,6,7]

注意,数组 [a[0], a[1], a[2], ..., a[n-1]] 旋转一次 的结果为数组 [a[n-1], a[0], a[1], a[2], ..., a[n-2]]

给你一个元素值 互不相同 的数组 nums ,它原来是一个升序排列的数组,并按上述情形进行了多次旋转。请你找出并返回数组中的 最小元素

你必须设计一个时间复杂度为 O(log n) 的算法解决此问题。

示例 1:

输入:nums = [3,4,5,1,2]
输出:1
解释:原数组为 [1,2,3,4,5] ,旋转 3 次得到输入数组。

示例 2:

输入:nums = [4,5,6,7,0,1,2]
输出:0
解释:原数组为 [0,1,2,4,5,6,7] ,旋转 4 次得到输入数组。

示例 3:

输入:nums = [11,13,15,17]
输出:11
解释:原数组为 [11,13,15,17] ,旋转 4 次得到输入数组。

提示:

  • n == nums.length
  • 1 <= n <= 5000
  • -5000 <= nums[i] <= 5000
  • nums 中的所有整数 互不相同
  • nums 原来是一个升序排序的数组,并进行了 1n 次旋转

思路:二分

二分查找

  • 计算中间位置 mid
  • 如果 nums[mid] 大于等于第一个元素且大于等于最后一个元素,则最小值在右半部分。
  • 如果 nums[mid] 小于等于第一个元素且小于等于最后一个元素,则最小值在左半部分。
  • 否则,最小值是 nums[0]nums[numsSize - 1] 中的较小值。

code

#include <stdio.h>
#include <math.h>

/**
 * 寻找旋转排序数组中的最小值函数
 * @param nums: 旋转后的有序数组
 * @param numsSize: 数组的大小
 * @return: 数组中的最小值
 */
int findMin(int* nums, int numsSize) {
    // 如果数组只有一个元素,直接返回该元素
    if (numsSize == 1) return nums[0];

    int l = 0, r = numsSize - 1; // 初始化左右指针

    // 二分查找
    while (l <= r) {
        int mid = (l + r) >> 1; // 计算中间位置

        // 如果中间元素大于等于第一个元素且大于等于最后一个元素,则最小值在右半部分
        if (nums[mid] >= nums[0] && nums[mid] >= nums[numsSize - 1]) {
            l = mid + 1;
        }
        // 如果中间元素小于等于第一个元素且小于等于最后一个元素,则最小值在左半部分
        else if (nums[mid] <= nums[0] && nums[mid] <= nums[numsSize - 1]) {
            r = mid - 1;
        }
        // 否则,最小值是 nums[0] 和 nums[numsSize - 1] 中的较小值
        else {
            return fmin(nums[0], nums[numsSize - 1]);
        }
    }

    // 返回最小值
    return nums[l];
}

68.寻找两个正序数组的中位数

题目描述

4. 寻找两个正序数组的中位数

给定两个大小分别为 mn 的正序(从小到大)数组 nums1nums2。请你找出并返回这两个正序数组的 中位数

算法的时间复杂度应该为 O(log (m+n))

示例 1:

输入:nums1 = [1,3], nums2 = [2]
输出:2.00000
解释:合并数组 = [1,2,3] ,中位数 2

示例 2:

输入:nums1 = [1,2], nums2 = [3,4]
输出:2.50000
解释:合并数组 = [1,2,3,4] ,中位数 (2 + 3) / 2 = 2.5

提示:

  • nums1.length == m
  • nums2.length == n
  • 0 <= m <= 1000
  • 0 <= n <= 1000
  • 1 <= m + n <= 2000
  • -10^6 <= nums1[i], nums2[i] <= 10^6

思路

1.中位数的定义:

  • 如果两个数组的总长度是奇数,则中位数是第 (m + n + 1) / 2 个数。

  • 如果总长度是偶数,则中位数是第 (m + n) / 2 个数和第 (m + n) / 2 + 1 个数的平均值。

2.核心思想:

  • 将问题转化为在两个有序数组中寻找第 k 小的数。

  • 使用二分查找的思想,每次排除掉一部分不可能包含第 k 小数的元素。

  • 每次比较两个数组的第 k/2 个元素,排除掉较小的部分。

只看思路可能有些抽象,可以结合代码和注释理解

code

#include <stdio.h>
#include <stdlib.h>

// 辅助函数:找到两个有序数组中第 k 小的数
int findKth(int* nums1, int nums1Size, int* nums2, int nums2Size, int k) {
    // 确保 nums1 是较短的数组
    if (nums1Size > nums2Size) {
        return findKth(nums2, nums2Size, nums1, nums1Size, k);
    }
    // 如果 nums1 为空,则直接返回 nums2 的第 k 小的数
    if (nums1Size == 0) {
        return nums2[k - 1];
    }
    // 如果 k == 1,则返回两个数组的第一个元素中的较小值
    if (k == 1) {
        return nums1[0] < nums2[0] ? nums1[0] : nums2[0];
    }

    // 在 nums1 和 nums2 中分别取第 k/2 个元素
    int i = nums1Size < k / 2 ? nums1Size : k / 2;
    int j = nums2Size < k / 2 ? nums2Size : k / 2;

    // 如果 nums1[i - 1] < nums2[j - 1],则排除 nums1 的前 i 个元素
    if (nums1[i - 1] < nums2[j - 1]) {
        return findKth(nums1 + i, nums1Size - i, nums2, nums2Size, k - i);
    } else {
        // 否则排除 nums2 的前 j 个元素
        return findKth(nums1, nums1Size, nums2 + j, nums2Size - j, k - j);
    }
}

/**
 * 寻找两个正序数组的中位数函数
 * @param nums1: 第一个有序数组
 * @param nums1Size: 第一个数组的大小
 * @param nums2: 第二个有序数组
 * @param nums2Size: 第二个数组的大小
 * @return: 两个数组的中位数
 */
double findMedianSortedArrays(int* nums1, int nums1Size, int* nums2, int nums2Size) {
    int total = nums1Size + nums2Size;
    if (total % 2 == 1) {
        // 如果总长度是奇数,返回第 (total + 1) / 2 小的数
        return findKth(nums1, nums1Size, nums2, nums2Size, (total + 1) / 2);
    } else {
        // 如果总长度是偶数,返回第 total / 2 小的数和第 total / 2 + 1 小的数的平均值
        int left = findKth(nums1, nums1Size, nums2, nums2Size, total / 2);
        int right = findKth(nums1, nums1Size, nums2, nums2Size, total / 2 + 1);
        return (left + right) / 2.0;
    }
}

http://www.kler.cn/a/570708.html

相关文章:

  • 养老小程序方案详解居家养老小程序系统
  • BIO、NIO、AIO、Netty从简单理解到使用
  • 2.数据结构:1.Tire 字符串统计
  • 【蓝桥杯单片机】第十二届省赛
  • 构建私有化AI知识库:基于CentOS的Ollama + DeepSeek-R1 +ragflow 整合部署教程
  • Android framwork 详细开发指南
  • 【UCB CS 61B SP24】Lecture 19 20: Hashing Hashing II 学习笔记
  • 跳石子(贪心)
  • 电机堵转电流与加减速箱堵转电流的关系
  • C++:四大强制类型转换
  • Kafka底层结构
  • 【算法系列】基数排序
  • 【CSS—前端快速入门】CSS 选择器
  • 内网渗透信息收集linuxkali扫描ip段,收集脚本(web安全)
  • FlashMLA(DeepSeek开源周,第一个框架):含源码分析
  • DDD该怎么去落地实现(4)多对多关系
  • Docker安装Postgres_16数据库
  • cordova app webpack升级为vite
  • B3DM转换成OBJ
  • 图论-腐烂的橘子