sf论数据结构(二)
1.前言
继上一章的风格特点,今天介绍的内容是最简单的数据结构线形表。本文用最简单的语言和实例,一起探讨线形表,从其逻辑结构出发,对应物理结构,从原理上剖析其应用。希望对大家有帮助。
2.线形表的逻辑结构
线形表的逻辑结构,就是我们在第一章讲到的线形结构。所谓的逻辑结构就是你作为程序的创造者,一个God的角色,给你的数据元素组成集合的时候制定的关系上的规则,任何情况不能违背其规则。简单来说就是
数据元素—-数据元素 之间的关系!
而线形结构就是说其「数据元素」在组成集合的时候,每个数据元素之间存在「一个接着一个」的关系。如图
用比较严谨的语言就是:
除首尾元素,其余所有元素都有唯一的「前趋」和「后继」!
(注解:根据线性的方向,数据元素“前面“有元素,后继就是“后面“有元素。不太理解的可以对应着图片看)
3.线形表的物理结构
简单重复前面物理结构的内容。
逻辑结构是数据元素与数据元素之间的关系,是人们想象出来的,画出来的。那么怎么去具体实现它呢?在确定好逻辑结构之后,想要在计算机上具体的存储实现,就是数据结构中的物理结构(也称为存储结构)。
值得注意的一点就是,一种逻辑结构对应多种物理结构。
3.1 线形表的顺序存储方式
顺序结构,指的是在内存上开辟一块连续的空间,用来存储一个集合的数据元素。基础较好的看客难免会想到数组就是这个玩意。没错,物理结构中的顺序结构就是借助数组去实现的!
数据元素与数据元素的物理地址相毗邻。数组的特点注定了它数据元素存储在其中符合我们给它们制定的规则—-逻辑结构。
首先是结构体的定义
typedef struct list
{
Element data[MAXSIZE]; // 用数组存储 数据元素
int length; // 这个是线形表的长度
}list;
如代码所示,结构体里面是一个类型为Element的数组,这个类型可以自己定义(int, double,或者自定义类型) MAXSIZE,因为C语言的特点。声明一个数组必须给它一个长度上限!
例如:int a[100];
这个100当然就是上限。这里的MAXSIZE 可以在预编译的时候写!
#define MAXSIZE 10000
length就是一个实时状态下的线形表的长度!因为既然作为一个线形表当然要动态的添加和删除数据元素,这里加一个成员可以保持伪动态性。
这是一个中规中矩的定义!有人嫌它太麻烦,直接用一个数组去表示线形表就可以。这里的数组0号位置是不存元素的,存的就是这个length,是不是简化了很多!
值得注意的是length与data的类型要相同!也就是说char类型的数组0号位置应该是个’1’这种东西。
anyway!不管你喜欢用哪种定义方式,都符合其原理!这才是最值得关注的东西!
至于优缺点的问题,我想介绍完链表之后对比着论述!
3.2线形表的链式存储结构
回顾一下上一节课讲的内容,链式存储结构要跟顺序存储结构相反。在内存空间上面,数据元素与数据元素之间不需要连续的空间去存储,但是又要符合线形表的逻辑结构。毕竟是你制定的规则,是不容被打破的!那该怎么做呢?
如图中的数据元素的集合,数据元素想要这样连接在一起,可以用到指针这个东西,一个节点中(节点就是结构体组成),有一个数据域存放的是数据元素,另外还有一个指针域,存放的是它后继的地址!
综上所叙,节点的结构体的定义!
typedef struct QNode
{
Element data; // 数据域
struct QNode *next; //指针域
}QNode;
有必要解释一下:
这里的数据域当然只能存一个数据,数据类型为Element。
而指针域,是一个指针,类型竟然是struct QNode,不要纳闷,这是c语言的特点,在没有typedef之前,结构体类型不是QNode而是struct QNode。具体其他语言有不同,大家自行复习结构体和指针。画一个节点。
3.3 线形表的基本操作
3.3.1 顺序表的建立
.3 线形表的基本操作
3.3.1 顺序表的建立
typedef struct list
{
Element data[MAXSIZE]; // 用数组存储 数据元素
int length; // 这个是线形表的长度
}list;
这是一个顺序表的结构体定义,其实在实际应用中只需要声明一个数组。数组的0号位等于其length即可。这里的重点在于链表的建立。
链表的建立分为头插法,尾插法。
头插法:
首先把结构体搬出来
typedef struct Qnode
{
Element data; // 数据域
struct QNode *next; //指针域
}Qnode;
void CreateListWithF(Qnode *&Head int data[],int size)
{//假设数据都在data数组里面,数组的大小为size, 注意这里的Head是个引用型,其意思很简单,原来的实参与形参之间是一个单向传值实参————形参。加了引用型之后可以双向传递。
Qnode *s; //Qnode 类型的指针S,用于存储数据元素
//构造成一个节点,使用malloc函数,不明白的可以查查这个函数
Head=(Qnode*)malloc(sizeof(Qnode));
Head->next=NUll;//这一步不能少
for(int i=0;i<size;i++){
s=(Qnode*)malloc(sizeof(Qnode));
s->data=data[i];
//头插法的重要步骤
s->next=Head->next;
Head->next=s;
}
}
如图所示
原始状态
第一步
第二步
这就是头插法的图示,对应着上述代码
接下来是尾插法
void CreateWithR(Qnode *&Head int data[],int size)
{
Qnode *s;
Head=(Qnode*)malloc(sizeof(Qnode));
Head->next=NUll;//这一步不能少
Head->next=NUll;
Qnode *r;//标示尾节点
r=Head;//初始化的时候只有头节点,所以头节点是最后一个,r=Head
//--------以上都是初始化————————
for(int i=0;i<size;i++)
{
s=(Qnode*)malloc(sizeof(Qnode));
s->data=data[i];
//尾插法重要步骤
r->next=s;
r=s;
}
}
图示
原始状态
第一步
第二步
其两步对应上述代码的两步
无论头插法,尾插法进行链表建立都包括了链表的基本插入方法。当然链表的操作不止如此,但是绝对不会脱离图示几个步骤。
记住诀窍-——找到插入位置——别丢后继!