文章目录
- 1 题目理解
- 2 分析思路
- 2.1 桶排序
- 2.2 二分+动态规划
- 2.3 用堆实现
- 3相似题目786
- 3.1 二分
- 3.2 堆
1 题目理解
输入:一个int数组nums,一个数字k
输出:返回所有数字对中,数字对距离第k小的距离。
规则:一个数组中两两配对计算差值得到距离。距离值从小到大排序,第k位置的元素就是返回值。
例如
Input:
nums = [1,3,1]
k = 1
Output: 0
所有的配对距离是:
(1,3) -> 2
(1,1) -> 0
(3,1) -> 2
所有距离中从小到大排序,第1个是0,所以返回0。
Note:
2 <= len(nums) <= 10000.
0 <= nums[i] < 1000000.
1 <= k <= len(nums) * (len(nums) - 1) / 2.
2 分析思路
内容来源于花花酱。
2.1 桶排序
最直接的想法是用两个for循环计算每个匹配对的距离,然后对距离进行排序。返回第k个元素 。排序算法选择桶排序,加快速度。int[] distance记录距离出现的次数。distance[0]表示有几个配对的距离为0。
class Solution {public int smallestDistancePair(int[] nums, int k) {Arrays.sort(nums);int n = nums.length;int max = nums[n-1];int[] distance = new int[max+1];for(int i=0;i<n;i++){for(int j=i+1;j<n;j++){distance[nums[j] - nums[i]]++;}}for(int i = 0;i<max;i++){k -= distance[i];if(k<=0){return i;}}return 0;}
}
时间复杂度O(n2max)O(n^2max)O(n2max)
2.2 二分+动态规划
我们先对数组nums排序。
返回值最小值是0,最大值=nums最大值-nums最小值。
用二分法查找返回值,也就是距离。
例如m=3,我们需要查找有多少个配对的距离小于等于3。
查找的方法使用动态规划。
设dp[0] 为所有和nums[0]配对的距离小于等于3,有多少个。
dp[1]为所有和nums[0],nums[1]的配对距离小于等于3,有多少个。
…
dp[n-1]表示所有配对中距离小于等于3,有多少个。
例如nums={1,1,3,5,8}。
最开始,i=0,j=0,
nums[0] - nums[0] =0<=m,成立,j++;
nums[1]-nums[0]=1-1=0<=m,成立,j++;
nums[2]-nums[0]=3-1=2<=m,成立,j++;
nums[3]-nums[0]=5-1=4<=m,不成立,退出循环,因为数组是有序的,再比较下去,距离只会越来越大。
那么dp[0] = j-i-1=3-0-1=2,表示所有和nums[0]配对中,有2个配对的距离<=m。
接着计算i=1,此时j不需要从0开始。
配对(0,2)在上一步已经计算了,配对(1,2)的距离一定<=m。
因为nums[2]-nums[0]<=m,在上一步已经计算过了,而nums[1]>nums[0],同样的数减去一个更大的数,差只能更小。
所以j不需要从0开始。
j=3,nums[3]-nums[1]=5-1=4<=m,不成立,退出循环。
dp[1] = dp[0] + (j-i-1)=3,表示所有和nums[0]、nums[1]配对中,有3个配对的距离<=m。
接着计算i=4,j=3。
…
class Solution {public int smallestDistancePair(int[] nums, int k) {Arrays.sort(nums);int n = nums.length;int l = 0;int r = nums[n-1]-nums[0];while(l<=r){int m = l+((r-l)>>1);if(countNumber(nums,m)>=k){r = m - 1;}else{l = m + 1;}}return l;}private int countNumber(int[] nums,int m){int j = 0;int n = nums.length;int[] dp = new int[n];for(int i=0;i<n;i++){while(j<n && nums[j]-nums[i]<=m) j++;dp[i] = ( i==0? (j-i-1) : dp[i-1]+(j-i-1));}return dp[n-1];}
}
时间复杂度O(nlogn)O(nlogn)O(nlogn),n应该是数组长度和数组最大值之间的最大值。当然最后的代码还可以做空间优化。
2.3 用堆实现
参考力扣
首先将数组排序,排序之后,相邻元素的差最小。先将nums[i]和nums[i+1],放入堆中,之后再依次将nums[i]和nums[i+2] nums[i]和nums[i+3]放入堆中 …
时间复杂度O((k+N)lgN),时间复杂度过高。
class Solution {public int smallestDistancePair(int[] nums, int k) {Arrays.sort(nums);PriorityQueue<Node> heap = new PriorityQueue<Node>(nums.length,Comparator.<Node> comparingInt(node -> nums[node.nei] - nums[node.root]));int n = nums.length;for(int i=0;i<n-1;i++){heap.offer(new Node(i,i+1));}Node node = null;for(;k>0;k--){node = heap.poll(); if(node.nei+1<n){heap.offer(new Node(node.root,node.nei+1));}}return nums[node.nei]- nums[node.root];}class Node {int root;int nei;Node(int r, int n) {root = r;nei = n;}}}
3相似题目786
3.1 二分
虽说相似,没有找到规律还是理解不了的。即使找到规律了也不一定会数数,在O(n)时间复杂度内。
class Solution {public int[] kthSmallestPrimeFraction(int[] A, int K) {double l = 0;double r = 1.0;int[] answer = new int[2];while(l<r){double m = (l+r)/2;int[] res = countSmallerOrEqual(A,m);if(res[0] == K){answer[0] = res[1];answer[1] = res[2];break;}else if(res[0]>=K){r = m;}else{l = m;}}return answer;}private int[] countSmallerOrEqual(int[] A,double m){int count = 0;int p = 0,q = 1;int i=0;for(int j=1;j<A.length;j++){while(A[i]<=m*A[j]) i++;count += i;if(i>0){i=i-1;if(p*A[j]<q*A[i]){p = A[i];q = A[j];}}}return new int[]{count, p, q};}}
对于countSmallerOrEqual函数不知道怎么写,可以先用两个for循环来写。会发现i的循环可以提取出来。因为A数组是递增的。例如[1,2,3,5]。如果1/2满足条件,那么1/3一定满足条件。因为分子不变,分母增加,数值会越来越小。
private int[] countSmallerOrEquals(int[] A ,double m){int count = 0;int p = 0,q = 1;for(int j=1;j<A.length;j++){int i=0;for(;i<j;i++){if(A[i]>m*A[j]){break;}}count += i;i=i-1;if(p*A[j]<q*A[i]){p = A[i];q = A[j];}}return new int[]{count, p, q};}
3.2 堆
class Solution {public int[] kthSmallestPrimeFraction(int[] A, int k) {int[] answer = new int[2];PriorityQueue<int[]> pq = new PriorityQueue<int[]>((a, b) ->A[a[0]] * A[b[1]] - A[a[1]] * A[b[0]]);for(int i=1;i<A.length;i++){pq.offer(new int[]{0,i});}while(k>1){int[] vals = pq.poll();if(vals[0]+1 < vals[1]){pq.offer(new int[]{vals[0]+1,vals[1]});}k--;}answer = pq.poll();return new int[]{A[answer[0]],A[answer[1]]};}}