力扣第39題 組合總和 中等難度 卡了一天沒做出來

加文斯利發表於2020-11-27

先看一眼題:
在這裡插入圖片描述

我的思路:比如[1,2,6],target=9
先找到大於等於9的數,沒有則找到陣列長度length
從length-1遍歷到0,
比如這裡:
從6開始遍歷,遍歷到最前面為止
···遍歷6的時候,遞迴的尋找一個3,使得3+6等於9
······遞迴時,target變為3
······先找到大於等於3的數,然後其下標-1
······比如這裡,從2開始遍歷,遍歷到最前面為止
······遍歷2的時候,遞迴的找到一個1,使得1+2等於3
·········遞迴時,target變為1
·········先找到大於等於1的數,然後其下標-1
·········下標為-1,退出遞迴
······此時將[6,2,1]加入結果佇列
······遍歷1的時候,將[6,1,1,1]加入結果佇列
······由於1前面沒有數,遞迴結束
···遍歷2的時候,遞迴找到一個7
······遞迴時,target變為7
······從下標0開始(值為1)遍歷,(過程省略),將[2,1,1,1,1,1,1,1]加入結果佇列,遞迴結束
···遍歷22=4的時候,遞迴的找到一個5
······從下標0開始(值為1)遍歷,(過程省略),將[2,2,1,1,1,1,1]加入結果佇列,遞迴結束
···遍歷2
3=6的時候,遞迴的找到一個3
······從下標0開始(值為1)遍歷,(過程省略),將[2,2,2,1,1,1]加入結果佇列,遞迴結束
···遍歷24=6的時候,遞迴的找到一個1
······從下標0開始(值為1)遍歷,(過程省略),將[2,2,2,2,1]加入結果佇列,遞迴結束
···由於2
5>9,故遍歷結束,遞迴結束
···遍歷1的時候,由於[1,1,1,1,1,1,1,1,1]滿足條件,將其加入結果佇列
···由於1前面沒有數,無法遞迴遍歷,因此遞迴結束
結束,返回結果佇列


思路是沒錯的,但是程式碼寫起來出問題了,大概知道哪裡出問題了,返回值沒設定好,錯誤程式碼如下:

public static List<List<Integer>> combinationSum(int[] candidates, int target) {
        Arrays.sort(candidates);
        List<List<Integer>> resList = new ArrayList<>();
        int index;
        //首先找到距離target最近的較大值或者target本身,從它-1開始對陣列前面的元素進行遍歷
        for (index = 0; index < candidates.length; index++) {
            if (candidates[index] == target) {
                List<Integer> list = new ArrayList<>();
                list.add(target);
                resList.add(list);
                break;
            } else if (candidates[index] > target) {
                break;
            }
        }
        for (int i = index - 1; i >= 0; i--) {
            //index對應的元素可能是由i對應的數的n倍+tar(新的target的簡稱)決定的
            for (int k = 1; k * candidates[i] <= target; k++) {
                List<Integer> list = new ArrayList<>();
                int old = k * candidates[i];
                //先放入k個candidates[i]
                for (int j = 0; j < k; j++) {
                    list.add(candidates[i]);
                }
                if (old == target) {
                    resList.add(list);
                } else {
                    //尋找target-old
                    int r = find(candidates, target - old, list, i, resList);
                    //如果r==target-old,說明list裡面沒有-1
                    if (r == target - old) {
                        resList.add(list);
                    }
                }
            }
        }
        return resList;
    }

    //從arr[0,right)裡找一個新的target,使得新的target+當前數==target,返回值為新的target
    //找不到則返回-1
    public static int find(int[] arr, int target, List<Integer> list, int right, List<List<Integer>> resList) {
        //首先找到距離target最近的較大值或者target本身,從它-1開始對陣列前面的元素進行遍歷
        int index;
        boolean firstT = false;
        for (index = 0; index < right; index++) {
            if (arr[index] == target) {
                list.add(target);
                resList.add(list);
                firstT = true;
                //注意:此時雖然找到了滿足的數,但是這個數也有可能是比它更小的多個陣列成
                //這兩種情況都要加入resList
                //但是也有可能不存在比它更小的多個陣列成,避免錯誤的返回-1,所以引入firstT
                //因為list是指標,所以不能直接remove
                list = new ArrayList<>(list);
                list.remove((Integer) target);
                break;

            } else if (arr[index] > target) {
                break;
            }
        }
        for (int i = index - 1; i >= 0; i--) {
            //index對應的元素可能是由i對應的數的n倍+tar(新的target的簡稱)決定的
            for (int k = 1; k * arr[i] <= target; k++) {
                int old = k * arr[i];
                //先放入k個arr[i]
                for (int j = 0; j < k; j++) {
                    list.add(arr[i]);
                }
                if (old == target) {
                    resList.add(list);
                    return target;
                } else {
                    //尋找target-old
                    int r = find(arr, target - old, list, i, resList);
                    //如果r==target-old,說明list裡面沒有-1
                    if (r == target - old) {
                        resList.add(list);
                        return r;
                    } else if (r == -1) {
                        //移除list裡放入的那個數
                        for (int j = 0; j < k; j++) {
                            list.remove((Integer) arr[i]);
                        }
                    }
                }
            }
        }
        if (firstT)
            return target;
        return -1;
    }

實在是不想改了,要改動感覺工程還蠻大的,關鍵是寫遞迴的這個思路沒理清,所以看答案了。
看了答案,我吐了。答案的思路就是我這個思路,而且這種思路有一個名字,叫做回溯。回溯確實是用遞迴實現的,但是傳入的引數和返回值一定要好好思考怎麼寫啊……
答案的程式碼就不放了,這個題馬克一下,回來再看。學一下怎麼用遞迴實現回溯

相關文章