摘要

  本章介绍了几种基本的数据结构,包括栈、队列、链表以及有根树,讨论了使用指针的简单数据结构来表示动态集合。本章的内容对于学过数据结构的人来说,没有什么难处,简单的总结一下。

1、栈和队列

  栈和队列都是动态集合,元素的出入是规定好的。栈规定元素是先进后出(FILO),队列规定元素是先进先出(FIFO)。栈和队列的实现可以采用数组和链表进行实现。在标准模块库STL中有具体的应用,可以参考http://www.cplusplus.com/reference/。

  栈的基本操作包括入栈push和出栈pop,栈有一个栈顶指针top,指向最新如栈的元素,入栈和出栈操作操作都是从栈顶端进行的。

  队列的基本操作包括入队enqueue和出队dequeue,队列有队头head和队尾tail指针。元素总是从队头出,从队尾入。采用数组实现队列时候,为了合理利用空间,可以采用循环实现队列空间的有效利用。

  关于栈和队列的基本操作如下图所示:

采用数组简单实现一下栈和队列,实现队列时候,长度为n的数组最多可以含有n-1个元素,循环利用,这样方便判断队列是空还是满。

栈的C++程序如下所示:

#include<iostream>
#include<cstdlib>
using namespace std;typedef struct stack { int *s; int stacksize; int top; }stack; void init_stack(stack *s,int n) { s->stacksize=n; s->s=(int*)malloc(sizeof(int)*s->stacksize); s->top=0; } bool stack_empty(stack *s) { if(s->top==0) return true; else return false; } bool stack_full(stack *s) { if(s->top==s->stacksize) return true; else return false; } void push(stack *s,int x) { if(stack_full(s)) { cout<<"overflow"<<endl; exit(1); } s->top=s->top+1; s->s[s->top]=x; } int pop(stack *s) { int x; if(stack_empty(s)) { cout<<"underflow"<<endl; exit(1); } x=s->s[s->top]; s->top--; return x; } int top(stack *s) { return s->s[s->top]; } int main() { stack s; init_stack(&s,100); push(&s,19); push(&s,23); push(&s,34); push(&s,76); push(&s,65); cout<<"top is "<<top(&s)<<endl; pop(&s); cout<<"top is "<<top(&s)<<endl; }

队列的C++代码实现:

#include<iostream>
#include<cstdlib>
using namespace std;typedef struct queue { int *q; int head,tail; int queuesize; }queue; void init_queue(queue *q,int n) { q->queuesize=n; q->q=(int*)malloc(sizeof(int)*q->queuesize); q->tail=q->head=0; } bool queue_empty(queue *q) { if(q->head==q->tail) return true; else return false; } bool queue_full(queue *q) { if(q->tail+1==q->head) return true; else return false; } int queue_length(queue *q) { return (q->tail-q->head+q->queuesize)%q->queuesize; } void enqueue(queue *q,int x) { if(queue_full(q)) { cout<<"queue overflow"<<endl; exit(1); } q->q[q->tail]=x; q->tail=(q->tail+1)%q->queuesize; } int dequeue(queue *q) { int x; if(queue_empty(q)) { cout<<"queue underflow"<<endl; exit(1); } x=q->q[q->head]; q->head=(q->head+1)%q->queuesize; return x; } int main() { queue q; init_queue(&q,100); enqueue(&q,10); enqueue(&q,30); cout<<"head: "<<q.head<<" tail: "<<q.tail<<endl; cout<<"value="<<dequeue(&q)<<endl; cout<<"value="<<dequeue(&q)<<endl; cout<<"head: "<<q.head<<" tail: "<<q.tail<<endl; enqueue(&q,10); exit(0); }

问题:

(1)说明如何用两个栈实现一个队列,并分析有关队列操作的运行时间。(始终用一个栈做为出,一个栈作为入队)

解答:栈中的元素是先进后出,而队列中的元素是先进先出。现有栈s1和s2,s1中存放队列中的结果,s2辅助转换s1为队列。

入队时,将元素压入s1。

出队时,判断s2是否为空,如不为空,则直接弹出顶元素;如为空,则将s1的元素逐个“倒入”s2,把最后一个元素弹出并出队。

(如果s1满了,s2既没有装满也不是非空,此时就不能继续入队了;如果s1满了,但s2是空的,则可以先将s1中的元素压人s2中,然后再进队。)

#include<iostream>
#include<stack>
#include<cstdlib>
using namespace std;template <typename T>
class StackToQueue { public: T dequeue(); void enqueue(const T &node); StackToQueue(){} ~StackToQueue(){} private: stack<T> stack1; stack<T> stack2; }; template <typename T> void StackToQueue<T>::enqueue(const T &node) { stack1.push(node); } template <typename T> T StackToQueue<T>::dequeue() { if(stack2.empty()&&stack1.empty()) { cout<<"underflow"<<endl; exit(1); } if(stack2.empty()&&!stack1.empty()) { while(!stack1.empty()) { T temp=stack1.top(); stack1.pop(); stack2.push(temp); } } T data=stack2.top(); stack2.pop(); return data; } int main() { StackToQueue<int> sq; sq.enqueue(1); sq.enqueue(2); sq.enqueue(3); cout<<sq.dequeue()<<endl; cout<<sq.dequeue()<<endl; cout<<sq.dequeue()<<endl; cout<<sq.dequeue()<<endl; }

