算法: 回溯法大全

回溯法(探索与回溯法)是一种选优搜索法,又称为试探法,按选优条件向前搜索,以达到目标。但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法,而满足回溯条件的某个状态的点称为“回溯点”。


1.题目:

Given two integers n and k, return all possible combinations of knumbers out of 1 ... n.

For example,
If n = 4 and k = 2, a solution is:

[
  [2,4],
  [3,4],
  [2,3],
  [1,2],
  [1,3],
  [1,4],
]
思路:

1.创建一个全局变量保存结果。

2.创建一个函数用于递归,传入的变量有,n,k(list中现在还差几个),start(从哪里开始),list(局部变量)

3.递归结束条件:当k<0时返回。当k==0时说明局部变量满足,放入全局变量后返回

4.递归部分: list中存入新值,调用本身,删除存入的心值。

代码:

import java.util.*;
public class Solution {
    ArrayList> ret = new ArrayList>();
    public ArrayList> combine(int n, int k) {
        ArrayList list = new ArrayList();
        helper(n,k,1,list);
        return ret;
    }
    public void helper(int n, int k, int start, ArrayList list)
    {
        if(k < 0 )
            return;
        if( k==0)
        {
            ret.add(new ArrayList(list));
        }
        else
        {
            for(int i=start; i<=n; i++)
            {
                list.add(i);
                helper(n, k-1, i+1, list);
                list.remove(list.size() - 1);
            }
        }
    }
}



2.题目:

Given a set of distinct integers, S, return all possible subsets.

Note:

  • Elements in a subset must be in non-descending order.
  • The solution set must not contain duplicate subsets.


For example,
If S =[1,2,3], a solution is:

[
  [3],
  [1],
  [2],
  [1,2,3],
  [1,3],
  [2,3],
  [1,2],
  []
]
思路:
1.创建一个全局变量保存结果。
2.创建一个函数用于递归,num(题目给定数组),k(list还剩k个满足条件),start(数组中从该索引开始),list(局部保存结果)
        3.递归结束条件: 当k<0时返回,但k==0时,将局部变量放入全局变量中后返回。
        4.递归部分: 想局部变量新增一个数,调用自身,然后删除新增的数。
代码:
using System;
using System.Collections.Generic;
namespace subsets
{
    class Program
    {
       static List> ret = new List>();
        static void Main(string[] args)
        {
            List list = new List();
            int[] num = {1,2,3};
            Array.Sort(num);
            for (int i = 0; i <= num.Length; i++)
            {
                helper(num, i,0,list);
            }
            //打印 ret
        }

        static void helper(int[] num, int k, int start, List list)
        {
            if (k < 0)
                return;
            if (k == 0)
            {
                ret.Add(list);
            }
            else
            {
                for (int i = start; i < num.Length; i++)
                {
                    list.Add(num[i]);
                    helper(num, k - 1, i + 1, list);
                    list.RemoveAt(list.Count -1);
                } 
            }
        }
    }
}


3.题目:
Description
乔治拿来一组等长的木棒,将它们随机地裁断,使得每一节木棍的长度都不超过50个长度单位。然后他又想把这些木棍恢复到为裁截前的状态,但忘记了初始时有多少木棒以及木棒的初始长度。请你设计一个程序,帮助乔治计算木棒的可能最小长度。每一节木棍的长度都用大于零的整数表示。
Input
输入包含多组数据,每组数据包括两行。第一行是一个不超过64的整数,表示砍断之后共有多少节木棍。第二行是截断以后,所得到的各节木棍的长度。在最后一组数据之后,是一个零。
Output
为每组数据,分别输出原始木棒的可能最小长度,每组数据占一行。
Sample Input
9  5 2 1 5 2 1 5 2 1  4  1 2 3 4  0  
Sample Output
6  5

思路:
1.将木棒按从大到小进行排序,该顺序为搜索顺序。组合木棒必须大于等于最长的木棒。
2.当一个木棒组合成功,那么跳入下一根木棒的组合。如果失败,则不需要进行寻找其他的可能组合。
3.但木棒组合失败,在本次组合中相同长度的木棒跳过。
4.如果木棒left等于零与木棒num[i]组合失败,即num[i]不能组合成功。

