TypeScript数组实现栈与对象实现栈的区别详解

目录
  • 前言
  • 数组实现栈
    • 实现思路
    • 实现代码
    • 编写测试代码
  • 对象实现栈
    • 实现代码
    • 编写测试代码
  • 二者的区别
    • 十进制转二进制

前言

栈作为一种数据结构,它可以应用在很多地方,当你需要经常获取刚存放进去的数据时,那么栈这种数据结构将是你的首选。

栈的实现方式一般有两种:数组实现和对象实现,这两种实现方式最终实现的功能都是一样的,但是在性能上却有着很大的差别。

本文将详细讲解这两种实现方式的差异并用TypeScript将其实现,欢迎各位感兴趣的开发者阅读本文。

数组实现栈

本文讲解的是栈用代码的实现,如果对栈这种数据结构还不是很了解的话,可以移步我的另一篇文章:栈与队列

实现思路

栈的核心思想为后进先出(LIFO),那么我们可以用数组来描述栈。

接下来,我们来看下,一个栈都需要具备哪些功能:

  • 入栈,添加一个新元素至栈顶(数组的末尾)。
  • 出栈,将栈顶的元素移除并返回被移除的元素。
  • 获取栈顶元素,获取当前栈顶元素返回。
  • 判断栈是否为空,判断栈(数组)内是否有数据。
  • 清空栈,移除栈内所有的元素。
  • 获取栈大小,返回栈里的元素个数。
  • 输出栈内数据,将栈中的所有元素以字符串的形式返回。

我们分析完栈都需要具备哪些功能后,发现数组中提供了很多现成的API可以实现上述功能,接下来,跟大家分享下上述功能的实现思路。

  • 入栈(push),可以使用数组的push方法直接往数组的末尾添加元素。
  • 出栈(pop),可以使用数组的pop方法直接移除栈中的元素,该方法会返回当前被移除的元素。
  • 栈顶元素(peek),可以通过数组的长度-1获取到数组中的最后一个元素。
  • 栈是否为空(isEmpty),可以通过判断数组的长度是否为0来实现。
  • 清空栈(clear),可以将数组直接赋值为空或者调用出栈方法直至栈中的数据为空。
  • 栈大小(size),可以返回数组的长度。
  • 输出栈内数据,可以调用数组的toString方法将数组转换为字符串。

实现代码

有了实现思路后,我们就可以将上述实现思路转换为代码了。

  • 新建一个Stack.ts文件
  • 定义栈并规定其类型
    private items: any[];
  • 在构造器中初始化栈
    constructor() {
      this.items = [];
    }
  • 根据实现思路实现栈中的函数
    // 入栈
    push(item:any) {
        this.items.push(item);
    }
    // 出栈
    pop() {
        return this.items.pop();
    }
    // 返回栈顶元素
    peek() {
        return this.items[this.items.length - 1];
    }
    // 判断栈是否为空
    isEmpty() {
        return this.items.length === 0;
    }
    // 清空栈栈内元素
    clear() {
        this.items = [];
    }
    // 获取栈内元素数量
    size():number{
        return this.items.length;
    }
    // 将栈内元素转为字符串
    toString(){
        return this.items.toString();
    }

完整代码请移步:Stack.ts

编写测试代码

上述代码我们实现了一个栈,接下来我们往栈中添加几条数据,测试栈内的方法是否正确执行。

  • 新建一个StackTest.js文件
  • 实例化一个栈
const stack = new Stack();
  • 测试栈内方法是否正确执行
// 入栈
stack.push("第一条数据");
stack.push("第二条数据");
// 出栈
stack.pop();
// 返回栈顶元素
console.log(stack.peek());
// 查看栈大小
console.log(stack.size());
// 判断栈是否为空
console.log(stack.isEmpty());
// 返回栈内所有元素
console.log(stack.toString())
// 清空栈
stack.clear()

完整代码请移步:StackTest.js

执行结果如下

对象实现栈

实现一个栈最简单的方式是通过数组存储每一个元素。在处理大量数据时,我们需要评估如何操作数据是最高效的。

在使用数组时,大部分方法的时间复杂度都为O(n),我们需要迭代整个数组直至找到目标元素,在最坏的情况下我们需要迭代数组的每一个位置。数组是元素的一个有序集合,为了保证元素排列有序,它会占用更多的内存空间。

如果我们可以直接获取元素,占用更少的内存空间,并且仍然保证所有元素都按照我们的需要进行排列,就属于最优解决方案了。

