1. 程式人生 > >請問二叉樹等數據結構的物理存儲結構是怎樣的?

請問二叉樹等數據結構的物理存儲結構是怎樣的?

key 來看 內存結構 以及 排列 ESS sta catch alt

  請問二叉樹等數據結構的物理存儲結構是怎樣的?

  好吧,咱們書上說了,一般兩種存儲方式: 1. 以完全二叉樹的形式用連續空間的數組存儲; 2. 以鏈表形式存儲,即各個數據之間保存了相關的數據的指針地址!
  如果回答就是這樣,那麽我想大家也不費那神了,直接洗洗睡吧?

咱們能不能深入點:

  數組是好理解的,在內存在磁盤都是一樣的,連續相鄰的空間;好吧,就算你正確?那麽鏈表呢?拿簡單的單鏈表來說,上一個節點保存下一個節點的指針?是如何保存的?我們能想到的,就是一個下一節點的存儲地址,好像是這樣的!

  那麽問題來了,這個下一節點地址到底是什麽樣的呢?是相對地址還是絕對地址?這個地址是怎麽算出來的?存儲在內存上是肯定沒有問題的!但是如果存儲在磁盤上呢?如果這個地址是固定的,那麽,如果換了硬盤(換了存儲介質),是否就找不到該地址(因為每個設備的地址自然是不一樣的)?

  針對這個問題,很是困擾了我很久!也詢問過幾個身邊的小夥伴,也都說不知道。後來在一次面試中,一面試官剛好問我這問題,我把自己的見解說完後,說我確實不知道是怎麽存儲的。最後我要求他給予答案,然後,他說,就是存儲的下一節點的地址(內存地址),壓根不存在什麽數據結構存儲於磁盤這種說法,內存中,是動態計算的值。如果存在內存拷貝,那麽,也會重新計算這些地址的,所以看起來相同的結構,在不同存儲工具上,會會表現出不同的地址空間。

  好吧,我將信將疑!被丟了n個鄙視的表情,然後被pass掉了。

  那麽,到底內存中的二叉樹怎麽存儲在硬盤上的呢?

  其實硬盤上並沒有什麽二叉樹的,硬盤只是充當了一個存儲介質,只是提供你要讀的時候可以取而已,而真正的數據結構,則需要在用的時候再還原出原來的樹形結構!

下面以一個簡單的示例來展示磁盤上的數據結構的存儲方式:

public class BinTreeDiskSample {
    private static int Sn = -1;
    private static Node root;
    static private class Node implements Serializable {
        private static final long serialVersionUID = -4780741633734920991L;
        int data;
        transient Node left;
        
transient Node right; int lHeight = -1, rHeight = -1; public Node(int data) { this.data = data; } public Node setLeft(Node left) { this.left = left; return this; } public Node setRight(Node right) { this.right = right; return this; } public Node getLeft() { return left; } public Node getRight() { return right; } // 後續遍歷寫入,先序遍歷讀出 public int write(ObjectOutputStream out) throws IOException { if (left != null) { lHeight = left.write(out); } if (right != null) { rHeight = right.write(out); } Sn++; out.writeObject(this); return Sn; } private void init(List<Node> list) { if (lHeight != -1) { left = list.get(lHeight); left.init(list); } if (rHeight != -1) { right = list.get(rHeight); right.init(list); } } } public static void binTreePreOrderPrint(Node root) { System.out.print(root.data + " "); // visit root if(root.left != null) { binTreePreOrderPrint(root.left); } if(root.right != null) { binTreePreOrderPrint(root.right); } } // 先序遍歷讀出 public static void read(ObjectInputStream in) throws IOException, ClassNotFoundException { List<Node> list = new ArrayList<Node>(); Node n; Object obj; try { while ((obj = in.readObject()) != null) { n = (Node) obj; list.add(n); } } catch (Exception e) { // EOFException ... // e.printStackTrace(); } root = list.get(list.size() - 1); root.init(list); } public static void main(String args[]) throws FileNotFoundException, IOException, ClassNotFoundException { // 構造一棵二叉樹 /* * 1 2 3 4 5 6 */ Node n6 = new Node(61); Node n4 = new Node(41).setLeft(n6); Node n5 = new Node(51); Node n2 = new Node(21).setLeft(n4).setRight(n5); Node n3 = new Node(31); Node n1 = new Node(11).setLeft(n2).setRight(n3); root = n1; System.out.println("output node: "); binTreePreOrderPrint(root); // 將數據寫稿磁盤 ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("btree.bin")); root.write(out); out.close(); root = null; // 將數據從磁盤讀入,並進行數據結構的重新構建 ObjectInputStream in = new ObjectInputStream(new FileInputStream("btree.bin")); read(in); in.close(); System.out.println("\nread node: "); binTreePreOrderPrint(root); } }

如上二叉樹的磁盤存儲,使用了java自帶的序列化工具,將節點寫入磁盤(註:這並不是一種好的實踐),然後在讀出的時候,按照寫稿時候的規則,進行重新構建二叉樹即可。

所以:

