[leetcode]DFS深度優先搜索題目整理 [編輯中]


做了一陣時間的leetcode,多多少少已經做了150左右的題量了。做多了對題目也有自己的心得。從以前看題目的毫無頭緒到現在的隱約抓住了一些規律性的東西。本篇是關於個人對leetcode上面典型DFS遞歸和深搜題目的總結整理,其中解題模式大同小異。本文會隨着刷題的過程逐漸更新。對於本篇文章的主題,如果要抽象出來一個公共思想,應該是如下的樣子:

def DFS(solution_Set, buildingAnswer, step)
    if Avaliable(buildingAnswer) :                        # 如果當前構造的解是可用的解,則添加到最終解之中
       solution_Set.add(buildingAnswer)
       return

    for bs in BuildingSpace:                                # 遍歷構造空間,是當前構造解可添加處理操作的空間
        if feasible(bs):                                    # 如果當前遍歷的操作對於當前階段是可行的,則對當前構造解施加操作
            Process(buildingAnswer, fs)
            DFS(solution_Set, buildingAnswer, step + 1)        # 在當前的處理上進入下一種處理,進一步搜索解
            Restore(buildingAnswer, fs)                        # 從下一個狀態搜索中返回,無論下一層是否是什么狀態。 恢復本階段的狀態,搜索本階段另外可施加的狀態。

以下的題目都是屬於一種類型的,只要無腦搜索配合適當的剪枝就可以了。

給定一個字符串,生成其中字符的所有的排列

 	   public ArrayList<String> Permutation(String str) {
	    	ArrayList<String> ret = new ArrayList<>();
	    	Set<String> hel=  new HashSet<>();
	    	StringBuilder sb = new StringBuilder(str);
	    	
	    	f(hel, sb, 0);
	    	ret = new ArrayList<>(hel);
	    	Collections.sort(ret);
	    	return ret;
	    }
		private void f(Set<String> hel, StringBuilder sb, int step){
			if(step == sb.length()-1){
				hel.add(sb.toString());
				return;
			}
			for(int i = step; i < sb.length(); i++){
				swap(sb,step,i);
				f(hel, sb, step+1);
				swap(sb,step, i);
				//while(i+1<sb.length() && sb.charAt(i+1) == sb.charAt(i))i++;
			}
			
		}
		private void swap(StringBuilder sb, int a, int b){
			char tc = sb.charAt(a);
			sb.setCharAt(a, sb.charAt(b));
			sb.setCharAt(b, tc);
		}

解析:該題注意,可能存在重復字符而且需要按照字典序輸出。這時就要感謝Java方便的集合庫了。唯一感覺代碼中不好的地方就是使用了這個Set,肯定有方法避免重復字符串的生成。其中f函數是典型的深度優先遍歷樹的從根到葉子的框架式代碼。

解法類似的題目有
46 Permutations -- 記得這題是某一年百度的筆試面試題。


22. Generate Parentheses
分析思路: 對於括號來說,有左括號和右括號(廢話)。對於DFS搜索來說,如果不加限制的搜索,就會生成(2^N)的結果。但是題目中要求的是可用的括號組合,即"(())"是正確的,")())"是錯誤的。因此,只要在dfs遞歸處加上if限制就行了,起到剪枝作用。套用上面的DFS框架就可以快速的寫出代碼。

    public List<String> generateParenthesis(int n) {
        List<String> ret = new ArrayList<>();
        StringBuilder sb = new StringBuilder();
        dfs(ret,sb, 0,0, n);
        
        return ret;
    }
    private void dfs(List<String> ret, StringBuilder sb, int ln, int rn, int n){
    	
    	if(ln + rn == 2*n){
    		ret.add(new String(sb));
    		return;
    	}
    	
    	if(ln < n){
    		sb.append("(");
    		dfs(ret, sb, ln+1, rn, n);
    		sb.deleteCharAt(sb.length()-1);
    	}
    	if(rn < ln){
    		sb.append(")");
    		dfs(ret, sb, ln, rn+1, n);
    		sb.deleteCharAt(sb.length()-1);
    	}
    }

105. Construct Binary Tree from Preorder and Inorder Traversal
這是一個給定中序、先序,生成樹的題目。
分析思路: 題目很簡單,即和手動計算的思路一樣。首先從先序序列中找到第一個,根據先序遍歷的性質,它就是第一個根,然后拿着這個根到中序序列中能把中序序列划分成兩個子序列,然后再遞歸的到先序中找下一個就是第二個根,然后再去中序中找。


public class Solution {
    
    private int findpos(int [] a, int k){
        for(int i = 0; i < a.length; i++){
            if(a[i] == k)return i;
        }
        return -1;//wrong
    }
    
    TreeNode f(int[] preorder, int [] inorder, int l, int r){

        TreeNode root = new TreeNode(preorder[rootind]);

        int k = findpos(inorder, preorder[rootind]);
        rootind++;
        
        if(k != l) {
            root.left = f(preorder, inorder,l, k-1);
        }
        if(k != r) {
            root.right = f(preorder, inorder,k+1, r);
        }
        return root;
    }

    int rootind;

    public TreeNode buildTree(int[] preorder, int[] inorder) {
        if(preorder.length == 0) return null;
        Integer rootind = 0;
        return f(preorder,inorder, 0, preorder.length-1);
    }
}



免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM