当前位置: 首页 > news >正文

数据结构——链表

目录

一、链表

1、单向链表

单向链表的遍历方式:

2、循环链表

3、双向链表

二、自行车停放(双向链表)


 

一、链表

  •  链表是由许多相同数据类型的数据项按特定顺序排列而成的线性表
  • 特性:存放的位置是不连续且随机的,动态分配内存
    • 内存泄漏(Memory Leak):变量使用了内存存放,使用后,没有释放内存
  • 优点:插入或删除数据方便
  • 缺点:查找不方便,要按序查找数据
  • Java中常见的链表实现类:
    • LinkedList 
      • 基于链表实现,插入和删除操作时间复杂度为 O(1),因为只需修改节点的指针。
      • 不支持随机访问,需要从头部或尾部开始遍历链表以访问特定位置的元素,时间复杂度为 O(n)。
      • 可存储相同的元素
      • 增删元素后,链表里的元素下标不会发生变化
      • 适用于插入删除操作频繁、读取操作相对较少的场景。另外,LinkedList还可以作为队列或栈来使用。
    • ArrayList 

      • 基于数组实现,支持随机访问,根据索引可以在 O(1) 的时间复杂度内访问元素。
      • 插入和删除操作需要移动元素,时间复杂度为 O(n)。如果在末尾插入或删除元素,时间复杂度为 O(1)。
      • 增删元素后,会自动调整链表里元素的下标
      • 适用于读取操作频繁、插入删除操作相对较少的场景。
    • ListNode 则是自定义节点类,通常用于手动实现链表结构。

1、单向链表

  • 单向链表是一种数据结构,其中的每个节点包含数据和一个指向下一个节点的引用。
  • 链表的第一个节点称为头节点,最后一个节点的引用指向 null。这意味着在单向链表中,只能从头节点开始,按顺序遍历到最后一个节点,而无法逆向遍历。

单向链表的示例代码:

// 定义链表节点,包含节点数据和指向下一个节点的引用
class Node {int data;Node next;// 节点构造函数public Node(int data) {this.data = data;this.next = null;}
}// 定义链表
class LinkedList {Node head;// 链表构造函数public LinkedList() {this.head = null;}// 在链表末尾添加节点public void append(int data) {Node newNode = new Node(data);if (head == null) {head = newNode;} else {Node current = head;while (current.next != null) {current = current.next;}current.next = newNode;}}// 删除特定数值节点public void delete(int data) {if (head == null) {return;}if (head.data == data) {head = head.next;return;}Node current = head;while (current.next != null) {if (current.next.data == data) {current.next = current.next.next;return;}current = current.next;}}// 打印链表public void printList() {Node current = head;while (current != null) {System.out.print(current.data + " ");current = current.next;}System.out.println();}
}

单向链表的遍历方式:

在 Java 中,可以使用不同的方法来遍历 List。以下是几种常用的遍历方法:

1. 使用 for 循环:

List<String> list = new ArrayList<>();
list.add("A");
list.add("B");
list.add("C");for (int i = 0; i < list.size(); i++) {String element = list.get(i);System.out.println(element);
}

2. 使用增强型 for 循环(foreach 循环):

for (String element : list) {System.out.println(element);
}

3. 使用迭代器(Iterator):

Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {String element = iterator.next();System.out.println(element);
}

2、循环链表

  • 循环链表和普通链表的区别在于循环链表的末尾节点指向链表的头部节点,形成一个环形结构。这种特殊的连接方式使得循环链表可以无限地往后遍历下去,直到再次经过链表的起始节点。

  • 循环链表可以是单向的,也可以是双向的,它们在实际应用中通常用于模拟循环队列或者循环缓冲区等结构。

  • 在循环链表中,基本的节点结构和普通链表一样,每个节点包含一个数据域和一个指向下一个节点的指针。

  • 循环链表通常具有一些特殊的操作,比如判断链表是否为空、遍历链表的方法等,这些操作和普通链表有所不同。在实际编程中,设计和操作循环链表需要特别注意避免陷入无限循环的情况。

循环链表的示例代码:

