數據結構之 Array/Vector

數據結構之 Array/Vector


寫在前面

數據結構是數據項的結構化集合,其結構性表現爲數據項之間的相互聯繫以及作用,可以理解爲定義於數據項之間的
某種邏輯次序。

數據結構(按數據項間的邏輯次序劃分)

  • 線性結構
    • 線性表\序列(Sequence)
    • 棧(stack)
    • 隊列(queue)
  • 半線性結構
    • 樹(Tree)
  • 非線性結構
    • 圖(graph)

線性表/序列(按存儲結構劃分)

  • 順序表/向量
    在這裏把順序表和向量放在一起了,其本質都是數組,但是在一般數據結構課中,更多地,我們稱爲順序表。進一步地,在C++的STL中,數組被封裝爲成爲適用更加廣泛的Vector了,通過模板參數制定數據元素的類型,還有大量的ADT接口使用起來特別方便。當然二者還是有一些區別的,在下面講解操作時會具體說。
    但本質上,二者的邏輯操作與實現是一致的,因此放在一起寫。
  • 鏈表/列表
    同上,一般數據結構課上就直接講鏈表,但在STL中封裝爲List了。

順序表/向量

  • 通常,程序語言都會把數組作爲一種內置的數據類型,支持其對一組相關元素的存儲組織與訪問操作。
  • 具體來講,一個集合S有n個元素,它們集中地存放於起始地址爲A、物理地址連續地一段存儲空間,並統稱爲數組(array)。通常以A作爲該數組的標識。並且,用A[i]表示這一段連續存儲空間中的第i+1個元素,值得注意的是,計算機中的i通常從0開始計數,而日常人們計數則從1開始。
    即 A = { a0, a1, a2, ……, a(n-1) }
  • 由此可以看出,A中任意一個元素 ai 都有唯一的 i 可以對其進行直接訪問,元素A[i]對應的物理地址爲$ A + i * s $,其中單個元素佔 ss 個單位空間。
  • 對於任何0i<j<n0 \le i \lt j \lt n, A[i]都是A[j]的前驅,特別地,當i=j1i=j-1時,A[i]是[j]的直接前驅,A[j]的所有前驅稱爲其前綴;相應地,A[j]都是A[i]的後繼,特別地,當i+1=ji+1=j時,A[j]是A[i]的直接後繼,A[i]的所有後繼稱爲其後綴。

基本操作(ADT接口)

Vector()

  • 構造函數,其實現方式最多樣,具體可參見下面的代碼。
  • 這是一個重點。array的初始化一般採用固定長度的方式,也就是說,其大小是在賦值之前就確定的,而Vector模板類採用動態分配空間方式,因此稍顯靈活。但當其規模頻繁變化時,二者都需要動態調整,不僅費時間,而且還會遇到沒有這麼大規模的連續空間的問題。所以,在需要規模頻繁變化時,一般不採用順序表,而採用鏈表/列表。

size()

  • 返回順序表/向量的規模
  • 值得注意的是,在insert或者remove這樣的會引起元素數量變化的時候,要及時地對size進行更新,這樣纔會保證每次調用此接口不用遍歷一遍計數,而是直接返回size變量的值時間複雜度爲O(1)

get( r )

  • 返回秩(rank,也就是通常所講的下標)爲r的元素
  • 時間複雜度O(1),充分體現了順序表可以隨機訪問的特徵
  • 這裏需要注意的是,雖然C++ STL中重載了[ ],也就是說vector仍然可以使用vec[i]的方式訪問某個元素,但實際上作爲一個封裝的抽象類,使用get( r )的方式更能體現其與底層無關的特點。

insert(r, e)

  • 將元素e插到秩爲r的位置
  • 時間複雜度O(n),在插入之前,需將秩爲n-1到r的所有元素依次向後移動一個位置,空出A[r]。r可爲0, 1, 2……, n,則每次需要移動n, n-1, n-2……, 0次,平均移動 n/2n/2 次,即時間複雜度爲O(n)

remove( r )

  • 刪除秩爲r的元素
  • 和插入一樣,刪除後需將後面的元素向前移動一個位置,時間複雜度爲O(n)

find(e)

  • 查找值等於e且秩最大的元素
  • 當有多個值爲e的元素時,返回秩最大的(至於原因,在實現排序時就能理解了),則可以從後往前依次查找,時間複雜度O(n)

traverse()

  • 遍歷所有元素
  • 顯然,當訪問每個元素只進行常數複雜度的操作時,此接口的時間複雜度爲O(n)

