链表经典操作与实战

简介: 文章深入探讨了链表的基本概念、分类和经典操作,通过LeetCode上的实战题目展示了如何应用虚拟头节点、双指针法、定位前驱节点等技巧来解决链表相关问题,并强调了算法在软件开发中的重要性。

一、前言

算法是计算机软件的基础,今年打算深入学习一些算法,记录一些算法理论以及最佳实践,希望可以坚持下去。

二、链表定义与分类

链表是通过指针关联数据节点的一种存储结构。

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; }
  }

根据指针方向可以分为单向链表、双向链表以及环形链表。

  • 单向链表

截屏2024-01-15 22.34.42.png

  • 双向链表

截屏2024-01-15 22.34.55.png

  • 环形链表

截屏2024-01-15 22.35.42.png

三、链表的几类经典操作

  • 设置虚拟头节点

第一点,通过给链表设置虚拟头节点,可以保证头节点动作和其他非头节点操作逻辑一致。

比如下面例子,我们需要删除头节点1,我们只需要使用虚拟头节点指向头节点的next节点即可。

截屏2024-01-15 22.47.34.png 和假如我们需要删除第二个节点,那么会使用下面的方法,这个操作逻辑和上面逻辑一致。这就是使用虚拟头节点非常重要的一个好处。

截屏2024-01-15 22.47.58.png

第二点,通过设置虚拟头节点,我们处理完链表之后还能方便取到头节点。只需要使用dumy.next即可获取到头节点。

  • 双指针法遍历链表

双指针法遍历链表对反转链表非常有用。比如有两个指针遍历下面链表。

  • 定位前驱节点

这个操作是一个经验,我们需要对某个节点P执行删除或者修改时,我们先找他他的前驱节点,这样操作节点P更加方便。

截屏2024-01-15 22.56.37.png

上面找到了节点P的前驱节点Prev,使用节点Prev操作节点P会更加便利。

  • 遇到相交的链表,相交后的链表路径相同

四、热点链表题目实战

leetCode206. 反转链表

这个题目就是双指针遍历链表的经典应用。一个前驱节点(用于当前节点反转指向的新节点),一个当前节点(需要反转的节点)

