剛開始寫,如有錯誤還望各位看官指出。若有描述不清,也請諸位提點。
導讀
通過動態規劃使用C語言實現求數組中最大升序(或降序)子序列長度問題。
原題
Longest Increasing Subsequence
Given an unsorted array of integers, find the length of longest increasing subsequence.
For example,
Given[10, 9, 2, 5, 3, 7, 101, 18],
The longest increasing subsequence is[2, 3, 7, 101], therefore the length is4. Note that there may be more than one LIS combination, it is only necessary for you to return the length.
Your algorithm should run in O(n2) complexity.
Follow up:Could you improve it to O(nlogn) time complexity?
Credits:
Special thanks to@pbrotherfor adding this problem and creating all test cases.
Subscribeto see which companies asked this question.
翻譯:
給出一個無序的整形數組,找出它的最大升序子序列。
舉個栗子,
示例數組 arr[] = {10, 9, 2, 5, 3, 7, 101, 18},
數組arr的最長升序子序列是 {2, 3, 7, 101},因此長度是4。
請注意,可能有一個以上的LIS(最長上升子序列)的組合,只需要返回長度就好。
時間復雜度O(n2)前提下實現。
進階:時間復雜度O(nlogn)前提下實現。
以下不譯。
正文
以下為兩種時間復雜度的C語言解法,已將log相關代碼注釋掉了,并粘上分步log,并簡要概述思路,共同學習進步。
O(n2)
思路:
定義一個數組lis,記錄以目標序列nums[0]開頭,以每個元素結尾的子串的最大升序子串長度。
取出lis的最大值即為結果。
重要判斷是代碼中的 if (nums[i] > nums[j] && lis[j] + 1 > lis[i])。
// 此為O(n2)方法
int lengthOfLIS_01(int* nums, int numsSize) {
if (numsSize < 2) {
return numsSize;
}
int lis[numsSize];
// printf("OriginalArray:");
// printArray(nums, numsSize);
// // 此for循環只是為了log美觀 無意義
// for (int i =0; i < numsSize; i++) {
// lis[i] = 0;
// }
int i, j, result = 0;
for (i = 0; i < numsSize; i++) {
lis[i] = 1;
for (j = 0; j < i; j++) {
if (nums[i] > nums[j] && lis[j] + 1 > lis[i]) {
lis[i] = lis[j] + 1;
}
}
result = result > lis[i] ? result : lis[i];
// printArray(lis, numsSize);
}
return result;
}
O(n2) lengthOfLIS_01 的 Log(為了便于觀察理解 打印時隱去了值為0的元素)
此處打印的是記錄以每個元素結尾的子串中最大升序子串長度的數組。
OriginalArray:10, 9, 2, 5, 3, 7,80,18, 1, 2, 3, 4, 5, 6, 7, 8, 9,
1,
1, 1,
1, 1, 1,
1, 1, 1, 2,
1, 1, 1, 2, 2,
1, 1, 1, 2, 2, 3,
1, 1, 1, 2, 2, 3, 4,
1, 1, 1, 2, 2, 3, 4, 4,
1, 1, 1, 2, 2, 3, 4, 4, 1,
1, 1, 1, 2, 2, 3, 4, 4, 1, 2,
1, 1, 1, 2, 2, 3, 4, 4, 1, 2, 3,
1, 1, 1, 2, 2, 3, 4, 4, 1, 2, 3, 4,
1, 1, 1, 2, 2, 3, 4, 4, 1, 2, 3, 4, 5,
1, 1, 1, 2, 2, 3, 4, 4, 1, 2, 3, 4, 5, 6,
1, 1, 1, 2, 2, 3, 4, 4, 1, 2, 3, 4, 5, 6, 7,
1, 1, 1, 2, 2, 3, 4, 4, 1, 2, 3, 4, 5, 6, 7, 8,
1, 1, 1, 2, 2, 3, 4, 4, 1, 2, 3, 4, 5, 6, 7, 8, 9,
result = 9
O(nlogn)
思路:
定義數組tmp記錄當前最大升序子串(∴tmp必然升序),用tmp的末位元素對比目標序列nums[1]開始的每個元素,若當前nums元素 > tmp末位元素,當前nums元素插入tmp。否則用當前nums元素替換tmp中合理位置元素即可。
// 折半(二分)查找
int binarySearch(int *array, int lenght, int key) {
int low = 0;
int high = lenght - 1;
int middle;
while (low <= high) {
middle = low + (high - low) / 2;
if (array[middle] > key) {
high = middle - 1;
} else if (array[middle] < key) {
low = middle + 1;
} else {
return middle;
}
}
return low;
}
// 此為O(nlog(n))方法
int lengthOfLIS_02(int* nums, int numsSize) {
// 長度若為0或1,無須比較直接返回
if (numsSize < 2) {
return numsSize;
}
// 聲明臨時數組,賦值首元素,當前lenght = 1
int tmp[numsSize], position;
// printf("OriginalArray:");
// printArray(nums, numsSize);
// // 此for循環只是為了log美觀 無意義
// for (int i =0; i < numsSize; i++) {
// tmp[i] = 0;
// }
tmp[0] = nums[0];
int lenght = 1;
// 遍歷nums[1]~nums[numsSize-1]
for (int i = 1; i < numsSize; i++) {
// 若當前nums元素 > tmp最大元素,插入tmp,length++
if (nums[i] > tmp[lenght - 1]) {
tmp[lenght] = nums[i];
lenght++;
// printf("Part-A i=%2d len=%2d Arr:", i, lenght);
} else {
// 二分定位 用當前nums元素 替換 tmp中合理位置元素
position = binarySearch(tmp, lenght, nums[i]);
tmp[position] = nums[i];
// printf("Part-B i=%2d pos=%2d Arr:", i, position);
}
// printArray(tmp, numsSize);
}
// printf("Finished\n");
// printArray(tmp, numsSize);
return lenght;
}
O(nlogn) lengthOfLIS_02 的 Log(為了便于觀察理解 打印時隱去了值為0的元素)
OriginalArray:10, 9, 2, 5, 3, 7, 80, 18, 1, 2, 3, 4, 5, 6, 7, 8, 9,
Part-B i= 1 pos= 0 Arr: 9,
Part-B i= 2 pos= 0 Arr: 2,
Part-A i= 3 len= 2 Arr: 2, 5,
Part-B i= 4 pos= 1 Arr: 2, 3,
Part-A i= 5 len= 3 Arr: 2, 3, 7,
Part-A i= 6 len= 4 Arr: 2, 3, 7, 80,
Part-B i= 7 pos= 3 Arr: 2, 3, 7, 18,
Part-B i= 8 pos= 0 Arr: 1, 3, 7, 18,
Part-B i= 9 pos= 1 Arr: 1, 2, 7, 18,
Part-B i=10 pos= 2 Arr: 1, 2, 3, 18,
Part-B i=11 pos= 3 Arr: 1, 2, 3, 4,
Part-A i=12 len= 5 Arr: 1, 2, 3, 4, 5,
Part-A i=13 len= 6 Arr: 1, 2, 3, 4, 5, 6,
Part-A i=14 len= 7 Arr: 1, 2, 3, 4, 5, 6, 7,
Part-A i=15 len= 8 Arr: 1, 2, 3, 4, 5, 6, 7, 8,
Part-A i=16 len= 9 Arr: 1, 2, 3, 4, 5, 6, 7, 8, 9,
Finished
1, 2, 3, 4, 5, 6, 7, 8, 9,
result = 9
相關的其他函數(非重要內容 可忽略)
void testCode() {
int arr[] = {10,9,2,5,3,7,80,18,1,2,3,4,5,6,7,8,9};
// int arr[] = {3,5,6,2,5,4,19,5,6,7,12};
// int count = lengthOfLIS_01(arr, sizeof(arr)/sizeof(int));
int count = lengthOfLIS_02(arr, sizeof(arr)/sizeof(int));
printf("result = %d\n", count);
}
void printArray(int *a, int count) {
for (int i = 0; i < count; i++) {
if (a[i] != 0)
printf("%2d ,",a[i]);
}
printf("\n");
}