了解 TypeScript 中的数据结构

2025/01/14 typescript数据结构

译文:Understanding Data Structures in TypeScript: A Practical Guide with Real-World Examples (opens new window)

大家好!今天,我们将开始一段令人兴奋的旅程,探索 TypeScript 中的数据结构领域。无论你是经验丰富的老鸟还是刚刚入门小白,了解数据结构对于编写高效、可维护的代码都至关重要。所以,拿起你最喜欢的饮料,让我们一起来学习吧!

# 简介

数据结构是高效算法和软件设计的基石。通过数据结构,我们可以有效地组织和管理数据,从而实现各种操作的最佳性能。在 TypeScript 中,我们可以实现各种数据结构,每种结构都适合不同的应用场景。

提示:请始终选择最适合您应用程序需求的数据结构。正确的选择可以大大提高性能和可读性。

# 数组

数组是最基本的数据结构,它允许我们在连续的内存块中存储元素。它非常适合需要通过索引快速访问元素的场景。

# 示例

管理任务列表

// Define an array of tasks
let tasks: string[] = ['Write blog post', 'Review code', 'Push to GitHub'];

// Add a new task
tasks.push('Update LinkedIn profile');
// Remove the last task
const lastTask = tasks.pop();
// Iterate over tasks
tasks.forEach((task, index) => {
    console.log(`${index + 1}: ${task}`);
});

说明

  • 我们定义了一个包含字符串的数组 tasks
  • 我们使用 push() 添加一个新任务;
  • 我们使用 pop() 删除最后一个任务;
  • 我们使用 forEach() 遍历这些任务;

# 最佳实践

使用 TypeScript 的类型注解(如 string[])确保所有数组元素都是预期类型,从而在编译时捕获潜在错误。

# 实际应用

数组是创建和管理有序集合(如音乐应用程序中的播放列表)的完美工具。您可以添加歌曲、删除歌曲或根据歌曲的位置获取歌曲。

# 链表

链表由节点组成,每个节点都包含数据和对下一个节点的引用。它非常适合需要高效插入和删除的应用程序。

# 示例

实现简单的单链路链表

// Define a node
class ListNode<T> {
  constructor(
    public value: T,
    public next: ListNode<T> | null = null
  ) {}
}

// Define the linked list
class LinkedList<T> {
    private head: ListNode<T> | null = null;
    // Add a node to the end
    append(value: T): void {
        const newNode = new ListNode(value);
        if (!this.head) {
            this.head = newNode;
            return;
        }
        let current = this.head;
        while (current.next) {
            current = current.next;
        }
        current.next = newNode;
    }
    // Print the list
    print(): void {
        let current = this.head;
        while (current) {
            console.log(current.value);
            current = current.next;
        }
    }
}
// Usage
const list = new LinkedList<number>();
list.append(10);
list.append(20);
list.append(30);
list.print();

说明

  • 我们定义了一个 ListNode 类,代表每个节点;
  • 我们定义了一个 LinkedList 类,该类具有添加节点和打印列表的方法;
  • 我们创建了一个链接列表,并添加了值为 10、20 和 30 的节点;

# 最佳实践

使用泛型(<T>)来创建可重用的、类型安全的数据结构。

# 实际应用

链接列表通常用于文本编辑器中的撤销功能。每个节点代表一个状态,在不同状态之间导航就像遍历列表一样简单。

#

遵循后进先出(LIFO, Last-In-First-Out)原则。它在表达式求值和回溯算法等任务中非常有用。

# 示例

利用栈反转字符串

class Stack<T> {
    private items: T[] = [];

    // Push an item onto the stack
    push(item: T): void {
        this.items.push(item);
    }
    // Pop an item off the stack
    pop(): T | undefined {
        return this.items.pop();
    }
    // Check if the stack is empty
    isEmpty(): boolean {
        return this.items.length === 0;
    }
}
// Usage: Reverse a string
function reverseString(input: string): string {
    const stack = new Stack<string>();
    for (const char of input) {
        stack.push(char);
    }
    let reversed = '';
    while (!stack.isEmpty()) {
        reversed += stack.pop();
    }
    return reversed;
}
console.log(reverseString('TypeScript'));

说明

  • 我们定义了一个 Stack 类,该类包含 pushpop 和检查是否为空的方法;
  • 我们使用栈来反转字符串,方法是将每个字符推送到栈,然后以相反的顺序弹出;

# 最佳实践

在弹出之前始终检查栈是否为空,以避免出现下溢错误。

# 实际应用

栈用于浏览器导航历史记录。每访问一个新页面,就会将其推入栈。按下「返回」按钮,最后访问的页面就会从栈中弹出。

# 队列

队列遵循先进先出(FIFO, First-In-First-Out)原则。队列是调度任务和管理流程秩序的理想选择。

