題目
兩個有序數組長度為N,找到第N、N+1大的數
思路1:雙指針,O(N)復雜度
簡述思路:
如果當前A指針指向的數組A的內容小于B指針指向的數組B的內容,那么A指針往右移動,然后nums(當前已經遍歷過的數字個數)也加一。
如果此時已經遍歷過的數字個數等于N那么九江移動之前的A指針指向的A數組的內容送入result。
其他情況,以相反的邏輯進行。
//雙指針法求解
vector<int> GetMid_On(vector<int>& A, vector<int>& B, int N)
{int A_index = 0;int B_index = 0;int nums = 0;vector<int> result;while (A_index < N && B_index < N){if (A[A_index] < B[B_index]){A_index++;nums++;if (nums == N){result.push_back(A[A_index - 1]);}if (nums == N + 1){result.push_back(A[A_index - 1]);return result;}}else {B_index++;nums++;if (nums == N){result.push_back(B[B_index - 1]);}if (nums == N + 1){result.push_back(B[B_index - 1]);return result;}}//如果兩個元素相同}return result;
}int main()
{vector<int> A = { 2,4,5,6,9 };vector<int> B = { 1,3,7,8,10 };vector<int> result = GetMid_On(A, B, 5);cout << result[0] << endl;cout << result[1] << endl;cout << "結束" << endl;return 0;
}
思路2:迭代法二分
簡述思路:
先初始化ab數組的start,end,mid;
然后比較各自mid指向的值的大小。
如果A[a_mid] > B[b_mid],說明,第N大的值在A數組a_mid的左邊,在B數組b_mid的右邊,所以對a_end以及b_start做出更新:
長度為奇數的時候,正好
a_end = a_mid;
b_start = b_mid;
當然還要考慮到長度為偶數的情況:
a_end = a_mid;
b_start = b_mid + 1;
這里只是對start進行修改,對于end值不需要修改。可以舉例:
A = {2,4,6,8};
B= {1,3,5,7};
a_start = 0,a_end = 3
b_start = 0,b_end = 3
a_mid = b_mid = 3/2 =1;
A[a_mid] > B[b_mid] ,并且,長度為偶數,所以
a_end = a_mid =1;
b_start = b_mid +1 =2;
所以A被分割為:{2,4};
B被分割為:{5,7};
a_start = 0,a_end = 1
b_start = 2,b_end = 3
a_mid = 1/2 =0;
b_mid = (2+3)/2= 2;
A[a_mid] < B[b_mid] ,并且,長度為偶數,所以
a_start = a_mid =1;
b_end = b_mid =2;
此時達成a_start == a_end || b_start == b_end條件,所以可以判斷兩個start的值的大小,取較小值可得到第N大的數:
//迭代二分法去解
vector<int> GetMid(vector<int>& A, vector<int>& B, int N)
{vector<int> result;int a_start = 0, a_end = N - 1;int b_start = 0, b_end = N - 1;//初始化中間位置int a_mid = (a_start + a_end) / 2;int b_mid = (b_start + b_end) / 2;//循環結束條件:當數組起始點與結束點重合的時候,說明存在第N大的數while (a_start != a_end || b_start != b_end){//更新中間坐標a_mid = (a_start + a_end) / 2;b_mid = (b_start + b_end) / 2;//如果此時的A中位數與B的中位數相同,說明兩個都是第N大的數if (A[a_mid] == B[b_mid]){result.push_back(A[a_mid]);result.push_back(A[a_mid] > B[b_mid] ? B[b_mid]: A[a_mid]);return result;}//如果A的中位數>B的中位數,說明此時第N大的數在A的左邊,B的右邊,所以需要更新a_end以及b_startelse if (A[a_mid] > B[b_mid]){//如果當前a_start-a_end之間的長度為偶數,那么中間值就是midif ((a_start + a_end) % 2 == 0){a_end = a_mid;b_start = b_mid;}else{a_end = a_mid;b_start = b_mid + 1;}}//如果A的中位數<B的中位數,說明此時第N大的數在A的右邊,B的左邊,所以需要更新a_start以及b_endelse{//如果當前a_start-a_end之間的長度為奇數,那么中間值就是midif ((a_start + a_end) % 2 == 0){a_start = a_mid;b_end = b_mid;}//如果當前a_start-a_end之間的長度為偶數,那么中間值就是mid+1else{a_start = a_mid + 1;b_end = b_mid;}}}//判斷兩個start的值的大小if (A[a_start] > B[b_start]){result.push_back(B[b_start]);if (b_start + 1 < N){if (A[a_start] > B[b_start + 1])result.push_back(B[b_start + 1]);elseresult.push_back(A[a_start]);}elseresult.push_back(A[a_start]);}else{result.push_back(A[a_start]);if (a_start + 1 < N){if (A[a_start + 1] <= B[b_start])result.push_back(A[a_start + 1]);elseresult.push_back(B[b_start]);}elseresult.push_back(B[b_start]);}return result;
}int main()
{vector<int> A = { 2,4,5,6,9 };vector<int> B = { 2,4,5,6,9 };//vector<int> B = { 1,3,7,8,10 };vector<int> result = GetMid(A, B, 5);for(int i = 0;i < result.size();i++)cout << result[i] << endl;cout << "結束" << endl;return 0;
}
思路3:遞歸法二分
寫完迭代法之后,遞歸將a_start,a_end,b_start,b_end,作為遞歸函數的參數放入。遞歸函數的一開始寫終止條件,參考迭代法。
終止條件后面,寫根據中間值的大小,對a_start,a_end,b_start,b_end進行不同賦值,達到分割數組的目的。
//遞歸二分法求解
vector<int> GetMid(vector<int>& A, vector<int>& B, int a_start,int a_end,int b_start,int b_end,int N)
{vector<int> result;//初始化中間位置int a_mid = (a_start + a_end) / 2;int b_mid = (b_start + b_end) / 2;//如果有答案了if (A[a_mid] == B[b_mid]){result.push_back(A[a_mid]);result.push_back(A[a_mid] > B[b_mid] ? B[b_mid] : A[a_mid]);return result;}if (a_start == a_end || b_start == b_end){//判斷兩個start的值的大小if (A[a_start] > B[b_start]){result.push_back(B[b_start]);if (b_start + 1 < N){if (A[a_start] > B[b_start + 1])result.push_back(B[b_start + 1]);elseresult.push_back(A[a_start]);}elseresult.push_back(A[a_start]);}else{result.push_back(A[a_start]);if (a_start + 1 < N){if (A[a_start + 1] <= B[b_start])result.push_back(A[a_start + 1]);elseresult.push_back(B[b_start]);}elseresult.push_back(B[b_start]);}return result;}//遞歸更新邊界值if (A[a_mid] > B[b_mid]){//如果當前a_start-a_end之間的長度為偶數,那么中間值就是midif ((a_start + a_end) % 2 == 0){return GetMid(A, B, a_start, a_mid, b_mid, b_end, N);}else{return GetMid(A, B, a_start, a_mid, b_mid + 1, b_end, N);}}//如果A的中位數<B的中位數,說明此時第N大的數在A的右邊,B的左邊,所以需要更新a_start以及b_endelse{//如果當前a_start-a_end之間的長度為偶數,那么中間值就是midif ((a_start + a_end) % 2 == 0){return GetMid(A, B, a_mid, a_end, b_start, b_mid, N);}else{return GetMid(A, B, a_mid + 1, a_end, b_start, b_mid, N);}}
}int main()
{vector<int> A = { 2,4,5,6,9 };//vector<int> B = { 2,4,5,6,9 };int N = A.size();vector<int> B = { 1,3,7,8,10 };vector<int> result = GetMid(A, B, 0, N-1,0,N-1,N);for(int i = 0;i < result.size();i++)cout << result[i] << endl;cout << "結束" << endl;return 0;
}