栈和队列
栈
限定性线性表:限定只能在线性表的一端进行插入和删除操作。
栈顶:表中允许进行插入和删除操作的一端
栈底:两外不能直接进行操作的一端
ps: 这种限定是一种 约定而不是一种无能为力
ps :栈的实现有 顺序栈和链栈两种,顺序栈内有一个顺序存储结构的数组和一个指示栈顶的指针(int型的一个数组下标),链栈内部有一个链式存储结构带有尾指针作为栈顶指示指针。
顺序栈
定义
#define Stack_Size 50
typedef struct
{
StactElementType elem[Stack_Size];
int top;
}SeqStact;
初始化函数
void initStack(SeqStack *S)
{
S->top=-1;
}
初始化栈,置栈顶指针为-1,即当前栈顶元素在数组的下标是-1,那下一个元素要放入的就是数组下标为0的位置了。
不用把栈中的数据初始化,第一操作时会覆盖,第二栈顶指针上面的部分暂时不会被使用。
判断栈空否
int IsEmpty(SeqStack *S)
{
return (S->top==-1?TURE:FLASE);// if(S->top==-1)return TRUE else return FLASE;
}
判断栈满
int IsFull(SeqStack *S)
{
return (S->top==Stack_size-1?TRUE:FLASE)// Stack_size数组的最大容量,Stack_size-1下标最大值。
}
进栈
int Push(SeqStack *S,StackElementType x)
{
if(S->top==Stack_size-1) return FLASE;
S->top++;
S->elem[S->top]=x;
return TRUE;
}
出栈
int Pop(SeqStack *S,StackElementType *x)
{
if(S->==-1) return FLASE;
esle
{
*x=S->elem[S->top];
S->top--;
return TRUE;
}
}
取栈顶(查)
int GetTop(SEqStack *S, StackElementType *x)
{
if(S->top==-1)return FLASE;
else
{
*x=S->elem[S->top];
return TRUE;
}
}
双端栈
对普通顺序栈的一种扩展,两个栈共用一个数组而已,节约了空间。
结构体定义
#define M 100
typedef struct
{
StackElementType Stack[M];
StackElementType top[2];
}DqStack;
用法没什么不同,区别无非就是
1.上面的S->top 变为 S->top[0]和S->top[1]
2.上面的那个栈 top[0]的操作相反了,如出栈top++ 进栈 top–后 添加 下面的和之前一样出栈top[1]–,入栈top[1]++
初始化
void InitStack(DqStack *S)
{
S->top[0]=-1;
S->top[1]=M;
}
进栈 此方法两栈共享
int Push(DqStack *S,StackElementType x,int i)
{
if(S->top[0]+1==S->top[1])return FLASE;//栈满了
switch(i)
{
case 0: S->top[0]++;
S->Stack[S->top[0]]=x;
break;
case 1:S->top[1]--;
S->Stack[S-top[1]]=x;
break;
}
return TRUE;//程序成功执行到这一步说明入栈成功了
}
栈的应用 (感觉这里应该不会考吧 没什么印象了)
递归函数的非递归化
我们知道递归函数存在的最大问题是,当递归次数足够大时,会导致函数栈溢出而死机,函数栈的大小一般是一个固定值,对于linux来说一般默认是8M。而数据栈是位于进程的head区的,可以分配的很大。
示例递归求N!阶乘
#define Stack_Size 50
typedef struct
{
StactElementType elem[Stack_Size];
int top;
}SeqStact;
int rec(int n)
{
SeqStact i;
i.top=0;
i.elem[0]=1;
for(int i=1;i<=n;i++)
{
i.top++;
i.elem[i.top]=i*i.i.elem[i.top-1];
}
return i.elem[i.top];
}
队列
约定只能在队尾添加,在对首删除。
普通队列定义
typedef struct
{
ElementType element[maxsize];
int front;
int rear;
}SeqQueue;
seqQueue *Q;// c语言可能还是用指针方便吧
循环队列:加一个取余和判断队满 重复利用空间
初始化
void init(SeqQueue *Q)
{
Q->front=Q->rear=maxsize-1;
}
这是mooc的课件上对于循环队列的初始化函数,可能是为了突出循环两个字吧,我也是醉了😂
void init(SeqQueue *Q)
{
Q->front=Q->rear=0;
}
这样初始化不好吗😈
其实,他为什么那么初始化呢
现在初始化之后是空队,元素入队的第一步是Q->elem[Q->rear]=x;Q->rear++%maxsize;
这里有一个易混点,就是队尾指针所在位置总是不存数据,队首指针和队尾指针在一起时为空,队尾指针+=队首指针时队满
入队
int EnterQueue(SeqQueue *Q,QueueElementType)
{
if((Q->rear+1)%MAXSIZE==Q->front) return FLASE;
Q->element[Q->rear]=x; //mooc课件上是尾指针有值的,但是这样可能会导致一些错误,所以我就吧这两句话换了一个位置。如果你希望尾指针所在位置有值将这两句话的位置再换回来就可以了。但要记得自己设置的位置到底有没有值。
Q-rear=(Q->rear+1)%MAXSIZE;
return TRUE;
}
出队
int DeleteQueue(SeqQueue *Q,QueueElementType x)
{
if(Q->front==Q->rear)return FALSE;
Q->front=(Q->front+1)%MAXSIZE;
*x=Q->element[Q->front];
return TRUE;
}
链队列
这个不会考,我先写上有空在补