二叉樹類型OJ題篇1

1、左葉子之和

計算給定二叉樹的所有左葉子之和

    3
   / \
  9  20
    /  \
   15   7

在這個二叉樹中,有兩個左葉子,分別是 9 和 15,所以返回 24

方法一:遞歸

    def sumOfLeftLeaves(self, root: TreeNode) -> int:
        if not root:
            return 0
		# 判斷左子樹是否符合條件
        if root.left and not root.left.left and not root.left.right:
            return root.left.val + self.sumOfLeftLeaves(root.right)
        # 進行遞歸
        return self.sumOfLeftLeaves(root.left) + self.sumOfLeftLeaves(root.right)

方法二:非遞歸

用一個隊列對二叉樹進行層序遍歷,如果是左子樹的葉子節點,則加入返回變量中

    int sumOfLeftLeaves(TreeNode* root) {
        queue<TreeNode*>q;
        if(root == nullptr){
            return 0;
        }
        q.push(root);
        int ans = 0;
        while(!q.empty()){
            TreeNode* temp = q.front();
            q.pop();
            if(temp->left && temp->left->left == nullptr 
                && temp->left->right == nullptr){

                ans+=temp->left->val;
            }
            if(temp->left){
                q.push(temp->left);
            }
            if(temp->right){
                q.push(temp->right);
            }
        }
        return ans;
    }

2、二叉樹的所有路徑

給定一個二叉樹,返回所有從根節點到葉子節點的路徑

說明: 葉子節點是指沒有子節點的節點

示例,輸入:

    1
   / \
  2   3
   \
    5
輸出: ["1->2->5", "1->3"]

方法一:遞歸

先往左子樹搜,再往右子樹搜,記錄訪問過的節點
停止的條件是到了葉子節點,即該節點沒有孩子節點了,這時候進行插入,然後把它用 -> 分割開來

    def binaryTreePaths(self, root: TreeNode) -> List[str]:
        res = []
        if not root:
            return res
        def dfs(root,path):
            if not root.left and not root.right:
                res.append('->'.join(path+[str(root.val)]))
            if root.left:
                dfs(root.left,path+[str(root.val)])
            if root.right:
                dfs(root.right,path+[str(root.val)])
        dfs(root,[])
        return res

C++ 版本:思路基本一致

    void dfs(TreeNode* root,string str,vector<string>&res){
        if(root->left == NULL && root->right==NULL){
            str += to_string(root->val);
            res.push_back(str);
            return;
        }
        str +=to_string(root->val) + "->";
        if (root->left){
            dfs(root->left,str,res);
        }
        if (root->right){
            dfs(root->right,str,res);
        }
    }
    vector<string> binaryTreePaths(TreeNode* root) {
        vector<string>res;
        if(root == NULL){
            return res;
        }
        string str = "";
        dfs(root,str,res);
        return res;
    }

方法二:非遞歸,利用棧

	vector<string> binaryTreePaths(TreeNode* root) {
        vector<string> ans;
        if(root==NULL) {
        	return ans;
        }
        TreeNode* p=root;
        stack<pair<TreeNode*,string>> s;
        string str;
        while(!s.empty() || p){
            while(p){
                if(p==root){
                	str=str+to_string(p->val);
				}else{
                	str=str+"->"+to_string(p->val);
                }
                s.push(pair<TreeNode*,string>(p,str));
                p=p->left;
            }
            p=s.top().first;
            str=s.top().second;
            s.pop();
            if(p->right==NULL&&p->left==NULL) 
            	ans.push_back(str);
            p=p->right;
        }
        return ans;
    }

非遞歸,利用隊列

    vector<string> binaryTreePaths(TreeNode* root) {
        vector<string>res;
        if(root == nullptr){
            return res;
        }
        queue<pair<TreeNode*,string>>q;
        q.push(pair<TreeNode*,string>\
	        (root,to_string(root->val)));
	        
        while(!q.empty()){
            TreeNode* node =  q.front().first;
            string path = q.front().second;
            q.pop();
            if(node->left == nullptr &&
               node->right == nullptr){
                res.push_back(path);
            }
            if(node->left){
                q.push(pair<TreeNode*,string>\
                	(node->left,path + "->"+to_string\
	                (node->left->val)));
            }
            if(node->right){
                q.push(pair<TreeNode*,string>\
   		        	(node->right,path + "->"+to_string\
                	(node->right->val)));
            }
        }
        return res;
    }

3、翻轉二叉樹

翻轉一棵二叉樹

方法一:遞歸

def invertTree(self, root: TreeNode) -> TreeNode:
	if root:
    	root.left,root.right = self.invertTree(root.right),self.invertTree(root.left)
	return root

C++ 版本

TreeNode* invertTree(TreeNode root) {
	if (root == null) {
    	return null;
	}
    TreeNode* temp = root->left;
    root->left = invertTree(root->right);
    root->right = invertTree(temp);
    return root;
}

