形如A0,A1,...An的称为表list。她只有一个开始节点和终止节点,其他的为中间节点。并且节点之间的前驱和后继关系具有反对称性和传递性。 我们讨论数据结构关注逻辑结构,存储结构和运算。 逻辑结构: 长度 表头 表尾 当前节点 存储结构: 顺序存储(数组),顺序表 链式存储(单链表,双向链表,循环链表),链表 操作: 线性表(不限制操作) 栈stack(先进后出) 队列queue(后进先出) 在c++ STL中,有采用数组实现的vector和采用双向链表实现的list。
先看下顺序表的数据结构:
class arrList : public List<T> { // 顺序表,向量
private: // 线性表的取值类型和取值空间
T * aList ; // 私有变量,存储顺序表的实例
int maxSize; // 私有变量,顺序表实例的最大长度
int curLen; // 私有变量,顺序表实例的当前长度
int position; // 私有变量,当前处理位置
public:
arrList(const int size) { // 创建新表,设置表实例的最大长度
maxSize = size; aList = new T[maxSize];
curLen = position = 0;
}
~arrList() { // 析构函数,用于消除该表实例
delete [] aList;
}
void clear() { // 将顺序表存储的内容清除,成为空表
delete [] aList; curLen = position = 0;
aList = new T[maxSize];
}
int length(); // 返回当前实际长度
bool append(const T value); // 在表尾添加元素 v
bool insert(const int p, const T value); // 插入元素
bool delete(const int p); // 删除位置 p 上元素
bool setValue(const int p, const T value); // 设元素值
bool getValue(const int p, T& value); // 返回元素
bool getPos(int &p, const T value); // 查找元素
};
链表的数据结构:
template <class T> class lnkList : public List<T> {
private:
Link<T> * head,
*tail; // 单链表的头、尾指针
Link<T> *setPos(const int p); // 第p个元素指针
public:
lnkList(int s); // 构造函数
~lnkList(); // 析构函数
bool isEmpty(); // 判断链表是否为空
void clear(); // 将链表存储的内容清除,成为空表
int length(); // 返回此顺序表的当前实际长度
bool append(cosnt T value); // 表尾添加一个元素 value,表长度增 1
bool insert(cosnt int p, cosnt T value); // 位置 p 上插入一个元素
bool delete(cosnt int p); // 删除位置 p 上的元素,表的长度减 1
bool getValue(cosnt int p, T& value); // 返回位置 p 的元素值
bool getPos(int &p, const T value); // 查找值为 value 的元素
}
插入于删除的代码实现,留给读者去思考。
线性表的主要实现方式比较
-
顺序表的主要优点
- 没有使用指针,不用花费额外开销
- 线性表元素的读访问非常简洁便利
-
链表的主要优点
- 无需事先了解线性表的长度
- 允许线性表的长度动态变化
- 能够适应经常插入删除内部元素的情况
-
总结
- 顺序表是存储静态数据的不二选择
- 链表是存储动态变化数据的良方
顺序表和链表比较
- 顺序表
- 插入、删除运算时间代价 O(n),查找则可常数时间完成
- 预先申请固定长度的连续空间
- 如果整个数组元素很满,则没有结构性存储开销
- 链表
- 插入、删除运算时间代价 O(1),但找第i个元素运算时间代价 O(n)
- 存储利用指针,动态地按照需要为表中新的元素分配存储空间
- 每个元素都有结构性存储开销
顺序表和链表存储密度
n 表示线性表中当前元素的数目, P 表示指针的存储单元大小(通常为 4 bytes) E 表示数据元素的存储单元大小 D 表示可以在数组中存储的线性表元素的最大数目
- 空间需求
- 顺序表的空间需求为 DE
- 链表的空间需求为 n(P + E)
- n 的临界值,即 n > DE / (P+E)
- n 越大,顺序表的空间效率就更高
- 如果P = E,则临界值为 n = D / 2
应用场合的选择
- 顺序表不适用的场合
- 经常插入删除时,不宜使用顺序表
- 线性表的最大长度也是一个重要因素
- 链表不适用的场合
- 当读操作比插入删除操作频率大时,不应选择链表
- 当指针的存储开销,和整个结点内容所占空间相比其比例较大时,应该慎重选择
顺序表和链表的选择
- 顺序表
- 结点总数目大概可以估计
- 线性表中结点比较稳定(插入删除少)
- n > DE / (P + E)
- 链表
- 结点数目无法预知
- 线性表中结点动态变化(插入删除多)
- n < DE / (P + E)
可以思考下一元多项式的实现,对于系数稠密情况采用顺序表比较好,而对于稀疏琴况明显采用链表好