版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
</div>
<!--一个博主专栏付费入口-->
<!--一个博主专栏付费入口结束-->
<link rel="stylesheet" href="https://csdnimg.cn/release/phoenix/template/css/ck_htmledit_views-4a3473df85.css">
<link rel="stylesheet" href="https://csdnimg.cn/release/phoenix/template/css/ck_htmledit_views-4a3473df85.css">
<div class="htmledit_views" id="content_views">
<h1><a name="t0"></a><span style="color:#f33b45;">一、线性表的介绍</span></h1>
二、数组实现线性表的方法
方式一
- 从数组的头端实现
- 例如有一个数组element[10],我们可以将元素{5,2,4,8,1}从其索引0处作为线性表的头开始实现,线性表的方向从左至右
方式二
- 从数组的尾端实现
- 接方式一,我们将数组element的最后一个索引处作为线性表的头开始实现,线性表的方向从右至左
方式三
- 从数组的指定索引处实现,我们可以从数组的指定索引处作为线性 表的头部开始实现
- 公式为:localtion(i)=(localtion(0)+i)%arrayLength
- localtion(0)是选取的线性表在数组中的第一个位置
- arrayLength是数组的长度
- 例如,下面我们将数组element的索引7作为线性表的头部开始从左至右实现,当元素超出数组的长度时,将元素从数组的索引0处开始插入。此处我们的公式为,localtion(i)=(7+i)%10
三、编码实现
异常类实现(illegalParameterValue)
- 这个异常类主要用来处理函数的参数异常
-
class illegalParameterValue
-
{
-
private:
-
string message;
-
public:
-
illegalParameterValue() : message(
"Illegal Parameter Value") {}
-
illegalParameterValue(
const
char* s) :message(s) {}
-
const char *what() {
-
return message.c_str();
-
}
-
};
修改数组长度函数(changeLength)
- 功能:该函数用来改变原来数组的长度,在函数中我们创建一个新的数组,然后将原数组拷贝进新数组,然后释放原数组所指向的地址空间(这个函数是全局函数,并不属于某一类)
- 参数:
- 参数1:数组指针引用
- 参数2:原数组的长度
- 参数3:新数组长度
- 函数的时间复杂度:创建一个长度为newLength的数组所需时间为Θ(1)。copy的时间复杂度为Θ(length)。所以,下面函数的总的时间复杂度为Θ(length)
-
template<
typename T>
-
void changeLength(T* &a, int oldLength, int newLength)
-
{
-
if (newLength <
0) {
-
throw illegalParameterValue(
"new length must be >=0");
-
}
-
-
T *arr =
new T[newLength];
-
int length = min(oldLength, newLength);
-
copy(a, a + length, arr);
-
delete [] a;
-
a = arr;
-
}
线性表类实现
- linearList是线性表的纯虚函数,其含有一系列线性表的操作方法;arrayList继承于linearList,是线性表的数组实现形式的类,其重写linearList的虚函数,并添加了自己的一些数据成员和方法
-
-
template<
typename T>
-
class linearList
-
{
-
public:
-
virtual ~linearList() {};
-
//当线性表为空时返回true
-
virtual bool empty() const =
0;
-
//返回线性表的元素个数
-
virtual int size() const =
0;
-
//返回索引theIndex的元素
-
virtual T& get(int theIndex)const =
0;
-
//返回元素theElement第一次出现时的索引
-
virtual int indexOf(const T& theElement) const =
0;
-
//删除索引为theIndex的元素
-
virtual void earse(int theIndex) =
0;
-
//把theElement插入线性表中索引为theIndex的位置上
-
virtual void insert(int theIndex, const T& theElement) =
0;
-
//把线性表插入输出流out
-
virtual void output(ostream& out)const =
0;
-
};
-
-
template<
typename T>
-
class arrayList :
public linearList<T>
-
{
-
public:
-
arrayList(
int initialCapacity =
10);
//构造函数
-
arrayList(
const arrayList<T>& other);
//拷贝构造
-
~arrayList();
//析构函数
-
-
bool empty()const override;
-
int size()const override;
-
T& get(int theIndex)const override;
-
int indexOf(const T& theElement)const override;
-
void earse(int theIndex)override;
-
void insert(int theIndex, const T& theElement)override;
-
void output(ostream& out)const override;
-
-
//返回数组的容量
-
int capacity()const;
-
protected:
-
T *element;
//存储线性表的一维数组
-
int arrayLength;
//一位数组的容量
-
int listSize;
//当前线性表的元素个数
-
protected:
-
void checkIndex(int theIndex)const;
//检查索引theIndex是否有效
-
};
构造函数
- 构造函数首先判断传入的数组长度是否有效,如果无效抛出一个异常。如果有效,初始化数组
- 时间复杂度:
- 如果T是基本数据类型,那么构造函数的时间复杂度是O(1)
- 如果T是用户自定义类型,那么构造函数的时间复杂度是O(initialCapacity)。因为数组的每一个元素都是自定义类型,需要调用构造函数
-
template<
typename T>
-
arrayList<T>::arrayList(
int initialCapacity)
-
{
-
if (initialCapacity <
1) {
-
ostringstream s;
-
s <<
"Initial capacity is " << initialCapacity <<
",Must be >0";
-
throw illegalParameterValue(s.str().c_str());
-
//str()将ostringstream转换为string,c_str()将string转换为const char*
-
}
-
-
arrayLength = initialCapacity;
-
element =
new T[arrayLength];
-
listSize =
0;
-
}
拷贝构造函数
- 初始化一些数据成员,然后调用copy函数进行拷贝
- 时间复杂度:为O(listSize),其中listSize是要复制的线性表的大小
-
template<
typename T>
-
arrayList<T>::arrayList(
const arrayList<T>& other)
-
{
-
arrayLength = other.arrayLength;
-
listSize = other.listSize;
-
-
element =
new T[arrayLength];
-
copy(other.element, other.element + listSize, element);
-
}
析构函数
-
template<
typename T>
-
arrayList<T>::~arrayList()
-
{
-
delete[] element;
-
}
empty、size、capacity
- 下面3个函数的时间复杂度都是O(1)
-
template<
typename T>
-
bool arrayList<T>::empty()
const
-
{
-
return listSize ==
0;
-
}
-
-
template<
typename T>
-
int arrayList<T>::size()
const
-
{
-
return listSize;
-
}
-
-
template<
typename T>
-
int arrayList<T>::capacity()
const
-
{
-
return arrayLength;
-
}
检索下标合格性(checkIndex)
- 时间复杂度是Θ(1)
-
template<
typename T>
-
void arrayList<T>::checkIndex(
int theIndex)
const
-
{
-
if ((theIndex<
0) || (theIndex>=arrayLength)) {
-
ostringstream s;
-
s <<
"index=" << theIndex <<
",size=" << arrayLength;
-
throw illegalParameterValue(s.str().c_str());
-
}
-
}
返回指定索引处值(get)
- 时间复杂度是Θ(1)
-
template<
typename T>
-
T& arrayList<T>::get(
int theIndex)
const
-
{
-
checkIndex(theIndex);
-
-
return element[theIndex];
-
}
返回元素在线性表的指定位置(indexOf)
- 时间复杂度是O(listsize)
-
template<
typename T>
-
int arrayList<T>::indexOf(
const T& theElement)
const
-
{
-
int index;
-
//find成功返回查找的迭代器位置,失败返回参数2
-
index = (
int)(find(element, element + listSize, theElement) - element);
-
//如果没有找到
-
if (index == listSize) {
-
return -
1;
-
}
-
else
-
return index;
-
}
删除指定索引处的元素(earse)
- 时间复杂度:
<ul><li>如果checkIndex抛出异常,那么时间复杂度是Θ(1)</li> <li>如果元素存在,那么要移动的元素个数是listSize-theIndex,所以时间复杂度是Θ(listSize-theIndex)(假设每一个元素移动的时间复杂度是O(1))</li> <li>因此,全部时间复杂度是O(listSize-theIndex)</li> </ul></li>
-
template<
typename T>
-
void arrayList<T>::earse(
int theIndex)
-
{
-
checkIndex(theIndex);
-
-
//参数12分别为要移动的区间的起始迭代位置和尾后迭代器
-
copy(element+ theIndex+
1, element+ listSize, element + theIndex);
-
-
element[--listSize].~T();
-
}
在指定索引处插入元素(insert)
- 这里没有没有使用copy函数来移动元素,而是使用copy_backward来移动元素
- 时间复杂度:
- 如果抛出异常,那么时间复杂度是Θ(1)
- 如果数组需要加长,那么时间复杂度是Θ(arrayLength)=Θ(listSize)
- copy_backward移动数组元素,时间复杂度是Θ(listSize-theIndex)
- 综上所述,下面函数的时间复杂度是O(listSize)
-
template<
typename T>
-
void arrayList<T>::insert(
int theIndex,
const T& theElement)
-
{
-
if ((theIndex<
0) || (theIndex>listSize)) {
-
ostringstream s;
-
s <<
"index=" << theIndex <<
",size=" << arrayLength;
-
throw illegalParameterValue(s.str().c_str());
-
}
-
-
if (listSize == arrayLength) {
-
changeLength(element, arrayLength, arrayLength *
2);
-
arrayLength *=
2;
-
}
-
-
//会复制前两个迭代器参数指定的序列。第三个参数是目的序列的结束迭代器
-
copy_backward(element+ theIndex, element+ listSize, element + listSize+
1);
-
element[theIndex] = theElement;
-
listSize++;
-
}
打印线性表元素(output)
- 时间复杂度:如果插入一个元素的时间是O(1),那么这个代码的时间复杂度是O(listSize)
-
template<
typename T>
-
void arrayList<T>::output(ostream& out)
const
-
{
-
copy(element, element+ listSize,ostream_iterator<T>(out,
" "));
-
}
重载流插入符<<
- 这个是全局函数
-
template<
typename T>
-
ostream&
operator<<(ostream& out,
const arrayList<T>& x)
-
{
-
x.output(out);
-
return out;
-
}
改写earse函数
- 为了能够在数组元素减少时释放一些数组空间,我们修改earse方法,当listSize<arrayLength/4时,将数组的长度减少到arrayLength/2
-
template<
typename T>
-
void arrayList<T>::earse(
int theIndex)
-
{
-
checkIndex(theIndex);
-
-
copy(element+ theIndex+
1, element+ listSize, element + theIndex);
-
-
if (listSize < (arrayLength /
4)) {
-
changeLength(element, arrayLength, arrayLength /
2);
-
}
-
-
element[--listSize].~T();
-
}
演示效果
-
int main()
-
{
-
linearList<
int>*
list = (linearList<
int>*)
new arrayList<
int>();
-
-
cout <<
"Current size:"<<
list->size()<<
"\n"<<
endl;
-
cout <<
"Current list is:";
-
list->output(
cout);
-
-
list->insert(
0,
1);
-
list->insert(
0,
2);
-
-
cout <<
"Current size:" <<
list->size() <<
"\n" <<
endl;
-
cout <<
"Current list is:";
-
list->output(
cout);
-
-
cout <<
"The index of 2 is:" <<
list->indexOf(
2)<<
"\n"<<
endl;
-
-
list->earse(
1);
-
cout <<
"Current list is:";
-
list->output(
cout);
-
return
0;
-
}
- 备注:此处我们使用抽象类linearList指向与派生类arrayList,所以不能使用基类的capacity()函数
四、迭代器的设计
- 我们定义了一个双向迭代器。这个迭代器是类arrayList的公有成员,此外我们还为arrayList定义了两个公有方法begin()和end()
-
template<
typename T>
-
class arrayList :
public linearList<T>
-
{
-
/*...*/
-
public:
-
class iterator;
-
iterator begin() {
-
return iterator(element);
-
}
-
iterator end() {
-
return iterator(element + listSize);
-
}
-
class iterator
-
{
-
public:
-
typedef bidirectional_iterator_tag iterator_category;
-
typedef T value_type;
-
typedef
ptrdiff_t difference_type;
-
typedef T* pointer;
-
typedef T& reference;
-
public:
-
iterator(T* thePosition =
0) {
-
position = thePosition;
-
}
-
/*~iterator() {
-
//析构函数出错,不知道为什么,待续
-
if (position) {
-
delete[] position;
-
position = nullptr;
-
}
-
}*/
-
-
T&
operator*()
const {
-
return *position;
-
}
-
T*
operator->()
const {
-
return &*position;
-
}
-
-
iterator&
operator++() {
-
++position;
-
return *
this;
-
}
-
iterator
operator++(
int) {
-
iterator old = *
this;
-
++position;
-
return old;
-
}
-
-
iterator&
operator--() {
-
--position;
-
return *
this;
-
}
-
iterator
operator--(
int) {
-
iterator old = *
this;
-
--position;
-
return old;
-
}
-
-
bool
operator==(
const iterator other)
const {
-
return position == other.position;
-
}
-
bool
operator!=(
const iterator other)
const {
-
return position != other.position;
-
}
-
protected:
-
T *position;
-
};
-
/*...*/
-
};
演示案例
-
int main()
-
{
-
arrayList<
int>
list;
-
list.insert(
0,
1);
-
list.insert(
1,
2);
-
list.insert(
2,
3);
-
list.insert(
3,
4);
-
-
arrayList<
int>::iterator iter;
-
for (iter =
list.begin(); iter !=
list.end(); ++iter) {
-
cout <<*iter <<
" ";
-
}
-
cout <<
"\n" <<
endl;
-
return
0;
-
}
五、总结
- 这次在设计的时候,迭代器的析构函数总是出错,不知道原因是什么,于是我把其注释掉了(待续解决)
- 迭代器的知识见:https://blog.csdn.net/qq_41453285/article/details/85802431
- copy、insert算法见文章:https://blog.csdn.net/qq_41453285/article/details/96005305
- 代码下载:https://download.csdn.net/download/qq_41453285/11992558