Java数据结构——线性表Ⅱ

一、链式存储结构概述

1. 基本概念(逻辑分析)

核心思想:用指针将离散的存储单元串联成逻辑上连续的线性表

设计动机:解决顺序表 "预先分配空间" 与 "动态扩展" 的矛盾

关键特性

  1. 结点空间动态分配,适应数据量动态变化
  2. 插入删除仅需修改指针,无需移动大量元素
  3. 存储单元离散,不要求连续内存空间

二、单链表(Single Linked List)

1. 结点与链表类定义(设计思路)

(1)LinkNode 结点类设计
  • 数据域 data:存储元素值,采用泛型 E 实现类型通用化
  • 指针域 next:指向下一结点,形成单向链表链
  • 构造方法重载:提供无参(初始空结点)和有参(初始化数据)两种构造方式
(2)LinkListClass 链表类设计
  • 头结点 head:哑结点设计,避免处理空表时的特殊情况
  • 空表初始化:头结点的 next 指针置 null,形成 "头结点 + 空数据区" 的初始结构
 
// 单链表结点泛型类(逻辑:每个结点保存数据和后继指针)class LinkNode<E> {E data; // 数据域,存储元素值LinkNode<E> next; // 指针域,指向下一结点public LinkNode() {next = null; // 无参构造:初始时不指向任何结点}public LinkNode(E d) {data = d; // 有参构造:初始化数据域next = null;}}// 单链表泛型类(逻辑:通过头结点管理整个链表)public class LinkListClass<E> {LinkNode<E> head; // 头结点,不存储实际数据public LinkListClass() {head = new LinkNode<E>(); // 创建头结点head.next = null; // 初始时头结点不指向任何数据结点}// 基本运算算法...}

2. 核心操作实现(逻辑解析)

(1)插入操作(在 p 结点后插入 s 结点)
  • 核心难点:如何在不丢失原指针的前提下完成插入
  • 操作步骤
  1. 先让新结点 s 指向 p 的原后继(避免指针丢失)
  2. 再让 p 指向新结点 s(完成链表连接)
  • 安全原则:始终遵循 "先连接新结点后继,再连接原结点后继" 的顺序
 
// 插入逻辑示意图(思路:两步指针修改完成插入)s.next = p.next; // 步骤1:新结点先指向原后继,防止指针丢失p.next = s; // 步骤2:原结点指向新结点,完成插入
(2)删除操作(删除 p 结点的后继)
  • 核心思路:通过修改 p 的 next 指针,直接跳过待删除结点
  • 内存管理:Java 自动垃圾回收,无需手动释放结点内存
 
p.next = p.next.next; // 思路:让p直接指向原后继的后继,跳过待删除结点
(3)头插法建表(CreateListF)
  • 算法思想
  1. 从空表开始,逐个处理数组元素
  2. 每个新结点始终插入到表头(头结点之后)
  3. 最终链表顺序与数组顺序相反
  • 适用场景:需要逆序构建链表的场景
