二叉樹

一裂垦、二叉樹的定義

????????在計(jì)算機(jī)科學(xué)中,樹是一種重要的非線性數(shù)據(jù)結(jié)構(gòu),直觀的看砂碉,它是數(shù)據(jù)元素按分支關(guān)系組織起來的結(jié)構(gòu)吟秩。二叉樹是每個(gè)節(jié)點(diǎn)最多有兩個(gè)子樹的有序樹。通常子樹的根被稱作“左子樹”和“右子樹”绽淘。二叉樹常被用做二叉查找樹和二叉堆或是二叉排序樹。二叉樹的每個(gè)節(jié)點(diǎn)至多只有兩顆子樹闹伪,二叉樹有左右之分沪铭,次序不能顛倒。簡(jiǎn)單來說只要滿足下面兩個(gè)條件就是二叉樹:

1.本身是有序樹偏瓤;

2.樹中包含的各個(gè)節(jié)點(diǎn)的度不能超過 2杀怠,即只能是 0、1 或者 2厅克;


圖1.二叉樹示意圖

二叉樹具有的性質(zhì):

1.二叉樹中赔退,第 i 層最多有2的i-1次方個(gè)結(jié)點(diǎn)。

2.如果二叉樹的深度為 K证舟,那么此二叉樹最多有 2K-1 個(gè)結(jié)點(diǎn)硕旗。

3.二叉樹中,終端結(jié)點(diǎn)數(shù)(葉子結(jié)點(diǎn)數(shù))為 n0女责,度為 2 的結(jié)點(diǎn)數(shù)為 n2漆枚,則 n0=n2+1。

1抵知、滿二叉樹

? ? ? ?二叉樹中墙基,除了葉子節(jié)點(diǎn),每個(gè)節(jié)點(diǎn)的度都為2刷喜,及每個(gè)節(jié)點(diǎn)都有左右兩個(gè)子節(jié)點(diǎn)残制。這時(shí)候的二叉樹稱之為“滿二叉樹”。


圖2.滿二叉樹示意圖

滿二叉樹除了具有二叉樹的基本心智以外還具有以下性質(zhì):

1.滿二叉樹中第 i 層的節(jié)點(diǎn)數(shù)為 2的i-1次方個(gè)掖疮。

2.深度為?K?的滿二叉樹必有?2^K - 1 個(gè)節(jié)點(diǎn) 初茶,葉子數(shù)為 2的K-1次方。

3.滿二叉樹中不存在度為 1 的節(jié)點(diǎn)浊闪,每一個(gè)分支點(diǎn)中都兩棵深度相同的子樹纺蛆,且葉子節(jié)點(diǎn)都在最底層。

4.具有 n 個(gè)節(jié)點(diǎn)的滿二叉樹的深度為 规揪。

2桥氏、完全二叉樹

? ? ? ?如果二叉樹中除去最后一層節(jié)點(diǎn)外,其它層為滿二叉樹猛铅,且最后一層的結(jié)點(diǎn)依次從左到右分布字支,則此二叉樹被稱為完全二叉樹。滿二叉樹一定是個(gè)完全二叉樹。


圖3.完全二叉樹示意圖

? ? ? ?如圖 3a) 所示是一棵完全二叉樹堕伪,圖 3b) 由于最后一層的節(jié)點(diǎn)沒有按照從左向右分布青灼,因此只能算作是普通的二叉樹娄蔼。

? ? ? ?完全二叉樹除了具有普通二叉樹的性質(zhì),它自身也具有一些獨(dú)特的性質(zhì),比如說炸渡,n 個(gè)結(jié)點(diǎn)的完全二叉樹的深度為 ?log2n?+1。

PS:?log2n? 表示取小于 log2n 的最大整數(shù)眼刃。例如截歉,?log24? = 2,而 ?log25? 結(jié)果也是 2霍比。

對(duì)于任意一個(gè)完全二叉樹來說幕袱,如果將含有的結(jié)點(diǎn)按照層次從左到右依次標(biāo)號(hào)(如圖 3a)),對(duì)于任意一個(gè)結(jié)點(diǎn) i 悠瞬,完全二叉樹還有以下幾個(gè)結(jié)論成立:

1.當(dāng) i>1 時(shí)们豌,父親結(jié)點(diǎn)為結(jié)點(diǎn) [i/2] 。(i=1 時(shí)浅妆,表示的是根結(jié)點(diǎn)望迎,無父親結(jié)點(diǎn))

