【TypeScript】常见数据结构与算法(一):常见线性结构

思维导图

学习路线

常见线性结构

  • 线性结构(Linear List)是由n(n>=0)个数据元素(结点)a[0],a[1],a[2]…,a[n-1]组成的有序序列。
  • 其中:

数据元素的个数n定义为表的长度 = list.length()

list.length=0 表示没有一个元素时称为空表

将非空的线性表(a>=1)记作:(a[0],a[1],a[2]…,a[n-1])
数据元素a[i] (0<= i <= n-1)只是个抽象符号,其具体含义在不同情况下可以不同

image.png

数组(Array)结构

数组(Array)结构是一种重要的数据结构:

  • 几乎每种编程语言都会提供一种原生数据结构(自带的);
  • 并且我们可以借住数据结构来实现其他的数据结构,比如栈(Stack)、队列(Queue)、堆(Heap);

通常数组的内存是连续的,所以数组在知道下标值的情况下,访问小绿是非常高的
image.png

栈结构(Stack)

认识栈结构和特性

数组是一种线性结构,并且可以在数组的任意位置插入和删除数据。
但是有时候我们为了实现某些功能,必须对这种任意性加以限制。
而栈和队列就是比较常见的受限的线性结构。

栈结构示意图

image.png

栈它是一种受限的线性结构,先进后出(LIFO)

  • 其限制是仅允许在表的一端进行插入和删除运算。这一端被称为栈顶,相对的,把另一端称为栈底。
  • LIFO(last in first out)表示就是后进入的元素,第一个弹出栈空间。
  • 像一个栈插入新元素又称为进栈、入栈或压栈。它是把新的元素放到栈顶元素的上门,使之成为新的栈顶元素。
  • 从一个栈删除元素又称作出栈或者退栈,它是把栈顶元素删除,使其相邻的元素成为新的栈顶元素。
面试题目

image.png
image.png

栈结构实现

实现栈结构:

  • 基于数组实现
  • 基于链表实现
创建栈结构的类

创建一个栈的类

// 封装Stack
class ArrayStack {
  // 定义一个数组,用于存储元素
  private data:any[] = []

  // 实现栈操作方法
  push() {

  }
}

代码解析:

  • 我们创建了一个Stack,用户创建栈的类,可以定义一个泛型类。
  • 在构造函数中,定义了一个变量,这个变量可以用于保存当前栈对象中所有的元素。
  • 这个变量是一个数组类型。
  • 我们之后无论是压栈操作还是出栈操作,都是从数组中添加和删除元素。
  • 栈有一些相关的操作方法,通常无论是什么语言,操作都是比较类似的。

栈常见有哪些操作呢?

  • push(element):添加一个新元素到栈顶位置。
  • pop()∶移除栈顶的元素,同时返回被移除的元素。
  • peek():返回栈顶的元素,不对栈做任何修改(这个方法不会移除栈顶的元素,仅仅返回它)。
  • isEmpty():如果栈里没有任何元素就返回true,否则返回false。
  • size():返回栈里的元素个数。这个方法和数组的length属性很类似。

源码

// 公共结构
interface IList<T> {
  // peek查看第一个
  peek(): T | undefined;
  // 判断非空
  isEmpty(): boolean;
  // 返回队列长度
  get size(): number;
}


// 定义栈结构
interface BasisStack<T> extends IList<T> {
  push(element:T):void
  pop():T | undefined
}


// 封装Stack
class ArrayStack<T = any> implements BasisStack<T> {
  // 定义一个数组,用于存储元素
  private data: T[] = [];

  // 实现栈操作方法

  // 元素压入栈
  push(element: T): void {
    this.data.push(element);
  }

  // 将栈顶元素弹出栈
  pop(): T | undefined {
    return this.data.pop();
  }

  // 查看栈顶元素
  peek(): T | undefined {
    return this.data[this.data.length - 1];
  }

  // 判断栈是否为空
  isEmpty(): boolean {
    return this.data.length === 0;
  }

  // 返回栈的数据个数
  get size(): number {
    return this.data.length;
  }
}

export default ArrayStack;

队列结构(Queue)

认识队列以及特性

受限的线性结构:

  • 我们已经学习了一种受限的线性结构:栈结构
  • 这种受限结构对于解决某些特定问题,有特别效果
  • 我们接下来学习另一个受限结构:队列

队列(Queue)它是一种受限的线性表,先进先出(FIFO First In First Out)

  • 在于它只允许在队列的前端(front)进行删除操作
  • 而在队列的后端(rear)进行插入操作

image.png
线程队列:

  • 在开发中,为了让任务可以并行处理,通常会开启多线程;
  • 但是我们不能让大量的线程同时处理任务(占用资源);
  • 如果需要开启线程处理任务,我们就会使用线程队列;
  • 线程队列会依照次序来启动线程,并且处理对应的任务;

队列还有很多其他应用,后续很多算法中也会用到队列(比如二叉树的层序遍历)

