669. 修剪二叉搜索樹-剪枝與回溯法應用或者分治法與回溯迭代法的應用

做題目前隨便說點

  • 樹是一種抽象數據類型,一種具有樹結構形式的數據集合。

  • 節點個數確定,有層次關係。

  • 有根節點。

  • 除了根,每個節點有且只有一個父節點。

  • 沒有環路。

  • 所有數據結構都可以用鏈表表示或者用數組表示,樹也一樣。

669. 修剪二叉搜索樹

給定一個二叉搜索樹,同時給定最小邊界L 和最大邊界 R。通過修剪二叉搜索樹,使得所有節點的值在[L, R]中 (R>=L) 。你可能需要改變樹的根節點,所以結果應當返回修剪好的二叉搜索樹的新的根節點。

示例 1:

輸入:
1
/
0 2

L = 1
R = 2

輸出:
1

2
示例 2:

輸入:
3
/
0 4

2
/
1

L = 1
R = 3

輸出:
3
/
2
/
1

來源:力扣(LeetCode)
鏈接:https://leetcode-cn.com/problems/trim-a-binary-search-tree
著作權歸領釦網絡所有。商業轉載請聯繫官方授權,非商業轉載請註明出處。

解題:

審題:

  • 這道題目要求我們自底向上的重構二叉樹。
  • 所以得采用回溯法。
  • 其次就是這道題目,可以寫剪枝函數,減少不必要的窮舉。

框架代碼如下:

class Solution {
    
     TreeNode rescursive(TreeNode node) {
        if( ... ) {
            return node
        } else {
           if(是否需要訪問左節點) {
              rescursive(node.left);
           } else {
             // 處理左節點
           }
           
           // 中序遍歷
           if(是否需要訪問右節點) {
             rescursive(node.right);
           } else {
             // 處理右節點
           }
           
           // 回溯處理
           // 返回結果
           return result;
           
        }
     }
    
}

開始解題:


/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public static int L;
    public static int R;

    public boolean isBelongToTarget(TreeNode node) {
        if(L <= node.val && node.val <= R) {
            return true;
        } else {
            return false;
        }
    }
    public boolean isNeedVisitLeft(TreeNode node) {
        if(node.val > L) {
            return true;
        } else {
            return false;
        }
    }
    public boolean isNeedVisitRight(TreeNode node) {
        if(node.val < R) {
            return true;
        } else {
            return false;
        }
    }

    TreeNode rescursive(TreeNode node) {
        if(node == null) {
            return node;
        } else {
           if(isNeedVisitLeft(node)) {
              node.left = rescursive(node.left);
           } else {
             // 處理左節點
             node.left = null;
           }
           
           // 中序遍歷
           if(isNeedVisitRight(node)) {
             node.right =  rescursive(node.right);
           } else {
             // 處理右節點
             node.right = null;
           }
           
           // 回溯處理
           if(isBelongToTarget(node)) {
               return node; 
           } else if(node.val > R) {
               return node.left;
           } else if( node.val < L) {
               return node.right;
           } else {
               return null;
           }
        }
     }


    public TreeNode trimBST(TreeNode root, int L, int R) {
        Solution.L = L;
        Solution.R = R;
        TreeNode result = rescursive(root);
        return result;
    }
}




總結:

  • 這道題目先用前序遍歷,實現左右子樹遞歸的剪枝。
  • 然後在對結果進行回溯處理。
  • 回溯處理其實屬於後序遍歷的操作。
  • 所以這道題目既用到前序遍歷剪枝技巧,又用到的後序遍歷的回溯處理。
  • 相對前面幾題難了一點點。
  • 如果這道題目要求寫迭代法又是怎樣的過程呢?
  • 依然是要實現回溯法,這個是我們繞不開的話題,只不過採用的是迭代算法。爲了降低問題的複雜度,我們可以用分治的思想解題。先把小於L的節點迭代刪除,然後再把大於R的節點迭代刪除。剩下的樹節點值,自然也就位於L與R之間。
  • 換句話說,如果問題拿到手很複雜,我們除了可以由簡單到複雜地左歸納法之外,應該考慮是否能用分治法簡化。
  • 分治法用於,當把問題分解後再組合依然能解決原來的問題就嘗試分治。
  • 分治之後依然可以進一步採用歸納法。
  • 你也可以在寫出了遞歸算法後利用計算機執行函數棧的思路去寫迭代算法,然後再簡化一下。(手動狗頭)
    考慮到代碼的維護難度,還是不建議複雜化。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章