二分查找

  1. x 的平方根
int mySqrt(int x) {
    if (x <= 1)
        return x;
    int left = 0, right = x; // 防止left+right溢出, left为0而不是1
    int mid;
    while (left <= right) {
        mid = (left + right) / 2;
        if (x / mid == mid) {
            return mid;
        } else if (x / mid < mid) {
            right = mid - 1;
        } else {
            left = mid + 1;
        }
    }
    return right;
}
  1. 在排序数组中查找元素的第一个和最后一个位置

解法1:先用二分法找到一个目标值,然后再往两边遍历(不满足时间复杂度为O(logN))

vector<int> searchRange(vector<int> &nums, int target) {
    int low = 0, high = nums.size() - 1, mid;
    int first = -1, last = -1;
    while (low <= high) {
        mid = (low + high) / 2;
        if (nums[mid] == target) {
            first = last = mid;
            break;
        } else if (nums[mid] > target) {
            high = mid - 1;
        } else {
            low = mid + 1;
        }
    }
    if (first == -1)return {-1, -1};
    int tmp = first - 1;
    while (tmp >= 0 && nums[tmp] == target) {
        tmp--;
    }
    first = tmp + 1;
    tmp = last + 1;
    while (tmp < nums.size() && nums[tmp] == target) {
        tmp++;
    }
    last = tmp - 1;
    return {first, last};
}

解法2:用二分法分别查找目标值的左边界和右边界

int lower(vector<int>& nums, int target) {
    if (nums.size() == 0)
        return -1;
    int left = 0, right = nums.size() - 1, mid;
    while (left <= right) {
        mid = (left + right) / 2;
        if (nums[mid] < target) {
            left = mid + 1;
        } else {
            right = mid - 1;
        }
    }
    if (left >= nums.size() || nums[left] != target)
        return -1;
    return left;
}

int upper(vector<int>& nums, int target) {
    if (nums.size() == 0)
        return -1;
    int left = 0, right = nums.size() - 1, mid;
    while (left <= right) {
        mid = (left + right) / 2;
        if (nums[mid] <= target) {
            left = mid + 1;
        } else {
            right = mid - 1;
        }
    }
    if (right >= 0 && nums[right] == target)
        return right;
    return -1;
}

vector<int> searchRange(vector<int>& nums, int target) {
    int first = lower(nums, target);
    if (first == -1)
        return {-1, -1};
    int last = upper(nums, target);
    return {first, last};
}
  1. 搜索旋转排序数组 II
bool search(vector<int>& nums, int target) {
    int low = 0, high = nums.size() - 1, mid;
    while (low <= high) {
        mid = (low + high) / 2;
        if (nums[mid] == target)
            return true;
        if (nums[mid] > nums[low]) {// 说明[low,mid]是排好序的
            if (target >= nums[low] && target < nums[mid]) {
                high = mid - 1;
            } else {
                low = mid + 1;
            }
        } else if (nums[mid] < nums[low]) {// 说明[mid,high]是排好序的
            if (target > nums[mid] && target <= nums[high]) {
                low = mid + 1;
            } else {
                high = mid - 1;
            }
        } else {// nums[mid]=nums[low],不能判断[low,mid]全部相同还是[mid,high]全部相同
            low++;
        }
    }
    return false;
}
  1. 寻找旋转排序数组中的最小值 II

解法1:暴力求解,找到第一个下降的元素即为最小的元素

int findMin(vector<int>& nums) {
    int res = 0;
    for (int i = 0; i < nums.size() - 1; ++i) {
        if (nums[i] > nums[i + 1]) {
            res = i + 1;
            break;
        }
    }
    return nums[res];
}

解法2:二分法,参考上一题 81. 搜索旋转排序数组 II

int findMin(vector<int>& nums) {
    int low = 0, high = nums.size() - 1, mid;
    while (low < high) {
        if (low + 1 == high)
            return min(nums[low], nums[high]);
        mid = (low + high) / 2;
        if (nums[mid] > nums[low]) {
            if (nums[mid] <= nums[high]) {
                return nums[low];
            } else {
                low = mid + 1;
            }
        } else if (nums[mid] < nums[low]) {
            high = mid;
        } else {
            low++;
        }
    }
    return nums[low];
}

解法3:解法2的变形,将nums[mid]和nums[high]进行比较来判断有序区间,

