問題描述
-
對於一個數字序列,請設計一個複雜度爲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)
使用一個輔助數組保存一個遞增子序列 :
- 碰到遞增的元素就依次存入數組末尾
- 碰到不是遞增的在數組中找到第一個比他大的元素替換
所以這個數組是最長遞增子序列的長度, 但不是那個最長自增子序列本身, 他是碰到不是遞增的在數組中找到一個合適的淘汰掉, 這樣就只用遍歷一遍, 加上二分查找, 就是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();
}