实现代码

我们可以使用一个对象来存储所有的栈元素,保证它们的顺序并且遵循LIFO原则。接下来我们来看看如何使用对象来实现栈。

  • 新建一个ObjStack.ts文件
  • 定义栈对象结构
interface StackObj {
    [propName: number] : any;
}
  • 定义栈并规定其类型,count用于记录栈的大小。
private items: StackObj;
private count: number;
  • 在构造器中初始化栈相关变量
this.items = {};
this.count = 0;
  • 入栈,当前栈的大小为新元素的key。
push(item: any) {
    this.items[this.count] = item;
    this.count++;
}
  • 出栈,当前栈大小-1,取出栈顶元素,删除栈顶元素,返回取出的栈顶元素
pop() {
    if(this.isEmpty()){
        return undefined;
    }
    this.count--;
    const result = this.items[this.count];
    delete this.items[this.count];
    console.log(this.items);
    return result;
}
  • 返回栈顶元素,以当前栈大小-1为key获取其对应的value值。
peek() {
    if(this.isEmpty()){
        return undefined;
    }
    return this.items[this.count - 1];
}
  • 判断栈是否为空,清空栈内元素,获取栈内元素数量
// 判断栈是否为空
isEmpty() {
    return this.count === 0;
   }
// 清空栈内元素
clear() {
    this.items = [];
    this.count = 0;
}
// 获取栈内元素数量
size():number{
    return this.count;
}
  • 将栈内元素转为字符串,遍历当前栈对象中的数据,将栈中的数据用逗号拼接并返回。
toString(){
    if (this.isEmpty()){
        return "";
    }
    let objString = `${this.items[0]}`;
    for (let i = 1; i < this.count; i++){
        objString = `${objString},${this.items[i]}`
    }
    return objString;
}

完整代码请移步:ObjStack.ts

编写测试代码

上述代码我们用对象实现了一个栈,接下来我们往栈中添加几条数据,测试栈内的方法是否正确执行。

  • 新建一个StackObjTest.js文件
  • 实例化一个栈
const stack = new ObjStack();
  • 测试栈内方法是否正确执行
// 入栈
stack.push("第一条数据");
stack.push("第二条数据");
// 出栈
stack.pop();
// 返回栈顶元素
console.log(stack.peek());
// 查看栈大小
console.log(stack.size());
// 判断栈是否为空
console.log(stack.isEmpty());
// 返回栈内所有元素
console.log(stack.toString())
// 清空栈
stack.clear()

完整代码请移步:StackObjTest.js

执行结果如下

二者的区别

数组大部分方法的时间复杂度都为O(n),数组中的元素是一个有序集合,为了保证元素排列有序,它会占用更多的内存空间。

对象可以通过key直接访问到目标元素时间复杂度为O(1),我们可以直接目标元素进行操作,速度明显比数组快了很多倍。

接下来,我们通过一个实例来看看这两者在执行速度上的差异。

十进制转二进制

把十进制转为二进制,需要将该十进制除以2并对商取整,直到结果是0为止。

  • 声明一个函数参数为一个十进制数
const decimalToBinaryStack = function (decNumber) {
}
  • 函数内部声明一个变量,用于接收当前传进来的参数进行除法运算后得到的值。
// 传进来的十进制数
let number = decNumber;
  • 函数内部实例化一个栈,用于保存模运算后得出的值。
  • 函数内部声明两个变量,用户保存当前模运算的值和最终生成的二进制字符串
// 余数
let rem;
// 二进制结果
let binaryString = "";
  • while循环,判断当前参数进行除法运算后得到的值是否为0,如果不为0就对当前结果进行模运算,将模运算得到的值入栈,对当前结果进行除法运算,直至当前结果为0。
while (number > 0) {
    // 模运算
    rem = Math.floor(number % 2);
    // 将余数入栈
    stack.push(rem);
    // 当前十进制结果除以二
    number = Math.floor(number / 2);
}
  • while循环,将栈中的数据取出拼接到二进制结果字符串中去
while (!stack.isEmpty()) {
    binaryString += stack.pop().toString();
}
  • 返回二进制结果字符串
return binaryString;

完整代码请移步:Examples.js

实现代码如上所述,唯一不同的就是一个使用的是对象栈一个使用的数组栈,接下来我们来看下不同栈的运行时间差距。

