主頁 > 後端開發 > 圖文示例二叉樹的編碼實作程序

圖文示例二叉樹的編碼實作程序

2023-06-26 09:39:33 後端開發

前言

在上一篇文章中,帶大家一起學習認識了樹型資料結構的定義和特點,并特別介紹了二叉樹的遍歷操作,分別有:前序遍歷、中序遍歷、后序遍歷,前中后的核心區別是根據根節點在遍歷程序中的位置決定的,即:根節點在最前面的稱之為中序遍歷,根節點在中間的稱之為中序遍歷,根節點在最后的稱之為后序遍歷,需要大家掌握根據樹形結構寫出對應的遍歷序列結果的能力,


全文大約【3700】 字,不說廢話,只講可以讓你學到技術、明白原理的純干貨!本文帶有豐富的案例及配圖視頻,讓你更好地理解和運用文中的技術概念,并可以給你帶來具有足夠啟迪的思考...

一. 二叉樹的編碼實作

接下來=就帶大家,通過代碼來進行二叉樹的編碼實作,

1. 定義二叉樹的結點

對于樹型資料結構中的結點,最多可以包含三部分:結點資料、左孩子子樹、右孩子子樹,我們使用Java對結點結構可以進行如下定義:

public class Node {
    //結點中存盤的資料
    Object data;
    //結點的左子樹
    Node left;
    //結點的右子樹
    Node right;
    //結點的高度
    int height;
	
    //構造方法
    Node(Object data){
        this.data = https://www.cnblogs.com/qian-fen/p/data;
    }

    Node(Object data,Node left,Node right){
        this.data = data;
        this.left = left;
        this.right = right;
        this.height = 0;
    }
    
}

2. 二叉樹的遍歷實作

image.png

如上圖所示,二叉樹的根節點為A,向下第二層為B結點、C結點,依次類推,根據上圖,我們很容易知道,若我們要對二叉樹進行遍歷,只需要從A結點出發,按照對應的前、中、后等不同的邏輯順序進行遍歷即可,因此,我們需要明確:在遍歷二叉樹時,只需要知道二叉樹根節點即可

2.1 前序遍歷

public void prevOrderTraversal(Node root){
    //若根節點為null,直接回傳
    if(root == null){
        return;
    }
	//先序遍歷,表示先訪問根節點,故先輸出傳入的結點中的資料
	System.out.printf("%s",root.data);

	//遍歷當前結點的左孩子結點
	prevOrderTraversal(root.left);
	//遍歷當前結點的右孩子結點
	prevOrderTraversal(root.right);
}

根據上述代碼,若要執行前序遍歷,只需要呼叫prevOrderTraversal時將A結點作為引數傳入,即可得到列印的二叉樹的前序遍歷的結果:A B D C E G F H J,

2.2 中序遍歷

public void inOrderTraversal(Node root){
    if(root == null){
        return;
    }
	//1、首先遍歷當前root結點的左子樹
	inOrderTraversal(root.left);

	//2、遍歷當前結點root中的資料,并進行輸出
	System.out.printf("%s ",root.data);

	//3、遍歷當前結點root的右子樹
	inOrderTraversal(root.right);
}

中序遍歷的代碼編程如上,先遍歷當前結點root的左子樹,接著將當前結點root中元素輸出,最后遍歷當前結點root的右子樹,呼叫上述inOrderTraversal函式,將A結點作為引數傳入,可以得到中序二叉樹中序遍歷的結果為:B D A G E C H F I,

2.3 后序遍歷

public void postOrderTraversal(Node root){
    if(root == null){
        return;
    }
	//1、先遍歷當前結點的左子樹
	postOrderTraversal(root.left);
	//2、先遍歷當前結點的右子樹
	postOrderTraversal(root.right);
	//3、最后當前結點的資料data
	System.out.printf("%s ",root.data);
}

如上所示的后序遍歷操作,在呼叫postOrderTraversal函式時,將樹的根結點A作為引數傳入,可以得到后序遍歷的序列為:D B G E H I F C A,

2.4 層序遍歷(廣度遍歷)

前面三種遍歷方式前序、中序、后序均屬于深度遍歷,前文我們曾經提到,層序遍歷又稱廣度遍歷,主要是按層對樹進行結點的遍歷,在編程實作上,層序遍歷與深度遍歷的操作稍有不同,具體實作如下:

