C++數據結構: 順序表 詳細實現

相比於我用Java實現的順序表,用C++實現更加繁複,因爲要自己管理內存,但思想還是一樣的。代碼複雜度的提升也意味着性能上的提升。

C++的迭代器跟Java不同,它的底層是個指針。開始喜歡Java迭代器的直白,然後慢慢喜歡上C++迭代器的靈活,各有千秋。

以下的CppArrayList,函數的複雜度都寫在旁邊。

#ifndef CPP_ARRAYLIST_H
#define CPP_ARRAYLIST_H

#include <iostream>
#include <sstream>
#include "ArrayListException.h"
using namespace std;

template <typename T>
class CppArrayList {
private:
    //存放元素的數組
    T *items;
    //目前擁有的元素數量
    int list_size;
    //容量,最大能存放元素數量
    int list_capacity;
    //檢查索引、下標。
    void checkIndex(int idx) const;
    //改變容量,可以傳入負數來減少容量,但最終必須大於等於元素個數。
    //突然感覺把這個函數改名成addCapacity比較好,但私有方法,不改啦!
    void changeCapacity(int newCapacity);

public:
    //默認構造函數,初始容量爲10
    explicit CppArrayList(int i = 10);
    //拷貝構造函數
    CppArrayList(const CppArrayList<T> &rlist);
    //析構函數
    ~CppArrayList() {
        delete[] items;
    }
    //轉換成數組
    T *toArray() const;

    //CppArrayList是否爲空?
    bool isEmpty() const {
        return list_size == 0;
    }
    //返回CppArrayList中的元素個數。
    int size() const {
        return list_size;
    }
    //返回CppArrayList的容量
    int capacity() const {
        return list_capacity;
    }
    //清空元素,容量不變
    void clear();
    //將多餘空間刪除
    void trimToSize();
    //返回索引處元素的引用
    //這裏就不定義const版本了,代碼類似。
    T &get(int idx) const {
        checkIndex(idx);
        return items[idx];
    }
    //下標運算符,跟get()一樣
    T &operator[](int idx) const;
    //當且僅當元素數量和對應位置元素值都相同,兩個容器才相等
    bool operator==(const CppArrayList<T> &c) const;
    bool operator!=(const CppArrayList<T> &c) const;
    //string式大小比較
    bool operator<(const CppArrayList<T> &c) const;
    bool operator>(const CppArrayList<T> &c) const;
    //拷貝賦值操作符
    CppArrayList<T> &operator=(const CppArrayList<T> &c);
    //從CppArrayList中查找元素e,若存在,返回第一個位置的索引;否則返回-1
    int indexOf(const T &e) const;
    int lastIndexOf(const T &e) const;
    //在CppArrayList的尾部添加元素e
    void add_back(const T &e) {
        insert(list_size, e);
    }
    //在索引位置前插入元素e
    void insert(int idx, const T &e);
    //對索引處的元素賦值
    void setVal(int idx, const T &e);
    //刪除索引出的元素
    void erase(int idx);
    //刪除索引從s到r的元素,包含s而不包含r。eraseRange(0,list_size)相當於clear()
    //或者也可以把這個函數看爲,從s開始(包含),刪除其和其後r個元素。
    void eraseRange(int s, int r);
    //將容器逆置。
    void reverse();
    void it_reverse();
    //打印數組
    void print_arr() {
        for (int i = 0; i<list_size; i++)
            cout << items[i] << " ";
        cout << endl;
    }
    //交換2個CppArrayList
    void swap(CppArrayList<T> &c);
    //雙向迭代器
    class iterator;
    iterator begin() {
        return iterator(items);
    }

    iterator end() {
        return iterator(items + list_size);
    }

    class iterator
    {
    public:
        //爲了與STL算法兼容,C++要求的類型
        typedef bidirectional_iterator_tag iterator_category;
        typedef T value_type;
        typedef ptrdiff_t difference_type;
        typedef T* pointer;
        typedef T& reference;

        iterator(T *p = 0) :position(p) {}
        //解引用
        T &operator*() const {
            return *position;
        }
        T *operator->() const {
            return &*position;
        }
        //遞增
        iterator operator++() {
            //前置++
            ++position;
            return *this;
        }
        iterator operator++(int) {
            //後置++
            iterator it = *this;
            ++position;
            return it;
        }
        //遞減
        iterator& operator--() {
            //前置--
            --position;
            return *this;
        }
        iterator operator--(int) {
            //後置--
            iterator old = *this;
            --position;
            return old;
        }

