【Java数据结构】经典链表OJ题——超详细做题笔记及心得(一)

简介: 【Java数据结构】经典链表OJ题——超详细做题笔记及心得(每行代码都有注释嗷)

⭐1.反转链表

题目:

f2e0e9520a654cf1bce74383556a7608.png


解题思路:

如下图,我们要实现的就是这样一个效果

c6367fb55e534c62bd9e183c1d87b33e.png

要实现上图的效果,需要以下步骤:

①设置两个指针,cur 指向链表头节点,prev 指向空
暂存 cur 的后继节点,curNext = cur.next
③将 cur.next 反指向prev(一开始prev为空)

④prev 指针后移,即将 prev 指向 cur
⑤cur 指针后移 ,即将 cur 指向 2 中暂存的 curNext 节点
⑥循环: 第2 到 5 步,直到 cur 遍历完整个链表

/**
* Definition for singly-linked list.
* public class ListNode {
*     int val;
*     ListNode next;
*     ListNode(int x) { val = x; }
* }
*/
class Solution {
   public ListNode reverseList(ListNode head) {
       if(head==null){
           System.out.println("链表为空");
       }
      //设置两个指针,cur 指向链表头节点,prev 指向空
       ListNode prev = null;
       ListNode cur = head;
       while(cur != null){
           ListNode curNext = cur.next;//暂存 cur 的后继节点,curNext = cur.next
           cur.next =prev;//将 cur.next 反指向prev(一开始prev为空)
           prev = cur;//prev 指针后移,即将 prev 指向 cur
           cur = curNext;//cur 指针后移 ,即将 cur 指向 2 中暂存的 curNext 节点
       }//循环: 第2 到 5 步,直到 cur 遍历完整个链表
       return prev;
   }
}

⭐2.给定一个带有头结点 head 的非空单链表,返回链表的中间结点

题目:

41e3886ae32549f29fda2cbb64ba437b.png


解题思路:

本题用的是双指针的方法
①分别设置一个快指针和一个慢指针
②快指针每次走两步,慢指针每次走一步
③当快指针走到最后尾节点的时候,慢指针就走到了链表中间节点

/**
* Definition for singly-linked list.
* public class ListNode {
*     int val;
*     ListNode next;
*     ListNode() {}
*     ListNode(int val) { this.val = val; }
*     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
   public ListNode middleNode(ListNode head) {
       ListNode fast = head;//快指针一开始指向头结点
       ListNode slow = head;//慢指针一开始也指向头结点
       while(fast!=null&&fast.next!=null){//由于要考虑偶数链表返回中间靠后的节点
       //所以要多设置一个fast.next!=null条件
           fast=fast.next.next;//快指针往后走两步
           slow=slow.next;//慢指针往后走一步
       }
       return slow;//返回慢指针
   }
}

⭐3.输入一个链表输出该链表中倒数第K个节点

题目:

d233ed34fad7453ba2471e61d8fe60ec.png

解题思路:

这题和上题一样,采用双指针的办法,遍历链表一次就能达到目的

e6bb86f65cf14834b02f9d0a73e048e1.png

具体需要以下步骤:

①初始化: 前指针 former 、后指针 latter ,双指针都指向头节点 head 。
②构建双指针距离: 前指针 former 先向前走 k-1 步(结束后,双指针 former 和 latter 间相距 k-1步)。

③双指针共同移动: 循环中,双指针 former 和 latter 每轮都向前走一步,直至 former 走到链表 尾节点 时跳出(跳出后, latter 与尾节点距离为 k-1步,即 latter 指向倒数第 k 个节点)
④返回值: 返回 latter 即可。

/**
* Definition for singly-linked list.
* public class ListNode {
*     int val;
*     ListNode next;
*     ListNode(int x) { val = x; }
* }
*/
class Solution {
   public ListNode getKthFromEnd(ListNode head, int k) {
       ListNode former = head;//设置一个先行节点,一开始指向头结点
       ListNode latter = head;//再设置一个后行节点,一开始指向头结点
       for(int i=1; i < k; i++){//构建双节点之间距离
           former = former.next;//
      }
       //构建距离完成之后,双节点同时向后走,直至先行节点到达尾节点处
       while(former.next!=null){//
           former = former.next;//
           latter = latter.next;//
       }
       return latter;//此时latter距离尾节点k-1步,也就是指向倒数第k个节点,直接返回latter即可
   }
}

⭐4.将两个有序链表合并为一个新的有序链表并返回

题目:

df981eb08f4d403482df73e8b5b5fe99.png

解题思路:

d62586292cde48a2b019be2506c93234.png

具体步骤如下
①设置傀儡节点,傀儡节点后边的节点组成的链表就是合并后的链表
②比较两个链表的每个节点,存入傀儡节点后的合并链表
③当有一条链已经遍历完成则将另一条链接到合并链表的尾部
④最后返回的是傀儡节点的下一个节点,这才是合并后的链表的头结点