方法二 :非遞歸,利用一個隊列進行迭代

    TreeNode* invertTree(TreeNode* root) {
        if(root==nullptr){
            return nullptr;
        }
        queue<TreeNode*>que;
        que.push(root);
        while(!que.empty()){
            TreeNode* cur = que.front();
            que.pop();
            TreeNode* temp = cur->left;
            cur->left = cur->right;
            cur->right = temp;
            if(cur->left){
                que.push(cur->left);
            }
            if(cur->right){
                que.push(cur->right);
            }
        }
        return root;
    }

4、二叉樹的右視圖

給定一棵二叉樹,想象自己站在它的右側,按照從頂部到底部的順序,返回從右側所能看到的節點值

示例
輸入: [1,2,3,null,5,null,4]
輸出: [1, 3, 4]

   1
  / \
 2   3
  \   \
   5   4

思路:其實就是個層次遍歷,然後把每層的最後一個節點附加到返回列表中。

    def rightSideView(self, root: TreeNode) -> List[int]:
        if not root:
            return []
        temp = [root]
        res = []
        while temp:
            res.append(temp[-1].val)
            temp = [kid for node in temp for kid in [node.left,node.right]if kid]
        return res

C++版本

	vector<int> rightSideView(TreeNode* root) {
        vector<int>res;
        if(!root){
            return res;
        }
        queue<TreeNode*>q;
        q.push(root);
        while (!q.empty()){
            int size = q.size();
            res.push_back(q.front()->val);
            while (size--){
                TreeNode* temp = q.front();
                q.pop();
                if (temp->right){
                    q.push(temp->right);
                }
                if (temp->left){
                    q.push(temp->left);
                }
            }
        }
        return res;
    }

5、重建二叉樹

根據前序數列和中序數列構建二叉樹

前序遍歷 preorder = [3,9,20,15,7]
中序遍歷 inorder = [9,3,15,20,7]

輸出

     3 
   /   \ 
  9    20
      /  \
    15    7

思路:前序遍歷的第一個節點是 root 根節點,根據這個根節點在中序遍歷中分成左子樹和右子樹,然後遞歸實現構建二叉樹

	TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
        int end = preorder.size()-1;
        return build(preorder,inorder,0,0,end);
    }
    TreeNode* build(vector<int>& preorder, vector<int>& inorder,\
    			int root,int start,int end)
    {
        if(start>end){
            return nullptr;
        }
        TreeNode* tree = new TreeNode(preorder[root]);
        int i = start;
        while(i<end && preorder[root] != inorder[i]){
            ++i;
        }
        tree->left = build(preorder,inorder,root+1,start,i-1);
        tree->right = build(preorder,inorder,root+1+i-start,i+1,end);
        return tree;
    }

python 寫法

	def buildTree(self, preorder: List[int], inorder: List[int]) -> TreeNode:
        if not preorder:
            return None
        local = inorder.index(preorder[0])
        root = TreeNode(preorder[0])
        root.left = self.buildTree(preorder[1:local+1],inorder[:local])
        root.right = self.buildTree(preorder[local+1:],inorder[local+1:])
        return root

可以對中序列表設置一個 unordered_map 容器,方便bianli

    unordered_map<int, int> mp;
    TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
        for (int i = 0; i < inorder.size(); i++) {
            mp[inorder[i]] = i;
        }
        return dfs(preorder, 0, preorder.size()-1, inorder, 0, inorder.size()-1);
    }
    
    TreeNode* dfs(const vector<int>& preorder, int pl, int pr, const vector<int>& inorder, int il, int ir) {
        if (pl > pr) return NULL;
        TreeNode *root = new TreeNode(preorder[pl]);
        int idx = mp[root->val];
        int cntL = idx - il;
        root->left = dfs(preorder, pl + 1, pl + cntL, inorder, il, idx-1);
        root->right = dfs(preorder, pl + cntL + 1, pr, inorder, idx+1, ir);
        return root;
    }

如果上面代碼不好理解,那這個應該比較簡單點

    TreeNode* reConstructBinaryTree(vector<int> pre,vector<int> vin) {
        if(vin.size()==0){
            return 0;
        }
        // 定義四個數組
        vector<int>pre_left,pre_right,vin_left,vin_right;
        // 只有下面這一行代碼是核心,其餘都是準備工作
        TreeNode* root = new TreeNode(pre[0]);
        int temp = 0;
        for(int i = 0;i < vin.size();++i){
            if(vin[i] == pre[0]){
                temp = i;
                break;
            }
        }
        for(int i = 0;i < temp;++i){
            pre_left.push_back(pre[i+1]);
            vin_left.push_back(vin[i]);
        }
        for(int i = temp+1;i < vin.size();++i){
            pre_right.push_back(pre[i]);
            vin_right.push_back(vin[i]);
        }
        root->left = reConstructBinaryTree(pre_left,vin_left);
        root->right = reConstructBinaryTree(pre_right,vin_right);
        return root;
    }

6、按’之’字形順序打印二叉樹

