中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

java中的二叉樹是什么

發布時間:2020-06-28 19:11:02 來源:億速云 閱讀:174 作者:元一 欄目:編程語言

java中的二叉樹是什么?很多新手對此不是很清楚,為了幫助大家解決這個難題,下面小編將為大家詳細講解,有這方面需求的人可以來學習下,希望你能有所收獲。

定義

樹是一種重要的非線性數據結構,直觀地看,它是數據元素(在樹中稱為結點)按分支關系組織起來的結構,很象自然界中的樹那樣。樹結構在客觀世界中廣泛存在,如人類社會的族譜和各種社會組織機構都可用樹形象表示。樹在計算機領域中也得到廣泛應用,如在編譯源程序如下時,可用樹表示源源程序如下的語法結構。又如在數據庫系統中,樹型結構也是信息的重要組織形式之一。一切具有層次關系的問題都可用樹來描述。滿二叉樹,完全二叉樹,排序二叉樹。

深度優先遍歷

一般我們深度優先遍歷二叉樹有三種最常見的順序遍歷:前序、中序、后序。

前序的遍歷順序為:訪問根結點 -> 遍歷左子樹 -> 遍歷右子樹

中序的遍歷順序為:遍歷左子樹 -> 訪問根結點 -> 遍歷右子樹

后序的遍歷順序為:遍歷左子樹 -> 遍歷右子樹 -> 訪問根結點

注意這里的左右是整個子樹,而不是一個結點,因為我們需要遍歷整棵樹,所以每次遍歷都是按照這個順序去執行,直到葉子結點。

舉個例子,假如有如下二叉樹:

java中的二叉樹是什么

前序遍歷得到的序列就是 A - B - C - D - E

中序遍歷得到的序列就是 B - A - D - C - E

后序遍歷得到的序列就是 B - D - E - C - A

思路我們就用前序遍歷來講(非常不建議去人肉遞歸,至少我的腦子吃不消三層。。。):

第一層遞歸:

先訪問根結點,所以輸出根結點 A,然后遍歷左子樹(L1),再遍歷右子樹(R1);

第二層遞歸:

對于 L1,先訪問根結點,所以輸出此時的根結點 B,然后發現 B 的左右子樹為空,結束遞歸;

對于 R1,先訪問根結點,所以輸出此時的根結點 C,然后遍歷左子樹(L2),再遍歷右子樹(R2);

第三層遞歸:

對于 L2,先訪問根結點,所以輸出此時的根結點 D,然后發現 D 的左右子樹為空,結束遞歸;

對于 R2,先訪問根結點,所以輸出此時的根結點 E,然后發現 E 的左右子樹為空,結束遞歸;

前中后序特征

根據前中后序的定義,其實我們不難發現有如下特征:

? 前序的第一個一定是 root 節點,后序的最后一個一定是 root 節點

? 每種排序的左子樹和右子樹分布都是有規律的

? 對于每一個子樹都遵循上面兩個規律的樹

java中的二叉樹是什么

這些特征也就是定義中對順序的表現。

各種推導

這邊列舉一下對于二叉樹的遍歷最基本的幾個算法題:

? 給定二叉樹得出其前/中/后序遍歷的序列;

? 根據前序和中序推導后序(或者推導整顆二叉樹);

? 根據后序和中序推導前序(或者推導整顆二叉樹);

對于二叉樹的遍歷,前面也講過,通常采用遞歸來做,對于遞歸,有模版可以直接套用:

public void recur(int level, int param) {
    
    // terminator
    if (level > MAX_LEVEL) {
        // process result
         return;   
    }
    
    // process current logic
    process(level, param);
    
    // drill down
    recur(level+1, newParam);
    
    // restore current status
}

這個是我這兩天看極客時間的算法訓練營中超哥(覃超)講到的比較實用的小技巧(這個模版對于新手特別好),遵循上面的三步驟(如果有局部變量需要釋放或者額外處理則第四步去做)能比較有條理的寫出遞歸代碼。