代码:
using System;

namespace 乔治小木棍
{
    class Program
    {
        static void Main(string[] args)
        {
            string line;
            string[] p;
            while ((line = Console.ReadLine()) != "0")
            {
                int n = int.Parse(line);
                p = Console.ReadLine().Trim().Split(' ');
                int[] num = new int[n];
                int sumLen = 0;//小木棍总长度 
                for (int i = 0; i < n; i++)
                {
                    num[i] = int.Parse(p[i]);
                    sumLen += num[i];
                }
                for (int i = 0; i < n; i++)
                {
                    for (int j = n - 1; j > i; j--)
                    {
                        if (num[j] > num[j - 1])
                        {
                            int temp = num[j];
                            num[j] = num[j - 1];
                            num[j - 1] = temp;
                        }
                    }
                }
                int curLen = num[0]; //检查当前长度是否满足条件,最小长度为最大的木棍长度
                bool[] flag = new bool[n]; //记录相对应位置小木棍否被使用过
                flag[0] = true;
                while (curLen <= sumLen)
                {
                    if (sumLen % curLen == 0)
                    { 
                        if(dfs(num, flag, n, sumLen - num[0], curLen, num[0]))
                            break;
                    }
                    curLen++;
                }
                Console.WriteLine(curLen);
            } 
        }

        public static bool dfs(int[] num, bool[] flag, int n, int sumLen, int curLen, int left)
        {
            if (sumLen == 0)
                return true;
            if (curLen == left)
                left = 0;
            for (int i = 0; i < n; i++)
            {
                if (!flag[i])
                {
                    if (left + num[i] == curLen)
                    {
                        flag[i] = true;
                        if (dfs(num, flag, n, sumLen - num[i], curLen, 0))
                            return true;
                        flag[i] = false;
                        return false;
                    }
                    else if (left + num[i] < curLen)
                    {
                        flag[i] = true;
                        if (dfs(num, flag, n, sumLen - num[i], curLen, left + num[i]))
                            return true;
                        flag[i] = false;
                        if (left == 0)
                            return false;
                        while (i+1




4.题目:
输入一颗二叉树和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。
思路:

1.典型的回溯法,用递归进行探索。

2.传递的参数包括:root //当前节点. ,expectNumber //期望值 ,sum//当前总数, list //当前遍历过的节点, ret //保存所有路径的容器

3.只有当满足期望值且左右子节点为空时才是期望的路径。

代码:

using System.Collections.Generic;
/*
public class TreeNode
{
    public int val;
    public TreeNode left;
    public TreeNode right;
    public TreeNode (int x)
    {
        val = x;
    }
}*/

class Solution
{
    public List> FindPath(TreeNode root, int expectNumber)
    {
        // write code here
        List> ret = new List>();
        dfs(root,expectNumber,0,new List(),ret);
        return ret;
    }
    
    public void dfs(TreeNode root,int expectNumber,int sum,List list, List> ret)
    {
        if(root == null)
            return;
        sum += root.val;
        list.Add(root.val);
        
        if(sum == expectNumber && root.left ==null && root.right==null)
        {
            ret.Add(new List(list));
        }
        else if(sum < expectNumber)
        {
            dfs(root.left,expectNumber,sum,list,ret);
            dfs(root.right,expectNumber,sum,list,ret);
        }
        
        list.RemoveAt(list.Count -1);
    }
}



5.题目
字符串的排列:输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。

思路:
1.利用原数组字符顺序来保存当前状态。
2.两种情况交换顺序:要么最开始,要么当前字符与开始字符不相等。

代码:
using System.Collections.Generic;
class Solution
{
    public List Permutation(string str)
    {
        // write code here
        List  list = new List();
        char[] chars = str.ToCharArray();
        helper(chars, 0, list);
        list.Sort();
        return list;
    }
    
    public void helper(char[] chars, int i, List list)
    {
        if(i == chars.Length-1)
        {
            list.Add(new string(chars));
        }
        else
        {
            for(int j=i; j










你可能感兴趣的:(算法)