2.如果 2*i>n(總結(jié)點(diǎn)的個(gè)數(shù)) ,則結(jié)點(diǎn) i 肯定沒有左孩子(為葉子結(jié)點(diǎn))凌外;否則其左孩子是結(jié)點(diǎn) 2*i 擂煞。

3.如果 2*i+1>n ,則結(jié)點(diǎn) i 肯定沒有右孩子趴乡;否則右孩子是結(jié)點(diǎn) 2*i+1 对省。

3、二叉搜索樹

? ? ? ? 從節(jié)點(diǎn)出發(fā)晾捏,左子樹節(jié)點(diǎn)的數(shù)據(jù)是小于節(jié)點(diǎn)蒿涎,右子樹節(jié)點(diǎn)的數(shù)據(jù)都是大于節(jié)點(diǎn),這樣的二叉樹稱為“二叉搜索樹”惦辛。


圖4.二叉搜索樹

4劳秋、平衡二叉搜索樹

? ? ? ? 在二叉搜索樹的基礎(chǔ)上,左子樹和右子樹高度差的絕對(duì)值不能超過1胖齐。


圖5.非平衡二叉樹

如圖5所示玻淑,左子樹的高度為4,右子樹的高度為2呀伙,高度差為2补履,所以它不是平衡二叉樹。

二剿另、二叉樹的存儲(chǔ)

1.線性存儲(chǔ):用一個(gè)字符數(shù)組來保存箫锤,將二叉樹的節(jié)點(diǎn)按照層級(jí)從左到右標(biāo)識(shí)下標(biāo)贬蛙,如下圖所示:


圖6.線性存儲(chǔ)

?按照下標(biāo)最終排的結(jié)果為:abcdefg,那如何找到節(jié)點(diǎn)的左右子節(jié)點(diǎn)呢谚攒?假設(shè)?i?為字符數(shù)組的下標(biāo)阳准,則它的左節(jié)點(diǎn)的數(shù)組下標(biāo)為2*i+1,右節(jié)點(diǎn)的數(shù)組下標(biāo)為2*i+2馏臭。

2.鏈?zhǔn)酱鎯?chǔ):內(nèi)部有兩個(gè)指針分別指向左右兩個(gè)子節(jié)點(diǎn)野蝇。常用的存儲(chǔ)方式。

三括儒、二叉樹的遍歷

1绕沈、深度遍歷

? ? ? ? 從根節(jié)點(diǎn)出發(fā),使用遞歸的方式遍歷塑崖。前、中痛倚、后序遍歷都是屬于深度遍歷规婆,使用的方法就是遞歸。除了遞歸蝉稳,還可以使用迭代法遍歷抒蚜。大多數(shù)情況下,迭代都能通過棧模擬出來遞歸耘戚,只是可能實(shí)現(xiàn)的方法要麻煩一些嗡髓。

前序遍歷:中左右

中序遍歷:左中右

后序遍歷:左右中

? ? ? ?簡(jiǎn)單理解一下,所謂的前收津、中饿这、后遍歷方式,只需要記住中(根)的位置在哪兒撞秋,在最前就是前序长捧,在中間就是中序,在最后就是后序吻贿,左右是不會(huì)變的串结。所謂的前中后的單位都是樹,如下圖所示舅列,a的左為b子樹肌割,b子樹包含了de節(jié)點(diǎn),處理a的左子樹的時(shí)候帐要,會(huì)將bde看作一棵樹把敞,然后再在這棵樹中使用左來遍歷。


圖7.二叉樹的遍歷-深度遍歷

2榨惠、廣度遍歷

? ? ? ? 一層一層的從左到右遍歷先巴。

3其爵、使用Java代碼實(shí)現(xiàn)二叉樹的插入和遍歷

