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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值