【PHP数据结构】栈的相关逻辑操作
对于逻辑结构来说,我们也是从最简单的开始。堆栈、队列,这两个词对于大部分人都不会陌生,但是,堆和栈其实是两个东西。在面试的时候千万不要被面试官绕晕了。堆是一种树结构,或者说是完全二叉树的结构。而今天,我们主要讲的就是这个栈的应用。
什么是栈?
栈一般就是一种顺序的数据结构。它最大的特点就是后进先出(LIFO),或者反过来说先进后出(FILO)也是可以的。这两句话到底是什么意思呢?最典型的例子就是大家看电视剧时,特别是枪战片时绝对会看到的一样东西:弹匣。
弹匣在装弹的时候都是一个一个的将子弹压进弹匣的,也就是说,第一颗子弹是被压在最底下的,而开枪的时候则是按相反的顺序从弹匣的最顶部弹出来的,第一颗放进去的子弹是最后一个才被打出来的。
这个例子其实已经非常形象了,我们再统一一下术语。将子弹压进弹匣叫做“入栈”,第一颗子弹在最底下,这个位置叫做“栈底”,最后一颗子弹在最顶上,这个位置叫做“栈顶”,打出的这颗子弹是“栈顶”的那颗子弹,这个操作叫做“出栈”。
通过上面术语的定义,我们就可以看出,栈的逻辑操作主要就是“入栈”和“出栈”,而逻辑结构最需要关心的是这个“栈顶”和“栈底”在进行出入栈时的状态。当然,栈的逻辑结构使用顺序或链式结构都是没有问题的,我们就一个一个地来看一下。
顺序栈
首先还是比较简单的顺序栈的实现。既然是顺序结构,那么就是用数组了。不过,我们还需要记录一下“栈顶”或“栈底”的情况,所以我们将顺序栈的这个数组封装到一个类中。同时,在这个类中定义一个属性来标明当前栈的“栈顶”或“栈底”指针,其实就是当前“栈顶”或“栈底”在数组中的下标位置。通常来说,我们只需要记录“栈顶”的位置就可以了,将“栈底”默认为 -1 即可。因为数组下标本身是从 0 开始的,所以当“栈顶”属性为 -1 时,这个栈就是一个空栈,因为它的“栈顶”和“栈底”在一起,里面并没有元素。
class SqStack
{
public $data;
public $top;
}
初始化顺序栈很简单,一个空的数组并将 $top 设置为 -1 。
function InitSqStack()
{
$stack = new SqStack();
$stack->data = [];
$stack->top = -1;
return $stack;
}
接下来就是“入栈”和“出栈”的操作了,先看代码。
function PushSqStack(SqStack &$stack, $x){
$stack->top ++;
$stack->data[$stack->top] = $x;
}
function PopSqStack(SqStack &$stack){
// 栈空
if($stack->top == -1){
return false;
}
$v = $stack->data[$stack->top];
$stack->top--;
return $v;
}
入栈很简单,给数组元素添加内容,然后 $top++ 就可以了。不过如果是 C 语言的话,因为它有数组长度的限制,所以在入栈的时候,我们也需要判断一下栈是否已经满了。当然,在 PHP 中我们就没有这个顾虑啦。
顺序栈入栈图示
出栈的时候需要判断当前的栈是否已经空了,这个就不区分什么语言了,因为要是比 -1 还小的话,再次使用这个栈就会出现问题了。在出栈的时候如果栈已经空了就不要再给 $top-- 了,然后获取栈顶元素并返回就可以了。
顺序栈出栈图示
我们来看一下这个顺序栈的测试结果。
$stack = InitSqStack();
PushSqStack($stack, 'a');
PushSqStack($stack, 'b');
PushSqStack($stack, 'c');
var_dump($stack);
// object(SqStack)#1 (2) {
// ["data"]=>
// array(3) {
// [0]=>
// string(1) "a"
// [1]=>
// string(1) "b"
// [2]=>
// string(1) "c"
// }
// ["top"]=>
// int(2)
// }
echo PopSqStack($stack), PHP_EOL; // c
echo PopSqStack($stack), PHP_EOL; // b
echo PopSqStack($stack), PHP_EOL; // a
var_dump($stack);
// object(SqStack)#1 (2) {
// ["data"]=>
// array(3) {
// [0]=>
// string(1) "a"
// [1]=>
// string(1) "b"
// [2]=>
// string(1) "c"
// }
// ["top"]=>
// int(-1)
// }
通过数组来操作栈是不是非常地简单。看完学习完链栈之后,我们还会讲到 PHP 已经为我们准备好的数组栈的操作函数哦,使用起来会更加的方便。
链栈
其实对于链式存储结构来说,核心的内容还是一样的,同样是要关心我们的栈顶,也同样要关心出入栈的操作。但是,在链式中,我们可以使有头插法,也就是让插入的数据保持在链的顶端来实现“栈顶”的效果。这样,我们就不需要一个专门的属性来保存当前的栈顶位置了。直接通过一个图来理解会更清晰。
class LinkStack{
public $data;
public $next;
}
数据的结构就是一个典型的链式结构就可以了,主要还是看出入栈的操作是如何进行的。
function InitLinkStack(){
return null;
}
function PushLinkStack(?LinkStack &$stack, $x){
$s = new LinkStack();
$s->data = $x;
$s->next = $stack;
$stack = $s;
}
function PopLinkStack(?LinkStack &$stack){
if($stack == NULL){
return false;
}
$v = $stack->data;
$stack = $stack->next;
return $v;
}
在链栈中其实初始化空栈的操作意义不大。我们可以直接定义一个 null 变量然后针对它进行链式操作就可以了,但在这里我们还是与顺序栈保持统一。就像顺序栈中的栈底为 -1 一样,在链栈中,我们也约定好栈底为一个 null 对象节点。
接下来就是入栈操作了。这里我们使用的是头插法,其实就是将新元素放到链表的顶端。先实例化一个节点,然后将这个节点的 next 指向链表的头节点。接着再让当前这个节点成为链表的新的头节点,就像下图所示的那样。
同理,出栈的操作其实也是类似的,将头节点变成当前头节点的 next 节点,直到当前节点变成 null ,也就是栈已经空了,如图所示:
最后,我们同样的测试一下这一套链式栈的代码运行情况如何。
$stack = InitLinkStack();
PushLinkStack($stack, 'a');
PushLinkStack($stack, 'b');
PushLinkStack($stack, 'c');
var_dump($stack);
// object(LinkStack)#3 (2) {
// ["data"]=>
// string(1) "c"
// ["next"]=>
// object(LinkStack)#2 (2) {
// ["data"]=>
// string(1) "b"
// ["next"]=>
// object(LinkStack)#1 (2) {
// ["data"]=>
// string(1) "a"
// ["next"]=>
// NULL
// }
// }
// }
echo PopLinkStack($stack), PHP_EOL; // c
echo PopLinkStack($stack), PHP_EOL; // b
echo PopLinkStack($stack), PHP_EOL; // a
var_dump($stack);
// NULL
是不是很多小伙伴已经看出之前我们花费了 4 篇文章的时间来讲述线性结构中的顺序表和链表的重要作用了吧。它们真的是一切其它逻辑结构的基础。不光是栈,在队列、树、图中我们都会有不同结构的线性和链式的实现。当然,更重要的是能体会它们之间的区别,在不同的业务场景中,两种不同的存储结构可能真的会带来完全不一样的体验。
PHP 为我们提供的数组栈操作
最后,我们简单的看一下在 PHP 中已经为我们准备好的两个数组操作函数。有了它们,对于顺序栈来说,我们的操作可以简化到非常傻瓜智能的效果。
$sqStackList = [];
array_push($sqStackList, 'a');
array_push($sqStackList, 'b');
array_push($sqStackList, 'c');
print_r($sqStackList);
// Array
// (
// [0] => a
// [1] => b
// [2] => c
// )
array_pop($sqStackList);
print_r($sqStackList);
// Array
// (
// [0] => a
// [1] => b
// )
echo count($sqStackList) > 0 ? $sqStackList[count($sqStackList) - 1] : false, PHP_EOL;
// b
array_pop($sqStackList);
echo count($sqStackList) > 0 ? $sqStackList[count($sqStackList) - 1] : false, PHP_EOL;
// c
array_pop($sqStackList);
print_r($sqStackList);
// Array
// (
// )
估计不少同学早就用过这两个函数了。array_push() 就是向数组中压入一个数据,其实说白了,增加一个数据到数组中而已,没什么特别稀罕的功能。而 array_pop() 则是将数组最后一个位置的数据弹出。是不是和我们上面自己实现的那个顺序栈是完全相同的概念。没错,既然语言环境已经为我们准备好了,那么除了在某些场景下需要链式结构的话,大部分情况下我们直接使用这两个函数就可以方便地实现 PHP 中的栈操作了。
总结
栈这个逻辑结构是不是非常的简单清晰呀,在日常应用中其实栈的使用非常广泛。比如算式中的前缀算式、中缀算式、后缀算式的转化,比如我们后面学习树、图时要接触到了BFS(深度搜索),再根据BFS引出递归这个概念。另外,在解析字符时的一些对称匹配、回文算法的判断等等,这些都是栈的典型应用。可以说,栈这个东西撑起了计算机算法的半壁江山。而另外半壁呢?当然就是我们下回要讲的:队列。
测试代码:
参考资料:
《数据结构》第二版,严蔚敏
《数据结构》第二版,陈越
《数据结构高分笔记》2020版,天勤考研
关注公众号:【硬核项目经理】获取最新文章
添加微信/QQ好友:【xiaoyuezigonggong/149844827】免费得PHP、项目管理学习资料
知乎、公众号、抖音、头条搜索【硬核项目经理】
B站ID:482780532
【PHP数据结构】栈的相关逻辑操作的更多相关文章
- 【PHP数据结构】队列的相关逻辑操作
在逻辑结构中,我们已经学习了一个非常经典的结构类型:栈.今天,我们就来学习另外一个也是非常经典的逻辑结构类型:队列.相信不少同学已经使用过 redis . rabbitmq 之类的缓存队列工具.其实, ...
- 【PHP数据结构】链表的相关逻辑操作
链表的操作相对顺序表(数组)来说就复杂了许多.因为 PHP 确实已经为我们解决了很多数组操作上的问题,所以我们可以很方便的操作数组,也就不用为数组定义很多的逻辑操作.比如在 C 中,数组是有长度限制的 ...
- 【PHP数据结构】顺序表(数组)的相关逻辑操作
在定义好了物理结构,也就是存储结构之后,我们就需要对这个存储结构进行一系列的逻辑操作.在这里,我们就从顺序表入手,因为这个结构非常简单,就是我们最常用的数组.那么针对数组,我们通常都会有哪些操作呢? ...
- 数据结构——栈(C语言实现)
#include <stdio.h> #include <stdlib.h> #include<string.h> #include<malloc.h> ...
- C++ 泛型 编写的 数据结构 栈
平时编程里经常需要用到数据结构,比如 栈和队列 等, 为了避免每次用到都需要重新编写的麻烦现将 C++ 编写的 数据结构 栈 记录下来,以备后用. 将 数据结构 栈 用头文件的形式 ...
- C语言数据结构-栈的实现-初始化、销毁、长度、取栈顶元素、查找、入栈、出栈、显示操作
1.数据结构-栈的实现-C语言 #define MAXSIZE 100 //栈的存储结构 typedef struct { int* base; //栈底指针 int* top; //栈顶指针 int ...
- 数据结构——栈的实现(数组、Java)
巩固数据结构 栈是一种有限制的线性表 只能对表尾进行操作 package com.shine.test.datastruct; import java.util.Arrays; public clas ...
- PHP SPL标准库之数据结构栈(SplStack)介绍(基础array已经可以解决很多问题了,现在开始解决问题)
PHP SPL标准库之数据结构栈(SplStack)介绍(基础array已经可以解决很多问题了,现在开始解决问题) 一.总结 SplStack就是继承双链表(SplDoublyLinkedList)实 ...
- java数据结构——栈(Stack)
学习数据结构与算法是枯燥的,但只有坚持不懈的积累,才会有硕果累累的明天. /** * 继续学习Java数据结构 ————栈 * 栈的实现其实还是使用数组,只不过我们不能直接访问数组下标,而是通过一个指 ...
随机推荐
- 用python 30行代码,搞定一个简单截图调取的百度识字功能
在做一个数据标注过程中人工需要识别文字. 想了想写了一个小脚本, 大致过程这样的. 截图功能写了好久也没写明白,索性直接调用第三方的截图工具了,在采用qq或者微信截图时,截图完成后保存大致保存在剪切板 ...
- QZEZTEST2021.7.27分析
T1 qzez 错误检测 题意 思路 代码 T2 qzez 比赛 题意 题面 有\(AB\)两队,每队\(n\)人,两队间进行\(n\)场比赛,每个人都要参赛,对手随机且概率均等.每人都有一个实力值, ...
- Pikachu-Unsafe Fileupload模块
一.概述 文件上传功能在web应用系统很常见,比如很多网站注册的时候需要上传头像.上传附件等等.当用户点击上传按钮后,后台会对上传的文件进行判断 比如是否是指定的类型.后缀名.大小等等,然后将其按照设 ...
- 生成二维码项目pom.xml中QRCode依赖报错
pom.xml报错如下: 解决方法: 1.将QRCode.jar包下载到E盘下 网盘下载地址: 链接:https://pan.baidu.com/s/1sY1NK5ekCkNH0uqraZMnKw 提 ...
- oracle给system用户增加sysdba和sysoper 角色
第一步:先用sys用户登录到数据库 第二步:打开一个sql窗口,输入grant sysoper to system; 点击执行 第三步:等待执行完成之后,再执行grant sysdba to sys ...
- Java社区——个人项目开发笔记(一)
1.maven安装与测试 安装过程略,常用的maven命令行工具: mvn --version 查看maven版本 mvn compile 编译maven工程 mvn clean 删除编译文件 mvn ...
- Centos7上yum安装mongodb4-2
vim /etc/yum.repos.d/mongodb-org-4.2.repo [mongodb-org-4.2] name=MongoDB Repository baseurl=https:// ...
- ASP.NET Core:ASP.NET Core程序使用Docker部署
一.前言 这篇文章介绍如何将一个ASP.NET Core应用程序在Docker中进行部署.开发工具使用的是Visual Studio 2019和VS Code. 二.使用Docker部署 我们选择新建 ...
- java 日期格式化-- SimpleDateFormat 的使用。字符串转日期,日期转字符串
日期和时间格式由 日期和时间模式字符串 指定.在 日期和时间模式字符串 中,未加引号的字母 'A' 到 'Z' 和 'a' 到 'z' 被解释为模式字母,用来表示日期或时间字符串元素.文本可以使用单引 ...
- webpack4学习之 babel
webpack之前一知半解,这次有空就把最新的webpack4好好学习一下(2019-05-29 因为webpack的很多东西版本都在升级,网上博客很多都是老版本的,所以加个时间方便大家决定是否有必要 ...