栈和队列都是「操作受限」的数据结构。和基本的数组和链表相比,它们提供的 API 是不完整的。
队列只允许在队尾插入元素,在队头删除元素,栈只允许在栈顶插入元素,从栈顶删除元素。
本篇文章重点介绍栈和队列两种数据结构的 TypeScript 代码实现。
栈代码实现
首先,我们要知道栈数据结构的基本 API 如下:
typescript
export class Stack<T> {
// 栈增操作,从栈顶插入元素
push(element: T): void {
}
// 栈删操作,移除栈顶的元素并返回
pop(): T | undefined {
}
// 删查操作,获取栈顶的元素,但不删除
peek(): T | undefined {
}
// 辅助方法,判断栈是否为空
isEmpty(): boolean {
}
// 辅助方法,返回栈的大小
size(): number {
}
}
然后我们要考虑到,在 TypeScript 中,栈可以基于三种数据结构来实现:
- 基于数组实现
- 基于对象实现
- 基于链表实现
下面我们依次来看这三种实现方式。
基于数组代码实现
我们直接看代码吧:
typescript
export class Stack<T> {
// 存储栈元素的数组
private items: T[] = [];
constructor() {
this.items = [];
}
// 栈增操作,从栈顶插入元素
push(element: T): void {
this.items.push(element);
}
// 栈删操作,移除栈顶的元素并返回
pop(): T | undefined {
if (this.isEmpty()) {
return undefined;
}
return this.items.pop();
}
// 删查操作,获取栈顶的元素,但不删除
peek(): T | undefined {
if (this.items.length === 0) {
return undefined;
}
return this.items[this.count - 1];
}
// 辅助方法,判断栈是否为空
isEmpty(): boolean {
return this.size() === 0;
}
// 辅助方法,返回栈的大小
size(): number {
return this.items.length;
}
}
基于对象代码实现
我们直接看代码吧:
typescript
export class Stack<T> {
// 栈的元素数量
private count: number;
// 存储栈元素的对象
private items: Record<number, T> = {};
// 栈增操作,从栈顶插入元素
push(element: T): void {
this.items[this.count] = element;
this.count++;
}
// 栈删操作,移除栈顶的元素并返回
pop(): T | undefined {
if (this.isEmpty()) {
return undefined;
}
const element = this.items[this.count - 1];
delete this.items[this.count - 1];
this.count--;
return element;
}
// 删查操作,获取栈顶的元素,但不删除
peek(): T | undefined {
if (this.isEmpty()) {
return undefined;
}
return this.items[this.count - 1];
}
// 辅助方法,判断栈是否为空
isEmpty(): boolean {
return this.size() === 0;
}
// 辅助方法,返回栈的大小
size(): number {
return this.count;
}
}
基于链表代码实现
我们考虑用双向链表来实现栈,看下面的代码:
typescript
export class Stack<T> {
// 存储栈元素的双向链表
private items: DoublyLinkedList<T>;
constructor() {
this.items = new DoublyLinkedList();
}
// 栈增操作,从栈顶插入元素
push(element: T): void {
this.items.push(element);
}
// 栈删操作,移除栈顶的元素并返回
pop(): T | undefined {
if (this.items.isEmpty()) {
return undefined;
}
return this.items.pop();
}
// 删查操作,获取栈顶的元素,但不删除
peek(): T | undefined {
if (this.items.isEmpty()) {
return undefined;
}
return this.items.getTail().element;
}
// 辅助方法,判断栈是否为空
isEmpty(): boolean {
return this.items.isEmpty();
}
// 辅助方法,返回栈的大小
size(): number {
return this.items.size();
}
}
队列
首先,我们要知道队列数据结构的基本 API 如下:
typescript
export class Queue<T> {
// 队列增操作,在队尾添加一个元素
enquque(element: T): void {
}
// 队列删操作,移除队头的元素并返回
dequeue(): T | undefined {
}
// 队列查操作,查看队头元素
peek(): T | undefined {
}
// 辅助方法,判断队列是否为空
isEmpty(): boolean {
}
// 辅助方法,返回队列的长度
size(): number {
}
}
然后我们要考虑到,在 TypeScript 中,队列可以基于三种数据结构来实现:
- 基于数组实现
- 基于对象实现
- 基于链表实现
下面我们依次来看这三种实现方式。
基于数组代码实现
我们直接看代码吧:
typescript
export class Queue<T> {
// 存储队列元素的数组
private items: T[];
constructor() {
this.items = [];
}
// 队列增操作,在队尾添加一个元素
enquque(element: T): void {
this.items.push(element);
}
// 队列删操作,移除队头的元素并返回
dequeue(): T | undefined {
if (this.isEmpty()) {
return undefined;
}
return this.items.pop();
}
// 队列查操作,查看队头元素
peek(): T | undefined {
if (this.isEmpty()) {
return undefined;
}
return this.items[this.size() - 1];
}
// 辅助方法,判断队列是否为空
isEmpty(): boolean {
return this.size() === 0;
}
// 辅助方法,返回队列的长度
size(): number {
return this.items.length;
}
}
基于对象代码实现
我们直接看代码吧:
typescript
export class Queue<T> {
private count: number;
private lowestCount: number;
private items: Record<number, T>;
contructor() {
this.count = 0;
this.lowestCount = 0;
this.items = {};
}
// 队列增操作,在队尾添加一个元素
enquque(element: T): void {
this.items[this.count] = element;
this.count++;
}
// 队列删操作,移除队头的元素并返回
dequeue(): T | undefined {
if (this.isEmpty()) {
return undefined;
}
const element = this.items[this.lowestCount];
delete this.items[this.lowestCount];
this.lowestCount++;
return element;
}
// 队列查操作,查看队头元素
peek(): T | undefined {
if (this.isEmpty()) {
return undefined;
}
return this.items[this.lowestCount];
}
// 辅助方法,判断队列是否为空
isEmpty(): boolean {
return this.size() === 0;
}
// 辅助方法,返回队列的长度
size(): number {
return this.count - this.lowestCount;
}
}
基于链表代码实现
我们考虑用双向链表来实现栈,看下面的代码:
typescript
export class Queue<T> {
private items: DoublyLinkedList<T>;
constructor() {
this.items = new DoublyLinkedList();
}
// 队列增操作,在队尾添加一个元素
enquque(element: T): void {
this.items.push(element);
}
// 队列删操作,移除队头的元素并返回
dequeue(): T | undefined {
if (this.items.isEmpty()) {
return undefined;
}
const element = this.items.getHead().element;
this.items.shift();
return element;
}
// 队列查操作,查看队头元素
peek(): T | undefined {
if (this.items.isEmpty()) {
return undefined;
}
return this.items.getHead().element;
}
// 辅助方法,判断队列是否为空
isEmpty(): boolean {
return this.items.isEmpty();
}
// 辅助方法,返回队列的长度
size(): number {
return this.items.size();
}
}
双端队列
标准队列只能在队尾插入元素,队头删除元素,而双端队列的队头和队尾都可以插入或删除元素。
我们要知道双端队列数据结构的基本 API 如下:
typescript
export class Deque<T> {
// 双端队列增操作,在队尾添加一个元素
addLast(element: T): void {
}
// 双端队列增操作,在队头添加一个元素
addFirst(element: T): void {
}
// 双端队列删操作,移除队尾的元素并返回
removeLast(): T | undefined {
}
// 双端队列删操作,移除队头的元素并返回
removeFirst(): T | undefined {
}
// 双端队列查操作,查看队尾的元素
peekLast(): T | undefined {
}
// 双端队列查操作,查看队头的元素
peekFirst(): T | undefined {
}
// 辅助方法,判断双端队列是否为空
isEmpty(): boolean {
}
// 辅助方法,返回双端队列的长度。
size(): number {
}
}
基于数组代码实现
我们直接看代码吧:
typescript
export class Deque<T> {
// 存储队列元素的数组
private items: T[];
constructor() {
this.items = [];
}
// 双端队列增操作,在队尾添加一个元素
addLast(element: T): void {
this.items.push(element);
}
// 双端队列增操作,在队头添加一个元素
addFirst(element: T): void {
this.items.unshift(element);
}
// 双端队列删操作,移除队尾的元素并返回
removeLast(): T | undefined {
if (this.isEmpty()) {
return undefined;
}
return this.items.pop();
}
// 双端队列删操作,移除队头的元素并返回
removeFirst(): T | undefined {
if (this.isEmpty()) {
return undefined;
}
return this.items.shift();
}
// 双端队列查操作,查看队尾的元素
peekLast(): T | undefined {
if (this.isEmpty()) {
return undefined;
}
return this.items[this.size() - 1];
}
// 双端队列查操作,查看队头的元素
peekFirst(): T | undefined {
if (this.isEmpty()) {
return undefined;
}
return this.items[0];
}
// 辅助方法,判断双端队列是否为空
isEmpty(): boolean {
return this.size() === 0;
}
// 辅助方法,返回双端队列的长度。
size(): number {
return this.items.length;
}
}
基于对象代码实现
我们直接看代码吧:
typescript
export class Deque<T> {
private count: number;
private lowestCount: number;
private items: Record<number, T>;
constructor() {
this.count = 0;
this.lowestCount = 0'
this.items = {};
}
// 双端队列增操作,在队尾添加一个元素
addLast(element: T): void {
this.items[this.count] = element;
this.count++;
}
// 双端队列增操作,在队头添加一个元素
addFirst(element: T): void {
if (this.lowestCount > 0) {
this.items[this.lowestCount] = element;
this.lowestCount--;
} else {
for (let i = this.count; i > this.lowestCount; i--) {
this.items[i] = this.items[i - 1];
}
this.items[this.lowestCount] = element;
this.count++;
}
}
// 双端队列删操作,移除队尾的元素并返回
removeLast(): T | undefined {
if (this.isEmpty()) {
return undefined;
}
const element = this.items[this.count - 1];
delete this.items[this.count - 1];
this.count--;
return element;
}
// 双端队列删操作,移除队头的元素并返回
removeFirst(): T | undefined {
if (this.isEmpty()) {
return undefined;
}
const element = this.items[this.lowestCount];
delete this.items[this.lowestCount];
this.lowestCount++;
return element;
}
// 双端队列查操作,查看队尾的元素
peekLast(): T | undefined {
if (this.isEmpty()) {
return undefined;
}
return this.items[this.count - 1];
}
// 双端队列查操作,查看队头的元素
peekFirst(): T | undefined {
if (this.isEmpty()) {
return undefined;
}
return this.items[this.lowestCount];
}
// 辅助方法,判断双端队列是否为空
isEmpty(): boolean {
return this.size() === 0;
}
// 辅助方法,返回双端队列的长度。
size(): number {
return this.count - this.lowestCount;
}
}
基于链表代码实现
我们考虑用双向链表来实现栈,看下面的代码:
typescript
export class Deque<T> {
private items: DoublyLinkedList<T>;
// 双端队列增操作,在队尾添加一个元素
addLast(element: T): void {
this.items.push(element);
}
// 双端队列增操作,在队头添加一个元素
addFirst(element: T): void {
this.items.unshift(element);
}
// 双端队列删操作,移除队尾的元素并返回
removeLast(): T | undefined {
if (this.isEmpty()) {
return undefined;
}
return this.items.pop();
}
// 双端队列删操作,移除队头的元素并返回
removeFirst(): T | undefined {
if (this.isEmpty()) {
return undefined;
}
return this.items.shift();
}
// 双端队列查操作,查看队尾的元素
peekLast(): T | undefined {
if (this.isEmpty()) {
return undefined;
}
return this.items.getTail().element;
}
// 双端队列查操作,查看队头的元素
peekFirst(): T | undefined {
if (this.isEmpty()) {
return undefined;
}
return this.items.getHead().element;
}
// 辅助方法,判断双端队列是否为空
isEmpty(): boolean {
return this.items.isEmpty();
}
// 辅助方法,返回双端队列的长度。
size(): number {
return this.items.size();
}
}