以上就是TypeScript数组实现栈与对象实现栈的区别详解的详细内容,更多关于TypeScript数组对象实现栈的资料请关注我们其它相关文章!

(0)

相关推荐

  • 在React项目中使用TypeScript详情

    目录 项目目录及ts文件划分 在项目中使用TypeScript具体实践 组件声明 React Hooks使用 useState useRef useCallback useMemo useContext useReducer useImperativeHandle Axios请求/响应定义封装 前言: 本文主要记录我如何在React项目中优雅的使用TypeScript,来提高开发效率及项目的健壮性. 项目目录及ts文件划分 由于我在实际项目中大部分是使用umi来进行开发项目,所以使用umi生成的

  • 前端算法之TypeScript包含min函数的栈实例详解

    目录 前言 思路梳理 实现代码 示例代码 前言 基于数据结构: “栈”,实现一个min函数,调用此函数即可获取栈中的最小元素.在该栈中,调用min.push.pop的时间复杂度都是O(1). 本文就跟大家分享下这个算法,欢迎各位感兴趣的开发者阅读本文. 思路梳理 相信大多数开发者看到这个问题,第一反应可能是每次往栈中压入一个新元素时,将栈里的所有元素排序,让最小的元素位于栈顶,这样就能在O(1)的时间内得到最小元素了.但这种思路不能保证最后入栈的元素能够最先出栈,因此这个思路行不通. 紧接着,我

  • Zod进行TypeScript类型验证使用详解

    目录 引言 什么是类型验证,为什么需要它? 为什么要使用zod? 使用 Zod 进行类型验证的示例 Primitives 对象 类型推断 组合类型 注意事项 安全解析 无法识别的Key被删除 其他事项 Zod 与其他库的比较 结论 引言 这篇文章将描述如何使用Zod为您的项目设置类型验证.Zod 是一个用于类型声明和验证的开源 TypeScript 库.我们将研究为什么使用 Zod 进行类型验证,提供如何使用它的示例,并将其与其他库进行比较. 什么是类型验证,为什么需要它? 类型验证是验证数据结

  • TypeScript判断对称的二叉树方案详解

    目录 前言 实现思路 实现代码 示例代码 前言 如果一颗二叉树和它的镜像一样,那么它就是对称的.实现一个函数用于判断一颗二叉树是否对称,你会怎么做? 本文将分享一种解决方案,欢迎各位感兴趣的开发者阅读本文. 实现思路 在上一篇文章二叉树的镜像中我们知道了此问题的解决方案是前序遍历,那么我们可以修改下前序遍历算法,父节点遍历后,先遍历它的右子节点,再遍历它的左子节点,我们把这种算法称为:对称前序遍历 如下图所示的两棵树,我们分别列举下两种遍历的结果: 树A: 前序遍历:8, 6, 5, 7, 6,

  • TypeScript获取二叉树的镜像实例

    目录 前言 思路分析 实现代码 前言 给定一颗二叉树,如何获取它的镜像?本文将跟大家分享这个问题的解决方案,欢迎各位感兴趣的开发者阅读本文. 思路分析 当我们把一张写有文字的纸放在镜子前面,你看到的内容正好与你写的内容是相反的.那么我们就可以依据照镜子的经验画出它的镜像了,如下所示: 镜像前后的两棵树根节点相同 镜像后的树与镜像前相比:它们的左.右子节点交换了位置 通过观察后,我们就得出了一颗树的镜像过程:先序遍历这棵树的每个节点,如果遍历到的节点有子节点,就交换它的两个子节点.当交换完所有非叶

  • Typescript 封装 Axios拦截器方法实例

    目录 引言 创建 class axios.create([config]) 封装 request(config)通用方法 封装-拦截器(单个实例独享) 扩展 Http 自定义拦截器 封装-拦截器(所有实例共享) 封装-拦截器(单个请求独享) 装修 Http class 返回经过 request 返回数据结构(DTO) 拦截器执行顺序 操作场景控制 引言 对 axios 二次封装,更加的可配置化.扩展性更加强大灵活 通过 class 类实现,class 具备更强封装性(封装.继承.多态),通过实例

  • js删除数组中的元素delete和splice的区别详解

    例如有一个数组是 :var textArr = ['a','b','c','d']; 这时我想删除这个数组中的b元素: 方法一:delete 删除数组 delete textArr[1]  结果为: ["a",undefined,"c","d"] 只是被删除的元素变成了 undefined 其他的元素的键值还是不变. 方法二:aplice 删除数组 splice(index,len,[item]) 注释:该方法会改变原始数组. index:数组开

  • c字符串,string对象,字符串字面值的区别详解

    一.字符串字面值字符串字面值是一串常量字符,字符串字面值常量用双引号括起来的零个或多个字符表示,为兼容C语言,C++中所有的字符串字面值都由编译器自动在末尾添加一个空字符.字符串没有变量名字,自身表示自身 复制代码 代码如下: "Hello World!" //simple string literal"" //empty string literal"\nCC\toptions\tfile.[cC]\n" //string literal us

  • Typescript 中的 interface 和 type 到底有什么区别详解

    interface VS type 大家使用 typescript 总会使用到 interface 和 type,官方规范 稍微说了下两者的区别 An interface can be named in an extends or implements clause, but a type alias for an object type literal cannot. An interface can have multiple merged declarations, but a type

  • TypeScript数组实现栈与对象实现栈的区别详解

    目录 前言 数组实现栈 实现思路 实现代码 编写测试代码 对象实现栈 实现代码 编写测试代码 二者的区别 十进制转二进制 前言 栈作为一种数据结构,它可以应用在很多地方,当你需要经常获取刚存放进去的数据时,那么栈这种数据结构将是你的首选. 栈的实现方式一般有两种:数组实现和对象实现,这两种实现方式最终实现的功能都是一样的,但是在性能上却有着很大的差别. 本文将详细讲解这两种实现方式的差异并用TypeScript将其实现,欢迎各位感兴趣的开发者阅读本文. 数组实现栈 本文讲解的是栈用代码的实现,如

  • js基础之DOM中document对象的常用属性方法详解

    -----引入 每个载入浏览器的 HTML 文档都会成为 Document 对象. Document 对象使我们可以从脚本中对 HTML 页面中的所有元素进行访问. 属性 1  document.anchors  返回对文档中所有 Anchor 对象的引用.还有document.links/document.forms/document.images等 2  document.URL       返回当前文档的url 3  document.title       返回当前文档的标题 4  do

  • 浅谈java中集合的由来,以及集合和数组的区别详解

    对象多了用集合存,数据多了用数组存. 数组是固定长度的,集合是可变长度的. 集合是:只要是对象就可以存,不管是不是同一种对象 而数组只能存储一种类型的对象 下面是集合的框架: 以上就是小编为大家带来的浅谈java中集合的由来,以及集合和数组的区别详解的全部内容了,希望对大家有所帮助,多多支持我们~

  • Vue路由对象属性 .meta $route.matched详解

    $route.fullPath 1 路由是:/path/:type真正路径是:/path/list 2 path匹配路径: /path/list 3 fullPath匹配路由: /path/:type 路由元信息 .meta const router = new VueRouter({ routes: [ { path: '/foo', component: Foo, children: [ { path: 'bar', component: Bar, // a meta field meta:

  • python中的数组赋值与拷贝的区别详解

    具体的注解我已经写在了程序里面:通俗的解释了python里面的浅拷贝与深拷贝的不同,请看程序. # -*- coding: utf-8 -*- import numpy as np import copy as cp import matplotlib.pyplot as plt import time import math fig = plt.figure() ax = fig.add_subplot(241) # 定义一个多维数组 x = np.array([[1, 2, 3], [4,

  • java数组的三种扩容方式以及程序实现详解

    因为数组是在内存中连续的一段存储空间,所以数组一旦被创建,空间就固定了,长度是不能扩增的. 数组的长度是固定的,如果需要扩充**,必须创建新数组,原数组的长度要复制到新数组中 .** java中,数组类型的变量传值的时候,事实上传递的是数组的地址 . Java数组扩容的原理 1)Java数组对象的大小是固定不变的,数组对象是不可扩容的. 2)利用数组复制方法可以变通的实现数组扩容. 3)System.arraycopy()可以复制数组. 4)Arrays.copyOf()可以简便的创建数组副本.

  • js 数组 find,some,filter,reduce区别详解

    区分清楚Array中filter.find.some.reduce这几个方法的区别,根据它们的使用场景更好的应用在日常编码中. Array.find Array.find 返回一个对象(第一个满足条件的对象)后停止遍历 const arrTest = [ { id: 1, name: "a" }, { id: 2, name: "b" }, { id: 3, name: "b" }, { id: 4, name: "c" }

随机推荐