数据结构和算法(三)——链表及其相关算法

链表基础知识

简介

链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。

链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针。

链表插入删除的时间复杂度为 O(1),而随机访问的时间复杂度为 O(n)。

单向链表

如图(图片来源极客时间的《数据结构与算法之美》专栏)

在这里插入图片描述

双向链表

如图(图片来源极客时间的《数据结构与算法之美》专栏)

在这里插入图片描述

循环链表

如图(图片来源极客时间的《数据结构与算法之美》专栏)

在这里插入图片描述

双向循环链表

如图(图片来源极客时间的《数据结构与算法之美》专栏)

在这里插入图片描述

常见算法思路

哨兵指针

示例1:《剑指offer》中的算法题 删除链表中重复的结点

在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头
指针。 例如,链表1->2->3->3->4->4->5 处理后为 1->2->5

有关链表的算法一般都比较简单,但是要注意边界条件。以示例1为例,要注意可能的特殊情况有:

  1. 链表头重复:1->1->1->1->2时处理后就为 2
  2. 链表全部重复:1->1->1->1->1时处理后就为 null

如图,增加一个哨兵结点;不管出现情况1还是情况2,都通过哨兵结点的 next 指针获取结果。
在这里插入图片描述

代码如下

/*
 public class ListNode {
    int val;
    ListNode next = null;
 
    ListNode(int val) {
        this.val = val;
    }
}
*/
public class Solution {
    
    
    public ListNode deleteDuplication(ListNode pHead)
    {
    
    
       if(pHead == null)return null;
       ListNode root = new ListNode(-1);//充当哨兵结点
       root.next = pHead;
       ListNode preNode = root;
       ListNode nowNode = root.next;
       while(nowNode != null&&nowNode.next != null){
    
    
           ListNode nextNode = nowNode.next;
           if(nextNode.val == nowNode.val){
    
    
               while(nextNode != null&&nextNode.val == nowNode.val){
    
    
                   nextNode = nextNode.next;
               }
               preNode.next = nextNode;
               nowNode = nextNode;
           }else{
    
    
               preNode.next = nowNode;
               preNode = preNode.next;
               nowNode = nowNode.next;
           }
       }
       return root.next;
    }
}

快慢指针

快慢指针的思路在链表相关的算法中很常见,它的思路就是利用快慢两个指针来遍历链表,来完成我们所需要的操作。

示例2:利用快慢指针来查找链表的中间结点

给定一个带有头结点 head 的非空单链表,返回链表的中间结点。
如果有两个中间结点,则返回第二个中间结点。

假设,链表长度为 n ,快指针的速度为 2个/次;而慢指针的速度为 1个/次。则有小学数学可以知道,当快指针到链表尾时,慢指针则到链表中点处。如果还不理解,可以假设 n = 10 或 9等数据来验证。

代码如下

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    
    
    public ListNode middleNode(ListNode list) {
    
    
            if (list == null)return null;
            ListNode fast = list;
            ListNode slow = list;
            while (fast != null){
    
    
                fast = fast.next;
                if (fast == null)break;
                fast = fast.next;
                slow = slow.next;
            }
            return slow;
    }
}

示例3:利用快慢指针来查找链表中环的入口结点

给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,输出null。

该算法题的解决思路要分两步:

第一步:判断是否存在环

在这里插入图片描述

快指针的速度为 2个/次;而慢指针的速度为 1个/次。它们从 A 点同时出发,如果链表中存在环,则快慢指针必定在 C 点相遇(C点为环内任意一个结点);如果,不存在环,则快结点为 null。代码如下:

        ListNode fast=pHead;
        ListNode low=pHead;
        while(fast!=null&&fast.next!=null){
    
    
            fast=fast.next.next;
            low=low.next;
            if(fast==low)
                break;
        }
        if(fast==null)//判断是否存在环
            return null;

第二步:找到链表中环的入口结点

在这里插入图片描述

相遇时:

快结点走过的路径为:AB + k*(BC+CB) + BC,(其中 k >= 1,k为整数)

慢结点走过的路径为:AB + BC

由于快指针的速度为慢结点的两倍,则 2*(AB + BC) = AB + K*(BC+CB) + BC,即AB = (k-1)(BC+CB) + CB。由于 k-1 >= 0,所以两个指针分别从 A 和 C 点出发一定会在 B 点相遇。

完整代码如下

/*
 public class ListNode {
    int val;
    ListNode next = null;

    ListNode(int val) {
        this.val = val;
    }
}
*/
public class Solution {
    
    
    public ListNode EntryNodeOfLoop(ListNode pHead)
    {
    
    
        ListNode fast=pHead;
        ListNode low=pHead;
        while(fast!=null&&fast.next!=null){
    
    
            fast=fast.next.next;
            low=low.next;
            if(fast==low)
                break;
        }
        if(fast==null||fast.next==null)//判断是否存在环
            return null;
        low=pHead;
        while(fast!=low){
    
    
            fast=fast.next;
            low=low.next;
        }
        return low;
    }
}

必须掌握的代码实现

  • 实现单链表、循环链表、双向链表,支持增删操作
  • 实现单链表反转
  • 实现两个有序的链表合并为一个有序链表
  • 实现求链表的中间结点
  • 链表中环的检测
  • 删除链表倒数第n个结点

面试常考的与链表相关的算法题

参考

  • leetcode
  • 牛客网
  • 《剑指offer》
  • 极客时间的《数据结构与算法之美》专栏

猜你喜欢

转载自blog.csdn.net/lichukuan/article/details/127063354