public void levelOrderTraversal(Node root){
    if(root == null){
        return;
    }

	Queue<Node> queue = new LinkedList<Node>();
	//首先訪問第1層的根節點
	queue.add(root);

	while(!queue.isEmpty()){
        //從佇列中取出一個結點,并輸出該結點,意味著已經訪問過該結點
        Node node = queue.poll();
        System.out.printf("%s ", node.data);

        //判斷并將該結點的左子樹存入到佇列中
        if(node.left != null){
            queue.add(node.left);
        }

        //判斷并將該結點的右子樹存入到佇列中
        if(node.right != null){
            queue.add(node.right);
        }
    }
}

如上述代碼所示,在進行層序遍歷(廣度遍歷)時,我們借用了前面已經學習過的資料結構佇列Queue,將每次訪問的結點輸出后,同時將結點的左右子樹存入到佇列中,因為我們知道佇列的特點是,元素輸入的順序與輸出的順序會保持一致,因此在每次取資料時,總是先取出之前存入的資料;同時,又會把下一層資料(左右子樹)存入佇列中,最終,上述代碼對樹的層序遍歷結果是:A B C D E F G H I,

二. 二叉查找樹

1. 二叉查找樹概念

二叉查找樹,全稱為Binary Search Tree,簡稱BST,從名字中我們容易理解,二叉查找樹是在二叉樹的基礎上,同時具備了某些特性的一種特殊的二叉樹型結構,二叉查找樹相較于二叉樹,額外滿足以下幾個條件:

(1). 若左子樹不為空,則左子樹上的所有結點的值均小于根結點位置上的值;

(2). 若右子樹不為空,則右子樹上的所有結點的值均大于根結點位置上的值;

(3). 左、右子樹也都是二叉查找樹,

總結上述幾個條件的,我們可以用一居話概括二叉查找樹的特點,左子樹的數值小于根結點點數值,根結點數值小于右子樹結點數值,整個二叉樹結點的數值從左至右是有序的

基于以上所總結的二叉查找樹的特點,有的資料和教材也把查找樹稱之為二叉搜索樹,以及二叉排序樹(Binary Sort Tree,簡稱BST),因此,大家要記住以下結論特征:左子樹結點數值 < 根結點數值 < 右子樹結點數值

如上圖所示,就是一棵二叉查找樹:在此二叉查找樹中,任意的子樹都滿足左子樹結點數值 < 父結點數值 < 右子樹結點數值的規律,

2. 二叉查找樹的操作

二叉查找樹最簡單的操作是結點查找,其次,因為整個二叉查找樹都滿足從左至右是有序的,因此如果二叉查找樹的結點數量發生變化,就會引起二叉查找樹需要重新調整的操作,所以,我們此處還會討論二叉查找樹新增結點和洗掉結點的操作,最后二叉查找樹與普通二叉樹一樣,都可以進行最基本的遍歷操作,

接下來,我們依次討論:結點查找、新增結點、洗掉結點、遍歷二叉查找樹,

2.1 結點查找

我們通過示例進行說明結點查找的邏輯,如下所示:

上圖中是一個二叉查找樹,現在我們希望查找數值5所在的結點,其具體的步驟如下:

①. 訪問根節點,根結點數值位7;

②. 要查找的數值5 < 7,因此訪問結點7的左子樹結點,并獲得左子樹結點數值4;

③. 要查找的數值5 > 4,因此訪問結點4的右子樹結點,并獲得右子樹結點數值6;

④. 要查找的數值5 < 6,因此訪問結點6的左子樹結點,并獲得左子樹結點數值5;

⑤. 要查找的數值5 == 5,因此表示找到了目標數值5所在的結點,

時間復雜度分析:假設一課二叉查找樹結點的個數為n,我們會發現在進行查找時,總是會先判斷要查找的數值和當前結點的數值的大小,然后根據判斷結果,選擇其中一側進行繼續查找;而不符合條件的另外一側子樹,可以直接放棄,因為我們知道二叉查找樹從左至右總是有序的,這種從左至右有序的二叉查找樹,在進行查找時,可以極大的節省時間,實際上,使用二叉查找樹查找某個結點,所需要的時間復雜度為O(log 2 n) ,該時間復雜度與樹的深度O(log2n)是一樣的,

2.2 新增結點

依然以上述二叉查找樹為例,現在要插入數值為3的結點,示意圖如下:

如上圖,插入數值為3的結點的步驟如下:

①. 訪問根結點,獲得根結點數值7;

②. 要插入結點的數值3 < 7,因此訪問根結點的左子樹,并獲得左子樹結點的數值4;

