leetcode 劍指 Offer 57 - II. 和為s的連續正數序列
阿新 • • 發佈:2021-02-09
劍指 Offer 57 - II. 和為s的連續正數序列
輸入一個正整數 target ,輸出所有和為 target 的連續正整數序列(至少含有兩個數)。序列內的數字由小到大排列,不同序列按照首個數字從小到大排列。
示例 1:
輸入:target = 9
輸出:[[2,3,4],[4,5]]
示例 2:
輸入:target = 15
輸出:[[1,2,3,4,5],[4,5,6],[7,8]]
限制:
1 <= target <= 10^5
解法一:dfs深度優先搜尋
思路:
dfs深度優先搜尋,每次新增之前先判斷temp是否為空或者 temp 是是否包含 i - 1,
1 class Solution { 2 public int[][] findContinuousSequence(int target) { 3 ArrayList<LinkedList<Integer>> res = new ArrayList<LinkedList<Integer>>(); 4 LinkedList<Integer> temp = new LinkedList<Integer>(); 5 // 使用dfs將所有的組合都找到並存在res列表中 6 dfs(0, target, 0, res, temp); 7 //System.out.println(res); 8 // 將res中的內容拷貝到一個二維陣列中 9 int[][] ret = new int[res.size()][]; 10 for(int i= 0; i < res.size(); i++){ 11 ret[i] = new int[res.get(i).size()]; 12 for(int j = 0; j < res.get(i).size(); j++){ 13 ret[i][j] = res.get(i).get(j); 14 } 15 } 16 return ret; 17 } 18 19 // dfs遞迴 20 public void dfs(int nowSum, int target, int nowNum, ArrayList<LinkedList<Integer>> res, LinkedList<Integer> temp){ 21 if(nowSum == target){ 22 res.add(new LinkedList<Integer>(temp)); 23 return; 24 } 25 if(nowSum > target){ 26 return; 27 } 28 // 嘗試新增每個大於當前數字的元素 29 for(int i = nowNum + 1; i < target; i++){ 30 //System.out.println(i); 31 // 只能是連續的數字組合 32 if((temp.isEmpty() || temp.contains(i-1)) && nowSum + i <= target){ 33 temp.addLast(i); 34 dfs(nowSum + i, target, i, res, temp); 35 temp.removeLast(); // 回溯之後消除上次新增元素的影響 36 } 37 } 38 39 } 40 }
這個方法當target比較大的時候會超時,只通過了22個測試用例
解法二:迭代
思路:
迭代, 以1-target/2的所有元素為起點,尋找滿足條件的組合
1 class Solution { 2 public int[][] findContinuousSequence(int target) { 3 ArrayList<int[]> res = new ArrayList<>(); 4 5 int sum = 0, limit = target / 2; // 因為最少兩個數,所以上界為target / 2 6 for(int i = 1; i <= limit; i++){ 7 for(int j = i; ; j++){ 8 sum += j; 9 if(sum > target){ 10 sum = 0; 11 break; 12 }else if(sum == target){ 13 int[] arr = new int[j - i + 1]; 14 for(int k = i; k <= j; k++){ 15 arr[k-i] = k; 16 } 17 res.add(arr); 18 } 19 } 20 } 21 22 return res.toArray(new int[res.size()][]); 23 } 24 }
leetcode執行時間為8ms, 空間為36.6MB
複雜度分析:
空間複雜度:除了儲存結果的陣列只需要常數個變數,所以空間複雜度為O(1)
解法三:滑動視窗
思路:
滑動視窗,這個解法其實是對上個解法的改進,當我們判斷到[i,j+1]的元素之和已經大於 target了,解法二是直接從( i + 1)開始,加上(i + 2), 一直到 下次sum 大於 target, 假設區間為 [i+1, k], 但是其實這個區間的[i+1,j]的和在上一次迭代的時候就已經求出來了,所以我們只需要在上一次迭代的sum減去 i, 然後從j開始累加,一直累加到k, 這樣可以避免很多重複計算
1 class Solution {
2 public int[][] findContinuousSequence(int target) {
3 ArrayList<int[]> res = new ArrayList<>();
4
5 int sum = 0, limit = target / 2; // 因為最少兩個數,所以上界為target / 2
6 int i = 1, j = 1;
7 while(i <= limit){
8 if(sum < target){
9 sum += j;
10 j++; // 右指標後移
11 }else if(sum > target){
12 sum -= i;
13 i++; // 左指標右移
14 }else{
15 int[] arr = new int[j-i]; // j還沒被新增到sum中,所以區間大小為j - i
16 for(int k = i; k < j; k++){
17 arr[k-i] = k;
18 }
19 res.add(arr);
20 sum -= i;
21 i++;
22 }
23
24 }
25
26 return res.toArray(new int[res.size()][]);
27 }
28 }
leetcode執行時間為:2ms, 空間為36.9MB
複雜度分析:
時間複雜度:右指標沒有回溯,所以相當於兩個指標都從 1增大到了 target/2, 所以時間複雜度為O(target)
空間複雜度:除了儲存結果的陣列只需要常數個變數,所以空間複雜度為O(1)