数据结构由某一数据元素的集合和该集合中数据元素之间的关系组成。记为 Data-Sturcture = {D,R}
数据结构的存储结构有:
顺序存储方法、链接存储方法、索引存储方法、散列表存储方法
下面开始谈谈线性表的相关内容。
线性表是由n(n≥0)个数据元素(结点)a[0],a[1],a[2]…,a[n-1]组成的有限序列。
其中:
- 数据元素的个数n定义为表的长度 = "list".length() ("list".length() = 0(表里没有一个元素)时称为空表)
- 将非空的线性表(n>=0)记作:(a[0],a[1],a[2],…,a[n-1])
- 数据元素a[i](0≤i≤n-1)只是个抽象符号,其具体含义在不同情况下可以不同
线性表的存储结构
- 顺序表
- 链表
- 单链表
- 动态单链表
- 静态单链表
- 双链表
- 循环链表
- 单循环链表
- 双循环链表
- 单链表
顺序表
用把线性表的结点按逻辑次序依次存放在一组地址连续的存储单元里的方法存储的线性表简称为顺序表。
顺序表是用向量实现的线性表,向量的下标可以看作结点的相对地址。因此顺序表的的特点是逻辑上相邻的结点其物理位置亦相邻。
顺序表是用向量实现的线性表,向量的下标可以看作结点的相对地址。因此顺序表的的特点是逻辑上相邻的结点其物理位置亦相邻。
在顺序表中,每个结点ai的存储地址是该结点在表中的位置i的线性函数。只要知道基地址和每个结点的大小,就可在相同时间内求出任一结点的存储地址。是一种
随机存取结构。
typedef int ElemType;
//定义数据元素的类型,类型可根据实际情况而定,这里假定为int
const int MaxSize = 100;
//表空间的大小可根据实际需要而定,这里假定为100
class Sqlist
{
private:
ElemType elem[MaxSize]; //数组,用于存放表结点
int length; //线性表的长度
public:
Sqlist(){};
~Sqlist(){};
void SetData(); //初建一个简表函数
void PrintOut(); //输出线性表函数
void Insert(int i,ElemType e); //插入函数
ElemType Delete(int i); //删除函数
}
例题1:设计一个高效算法,将顺序表的所有元素逆置,要求算法的空间复杂度为O(1)。
解:扫描顺序表L的前半部分元素,对于元素L.data[i] (0<=i<=L.length/2),将其与后半部分对应元素L.data[L.length-i-1]进行交换。
void Reverse (Sqlist &L)
{
int i;
ElemType x;
for ( i = 0; i < L.length/2; i++)
{
x = L.data[i];
L.data[i] = L.data[L.length-i-1];
L.SetData[L.length-i-1] = x;
}
}
顺序表的缺点:
在表中插入和删除,运行效率很低
需先分配,当表长度变化较大时,难以确定合适的存储空间
单链表
单链表是一种最简单的链表表示,也叫做线性链表,用它来表示线性表时,用指针表示结点间的逻辑关系。因此单链表的一个存储结点包含2个部分: data + link
其中data部分称为数据域,llink部分称为指针域,用来存放指示该链表下一个结点的开始存储地址。
单链表是整个链表的基础,必须要掌握单链表的基本运算算法,如单链表的建表、查找、插入和删除等操作,一定要做到非常熟悉。在设计算法时,要先通过图示理清算法思路,再进行算法编写。
单链表的特点是长度可以很方便地进行扩充。
带附加头结点的单链表
为了实现方便,为每一个链表加上一个“附加头结点”,data域不存信息或是放一个标志或表长。这统一了空表和非空表的操作实现,降低了程序结构的复杂性。
线性链表的其他变形
在单链表中,已知某点的地址,想要寻找其他所有点,在大多数情况下是不可能的。为解决这些问题,提出了单链表的其他变形
循环链表(circular list)
与单链表不同的是链表中表尾结点的link域中不是NULL,而是存放了一个指向链表开始结点的指针。
在实现循环链表的插入算法时,若在表的最前端插入,必须改变链尾最后一个结点的link域
双向链表(doubly linked list )
解决访问直接前驱和后续的问题。