【算法】Java-使用数组模拟单向链表,双向链表

news2024/12/26 21:55:57

目录

试题1:实现一个单链表,并实现以下功能:

试题2:实现一个双链表,并实现以下功能

思路总结:

什么情况下可能涉及到用数组实现链表呢?


      在学习时了解到了可以用数组模拟链表,使其兼顾数据查找快,链表新增和删除快的缺点,找来一些试题实现了下,如下:

试题1:实现一个单链表,并实现以下功能:

Java代码实现:

import org.apache.commons.lang3.StringUtils;

import java.util.Scanner;

public class ArrayLinkedList {
    public static final int N = 100000;

    private int head; // head
    private int idx; // 存储新元素的索引下标
    private int[] e; // 存放数据的数组;
    private int[] ne; // 当前节点的下一个节点的地址(数组下标)。比如使用头插法,单向链表,e[5] = 5,e[5]的下一个节点的坐标是ne[5],下一个节点是e[ne[5]]

    public ArrayLinkedList() {
        e = new int[N];
        ne = new int[N];
        head = -1;
        idx = 0;
    }

    public void insertToHead(int val) {
        e[idx] = val;
        ne[idx] = head; // head的值是头结点指向的下一个元素的下标值
        head = idx++;
    }

    /**
     * 将val插入到索引k后面
     *
     * @param k
     * @param val
     */
    public void insert(int k, int val) {
        e[idx] = val;
        ne[idx] = ne[k];
        ne[k] = idx++;
    }

    /**
     * 删除k节点后面的节点(只删一个)
     *
     * @param k
     */
    public void remove(int k) {
        if (k + 1 == 0) {
            head = ne[head];
        } else {
            ne[k] = ne[ne[k]];
        }

    }

    public static void main(String[] args) {
        System.out.println("请输入:");
        Scanner scanner = new Scanner(System.in);
        ArrayLinkedList arrayLinkedList = new ArrayLinkedList();
        int head = arrayLinkedList.head;
        int[] e = arrayLinkedList.e;
        int[] ne = arrayLinkedList.ne;
        while (scanner.hasNextLine()) {
            String inputString = scanner.nextLine();
            if (StringUtils.isBlank(inputString)) {
                break;
            }
            String[] inputArr = inputString.split(" ");
            String f = inputArr[0];
            int s = Integer.valueOf(inputArr[1]);
            switch (f) {
                case "H":
                    arrayLinkedList.insertToHead(s);
                    break;
                case "D":
                    arrayLinkedList.remove(s - 1); // 第k个插入的数,idx是k-1
                    break;
                case "I":
                    int val = Integer.valueOf(inputArr[2]);
                    arrayLinkedList.insert(s - 1, val);
                    break;
            }
        }

        for (int i = head; i != -1; i = ne[i]) {
            System.out.print(e[i] + " ");
        }
        scanner.close();
    }
}

试题2:实现一个双链表,并实现以下功能

Java代码实现:

import org.apache.commons.lang3.StringUtils;

import java.util.Scanner;

/**
 * 支持的操作:
 * 1、在最左侧插入一个数;
 * 2、在最右侧插入一个数;
 * 3、将第k个插入的数删除;
 * 4、在第k个插入的数左侧插入一个数;
 * 5、在第k个插入的数右侧插入一个数
 */
public class TwoWayLinkedList2 {
    public static final int N = 100000;
    // 存放数组的数据
    private int[] e;
    // 存放左指针下标数组
    private int[] l;
    // 存放右指针地址数组
    private int[] r;
    // 数组待存储下标
    private int idx;
    // e[0]表示链表头;e[1]表示链表尾
    // 向左侧插入,就是插入到上一个节点的右侧。所以插入的逻辑都抽象成插入到具体节点的右侧

    // 构造方法
    public TwoWayLinkedList2() {
        e = new int[N];
        r = new int[N];
        l = new int[N];
        r[0] = 1;
        l[1] = 0;
        idx = 2;

    }

    /**
     * 将节点插入到e[k]节点右侧
     *
     * @param k
     * @param val
     */
    public void add(int k, int val) {
        e[idx] = val;
        l[idx] = k;
        r[idx] = r[k];
        l[r[k]] = idx;
        r[k] = idx;
        idx++;
    }

    /**
     * 删除e[k]
     *
     * @param k
     */
    public void remove(int k) {
        r[l[k]] = r[k];
        l[r[k]] = l[k];
    }

