相比于我用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