主頁 > 軟體設計 > 為實習準備的資料結構(6)-- 伸展樹

為實習準備的資料結構(6)-- 伸展樹

2021-02-10 14:11:32 軟體設計

在這里插入圖片描述

文章目錄

    • 前言
    • 伸展樹
    • 自底向上旋轉
      • 更進一步:展開
        • 情況一:之字型(zig-zag)
        • 情況二:一字型(zig-zig)
        • 示例
      • 伸展樹的節點洗掉
    • 自頂向下伸展樹
      • zig(單旋轉)
      • zig-zig(一字型旋轉)
      • zig-zag(之字型旋轉)
      • 合并樹
      • 我一直沒看懂的示例
    • 自頂向下伸展樹代碼實作

前言

之前也寫過兩篇關于伸展樹的,一篇是概念,一篇是實作,
今天重溫一下,

回顧往昔,光陰似箭,日月如梭啊,


伸展樹

現在我們來介紹一種相對與AVL樹更簡單的資料結構,它叫伸展樹,它保證從空樹開始連續任意M次操作最多花費O(MlogN)時間,雖然這種保證并不排除任一次操作花費的時間為O(N),但是我們關注的是最終的結果,

伸展樹是基于這樣的事實:對于二叉查找樹來說,每次操作的最壞時間為O(N),并不壞,只要它不時常發生就行,

伸展樹的基本想法是:考慮到區域性原理(剛被訪問的內容下次可能仍會被訪問,查找次數多的內容可能下一次會被訪問),為了使整個查找時間更小,被查頻率高的那些節點應當經常處于靠近樹根的位置,這樣,很容易得想到以下這個方案:每次查找節點之后對樹進行重構,把被查找的節點搬移到樹根,這種自調整形式的二叉查找樹就是伸展樹,每次對伸展樹進行操作后,它均會通過旋轉的方法把被訪問節點旋轉到樹根的位置,

自底向上旋轉

旋轉方式參見為實習準備的資料結構(5)-- 圖解AVL樹(平衡二叉搜索樹)
實施上面描述的重新構造的一種方法是執行單旋轉,這意味著我們將在訪問路徑上的每一個節點和它們的父節點進行旋轉,
作為栗子,考慮在下面的樹中對k1進行一次訪問之后所發生的情況:
在這里插入圖片描述

一次旋轉之后:
在這里插入圖片描述

k1還沒到根,繼續轉:
在這里插入圖片描述

轉,再轉:
在這里插入圖片描述

好,轉完了,

可以看到,本來一棵長樹變成了近乎平衡的樹,
這些旋轉的效果是將k1不斷推向樹根,使得k1的進一步訪問很容易(沒被再推走之前),
不過缺點也很明顯,原先的帶頭大哥k3,百分之九十九也是剛剛坐上頭把交椅,屁股還沒坐熱就讓k1給踢到小角落去了,


更進一步:展開

展開的思路類似于前面介紹的旋轉的想法,不過在旋轉如何實施上我們稍微有一點選擇的余地,我們仍然從底部向上沿著訪問路徑旋轉,
令X是在訪問路徑上的一個非根節點,我們將在這個路徑上實施旋轉操作,如果X的父節點是根節點,那么我們只需要旋轉X和樹根,否則,X就有父親§和祖父(G),存在以上兩種情況以及對稱的情形要考慮(跟AVL樹差不多),

情況一:之字型(zig-zag)

也就是AVL樹里那倆要雙旋的,
在這里插入圖片描述

情況二:一字型(zig-zig)

也就是AVL樹里那倆只需要單旋的,
在這里插入圖片描述

注意甄別這次旋轉和之前旋轉的不同,更要看清楚和標準AVL單旋的差別,
這一次一字型旋轉,其中包含了兩次的AVL單旋

首先,對P做一次單旋
然后,對X做一次單旋

示例

來看個栗子,還是上面那個,k1
在這里插入圖片描述
展開的第一步是在k1,顯然是一個之字型,因此我們用k1,k2,k3執行一次標準的AVL雙旋轉,得到如下的樹:
在這里插入圖片描述
注意和上面的旋轉進行比對,

這一步轉完之后,迎接k1的是一個一字型,因此我們用k1,k4,k5來做一次一字型旋轉,注意看:
在這里插入圖片描述

雖然從一些小栗子上很難看出來,但是展開操作不僅將訪問節點移動到根處,而且還把訪問路徑上的大部分節點深度大致減少一半的效果(某些淺的節點最多向后推兩個層次),


