线性表 —— 数组、栈、队、链表

news2024/11/27 10:28:33

本文以 typescript 实现数据结构,虽说是 ts 实现,但更准确说是面向对象的方式实现,因此可以无缝切换成 Java 等面向对象语言。

什么是数据结构(Data Structure)?

  • “数据结构是ADT(抽象数据类型 Abstract Data Type)的物理实现。” — 《数据结构与算法分析》
  • “数据结构(data structure)是计算机中存储、组织数据的方式。通常情况下,精心选择的数据结构可以 带来最优效率的算法。” —中文维基百科

image.png

线性结构

◼ 线性结构(英語:Linear List)是由n(n≥0)个数据元素(结点)a[0],a[1],a[2]…,a[n-1]组成的有限序列。
◼ 其中:
 数据元素的个数n定义为表的长度 = “list”.length() (“list”.length() = 0(表里没有一个元素)时称为空表)。
 将非空的线性表(n>=1)记作:(a[0],a[1],a[2],…,a[n-1])。
 数据元素a[i](0≤i≤n-1)只是个抽象符号,其具体含义在不同情况下可以不同。
◼ 上面是维基百科对于线性结构的定义,有一点点抽象,其实我们只需要记住几个常见的线性结构即可

image.png

数组

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

概念

◼ 栈也是一种 非常常见 的数据结构, 并且在程序中的 应用非常广泛。
◼ 数组
 我们知道数组是一种线性结构, 并且可以在数组的 任意位置 插入和删除数据。
 但是有时候, 我们为了实现某些功能, 必须对这种任意性 加以 限制。
 而 栈和队列 就是比较常见的 受限的线性结构, 我们先来学习栈结构。

可以看到 栈 就是受限的线性结构,重点就是受限。它就像数组的“子集”一样。
因此不是严肃使用栈的场景,我们完全可以把数组来模拟当栈使用,比如刷题的时候。只是注意要自我限制,只用栈有的那几个方法,别突然用下标访问数组数据,那就已经不是栈了。

image.png

数组和链表都能实现。
js 中没有自带的链表结构,但 Java 中有 LinkList,它底层就是链表。ArrayList 是数组。

常见方法

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

实现

/**
 * 栈接口。后续想要实现栈,就 implement 这个接口。
 * @example
 * class ArrayStack<T> implements Stack<T>
 */
export interface Stack<T> {
    push(value: T): void;
    pop(): T | undefined;
    peek(): T | undefined;
    size(): number;
    isEmpty(): boolean;
}

import { Stack } from './Stack';

/*
 * 基于数组的栈
 */
export class ArrayStack<T> implements Stack<T> {
    private data: T[];

    constructor() {
        this.data = [];
    }

    push(element: T) {
        this.data.push(element);
    }

    pop() {
        return this.data.pop();
    }

    peek() {
        return this.data[this.data.length - 1];
    }

    isEmpty() {
        return this.data.length === 0;
    }

    size() {
        return this.data.length;
    }
}

题目

进制转换

// 十进制转二级制

import { ArrayStack } from '../ArrayStack';

/**
 * 首先 js 自带了进制转换的方法:Number(10).toString(2); 1010
 * @param decimal 10进制
 * @returns
 */
export function decimalToBinary(decimal: number) {
    return decimal.toString(2);
}

// 示例
// console.log(decimalToBinary(10)); // 输出:1010

/**
 * 栈实现
 * @param decimal 10进制
 * @param target 目标进制
 */
export function decimalToBinaryByStack(decimal: number, target: number = 2) {
    const stack = new ArrayStack();
    let remainder: number;

    while (decimal > 0) {
        remainder = decimal % target;
        stack.push(remainder);
        decimal = (decimal - remainder) / target;
    }
    let res = "";
    while (!stack.isEmpty()) {
        res += stack.pop();
    }

    return res;
}

有效的括号

  • https://leetcode.cn/problems/valid-parentheses/
/**
 * 思路:
 * 括号分成左右两部分
 * 遍历字符串只要碰到左部分,就把对应的右括号入栈。这样入栈,栈顶元素一定是最先成对的括号。入栈另一半只是为了更好的比较。
 * 这样只要遍历碰到右部分,就立即与栈顶比较,看是否一致,一致就出栈,表示一对已经配对完毕。
 */

import { ArrayStack } from '../ArrayStack';

