栈和队列
- 格式:doc
- 大小:239.50 KB
- 文档页数:28
数据结构--栈和队列基础知识⼀概述栈和队列,严格意义上来说,也属于线性表,因为它们也都⽤于存储逻辑关系为 "⼀对⼀" 的数据,但由于它们⽐较特殊,因此将其单独作为⼀篇⽂章,做重点讲解。
既然栈和队列都属于线性表,根据线性表分为顺序表和链表的特点,栈也可分为顺序栈和链表,队列也分为顺序队列和链队列,这些内容都会在本章做详细讲解。
使⽤栈结构存储数据,讲究“先进后出”,即最先进栈的数据,最后出栈;使⽤队列存储数据,讲究 "先进先出",即最先进队列的数据,也最先出队列。
⼆栈2.1 栈的基本概念同顺序表和链表⼀样,栈也是⽤来存储逻辑关系为 "⼀对⼀" 数据的线性存储结构,如下图所⽰。
从上图我们看到,栈存储结构与之前所了解的线性存储结构有所差异,这缘于栈对数据 "存" 和 "取" 的过程有特殊的要求:1. 栈只能从表的⼀端存取数据,另⼀端是封闭的;2. 在栈中,⽆论是存数据还是取数据,都必须遵循"先进后出"的原则,即最先进栈的元素最后出栈。
拿图 1 的栈来说,从图中数据的存储状态可判断出,元素 1 是最先进的栈。
因此,当需要从栈中取出元素 1 时,根据"先进后出"的原则,需提前将元素 3 和元素 2 从栈中取出,然后才能成功取出元素 1。
因此,我们可以给栈下⼀个定义,即栈是⼀种只能从表的⼀端存取数据且遵循 "先进后出" 原则的线性存储结构。
通常,栈的开⼝端被称为栈顶;相应地,封⼝端被称为栈底。
因此,栈顶元素指的就是距离栈顶最近的元素,拿下图中的栈顶元素为元素 4;同理,栈底元素指的是位于栈最底部的元素,下中的栈底元素为元素 1。
2.2 进栈和出栈基于栈结构的特点,在实际应⽤中,通常只会对栈执⾏以下两种操作:向栈中添加元素,此过程被称为"进栈"(⼊栈或压栈);从栈中提取出指定元素,此过程被称为"出栈"(或弹栈);2.3 栈的具体实现栈是⼀种 "特殊" 的线性存储结构,因此栈的具体实现有以下两种⽅式:1. 顺序栈:采⽤顺序存储结构可以模拟栈存储数据的特点,从⽽实现栈存储结构。
栈和队列的基本操作是线性表操作的子集,是限定性(操作受限制)的数据结构。
第三章栈和队列数据结构之栈和队列23. 1 栈¾定义:是限定仅在表尾进行插入或删除操作的线性表。
(后进先出线性表LIFO)¾栈底指针(base) :是线性表的基址;¾栈顶指针(top):指向线性表最后一个元素的后面。
¾当top=base 时,为空栈。
¾基本操作:InitStack(&S), DestroyStack(&S),StackEmpty(S) , ClearStack(&S),GetTop(S ,&e), StackLength(S) ,Push(&S, e): 完成在表尾插入一个元素e.Pop(&S,&e): 完成在表尾删除一个元素。
数据结构之栈和队列3¾栈的表示和实现¾顺序栈:是利用一组地址连续的存储单元依次存放自栈底到栈顶的数据元素;栈满之后,可再追加栈空间即为动态栈。
¾顺序栈的结构类型定义:typedef int SElemType;typedef struct{SElemType *base; /* 栈底指针*/SElemType *top; /* 栈顶指针*/int stacksize; /* 栈空间大小*/ }SqStack;数据结构之栈和队列4¾基本算法描述¾建立能存放50个栈元素的空栈#define STACK_INIT_SIZE 50#define STACKINCREMENT 10Status InitStack_Sq(Stack &S){S.base=(SET*)malloc(STACK_INIT_SIZE *sizeof(SET)); /*为栈分配空间*/if(S.base==NULL)exit(OVERFLOW); /*存储分配失败*/ S.top=S.base;S.stacksize = STACK_INIT_SIZE;return OK; }数据结构之栈和队列5¾出栈操作算法void pop(Sqstack s,SElemType e){if(s.top= = s.base)return ERROR;else{s.top--;e= *s.top;}return OK;}出栈操作topABY topABYbase base数据结构之栈和队列6¾压栈操作算法void Push(SqStack s,SElemType e)if(s.top-s.base>= S.stacksize;) {S.base=(SET*)realloc(S,base,(S.stacksize+STACKINCREMEN T) *sizeof(SET)); /*为栈重新分配空间*/if(!S.base)exit(OVERFLOW);S.top=S.base+S.stacksize;S.stacksize+=STACKINCREMENT;}*S.top=e;S.top++;}return OK; }topAB压栈操作topABebase base数据结构之栈和队列7¾栈的销毁void DestroyStack_Sq(Stack &S){ if (S.base) free(S.base);S.base=NULL;S.top=NULL;S.stacksize=0;}¾栈的清除void ClearStack_Sq(Stack &S){ S.top = S.base ;}数据结构之栈和队列8¾判断栈是否为空栈Status StackEmpty_Sq(Stack S){ if(S.top==S.base) return TRUE;else return FALSE;}¾获得栈的实际长度int StackLength_Sq(Stack S){return(abs(S.top-S.base));}数据结构之栈和队列9¾多个栈共享邻接空间两个栈共享一空间::::::top1top21m中间可用空间栈1栈2地址Base1Base 2……数据结构之栈和队列103. 3 栈与递归¾递归函数:一个直接调用自己或通过一系列的调用语句间接地调用自己的函数。
栈和队列是信息学竞赛中经常涉及的数据结构,它们在算法和程序设计中有着广泛的应用。
掌握栈和队列的基本原理和操作方法,对于参加信息学竞赛的同学来说是非常重要的。
本文将深入探讨栈和队列的相关知识点,帮助大家更好地理解和掌握这两种数据结构。
一、栈的定义与特点栈是一种先进后出(LIFO)的数据结构,它的特点是只允许在栈顶进行插入和删除操作。
栈可以用数组或链表来实现,常见的操作包括压栈(push)、出栈(pop)、获取栈顶元素(top)等。
栈的应用非常广泛,比如在计算机程序中,函数的调用和返回值的存储就是通过栈来实现的。
二、栈的基本操作1. 压栈(push):将元素压入栈顶2. 出栈(pop):将栈顶元素弹出3. 获取栈顶元素(top):返回栈顶元素的值,但不把它从栈中移除4. 判空:判断栈是否为空5. 获取栈的大小:返回栈中元素的个数三、栈的应用1. 括号匹配:利用栈来检查表达式中的括号是否匹配2. 表达式求值:利用栈来实现中缀表达式转换为后缀表达式,并进行求值3. 迷宫求解:利用栈来实现迷宫的路径搜索4. 回溯算法:在深度优先搜索和递归算法中,通常会用到栈来保存状态信息四、队列的定义与特点队列是一种先进先出(FIFO)的数据结构,它的特点是只允许在队尾进行插入操作,在队首进行删除操作。
队列同样可以用数组或链表来实现,常见的操作包括入队(enqueue)、出队(dequeue)、获取队首元素(front)、获取队尾元素(rear)等。
队列在计算机领域也有着广泛的应用,比如线程池、消息队列等都可以用队列来实现。
五、队列的基本操作1. 入队(enqueue):将元素插入到队列的末尾2. 出队(dequeue):从队列的头部删除一个元素3. 获取队首元素(front):返回队列的头部元素的值4. 获取队尾元素(rear):返回队列的尾部元素的值5. 判空:判断队列是否为空6. 获取队列的大小:返回队列中元素的个数六、队列的应用1. 广度优先搜索算法(BFS):在图的搜索中,通常会用队列来实现BFS算法2. 线程池:利用队列来实现任务的调度3. 消息队列:在分布式系统中,常常会用队列来进行消息的传递4. 最近最少使用(LRU)缓存算法:利用队列实现LRU缓存淘汰在信息学竞赛中,栈和队列的相关题目经常出现,并且有一定的难度。
栈和队列一、单项选择题(共59题)1. 假定一个链式队列的队首和队尾指针分别用front和rear表示,每个结点的结构为:,当出列时所进行的指针操作为()A. front = front->next;B. rear = rear->next;C. front->next = rear; rear = rear->next;D. front = front->next; front->next = rear;答案:A2. 向一个栈顶指针为HS的链栈中插入一个s所指结点时,则执行()。
A. HS->next = s;B. s->next = HS->next; HS->next = s;C. s->next = HS; HS = s;D. s->next = HS; HS = HS->next;答案:C3. 假定一个带头结点的循环链式队列的队首和队尾指针分别用front和rear表示,则判断队空的条件为()。
A. front == rear >nextB. rear == NULLC. front == NULLD. front == rear答案:D4. 若让元素1, 2, 3, 4依次进栈,则出栈次序不可能出现()的情况。
A. 3, 2, 1, 4B. 2, 1, 4, 3C. 4, 3, 2, 1D. 1, 4, 2, 3答案:D5. 假定一个顺序循环队列存储于数组a[N]中,其队首和队尾指针分别用f和r表示,则判断队满的条件为()。
A. (r - 1) % N == fB. (r + l) % N == fC. (f - 1) % N == rD. (f + l) % N == r答案:B6. 假定利用数组a[N]循环顺序存储一个队列,用f和r分别表示队首和队尾指针,并已知队未空,当出列并返回队首元素时所执行的操作为()A. return a[++r % N];B. return a[--r % N];C. return a[++f % N];D. return a[f++ % N];答案:C7. 假定利用数组a[N]顺序存储一个栈,用top表示栈顶指针,用top==N+l表示栈空,该数组所能存储的栈的最大长度为N,则表示栈满的条件为()A. top == 1B. top == 1C. top == 0D. top > 1答案:A8. 假定一个链式栈的栈顶指针用top表示,该链式栈为空的条件为()A. top != NULL;B. top == top->next;C. top == NULL;D. top != top->next;答案:C9. 判定一个循环队列Q(最多元素为M)为满队列的条件是()。
A. (Q->rear + 1) % M == Q->frontB. Q->rear - Q->front - 1 == MC. Q->front == Q->rearD. Q->front == Q->rear + 1答案:A10. 栈和队列的共同特点是()。
A. 都是先进先出B. 都是先进后出C. 只允许在端点处插入和删除元素D. 没有共同点答案:C11. 用不带头结点的单链表存储队列时,其队头指针指向队头结点,其队尾指针指向队尾结点,则在进行删除操作时()。
A. 仅修改队头指针B. 仅修改队尾指针C. 队头、队尾指针都要修改D. 队头、队尾指针都可能要修改答案:A12. 在一个长度为N的数组空间中,循环顺序存储着一个队列,该队列的队首和队尾指针分别用front和rear表示,则该队列中的元素个数为()A. (rear - front) % NB. (rear - front + N) % NC. (rear + N) % ND. (front + N) % N答案:B13. 如果进栈序列为e1,e2,e3,e4,则可能的出栈序列是()。
A. e3,e1,e4,e2B. e2,e4,e3,e1C. e3,e4,e1,e2D. 以上均有可能答案:D14. 栈的插入和删除操作在()进行。
A. 栈顶B. 栈底C. 任意位置D. 指定位置答案:A15. 假定一个链式队列的队首和队尾指针分别为front和rear,则判断队空的条件为()A. front == rearB. front != NULLC. rear != NULLD. front == NULL答案:D16. 假定一个带头结点的循环链式队列的队首和队尾指针分别用front和rear表示,每个结点包含值域data和指针域next,则使P所指结点入列所执行的操作为()A. p->next = NULL; rear = rear->next = p;B. p->next = rear->next; rear = rear->next = p;C. p->next = front; front = p;D. p->next = front->next; front->next = p;答案:B17. 在一个顺序循环队列中,队首指针指向队首元素的()位置。
A. 前一个B. 后一个C. 当前D. 最后答案:A18. 对于循环队列,下列叙述中正确的是()。
A. 队首指针是固定不变的B. 队首指针一定大于队尾指针C. 队首指针一定小于队尾指针D. 队首指针可以大于队尾指针,也可以小于队尾指针答案:D19. 将递归算法转换成对应的非递归算法时,通常需要使用()。
A. 栈B. 队列C. 链表D. 树答案:A20. 假定利用数组a[N]顺序存储一个栈,用top表示栈顶指针,top == -1表示栈空,并已知栈未满,当元素x进栈时所执行的操作为()A. a[--top] = x;B. a[top--] = x;C. a[++top] = x;D. a[top++] = x;答案:C21. 假定利用数组a[N]顺序存储一个栈,用top表示栈顶指针,top == -1表示栈空,并已知栈未空,当退栈并返回栈顶元素时所执行的操作为()A. return a[--top];B. return a[top--];C. return a[++top];D. return a[top++];答案:B22. 假定一个链式栈的栈顶指针用top表示,每个结点的结构为,退栈时所执行的指针操作为()A. top->next = top;B. top = top->data;C. top = top->next;D. top->next = top->next->next;答案:C23. 栈和队列都是()。
A. 顺序存储的线性结构B. 链式存储的非线性结构C. 限制存取点的线性结构D. 限制存取点的非线性结构答案:C24. 设计一个判别表达式中左右括号是否配对出现的算法,采用()数据结构最佳。
A. 线性表的顺序存储结构B. 队列C. 栈D. 线性表的链式存储结构答案:C25. 假定一个顺序循环队列的队首和队尾指针分别用f和r表示,则判断队空的条件为()A. f + 1 == rB. r + 1 == fC. f == 0D. f == r答案:D26. 从一个顺序循环队列中删除元素时,首先需要()A. 前移队首指针B. 后移队首指针C. 取出队首指针所指位置上的元素D. 取出队尾指针所指位置上的元素答案:B27. 假定利用数组a[N]循环顺序存储一个队列,用f和r分别表示队首和队尾指针,并已知队列未满,当元素x入列时所执行的操作为()A. a[++r % N] = x;B. a[r++ % N] = x;C. a[--r % N] = x;D. a[r-- % N] = x;答案:A28. 若已知一个栈的入栈队列是1,2,3,…,n,若输出序列的第一个元素是n,则输出的第i个元素是()(1<i<n)。
A. iB. n - iC. n - i + 1D. 不确定答案:C29. 用链接方式存储的队列,在进行删除运算时().A. 仅修改头指针B. 仅修改尾指针C. 头尾指针都要修改D. 头尾指针可能都要修改答案:D30. 如果以链表作为栈的存储结果,则出栈操作时()。
A. 必须判别栈是否为空B. 对栈不作任何判别C. 必须判别栈是否为满D. 判别栈元素的类型答案:A31. 当利用大小为N的数组循环顺序存储一个队列时,该队列的最大长度为()A. N - 2B. N - 1C. ND. N+1答案:B32. 当利用大小为N的数组顺序存储一个栈时,假定用top==N表示栈空,则向这个栈插入一个元素时,首先应执行()语句修改top指针。
A. top++;B. top--;C. top = 0;D. top = N - 1;答案:B33. 现有a、b、c、d 4个元素相继进入堆栈,则下面不可能得到的出栈序列为()。
A. abcdB. bdacC. cbdaD. dcba答案:B34. 在一个链队中,假设f和r分别为队首和队尾指针,则删除一个结点的运算是()。
A. r = f->nextB. r = r->nextC. f = f->nextD. f = r->next答案:C35. 判定一个顺序栈S(最多元素为M)为空的条件是()。
A. S->top != -1B. S->top == -1C. S->top != M - 1D. S->top == M - 1答案:B36. 用单链表表示的链式队列的队头在链表的()位置。
A. 链头B. 链尾C. 链中D. 任意答案:A37. 若用单链表表示队列,则应该选用()。
A. 带尾指针的非循环链表B. 带尾指针的循环链表C. 带头指针的非循环链表D. 带头指针的循环链表答案:B38. 在解决计算机主机与打印机之间速度不匹配问题时,通常设置一个打印数据缓冲区,主机将要输出的数据依次写入该缓冲区,而打印机则从该缓冲区中取出数据打印,先放入打印缓冲区的数据先被打印。
该缓冲区应该是一个()结构。
A. 堆栈B. 队列C. 数组D. 线性表答案:B39. 若用一个大小为6的数组来实现循环队列,且当前rear和front的值分别为0和3。
当从队列中删除一个元素,再加入两个元素后,rear和front的值分别为()。
A. 1和5 B. 2和4 C. 4和2 D. 5和1答案:B40. 设栈的输入序列为1,2,…,10,输出序列为a1,a2,…,a10,若a5=10,则a7为()。