1. 程式人生 > >學習筆記之使用前序遍歷和中序遍歷構造二叉樹

學習筆記之使用前序遍歷和中序遍歷構造二叉樹

總結一下學習筆記:

一、提出問題

給出一棵樹的前序遍歷和中序遍歷,構造二叉樹,你可以假設這棵樹中不存在重複的數。

例如:給出前序和中序遍歷序列:preorder = [3,9,20,15,7],inorder=[9,3,15,20,7] 得到如下所示的二叉樹: 在這裡插入圖片描述

二、解決方法

如何去遍歷這棵樹? 通常有兩種方式去遍歷,即Breadth First Search(BFS):廣度優先搜尋、Depth First Search(DFS):深度優先搜尋。

三、概念區分

在解釋BFS和DFS概念之前,先來區分一下圖和樹的區別。 1、什麼是圖? 答: 圖由頂點(vertex,node)和邊(edge)組成。頂點就是代表物件,因為有時候題不會直接給頂點,而是某種物件,直接看成頂點即可。我們使用連結兩頂點之間的線段來表示。頂點的集合V,邊的集合是E,所以圖記為G = (V,E), 連線兩點u和v的邊用e=(u,v)表示。

在這裡插入圖片描述

2、什麼是樹? 答:樹也是由一組頂點(vertex)以及若干條邊(edge)組成。類似於一般的圖,區別在於:樹是連通的無環圖。 在這裡插入圖片描述

3、圖的遍歷和樹的遍歷 圖的遍歷:包括廣度優先遍歷(BFS)、深度優先遍歷DFS。 樹的遍歷:包括前序、中序、後序、層次遍歷等。

圖的遍歷:從圖的某一點出發訪問其餘頂點,且使得每一個頂點僅僅被訪問一次,這過程叫圖的遍歷。

樹的遍歷: 1)、前序、中序、後序遍歷,其實都採用的是DFS思想,DFS思想常用遞迴實現。 2)、層次遍歷,採用的是BFS思想,採用佇列這種資料結構實現。

四、演算法思想

1、BFS演算法思想 類似於樹的層次遍歷,遍歷順序從樹的頂端一直遍歷到底部,頂部的節點比底部的節點要優先被訪問到。

2、DFS演算法思想 類似於樹的前、中、後序遍歷,這個策略中,我們採用深度優先作為優先順序,即沿著根節點一直遍歷到某個葉節點,然後再返回根節點,訪問其他的分支。根據根節點、左節點、右節點的順序,DFS可以分為前序、中序、後序遍歷。

下面四幅圖表示了DFS和BFS的訪問順序。 從左到右依次解釋: 下圖一:DFS的後序遍歷,節點訪問順序(L—>R—>V),即先訪問左孩子,再訪問右孩子、最後訪問父節點,訪問順序:(1,2,3,4,5)。 下圖二:DFS的前序遍歷,節點訪問順序(V—>L—>R),即先訪問父節點,再訪問左孩子,最後訪問右孩子,訪問順序(1,2,3,4,5)。 下圖三:DFS的中序遍歷,節點訪問順序(L—>V—>R),即先訪問左孩子,再訪問父節點,最後訪問右孩子,訪問順序(1,2,3,4,5)。 下圖四:BFS的層次遍歷,訪問順序:從左到右,從上到下依次訪問,訪問順序(1,2,3,4,5)。

在這裡插入圖片描述 下面介紹由前序遍歷和中序遍歷構造一棵二叉樹的實現方式。

五、程式碼實現

1、動畫展示實現思想: 在這裡插入圖片描述

2、程式碼實現: 1)、Java版本

// Definition for a binary tree node.
public class TreeNode {
  int val;
  TreeNode left;
  TreeNode right;

  TreeNode(int x) {
    val = x;
  }
}

import javafx.util.Pair;
class Solution {
  public Pair<TreeNode, int []> helper(int[] preorder, int[] inorder) {
    if (inorder.length == 0) {
      return new Pair(null, preorder);
    }

    // pick up the first element as a root
    int root_val = preorder[0];
    TreeNode root = new TreeNode(root_val);

    // find index of root in the inorder list
    int index = 0;
    for (; (index < inorder.length) && (inorder[index] != root_val); index++){}
    preorder = Arrays.copyOfRange(preorder, 1, preorder.length);

    // root splits inorder list
    // into left and right subtrees        
    int [] left_inorder = Arrays.copyOfRange(inorder, 0, index);
    int [] right_inorder = index + 1 <= inorder.length ? 
      Arrays.copyOfRange(inorder, index + 1, inorder.length) : new int [0];

    // recursion
    Pair<TreeNode, int []> p = helper(preorder, left_inorder);
    root.left = p.getKey();
    preorder = p.getValue();
    p = helper(preorder, right_inorder);
    root.right = p.getKey();
    preorder = p.getValue();

    return new Pair(root, preorder);
  }

  public TreeNode buildTree(int[] preorder, int[] inorder) {
    Pair<TreeNode, int []> result = helper(preorder, inorder);
    return result.getKey();
  }
}

2)、Python版本

# Definition for a binary tree node.
class TreeNode:
    def __init__(self, x):
        self.val = x
        self.left = None
        self.right = None
        
from collections import deque
class Solution:
    def buildTree(self, preorder, inorder):
        """
        :type preorder: List[int]
        :type inorder: List[int]
        :rtype: TreeNode
        """
        def helper(preorder, inorder):
            if not inorder:
                return None
            
            # pick up the first element as a root
            root_val = preorder.popleft()
            root = TreeNode(root_val)

            # root splits inorder list
            # into left and right subtrees
            index = inorder.index(root_val)

            # recursion 
            root.left= helper(preorder, inorder[:index])
            root.right = helper(preorder, inorder[index + 1:])
            return root
        
        return helper(deque(preorder), inorder)

3)、C++實現版本

#include "stdafx.h"
#include<vector>
#include<iostream>
#include<string>
using namespace std;
struct TreeNode
{
	int val;
	TreeNode *left;
	TreeNode *right;
	TreeNode(int x) :val(x), left(NULL), right(NULL) {}
};
class Solution
{
	TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder)
	{
		return builTreeHelper(preorder, 0, preorder.size(), inorder, 0, inorder.size());
	}

	TreeNode* builTreeHelper(vector<int>& preorder, int sp, int ep, vector<int>& inorder, int si, int ei) {
		if (sp == ep) return nullptr;
		TreeNode* root = new TreeNode(preorder[sp]);
		int dis = find(inorder.begin() + si, inorder.begin() + ei, preorder[sp]) - inorder.begin() - si;
		root->left = builTreeHelper(preorder, sp + 1, sp + 1 + dis, inorder, si, si + dis);
		root->right = builTreeHelper(preorder, sp + 1 + dis, ep, inorder, si + dis + 1, ei);
		return root;
	}
};

int main()
{
  //可自行新增測試程式碼
	return 0;
}

六、演算法複雜度分析

1、時間複雜度:O(N) 2、空間複雜度:O(N)