public class BinarySearchTree{

? ? private int data;

? ? private BinarySearchTreeleft;

? ? private BinarySearchTreeright;

? ? public BinarySearchTree(int data) {

? ? ? ? this.data = data;

? ? }

? ? public BinarySearchTree(int data, BinarySearchTree left, BinarySearchTree right) {

? ? ? ? this.data = data;

? ? ? ? this.left = left;

? ? ? ? this.right = right;

? ? }

? ? /**

? ? * 插入二叉搜索樹,比節(jié)點(diǎn)數(shù)據(jù)大的數(shù)據(jù)插入右子樹伸蚯,反之插入左子樹

? ? */

? ? public static BinarySearchTreeinsert(BinarySearchTree root, int val) {

? ? ? ? if (null == root) {

? ? ? ? ? ? return new BinarySearchTree(val);

? ? ? ? }

? ? ? ? if (root.data < val) {

? ? ? ? ? ? root.right =insert(root.right, val);

? ? ? ? }

? ? ? ? if (root.data > val) {

? ? ? ? ? ? root.left =insert(root.left, val);

? ? ? ? }

? ? ? ? return root;

? ? }

? ? /**

? ? * 遍歷二叉樹--遞歸--前序

? ? */

? ? public static List<Integer> preTraverse(BinarySearchTree root, List<Integer> rlt) {

? ? ? ? if (null == root) {

? ? ? ? ? ? return rlt;

? ? ? ? }

? ? ? ? rlt =null == rlt ?new ArrayList<>() : rlt;

? ? ? ? rlt.add(root.data);

? ? ? ? preTraverse(root.left, rlt);

? ? ? ? preTraverse(root.right, rlt);

? ? ? ? rlt.add(root.data);

? ? ? ? return rlt;

? ? }

? ? /**

? ? * 遍歷二叉樹--遞歸--中序

? ? */

? ? public static List<Integer> midTraverse(BinarySearchTree root, List<Integer> rlt) {

? ? ? ? if (null == root) {

? ? ? ? ? ? return rlt;

? ? ? ? }

? ? ? ? rlt =null == rlt ?new ArrayList<>() : rlt;

? ? ? ? preTraverse(root.left, rlt);

? ? ? ? rlt.add(root.data);

? ? ? ? preTraverse(root.right, rlt);

? ? ? ? return rlt;

? ? }

? ? /**

? ? * 遍歷二叉樹--遞歸--后序

? ? */

? ? public static List<Integer> posTraverse(BinarySearchTree root, List<Integer> rlt) {

? ? ? ? if (null == root) {

? ? ? ? ? ? return rlt;

? ? ? ? }

? ? ? ? rlt =null == rlt ?new ArrayList<>() : rlt;

? ? ? ? preTraverse(root.left, rlt);

? ? ? ? preTraverse(root.right, rlt);

? ? ? ? rlt.add(root.data);

? ? ? ? return rlt;

? ? }

? ? /**

? ? * 遍歷二叉樹--迭代--前序

? ? */

? ? public static List<Integer> preIteration(BinarySearchTree root, List<Integer> rlt) {

? ? ? ? if (null == root) {

? ? ? ? ? ? return rlt;

? ? ? ? }

? ? ? ? rlt =null == rlt ?new ArrayList<>() : rlt;

? ? ? ? Stack<BinarySearchTree> stack =new Stack<>();

? ? ? ? BinarySearchTree cur = root;

? ? ? ? while (cur !=null || !stack.isEmpty()) {

? ? ? ? ? ? while (cur !=null) {

? ? ? ? ? ? ? ? stack.push(cur);

? ? ? ? ? ? ? ? rlt.add(cur.data);

? ? ? ? ? ? ? ? cur = cur.left;

? ? ? ? ? ? }

? ? ? ? ? ? BinarySearchTree top = stack.pop();

? ? ? ? ? ? cur = top.right;

? ? ? ? }

? ? ? ? return rlt;

? ? }

? ? /**

? ? * 遍歷二叉樹--迭代--中序

? ? */

? ? public static List<Integer> midIteration(BinarySearchTree root, List<Integer> rlt) {

? ? ? ? if (null == root) {

? ? ? ? ? ? return rlt;

? ? ? ? }

? ? ? ? rlt =null == rlt ?new ArrayList<>() : rlt;

? ? ? ? Stack<BinarySearchTree> stack =new Stack<>();

? ? ? ? BinarySearchTree cur = root;

? ? ? ? while (cur !=null || !stack.isEmpty()) {

? ? ? ? ? ? while (cur !=null) {

? ? ? ? ? ? ? ? stack.push(cur);

? ? ? ? ? ? ? ? cur = cur.left;

? ? ? ? ? ? }

? ? ? ? ? ? BinarySearchTree top = stack.pop();

? ? ? ? ? ? rlt.add(cur.data);

? ? ? ? ? ? cur = top.right;

? ? ? ? }

? ? ? ? return rlt;

? ? }

? ? /**

? ? * 遍歷二叉樹--迭代--后序

? ? */

? ? public static List<Integer> posIteration(BinarySearchTree root, List<Integer> rlt) {

? ? ? ? if (null == root) {

? ? ? ? ? ? return rlt;

? ? ? ? }

? ? ? ? rlt =null == rlt ?new ArrayList<>() : rlt;

? ? ? ? Stack<BinarySearchTree> stack =new Stack<>();

? ? ? ? BinarySearchTree pre =null;

? ? ? ? BinarySearchTree cur = root;

? ? ? ? while (cur !=null || !stack.isEmpty()) {

? ? ? ? ? ? while (cur !=null) {

? ? ? ? ? ? ? ? stack.push(cur);

? ? ? ? ? ? ? ? cur = cur.left;

? ? ? ? ? ? }

? ? ? ? ? ? cur = stack.peek();

? ? ? ? ? ? if (cur.right ==null || pre == cur.right) {

? ? ? ? ? ? ? ? BinarySearchTree top = stack.pop();

? ? ? ? ? ? ? ? rlt.add(top.data);

? ? ? ? ? ? ? ? pre = cur;

? ? ? ? ? ? ? ? cur =null;

? ? ? ? ? ? } else {

? ? ? ? ? ? ? ? cur = cur.right;

? ? ? ? ? ? }

}

? ? ? ? return rlt;

? ? }

? ? /**

? ? * 廣度遍歷--迭代--層級(jí)

? ? */

? ? public static List<Integer> levelOderTraversal(BinarySearchTree root) {

? ? ? ? Queue<BinarySearchTree> q1 =new LinkedList<>();

? ? ? ? if (root ==null) {

? ? ? ? ? ? return null;

? ? ? ? }

? ? ? ? q1.offer(root);

? ? ? ? List<Integer> rlt =new ArrayList<>();

? ? ? ? while (!q1.isEmpty()) {

? ? ? ? ? ? BinarySearchTree top = q1.poll();

? ? ? ? ? ? rlt.add(top.data);

? ? ? ? ? ? if (top.left !=null) {

? ? ? ? ? ? ? ? q1.offer(top.left);

? ? ? ? ? ? }

? ? ? ? ? ? if (top.right !=null) {

? ? ? ? ? ? ? ? q1.offer(top.right);

? ? ? ? ? ? }

}

? ? ? ? return rlt;

? ? }

}

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末摩渺,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子剂邮,更是在濱河造成了極大的恐慌摇幻,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,743評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件挥萌,死亡現(xiàn)場(chǎng)離奇詭異绰姻,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)引瀑,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,296評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門狂芋,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人憨栽,你說我怎么就攤上這事帜矾。” “怎么了屑柔?”我有些...
    開封第一講書人閱讀 157,285評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵屡萤,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我掸宛,道長(zhǎng)死陆,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,485評(píng)論 1 283
  • 正文 為了忘掉前任唧瘾,我火速辦了婚禮措译,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘饰序。我一直安慰自己瞳遍,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,581評(píng)論 6 386
  • 文/花漫 我一把揭開白布菌羽。 她就那樣靜靜地躺著掠械,像睡著了一般。 火紅的嫁衣襯著肌膚如雪注祖。 梳的紋絲不亂的頭發(fā)上猾蒂,一...
    開封第一講書人閱讀 49,821評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音是晨,去河邊找鬼肚菠。 笑死,一個(gè)胖子當(dāng)著我的面吹牛罩缴,可吹牛的內(nèi)容都是我干的蚊逢。 我是一名探鬼主播层扶,決...
    沈念sama閱讀 38,960評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼烙荷!你這毒婦竟也來了镜会?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,719評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤终抽,失蹤者是張志新(化名)和其女友劉穎戳表,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體昼伴,經(jīng)...
    沈念sama閱讀 44,186評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡匾旭,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,516評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了圃郊。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片价涝。...
    茶點(diǎn)故事閱讀 38,650評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖持舆,靈堂內(nèi)的尸體忽然破棺而出色瘩,到底是詐尸還是另有隱情,我是刑警寧澤吏廉,帶...
    沈念sama閱讀 34,329評(píng)論 4 330
  • 正文 年R本政府宣布泞遗,位于F島的核電站惰许,受9級(jí)特大地震影響席覆,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜汹买,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,936評(píng)論 3 313
  • 文/蒙蒙 一佩伤、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧晦毙,春花似錦生巡、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,757評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至须揣,卻和暖如春盐股,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背耻卡。 一陣腳步聲響...
    開封第一講書人閱讀 31,991評(píng)論 1 266
  • 我被黑心中介騙來泰國(guó)打工疯汁, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人卵酪。 一個(gè)月前我還...
    沈念sama閱讀 46,370評(píng)論 2 360
  • 正文 我出身青樓幌蚊,卻偏偏與公主長(zhǎng)得像谤碳,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子溢豆,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,527評(píng)論 2 349

推薦閱讀更多精彩內(nèi)容