伸展樹的節點洗掉

這個洗掉略微抽象了一點,簡而言之,就是:

  1. 訪問被洗掉節點,從而使得它被提到根節點,
  2. 洗掉該節點,整棵二叉樹被一分為二(一般是,除非你洗掉的節點比較特殊,比如最大節點或最小節點)
  3. 兩棵樹記為TL和TR
  4. 方法一:找到TL中的最大元素m,得益于二叉搜索樹的順序性,此時節點m的左子樹必然為空,這時只需要將TR接在m的右側即可,
  5. 方法二:方法一的鏡像,

自頂向下伸展樹

上面的操作,需要一次自頂向下的一次遍歷,而后自底向上的一次遍歷,這可以通過備忘錄模式來實作(自底向上需要沿途保存節點),不過這需要大量的開銷,而且也需要處理許多特殊情況,那么,接下來我們來講一下如何在初始訪問路徑上施行一些旋轉,結果得到在實踐中更快的程序,只用到O(1)的額外空間,但卻保持了O(logN)的攤還時間界,

在這里插入圖片描述

為了敘述的方便,上圖的右旋叫做X繞Y右旋,左旋叫做Y繞X左旋,

當我們沿著樹向下搜索某個節點X的時候,我們將搜索路徑上的節點及其子樹移走,我們構建兩棵臨時的樹──左樹和右樹,沒有被移走的節點構成的樹稱作中樹,在伸展操作的程序中:
1、當前節點X是中樹的根,
2、左樹L保存小于X的節點,
3、右樹R保存大于X的節點,
開始時候,X是樹T的根,左右樹L和R都是空的,

和自底向上一樣,自頂向下也分了三種情況,

zig(單旋轉)

在這里插入圖片描述

如上圖,在搜索到X的時候,所查找的節點比X小,將Y旋轉到中樹的樹根,旋轉之后,X及其右子樹被移動到右樹上,很顯然,右樹上的節點都大于所要查找的節點,注意X被放置在右樹的最小的位置,也就是X及其子樹比原先的右樹中所有的節點都要小,這是由于越是在路徑前面被移動到右樹的節點,其值越大,讀者可以分析一下樹的結構,原因很簡單,(就這句,給我點醒了)

通了一點之后,后面就好辦了,

zig-zig(一字型旋轉)

在這里插入圖片描述

在這種情況下,所查找的節點在Z的子樹中,也就是,所查找的節點比X和Y都小,所以要將X,Y及其右子樹都移動到右樹中,首先是Y繞X右旋,然后Z繞Y右旋,最后將Z的右子樹(此時Z的右子節點為Y)移動到右樹中,注意右樹中掛載點的位置,

zig-zag(之字型旋轉)

在這里插入圖片描述

在這里插入圖片描述

在這種情況中,首先將Y右旋到根,這和Zig的情況是一樣的,然后變成上圖右邊所示的形狀,接著,對Z進行左旋,將Y及其左子樹移動到左樹上,這樣,這種情況就被分成了兩個Zig情況,這樣,在編程的時候就會簡化,但是操作的數目增加(相當于兩次Zig情況),

合并樹

在這里插入圖片描述

將中樹的左右子樹分別連接到左樹的右子樹和右樹的左子樹上,將左右樹作為X的左右子樹,重新最成了一所查找的節點為根的樹,

我一直沒看懂的示例

下面是一個查找節點19的例子:
在例子中,樹中并沒有節點19,最后,距離節點最近的節點18被旋轉到了根作為新的根,節點20也是距離節點19最近的節點,但是節點20沒有成為新根,這和節點20在原來樹中的位置有關系,

而一直困擾我的,就是第二步到第三步的轉化,為什么要把20提上去,現在明白了,

在這里插入圖片描述

自頂向下伸展樹代碼實作

#include <stdlib.h>
#include <stdio.h>
	
int size;  /* number of nodes in the tree */
             /* Not actually needed for any of the operations */
typedef struct tree_node Tree;
struct tree_node
{
    Tree *left, *right;
    int item;
};