  存儲磁盤的數據結構,只是一種約定的方式,只是為了方便在重新恢復鏈表,二叉樹等等內存結構的算法而已。

  如:數據庫索引是存儲在磁盤上,當表中的數據量比較大時,索引的大小也跟著增長,達到幾個G甚至更多。當我們利用索引進行查詢的時候,不可能把索引全部加載到內存中,只能逐一加載每個磁盤頁,這裏的磁盤頁就對應索引樹的節點。

B+/-樹索引用使用很多的數據結構,下面做一點簡單介紹:

一、B-Tree
  m階B-Tree滿足以下條件:

  1、每個節點最多擁有m個子樹

  2、根節點至少有2個子樹

  3、分支節點至少擁有m/2顆子樹(除根節點和葉子節點外都是分支節點)

  4、所有葉子節點都在同一層、每個節點最多可以有m-1個key,並且以升序排列

二、B+Tree的定義
  B+Tree是B樹的變種,有著比B樹更高的查詢性能,來看下m階B+Tree特征:

  1、有m個子樹的節點包含有m個元素(B-Tree中是m-1)

  2、根節點和分支節點中不保存數據,只用於索引,所有數據都保存在葉子節點中。

  3、所有分支節點和根節點都同時存在於子節點中,在子節點元素中是最大或者最小的元素。

  4、葉子節點會包含所有的關鍵字,以及指向數據記錄的指針,並且葉子節點本身是根據關鍵字的大小從小到大順序鏈接。

下面讓我們來看看現代數據庫的磁盤存儲結構吧:

以下部分內容摘自: https://blog.csdn.net/qq910894904/article/details/39312901

  我們都知道,數據庫通常使用B+樹作為索引,但是國內很少有人提到數據庫使用的是HeapFile來管理記錄的存儲。國外的一些大學在“數據庫系統實現”這門課上通常會讓學生實現一個簡單的數據庫,因此有不少HeapFile的資料。

基於Page的HeapFile
  采用鏈表形式的是HeapFile如下:

技術分享圖片

  Heap file和鏈表結構類似的地方:

    支持增加(append)功能
    支持大規模順序掃描
    不支持隨機訪問

  這種方式的HeapFile在尋找具有合適空間的半空Page時需要遍歷多個頁,I/O開銷大。因此一般常用的是采用基於索引的HeaFile.在HeapFile中使用一部分空間來存儲Page作為索引,並記錄對應Page的剩余量。如下:

技術分享圖片

像上圖那樣,索引單獨存在一個page上。數據記錄存在其他page上,如果有多個索引的page,則可以表示為:

技術分享圖片
下面是Heap file自有的一些特性:

數據保存在二級存儲體(disk)中:Heapfile主要被設計用來高效存儲大數據量,數據量的大小只受存儲體容量限制;

Heapfile可以跨越多個磁盤空間或機器:heapfile可以用大地址結構去標識多個磁盤,甚至於多個網絡;

數據被組織成頁;

頁可以部分為空(並不要求每個page必須裝滿);

頁面可以被分割在某個存儲體的不同的物理區域,也可以分布在不同的存儲體上,甚至是不同的網絡節點中。我們可以簡單假設每一個page都有一個唯一的地址標識符PageAddress,並且操作系統可以根據PageAddress為我們定位該Page。

一般情況下,使用page在其所在文件中的偏移量就可以表示了。

請問二叉樹等數據結構的物理存儲結構是怎樣的?