LeetCode-4 2組の秩序配列は中位数を探す
21803 ワード
トピックは、2つの正の配列の中位数を探すを参照してください.構想はLeetCodeの公式問題解から来ている.
2つのシーケンス配列の中位数を探して、mとnのサイズの2つのシーケンス(小さいから大きい)配列nums 1とnums 2を与えます.この2つのシーケンス配列の中位数を見つけて返してください.奇数であれば(m+n+1)/2番目の小さい数字を返し、偶数であれば(m+n)/2番目の小さい数と(m+n)/2+1番目の小さい数の2つの平均値を返します.
シナリオ1:集計.
シナリオ1.1真の統合
タイトルの2つの配列はすべて秩序配列であるため,集計後の新しい秩序配列を保存するために,空間サイズm+n m+n m+nの配列を開くことができる.2つの配列のうち1つの小さい数字を選択して集計後の新しい配列に配置するたびに、最後に新しい配列の中から問題の要求する中位数を選んで、返します.
シナリオ1.2擬似集計
実際、私たちは本当に空間の大きさがm+n m+n m+nのような大きな新しい配列を開く必要はありません.私たちは実は(m+n+1)/2番目の数字(偶数であれば2つ)の数字しか必要ありません.完全な配列を開いて保存する必要はありません.したがって、1つの数字を維持し、2つの配列間で2つの比較を行う場合、2つの配列間の最小の数字だけを残して、(m+n+1)/2番目の数字が見つかるまで下付きを増やします.このように空間複雑度はO(1)O(1)O(1)レベルに低下する.コードは次のとおりです.
シナリオ2:二分検索
より一般的には,2つの秩序配列におけるK番目の小さい数を探すことを考慮した.2つの配列である以上、1番目の配列のK 2frac{K}{2}の2 K番目の数字は必ず1番目の配列の前のK 2−1frac{K}{2}−1 2 K−1番目の数字より大きく、2番目の配列のK 2frac{K}{2}の2 K番目の数字は必ず2番目の配列の前のK 2−1frac{K}{2}−1 2 K−1番目の数字より大きい.この2つの中で最も小さい数字は、せいぜい前のK 2−1+K 2−1=K−2frac{K}{2}−1+frac{K}{2}−1=K−2 K−1+2 K−1=K−2の2つの数字(なぜ「最も多い」のか、この数字が他の数字の前のK 2−1frac{K}{2}−1 2 K−1の数字よりも小さい可能性が高い)であり、K番目の小さい数字ではないので、この数字の後ろから次の二分検索を行う必要があり、同時にK 2frac{K}{2}2 K個の数字も排除され(この配列の中で容量がK 2frac{K}{2}2 K個未満であれば、その配列の総容量が減少し、排除された数字はt t t個と記す)、次回探すのはK−t−K−t−tの小さい数字であるべきである.考えがはっきりしたら、コードを書けばいいです.
2つのシーケンス配列の中位数を探して、mとnのサイズの2つのシーケンス(小さいから大きい)配列nums 1とnums 2を与えます.この2つのシーケンス配列の中位数を見つけて返してください.奇数であれば(m+n+1)/2番目の小さい数字を返し、偶数であれば(m+n)/2番目の小さい数と(m+n)/2+1番目の小さい数の2つの平均値を返します.
シナリオ1:集計.
シナリオ1.1真の統合
タイトルの2つの配列はすべて秩序配列であるため,集計後の新しい秩序配列を保存するために,空間サイズm+n m+n m+nの配列を開くことができる.2つの配列のうち1つの小さい数字を選択して集計後の新しい配列に配置するたびに、最後に新しい配列の中から問題の要求する中位数を選んで、返します.
シナリオ1.2擬似集計
実際、私たちは本当に空間の大きさがm+n m+n m+nのような大きな新しい配列を開く必要はありません.私たちは実は(m+n+1)/2番目の数字(偶数であれば2つ)の数字しか必要ありません.完全な配列を開いて保存する必要はありません.したがって、1つの数字を維持し、2つの配列間で2つの比較を行う場合、2つの配列間の最小の数字だけを残して、(m+n+1)/2番目の数字が見つかるまで下付きを増やします.このように空間複雑度はO(1)O(1)O(1)レベルに低下する.コードは次のとおりです.
class Solution {
public:
double findMedianSortedArrays(vector<int>& a, vector<int>& b) {
int la = a.size();
int lb = b.size();
int i = 0, j = 0, id = 0, ans = 0, n = la + lb;
if(n==1){
for(auto t:a)return t;
for(auto t:b)return t;
}
if(n%2){
do{
if(i<la&&j<lb){
if(a[i]<b[j]) ans = a[i++];
else ans = b[j++];
}
else if(i < la) ans = a[i++];
else if(j < lb) ans = b[j++];
id++;
}while(id!=(n+1)/2);
return ans;
}
else{
double sum = 0;
do{
if(i<la&&j<lb){
if(a[i]<b[j]) ans = a[i++];
else ans = b[j++];
}
else if(i < la) ans = a[i++];
else if(j < lb) ans = b[j++];
id++;
if(id==n/2||id==n/2+1){
sum += ans;
}
}while(id!=n/2+1);
return sum / 2;
}
}
};
シナリオ2:二分検索
より一般的には,2つの秩序配列におけるK番目の小さい数を探すことを考慮した.2つの配列である以上、1番目の配列のK 2frac{K}{2}の2 K番目の数字は必ず1番目の配列の前のK 2−1frac{K}{2}−1 2 K−1番目の数字より大きく、2番目の配列のK 2frac{K}{2}の2 K番目の数字は必ず2番目の配列の前のK 2−1frac{K}{2}−1 2 K−1番目の数字より大きい.この2つの中で最も小さい数字は、せいぜい前のK 2−1+K 2−1=K−2frac{K}{2}−1+frac{K}{2}−1=K−2 K−1+2 K−1=K−2の2つの数字(なぜ「最も多い」のか、この数字が他の数字の前のK 2−1frac{K}{2}−1 2 K−1の数字よりも小さい可能性が高い)であり、K番目の小さい数字ではないので、この数字の後ろから次の二分検索を行う必要があり、同時にK 2frac{K}{2}2 K個の数字も排除され(この配列の中で容量がK 2frac{K}{2}2 K個未満であれば、その配列の総容量が減少し、排除された数字はt t t個と記す)、次回探すのはK−t−K−t−tの小さい数字であるべきである.考えがはっきりしたら、コードを書けばいいです.
class Solution {
public:
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
int m = nums1.size();
int n = nums2.size();
if ((m + n) %2 == 1){
return getTopK((m+n)/2+1,m,n,nums1,nums2);
}else{
return (getTopK((m+n)/2,m,n,nums1,nums2)+getTopK((m+n)/2+1,m,n,nums1,nums2))/2.0;
}
}
int getTopK(int k, int m, int n, vector<int>& nums1, vector<int>& nums2){
int index1 = 0, index2 = 0;
int mid_index1,nums_index1;
int mid_index2,nums_index2;
while(true){
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]);
}
mid_index1 = min(index1 + k / 2 - 1 , m-1);
mid_index2 = min(index2 + k / 2 -1 ,n-1);
nums_index1 = nums1[mid_index1];
nums_index2 = nums2[mid_index2];
if (nums_index1 < nums_index2){
k -= mid_index1 - index1 + 1;
index1 = mid_index1 +1;
}else{
k -= mid_index2 - index2 + 1;
index2 = mid_index2 + 1;
}
}
}
};