DP : 最長遞增子序列


問題描述

  • 對於一個數字序列,請設計一個複雜度爲O(nlogn)的算法,返回該序列的最長上升子序列的長度,
    這裏的子序列定義爲這樣一個序列U1,U2…,其中Ui < Ui+1,且A[Ui] < A[Ui+1]。
    給定一個數字序列A及序列的長度n,請返回最長上升子序列的長度。

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


時間複雜度爲O(n2)

挨個保存每個元素的最長遞增子序列


int findLongest(int A[], int len)
{
    //初始化一維數組存放每個元素對應的最大遞增子序列長度
    int* arr = new int[len];
    for (int i = 0; i < len; i++) 
        arr[i] = 1; //初始化長度爲1,即一個只有元素時的長度
    
    //控制循環條件,每個元素都從頭遍歷一遍進行比較
    for (int i = 1; i < len; i++)
    for (int j = 0; j < i; j++) //0到i-1去和A[i]比較
    {
        //如果當前元素比前面的元素大,且和其能組成更長的子序列
        if (A[i] > A[j] && arr[i] < arr[j] + 1)
            arr[i] = arr[j] + 1;    
    }
    //arr數組中最大值就是最長遞增子序列的長度
    int max = 0;
    for (int i = 0; i < len; i++) 
        if (max < arr[i])
            max = arr[i];
    delete[] arr;
    return max;
}

時間複雜度爲O(n logn)

使用一個輔助數組保存一個遞增子序列 :

  1. 碰到遞增的元素就依次存入數組末尾
  2. 碰到不是遞增的在數組中找到第一個比他大的元素替換

所以這個數組是最長遞增子序列的長度, 但不是那個最長自增子序列本身, 他是碰到不是遞增的在數組中找到一個合適的淘汰掉, 這樣就只用遍歷一遍, 加上二分查找, 就是O(n logn)

int findLongestSub(int A[], int len) 
{
    vector<int> arr;
    for (int i = 0; i < len; i++) 
        //如果輔助數組arr爲空,或者當前元素比輔助數組末尾元素大,就更新輔助數組末尾元素
        if (arr.size() == 0 || arr.back() < A[i]) 
            arr.push_back(A[i]);
        else 
        {
            int low = 0, high = arr.size() - 1;
            while (low < high) //二分查找輔助數組中第一個大於等於A[i]的數,進行替換
            {
                int mid = (low + high) / 2;
                if (arr[mid] < A[i])
                    low = mid + 1;
                else 
                    high = mid - 1;
            }
            arr[low] = A[i];
        }
    //輔助數組長度即爲最長遞增子序列的長度
    return arr.size();
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章