// 定义循环链表节点类
class ListNode {int val;ListNode next;// 节点类的构造函数ListNode(int val) {this.val = val;this.next = null;}
}// 定义循环链表类
class CircularLinkedList {private ListNode tail;  // 循环链表的尾节点// 添加节点到循环链表尾部public void addToTail(int value) {ListNode newNode = new ListNode(value);if (tail == null) {tail = newNode;tail.next = tail;  // 只有一个节点时,让节点指向自己形成循环} else {newNode.next = tail.next;  // 新节点指向头节点tail.next = newNode;       // 原尾节点指向新节点tail = newNode;            // 更新尾节点为新节点}}// 从循环链表中删除指定数值的节点public void deleteNode(int value) {if (tail != null) {ListNode current = tail;while (current.next != tail) {if (current.next.val == value) {current.next = current.next.next;  // 绕过匹配节点return;}current = current.next;}// 当只有一个节点时需要特殊处理if (tail.val == value) {if (tail == tail.next) {tail = null;  // 移除最后一个节点} else {tail.next = tail.next.next;  // 只有一个节点时删除自身}}}}// 遍历循环链表并打印节点数值public void printList() {if (tail != null) {ListNode current = tail.next;  // 头节点do {System.out.print(current.val + " ");current = current.next;} while (current != tail.next);}System.out.println();}
}

3、双向链表

  • 双向链表是一种数据结构,它由多个节点组成。
  • 每个节点包含两个指针,一个指向前一个节点,另一个指向后一个节点。这使得在双向链表中,每个节点都可以直接访问其前一个节点和后一个节点。
  • 双向链表相对于单向链表的优势在于可以双向遍历链表,可以在 O(1) 时间复杂度内实现在任意位置插入和删除节点。

双向链表的示例代码:

//双向链表节点的定义,每个节点包含一个整数值和两个指针,分别指向前一个节点和后一个节点。
class ListNode {int val;ListNode prev;ListNode next;// 构造函数ListNode(int val) {this.val = val;this.prev = null;this.next = null;}
}class DoublyLinkedList {ListNode head;// 在链表头部插入节点void insertAtHead(int val) {ListNode newHead = new ListNode(val);if (head != null) {newHead.next = head;head.prev = newHead;}head = newHead;}// 在链表尾部插入节点void insertAtTail(int val) {ListNode newTail = new ListNode(val);if (head == null) {head = newTail;return;}ListNode current = head;while (current.next != null) {current = current.next;}current.next = newTail;newTail.prev = current;}// 删除指定数值的节点void deleteNode(int val) {ListNode current = head;while (current != null) {if (current.val == val) {if (current.prev != null) {current.prev.next = current.next;} else {head = current.next;}if (current.next != null) {current.next.prev = current.prev;}return;}current = current.next;}}
}

二、自行车停放(双向链表)

分析:

  •  如果用单向链表,当n较大时,运行会超时
