一、栈
栈是一种线性数据结构,它是一种特殊的序列,它具有限制插入和删除操作的特定顺序。栈可以看作是一种具有限制的线性表,它只允许在表的一端进行插入和删除操作,被称为栈顶。当进行插入操作时,新元素插入到栈顶,当进行删除操作时,栈顶元素被弹出,其他元素则依次向栈顶移动。栈的特定顺序为“后进先出”(LIFO,Last In First Out),即最后插入的元素最先弹出,在实际应用中,栈常用于递归算法的实现、括号匹配、表达式计算和函数调用等场景。
栈顶(Top):线性表允许进行插入和删除的一端。
栈底(Bottom):固定的,不允许进行插入和删除的另一端。
空栈:不含任何元素。
如上图:a1为栈底元素,an为栈顶元素。由于栈只能在栈顶进行插入和删除操作,故进栈次序依次为a1,a2,... ,an 而出栈次序为an,...,a2,a1。栈的明显的操作特征为后进先出(Last In First Out,LIFO),故又称 后进先出的线性表。
1.1 如何判断一个栈是空的或者满的
在栈的实现中,通常会使用一个指针来指向栈顶元素。栈的判空和判断满的依据主要就是这个指针。
对于判空,当栈中没有元素时,栈顶指针指向的位置是没有有效数据的,因此可以将栈顶指针初始化为-1,当栈顶指针为-1时,即为栈空。
对于判满,当栈中元素个数达到了栈的容量时,栈顶指针已经指向了栈的最高位置,再进行插入操作就会导致溢出。因此,可以将栈的容量定为固定值,并在实现中检查栈顶指针是否达到了栈的容量减1,如果是,则说明栈已满。
空:top = -1
满:top = maxsize - 1
2、队列
队列是一种基于先进先出(FIFO,First-In-First-Out)原则的线性数据结构,它类似于排队等候服务的场景。队列中的元素称为队列元素,新的元素只能在队列的末尾插入,被称为入队(Enqueue),而队列中最早插入的元素则在队列的前端,被称为队首(front),队首元素只能通过出队(Dequeue)操作移除。与栈略有不同,队列支持在头部和尾部的两端进行插入和删除操作,但是数据元素的访问只能在队头和队尾进行。队列可以用于缓存、消息处理、任务调度等场景。
2.1 队列空与满的判断
线性队列:
线性队列判断空和满的条件如下:
-
判断队列是否为空:队列为空的条件是队头指针和队尾指针指向同一个位置。
-
判断队列是否为满:队列为满的条件是队列中已经存满了元素,此时队尾指针指向最后一个元素,下一个位置没有可用空间存放新元素。
循环队列判断空和满的条件如下:
-
判断队列是否为空:循环队列为空的条件是队列中没有任何元素,此时队头指针和队尾指针指向的是同一个位置。
-
判断队列是否为满:循环队列为满的条件是队列中已经存满了元素,此时队尾指针的下一个位置等于队头指针。特别地,如果下一个位置为0,也视为队列满。
3、栈和队列的区别
队列(Queue)和栈(Stack)是两种不同的数据结构。
3.1. 数据存储方式不同:队列按照先进先出(FIFO)的原则存储数据,新元素插入到队列的末尾,而被删除的元素则是队列中最早的元素,位于队列的开头;栈按照后进先出(LIFO)的原则存储数据,最后插入的元素最先被删除。
3.2. 插入和删除的位置不同:队列只允许在队尾进行插入(入队)操作,在队头进行删除(出队)操作;栈只允许在栈顶进行插入和删除操作。
3.3. 使用场景不同:队列经常用于缓存、消息处理、任务调度等场景,比如线程池中的任务队列;栈经常用于递归算法和计算表达式等场景,比如编译器中对代码的解析实现。
总的来说,队列与栈都是常见的线性数据结构,它们各自有着不同的功能、数据存储方式和应用场景,选择合适的数据结构可以提高算法效率。
4、栈和队列的联系
队列和栈都是一种基本的数据结构,它们都是线性数据结构,并且都支持在其结构的某一端进行插入、删除元素的操作。两者在插入和删除的方式上有所不同,队列是按照先进先出的原则进行操作,而栈是按照后进先出的原则进行操作。
队列和栈都具有一些相似的应用场景,比如:
1. 缓存
队列和栈都可以用于缓存数据。对于队列而言,可以用来存储需要异步处理的任务,这些任务可以按照入队顺序逐个进行处理;对于栈而言,可以用于存储最近使用的数据,比如在网页浏览器的后退功能中,会使用栈来保存最近访问的多个网页。
2. 消息处理
队列可以用于异步消息处理、事件处理等场景。比如在消息队列中,可以将要处理的消息按照入队顺序依次处理;而栈则不太适合消息处理场景。
3. 算法实现
队列和栈都常用于算法实现,比如栈可以用于递归算法、代码解析等场景,队列可以用于 BFS(广度优先搜索)算法等场景。
综上所述,虽然队列和栈在功能和应用场景上有着明显的区别,但是它们都是重要的数据结构,在编写程序时都具有广泛的应用价值。