    public static void main(String[] args) {
        System.out.println("请输入:");
        Scanner scanner = new Scanner(System.in);
        TwoWayLinkedList2 linkedList = new TwoWayLinkedList2();
        int[] e = linkedList.e;
        int[] l = linkedList.l;
        int[] r = linkedList.r;
        while (scanner.hasNextLine()) {
            String inputString = scanner.nextLine();
            if (StringUtils.isBlank(inputString)) {
                break;
            }
            String[] inputArr = inputString.split(" ");
            String f = inputArr[0];
            Integer s = Integer.valueOf(inputArr[1]);

            switch (f) {
                case "L":
                    linkedList.add(0, s);
                    break;
                case "R":
                    linkedList.add(l[1], s);
                    break;
                case "D":
                    linkedList.remove(s + 1); // 第k个插入的数,idx是k+1,因为我们用了0和1表示链表头和链表尾
                    break;
                case "IL":
                    linkedList.add(l[s + 1], Integer.valueOf(inputArr[2]));
                    break;
                case "IR":
                    linkedList.add(s + 1, Integer.valueOf(inputArr[2]));
                    break;
            }
        }

        // 遍历输出
        for (int i = r[0]; i != 1; i = r[i]) {
            System.out.printf("" + e[i]);
        }
        scanner.close();
    }
}

思路总结:

       数组实现链表,一个数组用于存放数据,一个数组存放"指针",这里的指针用数组下标代替。如果是双向链表,要用两个数组存放指针。同时要注意首节点和尾结点的记录方法。在实现双链表时,我曾用两个变量表示首尾节点,对比起来,没有用e[0],e[1]表示简洁,而且非常容易搞混。占用第0位和第1位保存链表头和尾时要注意初始的idx=2,第k个插入的元素的索引下标是k+1。大家可以使用更多方法实现,过程虽然曲折,但一顿操作下来,对链表的操作会非常的熟练。

什么情况下可能涉及到用数组实现链表呢?

        在没有操作系统和内存管理的情况下。

1. 链表的实现需要动态内存分配和释放,这需要操作系统提供的堆内存管理。没有 OS 的动态内存管理,就无法真正实现链表节点的创建和销毁。

2. 链表通过指针链接节点,需要操作系统提供的指针和地址引用机制。没有 OS,就无法真正用指针建立节点之间的链接关系。

3. 数组可以预先分配一块内存,这个内存块可以视为堆内存,用下标代替指针,通过数组操作就可以模拟出指针操作。

4. 数组是一块连续的内存,空间固定,不需要动态扩展,所以定义数组后直接就可以使用,不依赖动态内存管理。

5. 数组中的每个元素是连续存储的,通过下标可以直接访问,不需要指针来进行寻址。可以模拟指针的移动,改变指针的指向来实现链表的操作。

        这里我们从算法分析和学习的角度来看这个问题,但不适合实际项目,只能处理规模较小的数据。要实现一个真正的可扩展链表,还需在操作系统上进行动态内存管理。

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

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

相关文章

【LInux编译器gcc/g++】gcc使用方法和动静态库相关概念

目录 一.前言 二.源代码的翻译环境 三.gcc相关指令 四.动静态库 1.什么是库? 2.库的命名 3.库的链接方式 4.动静态链接的优缺点 5.小结 一.前言 在Windows系统上我们常用VisualStudio来进行C/C开发,VS并不是一款单一的软件,而是集成…

UI设计模式是什么?有哪些常用的?

UI设计模式是针对常见的用户界面问题提出的解决方案。当解决方案得到验证时,它经常被使用,最终演变成可重复使用的设计模式。 设计师可以根据不同的网站功能类型选择使用相应的网站UI设计模式,从而创建一致高效的网站UI界面。 本文推荐8种常…

选择排序——直接选择排序

直接选择排序:(以重复选择的思想为基础进行排序) 1、简述 顾名思义就是选出一个数,再去抉择放哪里去。 设记录R1,R2…,Rn,对i1,2,…,n-1,重复下…

【MMDetection】bug记录

