Java二叉樹的四種遍歷(遞歸和非遞歸)
二叉樹的遍歷可以分為前序、中序、后序、層次遍歷。
前中后是指何時(shí)訪問(wèn)中間節(jié)點(diǎn),即前序遍歷,遍歷節(jié)點(diǎn)的順序?yàn)椋褐小?gt;左—>右;
中序遍歷,遍歷節(jié)點(diǎn)的順序?yàn)椋鹤蟆?gt;中—>右;
后序遍歷,遍歷節(jié)點(diǎn)的順序?yàn)椋鹤蟆?gt;右—>中。
前序遍歷

遞歸實(shí)現(xiàn)
public void preorder_Traversal(TreeNode root)
{
if(root==null)return;
//訪問(wèn)節(jié)點(diǎn)的邏輯代碼塊
System.out.print(root.val+" ");
preorder_Traversal(root.left);
preorder_Traversal(root.right);
}
非遞歸過(guò)程如下:
1.每遍歷一個(gè)節(jié)點(diǎn)的時(shí)候,先節(jié)點(diǎn)入棧,然后尋找當(dāng)前節(jié)點(diǎn)的左子節(jié)點(diǎn)。(因?yàn)槭乔靶虮闅v,所以在節(jié)點(diǎn)入棧之前就可以對(duì)節(jié)點(diǎn)進(jìn)行訪問(wèn))
2.當(dāng)某個(gè)節(jié)點(diǎn)的左子節(jié)點(diǎn),當(dāng)左子節(jié)點(diǎn)不為空的時(shí)候,重復(fù)過(guò)程1.
3.當(dāng)左子節(jié)點(diǎn)為空的時(shí)候?qū)?dāng)前節(jié)點(diǎn)出棧,并且通過(guò)其尋找右子節(jié)點(diǎn),右子節(jié)點(diǎn)不為空的時(shí)候,重復(fù)過(guò)程1-2
4.當(dāng)右子節(jié)點(diǎn)也為空的時(shí)候,則跳回上一個(gè)該節(jié)點(diǎn)的父節(jié)點(diǎn)(即因?yàn)楫?dāng)前節(jié)點(diǎn)已經(jīng)出棧,所以現(xiàn)在在棧中最上層的節(jié)點(diǎn)是當(dāng)前節(jié)點(diǎn)的父節(jié)點(diǎn))
非遞歸實(shí)現(xiàn)
public void preorder(TreeNode root)
{
Stack<TreeNode> stack=new Stack<>();
while(root!=null||!stack.isEmpty())
{
//當(dāng)前節(jié)點(diǎn)不為空,則入棧,確保最后遍歷到的節(jié)點(diǎn)沒(méi)有左子節(jié)點(diǎn)
//因?yàn)槭乔靶虮闅v,所以再遍歷到每個(gè)節(jié)點(diǎn)的時(shí)候,都可以先訪問(wèn),再尋找其左右子節(jié)點(diǎn)。
while(root!=null)
{
System.out.print(root.val+" ");
stack.push(root);
root=root.left;
}
if(!stack.empty())
{
//把這兩步看成是一步,找到右節(jié)點(diǎn),并把已處理的中節(jié)點(diǎn)從stack當(dāng)中去除
root=stack.pop();
root=root.right;
}
}
}
中序遍歷

遞歸實(shí)現(xiàn)
public void inorder_Traversal(TreeNode root)
{
if(root==null)return;
inorder_Traversal(root.left);
//訪問(wèn)節(jié)點(diǎn)的邏輯代碼塊
System.out.print(root.val+" ");
inorder_Traversal(root.right);
}
非遞歸
對(duì)比前序、中序,發(fā)現(xiàn)代碼幾乎一模一樣,但唯一的不同的是,訪問(wèn)節(jié)點(diǎn)的位置不一樣,中序遍歷是當(dāng)左子節(jié)點(diǎn)被訪問(wèn)過(guò),或者不存在的時(shí)候,才可以訪問(wèn)中間節(jié)點(diǎn),所以再該處,訪問(wèn)節(jié)點(diǎn)的位置放在了當(dāng)左子節(jié)點(diǎn)不存在的時(shí)候,即節(jié)點(diǎn)出棧的時(shí)候,即是左子節(jié)點(diǎn)不存在的時(shí)候進(jìn)行訪問(wèn)。
非遞歸實(shí)現(xiàn)
public void Inorder(TreeNode root)
{
Stack<TreeNode> stack=new Stack<>();
while(root!=null||!stack.isEmpty())
{
//當(dāng)前節(jié)點(diǎn)不為空,則入棧,確保最后遍歷到的節(jié)點(diǎn)沒(méi)有左子節(jié)點(diǎn)
while(root!=null)
{
stack.push(root);
root=root.left;
}
//通過(guò)當(dāng)前節(jié)點(diǎn),跳到當(dāng)前節(jié)點(diǎn)的右節(jié)點(diǎn),因?yàn)槭侵行虮闅v,所以當(dāng)前節(jié)點(diǎn)沒(méi)有左節(jié)點(diǎn)的時(shí)候,就
可以訪問(wèn)當(dāng)前節(jié)點(diǎn)
if(!stack.empty())
{
root=stack.pop();
System.out.print(root.val+" ");
root=root.right;
}
}
}
后序遍歷