int findMin(vector<int>& nums) {
    int low = 0, high = nums.size() - 1, mid;
    while (low < high) {
        mid = (low + high) / 2;
        if (nums[mid] > nums[high]) {
            low = mid + 1;
        } else if (nums[mid] < nums[high]) {
            high = mid;
        } else {
            high--;
        }
    }
    return nums[low];
}
  1. 有序数组中的单一元素

解法1:使用异或:x^0=x, x^x=0

int res = 0;
    for (int i = 0; i < nums.size(); ++i) {
        res ^= nums[i];
    }
    return res;

解法2:根据每对元素的下标变化来进行二分查找单一元素的下标(单一元素之前的每队元素的下标是偶奇偶奇,单一元素之后的每队元素的下标是奇偶奇偶)

int singleNonDuplicate(vector<int>& nums) {
    int low = 0, high = nums.size() - 1, mid;
    while (low < high) {
        mid = (low + high) / 2;
        if (nums[mid] != nums[mid - 1] && nums[mid] != nums[mid + 1])
            return nums[mid];
        if (nums[mid] == nums[mid - 1]) {
            if (mid % 2 == 1) {
                low = mid + 1;
            } else {
                high = mid - 2;
            }
        } else {
            if (mid % 2 == 0) {
                low = mid + 2;
            } else {
                high = mid - 1;
            }
        }
    }
    return nums[low];
}
  1. 寻找两个正序数组的中位数

解法1:将两个数组合并后求中位数,略
解法2:已知两个正序数组长度分别是m和n,则中位数所在下标已确定(若m+n是奇数则中位数下标为(m+n)/2;若m+n是偶数则中位数下标为(m+n-1)/2和(m+n)/2),无需真正合并两个数组。

double findMedianSortedArrays2(vector<int> &nums1, vector<int> &nums2) {
    int m = nums1.size(), n = nums2.size();
    int index1 = 0, index2 = 0;
    int left = 0, right = 0;
    for (int i = 0; i <= (m + n) / 2; ++i) {
        left = right;
        if (index1 < m && (index2 >= n || nums1[index1] <= nums2[index2])) {
            right = nums1[index1++];
        } else {
            right = nums2[index2++];
        }
    }
    if ((m + n) % 2 == 0) {
        return (left + right) / 2.0;
    }
    return right;
}

解法3:使用二分法查找两个有序数组中第K小的数

//根据两个有序数组的长度可以求出中位数的下标,因此这道题可以转化成求第k小的元素
//1.每次取nums1[k/2-1]和nums2[k/2-1]进行比较
//2.如果nums1[k/2-1]<nums2[k/2-1],则排除nums1[0]-nums1[k/2-1]这k/2个元素
//3.如果nums1[k/2-1]>nums2[k/2-1],则排除nums2[0]-nums2[k/2-1]这k/2个元素
//4.然后k=k-已排除元素的个数
int getKthMinElement(vector<int> &nums1, vector<int> &nums2, int k) {
    int m = nums1.size(), n = nums2.size();
    int index1 = 0, index2 = 0, newIndex1, newIndex2;
    while (true) {
        // 二分查找结束的三种情况: nums1已全部遍历/nums2已全部遍历/已确定k-1个元素
        if (index1 == m) return nums2[index2 + k - 1];
        if (index2 == n) return nums1[index1 + k - 1];
        if (k == 1) return min(nums1[index1], nums2[index2]);
        newIndex1 = min(index1 + k / 2 - 1, m - 1);
        newIndex2 = min(index2 + k / 2 - 1, n - 1);
        if (nums1[newIndex1] <= nums2[newIndex2]) {
            k -= newIndex1 - index1 + 1;
            index1 = newIndex1 + 1;
        } else {
            k -= newIndex2 - index2 + 1;
            index2 = newIndex2 + 1;
        }
    }
}

double findMedianSortedArrays(vector<int> &nums1, vector<int> &nums2) {
    int m = nums1.size(), n = nums2.size();
    if ((m + n) % 2 == 1) {
        return getKthMinElement(nums1, nums2, (m + n) / 2 + 1);
    } else {
        return (getKthMinElement(nums1, nums2, (m + n) / 2) + getKthMinElement(nums1, nums2, (m + n) / 2 + 1)) /
               2.0;
    }
}
posted @ 2025-07-16 21:01  dengkang1122  阅读(7)  评论(0)    收藏  举报