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