export function isValid(s: string) {
    const stack = new ArrayStack<string>();
    const map = {
        '(': () => stack.push(')'),
        '[': () => stack.push(']'),
        '{': () => stack.push('}')
    };
    for (let i = 0; i < s.length; i++) {
        const fn = map[s[i]];
        if (fn) {
            fn();
        } else if (s[i] !== stack.peek()) {
            return false;
        } else {
            stack.pop();
        }
    }

    return stack.isEmpty();
}

概念

队列也是一种受限的线性结构。

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

image.png

数组和链表同样都能实现队列,但是链表效率更高。
因为队列涉及到首尾元素的删除。栈删除元素,我们可以让数组最后一个位置为栈顶,这样就不用移动所有元素,所以用数组和链表区别不大。但队列不行,如果用数组一定会移动所有元素。

常见方法

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

实现

数组实现

export interface Queue<T> {
    enqueue(element: T): void;
    dequeue(): T | undefined;
    peek(): T | undefined;
    isEmpty(): boolean;
    // 设为 getter,使用的时候就可以把 size 当属性使用(queue.size),而不是方法了(queue.size())
    get size(): number;
}

import { Queue } from './Queue';

export class ArrayQueue<T> implements Queue<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;
    }
}

队列和栈同为线性结构,它们都有相同的方法,如表长度,获取表元素 peek,表是否为空。
因此可以进一步抽象出接口。

export interface List<T> {
    get size(): number;
    isEmpty(): boolean;
    peek(): T | undefined;
}

import { List } from "../list/List";

export interface Queue<T> extends List<T> {
    enqueue(element: T): void;
    dequeue(): T | undefined;
}

链表实现

题目

击鼓传花 / 烫手山芋(hotPotato)

  • https://leetcode.cn/problems/yuan-quan-zhong-zui-hou-sheng-xia-de-shu-zi-lcof

◼ 原游戏规则:
 班级中玩一个游戏,所有学生围成一圈,从某位同学手里开始向旁边的同学传一束花。
 这个时候某个人(比如班长),在击鼓,鼓声停下的一颗,花落在谁手里,谁就出来表演节目。
◼ 修改游戏规则:
 我们来修改一下这个游戏规则。
 几个朋友一起玩一个游戏,围成一圈,开始数数,数到某个数字的人自动淘汰。
 最后剩下的这个人会获得胜利,请问最后剩下的是原来在哪一个位置上的人?
◼ 封装一个基于队列的函数:
 参数:所有参与人的姓名,基于的数字;
 结果:最终剩下的一人的姓名;

测试数据:["John", "Jack", "Camila", "Ingrid", "Carl"]

这种测试数据要循环使用,而不是遍历一遍就够了的问题,是否就是适合队列来解决呢?

/**
 * 思路:
 * 元素全部进队后,依次出队,并计数,计数不是目标数字的就从队尾重新进队,是目标数字就不再进队
 * 一直重复出队进队,直到队里只剩下一个人,这个人就是幸存者
 */

import { ArrayQueue } from "../ArrayQueue";

export function hotPotato(elements: string[], num: number): number {
    const queue = new ArrayQueue<string>();
    elements.forEach(element => queue.enqueue(element));
    let count = 0;

    while (queue.size > 1) {
        const el = queue.dequeue()!;
        count++;
        if (count === num) {
            count = 0;
        } else {
            queue.enqueue(el);
        }
    }
    const res = queue.dequeue();
    return elements.findIndex(item => item === res);
}

约瑟夫环问题

历史:

◼ 阿桥问题(有时也称为约瑟夫斯置换),是一个出现在计算机科学和数学中的问题。在计算机编程的算法中,类似问题又称为约瑟夫环。
 人们站在一个等待被处决的圈子里。
 计数从圆圈中的指定点开始,并沿指定方向围绕圆圈进行。
 在跳过指定数量的人之后,处刑下一个人。
 对剩下的人重复该过程,从下一个人开始,朝同一方向跳过相同数量的人,直到只剩下一个人,并被释放。
 在给定数量的情况下,站在第几个位置可以避免被处决?
◼ 这个问题是以弗拉维奥·约瑟夫命名的,他是1世纪的一名犹太历史学家。
 他在自己的日记中写道,他和他的40个战友被罗马军队包围在洞中。
 他们讨论是自杀还是被俘,最终决定自杀,并以抽签的方式决定谁杀掉谁。