Tree *splay (int i, Tree *t)
{
 /* Simple top down splay, not requiring i to be in the tree t.  */
 /* What it does is described above.                             */
    Tree N, *l, *r, *y;
    if (t == NULL)
        return t;
    N.left = N.right = NULL;
    l = r = &N;
    for (;;)
    {
        if (i < t->item)
        {
            if (t->left == NULL)
                break;
            if (i < t->left->item)
            {
                y = t->left;                           /* rotate right */
                t->left = y->right;
                y->right = t;
                t = y;
                if (t->left == NULL)
                    break;
            }
            r->left = t;                               /* link right */
            r = t;
            t = t->left;
        }     
        else if (i > t->item)
        {    
            if (t->right == NULL)
				break;
            if (i > t->right->item)
            {
                y = t->right;                          /* rotate left */
                t->right = y->left;
                y->left = t;
                t = y;
                if (t->right == NULL)
                    break;
            }
            l->right = t;                              /* link left */
            l = t;
            t = t->right;
        }     
        else 
            break;
    }
    l->right = t->left;                                /* assemble */
    r->left = t->right;
    t->left = N.right;
    t->right = N.left;
    return t;
}
/* Here is how sedgewick would have written this.                    */
/* It does the same thing.                                           */
Tree * sedgewickized_splay (int i, Tree * t)
{
    Tree N, *l, *r, *y;
    if (t == NULL)
        return t;
    N.left = N.right = NULL;
    l = r = &N;
    for (;;)
    {
        if (i < t->item)
        {
            if (t->left != NULL && i < t->left->item)
            {
                y = t->left;
                t->left = y->right;
                y->right = t;
                t = y;
	        }
            if (t->left == NULL)
                break;
            r->left = t;
            r = t;
            t = t->left;
        }
        else if (i > t->item)
        {
            if (t->right != NULL && i > t->right->item)
            {
                y = t->right;
                t->right = y->left;
                y->left = t;
                t = y;
            }
            if (t->right == NULL)
                break;
            l->right = t;
            l = t;
            t = t->right;
        }
        else
            break;
        }
    }
    l->right=t->left;
    r->left=t->right;
    t->left=N.right;
    t->right=N.left;
    return t;
}

Tree * insert(int i, Tree * t)
{
/* Insert i into the tree t, unless it's already there.    */
/* Return a pointer to the resulting tree.                 */
    Tree * new_node;
     
    new_node = (Tree *) malloc (sizeof (Tree));
    if (new_node == NULL)
    {
        printf("Ran out of space\n");
        exit(1);
    }
    new_node ->item = i;
    if (t == NULL)
    {
        new_node->left = new_node->right = NULL;
        size = 1;
        return new_node;
    }
    t = splay(i,t);
    if (i < t->item)
    {
        new_node->left = t->left;
        new_node->right = t;
        t->left = NULL;
        size ++;
        return new_node;
    }
    else if (i > t->item)
    {
        new_node->right = t->right;
        new_node->left = t;
        t->right = NULL;
        size++;
        return new_node;
    }
    else
    {
        /* We get here if it's already in the tree */
        /* Don't add it again                      */
        free(new_node);
        return t;
    }
}

Tree * delete(int i, Tree * t)
{
/* Deletes i from the tree if it's there.               */
/* Return a pointer to the resulting tree.              */
    Tree * x;
    if (t==NULL)
       return NULL;
    t = splay(i,t);
    if (i == t->item)
    {               /* found it */
        if (t->left == NULL)
            x = t->right;
        else
        {
            x = splay(i, t->left);
            x->right = t->right;
        }
        size--;
        free(t);
        return x;
    }
    return t;                         /* It wasn't there */
}

int main(int argv, char *argc[])
{
/* A sample use of these functions.  Start with the empty tree,         */
/* insert some stuff into it, and then delete it                        */
    Tree * root;
    int i;
    root = NULL;              /* the empty tree */
    size = 0;
    for (i = 0; i < 1024; i++)
    {
        root = insert((541*i) & (1023), root);
    }
    printf("size = %d\n", size);
    for (i = 0; i < 1024; i++)
    {
        root = delete((541*i) &(1023), root);
    }
    printf("size = %d\n", size);
}

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

標籤:其他

上一篇:Codeforces Round #700 (Div. 2) A ~ E ,6題全,超高質量良心題解【每日億題】2021/2/8

下一篇:spring cloud ouath2中的資源服務器

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