        bool operator!=(const iterator rit) const {
            return position != rit.position;
        }
        bool operator==(const iterator rit) const {
            return position == rit.position;
        }
    protected:
        T *position;
    };

};

template <typename  T>
CppArrayList<T>::CppArrayList(int i)
{
    if (i < 1)
        throw illegalCapacity("capacity mustn't < 1");
    items = new T[i];
    list_size = 0;
    list_capacity = i;
}


template <typename T>//O(N)
CppArrayList<T>::CppArrayList(const CppArrayList<T> &rlist)
{
    list_size = rlist.list_size;
    list_capacity = rlist.list_capacity;

    //替新對象開闢內存空間,將元素拷貝
    items = new T[list_capacity];
    for (int i = 0; i<list_size; i++)
    {
        items[i] = rlist.items[i];
    }
}

template <typename T>//O(1)
void CppArrayList<T>::checkIndex(int idx) const
{//如果索引不在有效範圍內,就拋出invalidIndex異常。
    if (idx < 0 || idx >= list_size)
        throw invalidIndex();
}

template <typename T>//O(N)
void CppArrayList<T>::changeCapacity(int newCapacity)
{//把容量改爲newCapacity
 //檢查新分配容量大小,看是否能裝得下原本的元素。
    if (list_capacity + newCapacity < list_size) {
        ostringstream s;
        s << "容量分配錯誤,總容量不能小於: " << list_size << "!";
        throw illegalCapacity(s.str());
    }
    else
    {//重新分配容量
        list_capacity += newCapacity;
        T *newItems = new T[list_capacity];
        //拷貝舊數組
        for (int i = 0; i<list_size; i++)
            newItems[i] = items[i];
        //釋放舊數組內存
        delete[] items;
        items = newItems;
    }
}

template <typename T>//O(N)
void CppArrayList<T>::trimToSize()
{
    int N = 1;
    if (list_size>1)
        N = list_size;
    //計算N和list_capacity的差,這是要被釋放的空間數。
    int capacity_diff = N - list_capacity;
    changeCapacity(capacity_diff);
}

template <typename T>//O(N)
int CppArrayList<T>::indexOf(const T &e) const
{//從頭遍歷CppArrayList,返回第一個相同元素的索引,未找到則返回-1;
    for (int i = 0; i<list_size; i++)
        if (items[i] == e)
            return i;
    return -1;
}

template <typename T>
int CppArrayList<T>::lastIndexOf(const T &e) const
{
    for (int i = list_size - 1; i >= 0; --i)
        if (items[i] == e)
            return i;
    return -1;
}

template <typename T>//O(N)
CppArrayList<T> &CppArrayList<T>::operator=(const CppArrayList<T> &c)
{
    T *newItems = new T[c.list_capacity];
    for (int i = 0; i<c.list_size; i++) {
        newItems[i] = c.get(i);
    }
    delete[] items;
    items = newItems;
    list_size = c.list_size;
    list_capacity = c.list_capacity;
    return *this;
}

template <typename T>//O(1)
T &CppArrayList<T>::operator[](int idx) const
{
    return get(idx);
}

template <typename T>//O(N)
bool CppArrayList<T>::operator==(const CppArrayList<T> &c) const
{
    if (list_size != c.list_size)
        return false;
    for (int i = 0; i<list_size; i++)
        if (items[i] != c.get(i))
            return false;
    return true;
}

template <typename T>//O(N)
bool CppArrayList<T>::operator!=(const CppArrayList<T> &c) const
{
    return !operator==(c);
}

template <typename T>//O(N)
bool CppArrayList<T>::operator<(const CppArrayList<T> &c) const
{
    if (list_size > c.list_size)
        return false;
    //左側容器list_size小於等於右側的情況下
    for (int i = 0; i<list_size; i++) {
        if (items[i] >= c.get(i))
            return false;
    }
    return true;
}

template <typename T>//O(N)
bool CppArrayList<T>::operator>(const CppArrayList<T> &c) const
{//重載">"操作符,這裏爲了方便調用"<"和"=="操作符,犧牲了一點效率。
    return !operator<(c) && !operator==(c);
}

