无序数组及其子序列的相关问题研究
算法中以數組為研究對象的問題是非常常見的. 除了排序大家經常會遇到之外, 數組的子序列問題也是其中的一大分類. 今天我就對自己經常遇到的無序數組的子序列相關問題在這里總結一下.
?
前置條件: 給定無序數組. 以下所以的問題均以此為前置條件. 例如無序數組nums = [2, 1, 3].
?
問題1:
求子序列的個數. 例如前置無序數組的子序列個數為8, 分別為[], [2], [2, 1], [1], [2, 1, 3], [1, 3], [2, 3], [3].
分析:?
這個問題應該非常好理解. 對于它的子序列sub而言, 數組元素nums[i], 它要么存在于sub中, 要么不存在于sub中, 只有這兩種可能. 所以, 元素nums[i]能構建的子序列個數為count(nums[i]) = 2. 因此, 對于數組nums的所有子序列的個數就為count(nums[0]) * count(nums[1]) * ... * count(nums[n-1]) = 2 * 2 * ... * 2 = 2^n(2的n次冪).?
所以, 對于長度為n的無序數組, 它的所有子序列的個數為 2^n.
?
問題2:
獲取nums的所有子序列.?例如前置無序數組的所有子序列為[], [2], [2, 1], [1], [2, 1, 3], [1, 3], [2, 3], [3].
分析:
首先, 我們按照問題1的思路, "對于它的子序列sub而言, 數組元素nums[i], 它要么存在于sub中, 要么不存在于sub中, 只有這兩種可能", 我們可以創建一個只包含了0和1的長度為n的數組, 則這個二進制數組可以表示的10進制數字的范圍恰好為[0, 2^n - 1]. 這個思路可以用下面這個表格表示如下(利用前置條件中給定的數組nums):
| binary\nums | 2 | 1 | 3 | subsequences |
| 0 | 0 | 0 | 0 | [] |
| 1 | 0 | 0 | 1 | [3] |
| 2 | 0 | 1 | 0 | [1] |
| 3 | 0 | 1 | 1 | [1, 3] |
| ... | ... | ... | ... | ... |
所以, 我們可以遍歷一下范圍[0, 2^n - 1], 然后將10進制變量k轉化為二進制數組, 然后利用二進制數組中1的個數和位置, 構建子序列.
這里提供一下上述思路的Java代碼如下:?
1 public List<List<Integer>> allSubsequences(int[] nums){ 2 if (nums == null){ 3 return null; 4 } 5 int max = Math.pow(2, nums.length); 6 List<List<Integer>> result = new ArrayList<>(); 7 for (int k = 0; k < max; k++){ 8 List<Integer> sub = new ArrayList<>(); 9 String binary = Integer.toBinaryString(k); 10 for(int i = binary.length() - 1; i >= 0; i--){ 11 if (binary.charAt(i) == '1'){ 12 sub.add(nums[nums.length - i - 1]); 13 } 14 } 15 result.add(sub); 16 } 17 return result; 18 }
?
與此同時, 我們也可以利用動態規劃的思想來解決這個問題.
對于元素nums[i], 我們將它的所有子序列表示為subs(i). 那么對于nums[i-1]和nums[i], 則存在這些一個關系:
subs(i) = [nums[i], (sub + nums[i])], 其中滿足限制條件 sub in subs(i - 1).?
上述表達式想要表達的意思是: subs(i)生成的所有子序列, 由nums[i]和subs(i - 1)生成的所有子序列末尾加上nums[i]組成.
由此, 依據這種思路, 即利用動態規劃思想生成所有子序列, 可以利用如下java代碼實現:
1 public List<List<Integer>> allSubsequences(int[] nums){ 2 if (nums == null){ 3 return null; 4 } 5 List<List<Integer>> result = new ArrayList<>(); 6 result.add(new ArrayList<>());//add empty subsequence 7 for(int num : nums){ 8 List<List<Integer>> tmp = new ArrayList<>(result); 9 for(List<Integer> list : tmp){ 10 list.add(num); 11 } 12 result.addAll(tmp); 13 } 14 return result; 15 }
?
問題3:
求所有遞增子序列的個數.?例如前置無序數組的遞增子序列個數為5, 分別為[2], [1], [1, 3], [2, 3], [3].
分析:?
我們假設count(i)表示以nums[i]結尾的遞增子序列個數. 則對于前置無序數組nums而言, 它的所有遞增子序列的個數, 就是以nums[i]結尾的遞增子序列的和, 表示為sum(count(i))(0 < i < n - 1).
理解了以上假設, 那么我們可以發現以下規律:?
count(0) = 1;
count(1) = 1; (nums[0] > nums[1])
count(2) = 1 + count(0) + count(1) (nums[0] < nums[2], nums[1] < nums[2])
? ? ? ? ? ? ?= 1 + 1 + 1 = 3;
所以, nums的所有遞增子序列個數就是count = count(0) + count(1) + count(2) = 1 + 1 + 3 = 5;
因而, 我的思路是: 建立一個數組counts = new int[nums.length + 1]用于保存以nums[i]結尾的遞增子序列的個數, 即counts[i] = count(i). 而counts[nums.length]則用于累積counts[i], 表示整個序列的遞增子序列的個數.
上述思路的Java實現如下:?
1 public int countIncreasingSubsequences(int[] nums){ 2 if (nums == null){ 3 return -1; 4 } 5 int[] counts = new int[nums.length + 1]; 6 for(int i = 0; i < nums.length; i++){ 7 counts[i] = 1;//單元素遞增序列計數 8 for(int j = 0; j < i; j++){ 9 if(nums[i] > nums[j]){ 10 counts[i] += counts[j]; 11 } 12 } 13 counts[nums.length] += counts[i]; 14 } 15 return counts[nums.length]; 16 }
?
問題4:
求它的所有遞增子序列.?例如前置無序數組的所有遞增子序列為[2], [1], [1, 3], [2, 3], [3].
分析:
需要利用動態規劃思想來分析這道題目. 其實, 大體思路跟求它的所有子序列相同, 只不過是本問題要求子序列是遞增的, 所以, 就利用遞增來修改求所有子序列的邏輯.
因而, 對于元素nums[i], 我們將它的所有子序列表示為subs(i). 那么對于nums[i-1]和nums[i], 則存在這些一個關系:
subs(i) = [nums[i], (sub + nums[i])], 其中滿足限制條件 sub in subs(i - 1) & sub.lastElement < nums[i].?
上述表達式想要表達的意思是: subs(i)生成的所有遞增子序列, 由nums[i]和subs(i - 1)生成的所有末尾元素小于nums[i]的遞增子序列末尾加上nums[i]組成.
由此, 依據這種思路, 即利用動態規劃思想生成所有遞增子序列, 可以利用如下java代碼實現:
1 public List<List<Integer>> allIncreasingSubsequences(int[] nums){ 2 if (nums == null){ 3 return null; 4 } 5 List<List<Integer>> result = new ArrayList<>(); 6 for(int num : nums){ 7 List<List<Integer>> tmp = new ArrayList<>(); 8 List<Integer> firstList = new ArrayList<>(); 9 firstList.add(num); 10 tmp.add(firstList); 11 for(List<Integer> list : result){ 12 if(list.get(list.size() - 1) < num){ 13 List<Integer> newList = new ArrayList<>(list); 14 newList.add(num); 15 tmp.add(newList); 16 } 17 } 18 } 19 return result; 20 }
?
最后, 關于遞減子序列的問題, 原理跟遞增子序列問題是一樣的, 這里就不再贅述, 有興趣的同學, 可以自己手動寫一下.
轉載于:https://www.cnblogs.com/littlepanpc/p/7954896.html
總結
以上是生活随笔為你收集整理的无序数组及其子序列的相关问题研究的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 黄山风景区门票老年人优惠多少
- 下一篇: JAVA-初步认识-第十一章-objec