1. 程式人生 > 實用技巧 >Java——順序表

Java——順序表

順序表

順序表是在計算機記憶體中以陣列的形式儲存的線性表,線性表的順序儲存是指用一組地址連續的儲存單元,依次儲存線性表中的各個元素、使得線性表中在邏輯結構上相鄰的資料元素儲存在相鄰的物理儲存單元中,即通過資料元素物理儲存的相鄰關係來反映資料元素之間邏輯上的相鄰關係。

順序表API設計

類名 SequenceList
構造方法 SequenceList(int capacity):建立容量為capacity的SequenceList物件
成員方法 1.public void clear():空置線性表
2.publicboolean isEmpty():判斷線性表是否為空,是返回true,否返回false
3.public int length():獲取線性表中元素的個數
4.public T get(int i):讀取並返回線性表中的第i個元素的值
5.public void insert(int i,T t):線上性表的第i個元素之前插入一個值為t的資料元素。
6.public void insert(T t):向線性表中新增一個元素t
7.public T remove(int i):刪除並返回線性表中第i個數據元素。
8.public int indexOf(T t):返回線性表中首次出現的指定的資料元素的位序號,若不存在,則返
回-1
成員變數 1.private T[] eles:儲存元素的陣列
2.private int N:當前線性表的長度

順序表的程式碼實現

// 順序表
public class SequenceList<T> implements Iterable<T> {
    //儲存元素的陣列
    private T[] eles;
    //記錄當前順序表中的元素個數
    private int N;

    //構造方法
    public SequenceList(int capacity) {
        //初始化陣列
        this.eles = (T[]) new Object[capacity];
        //初始化長度
        this.N = 0;
    }

    //將一個線性表置為空表
    public void clear() {
        this.N = 0;
    }

    //判斷當前線性表是否為空表
    public boolean isEmpty() {
        return N == 0;
    }

    //獲取線性表的長度
    public int length() {
        return N;
    }

    //獲取指定位置的元素
    public T get(int i) {
        return eles[i];
    }

    //向線型表中新增元素t
    public void insert(T t) {
        if (N == eles.length) {
            resize(2 * eles.length);
        }

        eles[N++] = t;
    }

    //在i元素處插入元素t
    public void insert(int i, T t) {
        if (N == eles.length) {
            resize(2 * eles.length);
        }

        //先把i索引處的元素及其後面的元素依次向後移動一位
        for (int index = N; index > i; index--) {
            eles[index] = eles[index - 1];
        }
        //再把t元素放到i索引處即可
        eles[i] = t;

        //元素個數+1
        N++;
    }

    //刪除指定位置i處的元素,並返回該元素
    public T remove(int i) {
        //記錄索引i處的值
        T current = eles[i];
        //索引i後面元素依次向前移動一位即可
        for (int index = i; index < N - 1; index++) {
            eles[index] = eles[index + 1];
        }
        //元素個數-1
        N--;

        if (N < eles.length / 4) {
            resize(eles.length / 2);
        }

        return current;
    }


    //查詢t元素第一次出現的位置
    public int indexOf(T t) {
        for (int i = 0; i < N; i++) {
            if (eles[i].equals(t)) {
                return i;
            }
        }
        return -1;
    }

    //根據引數newSize,重置eles的大小
    public void resize(int newSize) {
        //定義一個臨時陣列,指向原陣列
        T[] temp = eles;
        //建立新陣列
        eles = (T[]) new Object[newSize];
        //把原陣列的資料拷貝到新陣列即可
        for (int i = 0; i < N; i++) {
            eles[i] = temp[i];
        }
    }


    @Override
    public Iterator<T> iterator() {
        return new SIterator();
    }

    private class SIterator implements Iterator {
        private int cusor;

        public SIterator() {
            this.cusor = 0;
        }

        @Override
        public boolean hasNext() {
            return cusor < N;
        }

        @Override
        public Object next() {
            return eles[cusor++];
        }
    }
}
// 測試程式碼
public class SequenceListTest {

