思路是用二分法筛选该元素位序,计算有多少元素小于等于该位序的元素,与期望值k比较,请问问题在哪,怎么改进
本题的二分法确实具有迷惑性,题主的二分法将矩阵的下标和矩阵的值搞混了。下图是题解中的矩阵举例,每行呈递增趋势,每列呈递增趋势,并不是后一行一定大于前一行,如matrix[3][0] < matrix[0][3]. 这可能是造成题主理解错题目的根本原因。
下面给出了修改后的代码,并附带了每一处修改的原因。代码经过leetcode验证通过率100%。
class Solution {
public:
int kthSmallest(vector<vector<int>>&matrix,int k){
int count=0;
int n = matrix.size()*matrix[0].size();
// int left=0;
// int right=n-1; 修改此处【1】,left是矩阵的最小值,right是矩阵的最大值。
int left = matrix[0][0];
int right = matrix[matrix.size()-1][matrix.size()-1];
int mid;
while(left<right){
mid=left+(right-left)/2;
int i=matrix.size()-1,j=0;
// count = 0; count每统计完一次要清零
// while(i>=0&&j<=matrix[0].size()-1){
// if(matrix[i][j]>matrix[mid/n][mid%n]){ 修改此处【2】, 和【1】的修改理由相同,mid是数值不是下标
// j++;
// }
// else i--;
// count++; // count的统计方式不对
// }
while(i>=0&&j<=matrix[0].size()-1){
if(matrix[i][j]<=mid){
count += i+1;
j++;
}
else i--;
}
// if(count>k){ 修改【3】,此处的二分法区间对应错误
// left=mid+1;
// }
// else if(count<k){
// right=mid-1;
// }
if(count >= k){
right=mid;
}
else if(count<k){
left = mid+1;
}
}
// return matrix[mid/n][mid%n]; 这里同样是,left是返回值
return left;
}
};
关于第2处修改,题主可能会有疑惑,为什么count += i+1; 这里其实是按列统计的,如下图,扫描到6时,将6之上的一整列统计进去,因为其上的数必然小于6,也就小于mid。
因为每次循环中都保证了第k小的数在left~right之间,当left==right时,第k小的数即被找出,等于right或left
按照它的要求你可以用堆排
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
int kthSmallest(vector<vector<int>>& matrix, int k) {
int n = matrix.size();
priority_queue<int> maxHeap;
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
maxHeap.push(matrix[i][j]);
if (maxHeap.size() > k) {
maxHeap.pop();
}
}
}
return maxHeap.top();
}
int main() {
vector<vector<int>> matrix = {{1, 5, 9}, {10, 11, 13}, {12, 13, 15}};
int k = 8;
int result = kthSmallest(matrix, k);
cout << "The " << k << "th smallest element is: " << result << endl;
return 0;
}
引用 皆我百晓生 小程序回复内容作答:
在你的代码中,有几个问题需要改进:
在二分查找中,你没有更新 left 和 right 的值,导致死循环。你需要在 if(count > k) 和 if(count < k) 的条件语句中更新 left 和 right。
在计算 count 的时候,你没有考虑相等的情况。如果 matrix[i][j] == matrix[mid/n][mid%n],count 也需要加1。
在执行 while 循环之前,你应该先检查左上角元素和右下角元素,以确保它们不为空,
以下是修正后的代码:
class Solution {
public:
int kthSmallest(vector<vector<int>>& matrix, int k) {
int left = matrix[0][0];
int right = matrix.back().back();
while (left < right) {
int mid = left + (right - left) / 2;
int count = 0;
int j = matrix[0].size() - 1;
for (int i = 0; i < matrix.size(); i++) {
while (j >= 0 && matrix[i][j] > mid) {
j--;
}
count += (j + 1);
}
if (count < k) {
left = mid + 1;
} else {
right = mid;
}
}
return left;
}
};
这样修改之后,你的代码应该可以通过测试了。
二分法是一种非常有效的寻找数组中第 k 小元素的方法。这种方法的基本思想是将数组视为一个有序序列,通过不断将序列划分为两部分并比较中间元素与目标 k 的大小关系,最终找到第 k 小的元素。
下面是使用二分法寻找数组中第 k 小元素的步骤:
left
和 right
,分别指向数组的首尾元素。left <= right
时进行迭代:mid
,取整数部分,即 mid = left + (right - left) / 2
。nums[mid]
。nums[mid]
的元素个数 count
。count < k
,说明第 k 小的元素在右半部分,更新 left = mid + 1
。count >= k
,说明第 k 小的元素在左半部分或者就是中间元素,更新 right = mid - 1
。nums[left]
,即为数组中的第 k 小元素。下面是使用 Python 实现的代码示例:
def kthSmallest(nums, k):
left, right = 0, len(nums) - 1
while left <= right:
mid = left + (right - left) // 2
count = sum(num <= nums[mid] for num in nums)
if count < k:
left = mid + 1
else:
right = mid - 1
return nums[left]
请注意,在使用二分法时,需要确保数组中的元素没有重复值,否则可能会导致结果错误。如果数组中可能存在重复值,可以考虑使用其他方法,如快速选择算法。
【相关推荐】
结合GPT给出回答如下请题主参考
问题在于二分法只能帮我们找到一个可能的答案,但并不能保证这个答案就是第k小的元素。因为可能存在多个元素具有相同的值,而这些元素在排序后的位置可能不止一个,导致我们无法确定哪一个才是第k小的元素。
改进的方法可以是我们在进行二分查找时,不仅要计算有多少元素小于等于该位序的元素,还需要记录小于该位序的元素的集合,然后对这个集合进行一次排序。这样就能够确定该位序对应的元素在整个集合中的排序位置,然后再和期望值k进行比较即可。如果期望值k比该元素在集合中的排序位置小,则在小于该位序的集合中继续进行二分查找;反之,在大于等于该位序的集合中继续进行二分查找。这样就可以保证找到的元素就是第k小的元素了。