上一篇文章我们已经打好基础,做足准备,现在就开始正式学习数据结构吧。
这篇文章主要讲解数据结构的线性表。
本篇文章的主要内容:
代码部分可以先看一下不用理解,也可以忽略主要对概念的理解,只有把概念理解好了,才能知道题目怎么去解
线性表是n个数据特性相同的元素的组成有限序列,是其他数据结构的基础
线性表的逻辑结构无疑就是线性结构线性结构有很多如后面要学的栈,队列,串,数组)都是线性结构,而线性表是最基本的线性结构
知道逻辑结构之后就要来学习他的存储结构了有顺序和链式两种。
3.1顺序存储
顺序存储的特点:逻辑上相邻的数据元素,物理次序也是相邻的。只要确定好了存储线性表的起始位置,线性表中任一数据元素都可以随机存取,所以线性表的顺序存储结构是一种随机存取的储存结构,因为高级语言中的数组类型也是有随机存取的特性,所以通常我们都使用数组来描述数据结构中的顺序储存结构,用动态分配的一维数组表示线性表。
#define maxsize 100 //定义学生最大数量
#define OK 1 //正确标志
#define ERROR 0 //失败标志
//学生信息的数据结构
typedef struct
{
int id; //学生id
char name[30]; //学生姓名
}Student;
//顺序表数据结构
typedef struct
{
Student *elem; //储存空间的基地址
int length; //数据结构的长度
}SqList;
//定义SqList类型的变量
SqList L;
//而在考研中我们一般使用下面的代码
int A[maxsize]
int length=0;
这是一个十分简单的例子,这样我们就可以通过L.elem[i-1]访问序号为i的学生信息了
1.初始化
基本算法:
//初始化顺序表基本算法
Status InitList(SqList &L)
{
//构造一个空的顺序表L
L.elem = new ElemType[maxsize]; //分配内存空间
if(!L.elem) exit(-1);
L.length = 0;
return OK;
}
2.取值
基本算法:
//顺序表取值
Status Get(SqList &L,int i,ElemType &e)
{
if(i<1||i>L.length) return ERROR;
e = L.elem[i-1];
return OK;
}
3.查找
基本算法:
//顺序表查找
int Find(SqList L,ElemType e)
{
//查找值为e的数据元素,返回其序号
for(i=0;i<L.length;i++)
{
if(L.elem[i]==e) return i+1;
}
return ERROR; //查找失败
}
4.插入
基本算法:
//顺序表插入
Status ListInsert(SqList &L,int i,ElemType e)
{
if((i<1)||(i>L.length+1)) return ERROR; //i不合法
if(L.length == maxsize) return ERROR; //满了
for(j=L.length-1;j>=i-1;j--)
L.elem[j+1]=L.elem[j]; //将第n个至i个位置的元素后移
L.elem[i-1]=e; //将e放进第i个位置
}
5.删除
基本算法:
//顺序表删除
Status ListDelete(SqList &L,int i)
{
//删除第i个元素,i的值为[1,L.length]
if((i<1)||(i>L.length)) return ERROR;
for(j=i;j<=L.length-1;j++)
L.elem[j-1]=L.elem[j];
--L.length; //长度减一
return OK;
}
算法都十分的简单,眼尖的你可能发现了,为啥有的参数用的是引用,有的不是呢?
这里我就得讲下使用引用作为形参的作用了,主要有三点:
(1)使用引用作为参数与使用指针作为参数的效果是一样的,形参变化时实参对应也会变化,这个我在上篇文章(我上面给的链接)也有说明,引用只是一个别名。
(2)引用类型作为形参,在内存中并没有产生实参的副本,而使用一般变量作为形参,,形参和实参会分别占用不同给的存储空间,当数据量较大时,使用变量作为形参可能会浪费时间和空间。
(3)虽然使用指针也可以达到引用一样的效果,但是在被调函数中需要重复使用"*指针变量名"来访问,很容易产生错误并且使程序的阅读性变差。
此时你会发现,使用顺序表作为存储时,空间是一次性直接开辟的,所以可能会有空间不足或者浪费空间的情况出现,那么为啥不用一个就分配一个空间呢,再使用一个方式将这些空间串起来不就好了,是时候展现真正的技术了(链表)。
3.2链式存储
在链式存储中,每个存储结点不仅包含元素本身的信息(数据域),还包含元素之间逻辑关系的信息,即一个结点中包含有直接后继结点的地址信息,这称为指针域。这样可以通过一个结点的指针域方便的找到后继结点的位置。
由于顺序表中每个元素至多只有一个直接前驱元素和一个直接后继元素。当采用链式存储时,一种最简单也最常用的方法是:
在每个结点中除包含数据域外,只设置一个指针域用以指向其直接后继结点,这种构成的链接表称为线性单向链接表,简称单链表。
另一种方法是,在每个结点中除包含数值域外,设置两个指针域,分别用以指向直接前驱结点和直接后继结点,这样构成的链接表称为线性双向链接表,简称双链表。
单链表当访问一个结点后,只能接着访问它的直接后继结点,而无法访问他的直接前驱结点。双链表则既可以依次向后访问每个结点,也可以依次向前访问每个结点。
3.3循环链表是另一种形式的链式存储结构,它的特点是表中尾结点的指针域不再是空,而是指向头结点,整个链表形成一个环。由此,从表中任意一结点出发均可找到链表中其他结点。如图所示为带头结点的循环单链表和循环双链表: