面試常考演算法題總結(二)
題目1:對於一個無序陣列A,請設計一個演算法,求出需要排序的最短子陣列的長度。給定一個整數陣列A及它的大小n,請返回最短子陣列的長度。
測試樣例:[1,5,3,4,2,6,7],7
返回:4
分析:
1
.先判斷依次最小值是否在正確位置,直到找到不在正確位置最小值的應該在的位置,
作為最小需排序
的
起始點
2
.依次判斷最大值是否在正確位置,直到找不到正確位置最大值應
該在的位置,作為最小需排序的末
尾點
3
.計算首末位點間的整數個數,即為需要排序的最短
子陣列長度。
程式碼如下:
題目2:給定一個長度為N(N>1)的整型陣列A,可以將A劃分成左右兩個部分,左部 分A[0..K],右部分A[K+1..N-1],K可以取值的範圍是[0,N-2]。求這麼多劃分方案中,左 部分中的最大值減去右部分最大值的絕對值,最大是多少?給定整數陣列A和陣列的大 小n,請返回題目所求的答案。 測試樣例:class ShortSubsequence { public: int findShortest(vector<int> A, int n) { vector<int> B = A; sort(A.begin(),A.end()); int pos1 = 0,pos2 = 0; for(int i = 0;i<n;++i){ if(A[i]!=B[i]){ pos1 = i; break; } } for(int i = n-1;i>=0;--i){ if(A[i]!=B[i]){ pos2 = i; break; } } if(pos1==0&&pos2==0) return 0; else return pos2-pos1+1; } };
[2,7,3,1,1],5
返回:6分析:基本思路,全部遍歷一遍,程式碼如下:
另一種思路是先找出整個陣列中的最大值,最小值取陣列兩端的較小值,這個最大值和最小值的差值即為所求。class MaxGap { public: int findMaxGap(vector<int> A, int n) { vector<int> v; int res = 0; for(int i = 0;i<n-1;++i){ int max1 = 0,max2 = 0; for(int j = 0;j<=i;++j){ if(A[j]>max1) max1 = A[j]; } for(int j = i+1;j<n;++j){ if(A[j]>max2) max2 = A[j]; } v.push_back(abs(max1-max2)); } for(int i = 0;i<v.size();++i){ if(res<v[i]) res = v[i]; } return res; } };
程式碼如下:
class MaxGap { public: int findMaxGap(vector<int> A, int n) { int max=-1,min; for(int i=0;i<n;i++) if(A[i]>max) max=A[i]; min = A[0]>A[n-1]?A[n-1]:A[0]; return max-min; } };
題目3:定義區域性最小的概念。arr長度為1時,arr[0]是區域性最小。arr的長度為N(N>1)時,如果arr[0]<arr[1],那麼arr[0]是區域性最小;如果arr[N-1]<arr[N-2],那麼arr[N-1]是區域性最小;如果0<i<N-1,既有arr[i]<arr[i-1]又有arr[i]<arr[i+1],那麼arr[i]是區域性最小。 給定無序陣列arr,已知arr中任意兩個相鄰的數都不相等,寫一個函式,只需返回arr中任意一個區域性最小出現的位置即可。
分析:注意要考慮特殊情況,比如大小為0或者大小為1的情況,程式碼如下:
class Solution {
public:
int getLessIndex(vector<int> arr) {
int res;
if(arr.size()==0)
res = -1;
else if(arr.size()==1)
res = 0;
else if(arr.size()==2){
if(arr[0]<arr[1])
res = 0;
else
res = 1;
}
else{
if(arr[0]<arr[1])
res = 0;
else if(arr[arr.size()-1]<arr[arr.size()-2])
res = arr.size()-1;
else{
for(int i = 1;i<arr.size()-1;++i){
if(arr[i]<arr[i-1]&&arr[i]<arr[i+1]){
res = i;
break;
}
}
}
}
return res;
}
};
題目4:(子陣列最大乘積)給定一個double型別的陣列arr,其中的元素可正可負可0,返回子陣列累乘的最大乘積。例如arr=[-2.5,4,0,3,0.5,8,-1],子陣列[3,0.5,8]累乘可以獲得最大的乘積12,所以返回12。
分析:這是一個動態規劃問題。是leetcode中Maximum Product Subarray問題(連結:Maximum
Product Subarray)。程式碼如下:
class Solution {
public:
double maxProduct(vector<double> arr) {
if (arr.empty())
return 0;
double maxNum = arr[0];
double minNum = arr[0];
double res = arr[0];
double maxEnd = 0;
double minEnd = 0;
int i = 0;
/* max(arr[i])處的最大值和最小值在以下三個選項中
max(arr[i-1]) * arr[i]
min(arr[i-1]) * arr[i]
arr[i]
*/
for(i = 1; i < arr.size(); i++)
{
maxEnd = maxNum * arr[i];
minEnd = minNum * arr[i];
maxNum = max(max(maxEnd, minEnd), arr[i]);
minNum = min(min(maxEnd, minEnd), arr[i]);
res = max(res, maxNum);
}
return res;
}
};
題目5:給定一棵完全二叉樹的頭節點head,返回這棵樹的節點個數。如果完全二叉樹的節點數為N,請實現時間複雜度低於O(N)的解法。
分析:複雜度為O(N)的解法:
/**
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};
*/
class Solution {
public:
int nodeNum(struct TreeNode* head) {
if(head==NULL)
return 0;
else
return 1+nodeNum(head->left)+nodeNum(head->right);
}
};
思路2:
class Solution {
public:
int nodeNum(struct TreeNode* head) {
if(head==NULL)
return 0;
int result=0;
stack<struct TreeNode*> s;
s.push(head);
while(!s.empty()){
struct TreeNode* pTemp=s.top();
result++;
s.pop();
if(pTemp->right!=NULL){
s.push(pTemp->right);
}
if(pTemp->left!=NULL){
s.push(pTemp->left);
}
}
return result;
}
};
題目6:給定兩個有序陣列arr1和arr2,兩個陣列長度都為N,求兩個陣列中所有數的上中位數。
例如:
arr1 = {1,2,3,4};
arr2 = {3,4,5,6};
一共8個數則上中位數是第4個數,所以返回3。
arr1 = {0,1,2};
arr2 = {3,4,5};
一共6個數則上中位數是第3個數,所以返回2。
要求:時間複雜度O(logN)。
分析:要保證複雜度O(logN)肯定是要用二分查詢,只不過我最開始一直沒有找到結束的條件,以為是查詢元素相等就退出。最後在網上查了,結束條件應該是每個陣列都只剩下2個元素。首先取二者的中位數,在O(1)時間複雜度內求出,那麼陣列arr1的midValue = 2,陣列arr2的midValue = 4,則較小的元素midValue的左邊所有元素,也就是midPosition個,和較大元素右邊的所有元素,都要去掉,由於去掉的元素佔所有元素的一 半,所以複雜度為O(logn)。只需要移動Lindex和Rindex的位置即可,直到 Lindex == Rindex - 1。
class Solution {
public:
int getUpMedian(vector<int> arr1, vector<int> arr2) {
if (arr1.size()<=0 || arr2.size()<=0 || arr1.size() != arr2.size())
return 0;
int L1 = 0;
int R1 = arr1.size()-1;
int L2 = 0;
int R2 = arr2.size()-1;
while (L1 < R1 && L2 < R2){
int mid1 = (R1+L1)/2;
int mid2 = (R2+L2)/2;
if (arr1[mid1] == arr2[mid2])
return arr1[mid1];
if (arr1[mid1] <arr2[mid2]){
//如果元素個數是奇數,那麼R1+L1是偶數,者mid左右元素個數相同
//如果元素個數是偶數,那麼R1+L1是奇數,者mid左邊比右邊少一個
mid1 = ((R1+L1)%2 == 0) ? mid1 : (mid1 + 1);
L1 = mid1;
R2 = mid2;
}else{
mid2 = ((R2+L2)%2 == 0) ? mid2 : (mid2 + 1);
R1 = mid1;
L2 = mid2;
}
}
return (arr1[L1] > arr2[L2]) ? arr2[L2] : arr1[L1];
}
};
當然如果採用一般的思路,將兩個數組合並,再進行排序,即可找出結果,但複雜度高一些。
class Solution {
public:
int getUpMedian(vector<int> arr1, vector<int> arr2) {
int len = arr1.size();
if(arr1[len-1]<=arr2[0])
return arr1[len-1];
else{
vector<int> arr;
for(int i = 0;i<len;++i)
arr.push_back(arr1[i]);
for(int i = 0;i<len;++i)
arr.push_back(arr2[i]);
sort(arr.begin(),arr.end());
return arr[len-1];
}
}
};
題目7:給定兩個有序陣列arr1和arr2,在給定一個整數k,返回兩個陣列的所有數中第K小的數。
例如:
arr1 = {1,2,3,4,5};
arr2 = {3,4,5};
K = 1;
因為1為所有數中最小的,所以返回1;
arr1 = {1,2,3};
arr2 = {3,4,5,6};
K = 4;
因為3為所有數中第4小的數,所以返回3;
要求:如果arr1的長度為N,arr2的長度為M,時間複雜度請達到O(log(min{M,N}))。
分析:歸併排序思想
class Solution {
public:
int findKthNum(vector<int> arr1, vector<int> arr2, int kth) {
int pos=0;
int i=0,j=0;
while(i<arr1.size()&&j<arr2.size()){
int flag=0;
if(arr1[i]<arr2[j]){
i++;
flag=1;
}else{
j++;
flag=2;
}
pos++;
if(pos==kth){
if(flag==1)
return arr1[i-1];
if(flag==2)
return arr2[j-1];
}
}
if(j<arr2.size())
return arr2[j+kth-pos-1];
if(i<arr1.size())
return arr1[i+kth-pos-1];
return 0;
}
};
和上一題一樣,也可以直接合並陣列,但是複雜度高:
class Solution {
public:
int findKthNum(vector<int> arr1, vector<int> arr2, int kth) {
vector<int> arr;
int len1 = arr1.size(),len2 = arr2.size();
for(int i = 0;i<len1;++i)
arr.push_back(arr1[i]);
for(int i = 0;i<len2;++i)
arr.push_back(arr2[i]);
sort(arr.begin(),arr.end());
return arr[kth-1];
}
};