摘要:又是來自我的好朋友的投稿,以下是原文基本定義二分搜索樹的每個子節點最多有兩個葉子節點二分搜索樹的每個節點最多有一個根節點存儲的元素必須具有可比較性二分搜索樹每個子節點的值大于其左子節的所有節點的值小于其右子節點的所有節點的值二分搜索樹不一定
又是來自我的好朋友 EvilSay 的投稿,以下是原文:
1、基本定義二分搜索樹的每個子節點最多有兩個葉子節點
二分搜索樹的每個節點最多有一個根節點
存儲的元素必須具有可比較性
二分搜索樹每個子節點的值
大于其左子節的所有節點的值
小于其右子節點的所有節點的值
二分搜索樹不一定是滿的
2、二分搜索樹 Java 實現/** * @Author: EvilSay * @Date: 2019/8/6 19:00 */ public class BSTMain3、圖解二分搜索樹> { private class Node { public E e; private Node left, right; public Node(E e) { this.e = e; left = null; right = null; } } //根節點 private Node root; private int size; public BSTMain() { root = null; size = 0; } public int size() { return size; } public boolean isEmpty() { return size == 0; } public void add(E e){ root = add(this.root, e); } // 向node為根的二分搜索樹中插入元素E,遞歸算法 // 返回插入新節點后二分搜索樹的根 private Node add(Node node, E e){ if (node == null){ size ++; return new Node(e); } if (e.compareTo(node.e) < 0) node.left = add(node.left, e); else if (e.compareTo(node.e) > 0) node.right = add(node.right,e); return node; } // 看二分搜索樹中是否包含元素e public boolean contains(E e){ return contains(root,e); } // 看以node為根的二分搜索樹中是否包含元素e,遞歸算法 private boolean contains(Node node, E e){ if (node == null) return false; if (e.compareTo(node.e) == 0) return true; else if (e.compareTo(node.e) < 0) return contains(node.left, e); else return contains(node.right,e); } @Override public String toString() { StringBuilder res = new StringBuilder(); generateBSTSString(root,0,res); return res.toString(); } // 生成以node為根節點,深度為depth的描述二叉樹的字符串 private void generateBSTSString(Node root, int depth, StringBuilder res) { if (root == null){ res.append(generateDepthString(depth) + "null "); return; } res.append(generateDepthString(depth) + root.e + " "); generateBSTSString(root.left, depth + 1 ,res); generateBSTSString(root.right, depth + 1, res); } private String generateDepthString(int depth) { StringBuilder res = new StringBuilder(); for (int i = 0; i < depth; i++) res.append("--"); return res.toString(); } }
從圖上我們看出二分搜索樹每個節點的值大于其左子節的所有節點的值小于其右子節點的所有節點的值。
4、前序遍歷前序遍歷也叫先序遍歷,訪問順序是根左右,也就是先訪問根節點,再到左子樹,最后才到右子樹。所以上圖所示的訪問順序是 5、3、2、4、8、7、9。
二分搜索樹前序遍歷遞歸版與非遞歸版
//前序遍歷以node為根的二分搜索樹,遞歸算法 private void preOrder(Node node){ if (node == null) return; System.out.println(node.e); preOrder(node.left); preOrder(node.right); } //二分搜索樹的前序遍歷遞歸調用 public void preOrder(){ preOrder(root); } //二分搜索樹的前序遍歷非遞歸寫法 public void preOrderNG(){ Stackstack = new Stack<>(); //根節點 stack.push(root); while (!stack.isEmpty()){ Node cur = stack.pop(); System.out.println(cur.e); //判斷是否還有葉子節點 if (cur.right != null) stack.push(cur.right); if (cur.left != null) stack.push(cur.left); } }
理解非遞歸的實現邏輯、推導出前序遞歸的實現
創建一個堆棧,我們把根節點 5 推入棧中,接下來我們就要訪問 5 這個根節點了,所有把 5 從棧中推出 。
推出的元素有 {5},棧中的元素有 [] 。
在推入 5 的子節點就是 3,8,我們先入后出,先推入 8 再推入 3,現在堆棧的元素有 [8,3],棧頂的 3 就是我們下一次要訪問的節點所以把 3 推出 。
推出的元素有 {5,3},棧中的元素有 [8] 。
在推入 3 的子節點就是 2,4 繼續先入后出,先推入 4 再推入 2,現在堆棧的元素有 [8,4,2],棧頂的 2 就是我們下一次要訪問的節點所以把 2 推出 。
推出的元素有 {5,3,2},棧中的元素有 [8,4] 。
訪問棧頂 4,由于 2 和 4 沒有子節點。所以我們直接把棧頂中的 4 推出 。
推出的元素有 {5,3,2,4},棧中的元素有 [8] 。
訪問棧頂 8 把 8 推出棧堆,再把 8 的子節點 7、9 推入棧中,先推入 9 后推入 7 。
推出的元素有 {5,3,2,4,8},棧中的元素有 [9,7] 。
訪問 7,沒有子節點,推出。
推出的元素有 {5,3,2,4,8,7},棧中的元素有 [9] 。
訪問 9,沒有子節點,推出。
推出的元素有 {5,3,2,4,8,7,9},棧中的元素有 [] 。
5、中序遍歷中序遍歷,訪問順序是左根右,也就是先訪問左子樹,再到根節點,最后才到右子樹。所以上圖所示的訪問順序是 2、3、4、5、7、8、9。
二分搜索樹中序遍歷遞歸版與非遞歸版
//遞歸調用 public void inOrder(){ inOrder(root); } //二分搜索樹的中序遍歷遞歸寫法 private void inOrder(Node root){ if (root == null) return; inOrder(root.left); System.out.println(root.e); inOrder(root.right); } //二分搜索樹中序遍歷給遞歸寫法 public void preInOrderNG(){ // 創建棧,和前序遍歷類似 Stackstack = new Stack<>(); Node node = root; //添加暫時完畢,開始pop元素 while(node!=null || stack.size()>0 ){ while(node!=null){ stack.push(node); node = node.left; } //一邊pop并且一邊進行判斷,右結點不會null的,右子樹,繼續按照添加方法,將最左結點全部添加進去 if(stack.size()>0){ Node pop = stack.pop(); System.out.print(pop.e+" "); if(pop.right!=null){ node = pop.right; } } }
理解非遞歸的實現邏輯、推導出中序遞歸的實現
首先我們把 5 這個節點推入棧中,再把 5 的左子節點 3 推入,再把 3的 左子節點 2 推入,再把 2 的左子節點推入(此時 2 的左子節點為空,node==null while 循環退出)。
推出的元素有 {},棧中的元素有 [5,3,2]。
node 為空,但我們棧中還有元素,訪問棧頂元素 2,并查看 2 是否有右子節點。沒有則推出棧并結束循環。
推出的元素有 {2},棧中的元素有 [5,3]。
訪問棧頂元素 3,把 3 推出棧中,并把 3 的右子節點 4 推入棧中,結束循環。
推出的元素有 {2,3},棧中的元素有 [5]。
訪問棧頂元素5,把5推出棧中。把5的右子節點8推入棧中,并把8的左子節點7推入棧中,結束循環。
推出的元素有 {2,3,5},棧中的元素有 [8,7]
訪問棧頂元素 7,并查看 2 是否有右子節點。沒有則推出棧并結束循環。
推出的元素有 {2,3,5,7},棧中的元素有 [8]。
訪問棧頂元素 8,把 8 推出棧中。把 8 的右子節點 9 推入棧中
推出的元素有 {2,3,5,7,8},棧中的元素有 [9]。
訪問棧頂元素 9,并查看 2 是否有右子節點。沒有則推出棧并結束循環。
推出的元素有 {2,3,4,5,7,8,9},棧中的元素有 []。
6、后序遍歷中序遍歷,訪問順序是左右根,也就是先訪問左子樹,再到右子樹,最后才到根節點。所以上圖所示的訪問順序是 2、4、3、7、9、8、5。
二分搜索樹后序遍歷遞歸版與非遞歸版
//遞歸調用 public void postOrder() { postOrder(root); } //二分搜索樹的后序遍歷遞歸方法 private void postOrder(Node node){ if (node == null) return; postOrder(node.left); postOrder(node.right); System.out.println(node.e); } public void postOrderNG(){ Stackstack = new Stack<>(); //利用一個list集合記錄已將被遍歷過的根節點,防止產生死循環 ArrayList list = new ArrayList<>(); Node node = root; Node proud; int flag; //首頁檢查完樹的左子樹,再右子數,最后將根節點輸出 while (node != null || stack.size() > 0){ //將最左子樹添加完畢 while (node != null){ stack.push(node); node = node.left; } //和中序遍歷相似,為先輸出左子節點,但是做節點輸出完畢之后,不能直接將根節點彈出,而是必須先將右節點彈出, //最后再將根節點彈出來,就會牽扯到一個根節點的訪問狀態的問題,是否已經被遍歷過了 if (stack.size() > 0){ Node peek = stack.peek(); if (peek.right != null){ boolean con = list.contains(peek); if (con){ Node pop = stack.pop(); System.out.println(pop.e); }else{ list.add(peek); node = peek.right; } }else { Node pop = stack.pop(); System.out.println(pop.e); } } } }
理解非遞歸的實現邏輯、推導出后序遞歸的實現
把 5 這個節點推入棧中,再把 5 的左子節點 3 推入,再把 3 的左子節點 2 推入,再把 2 的左子節點推入(此時 2 的左子節點為空,node==null while 循環退出)。
推出的元素有 {},棧中的元素有 [5,3,2]。
node 為空但我們棧中還有元素,訪問棧頂元素 2,并查看 2 是否有左子節點。沒有則推出棧并結束循環。
推出的元素有 {2},棧中的元素有 [5,3]。
訪問棧頂元素 3,3 的右子節為 4,判斷 list 中是否有 3,沒有則把 3 放入 list 中并把 node 賦值為 4 結束循環。
推出的元素有 {2},棧中的元素有 [5,3]。
node 為 4,把 4 推入棧中,并訪問棧頂元素 4,并查看 4 是否有右子節點。沒有則推出棧并結束循環。
推出的元素有 {2,4},棧中的元素有 [5,3]。
訪問棧頂元素 3,list 中有 3,把 3 的推出棧中并結束循環。
推出的元素有 {2,4,3},棧中的元素有 [5]。
訪問棧頂元素 5,5 的右子節為 8,判斷 lis t中是否有 8,沒有則把 5 放入 list 中并把 node 賦值為 8 結束循環。
推出的元素有 {2,4,3},棧中的元素有 [5]。
node 為 8,把 8 推入棧中,并訪問棧頂元 素8,8 有左子節點為 7。把 7 推入棧中。
推出的元素有 {2,4,3},棧中的元素有 [5,8,7]。
訪問棧頂元素 7,并查看 7 是否有右子節點。沒有則推出棧并結束循環。
推出的元素有 {2,4,3,7},棧中的元素有 [5,8]。
訪問棧頂元素 8,8 的右子節點為 9。判斷 list 中是否有 9,沒有則把 8 放入 list 中并把 node 并把 node 賦值為 9 結束循環。
推出的元素有 {2,4,3,7},棧中的元素有 [5,8]。
node 為 9,把 9 推入棧中,并訪問棧頂元素 9,并查看 9 是否有右子節點。沒有則推出棧并結束循環。
推出的元素有 {2,4,3,7,9},棧中的元素有 [5,8]。
訪問棧頂元素 8,list 中有 8,把 8 的推出棧中并結束循環。
推出的元素有 {2,4,3,7,9,8},棧中的元素有 [5]。
node 為空棧中還有元素,訪問棧頂元素 5,list 中有 5,把 5 的推出棧中并結束循環。
推出的元素有 {2,4,3,7,9,8,5},棧中的元素有 []。
推薦閱讀:
1、java | 什么是動態代理
2、SpringBoot?| 啟動原理
3、SpringBoot | 自動配置原理
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/76286.html
摘要:在數據結構領域對應樹結構來說二叉樹是最常用的一種樹結構,二叉樹具有一個唯一的根節點,也就是最上面的節點。二叉樹每個節點最多有兩個孩子,一個孩子都沒有的節點通常稱之為葉子節點,二叉樹每個節點最多有一個父親,根節點是沒有父親節點的。 showImg(https://segmentfault.com/img/remote/1460000018597053?w=1832&h=9943); 前言...
摘要:在數據結構領域對應樹結構來說二叉樹是最常用的一種樹結構,二叉樹具有一個唯一的根節點,也就是最上面的節點。二叉樹每個節點最多有兩個孩子,一個孩子都沒有的節點通常稱之為葉子節點,二叉樹每個節點最多有一個父親,根節點是沒有父親節點的。 showImg(https://segmentfault.com/img/remote/1460000018597053?w=1832&h=9943); 前言...
摘要:我理解的數據結構五二分搜索樹一二叉樹和鏈表一樣,動態數據結構具有唯一根節點每個節點最多有兩個子節點每個節點最多有一個父節點具有天然的遞歸結構每個節點的左子樹也是二叉樹每個節點的右子樹也是二叉樹一個節點或者空也是二叉樹二二分搜索樹是二叉樹每個 我理解的數據結構(五)—— 二分搜索樹(Binary Search Tree) 一、二叉樹 和鏈表一樣,動態數據結構 具有唯一根節點 每個節點最...
摘要:我理解的數據結構五二分搜索樹一二叉樹和鏈表一樣,動態數據結構具有唯一根節點每個節點最多有兩個子節點每個節點最多有一個父節點具有天然的遞歸結構每個節點的左子樹也是二叉樹每個節點的右子樹也是二叉樹一個節點或者空也是二叉樹二二分搜索樹是二叉樹每個 我理解的數據結構(五)—— 二分搜索樹(Binary Search Tree) 一、二叉樹 和鏈表一樣,動態數據結構 具有唯一根節點 每個節點最...
摘要:我們現在來看二分搜索算法的兩種變形插值搜索和指數搜索。插值搜索是對二分搜索算法的改進,插值搜索可以基于搜索的值選擇到達不同的位置。 預警 在本篇文章中,將為各位老鐵介紹不同的搜索算法以及它們的復雜度。因為力求通俗易懂,所以篇幅可能較長,大伙可以先Mark下來,每天抽時間看一點理解一點。本文配套的Github Repo,歡迎各位老鐵star,會一直更新的。 開篇 和排序類似,搜索或者叫做...
閱讀 916·2023-04-25 18:51
閱讀 1867·2021-09-09 11:39
閱讀 3280·2019-08-30 15:53
閱讀 2096·2019-08-30 13:03
閱讀 1308·2019-08-29 16:17
閱讀 577·2019-08-29 11:33
閱讀 1882·2019-08-26 14:00
閱讀 2123·2019-08-26 13:41