遞歸實(shí)現(xiàn)
public void postorder_Traversal(TreeNode root)
{
if(root==null)return;
postorder_Traversal(root.left);
postorder_Traversal(root.right);
//訪問(wèn)節(jié)點(diǎn)的邏輯代碼塊
System.out.print(root.val+" ");
}
非遞歸版本一
借助兩個(gè)棧來(lái)存儲(chǔ)我們的節(jié)點(diǎn)以及標(biāo)示位,過(guò)程如下:
1.每遍歷一個(gè)節(jié)點(diǎn)的時(shí)候,先節(jié)點(diǎn)入棧s,并且s2入棧一個(gè)標(biāo)識(shí)位0,然后尋找當(dāng)前節(jié)點(diǎn)的左子節(jié)點(diǎn)。
2.當(dāng)某個(gè)節(jié)點(diǎn)的左子節(jié)點(diǎn),當(dāng)左子節(jié)點(diǎn)不為空的時(shí)候,重復(fù)過(guò)程1.
3.當(dāng)左子節(jié)點(diǎn)為空的時(shí)候?qū)?dāng)前節(jié)點(diǎn)peek出(即將節(jié)點(diǎn)拿出,但棧中還是有該節(jié)點(diǎn)),并且此時(shí)將s2對(duì)應(yīng)棧頂?shù)臉?biāo)識(shí)位改為1,通過(guò)其尋找右子節(jié)點(diǎn),右子節(jié)點(diǎn)不為空的時(shí)候,重復(fù)過(guò)程1-2
4.當(dāng)右子節(jié)點(diǎn)也為空的時(shí)候,并且s2對(duì)應(yīng)的標(biāo)識(shí)符為1的時(shí)候,則彈出s1棧頂?shù)漠?dāng)前節(jié)點(diǎn),并且將s2的標(biāo)識(shí)符彈出(即因?yàn)楫?dāng)前節(jié)點(diǎn)還沒(méi)有出棧,所以現(xiàn)在在棧中最上層的節(jié)點(diǎn)是當(dāng)前節(jié)),注意s1彈出當(dāng)前節(jié)點(diǎn)并訪問(wèn),但是不賦值給root,在這個(gè)root此時(shí)還是null
5.進(jìn)入過(guò)程3,此時(shí)root被peek賦值到當(dāng)前節(jié)點(diǎn)的父節(jié)點(diǎn)(因?yàn)樵谶^(guò)程4當(dāng)中,已經(jīng)pop出了當(dāng)前節(jié)點(diǎn),所以s1棧頂是當(dāng)前節(jié)點(diǎn)的父節(jié)點(diǎn))的右子節(jié)點(diǎn)。
6.重復(fù)過(guò)程1-5
public void Postorder(TreeNode root)
{
Stack<TreeNode> s =new Stack<>();
Stack<Integer> s2 =new Stack<>();
Integer i=new Integer(1);
while(root!=null||!s.isEmpty())
{
//只要當(dāng)前節(jié)點(diǎn)非空,就入棧
while(root!=null)
{
s.push(root);
s2.push(new Integer(0));
root=root.left;
}
//s2當(dāng)中如果存1,則意味著當(dāng)前s1對(duì)應(yīng)的節(jié)點(diǎn)的左右子節(jié)點(diǎn)都已經(jīng)遍歷過(guò)了。
while(!s.empty()&&s2.peek().equals(i))
{
s2.pop();
System.out.print(s.pop().val+" ");
}
if(!s.isEmpty())
{
s2.pop();
s2.push(new Integer(1));
root=s.peek();
root=root.right;
}
}
}
非遞歸版本二
實(shí)現(xiàn)思路:
在進(jìn)行后序遍歷的時(shí)候是先要遍歷左子樹,然后在遍歷右子樹,最后才遍歷根節(jié)點(diǎn)。所以在非遞歸的實(shí)現(xiàn)中要先把根節(jié)點(diǎn)入棧,然后再把左子樹入棧直到左子樹為空,此時(shí)停止入棧。此時(shí)棧頂就是需要訪問(wèn)的元素,所以直接取出訪問(wèn)p。在訪問(wèn)結(jié)束后,還要判斷被訪問(wèn)的節(jié)點(diǎn)p是否為棧頂節(jié)點(diǎn)的左子樹,如果是的話那么還需要訪問(wèn)棧頂節(jié)點(diǎn)的右子樹,所以將棧頂節(jié)點(diǎn)的右子樹取出賦值給p。如果不是的 話則說(shuō)明棧頂節(jié)點(diǎn)的右子樹已經(jīng)訪問(wèn)完了,那么現(xiàn)在可以訪問(wèn)棧頂節(jié)點(diǎn)了,所以此時(shí)將p賦值為null。判斷結(jié)束的條件是p不為空或者棧不為空,若果兩個(gè)條件都不滿足的話,說(shuō)明所有節(jié)點(diǎn)都已經(jīng)訪問(wèn)完成。
非遞歸實(shí)現(xiàn)
public void postOrder(Node root) {
Stack<Node> s = new Stack<Node>();
Node p = root;
while (p != null || !s.empty()) {
while(p != null) {
s.push(p);
p = p.left;
}
p = s.pop();
System.out.print(p.val+" ");
//這里需要判斷一下,當(dāng)前p是否為棧頂?shù)淖笞訕?,如果是的話那么還需要先訪問(wèn)右子樹才能訪問(wèn)根節(jié)點(diǎn)
//如果已經(jīng)是不是左子樹的話,那么說(shuō)明左右子書都已經(jīng)訪問(wèn)完畢,可以訪問(wèn)根節(jié)點(diǎn)了,所以講p復(fù)制為NULL
//取根節(jié)點(diǎn)
if (!s.empty() && p == s.peek().left) {
p = s.peek().right;
}
else p = null;
}
}
層次遍歷

用隊(duì)列實(shí)現(xiàn),步驟是:
1.對(duì)于不為空的結(jié)點(diǎn),先把該結(jié)點(diǎn)加入到隊(duì)列中;
2.從隊(duì)中拿出結(jié)點(diǎn),如果該結(jié)點(diǎn)的左右結(jié)點(diǎn)不為空,就分別把左右結(jié)點(diǎn)加入到隊(duì)列中;
3.重復(fù)以上操作直到隊(duì)列為空;
public void LaywerTraversal(TreeNode root){
if(root==null) return;
LinkedList<TreeNode> list = new LinkedList<TreeNode>();
list.add(root);
TreeNode currentNode;
while(!list.isEmpty()){
currentNode=list.poll();
System.out.println(currentNode.val);
if(currentNode.left!=null){
list.add(currentNode.left);
}
if(currentNode.right!=null){
list.add(currentNode.right);
}
}
}
到此這篇關(guān)于Java二叉樹的四種遍歷(遞歸和非遞歸)的文章就介紹到這了,更多相關(guān)Java二叉樹內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Java 數(shù)據(jù)結(jié)構(gòu)中二叉樹前中后序遍歷非遞歸的具體實(shí)現(xiàn)詳解
- Java二叉樹的四種遍歷(遞歸與非遞歸)
- java非遞歸實(shí)現(xiàn)之二叉樹的前中后序遍歷詳解
- 通俗易懂講解C語(yǔ)言與Java中二叉樹的三種非遞歸遍歷方式
- java棧實(shí)現(xiàn)二叉樹的非遞歸遍歷的示例代碼
- JAVA二叉樹的幾種遍歷(遞歸,非遞歸)實(shí)現(xiàn)
- java二叉樹的幾種遍歷遞歸與非遞歸實(shí)現(xiàn)代碼
- java二叉樹的非遞歸遍歷
- Java實(shí)現(xiàn)的二叉樹常用操作【前序建樹,前中后遞歸非遞歸遍歷及層序遍歷】
- Java開(kāi)發(fā)深入分析講解二叉樹的遞歸和非遞歸遍歷方法
相關(guān)文章
SpringBoot整合RedisTemplate實(shí)現(xiàn)緩存信息監(jiān)控的基本操作
SpringBoot中的 redistemplate 是一個(gè)用于操作 Redis 數(shù)據(jù)庫(kù)的高級(jí)模板類,它提供了一組方法,可以方便地執(zhí)行常見(jiàn)的 Redis 操作,如存儲(chǔ)、檢索和刪除數(shù)據(jù),本文給大家介紹了SpringBoot整合RedisTemplate實(shí)現(xiàn)緩存信息監(jiān)控的基本操作,需要的朋友可以參考下2025-02-02
Maven是什么?Maven的概念+作用+倉(cāng)庫(kù)的介紹+常用命令的詳解
Maven是一個(gè)項(xiàng)目管理工具,它包含了一個(gè)對(duì)象模型。一組標(biāo)準(zhǔn)集合,一個(gè)依賴管理系統(tǒng)。和用來(lái)運(yùn)行定義在生命周期階段中插件目標(biāo)和邏輯.,本文給大家介紹Maven的概念+作用+倉(cāng)庫(kù)的介紹+常用命令,感興趣的的朋友跟隨小編一起看看吧2020-09-09
idea中springboot項(xiàng)目連接數(shù)據(jù)庫(kù)報(bào)錯(cuò)的原因解析
Java設(shè)計(jì)模式之模版方法模式簡(jiǎn)介
Java批量操作文件系統(tǒng)的實(shí)現(xiàn)示例
淺談maven 多環(huán)境打包發(fā)布的兩種方式
解決在for循環(huán)中remove list報(bào)錯(cuò)越界的問(wèn)題