(2)说明如何用两个队列实现一个栈,并分析有关栈操作的运行时间。(始终保证有一个队列是空的)

需要注意的一点是,如果每次入栈都选则将元素插入到第一个队列中,出队时先将前n-1个元素移交到第二个队列中,然后返回队列一中剩余的唯一一个元素,再将队列二中的元素又依次移交回队列一中,这样做未免效率过于低下。

事实上这样的思路我们可以看作是一直选用队列一作为栈元素的存储容器,而使用队列二作为一个出队时的辅助工具,这样每次出栈时来回复制元素容器元素的操作,效率确实低下。因为两个队列完全一样的,所以我们完全有理由让两个队列轮流作为栈元素的存储容器,这样每次出栈时,只需将所有前n-1个元素从一个队列移交到另一个队列中,然后返回最后一个元素作为出栈结果,这样始终至少有一个队列是空的,而每次进栈时,只需将进占元素放在那个非空队列的末尾(如果两个队列均为空,则随便放在那个里面都行)。这样减少了一半的复制操作。

#include<iostream>
#include<queue>
#include<cstdlib>
using namespace std;template <typename T>
class QueueToStack { public: QueueToStack(){} ~QueueToStack(){} T pop(); void push(const T &node); private: queue<T> queue1; queue<T> queue2; }; template <typename T> T QueueToStack<T>::pop() { T data; if(queue1.empty()&&queue2.empty()) { cout<<"underflow"<<endl; exit(1); } if(!queue1.empty())//对工作的队列进行操作  {     //出队到队列中只剩下一个元素,就可以出栈了 while(queue1.size()>1) { T temp=queue1.front(); queue1.pop(); queue2.push(temp); } data=queue1.front(); queue1.pop(); } else if(!queue2.empty()) { while(queue2.size()>1) { T temp=queue2.front(); queue2.pop(); queue1.push(temp); } data=queue2.front(); queue2.pop(); } return data; } template <typename T> void QueueToStack<T>::push(const T &node) { if(!queue1.empty())//每次都压入到工作的队列中  queue1.push(node); else queue2.push(node); } int main() { QueueToStack<int> queue; queue.push(1); queue.push(2); queue.push(3); cout<<queue.pop()<<endl; cout<<queue.pop()<<endl; cout<<queue.pop()<<endl; }

上面的top没有写

#include <cstdlib>
#include <iostream>
#include <assert.h>
#include <deque>
using namespace std; /*两个队列模拟一个堆栈*/ /*队列A、B 入栈:将元素依次压入到非空的队列,第一个元素压倒对列A 出栈:把队列A的前n-1个元素倒到队列B,把第n个元素去掉。此时数据在B中,下次操作,则对B操作。 栈顶:把队列A的前n-1个元素倒到队列B,把第n个元素作为栈顶*/ template <typename T> class MyStack { public: //入栈,第一个元素进到队列deque1,以后每个元素进到非空的队列 void push(T element) { if (deque1.empty() && deque2.empty()) { deque1.push_back(element); } else if (!deque1.empty() && deque2.empty()) { deque1.push_back(element); } else if (deque1.empty() && !deque2.empty()) { deque2.push_back(element); } } //出栈,将非空队列的前n-1个元素转移到另一个空的队列,删除非空队列的第n个元素 void pop() { if (!deque1.empty()) { int size = deque1.size(); for (int i=0; i<size-1; i++) { deque2.push_back(deque1.front()); deque1.pop_front(); } deque1.pop_front(); } else { int size = deque2.size(); for (int i=0; i<size-1; i++) { deque1.push_back(deque2.front()); deque2.pop_front(); } deque2.pop_front(); } } //栈顶元素,将非空队列的前n-1个元素转移到另一个空的队列,将非空队列的第n个元素返回  T top() { if (!deque1.empty()) { int size = deque1.size(); for (int i=0; i<size-1; i++) { deque2.push_back(deque1.front()); deque1.pop_front(); } T temp = deque1.front(); deque1.pop_front(); deque2.push_back(temp); return temp; } else { int size = deque2.size(); for (int i=0; i<size-1; i++) { deque1.push_back(deque2.front()); deque2.pop_front(); } T temp = deque2.front(); deque2.pop_front(); deque1.push_back(temp); return temp; } } //栈是否为空 bool empty() { return (deque1.empty()&&deque2.empty()); } private: deque<T> deque1; deque<T> deque2; }; int main(int argc, char *argv[]) { MyStack<int> my; for (int i=0; i<10; i++) { my.push(i); } while (!my.empty()) { cout<<my.top()<<" "; my.pop(); } cout<<endl; }

基本数据结构 - 栈和队列相关推荐

  1. 数据结构栈和队列_使您的列表更上一层楼:链接列表和队列数据结构

    数据结构栈和队列 When you want to store several elements somewhere in a program, the go-to data type is an a ...

  2. 数据结构栈与队列的应用之汽车轮渡问题——自己的一些理解

    本题摘自王道数据结构栈与队列的应用的课后题,题目如下: 某汽车轮渡口,过江渡船每次能载10辆汽车过江.过江车辆分为客车类和货车类,上渡船有如下规定:同类车先到先上船,客车先于货车上船,且每上4辆客车, ...

  3. 数据结构——栈与队列相关题目

    数据结构--栈与队列相关题目 232. 用栈实现队列 思路 225. 用队列实现栈 1.两个队列实现栈 2.一个队列实现栈 20. 有效的括号 思路 1047. 删除字符串中的所有相邻重复项 思路 1 ...

  4. 第十章 基本数据结构——栈和队列

    摘要 本章介绍了几种基本的数据结构,包括栈.队列.链表以及有根树,讨论了使用指针的简单数据结构来表示动态集合.本章的内容对于学过数据结构的人来说,没有什么难处,简单的总结一下. 1.栈和队列 栈和队列 ...

  5. 大话数据结构-栈与队列

    文章知识点来至于大话数据结构里边章节知识, 这篇主要介绍栈与队列在计算机中存储形式, 以及在某些算法领域中对栈和队列的相关应用.章节最后介绍了著名的逆波兰表达式, 以及通过算法来实现该表达式的运算过程 ...

  6. 数据结构——栈与队列操作(用栈模拟队列)

    [栈与队列操作] 问题描述:假设有两个长度相同的栈 S1,S2,已知以下入栈.出栈.判栈满和判栈空操作: void Push(S,x); Elemtype Pop(S); bool StackFull ...

  7. 【数据结构-栈和队列】详解栈和队列(代码+STL+原理)

    一.栈的应用 栈是一种先进后出(FILO)的数据结构 1.1 栈的操作实现 清空(clear): // 栈的清空操作就是把栈顶top置为-1 void clear(){top=-1; } // 清空栈 ...

  8. 算法与数据结构 -- 栈与队列(四)

    栈与队列定义了数据的操作 一.栈 栈是一种先入先出的数据结构.可以用顺序表实现,也可以用链表实现 栈操作 判断是否为空 压栈.入栈push 出栈 pop 返回栈顶元素 peek 栈的元素个数 # co ...

  9. 六十二、数据结构栈和队列的相互实现

    @Author:Runsen 编程的本质来源于算法,而算法的本质来源于数学,编程只不过将数学题进行代码化. ---- Runsen 算法,一门既不容易入门,也不容易精通的学问. 栈和队列都是用来保存数 ...

  10. 数据结构栈和队列以及常见算法题

    栈 概念:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作.进行数据插入和删除操作的一端称为栈顶,另一端称为栈底.栈中的数据元素遵守后进先出LIFO(Last In First Out)的 ...

最新文章

  1. 熟练掌握python是什么概念-Quant面试时说:熟练掌握Python,请三思,不然凉凉~
  2. Android 使用 ActivityResult 处理 Activity 之间的数据通信及调起拍照实例
  3. 腾讯移动分析系统揭密
  4. assign与weak区别(面试)
  5. 485串口光纤转换器产品介绍
  6. 不间断电源ups标准_UPS不间断电源正确使用方法
  7. 超简单的Tomcat安装过程
  8. [机器学习] 概念解析:从经验风险、结构风险到代价函数、损失函数、目标函数
  9. Linux网络编程 --------- Socket编程---最浅显易懂的介绍
  10. 调侃《Head First设计模式》之总结篇
  11. 基于汇编与c语言的单片机原理及应用答案,基于汇编与C语言的单片机原理及应用...
  12. 如何用快解析自制IoT云平台
  13. 现在学 Prolog 递归
  14. 语义化版本号 Sematic Versioning
  15. 阿里云直播集成简要指南
  16. springboot+党员信息管理系统 毕业设计-附源码161528
  17. 【python】如何画简单的图
  18. 华为鸿蒙os和小米ov,稳了,华为鸿蒙手机 OS 发布!小米也能用?
  19. atl常量暴露的最简便方法
  20. 图片编辑工具哪个好?好用的图片编辑工具介绍

热门文章

  1. RabbitMQ 构建高可靠集群架构
  2. upupw启动mysql失败,Can't init tc log
  3. PHP中对数组进行分页处理的原理及分页实例
  4. http 请求 超时时间设置
  5. mysql 按照条件计数_灵活的CASE...WHEN:SQL同时按条件计数按条件加和
  6. BASE64Encoder及BASE64Decoder的正确用法
  7. linux下tcpdump的使用简介
  8. Go Web 编程--如何确保Cookie数据的安全传输
  9. Laravel同时接收路由参数和查询字符串中的参数
  10. 在集合中根据条件来筛选数据