劍指offer | 面試題33:二叉搜索樹的後序遍歷序列

轉載本文章請標明作者和出處
本文出自《Darwin的程序空間》
本文題目和部分解題思路來源自《劍指offer》第二版

在這裏插入圖片描述

開始行動,你已經成功一半了,獻給正在奮鬥的我們

題目

輸入一個整數數組,判斷該數組是不是某二叉搜索樹的後序遍歷結果。如果是則返回 true,否則返回 false。假設輸入的數組的任意兩個數字都互不相同;

參考以下這顆二叉搜索樹:
在這裏插入圖片描述

  • 示例 1:
    輸入: [1,6,3,2,5]
    輸出: false

  • 示例 2:
    輸入: [1,3,2,6,5]
    輸出: true

解題分析

要做這道題,首先我們得十分了解二叉搜索樹這個數據結構;


二叉查找樹(英語:Binary Search Tree),也稱爲 二叉搜索樹、有序二叉樹(Ordered Binary Tree)或排序二叉樹(Sorted Binary Tree),是指一棵空樹或者具有下列性質的二叉樹:

若任意節點的左子樹不空,則左子樹上所有節點的值均小於它的根節點的值;
若任意節點的右子樹不空,則右子樹上所有節點的值均大於它的根節點的值;
任意節點的左、右子樹也分別爲二叉查找樹;
沒有鍵值相等的節點。
二叉查找樹相比於其他數據結構的優勢在於查找、插入的時間複雜度較低。爲 O(\log n)O(logn)。二叉查找樹是基礎性數據結構,用於構建更爲抽象的數據結構,如集合、多重集、關聯數組等。

二叉查找樹的查找過程和次優二叉樹類似,通常採取二叉鏈表作爲二叉查找樹的存儲結構。中序遍歷二叉查找樹可得到一個關鍵字的有序序列,一個無序序列可以通過構造一棵二叉查找樹變成一個有序序列,構造樹的過程即爲對無序序列進行查找的過程。每次插入的新的結點都是二叉查找樹上新的葉子結點,在進行插入操作時,不必移動其它結點,只需改動某個結點的指針,由空變爲非空即可。搜索、插入、刪除的複雜度等於樹高,期望 O(\log n)O(logn),最壞 O(n)O(n)(數列有序,樹退化成線性表)。

雖然二叉查找樹的最壞效率是 O(n)O(n),但它支持動態查詢,且有很多改進版的二叉查找樹可以使樹高爲 O(\log n)O(logn),從而將最壞效率降至 O(\log n)O(logn),如 AVL 樹、紅黑樹等。

橫線中來源於leetcode


二叉搜索樹,自然是二叉樹的一種,而它最主要的特點就是,要不是一棵空樹,要不這棵樹上所有的節點的右邊的所有節點以及子節點都比這個節點要大,並且這棵樹上所有的節點的左邊的所有節點以及子節點都比這個節點要小;

而二叉樹的後續遍歷,就是先輸出左子樹的內容,再輸出右子樹的內容,最後輸出當前節點;

    public static void aftPrint(TreeNode root) {
        if (Objects.nonNull(root)) {
            aftPrint(root.left);
            aftPrint(root.right);
            System.out.println(root.val);
        }
    }

再結合,左子樹比當前節點大,右子樹比當前節點小來看,後序遍歷的數組,最後輸出的是根節點,並且數組前半部分是左子樹,均比根節點小,後半部分是右子樹,均比根節點大;如題中示例顯示;
在這裏插入圖片描述

對應的後續遍歷結果就是[1,3,2,6,5],5是跟節點,1,3,2是左子樹,均比5小,6是右子樹,比5大;
而[1,6,3,2,5],1比5小,是左子樹,6比5大之後,右有一個3和2比5小,就不符合二叉搜索樹的後續遍歷結果,就返回False;
然後我們對根節點的左右子樹都進行這個操作,一個遞歸的過程,最後判斷是不是二叉搜索樹的後序遍歷序列;

代碼(JAVA、Python3實現)

ps:這裏筆者使用的jdk爲1.8版本、python使用的3.7版本

  • java實現
class Solution {
    public boolean verifyPostorder(int[] postorder) {
        if (Objects.isNull(postorder) || postorder.length == 0) {
            return true;
        }
        return verifyPostorder(postorder,0,postorder.length - 1);
    }

    public boolean verifyPostorder(int[] postorder,int start,int end) {
        if (start > end) {
            return true;
        }
        int j;
        int num = postorder[end];
        for (j = start; j < end; j++) {
            if (postorder[j] > num) {
                break;
            }
        }
        for (int i = j + 1; i < end; i++) {
            if (postorder[i] < num) {
                return false;
            }
        }
        return verifyPostorder(postorder,j,end - 1) && verifyPostorder(postorder,start,j - 1);
    }
}
  • python實現(剛學了三天python,用python寫把練練手,如果有語法不對,或者更好的寫法,一定告訴我)
class Solution:
    def verify_postorder(self,postorder: List[int], left, right) -> bool:
        if left > right:
            return True
        point = left
        while point < right and postorder[point] < postorder[right]:
            point += 1

        point_1 = point + 1
        while point_1 < right:
            if postorder[point_1] < postorder[right]:
                return False
            point_1 += 1

        return self.verify_postorder(postorder, left, point - 1) and self.verify_postorder(postorder, point, right - 1)


    def verifyPostorder(self, postorder: List[int]) -> bool:
        if postorder is None or len(postorder) == 0:
            return True
        return self.verify_postorder(postorder, 0, len(postorder) - 1)


喜歡的朋友可以加我的個人微信,我們一起進步
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章