熱門瀏覽
  • 面試突擊第一季,第二季,第三季

    第一季必考 https://www.bilibili.com/video/BV1FE411y79Y?from=search&seid=15921726601957489746 第二季分布式 https://www.bilibili.com/video/BV13f4y127ee/?spm_id_fro ......

    uj5u.com 2020-09-10 05:35:24 more
  • 第三單元作業總結

    1.前言 這應該是本學期最后一次寫作業總結了吧。總體來說,對作業的節奏也差不多掌握了,作業做起來的效率也更高了。雖然和之前的作業一樣,作業中都要用到新的知識,但是相比之前,更加懂得了如何利用工具以及資料。雖然之間卡過殼,但總體而言,這幾次作業還算完成的比較好。 2.作業程序總結 相比前兩個單元,此單 ......

    uj5u.com 2020-09-10 05:35:41 more
  • 北航OO(2020)第四單元博客作業暨課程總結博客

    北航OO(2020)第四單元博客作業暨課程總結博客 本單元作業的架構設計 在本單元中,由于UML圖具有比較清晰的樹形結構,因此我對其中需要進行查詢操作的元素進行了包裝,在樹的父節點中存盤所有孩子的參考。考慮到性能問題,我采用了快取機制,一次查詢后盡可能快取已經遍歷過的資訊,以減少遍歷次數。 本單元我 ......

    uj5u.com 2020-09-10 05:35:48 more
  • BUAA_OO_第四單元

    一、UML決議器設計 ? 先看下題目:第四單元實作一個基于JDK 8帶有效性檢查的UML(Unified Modeling Language)類圖,順序圖,狀態圖分析器 MyUmlInteraction,實際上我們要建立一個有向圖模型,UML中的物件(元素)可能與同級元素連接,也可與低級元素相連形成 ......

    uj5u.com 2020-09-10 05:35:54 more
  • 6.1邏輯運算子

    邏輯運算子 1. && 短路與 運算式1 && 運算式2 01.運算式1為true并且運算式2也為true 整體回傳為true 02.運算式1為false,將不會執行運算式2 整體回傳為false 03.只要有一個運算式為false 整體回傳為false 2. || 短路或 運算式1 || 運算式2 ......

    uj5u.com 2020-09-10 05:35:56 more
  • BUAAOO 第四單元 & 課程總結

    1. 第四單元:StarUml檔案決議 本單元采用了圖模型決議UML。 UML檔案可以抽象為圖、子圖、邊的邏輯結構。 在實作中,圖的節點包括類、介面、屬性,子圖包括狀態圖、順序圖等。 采用了三次遍歷UML元素的方法建圖,第一遍遍歷建點,第二、三次遍歷設定屬性、連邊,實作圖物件的初始化。這里借鑒了一些 ......

    uj5u.com 2020-09-10 05:36:06 more
  • 談談我對C# 多型的理解

    面向物件三要素:封裝、繼承、多型。 封裝和繼承,這兩個比較好理解,但要理解多型的話,可就稍微有點難度了。今天,我們就來講講多型的理解。 我們應該經常會看到面試題目:請談談對多型的理解。 其實呢,多型非常簡單,就一句話:呼叫同一種方法產生了不同的結果。 具體實作方式有三種。 一、多載 多載很簡單。 p ......

    uj5u.com 2020-09-10 05:36:09 more
  • Python 資料驅動工具:DDT

    背景 python 的unittest 沒有自帶資料驅動功能。 所以如果使用unittest,同時又想使用資料驅動,那么就可以使用DDT來完成。 DDT是 “Data-Driven Tests”的縮寫。 資料:http://ddt.readthedocs.io/en/latest/ 使用方法 dd. ......

    uj5u.com 2020-09-10 05:36:13 more
  • Python里面的xlrd模塊詳解

    那我就一下面積個問題對xlrd模塊進行學習一下: 1.什么是xlrd模塊? 2.為什么使用xlrd模塊? 3.怎樣使用xlrd模塊? 1.什么是xlrd模塊? ?python操作excel主要用到xlrd和xlwt這兩個庫,即xlrd是讀excel,xlwt是寫excel的庫。 今天就先來說一下xl ......

    uj5u.com 2020-09-10 05:36:28 more
  • 當我們創建HashMap時,底層到底做了什么?

    jdk1.7中的底層實作程序(底層基于陣列+鏈表) 在我們new HashMap()時,底層創建了默認長度為16的一維陣列Entry[ ] table。當我們呼叫map.put(key1,value1)方法向HashMap里添加資料的時候: 首先,呼叫key1所在類的hashCode()計算key1 ......

    uj5u.com 2020-09-10 05:36:38 more