③. 要插入結點的數值3 < 4,因此訪問根結點的左子樹,并獲得左子樹結點的數值2;

④. 要插入結點的數值3 > 2,因此訪問根結點的右子樹,但右子樹為空;

⑤. 右子樹為空,即意味著找到了要插入的位置,即將新增的數值位3的結點作為結點2的右子樹,

時間復雜度分析:根據插入的步驟,我們可以非常容易的理解插入結點的操作時間復雜度也為O(log2n),與查找結點時間復雜度相同,

2.3 洗掉結點

洗掉結點的操作與前面的查找和增加操作不太一樣,洗掉操作需要分不同的情況進行討論,原因是洗掉操作會使二叉查找樹的結點減少,洗掉后需要讓剩余的結點繼續滿足二叉查找樹的規則和特點,就可能需要做出調整,

具體的,我們可以將洗掉結點操作分為三種情況:洗掉葉子結點、洗掉結點有1個子樹、洗掉結點有2個子樹,下面,我們詳細進行討論:

2.3.1 洗掉葉子結點

洗掉葉子結點是最簡單的操作,因為葉子結點是最底層的結點,因此不需要任何的調整,直接洗掉即可,洗掉葉子結點不會對二叉查找樹的性質產生影響,

2.3.2 洗掉結點有1個子樹

當要洗掉的結點有1個子樹時(可能是左子樹,也可能是右子樹),我們需要將洗掉結點的子樹結點,替換到洗掉結點的位置,比如,以下圖為例:

如上圖所示,我們希望洗掉數值位6的結點,由于結點6有一棵數值位5的左子樹,因此,在將結點6洗掉后,將子結點5放在原來結點6的位置上,如上右圖所示,

通過上述案例操作我們可以總結:當洗掉結點有1個子樹結點時,直接將子結點放在洗掉結點的位置

2.3.3 洗掉結點有2個子樹

當洗掉結點有2個子樹時,可以借助二叉樹的中序遍歷結果進行操作,具體步驟為:

(1). 找出二叉查找樹的中序遍歷序列;

(2). 找到要洗掉結點數值的前驅結點數值;

(3). 使用前驅結點數值替換洗掉的結點,

以下圖為例:要洗掉二叉查找樹的數值9所在的結點

如上圖所示,洗掉步驟如下:

①. 根據圖示的二叉查找樹,得到中序遍歷結果:1 2 4 5 6 7 8 9 10 12;

②. 確定要洗掉數值9的結點;

③. 在中序遍歷結果序列中找到9的前驅8;

④. 使用數值8結點替換結點9,替換后入上右圖所示,

2.4 遍歷二叉查找樹

我們已經知道二叉查找樹是具有特殊性質的二叉樹,因此二叉查找樹的遍歷與二叉樹的遍歷規則完全一致,此處我們就不再贅述,


四. 結語

通過本篇內容,就帶大家一起學習了二叉樹的Java編程實作,其實,前序遍歷、中序遍歷、后序遍歷的編程實作原理都是相同的,只是遍歷的順序不同而已, 同時,在進行編程實作時,我們發現,無論前序、中序還是后序遍歷,都出現了在函式內部繼續呼叫函式本身的現象,在計算機編程中,我們把程式呼叫自己本身的折中操作稱之為遞回,各位感興趣的讀者,可以自己查閱資料,了解遞回的相關概念和特點,

以上就是我們本篇的全部內容啦,大家get了嗎~

沒太明白的可以仔細看我們的視頻講解:零基礎學Java快速入門

更多干貨知識、學習路線圖,掃描文末二維碼可得↓↓↓

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/555980.html

標籤:Java

上一篇:SpringMVC

下一篇:返回列表

