LeetCode刷题系列(七)Linked List

  本篇是有关Linked List的几道相关题型,涉及链表的一些基本的操作和技巧,这些之前的blog也有提到过。

Sort List

  题目为把一个链表进行排序,要求时间复杂度为O(nlgn)。链表我们一般无法使用快排,因为它进行随机访问太耗时。回忆之前提到过的Merge Two Sorted List的解法,发现我们将链表一分为二然后再合并就好了,但是这里合并的两个链表都必须是有序的,因此我们需要使用分治法来解决来问题。

private ListNode findMiddle(ListNode head) {
    ListNode slow = head, fast = head.next;
    while (fast != null && fast.next != null) {
        fast = fast.next.next;
        slow = slow.next;
    }
    return slow;
}    

private ListNode merge(ListNode head1, ListNode head2) {
    ListNode dummy = new ListNode(0);
    ListNode tail = dummy;
    while (head1 != null && head2 != null) {
        if (head1.val < head2.val) {
            tail.next = head1;
            head1 = head1.next;
        } else {
            tail.next = head2;
            head2 = head2.next;
        }
        tail = tail.next;
    }
    if (head1 != null) {
        tail.next = head1;
    } else {
        tail.next = head2;
    }

    return dummy.next;
}

public ListNode sortList(ListNode head) {
    if (head == null || head.next == null) {
        return head;
    }

    ListNode mid = findMiddle(head);

    ListNode right = sortList(mid.next);
    mid.next = null;
    ListNode left = sortList(head);

    return merge(left, right);
}

  其中,sortList即为分治法,我们将链表一分为二,然后合并。

Partition List

  题目是给定一个目标值和一个链表,将链表中数值小于目标值的节点移到左侧,将数值大于目标值的节点移到右侧(该目标值节点的右侧)。此题目如之前所介绍,由于我们不知道头结点会是哪个元素,因此我们需要设置dummynode作为Head。另外,从题目可以看出我们将链表中的节点按目标值分成两个链表,之后直接接在一起即可。

  public ListNode partition(ListNode head, int x) {
    if (head == null) {
        return null;
    }

    ListNode leftDummy = new ListNode(0);
    ListNode rightDummy = new ListNode(0);
    ListNode left = leftDummy, right = rightDummy;

    while (head != null) {
        if (head.val < x) {
            left.next = head;
            left = head;
        } else {
            right.next = head;
            right = head;
        }
        head = head.next;
    }

    right.next = null;
    left.next = rightDummy.next;
    return leftDummy.next;
}

Reorder List

  题目:Given a singly linked list L: L0→L1→…→Ln-1→Ln, reorder it to: L0→Ln→L1→Ln-1→L2→Ln-2→… You must do this in-place without altering the nodes’ values. For example, Given {1,2,3,4}, reorder it to {1,4,2,3}.

  这道题目的思路是,首先将链表从中间分开(可用之前的findMiddle函数),然后将从中间节点开头的链表倒转(可用之前的reverse函数),最后将他们merge在一起,这个merge区别于之前排序时使用的merge,比他简单很多,仅交替节点相接即可。这里仅给出merge的代码:

private void merge(ListNode head1, ListNode head2) {
    int index = 0;
    ListNode dummy = new ListNode(0);
    while (head1 != null && head2 != null) {
        if (index % 2 == 0) {
            dummy.next = head1;
            head1 = head1.next;
        } else {
            dummy.next = head2;
            head2 = head2.next;
        }
        dummy = dummy.next;
        index ++;
    }
    if (head1 != null) {
        dummy.next = head1;
    } else {
        dummy.next = head2;
    }
}

Linked List Cycle

  题目为判断一个链表中是否有环。此题可以借鉴之前findMiddle的代码,用一个一次走两步的fast节点和一个一次仅走一步的slow节点。

public Boolean hasCycle(ListNode head) {
    if (head == null || head.next == null) {
        return false;
    }

    ListNode fast, slow;
    fast = head.next;
    slow = head;
    while (fast != slow) {
        if(fast==null || fast.next==null)
            return false;
        fast = fast.next.next;
        slow = slow.next;
    } 
    return true;
}

Linked List Cycle II

  此题目在Linked List Cycle基础上,要返回环开始的节点。因此,可根据之前代码的基础上做一些修改。根据之前代码分析:如果存在环,fast节点要比slow节点多走一圈,那么再进一步想,slow现在的位置到环开始处的节点的距离其实就是头结点到环开始处的节点的距离。

public ListNode detectCycle(ListNode head) {
    if (head == null || head.next==null) {
        return null;
    }

    ListNode fast, slow;
    fast = head.next;
    slow = head;
    while (fast != slow) {
        if(fast==null || fast.next==null)
            return null;
        fast = fast.next.next;
        slow = slow.next;
    } 

    while (head != slow.next) {
        head = head.next;
        slow = slow.next;
    }
    return head;
}
### LeetCode入门与基础知识 对于初学者而言,在LeetCode的主要目的是通过实际操作来巩固和加深对数据结构和算法的理解[^1]。因此,了解一些基础概念以及合理的学习路径是非常重要的。 #### 数据结构基础 以下是几种常见的数据结构及其应用场景: - **数组 (Array)** 数组是最简单的线性数据结构之一,支持随机访问元素。它适用于需要频繁读取固定位置元素的场景[^2]。 - **链表 (Linked List)** 链表是一种动态分配内存的数据结构,适合于频繁插入和删除节点的操作。单向链表、双向链表和循环链表是其常见变体。 - **栈 (Stack)** 和 **队列 (Queue)** 栈遵循后进先出(LIFO)原则,常用于括号匹配等问;队列则采用先进先出(FIFO),可用于广度优先搜索(BFS)。 - **哈希表 (Hash Table)** 哈希表提供快速查找功能,时间复杂度通常为O(1)。它是解决两数之和(Two Sum)这类问的核心工具。 - **树 (Tree)** 包括二叉树、平衡树(如AVL树)、红黑树(Red-Black Tree)、B/B+树等。它们广泛应用于文件系统索引、数据库查询优化等领域。 - **Trie树 (Prefix Tree)** Trie树特别擅长处理字符串前缀相关的问,例如字典单词检索或自动补全功能实现。 #### 算法初步 除了熟悉上述各种数据结构外,还需掌握若干经典算法: - 排序算法:冒泡排序、选择排序、插入排序属于简单但效率较低的方法;而快速排序(Quick Sort)、归并排序(Merge Sort)则是更高效的替代方案。 ```python def quick_sort(arr): if len(arr) <= 1: return arr else: pivot = arr[len(arr)//2] left = [x for x in arr if x < pivot] middle = [x for x in arr if x == pivot] right = [x for x in arr if x > pivot] return quick_sort(left)+middle+quick_sort(right) ``` - 查找算法:二分查找(Binary Search)是在有序列表中高效定位目标项的有效手段。 - 动态规划(Dynamic Programming, DP): 解决最优化问的强大技术,核心在于状态转移方程的设计。 #### 学习策略 制定合理的计划有助于提高学习效果: - 结合理论知识与实战练习同步推进; - 定期回顾已学内容以强化记忆; - 参考高质量资料如《LeetCode开源手册》获取更多技巧[^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值