主要的方法
- 深度優先搜索,回溯算法
- 寬度優先搜索
- 是否有相同元素需要考慮等問題
針對所給問題,確定問題的解空間:
-
首先應明確定義問題的解空間,問題的解空間應至少包含問題的一個(最優)解。
-
確定結點的擴展搜索範圍 for等一系列循環等問題
-
以深度優先方式搜索解空間,並在搜索過程中用剪枝函數避免無效搜索。 判斷減支情況
int a[n];
try(int i)
{
if(i>n)
輸出結果;
else
{
for(j = 下界; j <= 上界; j=j+1)
// 枚舉i所有可能的路徑
{
if(fun(j)) // 滿足限界函數和約束條件
{
a[i] = j;
// 其他操作
try(i+1);
回溯前的清理工作(如a[i]置空值等);
}
}
}
}
題目一
子集合,subset 不包括相同元素的情況
- 深度遞歸優先搜索算法
// Recursion
class Solution {
public:
vector<vector<int> > subsets(vector<int> &S) {
vector<vector<int> > res;
vector<int> out;
sort(S.begin(), S.end());
getSubsets(S, 0, out, res);
return res;
}
void getSubsets(vector<int> &S, int pos, vector<int> &out, vector<vector<int> > &res) {
res.push_back(out);
// pos 是下界, size 是上界, 這也是
for (int i = pos; i < S.size(); ++i) {
out.push_back(S[i]);
getSubsets(S, i + 1, out, res);
out.pop_back();
}
}
};
整個過程添加如下的情況
如果包含相同的元素的話,判斷前後兩個元素是否相等,如果相等直接跳過去就可以了
public List<List<Integer>> subsetsWithDup(int[] nums) {
List<List<Integer>> list = new ArrayList<>();
Arrays.sort(nums);
backtrack(list, new ArrayList<>(), nums, 0);
return list;
}
private void backtrack(List<List<Integer>> list, List<Integer> tempList, int [] nums, int start){
list.add(new ArrayList<>(tempList));
// subset 是從start 開始,而全排列是從全部的元素重新開始即可
for(int i = start; i < nums.length; i++){
// 如果[1,2,2,] 的話,我們直接跳過去就可以了
if(i > start && nums[i] == nums[i-1]) continue; // skip duplicates
tempList.add(nums[i]);
backtrack(list, tempList, nums, i + 1);
tempList.remove(tempList.size() - 1);
}
}
全排列和子集合問題的不同
子集合不同判斷元素個數問題,直接添加即可, 並且要指定pos, 然後在dfs 裏面再pos +1,的情況
全排列的問題
全排列和subset 的區別主要是元素的個數哪裏
for 代表解空間樹節點的可以擴展的方向是哪裏的
- start 代表下界,size代表上界的基本情況
subset
// 開始遍歷的順序不一樣的
public void dfs {
for(int i = start;i<nums.size();i++)
{
dfs( i+1, )
}
}
全排列
類似於寬度優先搜索,因此數組裏面的元素每個元素都需要使用得到的,因此我們
//所有的節點都得重新開始考慮的,上界是數組
public void dfs{
for (int i=0;i<nums.size();i++)
{
// 判斷tmp list 裏面是否已經存在該元素
//判斷該元素是否已經使用過的情況下來處理即可的問題
dfs(i開始)
}
}
combination sum
- 每個數字可以重複使用的情況,一個數字可以使用多次的情況,並且數組裏面沒有重複
class Solution {
public List<List<Integer>> combinationSum(int[] candidates, int target) {
List<List<Integer>> result =new ArrayList<>();
if(candidates==null ||candidates.length==0)
return result;
Arrays.sort(candidates);
backdfs(candidates,target,0,new ArrayList<>(),result);
return result;
}
private void backdfs(int [] candidates,int remain, int start, List<Integer>tmp, List<List<Integer>>result)
{
//如果不滿足直接停止往下搜索
if(remain<0)
return;
//滿足條件接着所示,
if(remain==0)
result.add(new ArrayList<>(tmp));
for(int i=start;i<candidates.length;i++){
//處理當前節點元素
tmp.add(candidates[i]);
//往下搜索
backdfs(candidates,remain-candidates[i],i,tmp,result);
//在該條路徑上面搜索結束,退回上一層節點
tmp.remove(tmp.size()-1);
}
}
}
- 數組裏面有重複元素,並且每個元素只能會用一遍的情況
public List<List<Integer>> combinationSum2(int[] candidates, int target) {
List<List<Integer>> list = new LinkedList<List<Integer>>();
Arrays.sort(candidates);
backtrack(list, new ArrayList<Integer>(), candidates, target, 0);
return list;
}
private void backtrack(List<List<Integer>> list, List<Integer> tempList, int[] cand, int remain, int start) {
if(remain < 0) return; /** no solution */
else if(remain == 0) list.add(new ArrayList<>(tempList));
else{
for (int i = start; i < cand.length; i++) {
if(i > start && cand[i] == cand[i-1]) continue; /** skip duplicates */
tempList.add(cand[i]);
//元素不可重用的情況
backtrack(list, tempList, cand, remain - cand[i], i+1);
tempList.remove(tempList.size() - 1);
}
}
}
樹的路徑和
從根節點到葉子節點情況,只要滿足該種情況即可,然後在這裏面我們需要注意的是向下搜索的方向,從i+1,變成了向左右孩子節點走下棋的情況
並且擴張方向只有一個節點,所有也是沒有for 循環該種情況發生過你,而是中間結果直接add
public List<List<Integer>> pathsum(TreeNode root, int sum){
List<List<Integer>> result =new List<>();
if(root==null)
return result;
List<Integer> current= new ArrayList<>();
dfs(result,root,current,sum);
return reuslt;
}
pulic private void dfs(TreeNode root,int sum, List<List<Integer>> result, List<Integer>current){
// 遞歸的出口信息
if(root==null)
return ;
// 滿足條件的情況
current.add(root.val);
if(root.left==null && root.right==null&& root.val==sum)
{
result.add(current);
current.remove(current.size()-1);
return;
}
dfs(root.left,sum-root.val,result,current);
dfs(root.right,sum-root.val,result,current);
current.remove(current.size()-1);
}
}