bug1: if env_cfg.get(cudnn_benchmark): AttributeError: NoneType object has no attribute get 解决办法: 配置文件缺少了env_cfg信息,需要在config文件中添加,如以下内容: env_cfg dict(cudnn_benchmarkFalse…

Java 使用 EMQX 实现物联网 MQTT 通信

一、介绍 1、MQTT MQTT(Message Queuing Telemetry Transport, 消息队列遥测传输协议),是一种基于发布/订阅(publish/subscribe)模式的"轻量级"通讯协议,该协议构建于TCP/IP协议上,由IBM在1999年发布。MQTT最大优点在于&#xff…

Netty核心原理:一、基础入门-01:初入JavaIO之门BIO、NIO、AIO实战练习

文章目录 一、前言介绍1.1 BIO:同步阻塞I/O模式1.2 NIO:同步非阻塞I/O模式1.3 AIO:异步非阻塞I/O模式 二、代码实现2.1 工程结构2.2 BIO:同步阻塞I/O实现2.2.1 BIO处理器2.2.2 BIO适配器2.2.3 BIO客户端处理器2.2.4 BIO客户端2.2.…

计算机网络第五章——传输层(上)

早知如此绊人心,何如当初莫相识 文章目录 前言 前言 虽然说是手机和手机之间的通信但是其实是手机之间的进程和进程之间的通信,所以这一章主要是研究进程之间通信的问题,在计算机网络中有一个重要的问题,在进行数据通信和资源共享…

【分享】golang windows 运行报错 undefined: syscall.SIGUSR1

在跟着煎鱼大佬学习 Golang-gin的时候,"在优雅的重启服务篇" ,为了gin服务的热更新,采用了 endlessfresh的方案,安装endless后无法在windows本地调试,然后报错。 (优雅的重启服务-地鼠文档优雅的重启服务-我不怎么喜欢左写写&#…

linux内核模块编译方法之模块编程详解

文章目录 一、模块传参二、模块依赖三、内核空间和用户空间四、执行流五、模块编程与应用编程的比较六、内核接口头文件查询总结 本期和大家主要分享的是驱动开发内核编译过程中对于模块是如何设计的,进行了详细的分享,从模块传参、模块依赖一直到内核空…

楼顶空地适合建造气膜体育馆吗?

众所周知,传统建筑的荷载太大,出于安全考虑,是不适合继续在楼顶加盖传统结构体育馆的,但是,气膜体育馆作为一种装配式建筑,它是可以在城市高空上建造一个轻盈又新颖独特的全天候气膜馆。 气膜体育馆作为一种…

小黑自己在家尝试涮牛排,肚子又开始了新一轮的胀气,喝到了酱香拿铁并烫了纹理发型体验一把的leetcode之旅:123. 买卖股票的最佳时机 III

动态规划1 class Solution:def maxProfit(self, prices: List[int]) -> int:# 数组长度n len(prices)if n < 2:return 0# 动态规划变量# 第一次买的价格first_price prices[0]# 第一次卖的收益first_cell 0# 第二次买的价格second_price prices[0]# 第二次卖second_…

STM32H750 HAL CUBEMX 时钟失败及死机无法下载问题解决

芯片采样电压设置&#xff0c;否则 无法运行 解决死机问题 设置swd 模式 短接 boot0 —vcc 3.3v即可正常下载

驱动开发,stm32mp157a开发板的led灯控制实验

1.实验目的 编写LED灯的驱动&#xff0c;在应用程序中编写控制LED灯亮灭的代码逻辑实现LED灯功能的控制&#xff1b; 2.LED灯相关寄存器分析 LED1->PE10 LED1亮灭&#xff1a; RCC寄存器[4]->1 0X50000A28 GPIOE_MODER[21:20]->01 (输出) 0X50006000 GPIOE_ODR[10]-&g…

5. HBase必知必会之理论进阶篇

HBase必知必会之理论进阶篇 1.1 集群搭建以及规模预测1.1.1 HBase集群搭建1.1.2 HBase集群规划 1.2 HBase重要的概念1.2.1 snapshot1.2.2 region 切分1.2.3 RIT1.2.4 HBase读优化1.2.4.1 客户端优化1.2.4.2 服务端优化1.2.4.3 hdfs 优化 1.2.5 HBase写优化1.2.5.1 客户端优化1.…

Linux centos7 bash编程训练__打印各类形状

利用for循环&#xff0c;打印各种不同的三角形、矩形和菱形。 主要是fort循环嵌套使用&#xff0c;及条件判断等。 因方法简单&#xff0c;不作更多解释&#xff0c;部分注释可以帮助初学者掌握代码。 下面列出代码&#xff0c;供参考。 #! /bin/bash ## 打印输出各种*型形…

中企出海,用火山引擎DataTester开启增长第一步

更多技术交流、求职机会&#xff0c;欢迎关注字节跳动数据平台微信公众号&#xff0c;回复【1】进入官方交流群 今年 Google 宣布其提供的A/B测试工具 Optimize 将在2023年9月30号停止服务。在全球化浪潮席卷下&#xff0c;越来越多的中国企业正在加速走向全球市场&#xff0c;…

使用 Webpack 从 0 到 1 构建 Vue3 项目 + ts

使用 Webpack 从 0 到 1 构建 Vue3 项目 1.初始化项目结构2.安装 webpack&#xff0c;补充智能提示3.初步编写 webpack.config.js3.1设置入口文件及出口文件3.2 指定 html 模板位置 4.配置 运行/打包 命令&#xff0c;首次打包项目5.添加 Vue 及相关配置5.1安装并引入 vue5.2 补…

一个详细且完整的公司局域网搭建案例,跟着操作!

局域网(Local Area Network&#xff0c;简称LAN)&#xff0c;用于将有限范围内&#xff08;例如一个实验室、一层办公楼或者校园&#xff09;的各种计算机、终端与外部设备互联成网。公司局域网怎么建立&#xff1f;首先来了解下不同规模企业网络组建方式。 10人以下企业网络组…

固定资产管理表怎么填写

在现代企业管理中&#xff0c;固定资产的管理是至关重要的环节。它不仅关系到企业运营的效率&#xff0c;也直接影响到企业的财务状况。因此&#xff0c;正确、有效地填写和管理固定资产管理表显得尤为重要。并提供一些创新的方法来优化这一过程。  让我们理解什么是固定资产…

Win10 cmd默认使用管理员身份运行的修改

一、在开始菜单搜索cmd&#xff0c;打开快捷方式文件位置 二、鼠标右键快捷方式&#xff0c;打开属性 三、选择高级&#xff0c;再勾选用管理员身份运行&#xff0c;点击确定即可