template <typename T>//O(1) O(N)
void CppArrayList<T>::insert(int idx, const T &e)
{//在索引位置前插入元素e
 //注意這裏的索引檢查跟checkIndex()的不一樣。
    if (idx < 0 || idx > list_size) {
        ostringstream s;
        s << "不能在索引" << idx << "處插入元素!索引範圍爲0~" << list_size;
        throw invalidIndex(s.str());
    }
    //如果CppArrayList滿了,就將容量變成兩倍。這裏用了changeCapacity的默認實參
    if (list_size == list_capacity)
        changeCapacity(list_capacity);

    //將索引位置的所有元素都向後移動一位,然後將索引位置賦值爲e
    for (int i = list_size; i>idx; i--)
        items[i] = items[i - 1];
    items[idx] = e;
    ++list_size;
}

template <typename T>//O(1)
void CppArrayList<T>::setVal(int idx, const T &e)
{
    checkIndex(idx);
    get(idx) = e;
}


template <typename T>//O(N)
void CppArrayList<T>::erase(int idx)
{
    checkIndex(idx);
    for (int i = idx; i<list_size - 1; i++)
        items[i] = items[i + 1];
    //基本數據類型不能調用析構函數,但是泛型能用
    items[list_size--].~T();
}

template <typename T>//O(N)
void CppArrayList<T>::eraseRange(int s, int r)
{//寫這個函數讓我想起了希爾排序~
    if (s>r) {
        ostringstream s;
        s << s << "必須小於等於" << r;
        throw invalidIndex(s.str());
    }
    checkIndex(s);
    checkIndex(r - 1);

    int n = r - s;
    for (int i = s; i<list_size - n; i++) {
        items[i] = items[i + n];
        items[i + n].~T();
    }
    list_size -= n;
}

template <typename T>//O(N)
T *CppArrayList<T>::toArray() const
{
    T *arr = new T[this->list_capacity];
    for (int i = 0; i<this->list_size; i++)
        *(arr + i) = this->get(i);
    return arr;
}

template <typename T>//O(1)
void CppArrayList<T>::swap(CppArrayList<T> &c)
{
    T *itemp = this->items;
    this->items = c.items;
    c.items = itemp;

    int stemp = this->list_size;
    this->list_size = c.list_size;
    c.list_size = stemp;

    int ctemp = this->list_capacity;
    this->list_capacity = c.list_capacity;
    c.list_capacity = ctemp;
}

template <typename T>//O(N)  如果是Java,那直接list_size = 0;O(1)
void CppArrayList<T>::clear()
{//這兒不太好用懶惰刪除,沒有垃圾回收機制,會造成內存泄露。要不就用智能指針。
 //反正都要回收內存,那就手動吧!
    for (int i = list_size - 1; i >= 0; --i)
        erase(i);//這樣每次都要checkIndex,先這樣,給CPU找點事做。
}


template <typename T>//O(N)
void CppArrayList<T>::reverse()
{//這裏沒用迭代器,因爲它只定義了==和!=操作符。不如用索引方便直接
    for (int b = 0, e = list_size - 1; b <= e; ++b, --e) {
        T temp = items[b];
        items[b] = items[e];
        items[e] = temp;
    }
}

template <typename T>//O(N)
void CppArrayList<T>::it_reverse()
{//reverse()的迭代器版本~中間那個判斷的雖然不復雜,但實在不美觀~
    for (auto ib = begin(), ie = end(); ib != ie&&ib != --ie; ++ib) {
        T temp = *ib;
        *ib = *ie;
        *ie = temp;
    }
}

#endif
#ifndef ARRAY_LIST_EXCEPTION_H
#define ARRAY_LIST_EXCEPTION_H
#include <iostream>
#include <string>
using namespace std;


class illegalCapacity {
private:
    string err_msg;
public:
    illegalCapacity() :err_msg("illegal capacity") {}

    illegalCapacity(string s) :err_msg(s) {}

    void print_err() {
        cout << err_msg << endl;
    }
};

class invalidIndex {
private:
    string err_msg;
public:
    invalidIndex() :err_msg("invalid Index") {}

    invalidIndex(string s) :err_msg(s) {}

    void print_err() {
        cout << err_msg << endl;
    }
};

#endif
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章