1. 程式人生 > >從陣列形式建立一棵樹(用於leetcode測試)

從陣列形式建立一棵樹(用於leetcode測試)

這段時間時不時地會在leetcode上做些題,最近做到的大部分是與樹相關的題。由於是在本地的IDE上碼程式碼,如果每次測試都要到leetcode上來測的話,不僅要選中複製貼上一遍,而且每次測試還要讀一會條,一次兩次還好,次數多了還是比較煩。而如果在本地的類內的main()函式裡測試的話,每次構建一個樹也比較麻煩,而且僅僅更換數值還好,若要更換樹的結構,那工程量就有點大了。

所以在這裡準備寫一個建立樹的函式來解決這個問題,後面做起題來效率也能高一些。

話不多說,下面先直接放上所用的程式碼:

本例中所構建的樹的結構
      5
     / \
    4   8
   /   / \
  11  13  4
 /  \      \
7    2      1
package learning_java;

import LeetCode.TreeNode;
import java.util.Deque;
import java.util.LinkedList;

public class ConstructTree {
    public static TreeNode constructTree(Integer[] nums){
        if (nums.length == 0) return new TreeNode(0);
        Deque<TreeNode> nodeQueue = new LinkedList
<>(); // 建立一個根節點 TreeNode root = new TreeNode(nums[0]); nodeQueue.offer(root); TreeNode cur; // 記錄當前行節點的數量(注意不一定是2的冪,而是上一行中非空節點的數量乘2) int lineNodeNum = 2; // 記錄當前行中數字在陣列中的開始位置 int startIndex = 1; // 記錄陣列中剩餘的元素的數量 int restLength =
nums.length - 1; while(restLength > 0) { // 只有最後一行可以不滿,其餘行必須是滿的 // // 若輸入的陣列的數量是錯誤的,直接跳出程式 // if (restLength < lineNodeNum) { // System.out.println("Wrong Input!"); // return new TreeNode(0); // } for (int i = startIndex; i < startIndex + lineNodeNum; i = i + 2) { // 說明已經將nums中的數字用完,此時應停止遍歷,並可以直接返回root if (i == nums.length) return root; cur = nodeQueue.poll(); if (nums[i] != null) { cur.left = new TreeNode(nums[i]); nodeQueue.offer(cur.left); } // 同上,說明已經將nums中的數字用完,此時應停止遍歷,並可以直接返回root if (i + 1 == nums.length) return root; if (nums[i + 1] != null) { cur.right = new TreeNode(nums[i + 1]); nodeQueue.offer(cur.right); } } startIndex += lineNodeNum; restLength -= lineNodeNum; lineNodeNum = nodeQueue.size() * 2; } return root; } public static void main(String[] args) { Integer[] nums = {5,4,8,11,null,13,4,7,2,null,null,null,1}; TreeNode root = ConstructTree.constructTree(nums); System.out.println(root); } }

使用時,像上面main()方法中一樣,只需要呼叫類內的靜態方法constructTree(Integer[] nums),輸入的參量為一個整型的陣列,陣列中的元素是按層次遍歷的二叉樹的值(若某節點在下一層中的某個兒子或兩個兒子為空,則在下一層的這一個或兩個位置填null),與Leetcode中樹的表示方法相同,即可以直接把Leetcode上的測試用例按它的形式拖過來直接使用。

簡單解釋一下所用的方法,核心思想是在每一層,用一個佇列nodeQueue來儲存該層的所有節點,然後用父節點的數量的兩倍來遍歷輸入的陣列(從上一層結束的地方開始),並從佇列中取出(位於上一層的)對應的父節點(此時已從佇列中刪去,因為用的方法為poll()而不是peek()),對於每一個值,建立相應的子節點連結到父節點,並加入到佇列中,依次不斷迴圈,直到遍歷完整個陣列。

這裡一個踩過的坑是,其中因為一部分數值為null,而如果用int基本型別的陣列的話,陣列內是不能用null的,因此這裡用了int的包裝類Integer的陣列來作為傳入的引數的宣告。

最後,讓我們測試一下我們的程式碼,測試用的樹與上面一樣,測試中我們採用先序遍歷來進行輸出,檢視結果是否正確:

/*

用於測試的樹,與上例中相同
      5
     / \
    4   8
   /   / \
  11  13  4
 /  \      \
7    2      1

 */
package learning_java.sortTry;

import LeetCode.TreeNode;
import learning_java.ConstructTree;

public class ConstructTreeTest {
    public void preOrder(TreeNode root) {
        if (root == null) return;
        System.out.print(root.val + " ");
        preOrder(root.left);
        preOrder(root.right);
    }

    public static void main(String[] args) {
        Integer[] nums = {5,4,8,11,null,13,4,7,2,null,null,null,1};
        TreeNode root = ConstructTree.constructTree(nums);
        new ConstructTreeTest().preOrder(root);
    }
}

測試結果:

5 4 11 7 2 8 13 4 1

可以看到,我們得到了一棵所需要的樹。

更新

在實際使用中發現,leetcode中所給的case經常並不是標準的個數,最後一行往往是不滿的,如

      5
     / 
    4   

這樣一棵樹,給出的陣列中僅有兩個數字:[5, 4],即最後一行中的末尾的null會被捨棄,因此,在我們的程式中,應在遍歷過程中加入停止條件(更嚴謹的方式是儲存判定錯誤輸入的條件,並僅在樹的最後一行的遍歷過程中進行停止判定)。