一、栈
1.定义:栈是限定仅在表尾进行插入和删除操作的线性表;
栈也是一种线性表,即一对一的关系
其重点在于队尾,注意利用这个点明白其实际应用。
相关概念:
- 栈顶
- 栈底
- 入栈/压栈/进栈
- 出栈/弹栈
- 空栈:不含任何数据元素
2.特点:
后进先出
只在队尾添加删除
3.储存方式
声明时两种方式都只用声明top的值
① 顺序:数组
-
用数列队尾的值当作top的值(下标为0的值和栈底的值性质类似,很少移动,而队尾可以变化比较常见,故选取队尾的值为top值)
-
当
top==0
时即代表栈顶指针指向已经储存了的一个元素,故top==-1
代表该数组为空 -
算法
- 声明
typedef struct { int data[maxsize]; int top; //只用加上top的值即可; }Sqstack;
- 入栈出栈时检验top的值,并注意更改top的值
-
两栈共享空间:当两个栈的数据类型一样,空间需求有相反关系,可以用一个数组储存两个栈的内容。即用一个数组的头尾分别储存两个栈的栈底。
这种方式可以更合理的利用空间。
② 链式:链栈
- 栈顶通常为头指针,空栈时
top==NULL
- 算法
-
声明
用两个结构体,一个和普通链表一样,另一个用来储存top值;第一个结构体在添加和删除数据的时候会申请空间重新建立和删除;第二个结构体始终不变,整个函数中就用这一个结构体,在主程序中为其申请空间,且只申请一次;
typedef struct stacknode{ int data; struct stacknode *next; }Stacknode,*linkstackptr; //定义头指针; typedef struct linkstack{ linkstackptr top; //用来记录栈顶的值,注意这里申请为linkstackptr类型的值; int count; //用来记录链表的数量 }Linkstack;
- 入栈时用头插法;
- 入栈出栈时修改top,count,及指针的值;
-
4.实例
-
斐波拉契数列
迭代法:用循环解决
递归法:递归函数,自己调用自己,程序能更加简洁//递归法 int Fbi(int i){ //递归函数 if(i<2){ return i==0?0:1; } return Fbi(i-1)+Fbi(i-2); } int main() { int n; int i; scanf("%d",&n); for(i=0;i<n;i++){ printf("%d",Fbi(i)); //此处我的设定是打印指定的斐波拉契列; } return 0; }
递归函数一定要注意有没有递归终止条件;
-
四则运算-后缀表示法(逆波兰法)
这部分内容在离散数学部分也有涉及,后缀表示法的求法有两种,一种是直接利用栈的原理,将中缀表示法(即我们所见的一般表示法)直接转化为后缀表示法,另一种是利用树的结构;
二、队列
1.定义:只在队头删除,在队尾添加的线性表
2.特点:
先进先出
用front指向头,rear指向尾
3.储存方式:
①顺序:数组
-
通常用front指向第零个元素(数组中储存的第一个值),用rear指向数组的最后一个值的下一位,数组满时始终有一位为空,即循环队列。
(不直接指向最后一位:数组满时front == rear
,和数组空的条件一样,不好分辨)
(如果指向最后一位,也可以单独用一个变量来表示数组是否为空) -
判别条件:
- 队列是否为满:front在尾,rear在首或者
front == rear+1
;
将rear和front整合为一个问题:(rear+1)%Queuesize == front
- 队列长度:(rear-front+Queuesize)%Queuesize
- 队列是否为满:front在尾,rear在首或者
-
算法
- 声明
typedef strcut{ int [Queuesize]; int front; int rear; }
- 插入时检验是否为满,删除时检验是否为空;
②链式:链队列
-
链队列一般都有头结点
-
通常用front指向头结点,rear指向尾结点
-
链表一般不存在链表为满的情况,因为其增加一个元素始终是在内存中申请空间
-
算法
-
声明:两个结构体
和上面的栈的声明类似,一个用于每次储存新的数据,另一个在一个队列中就用这一个。
typedef struct { int data; struct *next; } typedef struct { front; rear; }
- 插入时使用尾插法
- 删除时要注意删除到空表的条件是:
rear = front + 1
; - 若除了头结点,队列中只有一个元素,删除后将rear指向头结点,即使rear和front相等(方便统一管理)
-