樹形結構處理

wxlworkhard發表於2018-04-27

資料結構

資料結構描述了資料元素之間的關係,可以從邏輯結構和儲存結構兩個維度來分析。

邏輯結構分類

  • 集合,資料元素除了同屬一個集合沒有其他關係;
  • 線性結構,存在一個對一個的關係;
  • 樹形結構,一個對多個的關係;
  • 複雜結構(圖狀結構、網狀結構),資料元素之間存在多個對多個的關係。如下圖:

enter image description here

儲存(物理)結構分類

順序表示、連結表示、雜湊(雜湊)表示、索引表示(注意這裡的儲存結構是記憶體的結構,以二進位制形式儲存,外存的儲存不一樣)。

邏輯結構轉變成物理結構

邏輯結構最終需要變成物理結構,計算機才能使用,但是邏輯結構不能直接變成物理結構,中間需要經過程式語言處理,就是說我們需要:

  1. 用程式語言的資料結構來表示邏輯結構
  2. 執行程式
  3. 程式語言的資料結構就變成了物理結構

邏輯結構 -> 程式語言的資料結構 -> 物理結構。

樹形邏輯結構的程式語言表示

我們使用 JS 來描述樹形邏輯結構,有兩種方式:

巢狀表示(子表表示法)

var tree1 = [
    {
        name: 'L1',
        children: [
            {
                name: 'L2_1',
                children: [ { name: 'L3_1'} ]
            },

            {
                name: 'L2_2',
                children: [ { name: 'L3_1' } ]
            },
        ]
    }
];

首先,是一個陣列,包含所有的 L1(一級)節點;然後,每個節點有 children 屬性(陣列),儲存子節點。

問題:只有父 -> 子的關係,沒有子 -> 父的關係。

扁平表示(父指標表示法)

var tree2 = [
    { id: 1, name: 'L1' },
    { id: 2, pId: 1, name: 'L2_1' },
    { id: 3, pId: 2, name: 'L3_1' },

    { id: 4, pId: 1, name: 'L2_2' },
    { id: 5, pId: 4, name: 'L3_2' },
];

首先,也是一個陣列,包含所有節點,通過 id 唯一標識;然後,使用 id、pId 維護子 -> 父的關係。

問題:與巢狀表示的問題正好相反,有子 -> 父關係,沒有父 -> 子關係。

在業務場景中用到了樹形結構,要滿足所有的業務需求,如果沒有完整的父 -> 子、子 -> 父的關係,就會面臨很多困難,當然可以用各種方式來實現業務需求,但是這裡想給出一種規範的、簡便的方式。

完整結構

使用 id、pId 建立子 -> 父關係;在父節點的 children 屬性包含所有子節點,建立父 -> 子關係。這有就有了完整的父子關係,其實是一種圖形邏輯結構,可以滿足所有業務需求。

注意這裡有兩個關鍵點:

  • 每個節點應該有一個唯一的標誌 id
  • 整個結構首先是一個陣列,可以根據 id 獲取任意節點

這裡自己實現了一個工具庫,可以把巢狀、扁平結構轉變成完整結構。

樹形結構的資料庫儲存

不管是文件型資料庫還是關係型資料庫,都可以使用巢狀、扁平的方式來儲存。

巢狀方式問題:子節點可能沒有 id,這樣解析成完整結構的時候需要手動維護 id。

扁平方式問題:需要進行多次插入,即先插入一個父節點,得到 id 後,把子節點的 pId 賦值成 id 再插入子節點。

相關文章