1. 程式人生 > >資料結構與演算法(三)-線性表之靜態連結串列

資料結構與演算法(三)-線性表之靜態連結串列

前言:前面介紹的線性表的順序儲存結構和鏈式儲存結構中,都有對物件地引用或指向,也就是程式語言中有引用或者指標,那麼在沒有引用或指標的語言中,該怎麼實現這個的資料結構呢?

一、簡介

  定義:用陣列代替指標或引用來描述單鏈表,即用陣列描述的連結串列叫做靜態連結串列,這種描述方法叫做遊標實現法;   上面的靜態連結串列圖有兩個陣列遊標資料,其中資料陣列儲存資料,而遊標陣列儲存同下標為資料的下一個資料的下標值,簡單模擬一下靜態連結串列遍歷的過程:
  1. 先檢視下標為999的遊標陣列值:1;
  2. 根據遊標陣列值1,查詢下標為1的資料:A;
  3. 然後檢視遊標陣列為1的值:2;
  4. 根據遊標陣列值為2查詢對應的資料陣列的值:C;
  5. 然後迴圈3->4,直至遊標陣列的值為0;

二、程式碼實現

  靜態連結串列的建立:
  • 我們對陣列的第一個和最後一個元素做特殊處理,他們的data不存放資料;
  • 我們通常把未使用的陣列元素稱為備用連結串列;
  • 陣列的第一個元素,即下標為0的那個元素的cur就存放備用連結串列的第一個結點的下標;
  • 陣列的最後一個元素,即下標為MAXSIZE-1的cur則存放第一個有數值的元素的下標,相當於單鏈表中的頭結點作用;
  靜態連結串列建立程式碼實現:
 public class StaticChain<T> {

    //資料鏈
    private
T[] datas; //遊標鏈 private int[] vernier; private Integer size; private Integer length = 1000; StaticChain() { datas = (T[])new Object[length]; vernier = new int[length]; //將遊標鏈的末位(頭結點)初始化為1(下一節點的位置) vernier[length-1]=1; //將遊標鏈的首位(空閒位的位置)初始化為1
vernier[0]=1; size = 0; } }
  插入操作:

    1、獲取遊標鏈下標為0的值為空閒位置的下標,並將該值對應下標所在的值放在遊標鏈下標為0的地方;

    2、在5的位置插入值F,並將下標為5的遊標鏈的值修改為0;

    3、若插入值為末位則直接將對應下標為4的遊標鏈的值改為5,否則迴圈查詢要插入值的上一位,並對應下標為4的遊標鏈的值改為5;

 

插入操作程式碼如下:
    //插入到第幾個元素的後面
    public void add(Integer index,T t) throws Exception {
        if (index>size)
            throw new Exception("outof index");
        int insertIndex = vernier[0];
        if (index == size) {
            int freeIndex = vernier[insertIndex];
            //將空閒位置的下標放入遊標鏈的首位
            vernier[0] = freeIndex;
            //將剛插入末位值對應下標的遊標鏈的值置為0
            vernier[insertIndex] = 0;
            //將值插入對應的位置
            datas[insertIndex] = t;
            //獲取第幾個元素的遊標鏈對應的值
            Integer preIndex = this.getIndex(index);
            //將上一個元素的遊標鏈的值改為插入的值的下標
            vernier[preIndex] = insertIndex;
            size++;
        } else {
            //獲取第index+1個元素的遊標鏈對應的值
            Integer nextIndex = this.getIndex(index+1);
            //將插入位置下標對應的遊標鏈的值改為下一個元素的位置
            vernier[insertIndex] = vernier[nextIndex];
            datas[insertIndex] = t;
            //將上一個元素的遊標鏈的值改為插入的值的下標
            Integer preIndex = this.getIndex(index);
            vernier[preIndex] = insertIndex;
            size++;
            //重置遊標鏈的首位為空閒值下標
            Integer endIndex = this.getIndex(size);
            vernier[0] = vernier[endIndex];
            vernier[endIndex] = 0;
        }
    }

    //查詢幾個元素的遊標鏈對應的下標
    private Integer getIndex(Integer index) throws Exception {
        int k = length - 1;
        for (int i = 1; i <= index; i++)
            k = vernier[k];
        if (k==-1) {
            throw new Exception("outof index");
        }
        return k;
    }
add() 刪除操作:

   1、查詢到要刪除的節點的下標,將其對應的遊標鏈的值取出來放在上一個遊標鏈的指;

   2、並將刪除的結點對應的遊標鏈的值改為當前空閒指的下標,將空閒值的下標改為當前刪除節點的下標;

刪除操作程式碼如下:
    //刪除第index個元素
    public T remove(Integer index) throws Exception {
        T data = null;
        if (index == 1) {
            Integer delIndex = vernier[999];
            data = datas[delIndex];
            int nextIndex = vernier[delIndex];
            vernier[length-1] = nextIndex;
            vernier[delIndex] = vernier[0];
            vernier[0] = delIndex;
        } else {
            Integer delIndex = this.getIndex(index);
            data = datas[delIndex];
            int nextIndex = vernier[delIndex];
            Integer preIndex = this.getIndex(index - 1);
            vernier[preIndex] = nextIndex;
            vernier[delIndex] = vernier[0];
            vernier[0] = delIndex;
        }
        return data;
    }

三、總結

  優點:
  • 在插入和刪除操作時,只需要修改遊標,不需要移動元素,從而改進了在順序儲存結構中的插入和刪除操作需要移動大量元素的缺點;
  • 解決了在某些沒有引用和指標的高階語言中無法建立線性表的鏈式儲存結構;
  缺點:
  • 沒有解決連續儲存分配(陣列)帶來的表長難以確定的為題;
  • 失去了順序儲存結構隨機存取的特性;
 在這裡就要說一下靜態連結串列和動態連結串列的區別:
  • 靜態連結串列是陣列實現的,是順序儲存結構,在實體地址上是連續的,而且需要預先分配大小。而動態連結串列適用記憶體申請函式(malloc)動態申請記憶體的,所以每個節點的實體地址是不連續的,要通過指標來順序訪問;
  • 靜態連結串列的大小是一開始就定好的,所以當陣列放滿後就無法再放入了。而動態連結串列則無需考慮這種情況,隨時加隨時用;
總結:靜態連結串列其實是為了給沒有指標或引用的程式語言設計的一種實現單鏈表功能的方法,這種思想還是需要了解一下的——取他山之石以攻玉!

 本系列參考書籍:

  《寫給大家看的演算法書》

  《圖靈程式設計叢書 演算法 第4版》