package no1_1;
import java.util.*;public class Main {public static class TreeNode {int left;int right;}public static void main(String[] args) {Scanner sc = new Scanner(System.in);// 读取输入的值 n 和 kint n = sc.nextInt();TreeNode[] node = new TreeNode[100000 + 2];int k = sc.nextInt();// 初始化节点数组for (int i = 0; i <= 100001; i++) {node[i] = new TreeNode();node[i].left = -1;node[i].right = -1;}// 构建链表结构node[k].left = 0;node[k].right = 100001;node[0].right = k;node[n + 1].left = k;// 读取边的信息,构建链表for (int i = 0; i < n - 1; i++) {int x = sc.nextInt();int y = sc.nextInt();int z = sc.nextInt();if (z == 0) {// 在 y 的左边插入节点 xnode[x].left = node[y].left;node[x].right = y;node[node[y].left].right = x;node[y].left = x;} else {// 在 y 的右边插入节点 xnode[x].right = node[y].right;node[x].left = y;node[node[y].right].left = x;node[y].right = x;}}// 遍历链表并输出结果int index = 0;for (;;) {if (node[index].right == 100001) break;System.out.print(node[index].right + " ");index = node[index].right;}}
}

相关文章:

数据结构——链表

目录 一、链表 1、单向链表 单向链表的遍历方式&#xff1a; 2、循环链表 3、双向链表 二、自行车停放&#xff08;双向链表&#xff09; 一、链表 链表是由许多相同数据类型的数据项按特定顺序排列而成的线性表特性&#xff1a;存放的位置是不连续且随机的&#xff0c;动…...

uniapp使用vuex

1、uniapp中使用vuex_uniapp使用vuex-CSDN博客 2、uniapp中使用vuex(store)模块的例子 - 简书 (jianshu.com) 3、vuex介绍及使用指南&#xff08;面向实战&#xff09;_vuex 实战应用-CSDN博客...

C++从入门到精通——this指针

this指针 前言一、this指针的引出问题 二、this指针的特性三、例题什么时候会出现编译报错什么时候会出现运行崩溃this指针存在哪里this指针可以为空吗 四、C语言和C实现Stack的对比C语言实现C实现 前言 this指针是一个特殊的指针&#xff0c;在C类的成员函数中使用。它指向调…...

Hive3.0.0建库表命令测试

Hive创建表格格式如下&#xff1a; create [external] table [if not exists] table_name [(col_name data_type [comment col_comment],)] [comment table_comment] [partitioned by(col_name data_type [comment col_comment],)] [clustered by (col_name,col_name,...)…...

一起学习python——基础篇(7)

今天讲一下python的函数。 函数是什么&#xff1f;函数是一段独立的代码块&#xff0c;这块代码是为了实现一些功能&#xff0c;而这个代码块只有在被调用时才能运行。 在 Python 中&#xff0c;使用 def 关键字定义函数&#xff1a; 函数的固定结构就是 def(关键字)函数名字…...

【LeetCode热题100】74. 搜索二维矩阵(二分)

一.题目要求 给你一个满足下述两条属性的 m x n 整数矩阵&#xff1a; 每行中的整数从左到右按非严格递增顺序排列。每行的第一个整数大于前一行的最后一个整数。 给你一个整数 target &#xff0c;如果 target 在矩阵中&#xff0c;返回 true &#xff1b;否则&#xff0c;…...

Android OkHttp

目录 1.build.gradle 2.基本使用 3.POST请求 4.Builder构建者 1.build.gradle implementation("com.squareup.okhttp3:okhttp:4.12.0") 2.基本使用 GET同步请求 public void getSync(View view) {new Thread(){Overridepublic void run() {Request request …...

Java常用API_正则表达式_字符串的替换和截取方法——小练习

我将通过一个练习题来展示这两个方法 练习题&#xff1a; 有一段字符串&#xff1a;小张qwertyuiop123小李asdfghjkl456小王 要求1&#xff1a;把字符串中三个姓名之间的字母替换成vs 要求2&#xff1a;把字符串中的三个姓名切割出来 编写代码&#xff1a; public class Tes…...

从头开发一个RISC-V的操作系统(四)嵌入式开发介绍

文章目录 前提嵌入式开发交叉编译GDB调试&#xff0c;QEMU&#xff0c;MAKEFILE练习 目标&#xff1a;通过这一个系列课程的学习&#xff0c;开发出一个简易的在RISC-V指令集架构上运行的操作系统。 前提 这个系列的大部分文章和知识来自于&#xff1a;[完结] 循序渐进&#x…...

Web漏洞-文件上传常见验证

后缀名&#xff1a;类型&#xff0c;文件头等 后缀名&#xff1a;黑白名单 文件类型&#xff1a;MIME信息 文件头&#xff1a;内容头信息 常见黑名单&#xff08;明确不允许上传的格式后缀&#xff09;&#xff1a;asp、php、jsp、aspx、cgi、war &#xff08;如果没有完整…...

如何在 Node.js 中使用 bcrypt 对密码进行哈希处理

在网页开发领域中&#xff0c;安全性至关重要&#xff0c;特别是涉及到用户凭据如密码时。在网页开发中至关重要的一个安全程序是密码哈希处理。 密码哈希处理确保明文密码在数据库受到攻击时也难以被攻击者找到。但并非所有的哈希方法都是一样的&#xff0c;这就是 bcrypt 突…...

嵌入式学习49-单片机2

指令周期 1M 机器周期 12M &#xff08;晶体震荡器产生&#xff09; 中断两种方式 …...

汽车EDI:如何与奔驰建立EDI连接?

梅赛德斯-奔驰是世界闻名的豪华汽车品牌&#xff0c;无论是技术实力还是历史底蕴都在全球汽车主机厂中居于领先位置。奔驰拥有多种车型&#xff0c;多元化的产品布局不仅满足了不同用户画像的需求&#xff0c;也对其供应链体系有着极大的考验。 本文将为大家介绍梅赛德斯-奔驰乘…...

性能分析--内存知识

内存相关知识 计算机中与CPU进行数据交换的桥梁。内存的速度&#xff0c;比CPU的速度要慢很多。比磁盘速度要快很多。内存中存放数据&#xff0c;一旦断电就会消失。linux系统的 /proc路径下的文件&#xff0c;都是内存文件。内存大小&#xff0c;一般 是GB为单位。 现在都操作…...

目标检测标签分配策略,难样本挖掘策略

在目标检测任务中&#xff0c;样本的划分对于模型的性能具有至关重要的影响。其中&#xff0c;正样本指的是包含目标物体的图像或区域&#xff0c;而负样本则是不包含目标物体的图像或区域。然而&#xff0c;在负样本中&#xff0c;有一部分样本由于其与正样本在特征上的相似性…...

Java | Leetcode Java题解之第16题最接近的三数之和

题目&#xff1a; 题解&#xff1a; class Solution {public int threeSumClosest(int[] nums, int target) {Arrays.sort(nums);int n nums.length;int best 10000000;// 枚举 afor (int i 0; i < n; i) {// 保证和上一次枚举的元素不相等if (i > 0 && nums…...

FIN和RST的区别,几种TCP连接出现RST的情况

一、RST跟FIN的区别&#xff1a; 正常关闭连接的时候发的包是FIN&#xff0c;但是如果是异常关闭连接&#xff0c;则发送RST包 两者的区别在于&#xff1a; 1.RST不必等缓冲区的包都发出去&#xff0c;直接就丢弃缓存区的包发送RST包。而FIN需要先处理完缓存区的包才能发送F…...

2024/4/1—力扣—删除字符使频率相同

代码实现&#xff1a; 思路&#xff1a; 步骤一&#xff1a;统计各字母出现频率 步骤二&#xff1a;频率从高到低排序&#xff0c;形成频率数组 步骤三&#xff1a;频率数组只有如下组合符合要求&#xff1a; 1, 0...0n 1, n...n (, 0)n...n, 1(, 0) bool equalFrequency(char…...

Spring源码解析-容器基本实现

spring源码解析 整体架构 defaultListableBeanFactory xmlBeanDefinitionReader 创建XmlBeanFactory 对资源文件进行加载–Resource 利用LoadBeandefinitions(resource)方法加载配置中的bean loadBeandefinitions加载步骤 doLoadBeanDefinition xml配置模式 validationMode 获…...

Python 基于 OpenCV 视觉图像处理实战 之 OpenCV 简单视频处理实战案例 之四 简单视频倒放效果

Python 基于 OpenCV 视觉图像处理实战 之 OpenCV 简单视频处理实战案例 之四 简单视频倒放效果 目录 Python 基于 OpenCV 视觉图像处理实战 之 OpenCV 简单视频处理实战案例 之四 简单视频倒放效果 一、简单介绍 二、简单视频倒放效果实现原理 三、简单视频倒放效果案例实现…...

从HackRF到USRP B210:我的SDR设备升级之路与真实体验对比

从HackRF到USRP B210&#xff1a;我的SDR设备升级之路与真实体验对比 作为一个长期沉迷于软件定义无线电&#xff08;SDR&#xff09;技术的爱好者&#xff0c;设备的选择往往决定了探索的边界。从最初的HackRF One到如今的USRP B210&#xff0c;这段升级旅程不仅是对硬件性能的…...

智能体架构实战:从LangGraph状态机到多智能体协作

1. 从理论到实践&#xff1a;为什么我们需要一个“智能体架构大全”项目如果你在过去一年里关注过AI领域&#xff0c;尤其是大语言模型的应用开发&#xff0c;那么“智能体”这个词一定已经听得耳朵起茧了。从能帮你写代码的Devin&#xff0c;到能自主完成复杂任务的GPT-4o&…...

AI建站工具从0到1全流程保姆级攻略:零代码生成网站就这么简单

AI建站工具从0到1全流程保姆级攻略&#xff1a;零代码生成网站就这么简单被外包公司几万块的报价劝退&#xff1f;被老板催着下周上线活动页却连域名是什么都不清楚&#xff1f;别慌&#xff0c;用AI建站工具&#xff0c;不写一行代码、不学复杂技术&#xff0c;普通人也能在两…...

2026 年 Redis 面试题全解析:原理 + 实战 + 高频考点

Redis 高频面试题全解析&#xff08;2026 最新版&#xff09; Redis 作为后端开发高并发、高可用架构的核心组件&#xff0c;是面试中必问的核心考点。本文从基础入门、核心原理、高并发实战、高可用架构、进阶运维五大模块&#xff0c;整理大厂高频面试题与标准答案&#xff…...

从零部署Claude 3.5 Sonnet私有化实例:NVIDIA A10/A100实测吞吐对比、Token缓存优化与RAG集成避坑指南(含GitHub开源脚本)

更多请点击&#xff1a; https://intelliparadigm.com 第一章&#xff1a;Claude 3.5 Sonnet新功能详解 Anthropic 正式发布的 Claude 3.5 Sonnet 在推理速度、多模态理解与工具调用能力上实现了显著跃升。相比前代&#xff0c;其上下文窗口稳定支持 200K tokens&#xff0c;…...

shell脚本案例(dns主从服务配置)

dns主从服务配置主服务器shell脚本#!/bin/bashset -euo pipefail#configuration parametersMASTER_IP"192.168.153.131" DOMAIN"web.com" REV_ZONE"153.168.192.in-addr.arpa" SLAVE_IP"192.168.153.132"#tool parametersinfo(){ echo…...

Linux I2C设备驱动避坑指南:以MPU6050为例,解决i2c_transfer返回EIO错误

Linux I2C设备驱动深度排障&#xff1a;MPU6050的EIO错误全解析 调试嵌入式设备时&#xff0c;最令人沮丧的莫过于那些间歇性出现的错误。它们像幽灵一样时隐时现&#xff0c;让开发者陷入无尽的猜测和试错循环。MPU6050作为一款广泛使用的运动传感器&#xff0c;其I2C接口的稳…...

向量引擎、DeepSeek V4、GPT Image 2、api key:为什么 Agent 真正落地时,先补的不是模型,而是记忆层

向量引擎、DeepSeek V4、GPT Image 2、api key&#xff1a;为什么 Agent 真正落地时&#xff0c;先补的不是模型&#xff0c;而是记忆层最近这波 AI 的变化&#xff0c;有个很明显的信号。 模型还在继续变强&#xff0c;但讨论重心已经悄悄变了。 以前大家最爱问的是“哪个模型…...

【AI搜索时代生存指南】:Perplexity vs Google搜索的5大核心差异,90%的开发者还不知道的关键决策点

更多请点击&#xff1a; https://intelliparadigm.com 第一章&#xff1a;AI搜索时代的技术范式迁移 传统关键词匹配式搜索正被语义理解、上下文感知与生成式推理深度重构。AI搜索不再仅返回文档链接&#xff0c;而是直接合成答案、推演逻辑链、调用工具并动态验证结果——这标…...

2026年小白适用Hermes Agent/OpenClaw Token Plan集成全攻略大全

2026年小白适用Hermes Agent/OpenClaw Token Plan集成全攻略大全。OpenClaw作为阿里云生态下新一代的开源AI自动化代理平台&#xff0c;曾用名Moltbot/Clawdbot&#xff0c;凭借“自然语言交互自动化任务执行大模型智能决策”的核心能力&#xff0c;正在重构个人与企业的工作效…...