標籤雲
其他(161616) Python(38248) JavaScript(25513) Java(18260) C(15238) 區塊鏈(8272) C#(7972) AI(7469) 爪哇(7425) MySQL(7269) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5875) 数组(5741) R(5409) Linux(5347) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4606) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2437) ASP.NET(2404) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) .NET技术(1985) HtmlCss(1972) 功能(1967) Web開發(1951) C++(1942) python-3.x(1918) 弹簧靴(1913) xml(1889) PostgreSQL(1881) .NETCore(1863) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • 圖文示例二叉樹的編碼實作程序

    在上一篇文章中,帶大家一起學習認識了樹型資料結構的定義和特點,并特別介紹了二叉樹的遍歷操作,分別有:前序遍歷、中序遍歷、后序遍歷。前中后的核心區別是根據根節點在遍歷程序中的位置決定的,即:根節點在最前面的稱之為中序遍歷,根節點在中間的稱之為中序遍歷,根節點在最后的稱之為后序遍歷。需要大家掌握根據樹形... ......

    uj5u.com 2023-06-26 09:39:33 more
  • SpringMVC

    [TOC] # SpringMVC ssm mybatis+Spring+Spring+MVC MVC三層架構 spring : IOC 和 AOP Spring: spring執行流程! # 1、什么是SpringMVC Spring MVC是Spring Framework的一部分,是基于Jav ......

    uj5u.com 2023-06-26 08:00:44 more
  • [ARM 匯編]高級部分—ARM匯編編程實戰—3.3.3 嵌入式應用程式設

    在本章節中,我們將學習如何使用ARM匯編撰寫一個簡單的嵌入式應用程式。我們將以STM32F103微控制器為例,撰寫一個程式,實作按下按鈕時點亮LED的功能。 1. **硬體連接** 首先,我們需要將STM32F103微控制器的一個GPIO引腳連接到LED(通過一個合適的電阻),另一個GPIO引腳連接 ......

    uj5u.com 2023-06-26 08:00:35 more
  • C++面試八股文:std::array如何實作編譯器排序?

    某日二師兄參加XXX科技公司的C++工程師開發崗位第25面: > 面試官:`array`熟悉嗎? > > 二師兄:你說的是原生陣列還是`std::array`? > > 面試官:你覺得兩者有什么區別? > > 二師兄:區別不是很大,原生陣列(非動態陣列)和std::array都在堆疊上開辟空間,初始化 ......

    uj5u.com 2023-06-26 08:00:30 more
  • celery筆記七之周期/定時任務及crontab定義

    > 本文首發于公眾號:Hunter后端 > 原文鏈接:[celery筆記七之周期/定時任務及crontab定義](https://mp.weixin.qq.com/s/sNShaRbuM2gm2qn_codaTg) periodic task,即為周期,或者定時任務,比如說每天晚上零點零分需要運行一 ......

    uj5u.com 2023-06-26 08:00:23 more
  • 逍遙自在學C語言 | 指標陷阱-空指標與野指標

    ## 前言 在C語言中,指標是一種非常強大和靈活的工具,但同時也容易引發一些問題,其中包括空指標和野指標。 本文將帶你了解這兩個概念的含義、產生原因以及如何避免它們所導致的問題。 ## 一、人物簡介 - 第一位閃亮登場,有請今后會一直教我們C語言的老師 —— 自在。 ![](https://img2 ......

    uj5u.com 2023-06-26 08:00:11 more
  • 分享一些我技術成長的感悟

    今晚來聊聊我在**技術成長**中的一些感悟,跟大家分享下。 ## BALABALA 在大學的時候,我一個計算機專業相關的證書都沒考,自認為這些證書對我以后找作業沒什么大的幫助。于是我把時間更多地花在研究八股文上,因為八股文在面試的時候是要用到的。 (**利益化**) > **我會對我做的事情利益化* ......

    uj5u.com 2023-06-26 08:00:04 more
  • python測驗開發面試常考題:裝飾器

    ### 簡介 Python 裝飾器是一個可呼叫的(函式、方法或類),它獲得一個函式物件 func_in 作為輸入,并回傳另一函式物件 func_out。它用于擴展函式、方法或類的行為。 裝飾器模式通常用于擴展物件的功能。在日常生活中,這種擴展的例子有:在槍上加一個消音器,使用不同的相機鏡頭等等。 ! ......

    uj5u.com 2023-06-26 07:59:58 more
  • Kubernetes 系列:了解 k8s 架構(一)

    ### Kubernetes 概述 當下,我們很多專案于都在`Cloud Native`(云原生)的上面,這種方法旨在使組織能夠確保可用性并快速回應和適應變化,云原生其實就是一組本質上支持在不同云環境(公共云、私有云或混合云)上大規模構建、運行和管理應用程式的實踐和技術。 云原生離不開兩個概念:`容 ......

    uj5u.com 2023-06-26 07:58:59 more
  • TomCat快速安裝使用

    # 下載 這就不多說了,直接官網下載 https://tomcat.apache.org/ 直接解壓 配置 環境變數 (提前安裝好java,配置好java的環境變數) 配置Tomcat環境變數前一定要配置好java的環境變數,尤其是JAVA_HOME 新建 `CATALINA_HOME` 環境變數, ......

    uj5u.com 2023-06-26 07:58:51 more