约瑟夫环问题其实就是击鼓传花问题,它还有很多名字。

  • 剑指offer 62题:圆圈中的最后剩下的数字
  • LeetCode:LCR 187. 破冰游戏

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1433052.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

一文学会Axios的使用

异步请求 同步发送请求过程如下 浏览器页面在发送请求给服务器&#xff0c;在服务器处理请求的过程中&#xff0c;浏览器页面不能做其他的操作。只能等到服务器响应结束后才能&#xff0c;浏览器页面才能继续做其他的操作。 异步发送请求过程如下浏览器页面发送请求给服务器&…

蓝桥杯第八届省赛题笔记------基于单片机的电子钟程序设计与调试

题目要求&#xff1a; 一、基本要求 1.1 使用 CT107D 单片机竞赛板&#xff0c;完成“电子钟”功能的程序设计与调试&#xff1b; 1.2 设计与调试过程中&#xff0c;可参考组委会提供的“资源数据包”&#xff1b; 1.3 Keil 工程文件以准考证号命名&#xff0c;保存在…

032-安全开发-JavaEE应用Servlet路由技术JDBCMybatis数据库生命周期

032-安全开发-JavaEE应用&Servlet路由技术&JDBC&Mybatis数据库&生命周期 #知识点&#xff1a; 1、JavaEE-HTTP-Servlet技术 2、JavaEE-数据库-JDBC&Mybatis 演示案例&#xff1a; ➢JavaEE-HTTP-Servlet&路由&周期 ➢JavaEE-数据库-JDBC&Mybat…

MiniCPM:揭示端侧大语言模型的无限潜力

技术博客链接&#xff1a; &#x1f517;https://shengdinghu.notion.site/MiniCPM ➤ Github地址&#xff1a; &#x1f517;https://github.com/OpenBMB/MiniCPM ➤ Hugging Face地址&#xff1a; &#x1f517;https://huggingface.co/openbmb/MiniCPM-2B-sft-bf16 1 …

目标检测及相关算法介绍

文章目录 目标检测介绍目标检测算法分类目标检测算法模型组成经典目标检测论文 目标检测介绍 目标检测是计算机视觉领域中的一项重要任务&#xff0c;旨在识别图像或视频中的特定对象的位置并将其与不同类别中的对象进行分类。与图像分类任务不同&#xff0c;目标检测不仅需要…

工信部颁发的《计算机视觉处理设计开发工程师》中级证书

计算机视觉&#xff08;Computer Vision&#xff09;是一门研究如何让计算机能够理解和分析数字图像或视频的学科。简单来说&#xff0c;计算机视觉的目标是让计算机能够像人类一样对视觉信息进行处理和理解。为实现这个目标&#xff0c;计算机视觉结合了图像处理、机器学习、模…

RabbitMQ——基于 KeepAlived + HAProxy 搭建 RabbitMQ 高可用负载均衡集群

一、集群简介 1.1 集 群架构 当单台 RabbitMQ 服务器的处理消息的能力达到瓶颈时&#xff0c;此时可以通过 RabbitMQ 集群来进行扩展&#xff0c;从而达到提升吞吐量的目的。 RabbitMQ 集群是一个或多个节点的逻辑分组&#xff0c;集群中的每个节点都是对等的&#xff0c;每…

职业性格测试在求职应聘跳槽中的应用

人的性格总是千奇百怪&#xff0c;有的人总是想迎接挑战&#xff0c;超越自己&#xff0c;不停的奔着高处走&#xff0c;然而有的人总是喜欢随遇而安&#xff0c;踏踏实实一辈子&#xff0c;有份安稳的工作&#xff0c;有吃有喝就好。那么对于哪些喜欢迎接挑战&#xff0c;但又…

Adobe Camera Raw for Mac v16.1.0中文激活版

Adobe Camera Raw for Mac是一款强大的RAW格式图像编辑工具&#xff0c;它能够处理和编辑来自各种数码相机的原始图像。以下是关于Adobe Camera Raw for Mac的一些主要特点和功能&#xff1a; 软件下载&#xff1a;Adobe Camera Raw for Mac v16.1.0中文激活版 RAW格式支持&…

MyBatis一些常见知识点!

什么是 ORM 框架&#xff1f; MyBatis 有哪些优缺点&#xff1f; 典型回答&#xff1a; ORM&#xff08;Object-Relational Mapping&#xff0c;对象关系映射&#xff09;框架是一种将关系型数据库中的数据 与 应用程序中的对象进行映射的技术。它通过在程序代码中定义的类和属…

