编程知识 cdmana.com

数据结构学习(二) --基本概念、线性表、栈、队列

(一)基础概念

1. 数据结构

相互之间存在一种或多种特定关系的数据元素的集合。

2. 逻辑结构

指数据对象中数据元素之间的相互关系。

逻辑关系分为四种。分别是 集合、线性关系、树形关系和图。

l 集合

数据结构元素之间,除了同属于一个集合外,没有其它关系。

l 线性关系

数据结构元素之间是一对一的关系。

l 树形关系

数据结构的数据元素之间存在一种一对多的层次关系。

l 图

数据结构的数据元素之间是多对多的关系。

3. 物理结构

数据的逻辑结构在计算机中的存储形式。

数据的存储形式有两种。顺序存储链式存储

l 顺序存储

将数据元素放在地址连续的存储单元中。其数据之间的逻辑关系和物理关系是一致的。最常见的例子是数组

l 链式存储

将数据元素放在任意的存储单元中,这些存储单元可以连续也可以不连续【数据的存储关系不能反映数据元素的逻辑关系,因而需要指针存放元素的地址,因而通过指针能找到相关联的元素的位置】。

逻辑关系是面对问题的,物理关系是面对计算机的。基本的目标是将数据和数据的逻辑关系保存在计算机中。

4. 数据类型

一组数据性质相同的值的集合以及定义在该集合上的一些操作的总称。

分为元素类型结构类型。

5. 抽象数据类型

数学模型和定义在数据模型上的操作。

抽象数据类型往往对事情描述的更加本质。比如,栈的抽象数据类型是所有栈的抽象。可以这么理解。类是对象的抽象。抽象数据类型是一种类的抽象。

(二)线性表

零个或多个数据元素的有限序列。

1. 线性表的顺序存储

指的是用一段连续的存储单元依次存储线性表的数据元素。

(1) 基本概念

通常是一维数组。顺序存储结构需要三个属性。

存储空间的起始位置。数组data,它的存储位置就是存储空间的起始位置。

线性表最大的存储容量。数组长度MaxSize,容量。

线性表的当前长度。Length

(2) 线性表顺序存储的插入

向第i个位置插入一个元素。

l 如果插入位置不合理,报错

从最后一个位置向第i个位置遍历。分别将他们向后移动一位。

之后将数据插入第i个位置。

l 表长加一。

(3) 线性表顺序存储的删除

l 如果删除位置不合理,报错

从第i个位置开始遍历到最后一个元素的位置。分别将他们的数据向前移动一个位置。

l 表长减一。

2. 线性表的链式存储

数据保存需要保存数据元素本身和数据元素之间的关系。因此,数据单元称为结点。数据结点包含数据域和指针域两部分。分别存储数据自身信息和指向直接后继的结点的指针。

每个结点包含一个指针域的链表叫单链表。

(1) 单链表的插入

假设存储元素e的结点s,需要插入到pp->next两个结点之间。

其余结点不需要变化,只需要修改s->nextp->next即可。修改如下:

s->next = p->next;

 p->next = s;

(2) 单链表的删除

假设删除P结点之后的结点。具体代码如下:

q=p->next;

p->next = p->next->next;

Free(q);

记录需要删除的结点,将待删除的结点的前驱结点的指针域指向待删除的后继结点。

(3) 单链表的创建

头插法:创建的结点始终在第一个。因为有头指针,这个思路比较好理解。

尾插法:在迭代循环的过程中,不给指针域赋值。将节点赋值给r。等下次循环的时候,新创建结点p。给前一个结点的指针域赋值r->next=p;指定上一结点的指针为当前结点。

3. 静态链表

(1) 定义和理解

静态链表:用数据描述的链表叫静态链表。数组的每个元素都有两部分组成。Datacur。其中data是用来存储数据元素的。也是我们需要处理的数据。Cur相当于单链表的指针,存放该元素在数组中的下标。Cur也称之为游标。也称之为游标实现法。

