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

单链表的手动实现+相关OJ题

目录

链表的介绍

单链表的手动实现

单链表的基本框架

打印链表:

获取表长:

头插法新增节点:

尾插法新增节点:

在指定下标插入:

链表的查找

 删除链表中第一个出现的key:

删除链表中所有key值

链表置空

 总代码

测试代码

 链表相关的OJ题

1.反转单链表

 思路:​

2.查找链表中间节点

 思路:1.判空;2.定义两个指针,都指向head;3.设置循环,每次循环一个指针走两步一个指针走一步,当走的快的那个指针到达最后一个节点时,慢的节点就在中间;


链表的介绍

基于顺序表具有,插入数据,空间不足时要扩容,扩容有性能消耗和插入删除数据时有时候需要大量的挪动数据,导致程序的执行效率低的缺点,延升出了链表。

链表,是一种逻辑结构连续物理结构不一定连续的一种数据结构;由一个个的节点组成,每个节点包含数据域和指针域,指针域指向下一个节点。

单链表的手动实现

单链表的基本框架

//不带头结点的单链表
public class MySingleList implements IList{//链表由若干个头结点组成,每个节点都是一个完整的部分,所以可以定义一个内部类static class ListNode{public int val;//数据域public ListNode next;//指针域 存放下一个节点的地址public ListNode(int val) {this.val = val;}}public ListNode head;//定义一个存放节点的变量,默认值为null@Overridepublic void addFirst(int data) {}@Overridepublic void addLast(int data) {}@Overridepublic void addIndex(int index, int data) {}@Overridepublic boolean contains(int key) {return false;}@Overridepublic void remove(int key) {}@Overridepublic void removeAllKey(int key) {}@Overridepublic int size() {return 0;}@Overridepublic void clear() {}@Overridepublic void display() {}
}

为了更好的理解这个单链表的操作,我们先创建单链表的方法:

    public ListNode head;//定义一个存放节点的变量,默认值为nullpublic void createList(){//1.通过实例化ListNode创建几个节点ListNode node1 = new ListNode(12);ListNode node2 = new ListNode(22);ListNode node3 = new ListNode(32);ListNode node4 = new ListNode(42);//2.根据单链表的形态,把节点链接起来node1.next = node2;node2.next = node3;node3.next = node4;this.head = node1;}