Unity类银河恶魔城学习记录1-12 PlayerComboAttack源代码 P39

Alex教程每一P的教程原代码加上我自己的理解初步理解写的注释&#xff0c;可供学习Alex教程的人参考 此代码仅为较上一P有所改变的代码 【Unity教程】从0编程制作类银河恶魔城游戏_哔哩哔哩_bilibili PlayerPrimaryAttack.cs using System.Collections; using System.Collect…

七月论文审稿GPT第2.5版:微调GPT3.5 turbo 16K和llama2 13B以扩大对GPT4的优势

前言 我司自去年7月份成立大模型项目团队以来&#xff0c;至今已有5个项目组&#xff0c;其中 第一个项目组的AIGC模特生成系统已经上线在七月官网第二项目组的论文审稿GPT则将在今年3 4月份对外上线发布第三项目组的RAG知识库问答第1版则在春节之前已就绪至于第四、第五项目…

LLaVA:GPT-4V(ision) 的新开源替代品

LLaVA&#xff1a;GPT-4V(ision) 的新开源替代品。 LLaVA &#xff08;https://llava-vl.github.io/&#xff0c;是 Large Language 和Visual A ssistant的缩写&#xff09;。它是一种很有前景的开源生成式 AI 模型&#xff0c;它复制了 OpenAI GPT-4 在与图像对话方面的一些功…

(5)【Python/机器学习/深度学习】Machine-Learning模型与算法应用—12种聚类算法说明与归纳

目录 一、12种聚类(无监督学习)算法说明和区分比较 聚类算法的类型(一) ​编辑导入函数库 加载数据集 ​编辑 (1)K-Means --Centroid models (2)Mini-Batch K-Means -- Centroid models (3)AffinityPropagation (Hierarchical) -- Connectivity models (4)Mean Shift…

最新GPT4.0使用教程,AI绘画,GPT语音对话使用,DALL-E3文生图

一、前言 ChatGPT3.5、GPT4.0、GPT语音对话、Midjourney绘画&#xff0c;文档对话总结DALL-E3文生图&#xff0c;相信对大家应该不感到陌生吧&#xff1f;简单来说&#xff0c;GPT-4技术比之前的GPT-3.5相对来说更加智能&#xff0c;会根据用户的要求生成多种内容甚至也可以和…

算法练习-三数之和(思路+流程图+代码)

难度参考 难度&#xff1a;中等 分类&#xff1a;数组 难度与分类由我所参与的培训课程提供&#xff0c;但需要注意的是&#xff0c;难度与分类仅供参考。且所在课程未提供测试平台&#xff0c;故实现代码主要为自行测试的那种&#xff0c;以下内容均为个人笔记&#xff0c;旨在…

openGauss学习笔记-214 openGauss 性能调优-确定性能调优范围

文章目录 openGauss学习笔记-214 openGauss 性能调优-确定性能调优范围214.1 性能因素214.2 调优范围确定 openGauss学习笔记-214 openGauss 性能调优-确定性能调优范围 数据库性能调优通常发生在用户对业务的执行效率不满意&#xff0c;期望通过调优加快业务执行的情况下。正…

机器学习的整个流程

机器学习的整个流程定义了数据科学团队执行以创建和交付机器学习模型的工作流。此外&#xff0c;机器学习流程还定义了团队如何协作合作&#xff0c;以创建最有用的预测模型。 机器学习high level的流程 机器学习流程的关键步骤包括问题探索&#xff08;Problem Exploration&a…

《dx12 龙书》第四部分学习笔记——预备知识(下)

7、多重采样技术的原理 由于屏幕中显示的像素不可能是无穷小的&#xff0c;所以并不是任意一条直线都能在显示器上“平滑”而完美地呈现出来。即为以像素矩阵 &#xff08;matrix of pixels&#xff0c; 可以理解为“像素2D数组”&#xff09;逼近直线的方法所产生的“阶梯” &…

C# 使用 MailKit 接收邮件(附demo)

C# 使用 MailKit 接收邮件&#xff08;附demo&#xff09; 介绍安装包&#xff08;依赖&#xff09;案例简单代码 获取附件核心代码完整代码 介绍一下POP3 介绍 MailKit 是一个开源的 C# 邮件处理库&#xff0c;用于在应用程序中发送和接收电子邮件。它提供了一个强大且易于使…