這里拿根據前序和中序推導后序來舉例:

先初始化兩個序列:

int[] preSequence = {1, 2, 3, 4, 5, 6, 7, 8, 9};
int[] inSequence = {2, 3, 1, 6, 7, 8, 5, 9, 4};

通過上面說到的幾個特征,我們已經可以找到最小重復子問題了,每次遞歸

根據前序的第一個結點值去匹配中序中該結點值所在的索引 i,這樣我們就能得到索引 i 的前后兩部份分別對應左右子樹,接著分別去遍歷這兩個左右子樹,然后輸出當前前序的第一個結點值,也就是根結點。

根據自頂向下的程序設計方法,我們可以先寫出如下初始遞歸調用:

List<Integer> result = new ArrayList<>();
preAndInToPost(0, 0, preSequence.length, preSequence, inSequence, result);

第一個參數表示前序序列的第一個元素索引;

第二個參數表示中序序列的第一個元素索引;

第三個參數表示序列長度;

第四個參數表示前序序列;

第五個參數表示后序序列;

第六個參數用于保存結果;

先來考慮終止條件是什么,也就是什么時候結束遞歸,當我們的根結點為空的時候終止,對應這里就是序列長度為零的時候。

if (length == 0) {
    return;
}

接著考慮處理邏輯,也就是找到索引 i:

int i = 0;
while (inSequence[inIndex + i] != preSequence[preIndex]) {
    i++;
}

然后開始向下遞歸:

preAndInToPost(preIndex + 1, inIndex, i, preSequence, inSequence, result);
preAndInToPost(preIndex + i + 1, inIndex + i + 1, length - i - 1, preSequence, inSequence, result);
result.add(preSequence[preIndex]);

因為推導的是后序序列,所以順序如上,添加根結點的操作是在最后的。前三個參數如何得出來的呢,我們走一下第一次遍歷就可以得出來。

前序序列的第一個結點 1 在中序序列中的索引為 2,此時

左子樹的中序系列起始索引為總序列的第 1 個索引,長度為 2;

左子樹的前序序列起始索引為總序列的第 2 個索引,長度為 2;

右子樹的中序系列起始索引為總序列的第 3 個索引,長度為 length - 3;

右子樹的前序序列起始索引為總序列的第 3 個索引,長度為 length - 3;

完整代碼如下:

/**
 * 根據前序和中序推導后序
 *
 * @param preIndex    前序索引
 * @param inIndex     中序索引
 * @param length      序列長度
 * @param preSequence 前序序列
 * @param inSequence  中序序列
 * @param result      結果序列
 */
private void preAndInToPost(int preIndex, int inIndex, int length, int[] preSequence, int[] inSequence, List<Integer> result) {
    if (length == 0) {
        return;
    }

    int i = 0;
    while (inSequence[inIndex + i] != preSequence[preIndex]) {
        i++;
    }

    preAndInToPost(preIndex + 1, inIndex, i, preSequence, inSequence, result);
    preAndInToPost(preIndex + i + 1, inIndex + i + 1, length - i - 1, preSequence, inSequence, result);
    result.add(preSequence[preIndex]);
}

看完上述內容是否對您有幫助呢?如果還想對相關知識有進一步的了解或閱讀更多相關文章,請關注億速云行業資訊頻道,感謝您對億速云的支持。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

陈巴尔虎旗| 竹山县| 西城区| 睢宁县| 青阳县| 海城市| 宁城县| 宁强县| 通河县| 利川市| 乌恰县| 惠东县| 榆林市| 三门县| 九寨沟县| 南开区| 开封县| 克拉玛依市| 石台县| 京山县| 剑阁县| 铜川市| 凤庆县| 聂荣县| 工布江达县| 青铜峡市| 哈巴河县| 治多县| 腾冲县| 平武县| 彭州市| 利津县| 永和县| 齐齐哈尔市| 故城县| 冕宁县| 扶余县| 太原市| 云安县| 花莲市| 高密市|