算法系列15天速成 第六天 五大經(jīng)典查找【下】
就拿我們前幾天學(xué)過的排序就用到了堆和今天講的”二叉排序樹“,所以偏激的說,掌握的樹你就是牛人了。
今天就聊聊這個”五大經(jīng)典查找“中的最后一個”二叉排序樹“。
1. 概念:
<1> 其實很簡單,若根節(jié)點(diǎn)有左子樹,則左子樹的所有節(jié)點(diǎn)都比根節(jié)點(diǎn)小。
若根節(jié)點(diǎn)有右子樹,則右子樹的所有節(jié)點(diǎn)都比根節(jié)點(diǎn)大。
<2> 如圖就是一個”二叉排序樹“,然后對照概念一比較比較。
2.實際操作:
我們都知道,對一個東西進(jìn)行操作,無非就是增刪查改,接下來我們就聊聊其中的基本操作。
<1> 插入:相信大家對“排序樹”的概念都清楚了吧,那么插入的原理就很簡單了。
比如說我們插入一個20到這棵樹中。
首先:20跟50比,發(fā)現(xiàn)20是老小,不得已,得要?dú)w結(jié)到50的左子樹中去比較。
然后:20跟30比,發(fā)現(xiàn)20還是老小。
再然后:20跟10比,發(fā)現(xiàn)自己是老大,隨即插入到10的右子樹中。
最后: 效果呈現(xiàn)圖如下:
<2>查找:相信懂得了插入,查找就跟容易理解了。
就拿上面一幅圖來說,比如我想找到節(jié)點(diǎn)10.
首先:10跟50比,發(fā)現(xiàn)10是老小,則在50的左子樹中找。
然后:10跟30比,發(fā)現(xiàn)還是老小,則在30的左子樹中找。
再然后: 10跟10比,發(fā)現(xiàn)一樣,然后就返回找到的信號。
<3>刪除:刪除節(jié)點(diǎn)在樹中還是比較麻煩的,主要有三種情況。
《1》 刪除的是“葉節(jié)點(diǎn)20“,這種情況還是比較簡單的,刪除20不會破壞樹的結(jié)構(gòu)。如圖:
《2》刪除”單孩子節(jié)點(diǎn)90“,這個情況相比第一種要麻煩一點(diǎn)點(diǎn),需要把他的孩子頂上去。
《3》刪除“左右孩子都有的節(jié)點(diǎn)50”,這個讓我在代碼編寫上糾結(jié)了好長時間,問題很直白,
我把50刪掉了,誰頂上去了問題,是左孩子呢?還是右孩子呢?還是另有蹊蹺?這里我就
坦白吧,不知道大家可否知道“二叉樹”的中序遍歷,不過這個我會在后面講的,現(xiàn)在可以當(dāng)
公式記住吧,就是找到右節(jié)點(diǎn)的左子樹最左孩子。
比如:首先 找到50的右孩子70。
然后 找到70的最左孩子,發(fā)現(xiàn)沒有,則返回自己。
最后 原始圖和最終圖如下。
3.說了這么多,上代碼說話。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
namespace TreeSearch
{
class Program
{
static void Main(string[] args)
{
List<int> list = new List<int>() { 50, 30, 70, 10, 40, 90, 80 };
//創(chuàng)建二叉遍歷樹
BSTree bsTree = CreateBST(list);
Console.Write("中序遍歷的原始數(shù)據(jù):");
//中序遍歷
LDR_BST(bsTree);
Console.WriteLine("\n---------------------------------------------------------------------------n");
//查找一個節(jié)點(diǎn)
Console.WriteLine("\n10在二叉樹中是否包含:" + SearchBST(bsTree, 10));
Console.WriteLine("\n---------------------------------------------------------------------------n");
bool isExcute = false;
//插入一個節(jié)點(diǎn)
InsertBST(bsTree, 20, ref isExcute);
Console.WriteLine("\n20插入到二叉樹,中序遍歷后:");
//中序遍歷
LDR_BST(bsTree);
Console.WriteLine("\n---------------------------------------------------------------------------n");
Console.Write("刪除葉子節(jié)點(diǎn) 20, \n中序遍歷后:");
//刪除一個節(jié)點(diǎn)(葉子節(jié)點(diǎn))
DeleteBST(ref bsTree, 20);
//再次中序遍歷
LDR_BST(bsTree);
Console.WriteLine("\n****************************************************************************\n");
Console.WriteLine("刪除單孩子節(jié)點(diǎn) 90, \n中序遍歷后:");
//刪除單孩子節(jié)點(diǎn)
DeleteBST(ref bsTree, 90);
//再次中序遍歷
LDR_BST(bsTree);
Console.WriteLine("\n****************************************************************************\n");
Console.WriteLine("刪除根節(jié)點(diǎn) 50, \n中序遍歷后:");
//刪除根節(jié)點(diǎn)
DeleteBST(ref bsTree, 50);
LDR_BST(bsTree);
}
///<summary>
/// 定義一個二叉排序樹結(jié)構(gòu)
///</summary>
public class BSTree
{
public int data;
public BSTree left;
public BSTree right;
}
///<summary>
/// 二叉排序樹的插入操作
///</summary>
///<param name="bsTree">排序樹</param>
///<param name="key">插入數(shù)</param>
///<param name="isExcute">是否執(zhí)行了if語句</param>
static void InsertBST(BSTree bsTree, int key, ref bool isExcute)
{
if (bsTree == null)
return;
//如果父節(jié)點(diǎn)大于key,則遍歷左子樹
if (bsTree.data > key)
InsertBST(bsTree.left, key, ref isExcute);
else
InsertBST(bsTree.right, key, ref isExcute);
if (!isExcute)
{
//構(gòu)建當(dāng)前節(jié)點(diǎn)
BSTree current = new BSTree()
{
data = key,
left = null,
right = null
};
//插入到父節(jié)點(diǎn)的當(dāng)前元素
if (bsTree.data > key)
bsTree.left = current;
else
bsTree.right = current;
isExcute = true;
}
}
///<summary>
/// 創(chuàng)建二叉排序樹
///</summary>
///<param name="list"></param>
static BSTree CreateBST(List<int> list)
{
//構(gòu)建BST中的根節(jié)點(diǎn)
BSTree bsTree = new BSTree()
{
data = list[0],
left = null,
right = null
};
for (int i = 1; i < list.Count; i++)
{
bool isExcute = false;
InsertBST(bsTree, list[i], ref isExcute);
}
return bsTree;
}
///<summary>
/// 在排序二叉樹中搜索指定節(jié)點(diǎn)
///</summary>
///<param name="bsTree"></param>
///<param name="key"></param>
///<returns></returns>
static bool SearchBST(BSTree bsTree, int key)
{
//如果bsTree為空,說明已經(jīng)遍歷到頭了
if (bsTree == null)
return false;
if (bsTree.data == key)
return true;
if (bsTree.data > key)
return SearchBST(bsTree.left, key);
else
return SearchBST(bsTree.right, key);
}
///<summary>
/// 中序遍歷二叉排序樹
///</summary>
///<param name="bsTree"></param>
///<returns></returns>
static void LDR_BST(BSTree bsTree)
{
if (bsTree != null)
{
//遍歷左子樹
LDR_BST(bsTree.left);
//輸入節(jié)點(diǎn)數(shù)據(jù)
Console.Write(bsTree.data + "");
//遍歷右子樹
LDR_BST(bsTree.right);
}
}
///<summary>
/// 刪除二叉排序樹中指定key節(jié)點(diǎn)
///</summary>
///<param name="bsTree"></param>
///<param name="key"></param>
static void DeleteBST(ref BSTree bsTree, int key)
{
if (bsTree == null)
return;
if (bsTree.data == key)
{
//第一種情況:葉子節(jié)點(diǎn)
if (bsTree.left == null && bsTree.right == null)
{
bsTree = null;
return;
}
//第二種情況:左子樹不為空
if (bsTree.left != null && bsTree.right == null)
{
bsTree = bsTree.left;
return;
}
//第三種情況,右子樹不為空
if (bsTree.left == null && bsTree.right != null)
{
bsTree = bsTree.right;
return;
}
//第四種情況,左右子樹都不為空
if (bsTree.left != null && bsTree.right != null)
{
var node = bsTree.right;
//找到右子樹中的最左節(jié)點(diǎn)
while (node.left != null)
{
//遍歷它的左子樹
node = node.left;
}
//交換左右孩子
node.left = bsTree.left;
//判斷是真正的葉子節(jié)點(diǎn)還是空左孩子的父節(jié)點(diǎn)
if (node.right == null)
{
//刪除掉右子樹最左節(jié)點(diǎn)
DeleteBST(ref bsTree, node.data);
node.right = bsTree.right;
}
//重新賦值一下
bsTree = node;
}
}
if (bsTree.data > key)
{
DeleteBST(ref bsTree.left, key);
}
else
{
DeleteBST(ref bsTree.right, key);
}
}
}
}
運(yùn)行結(jié)果:
值的注意的是:二叉排序樹同樣采用“空間換時間”的做法。
突然發(fā)現(xiàn),二叉排序樹的中序遍歷同樣可以排序數(shù)組,呵呵,不錯!
PS: 插入操作:O(LogN)。
刪除操作:O(LogN)。
查找操作:O(LogN)。
- 算法系列15天速成 第十四天 圖【上】
- 算法系列15天速成——第十三天 樹操作【下】
- 算法系列15天速成 第十二天 樹操作【中】
- 算法系列15天速成 第十一天 樹操作(上)
- 算法系列15天速成 第十天 棧
- 算法系列15天速成 第八天 線性表【下】
- 算法系列15天速成 第九天 隊列
- 算法系列15天速成 第七天 線性表【上】
- 算法系列15天速成 第五天 五大經(jīng)典查找【中】
- 算法系列15天速成 第四天 五大經(jīng)典查找【上】
- 算法系列15天速成 第三天 七大經(jīng)典排序【下】
- 算法系列15天速成 第二天 七大經(jīng)典排序【中】
- 算法系列15天速成 第一天 七大經(jīng)典排序【上】
- 算法系列15天速成——第十五天 圖【下】(大結(jié)局)
相關(guān)文章
MAC系統(tǒng)IDEA顏值插件MaterialThemeUI
俗話說,工欲善其事必先利其器。工具的顏值也很重要,好的主題讓人賞心悅目,有碼代碼的欲望。今天推薦一個IDEA顏值類插件:Material Theme UI2021-09-09vscode配置leetcode插件并解決無法登錄問題(圖文詳解)
這篇文章主要介紹了vscode配置leetcode插件并解決無法登錄問題,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-06-06搜索歷史基本原理實現(xiàn)即時自動補(bǔ)全聯(lián)想搜索技巧
這篇文章主要為大家介紹了搜索歷史基本原理實現(xiàn)即時自動補(bǔ)全聯(lián)想搜索技巧示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-02-02tcp、udp、ip協(xié)議分析_動力節(jié)點(diǎn)Java學(xué)院整理
這篇文章主要為大家詳細(xì)介紹了tcp、udp、ip協(xié)議分析的相關(guān)資料,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-07-07vscode+picgo+github配置免費(fèi)圖床(圖文教程)
本文主要介紹了vscode+picgo+github配置免費(fèi)圖床,文中通過圖文介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-01-01