数据结构和算法——3.列表(List)
列表(List)
线性表的定义
➢ 线性表:简称表,是n(n≥0)个具有相同类型的数据元素的有限序列
➢ 线性表的长度:线性表中数据元素的个数
➢ 空表:长度等于零的线性表,记为:𝐿 = ()
➢ 非空表记为:𝐿 = e0, e1, … , ei-1, ei , … , en-1
其中, ei(0 ≤ 𝑖 < 𝑛)称为数据元素;
下角标 i 表示该元素在线性表中的位置或序号 。
线性表的特性
➢ 有限性:线性表中数据元素的个数是有穷的。
➢ 相同性:线性表中数据元素的类型是同一的。
➢ 顺序性:线性表中相邻的数据元素𝑒𝑖−1和𝑒𝑖之间存在序偶关系 𝑒𝑖−1, 𝑒𝑖 ,
即𝑒𝑖−1是𝑒𝑖的前驱,𝑒𝑖是𝑒𝑖−1的后继;𝑒0无前驱,𝑒𝑛−1无后继,其它每个
元素有且仅有一个前驱和一个后继。
➢ (a)结点集N中有一个唯一的开始结点,它没有前驱,但有一个唯一的后继;
➢ (b)对于有限集N, 它存在一个唯一的终止结点,该结点有一个唯一的前驱而没有后继;
➢ (c)其它的结点皆称为内部结点,每一个内部结点既有一个唯一的前驱,也有一个唯一的后继;
➢ (d)线性表所包含的结点个数称为线性表的长度,它是线性表的一个重要参数;长度为0的线性表称为空表;
线性表的操作
➢ LenList(L):求表L的长度
➢ GetElem(L, i):取表L中第i个数据元素
➢ SearchElem(L, e):按值查找
➢ InsertElem(&L, i, e):在表L中第i个位置插入新的数据元素e
➢ DeleteElem(&L, i):删除表L中第i个数据元素
线性表的ADT (Abstract Data Type)
Operations
- 求表的长度:size(L)
初始条件:线性表L已存在
操作结果:返回L中数据元素个数
-
取表L中第i个数据元素:GetElem(L, i)
初始条件:线性表L已存在, 1≤i≤LenList(L)
操作结果:返回L中第i个数据元素的值
-
按值查找:SearchElem(L, e)
初始条件:线性表已存在
操作结果:若e存在表中,返回第一个e的位置,否则返回0
-
在L中第i个位置插入新的数据元素e :InsertElem(&L, i, e)
初始条件:线性表L已存在, 1≤i≤LenList(L)+1
操作结果:在L中第i个位置插入新的数据元素e,表长加1
-
删除表中第i个数据元素:DeleteElem(&L, i)
初始条件:线性表已存在且非空,1≤i≤LenList(L)
操作结果:删除L中第i个位置的数据元素,表长减1
两种类型的存储结构
- 顺序存储结构:
➢ 用一组连续的存储单元依次存储数据元素
➢ 数据元素之间的逻辑关系由元素的存储位置来表示。
- 链接存储结构:
➢ 用一组任意的存储单元存储数据元素
➢ 数据元素之间的逻辑关系用指针来表示
基于两种存储结构的实现
➢ 顺序表(Sequence List, Array)
• 线性表的顺序存储结构,指的是用一段地址连续的存储单元依次存储线性表的数据元素
➢ 链表(Linked List)
• 线性表的链式存储结构,指的是用一组任意的存储单元存储线性表的数据元素,
这组存储单元可以是连续的,也可以是不连续的。
顺序表实现:
template<class T>
class arrayList : public linearList<T> {
public:
// 构造函数、复制构造函数和析构函数
arrayList(int initialCapacity = 10);
arrayList(const arrayList<T>&);
~arrayList() {delete [] element;}
// ADT 方法
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);
protected:
void checkIndex(int theIndex) const;
T* element; // 存储线性表元素的一维数组
int arrayLength; // 一维数组的容量
int listSize; // 线性表的元素个数
}
顺序表的运算
➢ 插入元素运算
• void insert(int theIndex, const T& theElement);
➢ 删除元素运算
• void erase(int theIndex);
➢ 查找元素运算
• T& get(int theIndex) const; //按位置查找
• int indexOf(const T& theElement) const; //按值查找
在位置i 插入元素e:
template<class T>
void arrayList<T>::insert(int theIndex, const T &theElement) {
// 在索引 theIndex处插入元素 theElement
if (theIndex < 0 || theIndex > listSize) {
// 无效索引
}
if(listSize == arrayLength) {
// 数组空间已满,数组长度倍增。这不是基础内容
}
// 把元素向右移动一个位置
copy_backward(element + theIndex, element + listSize, element + listSize + 1);
element[theIndex] = theElement;
listSize++;
}
删除位置theIndex的元素:
template<class T>
void arrayList<T>::erase(int theIndex) {
// 删除其索引为theIndex的元素
// 如果该元素不存在,则抛出异常 illegalIndex
checkIndex(theIndex);
// 有效索引,移动其索引大于 theIndex 的元素
copy(element + theIndex +1, element + listSize, element + theIndex);
element[--listSize].~T(); // 调用析构函数
}
插入和删除操作的比较
➢ 与顺序表的长度和位置相关
➢ 插入:
• 最坏:i=1,移动次数为n
• 最好: i=表长+1,移动次数为0
• 平均:等概率情况下,平均移动次数n/2
➢ 删除:
• 最坏:i=1,移动次数为n-1
• 最好: i=表长,移动次数为0
• 平均:等概率情况下,平均移动次数(n-1)/2
按位置查找:
template<class T>
T& arrayList<T>::get(int theIndex) const {
// 返回元素 theElement 第一次出现时的索引
// 若该元素不存在,则抛出异常
checkIndex(theIndex);
return element[theIndex];
}
按值查找:
template<class T>
int arrayList<T>::indexOf(const T &theElement) const {
// 返回元素 theElement 第一次出现时的索引
// 若该元素不存在,则返回 -1
// 查找元素 theElement
int theIndex = (int) (find(element, element + listSize, theElement) - element);
// 确定元素 theElement 是否找到
if (theIndex == listSize) {
// 没有找到
return -1;
} else {
return theIndex;
}
}
顺序表实现的特点
➢ 优点
• 顺序表的结构简单
• 顺序表的存储效率高,是紧凑结构,无须为表示节点间的逻辑关系而增加额外的存
储空间
• 顺序表是一个随机存储结构(直接存取结构)
➢ 缺点
• 在顺序表中进行插入和删除操作时,需要移动数据元素,算法效率较低。
• 对长度变化较大的线性表,或者要预先分配较大空间或者要经常扩充线性表,给
操作带来不方便。

浙公网安备 33010602011771号