從二叉樹的前中後序遍歷,咱們來講遞歸和快速排序

我是很喜歡算法的,打算寫一個數據結構與算法系列的記錄,不少都用到了遞歸,因此這星期打算說說你們都以爲很簡單,可是我以爲並不簡單的遞歸,畢竟不少人對遞歸的認識也就是本身調用本身,可是我以爲這並不深刻,或者說這並非遞歸的實質,或者說解釋遞歸解釋的並很差。

二叉樹

二叉樹的定義

首先咱們準備一顆二叉樹:算法

咱們須要定義一些術語,咱們所使用的數據結構由結點組成,結點包含的連接能夠爲空也能夠指向其餘結點.
在二叉樹中,每一個結點只能有一個父節點(只有一個例外,也就是根節點,它沒有父節點),並且每一個結點只有兩個連接,
   分別指向本身的左子結點和右子結點.儘管連接指向的是結點,但咱們能夠將每一個連接看作指向了另外一顆二叉樹,
   而這棵樹的根節點就是被指向的節點.所以,咱們能夠將二叉樹定義爲一個空連接
   ,或者是一個有左右兩個連接的結點,每一個連接都指向一顆(獨立的)二叉樹。----《算法》第四版。

前中後序遍歷(這是本篇文章的要討論的核心問題之一,由前中後序遍從來討論遞歸,而後再到荷蘭國旗問題和快速排序)
結點代碼:數組

public class TreeNode {
    
         int data;
         TreeNode leftNode;
         TreeNode rightNode;
    
        public TreeNode(int data) {
            this.data = data;
        }
    
    }

二叉樹:數據結構

public class BinaryTree {

    TreeNode root;

    public BinaryTree(TreeNode root) {
        this.root = root;
    }

    /**
     * 前序遍歷
     */
    public void frontShow(){
        
    }

    /**
     * 中序遍歷
     */
    public void middleShow(){

    }
    /**
     * 後序遍歷
     */
    public void afterShow(){


    }
}

雖然百度不招人喜歡,可是百度百科的一些詞條編輯的仍是不錯的.
從二叉樹的遞歸定義可知,一棵非空的二叉樹由根結點及左、右子樹這三個基本部分組成。所以,在任一給定結點上,能夠按某種次序執行三個操做:less

(1) 訪問結點自己(N),
(2) 遍歷該結點的左子樹(L),
(3) 遍歷該結點的右子樹(R)。函數

前序遍歷: 根節點 左子樹 右子樹
中序遍歷: 左子樹 根節點 右子樹
後序遍歷: 左子樹 右子樹 根節點工具

其實組合的話是有六種的,可是討論遞歸的話,前中後序就足夠了.this

咱們如今來任意的給一顆二叉樹,不用代碼。咱們來寫出前中後序遍歷的結果:spa

clipboard.png

前序遍歷: 3 8 5 2 6 1 7
中序遍歷: 5 8 2 3 1 6 7
後序遍歷: 5 2 8 1 7 6 3翻譯

前中後序遍歷的進一步解釋:3d

當我到達一個結點,先打印出來,再去訪問其餘子結點就是前序遍歷
當我到達一個結點,不是先打印當前結點,而是接着訪問該節點的左子節點,某個節點沒有子節點(或者說子結點是null)
我就打印當前節點,這就是中序遍歷

之前序遍歷爲例,我首先打印根節點,而後判斷它的左子節點是否爲空,若是非空,打印該節點,而後接着進行這樣的操做,
翻譯成代碼就是這樣的:

在BinaryTree中的代碼:

public void frontShow(){
    System.out.println(root.data);
    if (root.leftNode != null){
        root.leftNode.frontShow();
    }
    if (root.rightNode != null){
        root.rightNode.frontShow();
    }
}

結點中的代碼也是這樣的,

public void frontShow(){
        System.out.println(root.data);
        if (root.leftNode != null){
            root.leftNode.frontShow();
        }
        if (root.rightNode != null){
            root.rightNode.frontShow();
        }
}

中後序代碼把打印順序調整一下就能夠了,其實這個很好理解,不是多麼酷炫的事情.
如下一個問題在我我的看來纔是值得值得思考的,就是程序在遍歷二叉樹的過程,每一個節點通過了幾回。
其實這個問題,也是十分簡單,你按着程序走就能夠了.