/**
* Definition for singly-linked list.
* public class ListNode {
*     int val;
*     ListNode next;
*     ListNode() {}
*     ListNode(int val) { this.val = val; }
*     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
   public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
       ListNode newhead = new ListNode(-1);//创建一个虚拟傀儡节点,最后返回的是.next
       ListNode tmp =newhead;//创建局部引用,指向傀儡节点
       while (l1!=null && l2!=null) {//依次比较节点
           if (l1.val < l2.val) {//情况1
               tmp.next = l1;//将节点存入合并链表
               l1 = l1.next;//L2往后走一步
               tmp = tmp.next;//合并链表也要往后走一步好用于储存下一个比较结果
           } else {             //情况2
               tmp.next = l2;//将节点存入合并链表
               l2 = l2.next;//L2往后走一步
               tmp = tmp.next;//合并链表也要往后走一步好用于储存下一个比较结果
           }
       }
       if (l1==null){//当第一条链先走完
           tmp.next = l2;//将第二条链接到合并链表后
       }
       if (l2==null){//当第二条链先走完
           tmp.next = l1;//将第一条链接到合并链表后
       }
       return newhead.next;//最终返回傀儡节点的下一个节点,也就是合并链表的头结点
}


相关文章
|
8月前
|
前端开发 Java
java实现队列数据结构代码详解
本文详细解析了Java中队列数据结构的实现,包括队列的基本概念、应用场景及代码实现。队列是一种遵循“先进先出”原则的线性结构,支持在队尾插入和队头删除操作。文章介绍了顺序队列与链式队列,并重点分析了循环队列的实现方式以解决溢出问题。通过具体代码示例(如`enqueue`入队和`dequeue`出队),展示了队列的操作逻辑,帮助读者深入理解其工作机制。
280 1
|
6月前
|
存储 安全 Java
Java 集合面试题从数据结构到 HashMap 源码剖析详解及长尾考点梳理
本文深入解析Java集合框架,涵盖基础概念、常见集合类型及HashMap的底层数据结构与源码实现。从Collection、Map到Iterator接口,逐一剖析其特性与应用场景。重点解读HashMap在JDK1.7与1.8中的数据结构演变,包括数组+链表+红黑树优化,以及put方法和扩容机制的实现细节。结合订单管理与用户权限管理等实际案例,展示集合框架的应用价值,助你全面掌握相关知识,轻松应对面试与开发需求。
339 3
|
8月前
|
存储 Java 编译器
Java 中 .length 的使用方法:深入理解 Java 数据结构中的长度获取机制
本文深入解析了 Java 中 `.length` 的使用方法及其在不同数据结构中的应用。对于数组,通过 `.length` 属性获取元素数量;字符串则使用 `.length()` 方法计算字符数;集合类如 `ArrayList` 采用 `.size()` 方法统计元素个数。此外,基本数据类型和包装类不支持长度属性。掌握这些区别,有助于开发者避免常见错误,提升代码质量。
829 1
|
11月前
|
存储 Java 开发者
【潜意识Java】深入详细理解分析Java中的toString()方法重写完整笔记总结,超级详细。
本文详细介绍了 Java 中 `toString()` 方法的重写技巧及其重要
617 10
【潜意识Java】深入详细理解分析Java中的toString()方法重写完整笔记总结,超级详细。
|
10月前
|
DataX
☀☀☀☀☀☀☀有关栈和队列应用的oj题讲解☼☼☼☼☼☼☼
### 简介 本文介绍了三种数据结构的实现方法:用两个队列实现栈、用两个栈实现队列以及设计循环队列。具体思路如下: 1. **用两个队列实现栈**: - 插入元素时,选择非空队列进行插入。 - 移除栈顶元素时,将非空队列中的元素依次转移到另一个队列,直到只剩下一个元素,然后弹出该元素。 - 判空条件为两个队列均为空。 2. **用两个栈实现队列**: - 插入元素时,选择非空栈进行插入。 - 移除队首元素时,将非空栈中的元素依次转移到另一个栈,再将这些元素重新放回原栈以保持顺序。 - 判空条件为两个栈均为空。
|
11月前
|
前端开发 JavaScript Java
Java构建工具-maven的复习笔记【适用于复习】
这篇文档由「潜意识Java」创作,主要介绍Maven的相关知识。内容涵盖Maven的基本概念、作用、项目导入步骤、依赖管理(包括依赖配置、代码示例、总结)、依赖传递、依赖范围以及依赖的生命周期等七个方面。作者擅长前端开发,秉持“得之坦然,失之淡然”的座右铭。期待您的点赞、关注和收藏,这将是作者持续创作的动力! [个人主页](https://blog.csdn.net/weixin_73355603?spm=1000.2115.3001.5343)
379 3
|
存储 缓存 安全
Java 集合江湖:底层数据结构的大揭秘!
小米是一位热爱技术分享的程序员,本文详细解析了Java面试中常见的List、Set、Map的区别。不仅介绍了它们的基本特性和实现类,还深入探讨了各自的使用场景和面试技巧,帮助读者更好地理解和应对相关问题。
196 5
|
存储 算法
非递归实现后序遍历时,如何避免栈溢出?
后序遍历的递归实现和非递归实现各有优缺点,在实际应用中需要根据具体的问题需求、二叉树的特点以及性能和空间的限制等因素来选择合适的实现方式。
307 59
|
6月前
|
编译器 C语言 C++
栈区的非法访问导致的死循环(x64)
这段内容主要分析了一段C语言代码在VS2022中形成死循环的原因,涉及栈区内存布局和数组越界问题。代码中`arr[15]`越界访问,修改了变量`i`的值,导致`for`循环条件始终为真,形成死循环。原因是VS2022栈区从低地址到高地址分配内存,`arr`数组与`i`相邻,`arr[15]`恰好覆盖`i`的地址。而在VS2019中,栈区先分配高地址再分配低地址,因此相同代码表现不同。这说明编译器对栈区内存分配顺序的实现差异会导致程序行为不一致,需避免数组越界以确保代码健壮性。
140 0
栈区的非法访问导致的死循环(x64)

热门文章

最新文章