LeetCode高频题:链表(四)

我们讲解的题目都是leetcode上经典的题目,而且我们的解答一定也是最简单最经典的。今天带来两道关于链表的经典题。本期所用的链表的数据结构均如下:

public class ListNode {
     int val;
     ListNode next;
     ListNode(int x) { val = x; }
}

143. 重排链表

题意:给定一个单链表 L:L0→L1→…→Ln-1→Ln ,
将其重新排列后变为: L0→Ln→L1→Ln-1→L2→Ln-2→…
你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。如图:
在这里插入图片描述

这道题是leetcode经典的绕来绕去的题目,我们来看一下粉丝投稿的代码是怎么样的:

ListNode tailNode=null;
public void reorderList(ListNode head) {
    if(head==null)
        return;
    ListNode firstNode = head;
    ListNode lastNode = head;
    while (lastNode.next != tailNode)
        lastNode = lastNode.next;
    tailNode=lastNode;
    if(firstNode==lastNode){
        firstNode.next=null;
        return;
    }
    ListNode afterNode = firstNode.next;
    firstNode.next = lastNode;
    lastNode.next = afterNode;
    reorderList(afterNode);
}

还不错是不是,用了递归的思想,每一次处理第一个结点和最后一个结点,然后递归操作。边界条件也处理的很棒,用了一个tailNode巧妙的找到了最后一个结点,很棒。但是有没有更直观的解答呢?有!

以1->2->3->4->5->6为例,我们先找到中间的结点3,然后反转3后面的链表变成1->2->3->6->5->4。然后依次连接1->6,2->5,3->4,就可以了。我们来看代码:

 public void reorderList2(ListNode head) {
    if (head == null) {
        return;
    }
    // 找到中间的结点
    ListNode slow = head,fast=head;
    while (fast != null && fast.next != null) {
        slow = slow.next;
        fast = fast.next.next;
    }
    //反转后面的结点  1->2->3->4->5->6 to 1->2->3->6->5->4
    ListNode head2 = reverse(slow.next);
    slow.next = null;
    //开始一个一个连接  1->2->3->6->5->4 to 1->6->2->5->3->4
    while (head != null && head2 != null) {
        ListNode tmp1 = head.next;
        ListNode tmp2 = head2.next;
        head2.next = head.next;
        head.next = head2;
        head = tmp1;
        head2 = tmp2;
    }
}
private ListNode reverse(ListNode n) {
    ListNode prev = null;
    ListNode cur = n;
    while (cur != null) {
        ListNode tmp = cur.next;
        cur.next = prev;
        prev = cur;
        cur = tmp;
    }
    return prev;
}

这一种方法是不是清晰很多,虽然代码更长,但是更好理解,可以在纸上演算。

148. 排序链表

题意:在 O(n log n) 时间复杂度和常数级空间复杂度下,对链表进行排序。如图:
在这里插入图片描述

这道题跟排序有关,但是规定了复杂度。O(n log n)复杂度的排序大家能想到几个?快排?归并?还是……下面我们用归并的思想做一下,代码如下:

public ListNode sortList(ListNode head) {
    if (head == null || head.next == null)
        return head;
    // step 1. cut the list to two halves
    ListNode prev = null, slow = head, fast = head;
    while (fast != null && fast.next != null) {
        prev = slow;
        slow = slow.next;
        fast = fast.next.next;
    }
    prev.next = null;
    // step 2. sort each half
    ListNode l1 = sortList(head);
    ListNode l2 = sortList(slow);
    // step 3. merge l1 and l2
    return merge(l1, l2);
}
ListNode merge(ListNode l1, ListNode l2) {
    ListNode l = new ListNode(0), p = l;
    while (l1 != null && l2 != null) {
        if (l1.val < l2.val) {
            p.next = l1;
            l1 = l1.next;
        } else {
            p.next = l2;
            l2 = l2.next;
        }
        p = p.next;
    }
    if (l1 != null)
        p.next = l1;
    if (l2 != null)
        p.next = l2;
    return l.next;
}

我们可以看到merge部分就是合并两个有序链表的过程。

明天给大家带来链表的最后部分,链表和树结合的题目。再之后,应粉丝的要求,我们开始讲解动态规划的题目。

关注公众号,更多算法知识点告诉你。
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值