數據結構——數組

  數組是程序中最多見的數據結構,它能夠存儲一個固定大小的相同類型元素的順序集合(強類型語言)。數組的元素都是由連續的內存位置組成。最低的地址對應第一個元素,最高的地址對應最後一個元素,經過索引能夠很是容易找到某一個元素。python

  大多數時候咱們須要使用一個大小可變的數組(C#、Python中的list),本文就基於數組來實現一個動態數組,因爲在Python中的列表已經對數組封裝的很好,這裏咱們使用C#來實現一個List。在後續介紹數據結構文章中,我會使用python和C#分別來實現相應的數據結構。數組

  動態數組和普通數組在用戶使用上沒有區別,咱們定義一個類MyArray,內部維護一個數組,並不須要實現太多方法,最核心的是提供擴容、索引和增刪功能。數據結構

    class MyArray<T>
    {
        private T[] array; //存儲數組
        private int count; //存儲數組大小
        private int capacity; //數組容量
        
        public int Count { get { return count; } }
        public int Capacity {  get { return capacity; } }

        //重載構造函數 接收一個初始容量
        public MyArray(int capacity){ }

        // 改變數組大小
        private void resize(int capacity) { }

        //在指定索引處插入元素
        public void Insert(int index,T item) { }

        //移除指定位置元素
        public void RemoveAt(int index)  { }

        //正序獲取元素位置
        public int IndexOf(T item) { }

        //實現索引器
        public T this[int index]  { }
    }

  下面咱們按上面的方法一點一點來實現咱們的MyArray函數

 1.實現構造函數

  構造函數在功能上是爲了初始化數組,因此用戶能夠傳遞一個初始數組容量,固然考慮到咱們的數組自己是動態的,因此若是用戶不傳入初始容量時,咱們應該使用默認大小建立數組。this

        public MyArray(int capacity)
        {
            //接收一個初始容量capacity
            if (capacity > 0)
            {
                this.capacity = capacity;
                array = new T[capacity];
            }
            else
             throw new Exception("列表容量必須大於0");
        }

       public MyArray()
        {
            //構造函數 初始化默認數組
            capacity = 4;
            array = new T[capacity];
        }

 

   在這裏咱們設置若是用戶沒有傳入capacity,默認數組大小爲4。咱們還維護了一個變量this.capacity,其實這個變量並沒必要要,能夠直接經過array.length得到數組容量。spa

2.實現數組擴容方法

  思路十分簡單,咱們實例化一個新數組,把舊數組的數據複製到新數組便可。code

private void resize(int capacity) {
    // 改變數組大小
    T[] newarray = new T[capacity];
    Array.Copy(array, newarray, count);
    array = newarray;
    this.capacity = capacity;
}

 

3.增長元素—Insert方法

  把一個元素插入數組指定位置,能夠分兩種狀況討論:一是追加到數組末尾,二是插入到數組中。blog

  第一種狀況處理起來很簡單,由於咱們定義的類維護了一個count變量,它記錄了數組的實際大小,因此咱們只要賦值array[count],count++就能夠實現功能。索引

  第二種狀況表明原來的位置已經有元素了,那麼原位置以後的元素都應該集體向後挪一個位置,array[i+1] = array[i],咱們能夠用一個for循環來實現。內存

public void Insert(int index,T item)
{
    //在指定索引處插入元素
    if (index > count || index <0)
    {
        throw new Exception("索引超出範圍");
    }
    if (capacity == count)
    {
        //數組擴容
        resize(capacity * 2);
    }
    if (index == count)
    {
        //第一種狀況,在末尾追加元素追加元素
        array[count] = item;
        count++;
    } else
    {
        for (int i = count - 1; i>=index; i--)
        {
            //最後一次循環應爲array[index+1] = array[index]
            array[i + 1] = array[i];
        }
        array[index] = item;
        count++;
    }
}

 

  值得注意的是,插入元素可能會超出數組大小,因此咱們作了一層capacity==count的判斷,若是爲真,咱們就調用resize方法,將數組擴容至原來的兩倍。

4.刪除指定位置元素

  刪除元素的思路和增長元素的思路相反,把索引爲i的元素刪除後,後面的元素應該前進一位。

public void RemoveAt(int index)
{
    //移除指定位置元素
    if (index >= count || index < 0)
    {
        throw new Exception("索引超出範圍");
    } else
    {
        for (int i = index; i < count - 1; i++)
        {
            //只要實現array[index] = array[index+1]
            //若i=count-1;則i+1可能會出現超出索引的狀況,故條件爲i<count-1
            array[i] = array[i + 1];
        }
        count--;
        if (count < capacity / 4) {
            resize(capacity/2);
        }
    }
}

 

  在刪除元素中,咱們最後調用了resize方法,當元素個數小於數組的1/4時,咱們把數組縮小至原來的1/2。

5.查找元素的索引

  十分簡單,循環數組便可

public int IndexOf(T item)
{
    //正序獲取元素位置 若無返回-1
    for (int i = 0; i < count; i++)
    {
        if (array[i].Equals(item))
        {
            return i;
        }
    }
    return -1;
}

6.實現索引器

public T this[int index]
{
    get { return array[index]; }
    set { array[index] = value; }
} 

  好了,到如今咱們的MyArray最核心的功能完成了,固然你能夠爲它添加其餘方法,讓它在用戶使用體驗上,和原生數組更爲相近。最後咱們來看看各項操做的時間複雜度。

  Insert方法,在末尾添加元素時間複雜度爲O(1),在數組最前面添加元素爲O(n),均攤時間複雜度爲O(n)

  RemoveAt方法,在末尾刪除元素時間複雜度爲O(1),在數組最前面添加元素爲O(n),均攤時間複雜度爲O(n)

  IndexOf和resize方法,遍歷數組,時間複雜度爲O(n)

  索引器,按索引訪問元素,時間複雜度爲O(1)

相關文章
相關標籤/搜索