clipboard.png

首先來到3,接着來到8,接着來到5,而後發現5的左右子節點都是空的,而後回到....

這裏再介紹另外一種在形式上略有不一樣的前序遍歷方式:

public void frontShow(TreeNode root){
        if (root == null){
            return;
        }
        System.out.println(data);
        frontShow(root.leftNode);
        frontShow(root.rightNode);
    }

這種形式上的前中後序遍歷:

每一個節點會到達三次,前序遍歷就是第一次碰到的時候打印出來,中序是第二次,後序是第三次
各位有興致的話能夠本身寫一寫,畫一畫.

請注意上面那張圖,雖然他十分的醜,可是你們不以爲它像一個棧嗎? 遞歸就是程序在幫你壓棧.

非遞歸版

那既然遞歸是系統在幫你壓棧,那非遞歸版我就本身壓棧,不讓程序幫咱們壓棧就能夠了嗎?
咱們先用下圖來演示非遞歸版的遍歷:

clipboard.png

從荷蘭國旗問題到快速排序

許多時候,問題的規模是會影響咱們的判斷,爲了解決問題,咱們能夠先把問題的規模先下降到看起來容易解決的地步,再試着去解決問題,若是解決了, 咱們再逐步的擴大問題的規模

荷蘭國旗問題

荷蘭國旗問題: 給定一個數組arr和一個數num,請把小於num的數放在num的左邊,大於num的數放在num右邊。
等於num的數放在中間

例子: 輸入: arr [5,7,5,8,1,9,10] ,num =5

輸出:   [1, 5, 5, 8, 9, 10, 7]]

思路:

> 準備一個變量less和more。
             1. less 區域內(即數組下標 <= less)所有是小於num的,         
             2. more區域內(即數組下標>more)所有是大於num
             3. 咱們須要一個變量來幫助咱們遍歷數組。
             在一開始的時候,less  = -1 , more = 數組的長度。 這表明剛開始這兩個區域還不存在
             當arr[curr] < num 的時候, less區域的下一個數和arr[curr]交換。而後less右移一個位置,
             curr右移一個位置
             當arr[curr] > num 的時候, more區域的上一個數和arr[curr]交換。而後more左移一個位置,
             此時咱們是沒法保證從more區域的上一個數,到底是大於num仍是小於num,
             所以咱們仍然須要將num和這個數進行比較.
             以上的思路翻譯成代碼是用循環來完成的,那麼循環結束的條件是什麼呢?
             curr的左側是less區域,  當排序完成的時候, (less,curr]區域爲等於num的區域,
             可是當curr + 1 = more 時,這個時候arr[curr+1]仍是未進行判斷的,
             咱們仍然須要對arr[curr+1] 和num進行比較

clipboard.png

快速排序

那這跟快速排序有什麼問題呢?咱們來思考一下快速排序(這裏討論是基礎版的快速排序)。快速排序是一種分治的排序算法,它將一個數組分紅兩個子數組,將兩部分獨立的排序。荷蘭國旗就至關於快速排序中的切分過程。快速排序的關鍵就在於這個切分過程。

快速排序的思想就是:

首先根據num,將數組切分紅兩個子數組,而後再對這兩個子數組進行切分,當子數組的數量小於2,默認數組即爲有序。
理解這個切分過程對快速排序來講十分的重要

如何證實你的算法的正確性

這也是我在寫算法的時候考慮的一個問題,

答案有兩種:
1. 嚴謹一點的話就是數學建模,經過數學來證實你算法的正確性。
2. 利用計算機這個工具來印證咱們算法的正確性,意思就是試,找到一個雖然是正確的,可是時間複雜度不那麼好的正確算法。
經過隨機函數產生大量的樣本,比較你的算法和時間複雜度不那麼好的正確算法所產生的結果。這種方法

好比說對於排序算法,
首先利用隨機函數不斷的產生各類各樣的數組,而且向數組中填充隨機數.

clipboard.png

而後將數組複製一份,給正確的算法用

clipboard.png

而後比較兩個算法的結果.

clipboard.png

相關文章
相關標籤/搜索