概述
- 线性表是最常用且最简单的一种数据结构。简言之,一个线性表是 n n n个数据元素的有限序列
- 线性表中的数据元素可以是各种各样的,但同一线性表中的元素必定具有相同的特性
- 线性表的是一个相当灵活的数据结构,它的长度可根据需要增长或缩短,即对线性表的数据元素不仅可以进行访问,还可进行插入和删除
线性表的特点:
- 插入方便 (优点)
- 空间利用率低
- 插入、删除麻烦
线性表的顺序表示
线性表的顺序表示指的是用一组地址连续的存储单元依次存储线性表的数据元素
上图即为线性表的顺序存储结构,从上图中可以看出,线性表中每个元素的地址差值均为一个固定大小的数值
k
k
k,这个
k
k
k即是所存储的数据类型大小,每个元素在内存空间中相邻存储,所以也被称为顺序存储
根据上图中的这种存储结构,我们可以很容易的想到数组这一结构,因此在实现中采用了数组作为存储线性表的结构;然后既然采用 C + + C++ C++实现,就将操作都放在类中声明,得出以下实现:
const int MAXSIZE = 100; //顺序表的最大长度
template <class DataType> //将其设为模板
class SeqList {
public:
SeqList() {length = 0;}
SeqList(DataType a[], int n);
~SeqList(){} //析构
int Getlength() {return length;} //获得线性表的长度
bool listempty(); //判断线性表是否为空
DataType GetElem(int i); //获得第i个位置的元素
int LocateElem(DataType e); //查找与给定值e相等的元素,找到则返回下标,否则返回0
void Insert(int i, DataType e); //在给定位置i插入元素e
DataType Delete(int i); //删除给定位置i的元素,并返回该值
void PrintList(); //打印线性表
private:
DataType data[MAXSIZE]; //存储线性表
int length; //线性表的实际长度
};
- 线性表的初始化
- 默认构造函数初始化:
SeqList() {length = 0;}
- 用构造函数来对线性表进行初始化:
template <class DataType>
SeqList<DataType>::SeqList(DataType a[], int n) {
if (n > MAXSIZE)
cout << "Excess length" << endl;
for (int i = 0; i < n; ++i)
data[i] = a[i];
length = n;
}
- 查找元素:
- 获得第 i i i个位置的元素:
template<class DataType>
DataType SeqList<DataType>::GetElem(int i) {
if (i > length || i < 1 || length == 0)
throw "wrong Location";
return data[i - 1];
}
- 查找指定元素 e e e:
template <class DataType>
int SeqList<DataType>::LocateElem(DataType e) {
if (length == 0)
return 0;
for (int i = 0; i < length; ++i) {
if (data[i] == e)
return i + 1; //查找到返回元素位置
}
return 0; //未查找到则返回0
}
- 插入与删除:
- 插入:
template <class DataType>
void SeqList<DataType>::Insert(int i, DataType e) {
if (length >= MAXSIZE)
throw "Overflow";
if (i < 1 || i > length + 1)
throw "wrong Location";
if (i <= length) {
for (int j = length; j >= i; --j)
data[j] = data[j - 1];
}
data[i - 1] = e;
++length;
}
- 删除:
template <class DataType>
DataType SeqList<DataType>::Delete(int i) {
if (length == 0)
throw "Underflow";
if (i < 1 || i > length)
throw "wrong Location";
int tmp = data[i - 1];
if (i < length) {
for (int j = i - 1; j < length - 1; ++j)
data[j] = data[j + 1];
}
--length;
return tmp;
}
在顺序存储结构的线性表中插入或删除一个数据元素时,平均约移动表中一半元素。若表长为 n n n,则 I n s e r t Insert Insert与 D e l e t e Delete Delete的时间复杂度为 O ( n ) O(n) O(n)
线性表的链式表示
线性表的链式存储结构的特点是用一组任意的存储单元存储线性表的数据元素(这种存储单元可以是连续的,也可以是不连续的)
从上图中可以看出链式存储的一般特点,即每一个节点由一个元素与一个指针构成,该指针指向下一个节点
可以以一个结构体来表示这种节点的构成:
template <class DataType>
struct Node {
DataType data;
Node<DataType> *next;
};
同样的,用类来实现链式存储之一单链表:
template <class DataType>
class LinkList {
public:
LinkList();
LinkList(DataType a[], int n);
~LinkList(); //析构
int Getlength(); //获得单链表的长度
bool listempty(); //判断单链表是否为空
DataType GetElem(int i); //获得第i个位置的元素
int LocateElem(DataType e); //查找与给定值e相等的元素,找到则返回下标,否则返回0
void Insert(int i, DataType e); //在给定位置i插入元素e
DataType Delete(int i); //删除给定位置i的元素,并返回该值
void PrintList(); //打印单链表
private:
Node<DataType> *first;
};
- 单链表初始化:
- 默认构造函数:
template <class DataType>
LinkList<DataType>::LinkList() {
first = new Node<DataType>;
first->next = NULL;
}
- 构造函数初始化:
template <class DataType>
LinkList<DataType>::LinkList(DataType a[], int n) {
first = new Node<DataType>;
first->next = NULL;
for (int i = 0; i < n; ++i) {
Node<DataType> *s = new Node<DataType>;
s->data = a[i];
s->next = first->next;
first->next = s;
}
}
- 查找元素:
- 获得第i个位置的元素:
template<class DataType>
DataType LinkList<DataType>::GetElem(int i) {
Node<DataType> *p = first->next;
int count = 1;
while (p && count < i) {
p = p->next;
++count;
}
if (p == NULL)
throw "wrong Location";
else
return p->data;
}
- 查找指定元素e:
template <class DataType>
int LinkList<DataType>::LocateElem(DataType e) {
Node<DataType> *p = first->next;
int count = 1;
while (p) {
if (p->data == e)
return count;
p = p->next;
++count;
}
return 0;
}
- 插入与删除:
- 插入:
template <class DataType>
void LinkList<DataType>::Insert(int i, DataType e) {
Node<DataType> *p = first->next;
int count = 0;
while (p && count < i - 1) {
p = p->next;
++count;
}
if (p == NULL)
throw "wrong Location";
else {
Node<DataType> *s = new Node<DataType>;
s->next = p->next;
p->next = s;
s->data = e;
}
}
- 删除:
template <class DataType>
DataType LinkList<DataType>::Delete(int i) {
Node<DataType> *p = first->next;
int count = 0;
while (p && count < i - 1) {
p = p->next;
++count;
}
if (p == NULL)
throw "wrong Location";
else {
Node<DataType> *s = p->next;
int tmp = s->data;
p->next = s->next;
return tmp;
}
}
链式存储除了单链表外,还有静态链表(用数组实现仿链表)、双向链表,关于这两个链表的具体实现 G i t h u b Github Github
单链表结构vs顺序存储结构
单链表结构 | 顺序存储结构 | |
---|---|---|
存储分配方式 | 采用链式存储结构,用一组任意的存储单元存放线性表的元素 | 用一段连续的存储单元依次存储线性表的数据元素 |
时间性能 | 查找: O ( n ) O(n) O(n) 插入与删除:在找出某位置地指针后,查找与删除时间仅为 O ( 1 ) O(1) O(1) | 查找: O ( 1 ) O(1) O(1) 插入与删除:需要平均移动表长一半的元素,时间为 O ( n ) O(n) O(n) |
空间性能 | 不需要分配存储空间,只要有就可以分配,元素个数也不受限制 | 需要预分配存储空间,分大了,浪费,分小了易发生上溢 |