以下是根據鄧公學習時自己仿寫的,只給出頭文件,部分簡單的接口已經實現了,剩下的將會在結合具體算法講的時候在貼出來。雖然順序表很簡單,但是越簡單的東西可以變化的招式就越多,自己寫完之後才能體會哪裏是容易栽跟頭的地方。
另外,同一種接口有多種實現方式,因此,可結合複雜度分析,判斷優劣。

#ifndef VECTOR_H
#define VECTOR_H
// Vector template

typedef int Rank;  //秩
#define DEFAULT_CAPACITY 3  //默認的初始容量(實際應用中可設置爲更大)

template <typename T>
class Vector {  //向量模板類
   protected:
    Rank _size;
    int _capacity;
    T* _elem;                                     //規模、容量、數據區
    void copyFrom(T const* A, Rank lo, Rank hi);  //複製數組區間A[lo, hi)
    void expand();                                //空間不足時擴容
    void shrink();                                //裝填因子過小時壓縮
    bool bubble(Rank lo, Rank hi);                //掃描交換
    void bubbleSort(Rank lo, Rank hi);            //起泡排序算法
    Rank max(Rank lo, Rank hi);                   //選取最大元素
    void selectionSort(Rank lo, Rank hi);         //選擇排序算法
    void merge(Rank lo, Rank mi, Rank hi);        //歸併算法
    void mergeSort(Rank lo, Rank hi);             //歸併排序算法
    Rank partition(Rank lo, Rank hi);             //軸點構造算法
    void quickSort(Rank lo, Rank hi);             //快速排序算法
    void heapSort(Rank lo, Rank hi);              //堆排序
   public:
    // 構造函數
    Vector(int c = DEFAULT_CAPACITY, int s = 0, T v = 0){  //容量爲c、規模爲s、所有元素初始爲v
        _elem = new T[_capacity = c];
        for (_size = 0; _size < s; _elem[_size++] = v);
    }                                                  // s<=c
    Vector(T const* A, Rank n) { copyFrom(A, 0, n); }  //數組整體複製
    Vector(T const* A, Rank lo, Rank hi) { copyFrom(A, lo, hi); }  //區間
    Vector(Vector<T> const& V) {
        copyFrom(V._elem, 0, V._size);
    }  //向量整體複製
    Vector(Vector<T> const& V, Rank lo, Rank hi) {
        copyFrom(V._elem, lo, hi);
    }  //區間
    // 析構函數
    ~Vector() {delete[] _elem; }  //釋放內部空間
    // 只讀訪問接口
    Rank size() const { return _size; }    //規模
    bool empty() const { return !_size; }  //判空
    int disordered() const;                //判斷向量是否已排序
    Rank find(T const& e) const {
        return find(e, 0, _size);
    }  //無序向量整體查找
    Rank find(T const& e,
              Rank lo,
              Rank hi) const;  //無序向量區間查找,從右向左遍歷
    Rank search(T const& e) const  //有序向量整體查找,二分查找
    {
        return (0 >= _size) ? -1 : search(e, 0, _size);
    }
    Rank search(T const& e, Rank lo, Rank hi) const;  //有序向量區間查找
                                                      // 可寫訪問接口
    T& operator[](Rank r) const;  //重載下標操作符,可以類似於數組形式引用各元素
    Vector<T>& operator=(Vector<T> const&);  //重載賦值操作符,以便直接克隆向量
    T remove(Rank r);                        //刪除秩爲r的元素
    int remove(Rank lo, Rank hi);  //刪除秩在區間[lo, hi)之內的元素
    Rank insert(Rank r, T const& e);                      //插入元素
    Rank insert(T const& e) { return insert(_size, e); }  //默認作爲末元素插入
    void sort(Rank lo, Rank hi);                          //對[lo, hi)排序
    void sort() { sort(0, _size); }                       //整體排序
    void unsort(Rank lo, Rank hi);                        //對[lo, hi)置亂
    void unsort() { unsort(0, _size); }                   //整體置亂
    int deduplicate();                                    //無序去重
    int uniquify();                                       //有序去重
                                                          // 遍歷
    void traverse(void (*)(T&));  //遍歷(使用函數指針,只讀或局部性修改)
    template <typename VST>
    void traverse(VST&);  //遍歷(使用函數對象,可全局性修改)
};                        // Vector

#endif
發佈了6 篇原創文章 · 獲贊 2 · 訪問量 2979
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章