# 示例

模拟打印队列

class Queue<T> {
    private items: T[] = [];

    // Enqueue an item
    enqueue(item: T): void {
        this.items.push(item);
    }
    // Dequeue an item
    dequeue(): T | undefined {
        return this.items.shift();
    }
    // Check if the queue is empty
    isEmpty(): boolean {
        return this.items.length === 0;
    }
}
// Usage: Print queue simulation
const printQueue = new Queue<string>();
printQueue.enqueue('Document1.pdf');
printQueue.enqueue('Document2.pdf');
printQueue.enqueue('Document3.pdf');
while (!printQueue.isEmpty()) {
    const document = printQueue.dequeue();
    console.log(`Printing: ${document}`);
}

说明

  • 我们定义了一个 Queue 类,其中包含了 enqueuedequeue 和检查是否为空的方法;
  • 我们通过 enqueue 文档和 dequeue 文档来模拟打印队列;

# 最佳实践

使用队列管理需要按顺序处理的任务,确保公平处理操作。

# 实际应用

队列广泛应用于客户服务系统,以管理收到的支持票据或电话。 添加的第一张单子最先得到处理。

#

是具有根节点和子节点的分层数据结构。常用于表示组织结构或文件系统等场景。

# 示例

表示简单文件系统

// Define a tree node
class TreeNode<T> {
    constructor(
        public value: T,
        public children: TreeNode<T>[] = []
    ) {}
}

// Function to print the tree
function printTree<T>(node: TreeNode<T>, indent: string = ''): void {
    console.log(indent + node.value);
    for (const child of node.children) {
        printTree(child, indent + '  ');
    }
}
// Usage: File system representation
const root = new TreeNode('root', [
    new TreeNode('home', [
        new TreeNode('user1'),
        new TreeNode('user2'),
    ]),
    new TreeNode('etc', [
        new TreeNode('nginx'),
        new TreeNode('ssh'),
    ]),
    new TreeNode('var', [
        new TreeNode('log'),
        new TreeNode('tmp'),
    ]),
]);


printTree(root);

说明

  • TreeNode 类表示树中的每个节点,包含一个值和一个子节点数组;
  • printTree 函数递归遍历并打印树结构;
  • 示例模拟了一个简单的文件系统层次结构;

# 最佳实践

始终确保树的平衡(如适用),以提高性能,尤其是在搜索或排序算法中使用时

# 实际应用

树用于表示公司的组织结构图。根节点是首席执行官,每个子节点代表直接下属。

#

是一种功能强大的数据结构,用于表示社会关系或交通路线等网络。它们由通过连接的**节点(顶点)**组成。

# 示例

表示社交网络

// Define a graph using an adjacency list
class Graph<T> {
    private adjacencyList: Map<T, T[]> = new Map();

    // Add a vertex
    addVertex(vertex: T): void {
        if (!this.adjacencyList.has(vertex)) {
            this.adjacencyList.set(vertex, []);
        }
    }
    // Add an edge
    addEdge(vertex1: T, vertex2: T): void {
        if (this.adjacencyList.has(vertex1)) {
            this.adjacencyList.get(vertex1)!.push(vertex2);
        }
        if (this.adjacencyList.has(vertex2)) {
            this.adjacencyList.get(vertex2)!.push(vertex1); // Assuming undirected graph
        }
    }
    // Print the graph
    printGraph(): void {
        for (const [vertex, edges] of this.adjacencyList) {
            console.log(`${vertex} -> ${edges.join(', ')}`);
        }
    }
}
// Usage: Social network example
const socialGraph = new Graph<string>();
socialGraph.addVertex('Alice');
socialGraph.addVertex('Bob');
socialGraph.addVertex('Charlie');
socialGraph.addEdge('Alice', 'Bob');
socialGraph.addEdge('Alice', 'Charlie');
socialGraph.addEdge('Bob', 'Charlie');
socialGraph.printGraph();

说明

  • Graph 类使用邻接表来存储顶点及其连接;
  • 我们添加顶点和边来表示社交网络中的关系;
  • 会以可读格式打印出来;

# 最佳实践

根据图的密度在邻接表和矩阵之间做出选择,以获得最佳性能。

# 实际应用

图在 GPS 导航应用程序的路线优化中至关重要。节点代表位置,边代表路线,有助于找到最短或最快的路径。

# 结语

掌握 TypeScript 中的数据结构对于任何开发人员来说都将改变游戏规则。数据结构应用广泛,使我们能够编写高效、简洁和可维护的代码。

请记住,成为编码高手的过程既是理解概念的过程,也是实践的过程。使用这些示例进行实验、修改并创建自己的实现,这样才能真正掌握数据结构之美。

上次更新: 2025/1/14 08:31:52