备用链表:静态链表中未被使用的数组元素称之为备用链表。静态链表的第一个和最后一个元素作为特殊元素处理,不存数据。静态链表的第一个元素,即下标为0的元素的cur就存放备用链表的第一个结点的下标。静态链表需要插入数据时候,只需要space[0]->cur ,即获得备用链表中第一个未被使用的元素。而最后一个元素的cur存放的是第一个有数值的元素的下标。相当于单链表中表头的作用。当整个链表为空的时候,为O2.

难点:个人认为,静态链表的难点在于理解备用链表在初始化、插入数据和删除数据过程中的变化。只要理解了这个流程,静态链表就完全理解了。Space[n]代表静态链表。

l 备用链表的初始化

在静态链表初始化的过程中,整个链表还是空的。所有分量位置上都是空的。代码如下。保证 space[0].cur为第一个未被使用的结点的下标。pace[MaxSize-1].cur 为第一个使用的元素的下标。即静态链表的表头下标。

for (i = 0; i < MaxSize - 1; i++)
    space[i].cur = i + 1;
space[MaxSize-1].cur = 0;

 

l 静态链表插入数据备用链表的变化

插入需求为:给静态链表的第i个位置插入一个元素。数值为value。则从备用链表中获取第一个空闲位j,【在此过程中已经对space[0]的修改为j】。则将数据保存到下标为j的元素的data中。然后将使用链表和备用链表的下标调整正确。

 

space[j].cur = space[i-1].cur; // 原有链表中第i个元素的下标,赋值给当前新元素的cur。
space[i-1].cur  = j;// 删除的元素放在备用链表的第一个位置。

 

l 静态链表删除数据备用链表的变化

space[k].cur = space[0].cur; // 原有的备用链表放在被删除结点的后面
space[0].cur = k;// 删除的元素放在备用链表的第一个位置。

 

 

数据结构包含两部分,数据元素的数据和数据元素之间的关系。如果能将这两部分存储到计算机中,就说明当前数据完整的保存到了计算机。静态链表结点的数据用node的数据域data表示,数据之间的关系有结点node的指针域cur来表示。通常记录当前node的后驱位置。

使用数组来描述链表。个人感觉很别出心裁的。它的好处也是缺点。申请的数组的时候预先需要确定大小的。优点在于每个结点直接使用,无需申请和释放内存,节省时间缺点申请的资源大小固定,扩容比较麻烦。只有和循环链表配合使用,意义更大。

 

(2) 插静态链表插入

为了区分数组中那些分量未使用,解决的办法是:将所有未被使用过及已被删除的分量用游标链成一个备用的链表。每当插入时候,需从备用链表取第一个结点作为待插入的新结点。

基本的链表操作:假设将新结点M插入PQ之间。前结点的指针指向新结点的指针。新结点的指针覆盖指向前结点的指针。

M->next = P->next;P->next = M;

备用链表的操作

(3) 静态链表删除

基本链表操作:将设PQ是按照顺序的两个结点。需删除Q,则

P->next = Q->next

备用链表操作:将备用链表的第一个元素作为删除结点的下一个元素。删除结点作为备用链表的第一个元素。

4. 循环链表

将链表的终端结点的指针由空指针指向头结点。使单链表形成一个环。单链表的循环链表简称为循环链表。

在单链表的结点中再添加一个前驱结点的指针域。称之为双线链表。

(三)栈和队列

栈和队列两种数据结构,我的理解是线性表的具体应用。因为数据结构代表两类含义。数据本身和数据元素之间的关系。现实场景通常根据数据元素之间的关系【线性表而言是前后驱关系】来决定使用数据的顺序。通常有“先来先用”和“后来先用”两种场景。为此,对线性表的获取位置做限制,从而出现了队列和栈的两种数据结构。

