LeetCode279完全平方數(遞歸/動態規劃(DFS)+(廣度優先搜索BFS)

完全平方數>>>
在這裏插入圖片描述

思路一:就是搜索問題,在數據中查找滿足某種條件的組合,根據組合問題則可以採用遞歸+回溯的深度優先搜索算法
在這裏插入圖片描述

  /**
     * 回溯法:
     * 相當於一種暴力法:考慮所有的分解方案,找出最小的解
     * 超出時間限制
     */
    public int numSquares(int n) {
        return numSquaresHelper(n,new HashMap<>());
    }

    //暴力回溯
    private int numSquaresHelper(int n) {
        if (n == 0) {
            return 0;
        }
        int count = Integer.MAX_VALUE;
        //依次減去一個平方數
        for (int i = 1; i * i <= n; i++) {
            //選最小的
            count = Math.min(count, numSquaresHelper(n - i * i) + 1);
        }
        return count;
    }



上面可能存在時間超時的問題,同時也存在重複計算的問題,可以採用備忘錄的模式,在遞歸的過程中,將一些中間結果f(n)保存,再次求f(n)值的時候可以直接取而不用重新計算


 /**
     * 回溯法改進:保存之前計算過得值
     * 當然上邊的會造成很多超時,很多解會重複的計算,之前也遇到了很多這種情況
     * 我們需要memoization技術,也就是把過程中的解利用HashMap全部保存起來
     * @param n
     */
    private int numSquaresHelper(int n, HashMap<Integer,Integer>map) {

        if(n==0) return 0;
        //使用HashMap保存
        if(map.containsKey(n)) return map.get(n);

        int count = Integer.MAX_VALUE;

        for(int i=1;i*i<=n;i++){
            int size = numSquaresHelper(n-i*i,map)+1;
            count = Math.min(count,size);
        }
        map.put(n,count);
        return count;
    }

思路二:上面的遞歸是自頂向下的方法,存在大量的重複計算,思考是否可以採用動態規劃那麼就需要思考狀態轉移方程,以及狀態定義和狀態初始值dp[i]=Math.min(dp[i-j*j]+1,dp[i])

   /**
     * 更進一步採用動態規劃
     * @param n
     * dp[i] 構成 數字i的完全平方數的個數,
     *          dp[i] = Math.min(dp[i-j*j])+1  其中j處於1和sqrt(i)之間
     */
    public int numSquares1(int n) {

        if(n==0) return 0;

        int[] dp = new int[n+1];

        dp[0]=0;
        dp[1]=1;

        for(int i=2;i<n+1;i++){
            dp[i]=Integer.MAX_VALUE;
            for(int j=1;j*j<i;j++){
                dp[i]=Math.min(dp[i],dp[i-j*j]+1);
            }

        }
        return dp[n];
    }

思路三:上面都是深度優先搜索,一條路先走到底,接着嘗試走其他路,可以採用廣度優先搜索BFS,多條路一起走,DFS 是一直做減法,然後一直減一直減,直到減到 0 算作找到一個解。屬於一個解一個解的尋找。BFS 的話,我們可以一層一層的算。第一層依次減去一個平方數得到第二層,第二層依次減去一個平方數得到第三層。直到某一層出現了 0,此時的層數就是我們要找到平方數和的最小個數。

在這裏插入圖片描述

    /**
     * 採用BFS
     * @param n
     * @return
     * 相對於之前的DFS,當然可以使用BFS
     * DFS 是一直做減法,然後一直減一直減,直到減到 0 算作找到一個解。屬於一個解一個解的尋找。
    BFS 的話,我們可以一層一層的算。第一層依次減去一個平方數得到第二層,第二層依次減去一個平方數得到第三層。直到某一層出現了 0,此時的層數就是我們要找到平方數和的最小個數。

     */
    public int numSquares2(int n){

        Queue<Integer> queue = new LinkedList<>();
        HashSet<Integer> visited = new HashSet<>();

        int level = 0;
        queue.add(n);

        while(!queue.isEmpty()){

            int size = queue.size();
            level++;//開始生成下一層
            //把隊列中的所有元素取出來
            for(int i=0;i<size;i++){
                //隊列中取出一個元素
                int cur = queue.poll();
                //依次減去1,4,9......生成下一層節點
                for(int j=1;j*j<=cur;j++){
                    int next = cur-j*j;
                    if(next==0) return level;

                    //隊列中不包含該元素則將其添加到隊列中
                    if (!visited.contains(next)) {
                        queue.offer(next);
                        visited.add(next);
                    }
                }






            }

        }

        return -1;
    }

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章