当前位置: 首页> 汽车> 报价 > 苏州优化网站哪家好_成都网站建设推来客网站系统_电子商务网站推广_seo技术推广

苏州优化网站哪家好_成都网站建设推来客网站系统_电子商务网站推广_seo技术推广

时间:2025/8/23 13:38:47来源:https://blog.csdn.net/qq_43060884/article/details/143568343 浏览次数: 0次
苏州优化网站哪家好_成都网站建设推来客网站系统_电子商务网站推广_seo技术推广

关于二分,之前也写过一篇,参考二分Acwing

1.搜索插入位置

在这里插入图片描述思路分析:典型的 二分查找算法,用于在一个已排序的数组中查找目标值的位置。如果找到了目标值,返回其索引;如果没有找到,则返回目标值应该插入的位置。

  • 初始化左右边界
  • 二分查找
    • 每次计算mid,通过 mid = left + (right - left) / 2 来避免可能的溢出;
    • 如果目标值 target 与 nums[mid] 相等,则返回 mid
    • 如果 nums[mid] 大于 target,则将右边界 right 移动到 mid - 1,继续在左半边查找;
    • 如果 nums[mid] 小于 target,则将左边界 left 移动到 mid + 1,继续在右半边查找。
  • 返回插入位置:如果没有找到目标值,left 会指向目标值应该插入的位置。此时,left 就是目标值应该插入的索引。

具体实现代码(详解版):

class Solution {
public:int searchInsert(vector<int>& nums, int target) {int left = 0, right = nums.size() - 1;  // 初始化左右边界while (left <= right) {  // 当左边界不超过右边界时继续查找int mid = left + (right - left) / 2;  // 计算中间位置,避免溢出if (nums[mid] == target) {  // 如果找到了目标值return mid;  // 返回该值的索引} else if (nums[mid] > target) {  // 如果目标值小于中间值right = mid - 1;  // 向左半边查找} else {  // 如果目标值大于中间值left = mid + 1;  // 向右半边查找}}return left;  // 返回左边界位置,即目标值应插入的位置}
};
  • 时间复杂度:O(log n),每次查找都会将搜索空间减小一半,最多执行log n次;
  • 空间复杂度:O(1)

2.搜索二维矩阵

在这里插入图片描述
思路分析:由于每一行是升序排列的,而且每列也是升序排列的,我们可以通过二分查找直接在二维矩阵中进行查找,而不需要将矩阵展平为一维数组。

  • 单一的二分查找:我们将二维矩阵视为一个有序的 1D 数组,矩阵的元素总数是 m * n。关键点是将每个中间索引 mid 映射到二维矩阵中的位置,mid / n 对应行,mid % n 对应列。;
  • 索引映射:通过 mid / n 得到对应的行索引,通过 mid % n 得到列索引。这种映射方式相当于将矩阵展平,但不需要额外的空间。

具体实现代码(详解版):

class Solution {
public:bool searchMatrix(vector<vector<int>>& matrix, int target) {if (matrix.empty() || matrix[0].empty()) return false;int m = matrix.size();int n = matrix[0].size();int left = 0, right = m * n - 1;while (left <= right) {int mid = left + (right - left) / 2;//就这一句不同,yydsint midValue = matrix[mid / n][mid % n]; // 将 1D 索引转换为 2D 索引if (midValue == target) {return true;} else if (midValue < target) {left = mid + 1;} else {right = mid - 1;}}return false;}
};
  • 时间复杂度:O(log(m * n))
  • 空间复杂度:O(1)

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

在这里插入图片描述
思路分析:这个问题可以分为两个子问题:查找目标值的起始位置,查找目标值的结束位置;

  • 1.查找起始位置

    • 设定两个指针,分别指向数组的左右端点;
    • 在每次比较时,我们计算中间位置 mid,然后根据数组的值来调整左右边界;
      • 如果 nums[mid] 大于或等于 target,我们就缩小右边界 r = mid,因为目标值可能在当前的 mid 或左边部分。
      • 如果 nums[mid] 小于 target,说明目标值在右边,因此移动左边界 l = mid + 1。
    • 当 l 和 r 收敛时,我们检查 nums[l] 是否等于 target,如果是,l 就是目标值的 起始位置。
  • 2.查找结束位置

    • 查找 结束位置 的方法与查找起始位置非常相似,但这次我们需要找到 最后一个出现的目标值。因此,在二分查找过程中,当目标值出现时,我们继续往右查找,直到找到最后一个目标值。
    • 使用两个指针 l2 和 r2,开始时设定为整个数组的范围。
    • 计算中间位置 mid,并根据 nums[mid] 和 target 的关系来调整左右边界:
      • 如果 nums[mid] 小于或等于 target,我们可能找到了目标值的最后位置,因此继续向右查找,调整 l2 = mid。
      • 如果 nums[mid] 大于 target,则目标值只能在 mid 左边,因此调整右边界 r2 = mid - 1。
    • 当 l2 和 r2 收敛时,l2 就是目标值的 结束位置。

具体实现代码(详解版)

class Solution {
public:vector<int> searchRange(vector<int>& nums, int target) {vector<int> res = {-1, -1};if (nums.empty()) {return res;}// 查找目标值的起始位置int l = 0, r = nums.size() - 1;while (l < r) {int mid = l + (r - l) / 2; // 防止溢出if (nums[mid] >= target) {r = mid; // 目标值可能在左半部分或是 mid 本身} else {l = mid + 1;}}// 确保 nums[l] 是目标值if (nums[l] != target) {return res; // 如果没有找到目标值,直接返回 {-1, -1}}int start = l;// 查找目标值的结束位置int l2 = 0, r2 = nums.size() - 1;while (l2 < r2) {int mid = l2 + (r2 - l2 + 1) / 2; // 防止溢出if (nums[mid] <= target) {l2 = mid; // 目标值可能在右半部分或是 mid 本身} else {r2 = mid - 1;}}int end = l2;// 返回目标值的起始位置和结束位置return {start, end};}
};
  • 时间复杂度:O(log n);
  • 空间复杂度:O(1)

4.搜搜旋转排序数组

在这里插入图片描述
思路分析:旋转后的数组会分为两个升序的部分:一部分可能是从旋转点到数组的末尾,另一部分是从数组的开头到旋转点。我们可以利用二分查找,但需要判断中间元素的位置是处于旋转后的哪个部分。通过这个判断,我们能够缩小搜索范围。

  • 初始化两个指针 left 和 right,分别指向数组的头和尾。;
  • 计算中间位置 mid = left + (right - left) / 2
  • 判断 nums[mid] 是否等于目标值 target,如果是,直接返回 mid。
  • 判断 nums[mid] 是否等于目标值 target,如果是,直接返回 mid。
    • 如果左半部分升序:nums[left] <= nums[mid],此时:
      • 如果 nums[left] <= target < nums[mid],目标值在左半部分,更新 right = mid - 1;
      • 否则,目标值在右半部分,更新 left = mid + 1。
    • 如果右半部分升序:nums[mid] <= nums[right],此时:
      • 如果 nums[mid] < target <= nums[right],目标值在右半部分,更新 left = mid + 1;
      • 否则,目标值在左半部分,更新 right = mid - 1。
  • 如果 left 超过 right,说明目标值不存在于数组中,返回 -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) / 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;  // 找不到目标值}
};

5.寻找寻转数组中的最小值

在这里插入图片描述
思路分析:旋转的关键在于数组的最小值。最小值一定在旋转点处,旋转后的数组就像是两个升序数组拼接在一起,最小值一定是较小的部分的第一个元素。通过二分查找来高效地找到最小值的位置:

  • 如果 nums[mid] >= nums[right],说明旋转点在右半部分或是 mid 本身可能是旋转点的前一个位置。此时最小值必然在右半部分,我们将 left = mid + 1。
  • 否则,说明最小值在左半部分或 mid 本身就是最小值,我们将 right = mid。
  • 最终,left == right,nums[left]就是最小值。

具体实现代码(详解版):

class Solution {
public:int findMin(vector<int>& nums) {int left = 0, right = nums.size() - 1;// 二分查找while (left < right) {int mid = left + (right - left) / 2;// 如果 mid 元素大于右边的元素,最小值一定在右边if (nums[mid] > nums[right]) {left = mid + 1;} else {// 如果 mid 元素小于等于右边的元素,最小值可能在左边right = mid;}}// 当 left == right 时,left 就是最小值的位置return nums[left];}
};

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

在这里插入图片描述

真实难题!
思路分析:由于我们需要在两个有序数组中找到中位数,可以考虑通过二分查找的方式,在一个数组中找到合适的分割点,并利用这个分割点将另一个数组分成合适的部分。

  • 中位数的定义:如果合并两个数组后的总元素数是奇数,那么中位数就是中间那个元素;如果合并两个数组后的总元素数是偶数,那么中位数是中间两个元素的平均值。
  • 通过二分查找进行分割
    • 我们将数组 nums1 和 nums2 分成两部分,使得:
      • 左边部分包含的是小于等于右边部分的元素。
      • 两个数组的左部分和右部分的元素个数总是相等(或者左部分多一个)。
    • 我们希望通过二分查找来找到数组 nums1 中的分割点 i,从而通过 i 可以推算出数组 nums2 中的分割点 j。
    • 条件检查:对于每一对 i 和 j,我们检查是否满足:
      • nums1[i-1] <= nums2[j](nums1 左半部分最大值小于等于 nums2 右半部分最小值)
      • nums2[j-1] <= nums1[i](nums2 左半部分最大值小于等于 nums1 右半部分最小值)
      • 如果满足条件,计算并返回中位数。
    • 如果不满足上述条件,调整 i,继续二分查找。

具体实现代码(详解版):

class Solution {
public:double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {if (nums1.size() > nums2.size()) {// 确保 nums1 是较小的数组return findMedianSortedArrays(nums2, nums1);}int m = nums1.size();int n = nums2.size();int left = 0, right = m;while (left <= right) {int i = left + (right - left) / 2;  // nums1 的分割点int j = (m + n + 1) / 2 - i;       // nums2 的分割点// 处理 nums1[i-1] 和 nums2[j-1] 可能越界的情况int nums1LeftMax = (i == 0) ? INT_MIN : nums1[i - 1];int nums1RightMin = (i == m) ? INT_MAX : nums1[i];int nums2LeftMax = (j == 0) ? INT_MIN : nums2[j - 1];int nums2RightMin = (j == n) ? INT_MAX : nums2[j];// 检查是否找到了合适的分割点if (nums1LeftMax <= nums2RightMin && nums2LeftMax <= nums1RightMin) {// 如果总数为奇数,返回左半部分最大值if ((m + n) % 2 == 1) {return max(nums1LeftMax, nums2LeftMax);}// 如果总数为偶数,返回两者中间的平均值return (max(nums1LeftMax, nums2LeftMax) + min(nums1RightMin, nums2RightMin)) / 2.0;} else if (nums1LeftMax > nums2RightMin) {// nums1[i-1] 太大,缩小 iright = i - 1;} else {// nums2[j-1] 太大,增大 ileft = i + 1;}}return 0.0;}
};
  • 时间复杂度:O(log(min(m,n))
  • 空间复杂度:O(1)
关键字:苏州优化网站哪家好_成都网站建设推来客网站系统_电子商务网站推广_seo技术推广

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

责任编辑: