亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

js數(shù)據(jù)結(jié)構(gòu)-二叉樹(二叉搜索樹)

codeKK / 794人閱讀

摘要:前言可能有一部分人沒(méi)有讀過(guò)我上一篇寫的二叉堆,所以這里把二叉樹的基本概念復(fù)制過(guò)來(lái)了,如果讀過(guò)的人可以忽略前面針對(duì)二叉樹基本概念的介紹,另外如果對(duì)鏈表數(shù)據(jù)結(jié)構(gòu)不清楚的最好先看一下本人之前寫的數(shù)據(jù)結(jié)構(gòu)鏈表二叉樹二叉樹是一種樹形結(jié)構(gòu),它的特點(diǎn)是

前言

可能有一部分人沒(méi)有讀過(guò)我上一篇寫的二叉堆,所以這里把二叉樹的基本概念復(fù)制過(guò)來(lái)了,如果讀過(guò)的人可以忽略前面針對(duì)二叉樹基本概念的介紹,另外如果對(duì)鏈表數(shù)據(jù)結(jié)構(gòu)不清楚的最好先看一下本人之前寫的js數(shù)據(jù)結(jié)構(gòu)-鏈表

二叉樹

二叉樹(Binary Tree)是一種樹形結(jié)構(gòu),它的特點(diǎn)是每個(gè)節(jié)點(diǎn)最多只有兩個(gè)分支節(jié)點(diǎn),一棵二叉樹通常由根節(jié)點(diǎn),分支節(jié)點(diǎn),葉子節(jié)點(diǎn)組成。而每個(gè)分支節(jié)點(diǎn)也常常被稱作為一棵子樹。

根節(jié)點(diǎn):二叉樹最頂層的節(jié)點(diǎn)

分支節(jié)點(diǎn):除了根節(jié)點(diǎn)以外且擁有葉子節(jié)點(diǎn)

葉子節(jié)點(diǎn):除了自身,沒(méi)有其他子節(jié)點(diǎn)

常用術(shù)語(yǔ)
在二叉樹中,我們常常還會(huì)用父節(jié)點(diǎn)和子節(jié)點(diǎn)來(lái)描述,比如圖中2為6和3的父節(jié)點(diǎn),反之6和3是2子節(jié)點(diǎn)

二叉樹的三個(gè)性質(zhì)

在二叉樹的第i層上,至多有2^i-1個(gè)節(jié)點(diǎn)

i=1時(shí),只有一個(gè)根節(jié)點(diǎn),2^(i-1) = 2^0 = 1

深度為k的二叉樹至多有2^k-1個(gè)節(jié)點(diǎn)

i=2時(shí),2^k-1 = 2^2 - 1 = 3個(gè)節(jié)點(diǎn)

對(duì)任何一棵二叉樹T,如果總結(jié)點(diǎn)數(shù)為n0,度為2(子樹數(shù)目為2)的節(jié)點(diǎn)數(shù)為n2,則n0=n2+1

樹和二叉樹的三個(gè)主要差別

樹的節(jié)點(diǎn)個(gè)數(shù)至少為1,而二叉樹的節(jié)點(diǎn)個(gè)數(shù)可以為0

樹中節(jié)點(diǎn)的最大度數(shù)(節(jié)點(diǎn)數(shù)量)沒(méi)有限制,而二叉樹的節(jié)點(diǎn)的最大度數(shù)為2

樹的節(jié)點(diǎn)沒(méi)有左右之分,而二叉樹的節(jié)點(diǎn)有左右之分

二叉樹分類

二叉樹分為完全二叉樹(complete binary tree)和滿二叉樹(full binary tree)

滿二叉樹:一棵深度為k且有2^k - 1個(gè)節(jié)點(diǎn)的二叉樹稱為滿二叉樹

完全二叉樹:完全二叉樹是指最后一層左邊是滿的,右邊可能滿也可能不滿,然后其余層都是滿的二叉樹稱為完全二叉樹(滿二叉樹也是一種完全二叉樹)

二叉搜索樹

二叉搜索樹滿足以下的幾個(gè)性質(zhì):

若任意節(jié)點(diǎn)的左子樹不空,則左子樹上所有節(jié)點(diǎn)的值均小于它的根節(jié)點(diǎn)的值;

若任意節(jié)點(diǎn)的右子樹不空,則右子樹上所有節(jié)點(diǎn)的值均大于它的根節(jié)點(diǎn)的值;

任意節(jié)點(diǎn)的左、右子樹也需要滿足左邊小右邊大的性質(zhì)

我們來(lái)舉個(gè)例子來(lái)深入理解以下

一組數(shù)據(jù):12,4,18,1,8,16,20
由下圖可以看出,左邊的圖滿足了二叉樹的性質(zhì),它的每個(gè)左子節(jié)點(diǎn)都小于父節(jié)點(diǎn),右子節(jié)點(diǎn)大于其父節(jié)點(diǎn),同時(shí)左子樹的節(jié)點(diǎn)都小于根節(jié)點(diǎn),右子樹的節(jié)點(diǎn)都大于根節(jié)點(diǎn)

二叉搜索樹主要的幾個(gè)操作:

查找(search)

插入(insert)

遍歷(transverse)

二叉樹搜索樹的鏈?zhǔn)酱鎯?chǔ)結(jié)構(gòu)

通過(guò)下圖,可以知道二叉搜索樹的節(jié)點(diǎn)通常包含4個(gè)域,數(shù)據(jù)元素,分別指向其左,右節(jié)點(diǎn)的指針和一個(gè)指向父節(jié)點(diǎn)的指針?biāo)鶚?gòu)成,一般把這種存儲(chǔ)結(jié)構(gòu)稱為三叉鏈表。

用代碼初始化一個(gè)二叉搜索樹的結(jié)點(diǎn):

一個(gè)指向父親節(jié)點(diǎn)的指針 parent

一個(gè)指向左節(jié)點(diǎn)的指針 left

一個(gè)指向右節(jié)點(diǎn)的指針 right

一個(gè)數(shù)據(jù)元素,里面可以是一個(gè)key和value

    class BinaryTreeNode {
        constructor(key, value){
            this.parent = null;
            this.left = null;
            this.right = null;
            this.key = key;
            this.value = value;
        }
    }

接著我們?cè)儆么a去初始化一個(gè)二叉搜索樹

在二叉搜索樹中我們會(huì)維護(hù)一個(gè)root指針,這個(gè)就相當(dāng)于鏈表中的head指針,在沒(méi)有任何節(jié)點(diǎn)插入的時(shí)候它指向空,在有節(jié)點(diǎn)插入以后它指向根節(jié)點(diǎn)。

    class BinarySearchTree {
        constructor() {
            this.root = null;
        }
    }
創(chuàng)建節(jié)點(diǎn)
    static createNode(key, value) {
        return new BinarySearchTree(key, value);
    }
插入操作

看下面這張圖,13是我們要插入的節(jié)點(diǎn),它插入的具體步驟:

跟根節(jié)點(diǎn)12做比較,比12大,所以我們確定了,這個(gè)節(jié)點(diǎn)是往右子樹插入的

而根節(jié)點(diǎn)的右邊已經(jīng)有節(jié)點(diǎn),那么跟這個(gè)節(jié)點(diǎn)18做比較,結(jié)果小于18所以往18的左節(jié)點(diǎn)找位置

而18的左節(jié)點(diǎn)也已經(jīng)有節(jié)點(diǎn)了,所以繼續(xù)跟這個(gè)節(jié)點(diǎn)做比較,結(jié)果小于16

剛好16的左節(jié)點(diǎn)是空的(left=null),所以13這個(gè)節(jié)點(diǎn)就插入到了16的左節(jié)點(diǎn)

通過(guò)上面的描述,我們來(lái)看看代碼是怎么寫的

定義兩個(gè)指針,分別是p和tail,最初都指向root,p是用來(lái)指向要插入的位置的父節(jié)點(diǎn)的指針,而tail是用來(lái)查找插入位置的,所以最后它會(huì)指向null,用上圖舉個(gè)例子,p最后指向了6這個(gè)節(jié)點(diǎn),而tail最后指向了null(tail為null則說(shuō)明已經(jīng)找到了要插入的位置)

循環(huán),tail根據(jù)我們上面分析的一步一步往下找位置插入,如果比當(dāng)前節(jié)點(diǎn)小就往左找,大則往右找,一直到tail找到一個(gè)空位置也就是null

如果當(dāng)前的root為null,則說(shuō)明當(dāng)前結(jié)構(gòu)中并沒(méi)有節(jié)點(diǎn),所以插入的第一個(gè)節(jié)點(diǎn)直接為跟節(jié)點(diǎn),即this.root = node

將插入后的節(jié)點(diǎn)的parent指針指向父節(jié)點(diǎn)

    insert(node){
        let p = this.root;
        let tail = this.root;
        
        // 循環(huán)遍歷,去找到對(duì)應(yīng)的位置
        while(tail) {
            p = tail;
            // 要插入的節(jié)點(diǎn)key比當(dāng)前節(jié)點(diǎn)小
            if (node.key < tail.key){
                tail = tail.left;
            }
            // 要插入的節(jié)點(diǎn)key比當(dāng)前節(jié)點(diǎn)大
            else {
                tail = tail.right;
            }
        }
        
        // 沒(méi)有根節(jié)點(diǎn),則直接作為根節(jié)點(diǎn)插入
        if(!p) {
            this.root = node;
            return;
        }
        
        // p是最后一個(gè)節(jié)點(diǎn),也就是我們要插入的位置的父節(jié)點(diǎn)
        // 比父節(jié)點(diǎn)大則往右邊插入
        if(p.key < node.key){
            p.right = node;
        }
        // 比父節(jié)點(diǎn)小則往左邊插入
        else {
            p.left = node;
        }
        
        // 指向父節(jié)點(diǎn)
        node.parent = p;

    }
查找

查找就很簡(jiǎn)單了,其實(shí)和插入差多,都是去別叫左右節(jié)點(diǎn)的大小,然后往下找

如果root = null, 則二叉樹中沒(méi)有任何節(jié)點(diǎn),直接return,或者報(bào)個(gè)錯(cuò)什么的。

循環(huán)查找

    search(key) {
        let p = this.root;
        if(!p) {
            return;
        }
        
        while(p && p.key !== key){
            if(p.key
遍歷

中序遍歷(inorder):先遍歷左節(jié)點(diǎn),再遍歷自己,最后遍歷右節(jié)點(diǎn),輸出的剛好是有序的列表

前序遍歷(preorder):先自己,再遍歷左節(jié)點(diǎn),最后遍歷右節(jié)點(diǎn)

后序遍歷(postorder):先左節(jié)點(diǎn),再右節(jié)點(diǎn),最后自己

最常用的一般是中序遍歷,因?yàn)橹行虮闅v可以得到一個(gè)已經(jīng)排好序的列表,這也是為什么會(huì)用二叉搜索樹排序的原因

根據(jù)上面對(duì)中序遍歷的解釋,那么代碼就變的很簡(jiǎn)單,就是一個(gè)遞歸的過(guò)程,遞歸停止的條件就是節(jié)點(diǎn)為null

先遍歷左節(jié)點(diǎn)-->yield* this._transverse(node.left)

遍歷自己 --> yield* node

遍歷左節(jié)點(diǎn) --> yield* this._transverse(node.right)

    transverse() {
        return this._transverse(this.root);
    }
    
    *_transverse(node){
        if(!node){
            return;
        }
        yield* this._transverse(node.left);
        yield node;
        yield* this._transverse(node.right)
    }


看上面這張圖,我們簡(jiǎn)化的來(lái)看一下,先訪問(wèn)左節(jié)點(diǎn)4,再自己12,然后右節(jié)點(diǎn)18,這樣輸出的就剛好是一個(gè)4,12,18

補(bǔ)充:這個(gè)地方用了generater,所以返回的一個(gè)迭代器??梢酝ㄟ^(guò)下面這種方式得到一個(gè)有序的數(shù)組,這里的前提就當(dāng)是已經(jīng)有插入的節(jié)點(diǎn)了

   const tree = new BinaryTree();
   //...中間省略插入過(guò)程
    
   // 這樣就返回了一個(gè)有序的數(shù)組 
   var arr = [...tree.transverse()].map(item=>item.key);
完整代碼
class BinaryTreeNode {
  constructor(key, value) {
    // 指向父節(jié)點(diǎn)
    this.p = null;

    // 左節(jié)點(diǎn)
    this.left = null;

    // 右節(jié)點(diǎn)
    this.right = null;

    // 鍵
    this.key = key;

    // 值
    this.value = value;
  }
}

class BinaryTree {
  constructor() {
    this.root = null;
  }

  static createNode(key, value) {
    return new BinaryTreeNode(key, value);
  }

  search(key) {
    let p = this.root;
    if (!p) {
      return;
    }

    while (p && p.key !== key) {
      if (p.key < key) {
        p = p.right;
      } else {
        p = p.left;
      }
    }

    return p;
  }

  insert(node) {
    // 尾指針的父節(jié)點(diǎn)指針
    let p = this.root;

    // 尾指針
    let tail = this.root;

    while (tail) {
      p = tail;
      if (node.key < tail.key) {
        tail = tail.left;
      } else {
        tail = tail.right;
      }
    }

    if (!p) {
      this.root = node;
      return;
    }

    // 插入
    if (p.key < node.key) {
      p.right = node;
    } else {
      p.left = node;
    }

    node.p = p;
  }

  transverse() {
    return this.__transverse(this.root);
  }

  *__transverse(node) {
    if (!node) {
      return;
    }
    yield* this.__transverse(node.left);
    yield node;
    yield* this.__transverse(node.right);
  }
}
總結(jié)

二叉查找樹就講完了哈,其實(shí)這個(gè)和鏈表很像的,還是操作那么幾個(gè)指針,既然叫查找樹了,它主要還是用來(lái)左一些搜索,還有就是排序了,另外補(bǔ)充一下,二叉查找樹里找最大值和最小值也很方便是不是,如果你大致讀懂了的話。
這篇文章我寫的感覺(jué)有點(diǎn)亂誒,因?yàn)榭偢杏X(jué)哪里介紹的不到位,讓一些基礎(chǔ)差的人會(huì)看不懂,如果有不懂或者文章哪里寫錯(cuò)了,歡迎評(píng)論留言哈

后續(xù)

后續(xù)寫什么呢,這個(gè)問(wèn)題我也在想,排序算法,react第三方的一些模擬實(shí)現(xiàn)?,做個(gè)小程序組件庫(kù)?還是別的,容我再想幾個(gè)小時(shí),因?yàn)榭梢?,有建議的朋友們也可以留言說(shuō)一下哈。
最后最后,最重要的請(qǐng)給個(gè)贊,請(qǐng)粉一個(gè)呢,謝謝啦

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/100812.html

相關(guān)文章

  • JS數(shù)據(jù)結(jié)構(gòu)與算法_

    摘要:上一篇數(shù)據(jù)結(jié)構(gòu)與算法集合字典一遞歸學(xué)習(xí)樹離不開遞歸。先序遍歷的一種應(yīng)用是打印一個(gè)結(jié)構(gòu)化的文檔下面的圖描繪了先序遍歷方法的訪問(wèn)路徑后序遍歷后序遍歷則是先訪問(wèn)節(jié)點(diǎn)的后代節(jié)點(diǎn),再訪問(wèn)節(jié)點(diǎn)本身。 上一篇:JS數(shù)據(jù)結(jié)構(gòu)與算法_集合&字典 一、遞歸 學(xué)習(xí)樹離不開遞歸。 1.1 介紹 遞歸是一種解決問(wèn)題的方法,它解決問(wèn)題的各個(gè)小部分,直到解決最初的大問(wèn)題。遞歸通常涉及函數(shù)調(diào)用自身。 通俗的解釋:年級(jí)...

    tabalt 評(píng)論0 收藏0
  • 數(shù)據(jù)結(jié)構(gòu)與算法——(下)

    摘要:所以,如果中序遍歷二叉搜索樹,會(huì)得到一個(gè)有序的數(shù)據(jù),時(shí)間復(fù)雜度是,所以二叉搜索樹又叫做二叉排序樹。所以,我們需要一種方式來(lái)維持二叉樹的平衡,最好是將其維持為滿二叉樹或者完全二叉樹,這就是后面會(huì)說(shuō)到的平衡二叉查找樹,常見的有樹,紅黑樹。 1. 概述 前面的文章說(shuō)到了二叉樹,其實(shí)今天講的二叉搜索(查找)樹就是二叉樹最常用的一種形式,它支持高效的查找、插入、刪除操作,它的定義是這樣的:對(duì)于樹...

    Awbeci 評(píng)論0 收藏0
  • 【從蛋殼到滿天飛】JAVA 數(shù)據(jù)結(jié)構(gòu)解析和算法實(shí)現(xiàn)-二分搜索

    摘要:在數(shù)據(jù)結(jié)構(gòu)領(lǐng)域?qū)?yīng)樹結(jié)構(gòu)來(lái)說(shuō)二叉樹是最常用的一種樹結(jié)構(gòu),二叉樹具有一個(gè)唯一的根節(jié)點(diǎn),也就是最上面的節(jié)點(diǎn)。二叉樹每個(gè)節(jié)點(diǎn)最多有兩個(gè)孩子,一個(gè)孩子都沒(méi)有的節(jié)點(diǎn)通常稱之為葉子節(jié)點(diǎn),二叉樹每個(gè)節(jié)點(diǎn)最多有一個(gè)父親,根節(jié)點(diǎn)是沒(méi)有父親節(jié)點(diǎn)的。 showImg(https://segmentfault.com/img/remote/1460000018597053?w=1832&h=9943); 前言...

    ghnor 評(píng)論0 收藏0
  • 【從蛋殼到滿天飛】JAVA 數(shù)據(jù)結(jié)構(gòu)解析和算法實(shí)現(xiàn)-二分搜索

    摘要:在數(shù)據(jù)結(jié)構(gòu)領(lǐng)域?qū)?yīng)樹結(jié)構(gòu)來(lái)說(shuō)二叉樹是最常用的一種樹結(jié)構(gòu),二叉樹具有一個(gè)唯一的根節(jié)點(diǎn),也就是最上面的節(jié)點(diǎn)。二叉樹每個(gè)節(jié)點(diǎn)最多有兩個(gè)孩子,一個(gè)孩子都沒(méi)有的節(jié)點(diǎn)通常稱之為葉子節(jié)點(diǎn),二叉樹每個(gè)節(jié)點(diǎn)最多有一個(gè)父親,根節(jié)點(diǎn)是沒(méi)有父親節(jié)點(diǎn)的。 showImg(https://segmentfault.com/img/remote/1460000018597053?w=1832&h=9943); 前言...

    FuisonDesign 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<