最新发布
  • 【中介者設計模式詳解】C/Java/JS/Go/Python/TS不同語言實作

    * 中介者模式是一種行為型設計模式,它可以用來減少類之間的直接依賴關系,
    * 將物件之間的通信封裝到一個中介者物件中,從而使得各個物件之間的關系更加松散。
    * 在中介者模式中,物件之間不再直接相互互動,而是通過中介者來中轉訊息。 ......

    uj5u.com 2023-04-20 08:20:47 more
  • 露天煤礦現場調研和交流案例分享

    他們集團的資訊化公司及研究院在一個礦區正在做智能礦山的統一平臺的 試點,專案投資大概1億,包括了礦山的各方面的內容,顯示得我們這次交流有點多余。他們2年前開始做智能礦山的規劃,有很多煤礦行業專家的加持,他們的描述是非常完美,但是去年底應該上線的平臺,現在還沒有看到影子。他們確實有很多場景需求,但是被... ......

    uj5u.com 2023-04-20 08:20:25 more
  • 《社區人員管理》實戰案例設計&個人案例分享

    設計是一個讓人夢想成真程序,開始編碼、測驗、除錯之前進行需求分析和架構設計,才能保證關鍵方面都做正確 ......

    uj5u.com 2023-04-20 08:20:17 more
  • 軟體架構生態化-多角色交付的探索實踐

    作為一個技術架構師,不僅僅要緊跟行業技術趨勢,還要結合研發團隊現狀及痛點,探索新的交付方案。在日常中,你是否遇到如下問題 “ 業務需求排期長研發是瓶頸;非研發角色感受不到研發技改提效的變化;引入ISV 團隊又擔心質量和安全,培訓周期長“等等,基于此我們探索了一種新的技術體系及交付方案來解決如上問題。 ......

    uj5u.com 2023-04-20 08:20:10 more
  • 【中介者設計模式詳解】C/Java/JS/Go/Python/TS不同語言實作

    * 中介者模式是一種行為型設計模式,它可以用來減少類之間的直接依賴關系,
    * 將物件之間的通信封裝到一個中介者物件中,從而使得各個物件之間的關系更加松散。
    * 在中介者模式中,物件之間不再直接相互互動,而是通過中介者來中轉訊息。 ......

    uj5u.com 2023-04-20 08:19:44 more
  • 露天煤礦現場調研和交流案例分享

    他們集團的資訊化公司及研究院在一個礦區正在做智能礦山的統一平臺的 試點,專案投資大概1億,包括了礦山的各方面的內容,顯示得我們這次交流有點多余。他們2年前開始做智能礦山的規劃,有很多煤礦行業專家的加持,他們的描述是非常完美,但是去年底應該上線的平臺,現在還沒有看到影子。他們確實有很多場景需求,但是被... ......

    uj5u.com 2023-04-20 08:19:07 more
  • 《社區人員管理》實戰案例設計&個人案例分享

    設計是一個讓人夢想成真程序,開始編碼、測驗、除錯之前進行需求分析和架構設計,才能保證關鍵方面都做正確 ......

    uj5u.com 2023-04-20 08:18:57 more
  • 軟體架構生態化-多角色交付的探索實踐

    作為一個技術架構師,不僅僅要緊跟行業技術趨勢,還要結合研發團隊現狀及痛點,探索新的交付方案。在日常中,你是否遇到如下問題 “ 業務需求排期長研發是瓶頸;非研發角色感受不到研發技改提效的變化;引入ISV 團隊又擔心質量和安全,培訓周期長“等等,基于此我們探索了一種新的技術體系及交付方案來解決如上問題。 ......

    uj5u.com 2023-04-20 08:18:49 more
  • 05單件模式

    #經典的單件模式 public class Singleton { private static Singleton uniqueInstance; //一個靜態變數持有Singleton類的唯一實體。 // 其他有用的實體變數寫在這里 //構造器宣告為私有,只有Singleton可以實體化這個類! ......

    uj5u.com 2023-04-19 08:42:51 more
  • 【架構與設計】常見微服務分層架構的區別和落地實踐

    軟體工程的方方面面都遵循一個最基本的道理:沒有銀彈,架構分層模型更是如此,每一種都有各自優缺點,所以請根據不同的業務場景,并遵循簡單、可演進這兩個重要的架構原則選擇合適的架構分層模型即可。 ......

    uj5u.com 2023-04-19 08:42:41 more