打印链表:

    public void display() {//1.借助一个cur节点去遍历链表,移动cur的位置ListNode cur = head;while (cur !=null){//cur=null表示整个链表都走完了System.out.print(cur.val+" ");cur = cur.next;}System.out.println();}

注意区分循环条件 cur !=null 和cur.next !=null

获取表长:

获取表长的方法是打印方法衍生出来的,无非就是打印的方法中加一个计数器去记表长:

    public int size() {ListNode cur = head;int count = 0;while (cur != null){//注意这个循环条件count++;cur = cur.next;}return count;}

头插法新增节点:

    public void addFirst(int data) {//1.实例化一个节点ListNode node = new ListNode(data);//2.新增节点的指针域指向头节点更新头结点 顺序不可变node.next = head;this.head = node;}

 注意:

  • 插入数据时先绑定后面,如果我们先绑定前面的话会发生什么呢?当this.head = node先执行的完后,新增节点指针域该指向谁了,为了防止数据丢失,插入数据时因先绑定后面

尾插法新增节点:

    public void addLast(int data) {ListNode node = new ListNode(data);//1.找到链表最后一个节点if (head == null){head = node;}else {//遍历链表去找ListNode cur = head;while (cur.next != null) {cur = cur.next;}cur.next = node ;//增}}

在指定下标插入:

    public void addIndex(int index, int data) {//1.判断index的合法性if (index < 0 || index > size()){throw new IndexException("index非法"+index);}//表空的情况ListNode node = new ListNode(data);if (head == null){head = node;return;}//2.在0位置插入时调用头插if (index == 0 ){addFirst(data);return;}//3.index=size()尾插法if (index == size()){addLast(data);return;}//在index位置插入时,找到index位置的前一个节点去操作ListNode cur = findPrevIndex(index);node.next = cur.next;cur.next = node;}private ListNode findPrevIndex(int index){ListNode cur = head;int count = 0;while (count != index - 1){count++;cur = cur.next;}return cur;}

链表的查找

    public boolean contains(int key) {ListNode cur = head;while (cur !=null){if (cur.val == key){return true;}cur = cur.next;}return false;}

 删除链表中第一个出现的key:

   public void remove(int key) {//1.表空if (head == null){return;}//2.头结点删除if (head.val == key){head = head.next;return;}//3.中间元素删除找key的前驱节点去操作ListNode cur = findPrevKey(key);cur.next = cur.next.next;//也可以借助一个变量去删除/*ListNode del = cur.next;cur.next = del.next;*/}//尽量以方法的方式去实现功能,降低代码的耦合度private ListNode findPrevKey(int key){ListNode cur = head;while (cur.next !=null){if (cur.next.val == key){return cur;}else {cur = cur.next;}}return null;}

删除链表中所有key值

    public void removeAllKey(int key) {if (head == null) {return;}//定义两个指针分别指向头和头的下一个ListNode prev = head;ListNode cur = head.next;//可能要被移除的元素while (cur != null) {//遍历if (cur.val == key) {prev.next = cur.next;cur = cur.next;} else {//不用删除就往后继续遍历prev = cur;cur = cur.next;}//此时除了头结点其他节点都遍历完了,就判断头节点if (head.val == key) {head = head.next;}}}

链表置空

    public void clear() {//法1:循环把每个节点都置空//法2:直接把最开始的节点中(只要是没有人引用的对象内存都会回收置空),这样就把整个链表置空了head = null;}

 总代码

//不带头结点的单链表
public class MySingleList implements IList{//链表由若干个头结点组成,每个节点都是一个完整的部分,所以可以定义一个内部类static class ListNode{public int val;//数据域public ListNode next;//指针域 存放下一个节点的地址public ListNode(int val) {this.val = val;}}public ListNode head;//定义一个存放节点的变量,默认值为nullpublic void createList(){//1.通过实例化ListNode创建几个节点ListNode node1 = new ListNode(12);ListNode node2 = new ListNode(22);ListNode node3 = new ListNode(32);ListNode node4 = new ListNode(42);//2.根据单链表的形态,把节点链接起来node1.next = node2;node2.next = node3;node3.next = node4;this.head = node1;}@Overridepublic void addFirst(int data) {//1.实例化一个节点ListNode node = new ListNode(data);//2.新增节点的指针域指向头节点更新头结点node.next = head;this.head = node;}@Overridepublic void addLast(int data) {ListNode node = new ListNode(data);//1.找到链表最后一个节点if (head == null){head = node;}else {//遍历链表去找ListNode cur = head;while (cur.next != null) {cur = cur.next;}cur.next = node ;//增}}@Overridepublic void addIndex(int index, int data) {//1.判断index的合法性if (index < 0 || index > size()){throw new IndexException("index非法"+index);}//表空的情况ListNode node = new ListNode(data);if (head == null){head = node;return;}//2.在0位置插入时调用头插if (index == 0 ){addFirst(data);return;}//3.index=size()尾插法if (index == size()){addLast(data);return;}//在index位置插入时,找到index位置的前一个节点去操作ListNode cur = findPrevIndex(index);node.next = cur.next;cur.next = node;}private ListNode findPrevIndex(int index){ListNode cur = head;int count = 0;while (count != index - 1){count++;cur = cur.next;}return cur;}@Overridepublic boolean contains(int key) {ListNode cur = head;while (cur !=null){if (cur.val == key){return true;}cur = cur.next;}return false;}@Overridepublic void remove(int key) {//1.表空if (head == null){return;}//2.头结点删除if (head.val == key){head = head.next;return;}//3.中间元素删除找key的前驱节点去操作ListNode cur = findPrevKey(key);cur.next = cur.next.next;//也可以借助一个变量去删除/*ListNode del = cur.next;cur.next = del.next;*/}//尽量以方法的方式去实现功能,降低代码的耦合度private ListNode findPrevKey(int key){ListNode cur = head;while (cur.next !=null){if (cur.next.val == key){return cur;}else {cur = cur.next;}}return null;}@Overridepublic void removeAllKey(int key) {if (head == null) {return;}//定义两个指针分别指向头和头的下一个ListNode prev = head;ListNode cur = head.next;//可能要被移除的元素while (cur != null) {//遍历if (cur.val == key) {prev.next = cur.next;cur = cur.next;} else {//不用删除就往后继续遍历prev = cur;cur = cur.next;}//此时除了头结点其他节点都遍历完了,就判断头节点if (head.val == key) {head = head.next;}}}@Overridepublic int size() {ListNode cur = head;int count = 0;while (cur != null){count++;cur = cur.next;}return count;}@Overridepublic void clear() {//法1:循环把每个节点都置空//法2:直接把最开始的节点中(只要是没有人引用的对象内存都会回收置空),这样就把整个链表置空了head = null;}@Overridepublic void display() {if (head == null){System.out.println("链表为空");return;}//1.借助一个cur节点去遍历链表,移动cur的位置ListNode cur = head;while (cur !=null){//cur=null表示整个链表都走完了System.out.print(cur.val+" ");cur = cur.next;}System.out.println();}}

测试代码

public class Test {public static void main(String[] args) {MySingleList mySingleList = new MySingleList();mySingleList.createList();System.out.println();mySingleList.display();//12 22 32 42System.out.println(mySingleList.size());//4mySingleList.addFirst(2);mySingleList.display();//2 12 22 32 42mySingleList.addLast(52);mySingleList.display();//2 12 22 32 42 52mySingleList.addIndex(2,20);mySingleList.display();//2 12 20 22 32 42 52System.out.println(mySingleList.contains(52));//truemySingleList.remove(12);mySingleList.display();//2 20 22 32 42 52mySingleList.addLast(2);mySingleList.addIndex(3,2);mySingleList.display();//2 20 22 2 32 42 52 2mySingleList.removeAllKey(2);mySingleList.display();//20 22 32 42 52mySingleList.clear();mySingleList.display();//链表为空}
}

 链表相关的OJ题

1.反转单链表

反转一个单链表

 思路:

    public ListNode reverseList(){if (head == null){return null;}if (head.next == null){return head;}//ListNode cur = head.next;//定义一个cur记录当前需要反转的节点head.next = null;while(cur != null){ListNode curNext = cur.next;//记录下一个反转点cur.next = head;//当前要反转的节点指向headhead = cur;//更新headcur = curNext;//更新cur}return head;}

2.查找链表中间节点

题目

 思路:1.判空;2.定义两个指针,都指向head;3.设置循环,每次循环一个指针走两步一个指针走一步,当走的快的那个指针到达最后一个节点时,慢的节点就在中间;

    public ListNode middleNode(){if (head == null){return null;}ListNode fast = head;ListNode slow = head;while (fast != null && fast.next !=null){//顺序不能反,否则报空指针异常fast = fast.next.next;slow = slow.next;}return slow;}

 感谢大家阅读📚点赞👍收藏⭐评论✍关注❤

博客主页: 【长毛女士-CSDN博客 

http://www.dtcms.com/a/289906.html

相关文章:

  • PostgreSQL 字段类型速查与 Java 枚举映射
  • 【硬件】GalaxyTabPro10.1(SM-T520)刷机/TWRP/LineageOS14/安卓7升级全过程
  • 讲座|人形机器人多姿态站起控制HoST及宇树G1部署
  • C++ 并发 future, promise和async
  • 2025年AIR SCI1区TOP,缩减因子分数阶蜣螂优化算法FORDBO,深度解析+性能实测
  • 基于51单片机的温湿度检测系统Protues仿真设计
  • 创建一个触发csrf的恶意html
  • 低速信号设计之I3C篇
  • windows11环境配置torch-points-kernels库编译安装详细教程
  • 【前端】懒加载(组件/路由/图片等)+预加载 汇总
  • NJU 凸优化导论(10) Approximation+Projection逼近与投影的应用(完结撒花)
  • InfluxDB 数据模型:桶、测量、标签与字段详解(二)
  • springboot --大事件--文章管理接口开发
  • 简洁高效的C++终端日志工具类
  • 响应式编程入门教程第七节:响应式架构与 MVVM 模式在 Unity 中的应用
  • SEO中关于关键词分类与布局的方法有那些
  • 【实战1】手写字识别 Pytoch(更新中)
  • Codes 通过创新的重新定义 SaaS 模式,专治 “原教旨主义 SaaS 的水土不服
  • 一文速通《二次型》
  • 复盘与导出工具最新版V27.0版本更新-新增财联社涨停,自选股,表格拖拽功能
  • Agentic-R1 与 Dual-Strategy Reasoning
  • Raspi4 切换QNX系统
  • cmake语法学习笔记
  • 模电基础-开关电路和NE555
  • 【2025西门子信息化网络化决赛】模拟题+技术文档+实验vrrp standby vxlan napt 智能制造挑战赛 助力国赛!
  • Linux之conda安装使用
  • 【数据结构】栈和队列(接口超完整)
  • 实践教程:基于RV1126与ZeroTier的RTSP摄像头内网穿透与远程访问
  • InfluxDB 数据模型:桶、测量、标签与字段详解(一)
  • iptables -m connlimit导致内存不足