队列和栈只是对线性表的插入和删除位置做了限制。

1. 

仅限制在队尾进行插入和删除的线性表。允许插入的一端称为栈顶(top),另一端称之为栈底(bottom),栈称为后进先出LIFO的线性表。

栈的插入操作称之为:进栈、入栈、压栈。栈的删除操作称之为出栈、弹栈。

(1) 栈的顺序存储实现

栈的数据结构,一个线性表、topstackSize

Top:栈顶元素所在栈数组的位置(从0开始的),-1代表空栈。Top的最大值为StackSize-1.

StackSize:栈的长度。也就是栈的线性表的长度。

l 栈操作

入栈top自增1s->top++;给线性表,下标为top的元素赋值s.data[top]=e;

出栈:获取top位置的数据,top--

l 两栈共享空间

两栈数据元素的数据类型一致,存在一个元素增加另外一个元素减少的场景,比较合适使用两栈共享内存。

分别使用线性表的表头和表位做栈底,使用顺序从两侧向中间靠拢。Top1top2分表代表两个栈的栈顶第一个元素的下标。Top1+1=top2表示栈满。

(2) 栈的链式存储实现

对链栈而言,不需要头指针的。因为栈的地址就代表头指针的位置。

组成:链表,top:栈顶元素的地址,count:栈中元素的个数。

入栈:把当前栈顶元素赋给新结点的直接后继(指针域)。将新结点赋值给栈顶指针。Count++

出栈:获取元素 s->top->data,重新制定栈顶指针。S->top = s->top->next;

(3) 栈的作用及应用

栈的引入简化了程序设计问题。在程序设计语言中实现了递归。

递归函数:一个调用自己或者间接调用自己的函数,称为递归函数。

递归的定义中,至少有一个条件满足时,递归不再进行。即不需要引用递归而返回值退出。

递归和迭代的区别:迭代使用循环的结构,递归使用选择的结构。

 

四则运算表达式:

后续表达的计算规则:遇到数字,就进栈。遇到符号,就将栈顶的两个元素弹出,进行运算,将运算结果进栈。一直获得最终的结果。

 

Java虚拟机中,栈帧中的操作数栈就是这样进行数据计算的。

2. 队列

只允许在一端插入操作,在另一端进行删除操作的线性表。队列是一种先进先出的线性表。允许插入的一端为队尾,允许删除的一端为队头。

(1) 循环队列

队列为了避免只有一个元素,队头和队尾重合的情况,引入两个指针。Front指向队头的元素,rear指向队尾的元素。队列的头尾相接的顺序存储结构称之为循环队列。

空队列:front==rear

队列满:【因为front可能比rear小,也可能比rear大(rear循环一轮,跑到队头)】时,frontrear之间的关系为相差为(rear+1%QueueSize = front

队长(rear+QueueSize -front)%QueueSize

队插入数据:判断是否队满,没满则将元素插入data[rear] =e,然后将rear向后移动一位。Rear=(rear+1)%QueueSize

队删除数据:判断是否为空队列,如果不是,从队头获取数据data[front]。队front后移一位front=(front+1)%QueueSize

(2) 队列的链式存储

线性表的链式存储本质上就是线性表的单链表。只不过它只能从尾进头出而已。也称之为链队列。把队头指针front指向头结点,队尾指针rear指向终端结点。空队列:frontrear都指向头指针。

入队操作:将新元素的地址赋值给队尾元素的指针。Q->rear->next=s;将新元素地址赋给rearQ->rear=s;

出队操作:获取头元素的后继(第一个数据元素)p=Q->front->next;获取数据。e=P->data;判断p是否为尾结点。如果是尾结点,rear需要重新赋值。

If(Q->rear==p)

Q-rear=Q->front;

版权声明
本文为[IT迷途小书童]所创,转载请带上原文链接,感谢
https://www.cnblogs.com/maopneo/p/13958478.html

Scroll to Top