Leetcode103 二叉樹的鋸齒形層次遍歷 C++,Java,Python

Leetcode103 二叉樹的鋸齒形層次遍歷

來源:力扣(LeetCode)
鏈接:https://leetcode-cn.com/problems/binary-tree-zigzag-level-order-traversal/

博主Githubhttps://github.com/GDUT-Rp/LeetCode

題目:

給定一個二叉樹,返回其節點值的鋸齒形層次遍歷。(即先從左往右,再從右往左進行下一層遍歷,以此類推,層與層之間交替進行)。

例如:
給定二叉樹 [3,9,20,null,null,15,7],

    3
   / \
  9  20
    /  \
   15   7

返回鋸齒形層次遍歷如下:

[
  [3],
  [20,9],
  [15,7]
]

解題思路:

方法一:BFS(廣度優先遍歷)

直觀想法

最直觀的方法是 BFS,逐層遍歷樹。

BFS 在每層的默認順序是從左到右,因此需要調整 BFS 算法以生成鋸齒序列。

最關鍵的是使用雙端隊列遍歷,可以在隊列的任一端插入元素。

如果需要 FIFO (先進先出)的順序,則將新元素添加到隊列尾部,後插入的元素就可以排在後面。如果需要 FILO (先進後出)的順序,則將新元素添加到隊列首部,後插入的元素就可以排在前面。

在這裏插入圖片描述

算法

實現 BFS 的幾種算法。

  • 使用兩層嵌套循環。外層循環迭代樹的層級,內層循環迭代每層上的節點。

  • 也可以使用一層循環實現 BFS。將要訪問的節點添加到隊列中,使用 分隔符(例如:空節點)把不同層的節點分隔開。分隔符表示一層結束和新一層開始。

這裏採用第二種方法。在此算法的基礎上,藉助雙端隊列實現鋸齒形順序。在每一層,使用一個空的雙端隊列保存該層所有的節點。根據每一層的訪問順序,即從左到右或從右到左,決定從雙端隊列的哪一端插入節點。

在這裏插入圖片描述

  • 實現從左到右的遍歷順序(FIFO)。將元素添加到隊列尾部,保證後添加的節點後被訪問。從上圖中可以看出,輸入序列 [1, 2, 3, 4, 5],按照 FIFO 順序得到輸出序列爲 [1, 2, 3, 4, 5]
  • 實現從右到左的遍歷順序(FILO)。將元素添加到隊列頭部,保證後添加的節點先被訪問。輸入序列 [1, 2, 3, 4, 5],按照 FILO 順序得到輸出序列爲 [5, 4, 3, 2, 1]

C++

#include <iostream>
#include <vector>

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    vector<vector<int>> zigzagLevelOrder(TreeNode* root) {
        vector<vector<int>> result;
        if (root == NULL) {
            return result;
        }
        deque<TreeNode*> node_queue;
        node_queue.push_back(root);
        node_queue.push_back(NULL);

        deque<int> level_list;
        bool is_order_left = true;

        while (node_queue.size() > 0) {
            TreeNode* curr_node = node_queue.front();
            node_queue.pop_front();
            if (curr_node != NULL) {
                if (is_order_left) {
                    level_list.push_back(curr_node->val);
                } else {
                    level_list.push_front(curr_node->val);
                }
                if (curr_node->left != NULL) {
                    node_queue.push_back(curr_node->left);
                }
                if (curr_node->right != NULL) {
                    node_queue.push_back(curr_node->right);
                }
            } else {
                vector<int> tmp;
                for (int a:level_list){
                    tmp.push_back(a);
                }
                result.push_back(tmp);
                level_list.clear();
                if (node_queue.size() > 0) {
                    node_queue.push_back(NULL);
                }
                is_order_left = !is_order_left;
            }
        }
        return result;
    }
};

Java

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
  public List<List<Integer>> zigzagLevelOrder(TreeNode root) {
    if (root == null) {
      return new ArrayList<List<Integer>>();
    }

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

    // add the root element with a delimiter to kick off the BFS loop
    LinkedList<TreeNode> node_queue = new LinkedList<TreeNode>();
    node_queue.addLast(root);
    node_queue.addLast(null);

    LinkedList<Integer> level_list = new LinkedList<Integer>();
    boolean is_order_left = true;

    while (node_queue.size() > 0) {
      TreeNode curr_node = node_queue.pollFirst();
      if (curr_node != null) {
        if (is_order_left)
          level_list.addLast(curr_node.val);
        else
          level_list.addFirst(curr_node.val);

        if (curr_node.left != null)
          node_queue.addLast(curr_node.left);
        if (curr_node.right != null)
          node_queue.addLast(curr_node.right);

      } else {
        // we finish the scan of one level
        results.add(level_list);
        level_list = new LinkedList<Integer>();
        // prepare for the next level
        if (node_queue.size() > 0)
          node_queue.addLast(null);
        is_order_left = !is_order_left;
      }
    }
    return results;
  }
}

Python

# -*- coding: utf-8 -*-
# @File   : LeetCode103.py
# @Author : Runpeng Zhang
# @Date   : 2020/3/22
# @Desc   : 二叉樹的鋸齒形層次遍歷


from collections import deque


# Definition for a binary tree node.
class TreeNode:
    def __init__(self, x):
        self.val = x
        self.left = None
        self.right = None


class Solution:
    def zigzagLevelOrder(self, root: TreeNode):
        """
        :type root: TreeNode
        :rtype: List[List[int]]
        """
        ret = []
        level_list = deque()
        if root is None:
            return []
        # start with the level 0 with a delimiter
        node_queue = deque([root, None])
        is_order_left = True

        while len(node_queue) > 0:
            curr_node = node_queue.popleft()

            if curr_node:
                if is_order_left:
                    level_list.append(curr_node.val)
                else:
                    level_list.appendleft(curr_node.val)

                if curr_node.left:
                    node_queue.append(curr_node.left)
                if curr_node.right:
                    node_queue.append(curr_node.right)
            else:
                # we finish one level
                ret.append(list(level_list))
                # add a delimiter to mark the level
                if len(node_queue) > 0:
                    node_queue.append(None)

                # prepare for the next level
                level_list = deque()
                is_order_left = not is_order_left
        return ret

複雜度分析

時間複雜度:O(N)\mathcal{O}(N),其中 N 是樹的結點數,因爲每個結點都訪問一次。
空間複雜度:O(N)\mathcal{O}(N),其中 N 是樹中節點的數量。

除了輸出數組,主要的內存開銷是雙端隊列。

任何時刻,雙端隊列中最多隻存儲兩層節點。因此雙端隊列的大小不超過 2L2 \cdot L,其中 LL 是一層的最大節點數。包含最多節點的層可能是完全二叉樹的葉節點層,大約有 L=N2L=2NL = \frac{N}{2}L= 2N個節點。因此最壞情況下,空間複雜度爲 2N2=N2 \cdot \frac{N}{2} =N

方法二:DFS (深度優先遍歷)

直觀想法

也可以使用 DFS 實現 BFS 的遍歷順序。

在 DFS 遍歷期間,將結果保存在按層數索引的全局數組中。即元素 array[level] 存儲同一層的所有節點。然後在 DFS 的每一步更新全局數組。

在這裏插入圖片描述

與改進的 BFS 算法類似,使用雙端隊列保存同一層的所有節點,並交替插入方向(從首部插入或從尾部插入)得到需要的輸出順序。

算法

使用遞歸實現 DFS 算法。定義一個遞歸方法 DFS(node, level),方法參數爲當前節點 node 和指定層數 level。該方法共執行三個步驟:

  • 如果是第一次訪問該層的節點,即該層的雙端隊列不存在。那麼創建一個雙端隊列,並添加該節點到隊列中。

  • 如果當前層的雙端隊列已存在,根據順序,將當前節點插入隊列頭部或尾部。

  • 最後,爲每個節點調用該遞歸方法。

C++

#include <iostream>
#include <vector>

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    vector<vector<int>> zigzagLevelOrder(TreeNode* root) {
        vector<vector<int>> results;
        if (root == NULL) {
            return results;
        }
        zigzagLevelOrderDFS_helper(root, 0, results);
        return results;
    }

    void zigzagLevelOrderDFS_helper(TreeNode* node, int level, vector<vector<int>> & results) {
        if (level >= results.size()) {
            vector<int> new_level;
            new_level.push_back(node->val);
            results.push_back(new_level);
        } else {
            if (level % 2 == 0) {
                results[level].push_back(node->val);
            } else {
                results[level].insert(results[level].begin(), node->val);
            }
        }
        if (node->left != NULL) {
            zigzagLevelOrderDFS_helper(node->left, level + 1, results);
        }
        if (node->right != NULL) {
            zigzagLevelOrderDFS_helper(node->right, level + 1, results);
        }
    }
};

Java

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
  protected void DFS(TreeNode node, int level, List<List<Integer>> results) {
    if (level >= results.size()) {
      LinkedList<Integer> newLevel = new LinkedList<Integer>();
      newLevel.add(node.val);
      results.add(newLevel);
    } else {
      if (level % 2 == 0)
        results.get(level).add(node.val);
      else
        results.get(level).add(0, node.val);
    }

    if (node.left != null) DFS(node.left, level + 1, results);
    if (node.right != null) DFS(node.right, level + 1, results);
  }

  public List<List<Integer>> zigzagLevelOrder(TreeNode root) {
    if (root == null) {
      return new ArrayList<List<Integer>>();
    }
    List<List<Integer>> results = new ArrayList<List<Integer>>();
    DFS(root, 0, results);
    return results;
  }
}

Python

# -*- coding: utf-8 -*-
# @File   : LeetCode103.py
# @Author : Runpeng Zhang
# @Date   : 2020/3/22
# @Desc   : 二叉樹的鋸齒形層次遍歷


from collections import deque


# Definition for a binary tree node.
class TreeNode:
    def __init__(self, x):
        self.val = x
        self.left = None
        self.right = None


class Solution:
    def zigzagLevelOrder(self, root: TreeNode):
        """
        :type root: TreeNode
        :rtype: List[List[int]]
        """
        if root is None:
            return []

        results = []

        def dfs(node, level):
            if level >= len(results):
                results.append(deque([node.val]))
            else:
                if level % 2 == 0:
                    results[level].append(node.val)
                else:
                    results[level].appendleft(node.val)

            for next_node in [node.left, node.right]:
                if next_node is not None:
                    dfs(next_node, level + 1)

        # normal level order traversal with DFS
        dfs(root, 0)
        for i, enum in enumerate(results):
            results[i] = list(results[i])
        return results

算法複雜度:

時間複雜度:訪問每個節點恰好一次,時間複雜度爲 O(N)O(N) ,其中 NN 是節點的個數,也就是樹的大小。
空間複雜度:取決於樹的結構,最壞情況存儲整棵樹,因此空間複雜度是 O(N)O(N)

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