1. 程式人生 > 其它 >論文解讀(DAEGC)《Improved Deep Embedded Clustering with Local Structure Preservation》

論文解讀(DAEGC)《Improved Deep Embedded Clustering with Local Structure Preservation》

回溯

0 回溯框架

解決一個回溯問題,實際上就是一個決策樹的遍歷過程。只需要思考 3 個問題:

  • 路徑:也就是已經做出的選擇

  • 選擇列表:也就是你當前可以做的選擇

  • 結束條件:也就是到達決策樹底層,無法再做選擇的條件

回溯演算法的框架:

result = []
def backtrack(路徑, 選擇列表):
    if 滿足結束條件:
        result.add(路徑)
        return
    
    for 選擇 in 選擇列表:
        做選擇
        backtrack(路徑, 選擇列表)
        撤銷選擇

核心: for 迴圈裡面的遞迴,在遞迴呼叫之前「做選擇」,在遞迴呼叫之後「撤銷選擇」

1 問題列表

2 括號生成

2.1 問題重現

數字 n 代表生成括號的對數,請你設計一個函式,用於能夠生成所有可能的並且 有效的 括號組合

示例 1:

輸入:n = 3
輸出:["((()))","(()())","(())()","()(())","()()()"]

示例 2:

輸入:n = 1
輸出:["()"]

提示:

  • 1 <= n <= 8
2.2 解題思路

根據回溯演算法的框架,我們需要:

  • 一個儲存最終結果的列表 res ,用來儲存所有合法的路徑
  • 一個路徑選擇列表 road ,表示當前可以做的選擇

那麼,接下來要討論的就是什麼條件下符合?什麼條件下不符合?(即:回溯繼續條件 && 回溯終止條件)

比如 (())、()() 就是一個合法的括號,))(( 就不是一個合法的括號,即:

  • 左右括號數量必須相等(繼續條件)
  • 不能只出現左(右)括號而不出現右(左)括號(終止條件)
  • 當左右括號數量相等且都用完的情況下,得到一個合法的路徑,將結果加入到總結果 res 中(終止條件)

接下來就是回溯的核心:做選擇 && 撤銷選擇

  • 做選擇:只有兩種情況,要麼選擇 ( ,要麼選擇 )

  • 做完選擇後,選擇列表 road 發生了該表,繼續進行回溯

  • 撤銷選擇:左選擇時選擇了左(右)括號,那麼就撤銷左(右)括號

至此,演算法大題思路已經成型,程式碼如下:

class Solution {
	
    //總的結果列表
    List<String> res = new ArrayList<>();

    public List<String> generateParenthesis(int n) {
        String road = "";
        backtrack(n, n, road, res);
        return res;
    }

    public void backtrack(int left, int right, String road, List<String> res){
        //非法條件,回溯終止
        if (left < 0 || right < 0) 
            return;
        if (right < left) 
            return;
        //合法情況,加入結果 res
        if (left == 0 && right == 0){
            res.add(road);
            return;
        }

        //嘗試新增左括號並撤銷
        road += "(";
        backtrack(left - 1, right, road, res);
        road = road.substring(0, road.length() - 1);
		
        //嘗試新增右括號並撤銷
        road += ")";
        backtrack(left, right - 1, road, res);
        road = road.substring(0, road.length() - 1);
    }
}

提交通過!

3 全排列

3.1 問題重現

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

示例 1:

輸入:nums = [1,2,3]
輸出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]

示例 2:

輸入:nums = [0,1]
輸出:[[0,1],[1,0]]

示例 3:

輸入:nums = [1]
輸出:[[1]]

提示:

  • 1 <= nums.length <= 6
  • -10 <= nums[i] <= 10
  • nums 中的所有整數 **互不相同 **
3.2 解題思路

最後的總結果列表不用多說,必備的 res

還需要一個當前選擇列表 road,怎麼判斷呢?每次迴圈的時候,判斷一下是否該元素已經被使用,若未被使用,才可以進行回溯

結束條件:當前選擇路徑列表 road 長度等於 nums 的長度時,說明已經兩所有元素使用完,產生了一個合法的路徑,將該路徑加入總結果列表 res 中,本次回溯結束

思路清晰,程式碼如下:

class Solution {

    List<List<Integer>> res = new LinkedList<>();

    public List<List<Integer>> permute(int[] nums) {
        LinkedList<Integer> road = new LinkedList<>();
        backtrack(nums, road);
        return res;
    }

    public void backtrack(int[] nums, LinkedList<Integer> road){
        if (nums.length == road.size()){
            res.add(new LinkedList<>(road));
            return;
        }

        for (int num : nums) {
            if (road.contains(num)) {
                continue;
            }
            road.add(num);
            backtrack(nums, road);
            road.removeLast();
        }
    }
}

提交通過!