天天看點

二叉樹的四種周遊方式

前言

二叉樹作為一種重要的資料結構,在算法中起到了承前啟後的作用,它是數組和連結清單的延伸,也是圖的基礎。是以學習二叉樹的相關知識是十分有必要的,而在相關的操作中,二叉樹的周遊是最頻繁的,今天就來看看二叉樹的 4 種周遊方法!

二叉樹資料結構

所謂二叉樹,指的是每個結點最多有兩個分支的樹結構,其分支通常被稱為“左子樹”和“右子樹”,而且他們的次序是固定的,不能随意颠倒,一棵二叉樹的示例如下:

二叉樹的四種周遊方式

class TreeNode{

   int val;

   // 左子樹

   TreeNode left;

   // 右子樹

   TreeNode right;

}

前序周遊

也叫做先序周遊,首先通路根節點,然後周遊左子樹,最後再周遊右子樹。而在周遊左右子樹時,仍然按照先通路根節點,然後周遊左子樹,最後周遊右子樹的方式,直到二叉樹為空則傳回!

周遊的方式又主要分為遞歸和疊代的方式,其具體實作如下所示。

遞歸

public ArrayList<Integer> preOrderReverse(TreeNode root){

   ArrayList<Integer> list = new ArrayList<>();

   preOrder(root, list);

   return list;

public void preOrder(TreeNode root, ArrayList<Integer> list){

   if(root != null){

       list.add(root.val);    

       preOrder(root.left, list);

       preOrder(root.right, list);

   }

疊代

/**

* 用棧來進行疊代,由于棧是一種 先進後出 的資料結構,要輸出的順序是 中、左、右

* 是以我們優先将根節點加入 stack 後,然後先加入右子樹,再加入左子樹

*/

public ArrayList<Integer> preOrderReverse(TreeNode root){

   // 棧,先進後出

   Stack<TreeNode> stack = new Stack<>();

       // 入棧

       stack.push(root);

       while(!stack.empty()){

           // 出棧

           TreeNode node = stack.pop();

           list.add(node.val);

           // 棧是一種先進後出的資料結構,是以先入右子樹,再入左子樹

           if(node.right != null){

               stack.push(node.right);

           }

           if(node.left != null){

               stack.push(node.left);

       }

中序周遊

首先周遊左子樹,然後通路根節點,最後再周遊右子樹。而在周遊左右子樹時,仍然按照先周遊左子樹,然後通路根節點,最後周遊右子樹的方式,直到二叉樹為空則傳回!

public ArrayList<Integer> inOrderReverse(TreeNode root){

   inOrder(root, list);

public void inOrder(TreeNode root, ArrayList<Integer> list){

   if(root != null){    

       inOrder(root.left, list);

       list.add(root.val);

       inOrder(root.right, list);

* 中序周遊,按照 左、中、右 的順序列印

* 是以優先将左子樹壓入棧中,接着進行中間節點,最後處理右子樹

*

public ArrayList<Integer> inOrderReverse(TreeNode root){

   Stack<TreeNode> stack = new Stack<TreeNode>();

   TreeNode curr = root;

   while(curr != null || !stack.isEmpty()){

       // 節點不為空就一直壓棧

       while(curr != null){

           stack.push(curr);

           // 考慮左子樹

           curr = curr.left;      

       // 節點為空,出棧

       curr = stack.pop();

       // 加入目前值

       list.add(curr.val);

       // 考慮右子樹

       curr = curr.right;

後序周遊

後序周遊首先周遊左子樹,然後周遊右子樹,最後通路根結點,在周遊左、右子樹時,仍然先周遊左子樹,然後周遊右子樹,最後周遊根結點,直到二叉樹為空則傳回!

public ArrayList<Integer> postOrderReverse(TreeNode root){

   postOrder(root, list);

public void postOrder(TreeNode root, ArrayList<Integer> list){

       postOrder(root.left, list);

       postOrder(root.right, list);

   List<Integer> list = new ArrayList<Integer>();

   TreeNode current = root;

   // 用來區分之前的結點是否被通路過

   TreeNode last = null;  

   while(current != null || !stack.isEmpty()){

       // 到樹的最左面

       if(current != null){    

           stack.push(current);

           current = current.left;

       }else{

           //看最左結點有沒有右子樹

           current = stack.peek();    

           if(current.right != null && current.right != last){

               current = current.right;

               stack.push(current);

               //右子樹再到最左

               current = current.left;    

           }else{

               //通路該結點,并标記被通路

               current = stack.pop();    

               list.add(current.val);

               last = current;

               current = null;

層次周遊

層次周遊也叫做廣度優先周遊,它會優先通路離根節點最近的節點,其實作一般借助隊列實作。

public List<List<Integer>> levelOrder(TreeNode root) {

   List<List<Integer>> lists = new ArrayList<List<Integer>>();

   if(root != null){            

       // 根節點不為 null,遞歸

       dfs(1, root, lists);

   return lists;

// index : 層數

public void dfs(int index, TreeNode root, List<List<Integer>> lists){

   // 若 lists 中序列數小于層數,則将 lists 中加入一個空的序列

   if(lists.size() < index){

       lists.add(new ArrayList<Integer>());

   // 然後将目前節點加入 lists 的子序列中

   lists.get(index - 1).add(root.val);

   // 以上就處理完 root 節點

   // 接着處理左右子樹即可,處理時,層數到下一次,是以要 +1

   if(root.left != null){

       dfs(index + 1, root.left, lists);

   if(root.right != null){

       dfs(index + 1, root.right, lists);

ArrayList<ArrayList<Integer>> levelOrder(TreeNode root){

   List<List<Integer>> res = new ArrayList<>();

   Queue<TreeNode> queue = new ArrayDeque<>();

   if (root != null) {

       queue.add(root);

   while (!queue.isEmpty()) {

       // 擷取目前隊列的長度,這個長度相當于目前這一層的節點個數

       int n = queue.size();

       // 将隊列中的元素都拿出來(也就是擷取這一層的節點),放到臨時list中

       // 如果節點的左/右子樹不為空,也放入隊列中

       List<Integer> level = new ArrayList<>();

       int i = 0;

       while(i < n){

           TreeNode node = queue.poll();

           level.add(node.val);

           if (node.left != null) {

               queue.add(node.left);

           if (node.right != null) {

               queue.add(node.right);

           i++;

       // 将臨時list加入最終傳回結果中

       res.add(level);

   return res;

總結

以上就是資料結構二叉樹的 4 種周遊,如果你有更多關于各種周遊的實作,歡迎留言交流呀!

繼續閱讀