定义
线性表(linear list ):零个或多个数据元素的有限序列。
线性表也称有序表(ordered list ),它的每一个实例都是元素的一个有序集合。每一个实例的形式为(
e
0
e_0
e0?,
e
1
e_1
e1?, …,
e
n
?
1
e_n-_1
en??1?),其中n 是有穷自然数,是线性表的长度或大小,
e
i
e_i
ei?是线性表的元素,i 是元素
e
i
e_i
ei?的索引。
元素本身的结构与线性表的结构无关,在较复杂的线性表中,一个数据元素可以由若干个数据项组成。
当n = 0 时,线性表称为空表;当n > 0 时,
e
0
e_0
e0?是线性表的第0个元素或首元素,
e
n
?
1
e_n-_1
en??1?是线性表的最后一个元素。可以认为
e
i
?
1
e_i-_1
ei??1?先于
e
i
e_i
ei?,
e
i
e_i
ei?先于
e
i
+
1
e_i+_1
ei?+1?,称
e
i
?
1
e_i-_1
ei??1?是
e
i
e_i
ei?的直接前驱元素,称
e
i
+
1
e_i+_1
ei?+1?是
e
i
e_i
ei?的直接后继元素。除了这种先后关系之外,线性表不再有其他关系。
?
抽象数据类型(ADT)linearList
ADT linearList
{
Data
有限个元素的有序集合
Operation
empty(): 若表空,则返回true,否则返回false
size(): 返回线性表的大小(即元素个数)
get(index): 返回线性表中索引为index的元素
indexOf(x): 返回线性表中第一次出现的x的索引。若x不存在,则返回-1
erase(index): 删除索引为index的元素,索引大于index的元素其索引减1
insert(index, x): 把x插入线性表中索引为index的位置上,索引大于等于index的元素其索引加1
output(): 从左到右输出表元素
}
?
抽象类 linearList
对上面的抽象数据类型ADT linearList ,除了用上面的非形式语言方法来描述,我们还可以用抽象类来描述。
template<class T>
class linearList
{
public:
virtual ~linearList() {};
virtual bool empty() const = 0;
virtual int size() const = 0;
virtual T& get(int theIndex) const = 0;
virtual int indexOf(const T& theElement) const = 0;
virtual void erase(int theIndex) = 0;
virtual void insert(int theIndex, const T& theElement) = 0;
virtual void output(ostream& out) const = 0;
};
?
数组描述的线性表
在数组描述中,用数组来存储线性表的元素。假定使用一个一维数组element 来存储线性表的元素,arrayLength 表示数组长度或容量。数组的每一个位置都可以存储线性表的一个元素,我们需要一个映射,使线性表的一个元素对应数组的一个位置。
?
要创建一个数组类,以实现抽象数据类型linearList ,必须首先选择数组element 的类型和数组长度。使用模板类可以很好地解决第一个问题,使用动态数组可以很好地解决第二个问题,首先按照用户估计的长度创建数组,然后在数组空间不足的情况下,动态地增加数组长度。
?
变长一维数组
一维数组a ,线性表元存储在a[0:n-1] 中。要增加或减少这个数组的长度,首先要建立一个具有新长度的数组,然后把数组a 的元素复制到这个新数组,最后改变数组a 的值,使它能够引用新数组。
template<class T>
void changeLength1D(T*& a, int oldLength, int newLength)
{
if (newLength < 0)
throw illegalParameterValue("new length must be >= 0");
T* temp = new T[newLength]; // 新数组
int number = min(oldLength, newLength); // 需要复制的元素个数
copy(a, a + oldLength, temp);
delete[] a; // 释放老数组的内存空间
a = temp;
}
创建一个长度为m的数组所需的时间为O(1) 。
当数组满而需要加大数组长度时,数组长度常常是要加倍的,这个过程称为数组倍增(array doubling )。数组倍增的时间,从渐进意义上考量,不会大于元素插入的总时间。
?
类 arrayList
我们定义一个C++抽象类linearList 的派生类arrayList ,它利用映射公式:location(i) = i 实现ADT linearList ,arrayList 是一个具体类,所以它必须实现抽象类linearList 的所有方法。不仅如此,它还包含基类linearList 没有声明的方法,例如,capacity 和checkIndex 。
类arrayList 的定义
template<class T>
class arrayList : public linearList<T>
{
public:
arrayList(int initialCapacity = 10);
arrayList(const arrayList<T>&);
~arrayList() { delete[] element; }
bool empty() const { return listSize == 0; }
int size() const { return listSize; }
T& get(int theIndex) const;
int indexOf(const T& theElement) const;
void erase(int theIndex);
void insert(int theIndex, const T& theElement);
void output(ostream& out) const;
int capacity() const { return arraylength; }
protected:
void checkIndex(int theIndex) const;
T* element;
int arraylength;
int listSize;
};
?
类arrayList 的构造函数
template<class T>
arrayList<T>::arrayList(int initialCapacity)
{
if (initialCapacity < 1)
{
ostringstream s;
s << "Initial capacity = " << initialCapacity << " must be > 0";
throw illegalParameterValue(s.str());
}
arraylength = initialCapacity;
element = new T[arraylength];
listSize = 0;
}
template<class T>
arrayList<T>::arrayList(const arrayList<T>& theList)
{
arrayLength = theList.arraylength;
listSize = theList.listSize;
element = new T[arraylength];
copy(theList.element, theList.element + listSize, element);
}
?
类arrayList —checkIndex、get、indexOf 函数的实现
template<class T>
void arrayList<T>::checkIndex(int theIndex) const
{
if (theIndex < 0 || theIndex >= listSize)
{
ostringstream s;
s << "index = " << theIndex << " size = " << listSize;
throw illegalParameterValue(s.str());
}
}
template<class T>
T& arrayList<T>::get(int theIndex) const
{
checkIndex(theIndex);
return element[theIndex];
}
template<class T>
int arrayList<T>::indexOf(const T& theElement) const
{
int theIndex = (int)(find(element, element + listSize, theElement) - element);
return (theIndex == listSize) ? -1 : theIndex;
}
?
删除一个元素
为了从线性表中删除索引为theIndex 的元素,首先要确定线性表包含这个元素,然后删除这个元素。若没有这个元素,则抛出类型为illegalIndex 的异常。
template<class T>
void arrayList<T>::erase(int theIndex)
{
checkIndex(theIndex);
copy(element + theIndex + 1, element + listSize, element + theIndex);
element[--listSize].~T();
}
?
插入一个元素
要在表中索引为theIndex 的位置上插入一个新元素,首先把索引从theIndex 到listSize-1 的元素向右移动一个位置,然后将新元素插入索引为theIndex 的位置,最后将变量listSize 的值加1,copy_backward 函数时从最右端开始移动元素的。
template<class T>
void arrayList<T>::insert(int theIndex, const T& theElement)
{
checkIndex(theIndex);
if (listSize == arrayLength)
{
changeLength1D(element, arrayLength, 2 * arrayLength);
arrayLength *= 2;
}
copy_backward(element + theIndex, element + listSize, element + listSize + 1);
element[theIndex] = theElement;
listSize++;
}
|