大话数据结构笔记(四)栈与队列
栈:只允许在表尾进行插入和删除操作的线性表。(先进后出,只允许操作的一端为栈顶,另一端为栈底,不含任何数据元素的栈称为空栈,(LIFO_LAST IN FIRST OUT)结构),最先进栈的元素不一定最后出栈,原因是栈只对线性表操作的位置进行限制,却没有对元素进出栈的时间进行限制。即在不是所有元素都进栈的条件下,先前进去的元素可以出栈,只要保证是栈顶元素出栈就好。
队列:是只允许在一端进行插入操作,在另一端进行删除操作的线性表。
栈顶位置必小于栈的存储大小,栈村子一个元素时,top等于0,空栈的判定条件为top=-1。
栈的定义结构: typedef int SElemType; typedef struct { sElemType DATA[STACK_SIZE]; int top; }SqStack; 进栈操作: Status Push(SqStack *S,SElemType e) { if(s->top == MAXSIZE-1)/*栈满*/ { return ERROR; } s->top ++; s->data[s->top]= e; return OK; } 出栈操作POP: Status Pop(SqStack *S,SElemType e) { if(s->top==-1) return ERROR; *e = S->data[S->top]; S->top--; return OK; }
用一个数组存储两个栈,一个栈的栈底位于数组【0】处(空栈为-1),另一个栈位于数组【n-1】处(空栈为n),当二者的栈顶的值差为1时,说明栈满。
两栈共享空间代码 typedef int SElemType; typedef struct { SElemType Data[MAX_SIZE]; int top1; int top2; }SqDoubleStack; 在栈共享空间中加入新元素e Status Push(SqDoubleStack *S,SElemType e,int statckNumber) { if(S->top1 + 1 == S->top2) return ERROR; if(stackNumber == 1) S->data[++S->top1]= e; else if(stackNumber == 2) S->data[--S->top2] =e; return OK; } 在栈共享空间中删除元素e Status Pop(SqDoubleStack *S,SElemType *e ,int stackNumber) { if(stackNumber ==1) { if(S->top1 == -1) return ERROR; *e = S->data[S->top1--] ; } else if(stackNumber ==2) { if(S->top2 == MAX_SIZE) return ERROR; *e =S->data[S->top2++]; } return OK; }
栈共享空间适用于存储相同数据类型。
栈的链式结构:将栈顶指针放在链表的头部,并且不需要头结点。
链栈的空是top = NULL(由链表的头指针指向空得到
链栈的结构代码: typedef struct StackNode { SElemType data; struct StackNode *next; }StackNode,*LinkStackPtr; typedef struct LinkStack { LinkStackPtr top; int count; }LinkStack; 进栈操作代码 Staus Push(LinkStack *S,SElemType e) { LinkStackPtr s = (LinkStackPtr)malloc(sizeof(StackNode)); s->data =e; s->next = S->top; S->top = s; S->count ++l return OK; } 出栈操作代码: Status Pop(LinkStack *S,SElemType *e) { LinkStackPtr p; if(StackEmpty(*S)) return ERROR; *e = S->top->data; p = S->top; S->top = S->top->next; free(p); S->count--; return OK; }
顺序栈和链栈的时间复杂度都为O(1),顺序栈要预先知道存储大小,链栈不受限,但要求每个元素有指针域,增加内存开销。
栈的应用-递归
我们将直接调用自己或通过一系列的调用语句间接地调用自己的函数,叫做递归函数。
迭代与递归的区别是:迭代使用循环结构,递归使用的是选择结构。递归调用会建立函数副本,会耗费时间和内存,但形式结构更清晰简洁。迭代不需反复调用函数和占用额外的内存。
前行阶段每一层递归,函数的局部变量,参数值及返回地址都被压入栈,在退回阶段,位于栈顶的局部变量,参数值和返回值地址被弹出。
栈的应用-四则运算表达式求值
遇到左括号就进栈,后面遇到右括号就将栈顶的左括号出栈,期间进行括号内的计算。
后缀表达式:所有符号出现在数值的后面
example "9+(3-1)*3+10/2"->"9 3 1 -3*+10 2/ +"
中缀表达式转为后缀表达式规则:
队列:先进先出(FIFO),只允许在一端进行插入操作,而在另一端进行删除操作。队尾:允许插入的一端,队头:允许删除的一端。
队列满的条件:(rear+1)%QueueSize == front
队列的长度公式:(rear-front +QueueSize)%QueueSize
队列顺序存储: typedef int QElemtype typedef struct { QElemtype data[MAXSIZE]; int rear; int front; }SqQuene //初始化队列 Status InitQuene(SqQuene *q) { q.rear = 0; q.front = 0; return OK; } int QueneLength(SqQuene q) { return (q.rear-q.front+MAXSIZE)%MAXSIZE; } //循环队列入队列操作 Status EnQuene(SqQuene *q,ElemType e) { if((q->rear+1)%MAXSIZE == rear->front) return ERROR; q->data[q->rear]=e; q->rear=(q->rear+1)%MAXSIZE; return OK; } //循环队列出队列操作 Status DeQuene(SqQuene *q,ElemType *e) { if(q->rear==q->front) return ERROR; *e = q->data[q->front]; q->front = (q->front+1)%MAXSIZE; return OK: }
链队列结构 typedef int QElemType //队列节点 typedef struct QNode { QElemType data; struct QNode *next; }QNode,*QueuePtr; typedef struct { QueuePtr front ,rear; }LinkQueue;
入队操作:
Status EnQueue(LinkQueue *Q,QElemtype e ) { QueuePtr s = (QueuePtr)malloc(sizeof(Node)); if(!s) exit(OVERFLOW); s->data = e; s->Next = NULL; Q->rear->next = s; Q->rear =s ; return OK; }
出队操作:
Status DeQueue(QElemtype *e,LinkQueue *Q) { QueuePtr p ; if(Q->rear==Q->front) return ERROR; p = Q->front->nextl; *e = p->data; Q->front->next = p->next; if(Q->rear == p) Q->rear = Q->front; free(p); return OK; }