1. 程式人生 > 實用技巧 >阿里18道常見的MySQL面試題,含解析

阿里18道常見的MySQL面試題,含解析

1.問題描述

我們把陣列 A 中符合下列屬性的任意連續子陣列 B 稱為 “山脈”

  • B.length >= 3
  • 存在 0 < i< B.length - 1 使得 B[0] < B[1] < ... B[i-1] < B[i] > B[i+1] > ... > B[B.length - 1]

(注意:B 可以是 A 的任意子陣列,包括整個陣列 A。)

給出一個整數陣列 A,返回最長 “山脈”的長度。

如果不含有 “山脈” 則返回 0

示例 1:

輸入:[2,1,4,7,3,2,5]
輸出:5
解釋:最長的 “山脈” 是 [1,4,7,3,2],長度為 5。

示例 2:

輸入:[2,2,2]
輸出:0
解釋:不含 “山脈”。

提示:

  1. 0 <= A.length <= 10000
  2. 0 <= A[i] <= 10000

2.求解

暴力法列舉山頂

  • 列舉陣列[1,length-1]的元素,對於每個元素,判斷其是否能向左右延伸成為山脈,最後的右延伸距離減去左延伸距離+1即為當前山頂的長度

程式碼如下

/*
 * 執行用時:4 ms, 在所有 Java 提交中擊敗了22.60% 的使用者
 * 記憶體消耗:39.5 MB, 在所有 Java 提交中擊敗了83.89% 的使用者
 * */
public int longestMountain(int[] A) {
    int left, right;
    int maxAns = 0;
    //列舉每一個可能是山頂的點
    for (int i = 1; i < A.length - 1; i++) {
        int ans;
        int pre = A[i];
        left = i;
        right = i;
        //判斷是否能成為山頂(左右兩側是否小於它)
        if (A[i - 1] < pre && A[right + 1] < pre) {
            left = i - 1;
            right = i + 1;
            //判斷左側山腳
            while (left > 0 && A[left] < pre) {
                pre = A[left];
                left--;
            }
            if (A[left] >= pre) left++; //修正左側山腳位置
            pre = A[i];
            //判斷右側山腳
            while (right < A.length - 1 && A[right] < pre) {
                pre = A[right];
                right++;
            }
        }
        if (A[right] >= pre) right--;   //修正右側側山腳位置
        ans = right - left + 1;
        maxAns = Math.max(ans, maxAns); //計算山脈總長度
    }
    return maxAns;
}
  • 時間複雜度平方級,執行用時較長

動態規劃

  • 新建一個大小等於A陣列長度的陣列left,left陣列內部元素初始值都為0
  • left[i]來表示A[i]最多能向左側擴充套件的長度,如果A[i] > A[i-1]則表示A[i]可以向左側延伸到A[i-1],所以有left[i] = left[i-1] + 1
  • 同樣我們使用陣列right[i]來表示A[i]最多能向右側擴充套件的長度,只不過這次是從右側倒序遍歷
  • 最後我們遍歷每一個可能是山頂的位置A[i],計算它向左側和右側延伸的距離和即可

程式碼如下

/*
 * 執行用時:3 ms, 在所有 Java 提交中擊敗了69.39% 的使用者
 * 記憶體消耗:38.8 MB, 在所有 Java 提交中擊敗了100.00% 的使用者
 * */
public int longestMountain(int[] A) {
    int len = A.length;
    if (len == 0) {
        return 0;
    }
    //對於每個A[i],記錄下能夠向左延伸到的距離
    int[] left = new int[len];
    for (int i = 1; i < len; i++) {
        left[i] = A[i - 1] >= A[i] ? 0 : left[i - 1] + 1;
    }
    //對於每個A[i],記錄下能夠向右延伸到的距離
    int[] right = new int[len];
    for (int i = len - 2; i >= 0; i--) {
        right[i] = A[i + 1] >= A[i] ? 0 : right[i + 1] + 1;
    }
    //對於每個可能成為山頂的位置,判斷是能否成為山脈  
    int maxAns = 0;
    for (int i = 1; i < len - 1; i++) {
        //若是山脈,則計算山脈的長度
        if (left[i] > 0 && right[i] > 0) {
            maxAns = Math.max(maxAns, left[i] + right[i] + 1);
        }
    }
    return maxAns;
}
  • 時間複雜度:O(n),其中 n是陣列 AAA 的長度。

  • 空間複雜度:O(n),即為陣列 left 和 right 需要使用的空間。

雙指標列舉山腳位置

  • 定義指標left、right,固定left位置,每次向右移動right指標,判斷能否成為山脈
  • 對於left的情況分析
    1. A[left] > A[left + 1],不構成左側山腳,令left = right,右側山腳成為下一次列舉左側山腳的起點
    2. 1判斷通過,證明存在左側山腳,移動right指標延伸左側山腳
      1. 存在右側山腳,即A[right] > A[right+1],移動right指標延伸右側山腳,完畢後判斷山脈長度,即當前山脈長度 = right - left + 1
      2. 不存在右側山腳,因為right = left + 1,說明當前right所在的位置也不可能成為左側山腳,right指標右移一位,令left = right,右側山腳成為下一次列舉左側山腳的起點

程式碼如下

/*
 * 執行用時:2 ms, 在所有 Java 提交中擊敗了99.84% 的使用者
 * 記憶體消耗:39.7 MB, 在所有 Java 提交中擊敗了75.75% 的使用者
 * */
public int longestMountain(int[] A) {
    int n = A.length;
    int left = 0;
    int maxAns = 0;
    //少於3個點不構成山脈
    while (left + 2 < n) {
        int right = left + 1;
        //判斷是否構成左側山腳
        if (A[left] < A[left + 1]) {
            //延伸左側山腳
            while (right + 1 < n && A[right] < A[right + 1]) {
                right++;
            }
            //判斷是否構成右側山腳
            if (right + 1 < n && A[right] > A[right + 1]) {
                //延伸右側山腳
                while (right + 1 < n && A[right] > A[right + 1]) {
                    right++;
                }
                //所有條件滿足,構成山脈,判斷山脈長度
                maxAns = Math.max(maxAns, right - left + 1);
            } else {
                right++;
            }
        }
        //從右側山腳開始判斷是否是下一個左側山腳
        left = right;
    }
    return maxAns;
}
  • 時間複雜度:O(n),其中 n 是陣列 A 的長度。

  • 空間複雜度:O(1)。