常用数据结构算法之链表

本文深入解析链表数据结构,涵盖单链表与双链表的基本概念,以及多种链表操作如删除节点、判断环的存在、反转链表、移除特定元素和奇偶链表重组的算法实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

同数组一样,链表也是一种线性数据结构,不同的是它通过引用字段将所有分离的元素链接在一起。

在操作上,链表通常可以O(1)的时间复杂度完成插入和删除元素的操作,同时它不像数组,无法通过索引立即访问任意元素,只能通过头结点遍历的方式来访问,所以通常访问元素是O(n)时间复杂度。 这两点和数组正好相反。

有两种常用的链表:单链表双链表。

1、单链表定义

每个结点包含自身的值和到下个结点的引用。

以下是python中链表结点的类定义

class Node(object):
    def __init__(self, x):
        self.val = x
        self.next = None

例题一:删除链表中的节点 (题目来源 leetcode 237题)

请编写一个函数,使其可以删除某个链表中给定的(非末尾)节点,你将只被给定要求被删除的节点。

现有一个链表 -- head = [4,5,1,9],它可以表示为:

输入: head = [4,5,1,9], node = 5
输出: [4,1,9]
解释: 给定你链表中值为 5 的第二个节点,那么在调用了你的函数之后,该链表应变为 4 -> 1 -> 9.

这里的注意点是,只给了要删除的节点。

刚看到这个题目是有点懵,因为我们一般情况是给了链表的头节点,要删除某个节点时,只需从头部遍历到要删除节点的前一个节点,然后修改next指向,便可完成操作。所以这道题是有点tricky的。

def deleteNode(self, node):
        """
        :type node: ListNode
        :rtype: void Do not return anything, modify node in-place instead.
        """
        node.val = node.next.val
        node.next = node.next.next

其实这里思路转换了下,既然无法通过常规方法删除节点,我们通过把要删除节点的下个节点值向前覆盖当前节点,通过修改指针指向下个节点的下个节点,就实现了下个节点前移覆盖当前节点的操作,实际上就是等于删除了 node节点

 

例题二:判断链表中是否存在环

解法两种,一种是借助哈希表 但这种空间复杂度O(N)时间复杂度也是O(N),一种是经典的快慢指针 技巧 ,不需要额外的空间

class Solution(object):
    def hasCycle(self, head):
        """
        :type head: ListNode
        :rtype: bool
        """
        slow = quick = head
        while quick and quick.next:
            slow = slow.next
            quick = quick.next.next
            if quick == slow:
                return True
        return False

例题三,环形链表 II

给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null

为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。

说明:不允许修改给定的链表。

这道题,同样有两种常用解法,和上题类似,第一种解法,借助哈希表,但空间复杂度高,第二种用快慢指针和一个小技巧。

public class Solution {
    //方法一 
    // public ListNode detectCycle(ListNode head) {
    //     Set<ListNode> sets = new HashSet<ListNode>();
    //     ListNode node = head;
    //     while(node!=null)
    //     {
    //         if(sets.contains(node))
    //             return node;
    //         sets.add(node);
    //         node = node.next;
    //     }
    //     return null;
    // }
    
    ///方法二
    public ListNode detectCycle(ListNode head) {
        ListNode quick=head,slow=head;
        boolean flag = false;
        while(quick!=null && quick.next!=null)
        {
            quick = quick.next.next;
            slow = slow.next;
            if(quick == slow)
            {
                flag = true;
                break;
            }
        }
        if(flag == true)
        {
            ListNode p = head;
            while(p!=slow)
            {
                p=p.next;
                slow= slow.next;
            }
            return p;
        }
        else
            return null;
    }
    

}

上面是Java代码,python代码类似,第一种方法,HashSet 换成 集合set即可,由于保存了每个遍历的节点,所以当存在环时,只要再次出现访问过的节点,就是环的入口位置。

第二种方法,先用快慢指针判断是否存在环,如果存在,再用一个指针从头开始,和慢指针同步向前移动,当两者重合时,即是环的入口位置。

题目四:反转一个单链表。

示例:

输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL
class Solution:
    def reverseList(self, head: ListNode) -> ListNode:
        if head == None:
            return head
        prev,current,next = None,head,None
        while current:
            next = current.next
            current.next = prev
            prev = current
            current = next
        return prev

核心思想就是使用三个指针,遍历一遍链表,逐个去翻转,每次翻转前要保存后下个节点。

题目5: 移除链表元素 leetcode 203

删除链表中等于给定值 val 的所有节点。

示例:

输入: 1->2->6->3->4->5->6, val = 6
输出: 1->2->3->4->5
def removeElements(self, head: ListNode, val: int) -> ListNode:
        if head == None:
            return head
       
        while head:
            if head.val == val:
                head = head.next
            else:
                break
                
        tmp = head
        while tmp.next:
            if tmp.next.val == val:
                tmp.next = tmp.next.next
            else:
                tmp = tmp.next
        
        return head

主要是考虑头元素是 val的情况

 

题目5:奇偶链表 (Leetcode 328)

给定一个单链表,把所有的奇数节点和偶数节点分别排在一起。请注意,这里的奇数节点和偶数节点指的是节点编号的奇偶性,而不是节点的值的奇偶性。

请尝试使用原地算法完成。你的算法的空间复杂度应为 O(1),时间复杂度应为 O(nodes),nodes 为节点总数。

示例 1:

输入: 1->2->3->4->5->NULL
输出: 1->3->5->2->4->NULL

这道题还是有点考察逻辑思维和链表的掌握程度的。

def oddEvenList(self, head: ListNode) -> ListNode:
        if head == None or head.next == None or head.next.next == None:
            return head
        odd,even,evenhead = head,head.next,head.next
        while even and even.next:
            odd.next = even.next
            odd = odd.next
            
            even.next = odd.next
            even = even.next
        odd.next = evenhead
        
        return head

核心思想是奇数位置的下一位肯定是偶数,偶数的下一个位置肯定是奇数,每次奇奇连接,或偶偶连接后,要把odd 指针或even指针移动到奇队列末尾 或 偶队列末尾,遍历结束后分成了奇链表和偶链表,最后奇链表的末尾连接到偶链表的首节点 即可。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值