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
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值