二分法---打开新思路

一.寻找重复数

力扣传送门
题目:给定一个包含 n + 1 个整数的数组 nums,其数字都在 1 到 n 之间(包括 1 和 n),可知至少存在一个重复的整数。假设只有一个重复的整数,找出这个重复的数。
示例 1:
输入: [1,3,4,2,2]
输出: 2

示例 2:
输入: [3,1,3,4,2]
输出: 3

说明:
不能更改原数组(假设数组是只读的)。
只能使用额外的 O(1) 的空间。
时间复杂度小于 O(n2) 。
数组中只有一个重复的数字,但它可能不止重复出现一次。
解法一:常规解法,改变了原数组

 public int findDuplicate(int[] nums) {//解法一:把数字i,交换到i的位置上去,如果已经存在了,就说明重复
        if (nums == null || nums.length == 0) return -1;
        int n = nums.length;
        int i = 0;
        while (i < n) {
            if (i != nums[i]) {//索引i的上数字nums[i]不等于i,就考虑把nums[i]交换到索引为nums[i]的位置上去
                if (nums[nums[i]] == nums[i]) {//首先要判断索引为nums[i]的位置上是否已经存在数字nums[i]
                    return nums[i];
                } else {//不存在就交换.此时不可以i++,因为还要继续判断交换后的数字
                    swap(nums, i, nums[i]);
                }
            } else {
                i++;
            }
        }
        return -1;
    }

    public void swap(int nums[], int i, int j) {
        int tmp = nums[i];
        nums[i] = nums[j];
        nums[j] = tmp;
    }

解法二:二分

public int findDuplicate2(int[] nums) {//解法二:如果题目要求不能改变数组,就可以用二分法
        int left = 0;
        int right = nums.length - 1;
        while (left < right) {
            int mid = (left + right) / 2;//找到中间位置的索引mid
            int cnt = 0;
            for (int i = 0; i < nums.length; i++) {
                if (nums[i] <= mid) cnt++;//在整个数组中统计小于等于mid的数量
            }
            if (cnt > mid) {//如果大于mid说明左面有重复
                right = mid;
            } else {
                left = mid + 1;
            }
        }
        return left;
    }

二.有序矩阵中第K小的元素

力扣传送门
题目:给定一个 n x n 矩阵,其中每行和每列元素均按升序排序,找到矩阵中第k小的元素。
请注意,它是排序后的第 k 小元素,而不是第 k 个不同的元素。
在这里插入图片描述
解法一:维护一个k个大的大顶堆
解法二:二分法

 /*
    * 有序矩阵中第k小的元素
    *
    * */
    public int kthSmallest(int[][] matrix, int k) {
        int left = matrix[0][0];
        int m = matrix.length, n = matrix[0].length;
        int right = matrix[m - 1][n - 1];
        while (left < right) {
            int mid = (left + right) / 2;//找到中位数
            int cnt = 0;
            for (int i = 0; i < m; i++) {
                for (int j = 0; j < n; j++) {
                    if (matrix[i][j] <= mid) cnt++;//遍历数组中所有元素,统计小于等于中位数的元素的个数
                }
            }
            if(cnt>=k){//如果小于等于中位数的元素的个数大于等于k个,说明这个数字在mid左边或就是mid
                right=mid;
            }else{
                left=mid+1;
            }
        }
        return left;
    }
发布了184 篇原创文章 · 获赞 60 · 访问量 16万+

猜你喜欢

转载自blog.csdn.net/StubbornAccepted/article/details/105306227
今日推荐