1. 程式人生 > >面試常考演算法題總結(二)

面試常考演算法題總結(二)

題目連結:試卷1試卷2

題目1:對於一個無序陣列A,請設計一個演算法,求出需要排序的最短子陣列的長度。給定一個整數陣列A及它的大小n,請返回最短子陣列的長度。

測試樣例:
[1,5,3,4,2,6,7],7
返回:4

分析:1.先判斷依次最小值是否在正確位置,直到找到不在正確位置最小值的應該在的位置,

作為最小需排序起始點2.依次判斷最大值是否在正確位置,直到找不到正確位置最大值應

該在的位置,作為最小需排序的末尾點3.計算首末位點間的整數個數,即為需要排序的最短

子陣列長度。

程式碼如下:

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:給定一個長度為N(N>1)的整型陣列A,可以將A劃分成左右兩個部分,左部 分A[0..K],右部分A[K+1..N-1],K可以取值的範圍是[0,N-2]。求這麼多劃分方案中,左 部分中的最大值減去右部分最大值的絕對值,最大是多少?給定整數陣列A和陣列的大 n,請返回題目所求的答案。 測試樣例:
[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];
    }
};