目录
一、栈基础知识
1.栈的特点
后进先出(LIFO)
2.栈的定义
是一种特殊的线性表,其插入和删除操作均
在表的一端进行,是一种运算受限的线性表。
3.栈的术语
栈顶(top)是栈中允许插入和删除的一端
栈底(bottom)是栈顶的另一端
二、栈的实现及基本操作
1.栈的定义实现
typedef struct{
SElemType *base;
SElemType *top;
int stacksize;
}SqStack;
Sqstack S;
2.栈的初始化
int InitStack(SqStack *s){
s->base=(SElemType*)malloc(STACK_INIT_SIZE*sizeof(SElemType));
if(!s->base) exit(OVERFLOW);
s->top=s->base;
s->stacksize=STACK_INIT_SIZE;
return OK;
}
3.入栈操作(记得判断是否栈满)
重点:*S.top=e; S.top++;即*S.top++=e;
int Push(SqStack *S,SElemType e){
if(S->top-S->base>=S->stacksize){
S->base=(SElemType*)realloc(S->base,(S->stacksize+STACKINCREMENT)*sizeof(SElemType));
if(!S->base) exit(OVERFLOW);
S->top=S->base+S->stacksize;
S->stacksize+=STACKINCREMENT;
}
*S->top++=e;
return OK;
}
4.出栈操作(记得判断是否栈空)
int Pop(SqStack *S,SElemType *e){
if(S->top==S->base) return error;
else{
*e=*(S->top-1);//注意:不是e=S->top-1!!!后者只是在函数内部改变了e的指向
S->top--;
return OK;
}
}
5.链栈的c语言类型定义
链栈的入栈->单链表的头插法
链栈的出栈->单链表删除头结点的后驱结点
三、栈的应用举例
1.进制转换问题
取余依次求出个位、十位、百位......但写数习惯是高位到低位,刚好符合栈的特点
void conversion()
{
SqStack *S;
S=(SqStack*)malloc(sizeof(SqStack));
InitStack(S);
int N;
scanf("%d",&N);
while(N)
{
Push(S,N%8);
printf("%d\n",N%8);
N=N/8;
}
while(!StackEmpty(*S)){
int *p = (int*)malloc(sizeof(int));//!!!一定要分配空间,不要初始化成null,因为进函数后
//直接解引用会把程序跑死
Pop(S,p);
printf("%d",*p);
}
}
2.括号匹配问题
int check()
{
SqStack *P;
P=(SqStack*)malloc(sizeof(SqStack));
InitStack(P);
SElemType ch;
SElemType *e=(SElemType*)malloc(sizeof(SElemType));
while((ch=getchar())!='#')
{
switch(ch)
{
case '(':
case'[':
case'{':
Push(P,ch);
break;
case ')':
if(StackEmpty(*P)) return FALSE;
else
{
Pop(P,e);
if(*e!='(') return FALSE;
}
break;
case ']':
if(StackEmpty(*P)) return FALSE;
else
{
Pop(P,e);
if(*e!='[') return FALSE;
}
break;
case '}':
if(StackEmpty(*P)) return FALSE;
else
{
Pop(P,e);
if(*e!='{') return FALSE;
}
break;
default:
break;
}
}
if(StackEmpty(*P)) return TRUE;
else return FALSE;
}
四、队列的基础知识
1.定义:
队列
(Queue)——
是一种运算受限的特殊的线性
表,它只允许在表的一端进行插入,而在表的另一端
进行删除。
2.术语:
队尾(rear)是队列中允许插入的一端
队头(front)是队列中允许删除的一端
五、队列基本操作
1.链队列
(1)结点实现
typedef struct QNode{
QElemType data;
struct QNode *next;
}QNode,*QueuePtr;
typedef struct{
QueuePtr front;
QueuePtr rear;
}LinkQueue;
(2)带头结点的链队列初始化
Status InitQueue(LinkQueue *Q){
Q->front=Q->rear=(QueuePtr)malloc(sizeof(QNode));
if(!Q->front) exit(OVERFLOW);
Q->front->next=NULL;
return OK;
}
(3)带头结点的链队列的入队操作
Status EnQueue(LinkQueue *Q,QElemType e){
QueuePtr p;
p=(QueuePtr)malloc(sizeof(QNode));
if(!p) exit(OVERFLOW);
p->data=e;
Q->rear=p;
Q->rear=Q->rear->next;
return OK;
}
(4)带尾结点的链队列的出队操作
Status DeQueue(LinkQueue *Q,QElemType *e){
if(Q->front==Q->rear) return error;
p=Q->front->next;
*e=*(p->data);
Q->front->next=p->next;
if(Q->rear==p) Q->rear=Q->front;//!!!这一步是为了防止原队列中就只有一个元素,如果直接释放该
//元素,Q->rear就变成了野指针
free(p);
return OK;
}
2.循环队列
(1)循环队列类型实现
typedef struct{
QElemType *base;
int front;//即数组下标
int rear;//即数组下标
}SqQueue;
空状态和满状态都满足Q.front==Q.rear->判别队列是空还是满:
(1)另设一个标志区别队列是空是满,如:计数变量,记录元素个数
(2)牺牲一个存储空间,以队头指针在队列尾指针的下一个位置作为队列呈满状态的标志
Status InitQueue(SqQueue *Q){
Q->base=(QElemType*)malloc(sizeof(QElemType));
if(!Q->base) exit(OVERFLOW);
Q->front=Q->rear=0;//!!!注意和链队列的区别
return OK;
}
(2)入队列操作
Status EnQueue(SqQueue *Q,QElemType e){
if((Q->rear+1)%MAXSIZE==Q->front) return ERROR;
*(Q->base[Q->rear])=e;//!
Q->rear=(Q->rear+1)*MAXSIZE;//!
return OK;
}
(3)出队列操作
Status DeQueue(SqQueue *Q,QElemType *e){
if(Q->front==Q->rear) return ERROR;
*e=*(Q->base[Q->front])
Q->front=(Q->front+1)%MAXSIZE;
return OK;
}
(4)求队列长度
int QueueLength(SqQueue Q){
return (Q->rear-Q->front+MAXSIZE)%MAXSIZE;
}
3.双端队列
(1)双端队列定义
双端队列是一种特殊的线性表,特殊在限定插入
和删除操作只能在表的两端进行。
(2)双端队列应用
判断是不是回文数:
*注意表达式求值、括号匹配、数值转换、迷宫求解的应用