实现队列结构封装

队列的实现和栈一样,有两种方案:

  • 基于数组实现
  • 基于链表实现

创建一个类实现队列

队列结构常见方法

常见操作:

  • enqueue(element):向尾部添加一个(或多个)新的项。
  • dequeue():移除队列的第一(即排在队列最前面的)项,并返回被移除的元素。
  • front/peek():返回一队列中的第一个元素----最先被添加的,也就是最先被移除的元素。队列不做任何变动(不移除元素,只返回元素信息—与Stack类的peek方法非常相似)。
  • isEmpty():如果队列中不包含任何元素,返回true,否则返回false。
  • size():返回队列包含的元素个数,与数组的length属性类似。

源码

interface IList<T> {
  // peek查看第一个
  peek(): T | undefined;
  // 判断非空
  isEmpty(): boolean;
  // 返回队列长度
  get size(): number;
}

interface BasisQueue<T> extends IList<T> {
  // 入队方法
  enqueue(element: T): void;
  // 出队方法
  dequeue(): T | undefined;
}

class ArrayQueue<T> implements BasisQueue<T> {
  // 内部通过数组保存
  private data: T[] = [];
  enqueue(element: T): void {
    this.data.push(element);
  }
  dequeue(): T | undefined {
    return this.data.shift();
  }
  peek(): T | undefined {
    return this.data[0];
  }
  isEmpty(): boolean {
    return this.data.length === 0;
  }
  get size(): number {
    return this.data.length;
  }
}

export default ArrayQueue;
面试题
击鼓传花

使用队列可以非常方便的实现最终结果
原游戏规则:

  • 所有人围成一个圈,从末尾同学手里开始向旁边的同学传花。
  • 这个时候当鼓声停止,花落谁家,谁就中大奖。

修改后的规则:

  • 所有人一圈开始数数,数到某个数字时自动淘汰
  • 最后剩下的这个人会获取胜利,请问最后剩下的时原来在哪一个位置上的人?

封装一个基于队列的函数:

  • 参数:所有人参与的姓名,基于数字
  • 结果:最终剩下一个人的名字
约瑟夫环:
  • 阿桥问题(有时也称为约瑟夫斯置换),是一个出现在计算机科学和数学中的问题。在计算机编程的算法中,类似问题又称为约瑟夫环。
  • 人们站在一个等待被处决的圈子里。
  • 计数从圆圈中的指定点开始,并沿指定方向围绕圆圈进行。在跳过指定数量的人之后,处刑下一个人。
  • 对剩下的人重复该过程,从下一个人开始,朝同一方向跳过相同数量的人,直到只剩下一个人,并被释放。
  • 在给定数量的情况下,站在第几个位置可以避免被处决?
  • 23
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
TypeScript是一种静态类型的编程语言,它提供了丰富的类型系统和面向对象的特性,使得开发者可以更好地组织和管理代码。在TypeScript中,高阶数据结构和算法可以通过类和泛型等特性来实现。 高阶数据结构是指那些在基本数据结构的基础上进行扩展或组合得到的数据结构。例如,堆、图、树等都可以被视为高阶数据结构。在TypeScript中,我们可以使用类来定义这些高阶数据结构,并通过泛型来指定其内部存储的数据类型。 下面是一个使用TypeScript实现的堆(Heap)数据结构的示例: ```typescript class Heap<T> { private data: T[] = []; public size(): number { return this.data.length; } public isEmpty(): boolean { return this.data.length === 0; } public insert(value: T): void { this.data.push(value); this.siftUp(this.data.length - 1); } public extractMin(): T | null { if (this.isEmpty()) { return null; } const min = this.data[0]; const last = this.data.pop()!; if (!this.isEmpty()) { this.data[0] = last; this.siftDown(0); } return min; } private siftUp(index: number): void { while (index > 0) { const parentIndex = Math.floor((index - 1) / 2); if (this.data[index] >= this.data[parentIndex]) { break; } [this.data[index], this.data[parentIndex]] = [this.data[parentIndex], this.data[index]]; index = parentIndex; } } private siftDown(index: number): void { const size = this.size(); while (index * 2 + 1 < size) { let childIndex = index * 2 + 1; if (childIndex + 1 < size && this.data[childIndex + 1] < this.data[childIndex]) { childIndex++; } if (this.data[index] <= this.data[childIndex]) { break; } [this.data[index], this.data[childIndex]] = [this.data[childIndex], this.data[index]]; index = childIndex; } } } ``` 以上是一个最小堆的实现,使用了数组来存储数据,并提供了插入和提取最小值的操作。堆是一种常见的高阶数据结构,用于解决许多问题,如优先队列和排序等。 通过使用TypeScript,我们可以更加清晰地定义和使用高阶数据结构和算法,并通过类型检查来减少错误和提高代码的可维护性。当然,这只是其中的一个例子,还有许多其他高阶数据结构和算法可以在TypeScript中实现。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值