請實現一個函數按照之字形打印二叉樹,即第一行按照從左到右的順序打印,第二層按照從右至左的順序打印,第三行按照從左到右的順序打印,其他行以此類推

比如:

     1
   /   \
  2     3
 / \   / \
4  5  6   7

首先打印1,然後打印 3,2,然後打印4,5,6,7
規則就是,奇數行從左往右,偶數行從右往左

利用兩個棧進行交換保存,雖然代碼看起來挺多,但還是比較好理解的

	vector<vector<int> > Print(TreeNode* pRoot) {
        vector<vector<int>>res;
        if(pRoot == nullptr){
            return res;
        }
        // 定義兩個棧,分別保存奇數行和偶數行
        stack<TreeNode*>s1;
        stack<TreeNode*>s2;
        vector<int>temp;// 臨時數組
        temp.push_back(pRoot->val);
        res.push_back(temp);
        s1.push(pRoot);
        temp.clear();
        while(!s1.empty() || !s2.empty()){
            while(!s1.empty()){
  				// 遍歷 s1 棧,每遍歷一個彈出一個
                TreeNode* node = s1.top();
                s1.pop();
                if(node->right){
                    s2.push(node->right);
                    temp.push_back(node->right->val);
                }
                if(node->left){
                    s2.push(node->left);
                    temp.push_back(node->left->val);
                }
            }
            // 最後插入完後,要情況一下
            if(!temp.empty()){
                res.push_back(temp);
                temp.clear();
            }
            while(!s2.empty()){
            	// 遍歷 s2 棧,每遍歷一個彈出一個
                TreeNode* cur = s2.top();
                s2.pop();
                if(cur->left){
                    s1.push(cur->left);
                    temp.push_back(cur->left->val);
                }
                if(cur->right){
                    s1.push(cur->right);
                    temp.push_back(cur->right->val);
                }
            }
			// 最後插入完後,要情況一下
            if(!temp.empty()){
                res.push_back(temp);
                temp.clear();
            }
        }
        return res;
    }

方法二:

class Solution {
public:
    vector<vector<int>> levelOrder(TreeNode* root) {
        vector<vector<int>>res;
        if(root == nullptr){
            return res;
        }
        queue<TreeNode*>q;
        q.push(root);
        bool isLeft = false;
        while(!q.empty()){
            int size = q.size();
            vector<int>temp;
            for(int i = 0;i<size;++i){
                TreeNode* node = q.front();
                q.pop();
                temp.push_back(node->val);
                if(node->left){
                    q.push(node->left);
                }
                if(node->right){
                    q.push(node->right);
                }
            }
            isLeft = !isLeft;
            if(!isLeft){
                res.push_back(vector<int>(temp.rbegin(),temp.rend()));
            }else{
                res.push_back(temp);
            }
        }
        return res;
    }
};

7、序列化二叉樹

請實現兩個函數,分別用來序列化和反序列化二叉樹

二叉樹的序列化是指:把一棵二叉樹按照某種遍歷方式的結果以某種格式保存爲字符串,從而使得內存中建立起來的二叉樹可以持久保存。序列化可以基於先序、中序、後序、層序的二叉樹遍歷方式來進行修改,序列化的結果是一個字符串,序列化時通過 某種符號表示空節點(#),以 ! 表示一個結點值的結束(value!)

二叉樹的反序列化是指:根據某種遍歷順序得到的序列化字符串結果str,重構二叉樹

class Solution {
public:
    char* Serialize(TreeNode *root) {    
        buf.clear();
        SerializePre(root);
        int size = buf.size();
        int* res = new int[size];
        for(int i = 0;i < size;++i){
            res[i] = buf[i];
        }
        return (char*)res;
    }
    TreeNode* Deserialize(char *str) {
        int* p = (int*)str;
        return DeserializePre(p);
    }
private:
    // 進行前序遍歷,序列化二叉樹
    void SerializePre(TreeNode *root){
        if(root == nullptr){
            buf.push_back(0xFFFFFFFF);
        }else{
            buf.push_back(root->val);
            SerializePre(root->left);
            SerializePre(root->right);
        }
    }
    // 反序列化二叉樹
    TreeNode* DeserializePre(int* &p){
        if(*p == 0xFFFFFFFF){
            p++;
            return nullptr;
        }
        TreeNode* res = new TreeNode(*p);
        p++;
        res->left = DeserializePre(p);
        res->right = DeserializePre(p);
        return res;
    }
private:
    vector<int>buf;
};

8、 二叉樹搜索樹的第 k 個節點

    TreeNode* res = nullptr;
    int count = 0;
    TreeNode* KthNode(TreeNode* pRoot, int k){
        if(pRoot == nullptr || k < 1){
            return nullptr;
        }
        count = k;
        KthNodeIn(pRoot);
        return res;
    }
    void KthNodeIn(TreeNode* pRoot){
        if(pRoot == nullptr){
            return;
        }
        KthNodeIn(pRoot->left);
        if(--count ==0){
            res = pRoot;
        }
        KthNodeIn(pRoot->right);
    }

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