枚举算法

常用算法与数据结构模板系列(一)

Posted by John Mactavish on August 26, 2020

2020-09-16 更新

今天发现“常用算法与数据结构模板”这个主题的内容写得实在是太多了,以至于放在一篇博文里面 显得不太合适。所以今天把这个主题分拆成一系列的文章(话说,这其实应该是我写的第一个系列)。

DFS

模板如下:

void dfs(int 当前状态)
{
      if(当前状态为边界状态)
      {
        记录或输出
        return;
      }
      for(每一个子状态)		//横向遍历解答树所有子节点
      {
            修改了全局变量
            if(子状态满足约束条件)
            {
              dfs(子状态)
            }
            恢复全局变量//回溯部分
      }
}

实例:

# 题目:输出一个数组中所有元素的全排列

def full_permutation(arr):
    visited = [1] * len(arr)
    temp = arr[:] # 浅拷贝
    result = []
    def dfs(position):
        if position == len(arr):
            result.append(temp[:])  # 浅拷贝,否则result中所有行都一样
            return
        for index in range(len(arr)): # range all possible paths downside
            if visited[index]:
                temp[position] = arr[index]
                visited[index] = 0
                dfs(position+1)
                visited[index] = 1
    dfs(0)
    return result

print(full_permutation([1,2,3]))
# [[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 2, 1], [3, 1, 2]]
// 题目:给定一个整型数组, 你的任务是找到所有该数组的递增子序列,递增子序列的长度至少是2。

// 示例:
// 输入: [4, 6, 7, 7]
// 输出: [[4, 6], [4, 7], [4, 6, 7], [4, 6, 7, 7], [6, 7], [6, 7, 7], [7,7], [4,7,7]]

// 说明:
// 给定数组的长度不会超过15。
// 数组中的整数范围是 [-100,100]。
// 给定数组中可能包含重复数字,相等的数字应该被视为递增的一种情况。

import java.util.ArrayList;
import java.util.List;

class Solution { // 递归枚举
    private List<List<Integer>> res = new ArrayList<>();
    private int[] nums;

    public List<List<Integer>> findSubsequences(int[] nums) {
        this.nums = nums;
        backtrace(new ArrayList<Integer>(), 0, Integer.MIN_VALUE);
        return res;
    }

    private void backtrace(List<Integer> path, int cur, int last) {
        if (cur >= nums.length) {
            if (path.size() >= 2) {
                res.add(new ArrayList<>(path)); // note to copy `path`
            }
            return;
        }

        if (last != nums[cur]) { // last 用于防止重复枚举
            // 两个相同元素有四种情况:
            //     前者被选择,后者被选择
            //     前者被选择,后者不被选择
            //     前者不被选择,后者被选择
            //     前者不被选择,后者不被选择
            // 第二种情况和第三种情况是重复的,我们令“前者被选择时后者必被选择”,舍弃了第二种
            backtrace(path, cur + 1, last);
        }
        if (nums[cur] >= last) {
            path.add(nums[cur]);
            backtrace(path, cur + 1, nums[cur]);
            path.remove(path.size() - 1); // `path` is reused throughout the whole DFS
        }
    }
}

最后附上 GitHub:https://github.com/gonearewe