/**
 * 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 reverseList(ListNode head) {
   
   

        ListNode prev = null; //前驱节点
        ListNode curr = head; //当前节点

        while(curr != null) {
   
   

           ListNode temp = curr.next;
           //反转
           curr.next = prev;
           //当前节点变成前驱节点
           prev = curr;
           //当前节点指向下一个需要反转的节点
           curr = temp;
        }
        return prev;
    }
}

leetcode两两翻转链表

/**
 * 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 swapPairs(ListNode head) {
   
   
        if(head == null || head.next == null) {
   
   
            return head;
        }

        //虚拟头节点指向头节点
        ListNode dumy = new ListNode();
        dumy.next = head;
        //当前节点指向虚拟节点,next,next.next是需要操作的节点
        ListNode curr = dumy;

        //执行两两交换
        while(curr.next != null && curr.next.next != null) {
   
   

            ListNode temp = curr.next;
            ListNode temp1 = curr.next.next;
            ListNode temp3 = curr.next.next.next;

            //执行两两翻转
            temp1.next =temp;
            curr.next = temp1;
            temp.next = temp3;

            //curr重新移动两个位置
            curr = curr.next.next;
        }

        return dumy.next;
    }
}

Curr指针指向需要翻转节点的前驱节点。 截屏2024-01-15 23.35.35.png

执行3步完成翻转。 截屏2024-01-15 23.34.16.png

这道题目使用到了虚拟头节点定位到前驱节点经典操作,对于解题提供了极大的便利。

leetcode 面试题 02.07. 链表相交

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
   
   
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
   
   
        //如果相交,那么后面的节点和长度是一致的。
        ListNode currA = headA;
        int lenA = 0;
        while(currA != null) {
   
   
            lenA++;
            currA = currA.next;
        }

        ListNode currB = headB;
        int lenB = 0;
        while(currB != null) {
   
   
            lenB++;
            currB = currB.next;
        }

        if(lenA > lenB) {
   
   
         int gap =   lenA - lenB;
         while(gap > 0) {
   
   
             headA = headA.next;
             gap--;
         }
        } else {
   
   
            int gap =   lenB - lenA;
         while(gap > 0) {
   
   
             headB = headB.next;
             gap--;
         }

        }

        if(headA == headB) {
   
   
            return headA;
        }

        while(headA != null) {
   
   
             headA = headA.next;
             headB = headB.next;

             if(headA == headB) {
   
   
                 return headA;
             }

         }

         return null;

    }
}

这道题目解题的关键依据是遇到相交的链表,相交后的链表路径相同

2024加油,深入理解算法,学习算法,一起学习。

image.png

相关文章
|
6月前
|
存储 缓存 算法
链表全景:探索单链表、双向链表与循环链表【实战演练】
链表全景:探索单链表、双向链表与循环链表【实战演练】
75 3
|
2月前
|
存储 算法 C语言
C语言手撕实战代码_循环单链表和循环双链表
本文档详细介绍了用C语言实现循环单链表和循环双链表的相关算法。包括循环单链表的建立、逆转、左移、拆分及合并等操作;以及双链表的建立、遍历、排序和循环双链表的重组。通过具体示例和代码片段,展示了每种算法的实现思路与步骤,帮助读者深入理解并掌握这些数据结构的基本操作方法。
|
4月前
|
存储 数据管理 C语言
C语言实战 | 使用链表完成“贪吃蛇”游戏
【7月更文挑战第1天】整体思维,即系统思维,强调以整体视角理解事物。在编程中,结构体体现这种思想,将相关变量打包处理。示例展示了如何用链表而非数组实现“贪吃蛇”游戏,链表提供了更灵活的动态数据管理。一系列代码图片详细描绘了链表结构体在游戏中的应用,包括节点定义、移动、碰撞检测等,凸显了使用链表的优势和代码的清晰组织。
44 0
C语言实战 | 使用链表完成“贪吃蛇”游戏
|
6月前
|
存储
🚀链表理论:基础概念与实战技巧!
【2月更文挑战第8天】
159 43
|
6月前
|
算法 Java
快慢指针该如何操作?本文带你认识快慢指针常见的三种用法及在链表中的实战
快慢指针该如何操作?本文带你认识快慢指针常见的三种用法及在链表中的实战
101 0
|
算法
​LeetCode刷题实战426:将二叉搜索树转化为排序的双向链表
算法的重要性,我就不多说了吧,想去大厂,就必须要经过基础知识和业务逻辑面试+算法面试。所以,为了提高大家的算法能力,这个公众号后续每天带大家做一道算法题,题目就从LeetCode上面选 !
275 0
​LeetCode刷题实战426:将二叉搜索树转化为排序的双向链表
|
算法
​LeetCode刷题实战237:删除链表中的节点
算法的重要性,我就不多说了吧,想去大厂,就必须要经过基础知识和业务逻辑面试+算法面试。所以,为了提高大家的算法能力,这个公众号后续每天带大家做一道算法题,题目就从LeetCode上面选 !
111 0
​LeetCode刷题实战237:删除链表中的节点
|
存储 算法 索引
​LeetCode刷题实战141: 环形链表
算法的重要性,我就不多说了吧,想去大厂,就必须要经过基础知识和业务逻辑面试+算法面试。所以,为了提高大家的算法能力,这个公众号后续每天带大家做一道算法题,题目就从LeetCode上面选 !
102 0
​LeetCode刷题实战141: 环形链表
|
算法 索引
​LeetCode刷题实战138:复制带随机指针的链表
算法的重要性,我就不多说了吧,想去大厂,就必须要经过基础知识和业务逻辑面试+算法面试。所以,为了提高大家的算法能力,这个公众号后续每天带大家做一道算法题,题目就从LeetCode上面选 !
97 0
​LeetCode刷题实战138:复制带随机指针的链表
|
算法 安全 Java
【Java数据结构及算法实战】系列010:Java队列04——链表实现的阻塞队列LinkedBlockingQueue
【Java数据结构及算法实战】系列010:Java队列04——链表实现的阻塞队列LinkedBlockingQueue
140 0