一.寻找重复数
力扣传送门
题目:给定一个包含 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;
}