public void CreateListF(E[] a) {LinkNode<E> s;for (int i = 0; i < a.length; i++) {s = new LinkNode<E>(a[i]);s.next = head.next; // 新结点先指向原表头,保持链表连续性head.next = s; // 头结点指向新结点,完成表头插入}}
(4)尾插法建表(CreateListR)
  • 算法思想
  1. 用尾指针 t 跟踪链表尾部
  2. 新结点始终插入到 t 之后
  3. 每次插入后更新 t 为新的尾结点
  • 关键优化:避免头插法的 O (n) 查找尾结点操作,时间复杂度优化为 O (1)
public void CreateListR(E[] a) {LinkNode<E> s, t;t = head; // t初始指向头结点,作为初始尾结点for (int i = 0; i < a.length; i++) {s = new LinkNode<E>(a[i]);t.next = s; // 尾结点指向新结点t = s; // 尾指针更新为新结点}t.next = null; // 最后一个结点的next置null,标识链表尾部}

3. 基本运算实现(逻辑拆解)

(1)获取指定序号的结点(geti)
  • 算法思路
  1. 从 head 开始遍历
  2. 用计数器 j 记录当前遍历到的序号
  3. 当 j 等于目标序号 i 时返回对应结点
  • 边界处理:i=-1 时返回头结点(用于插入操作的前驱查找)
private LinkNode<E> geti(int i) {LinkNode<E> p = head; // 从头结点开始遍历int j = -1; // j=-1对应头结点,j=0对应首数据结点while (j < i) {j++;p = p.next; // 指针后移}return p; // 返回序号为i的结点}
(2)添加元素到表尾(Add)
  • 算法步骤
  1. 新建结点 s 存储元素 e
  2. 查找当前尾结点(p.next==null)
  3. 将尾结点的 next 指向 s
  • 时间复杂度:O (n),需遍历链表查找尾结点
public void Add(E e) {LinkNode<E> s = new LinkNode<E>(e);LinkNode<E> p = head;while (p.next != null) {p = p.next; // 循环找到尾结点}p.next = s; // 尾结点指向新结点}
(3)求表长度(size)
  • 计数思路
  1. 从 head 开始遍历
  2. 每次遇到非空 next 时计数器 + 1
  3. 直到遇到 null 指针(链表尾部)
  • 优化方向:若维护 size 变量,可将时间复杂度降为 O (1)
public int size() {LinkNode<E> p = head;int cnt = 0;while (p.next != null) {cnt++;p = p.next; // 指针后移并计数}return cnt;}

4. 应用算法示例(问题解决思路)

(1)查找中间位置元素(两种算法对比)

计数法(Middle1)

  • 问题分析:已知链表长度 n,中间位置为:
  • 奇数长度:(n-1)/2+1(如 n=3,位置 2
  • 偶数长度:(n-1)/2+1(如 n=4,位置 2)
  • 本质:通过数学计算减少遍历次数
  • 算法步骤
  1. 计算长度 n
  2. 遍历 (n-1)/2 次到达中间结点
public static int Middle1(LinkListClass<Integer> L) {int j = 1, n = L.size();LinkNode<Integer> p = L.head.next; // p指向首结点(序号1)while (j <= (n - 1) / 2) { // 需遍历(n-1)/2次j++;p = p.next;}return p.data;}

快慢指针法(Middle2)

  • 核心思想
  • 快指针每次走 2 步,慢指针每次走 1 步
  • 当快指针到达末尾时,慢指针恰好到达中间
  • 时间优化:只需 O (n) 时间,无需两次遍历
public static int Middle2(LinkListClass<Integer> L) {LinkNode<Integer> slow = L.head.next; // 慢指针LinkNode<Integer> fast = L.head.next; // 快指针while (fast.next != null && fast.next.next != null) {slow = slow.next; // 慢指针走1步fast = fast.next.next; // 快指针走2步}return slow.data; // 快指针无法再走2步时,慢指针指向中间}
(2)逆置链表(Reverse)
  • 算法思路
  1. 将原链表置为空表(head.next=null)
  2. 逐个取出原链表结点
  3. 每次将结点插入到新链表的表头
  • 关键技巧:使用临时变量 q 保存后继结点,防止指针丢失
public static void Reverse(LinkListClass<Integer> L) {LinkNode<Integer> p = L.head.next, q; // p指向首结点L.head.next = null; // 清空原链表(仅保留头结点)while (p != null) {q = p.next; // 保存p的后继结点,防止断链p.next = L.head.next; // 插入到表头(头结点之后)L.head.next = p;p = q; // p指向下一个待处理结点}}

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

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

    相关文章

    技术基石:SpreadJS 引擎赋能极致体验

    在能源行业数字化转型的浪潮中&#xff0c;青岛国瑞信息技术有限公司始终以技术创新为核心驱动力&#xff0c;不断探索前沿技术在能源领域的深度应用。其推出的 RCV 行列视生产数据应用系统之所以能够在行业内脱颖而出&#xff0c;离不开背后强大的技术基石 ——SpreadJS 引擎。…

    Typora - Typora 打字机模式

    Typora 打字机模式 1、基本介绍 Typora 打字机模式&#xff08;Typewriter Mode&#xff09;是一种专注于当前写作行的功能 打字机模式会自动将正在编辑的行保持在屏幕中央&#xff0c;让用户更集中注意力&#xff0c;类似于传统打字机的体验 2、开启方式 点击 【视图】 -…

    3.0 compose学习:MVVM框架+Hilt注解调用登录接口

    文章目录 前言&#xff1a;1、添加依赖1.1 在settings.gradle.kts中添加1.2 在应用级的build.gradle.kts添加插件依赖1.3 在module级的build.gradle.kts添加依赖 2、实体类2.1 request2.2 reponse 3、网络请求3.1 ApiService3.2 NetworkModule3.3 拦截器 添加token3.4 Hilt 的 …

    git学习资源

    动画演示&#xff1a;Learn Git Branching 终极目标&#xff08;能看懂即入门&#xff09;&#xff1a;git 简明指南 Git 教程 | 菜鸟教程

    C++ 第二阶段:模板编程 - 第一节:函数模板与类模板

    目录 一、模板编程的核心概念 1.1 什么是模板编程&#xff1f; 二、函数模板详解 2.1 函数模板的定义与使用 2.1.1 基本语法 2.1.2 示例&#xff1a;通用交换函数 2.1.3 类型推导规则 2.2 函数模板的注意事项 2.2.1 普通函数与函数模板的调用规则 2.2.2 隐式类型转换…

    Docker 报错“x509: certificate signed by unknown authority”的排查与解决实录

    目录 &#x1f527;Docker 报错“x509: certificate signed by unknown authority”的排查与解决实录 &#x1f4cc; 问题背景 &#x1f9ea; 排查过程 步骤 1&#xff1a;确认加速器地址是否可访问 步骤 2&#xff1a;检查 Docker 是否真的使用了镜像加速器 步骤 3&…

    达梦以及其他图形化安装没反应或者报错No more handles [gtk_init_check() failed]

    本人安装问题和解决步骤如下&#xff0c;仅供参考 执行 DMInstall.bin 报错 按照网上大部分解决方案 export DISPLAY:0.0 xhost 重新执行 DMInstall.bin&#xff0c;无报错也无反应 安装xclock测试也是同样效果&#xff0c;无报错也无反应 最开始猜测可能是连接工具问题&a…

    项目节奏不一致时,如何保持全局平衡

    项目节奏不一致时&#xff0c;如何保持全局平衡的关键在于&#xff1a;构建跨项目协调机制、合理配置资源、建立共享节奏看板、优先明确战略驱动、引入缓冲与预警机制。其中&#xff0c;构建跨项目协调机制尤为关键&#xff0c;它能将各项目的排期、优先级和风险实时联动&#…

    macOS - 安装微软雅黑字体

    文章目录 1、下载资源2、安装3、查看字体 app4、卸载字体 macOS 中打开 Windows 传输过来的文件的时候&#xff0c;经常会提示 xxx 字体缺失。下面以安装 微软雅黑字体为例。 1、下载资源 https://github.com/BronyaCat/Win-Fonts-For-Mac 2、安装 双击 Fonts 文件夹下的 msy…

    ArkUI-X资源分类与访问

    应用开发过程中&#xff0c;经常需要用到颜色、字体、间距、图片等资源&#xff0c;在不同的设备或配置中&#xff0c;这些资源的值可能不同。 应用资源&#xff1a;借助资源文件能力&#xff0c;开发者在应用中自定义资源&#xff0c;自行管理这些资源在不同的设备或配置中的…

    11-StarRocks故障诊断FAQ

    StarRocks故障诊断FAQ 概述 本文档整理了StarRocks故障诊断过程中常见的问题和解决方案,涵盖了故障排查、日志分析、性能诊断、问题定位等各个方面,帮助用户快速定位和解决StarRocks相关问题。 故障排查FAQ Q1: 如何排查连接故障? A: 连接故障排查方法: 1. 网络连通性…

    敏捷项目管理怎么做?4大主流方法论对比及工具适配方案

    在传统瀑布式项目管理中&#xff0c;需求定义、设计、开发、测试等环节如同工业流水线般严格线性推进&#xff0c;展现出强大的流程控制能力。不过今天的软件迭代周期已压缩至周级乃至日级&#xff0c;瀑布式管理难以应对需求的快速变化&#xff0c;敏捷式项目管理则以“小步快…

    解决YOLO模型从Python迁移到C++时目标漏检问题——跨语言部署中的关键陷阱与解决方案

    问题背景 当我们将Python训练的YOLO模型部署到C环境时&#xff0c;常遇到部分目标漏检问题。这通常源于预处理/后处理差异、数据类型隐式转换或模型转换误差。本文通过完整案例解析核心问题并提供可落地的解决方案。 一、常见原因分析 预处理不一致 Python常用OpenCV&#xff…

    【2025CCF中国开源大会】开放注册与会议通知(第二轮)

    点击蓝字 关注我们 CCF Opensource Development Committee 2025 CCF中国开源大会 由中国计算机学会主办的 2025 CCF中国开源大会&#xff08;CCF ChinaOSC&#xff09;拟于 2025年8月2日-3日 在上海召开。本届大会以“蓄势引领、众行致远”为主题&#xff0c;由上海交通大学校长…

    本地聊天室

    测试版还没测试过&#xff0c;后面更新不会继续开源&#xff0c;有问题自行修复 开发环境: PHP版本7.2 Swoole扩展 本地服务器环境&#xff08;如XAMPP、MAMP&#xff09; 功能说明: 注册/登录系统&#xff0c;支持本地用户数据存储 ​ 发送文本、图片和语音消息 ​ 实…

    golang学习随便记x-调试与杂类(待续)

    编译与调试 调试时从终端键盘输入 调试带有需要用户键盘输入的程序时&#xff0c;VSCode报错&#xff1a;Unable to process evaluate: debuggee is running&#xff0c;因为调试器不知道具体是哪个终端输入。需要配置启动文件 .vscode/launch.json 类似如下&#xff08;注意…

    MultipartFile、File 和 Mat

    1. MultipartFile (来自 Spring Web) 用途&#xff1a; 代表通过 multipart 形式提交&#xff08;通常是 HTTP POST 请求&#xff09;接收到的文件。 它是 Spring Web 中用于处理 Web 客户端文件上传的核心接口。 关键特性&#xff1a; 抽象&#xff1a; 这是一个接口&#xf…

    .NET 9.0 SignalR 支持修剪和原生 AOT

    什么是 SignalR&#xff1f; SignalR 是一个库&#xff0c;可用于向应用程序添加实时 Web 功能。它提供了一个简单的 API&#xff0c;用于创建可从服务器和客户端调用的服务器到客户端远程过程调用 (RPC)。现在&#xff0c;SignalR 在 .NET 8.0 和 .NET 9.0 中支持修剪和原生 …

    下载资源管理

    本文章仅用于作者管理自己的站内资源&#xff0c;方便日后查找&#xff0c;后续更新资源该文章持续更新。 1、环境安装 python3.11.11环境 python3.7.9 ARM.CMSIS.5.6.0(这个在站内重复上传了) Nordic8.32.1 java8 2、工具类软件安装包 2.1、蓝牙类 SI Connect 蓝牙OT…

    ​​FFmpeg命令全解析:三步完成视频合并、精准裁剪​​、英伟达显卡加速

    一、裁剪 常规裁剪 根据时长裁剪&#xff0c;常规的裁剪 -c copy 表示直接复制流&#xff08;不重新编码&#xff09;&#xff0c;速度极快&#xff0c;但要求切割时间必须是关键帧。否则裁剪下来的画面开头/结尾 会模糊花屏 ffmpeg -i input.mp4 -ss 00:00:30 -to 00:01:00 …