    public static void main(String[] args) {
        //建立順序表物件
        SequenceList<String> sl = new SequenceList<>(10);
        //測試插入
        sl.insert("姚明");
        sl.insert("科比");
        sl.insert("麥迪");
        sl.insert(1, "詹姆斯");

        for (String s : sl) {
            System.out.println(s);
        }

        System.out.println("------------------------------------------");

        //測試獲取
        String getResult = sl.get(1);
        System.out.println("獲取索引1處的結果為:" + getResult);
        //測試刪除
        String removeResult = sl.remove(0);
        System.out.println("刪除的元素是:" + removeResult);
        //測試清空
        sl.clear();
        System.out.println("清空後的線性表中的元素個數為:" + sl.length());
    }
}
public class SequenceListTest2 {

    public static void main(String[] args) {
        SequenceList<String> sl = new SequenceList<>(3);
        sl.insert("張三");
        sl.insert("李四");
        sl.insert("王五");
        sl.insert("趙六");
    }
}

順序表的遍歷

  • 一般作為容器儲存資料,都需要向外部提供遍歷的方式,因此我們需要給順序表提供遍歷方式。

  • 在java中,遍歷集合的方式一般都是用的是foreach迴圈,如果想讓我們的SequenceList也能支援foreach迴圈,則需要做如下操作:

    • 1.讓SequenceList實現Iterable介面,重寫iterator方法;

    • 2.在SequenceList內部提供一個內部類SIterator,實現Iterator介面,重寫hasNext方法和next方法;

順序表的容量可變

  • 在之前的實現中,當我們使用SequenceList時,先new SequenceList(5)建立一個物件,建立物件時就需要指定容器的大小,初始化指定大小的陣列來儲存元素,當我們插入元素時,如果已經插入了5個元素,還要繼續插入資料,則會報錯,就不能插入了。這種設計不符合容器的設計理念,因此我們在設計順序表時,應該考慮它的容量的伸縮性。

  • 考慮容器的容量伸縮性,其實就是改變儲存資料元素的陣列的大小,那我們需要考慮什麼時候需要改變陣列的大小?

  • 1.新增元素時(擴容):

    • 新增元素時,應該檢查當前陣列的大小是否能容納新的元素,如果不能容納,則需要建立新的容量更大的陣列,我們這裡建立一個是原陣列兩倍容量的新陣列儲存元素。

  • 2.移除元素時(縮容):

    • 移除元素時,應該檢查當前陣列的大小是否太大,比如正在用100個容量的陣列儲存10個元素,這樣就會造成記憶體空間的浪費,應該建立一個容量更小的陣列儲存元素。如果我們發現數據元素的數量不足陣列容量的1/4,則建立一個是原陣列容量的1/2的新陣列儲存元素。

順序表的時間複雜度

  • get(i):不難看出,不論資料元素量N有多大,只需要一次eles[i]就可以獲取到對應的元素,所以時間複雜度為O(1);

  • insert(int i,T t):每一次插入,都需要把i位置後面的元素移動一次,隨著元素數量N的增大,移動的元素也越多,時間複雜為O(n);

  • remove(int i):每一次刪除,都需要把i位置後面的元素移動一次,隨著資料量N的增大,移動的元素也越多,時間複雜度為O(n);

  • 由於順序表的底層由陣列實現,陣列的長度是固定的,所以在操作的過程中涉及到了容器擴容操作。這樣會導致順序表在使用過程中的時間複雜度不是線性的,在某些需要擴容的結點處,耗時會突增,尤其是元素越多,這個問題越明顯

java中ArrayList實現

  • java中ArrayList集合的底層也是一種順序表,使用陣列實現,同樣提供了增刪改查以及擴容等功能。
    • 1.是否用陣列實現;
    • 2.有沒有擴容操作;
    • 3.有沒有提供遍歷方式;
  • ArrayList通用性強,有時候我們需要自己設計用於處理特殊情況的順序表