- 从数据结构角度, 栈与队列属于线性结构,它们的操作是线性操作的子集,是操作受限的线性表。
- 从数据类型角度,它们是和线性表大不相同的抽象数据类型
- 【栈】仅仅允许在一端进行插入和删除操作的线性表,此端称【栈顶】,固定的不允许插入和删除的一端称【栈底】,不含元素的空表【空栈】
- 【顺序栈】栈的顺序存储结构实现:利用一组地址连续的存储单元存放自栈底到栈顶到数据元素,同时附设(top)指针,指向栈顶
Typedef struct{
ElemType data[maxsize];
Int top;
}SqStack;
- S栈的栈顶指针S.top,初始化S.top=-1;栈顶元素S.data[S.top]
-
3-> 2-> 1
A[3] A[1] A[0] A[-1]
^
|
S->top
-
若当初始化S-top=-1
若要【入栈】则应添加到数组的a[0],故要先将指针下移变成a[0],再进行插入
若要【出栈】,则先吐数据,再前移指针
【判空】,S->top==-1;
【栈满】S->top==MaxSize-1;
若当初始化S-top=0
若要【入栈】则应添加到数组的a[0],故直接先添加数据至a[s->top](a[0]),再将指针后移
【出栈】,先将指针前移,再吐出当前数据
【判空】S->top==0;
【栈满】S->top==MaxSize;
栈内计数
N=(S->top - 初始条件)
- 【共享栈】由于现实逻辑的独立性,当我们在计算机中使用【顺序栈】来表示现实中的「先进后出」的逻辑时会造成一种现象:已经建立起来的栈,会出现一些栈存放数据多,一些存放少的情况。由于它们是静态建立的,所以已经占用了系统空间,故存放数据较少的栈空间利用率不高,所以引入共享栈。
- 即,两个顺序栈,共享同一个一维数组空间。
- Top1==-1)时1号栈为空,(top==Maxsize)时2号栈为空
- Maxsize=n数组为a[Maxsize-1](从0开始)
- 指针相邻时<=>指针差1时<=>下标加和为Maxsize ——栈满
- 优点:更好有效利用计算机资源更好利用存储空间,两个栈的空间相互调节,栈满之后才会发生上溢。
- 缺点:当某一侧的数量过大时会压榨另一侧的空间,限制另一侧的进栈
- O(1)
【链式栈】
- 优点:便于多个栈共享存储空间,提高效率,且不存在栈满上溢。
- 「通常」采用单链表实现,并规定所有操作仅在表头进行。头结点可以有,可以没有,一般没有,实现过程也有差别。
- 向栈顶top插入一个x结点:x->next=top;top=x;
- Pop{x=top->data;top=top->next;}
- Catalan公式,对于n个不同的元素依次进栈(进栈次序固定),则出栈序列个数
- (1/n+1) * ( (2n)!/(n!*n!) )
【队列】删除端——头,插入端——尾
- 顺序实现:分配一块联系的存储单元存放队列中的数据元素,并附设front和rear指针分别指队头、队尾的下一个位置「进队时,先进再++」(指对头的前一个位置、队尾「先++,再进队」)
- 队空Q.front==Q.rear==0(假溢出情形也是Q.front==Q.rear所以判空时==0不能少)
- 进队Q.data[Q.rear]=x;Q.rear=Q.rear+1;
- 出队x=Q.data[Q.front];Q.front=Q.front-1;
【循环队列】
- 区分队空队满
- 牺牲一个单元来区分队空和队满“对头指针在队尾指针的下一位置则标志着队满”
- 队满:(Q.rear+1)%Maxsize==Q.front
- 对空Q.front==Q.rear
- 计数:N=(Q.rear-Q.front+Maxsize)%Maxsize
- 增设元素个数数据成员:队空:Q.size==0;队满:Q.size=Maxsize
- 增设flag,标志着由于删除导致队空,还是由插入导致队满
- 牺牲一个单元来区分队空和队满“对头指针在队尾指针的下一位置则标志着队满”
【链式队列】
- 判空 Q.front==Q.rear;
-
入队s->data=x;
s->next=NULL;
Q.rear=s;
出队p=Q.front->next;
x=p->data;
Q.front->next=p->next;(越过第一个)
if(Q.rear==p)Q.rear=Q.front;
Free(p);
- 最适合做链队的链表是(带队首指针和队尾指针的非循环单链表,即可以实现一切需要的操作,又不会由于一些临界操作而修改大量指针)
- 用单链表实现队列时,队头在链头位置,进队直接接到rear->next上,出队从队头出第一个
- 链队删除操作时,一般仅修改头指针,若的是删除最后一个元素,则头尾都需要修改,都指向头结点
【双端队列】两端都可以进行「入队」和「出队」的队列,逻辑结构仍是「线性」结构
- 前端进的元素排列在后端进的元素的前面,后端进的排在前短的后面
【递归】如果说在一个函数或者说一个过程或数据结构的定义中应用了它自身,就称其是递归定义的,简称:递归。特点:代码量小,效率往往不太高。
【斐波那契数列】递归实现
Def: { fib(n-1)+fib(n-2) n>1
Fib(n){. 1 N=1
{. 0. N=0
Int Fib(n)
{
If(n==0) return 0;
If(n==1) return 1;
Else
Return Fib(n-1)+Fib(n-2);
}
【递归的精髓】将原始问题转化为属性相同但规模较小的问题。
【将递归算法转化为非递归算法,往往要借助栈来实现】 |
|
【递归算法】
- 递归算法的设计【实际上】就是对问题抽象的过程,如果抽象到每个小问题都有相同的特征时,那就形成了递归的算法。
- 递归的【定义】由基本项和归纳项两部分组成。
- 【基本项】描述了递归过程的一个或几个【终结状态】,即不需要继续递归就可以求值的状态—出口。
- 【归纳项】描述了从当前状态向终结状态的转换。即将复杂问题化为较简单的问题,而简单的问题与复杂的问题的形式是一样的。每递归一次都要向终止条件靠近一步,最终达到终止条件。