LeetCode之回溯算法

回溯法也可以叫做回溯搜索法,它是一種搜索的方式。回溯是遞歸的副產品,只要有遞歸就會有回溯。因爲回溯的本質是窮舉,窮舉所有可能,然後選出我們想要的答案,如果想讓回溯法高效一些,可以加一些剪枝的操作,但也改不了回溯法就是窮舉的本質。

回溯法,一般可以解決如下幾種問題:

組合問題:N個數裏面按一定規則找出k個數的集合
切割問題:一個字符串按一定規則有幾種切割方式
子集問題:一個N個數的集合裏有多少符合條件的子集
排列問題:N個數按一定規則全排列,有幾種排列方式
棋盤問題:N皇后,解數獨等等

回溯搜索的遍歷過程:


回溯算法模板框架如下:

void backtracking(參數) {
    if (終止條件) {
        存放結果;
        return;
    }

    for (選擇:本層集合中元素(樹中節點孩子的數量就是集合的大小)) {
        處理節點;
        backtracking(路徑,選擇列表); // 遞歸
        回溯,撤銷處理結果
    }
}

子集

給你一個整數數組 nums ,數組中的元素 互不相同 。返回該數組所有可能的子集(冪集)。

解集 不能 包含重複的子集。你可以按 任意順序 返回解集。

題解:
class Solution {
    List<List<Integer>> result = new ArrayList<>();
    List<Integer> ans = new ArrayList<>();

    public List<List<Integer>> subsets(int[] nums) {
        dfs(nums, 0);
        return result;
    }

    public void dfs(int[] nums, int index) {
        if (index == nums.length) {
            //到最底一級分支了
            result.add(new ArrayList<Integer>(ans));
            return;
        }

        ans.add(nums[index]);
        dfs(nums, index+1); //繼續向下一級搜索
        ans.remove(ans.size()-1);  //執行結束後需要對 ans 進行回溯
        dfs(nums, index+1);
    }
}

全排列

給定一個不含重複數字的數組 nums ,返回其 所有可能的全排列 。你可以 按任意順序 返回答案。

題解:
class Solution {
    /**
     * 思路:
     * n個數全排列,從數學上想,一共有n!種排列方式,
     * 因爲第一個位置有n個選擇,第二個位置有(n-1)個選擇,以此類推,所有一共有 n*(n-1)*(n-2)*2*1
     * 解題過程就是要模擬這個過程
     *
     * baseCase  處理完最後一個元素,把當前Array做一份copy添加到答案裏
     * backtrack(array, index)
     * 從i= [index, n]之間依次選取所有元素進行搜索
     * 每次將i和index的元素交換 -> next_state
     * dfs(array, index+1)
     * 將i和index元素交換回來 -> restore state (回溯狀態)
     * @param nums
     * @return
     */
    public List<List<Integer>> permute(int[] nums) {
        List<List<Integer>> result = new ArrayList<>();  //最終返回結果
        dfs(result, nums, 0);
        return result;
    }

    /**
     * 回溯算法過程
     * @param result
     * @param nums
     * @param index
     */
    private void dfs(List<List<Integer>> result, int[] nums, int index) {
        if (index >= nums.length) {
            //當前分支搜索完畢,將當前集合遍歷,並存入總集合中
            List<Integer> ans = new ArrayList<>();
            for (int i=0;i<nums.length;i++) {
                ans.add(nums[i]);
            }
            result.add(ans);
            return;
        }

        //搜索沒有結束,將當前放的位置和剩下的數每一個進行交換,並且調用當前層級的dfs,然後再交換回來
        for (int i=index;i<nums.length;i++) {
            swap(nums, index, i);
            dfs(result, nums, index+1);
            swap(nums, i, index);
        }
    }

    public void swap(int[] nums, int i, int j) {
        int temp = nums[i];
        nums[i] = nums[j];
        nums[j] = temp;
    }
}
參考:

https://www.bilibili.com/video/BV1Ty4y1E7RM/?spm_id_from=333.337.search-card.all.click&vd_source=40c24e77b23dc2e50de2b7c87c6fed59

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章