zz: LeetCode 阶段性总结(一)

来源:互联网 发布:c语言的头文件在哪里 编辑:程序博客网 时间:2024/05/17 21:43

原文转自:http://www.yclod.com/leetcode-jie-duan-xing-zong-jie/


前段时间忙着准备面试找工作,抽空做了LeetCode上的题,由于时间的原因没有全部做完,而是根据“二爷”的难度表从各种分类里面挑着做了一些。后来给我的小伙伴们总结性的share了一下。主要内容包括3个部分:LinkedList, DFS/BFS, DP.

现在记录一些我总结出来的解题思路。

LinkedList

思路: 链表的题应该算是比较容易的题,因为数据结构较为单一,所以可想考点不多。那么最容易考什么呢?这就要从链表最大的特色说起 -- “没有Index”。因为链表没有index,一般不能直接寻址,所以一些在数组(直接寻址)上很容易做到的操作,在链表上会有更高的时间开销。比如经典题,寻找倒数第N个元素等。所以考点就在于如何利用一些方法,来尽可能小的减小时间和空间的开销(当你发现你的算法需要不止一次遍历链表或者需要额外存储O(n)以上的空间时,就至少应该考虑考虑有没有方法可以优化了)。其中最常见的方法是:

2个指针 : 一快一慢,一远一近,一前一后

这个方法,就是通过建立具有一定关系的两个(或更多)指针,用来减少遍历。

典型例题如:

1. Remove Nth Node From End of List

Given a linked list, remove the nth node from the end of list and return its head.

For example,

Given linked list: 1->2->3->4->5, and n = 2.

After removing the second node from the end, the linked list becomes 1->2->3->5. Note: 
Given n will always be valid. 
Try to do this in one pass.

这道题建立2个指针,一前一后,相距n的距离,然后一起移动,当后者为空(遍历完链表),前者即为所求。

public class Solution {    public ListNode removeNthFromEnd(ListNode head, int n) {        if(head == null) return null;        if(n <= 0) return head;        ListNode preHead = new ListNode(-1);        preHead.next = head;        ListNode p1  = head;        ListNode p2 = p1;        ListNode preP1 = preHead;        while(n-- > 0){            p2 = p2.next;        }        while(p2 != null){            preP1 = p1;            p1 = p1.next;            p2 = p2.next;        }        preP1.next = p1.next;        return preHead.next;    }}

2. Linked List Cycle I / II

I. Given a linked list, determine if it has a cycle in it.

II. Given a linked list, return the node where the cycle begins. If there is no cycle, return null.

Follow up: Can you solve it without using extra space?

这道题第一问是要检查一个单链表中是否有环,这个问题乍一看需要用到O(n)的额外空间来记录之前遍历过的节点。此题依然用“两个指针”的思路来优化:建立两个指针,一快一慢,慢的每次走一步,快的每次走两步,那么如果有环,快指针一定会追上慢指针。条件判断一旦快指针全等于慢指针时,必然有环。为什么会相等?会不会永远正好错过不相等?考虑这样的情况:快指针绕了n圈以后出现在慢指针后面,在快要相交的时候,要么在慢指针身后距离2的地方,要么在距离1的地方,如果距离为2,那么在下一次移动时就变成了距离为1.所以总有一个时候,快指针在慢指针后面距离为1的地方,在这个时刻的下一个时刻,就会相等。而不会出现一次次错过的情况。

public class Solution {    public boolean hasCycle(ListNode head) {        if(head == null || head.next == null) return false;        ListNode slowP = head;        ListNode fastP = head.next;        while(slowP != fastP){            if(fastP == null || fastP.next == null) return false;            slowP = slowP.next;            fastP = fastP.next.next;        }        return true;    }}

第二问是返回可能存在环的起点。这一问比较难证明(有兴趣的同学可以证明一下),不过结论还是很好记住的,即:接第一问,当快慢两指针重合的时候,慢指针继续慢慢一步一步向前走,将快指针指向链表的开头,然后也改为很慢指针一样的速度一步一步向前走,当两个指针再次重合的时候,即为所求的环的起点。

public class Solution {    public ListNode detectCycle(ListNode head) {        // IMPORTANT: Please reset any member data you declared, as        // the same Solution instance will be reused for each test case.        if(head == null || head.next == null) return null;        ListNode slowP = head;        ListNode fastP = head.next;        while(slowP != fastP){            if(fastP == null || fastP.next == null) return null;            slowP = slowP.next;            fastP = fastP.next.next;        }        ListNode cur = head;        while(slowP.next !=  cur){            cur = cur.next;            slowP = slowP.next;        }        return cur;    }}

总之,“两个指针”四个字,为解决很多链表题,提供了很好的思路。

DFS / BFS

掌握了哥的GetAdj,剩下的就是狂狂写代码了。

(未完,待续)


0 0