2014年9月30日星期二

[Leetcode] Copy List with Random Pointer

A linked list is given such that each node contains an additional random pointer which could point to any node in the list or null.
Return a deep copy of the list.
一道相对比较复杂的链表题。第一种思路是使用HashMap来存储原来的节点和新生成的节点之间的对应关系,通过这个对应关系来恢复新生成的节点的random所指向的节点。这种方法的空间复杂度为O(n)。
public RandomListNode copyRandomList(RandomListNode head) {
        if(head == null) return head;
        HashMap<RandomListNode, RandomListNode> map = new HashMap<RandomListNode, RandomListNode>();
        RandomListNode dummy = new RandomListNode(0);
        RandomListNode temp1 = head, temp2 = dummy;
        
        while(temp1 != null) {
            RandomListNode node = new RandomListNode(temp1.label);
            map.put(temp1, node);
            temp2.next = node;
            temp2 = temp2.next;
            temp1 = temp1.next;
        }
        temp1 = head; temp2 = dummy.next;
        while(temp1 != null) {
            if(temp1.random != null) {
                temp2.random = map.get(temp1.random);
            }
            temp1 = temp1.next;
            temp2 = temp2.next;
        }
        
        return dummy.next;
    }
另一种方法比较巧妙,通过改变原来的链表,即将新生产的节点插入在原来的节点后面,然后第二遍扫描的时候来恢复random的值,然后第三遍扫描将原来的节点和新生产的节点分离,从而得到一个deep copy原来的链表。

public RandomListNode copyRandomList(RandomListNode head) {
        if(head == null) return head;
        RandomListNode temp = head;
        
        while(temp != null) {
            RandomListNode node = new RandomListNode(temp.label);
            node.next = temp.next;
            temp.next = node;
            temp = node.next;
        }
        temp = head;
        while(temp != null) {
            if(temp.random != null) {
                temp.next.random = temp.random.next;
            }
            temp = temp.next.next;
        }
        RandomListNode nhead = head.next, ntemp = head.next;
        temp = head;
        while(head != null) {
            temp.next = ntemp.next;
            temp = temp.next;
            if(temp == null) break;
            ntemp.next = temp.next;
            ntemp = ntemp.next;
        }
        
        return nhead;
    }

[Leetcode] Remove Duplicates from Sorted List II

Given a sorted linked list, delete all nodes that have duplicate numbers, leaving only distinct numbers from the original list.
For example,
Given 1->2->3->3->4->4->5, return 1->2->5.
Given 1->1->1->2->3, return 2->3.
稍微复杂一点的链表题,牢牢记住,只要涉及删除并且是单链表的,就需要记录可能删除的节点的之前的节点,所以使用三个指针,其中两个用来扫描,另外一个表示目前目前的合法的链表的结尾。尤其注意空指针的情况!需要对两个用来扫描的指针进行判断以免出现空指针的错误。
public ListNode deleteDuplicates(ListNode head) {
        if(head == null || head.next == null) return head;
        ListNode dummy = new ListNode(0);
        dummy.next = head;
        ListNode temp = dummy, temp1 = head, temp2 = head.next;
        
        while(temp2 != null) {
            if(temp1.val == temp2.val) {
                while(temp2 != null && temp2.val == temp1.val) temp2 = temp2.next;
                temp.next = temp2;
                temp1 = temp2;
                if(temp2 != null) temp2 = temp2.next;
            }
            else {
                temp = temp1;
                temp1 = temp2;
                temp2 = temp2.next;
            }
        }
        
        return dummy.next;
    }

[Leetcode] Insertion Sort List

Sort a linked list using insertion sort.
比较简单的题,比较容易实现,按照插入排序的形式写就好了。


public ListNode insertionSortList(ListNode head) {
        if(head == null || head.next == null) {
            return head;
        }
        ListNode dummy = new ListNode(0);
        ListNode temp1 = dummy, temp2 = head, temp3 = head;
        
        while(temp2 != null) {
            temp1 = dummy; temp3 = temp2.next;
            while(temp1.next != null && temp1.next.val < temp2.val) {
                temp1 = temp1.next;
            }
            temp2.next = temp1.next;
            temp1.next = temp2;
            temp2 = temp3;
        }
        
        return dummy.next;
    }

[Leetcode] Add Two Numbers

You are given two linked lists representing two non-negative numbers. The digits are stored in reverse order and each of their nodes contain a single digit. Add the two numbers and return it as a linked list.
Input: (2 -> 4 -> 3) + (5 -> 6 -> 4)
Output: 7 -> 0 -> 8
比较简单的链表题,其实也是模拟题,模拟加法运算的过程,只需要留意最后可能多出来的进位不要忘记加上去就好了。

public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        ListNode dummy = new ListNode(0);
        ListNode temp = dummy;
        int carry = 0, sum = 0;
        
        while(l1 != null && l2 != null) {
            sum = l1.val + l2.val + carry;
            carry = sum / 10;
            ListNode node = new ListNode(sum % 10);
            temp.next = node;
            temp = temp.next;
            l1 = l1.next; l2 = l2.next;
        }
        while(l1 != null) { // only one of these will be excuted
            sum = l1.val + carry;
            carry = sum / 10;
            ListNode node = new ListNode(sum % 10);
            temp.next = node;
            temp = temp.next;
            l1 = l1.next;
        }
        while(l2 != null) {
            sum = l2.val + carry;
            carry = sum / 10;
            ListNode node = new ListNode(sum % 10);
            temp.next = node;
            temp = temp.next;
            l2 = l2.next;
        }
        if(carry > 0) { // important here
            ListNode node = new ListNode(carry);
            temp.next = node;
        }
        
        return dummy.next;
    }

[Leetcode] Flatten Binary Tree to Linked List

Given a binary tree, flatten it to a linked list in-place.
For example,
Given
         1
        / \
       2   5
      / \   \
     3   4   6
The flattened tree should look like:
   1
    \
     2
      \
       3
        \
         4
          \
           5
            \
             6
利用类似Moris遍历的方式来进行处理,记住在将左子树放到右子树之后,将左子树置空。
public void flatten(TreeNode root) {
        TreeNode temp = null;
        
        while(root != null) {
            if(root.left == null) root = root.right;
            else {
                temp = root.left;
                while(temp.right != null) temp = temp.right;
                temp.right = root.right;
                root.right = root.left;
                root.left = null; // important, don't forget
                root = root.right;
            }
        }
        
    }

[Leetcode] 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.
去除某个节点,需要维护改节点前面的节点,因为是单向链表,使用两个指针,其中一个比另一个快n步,这里由给出的n都是合法的,因此不需要特殊考虑,不过在实际中若需要考虑n为不合法的情况,则需要直接返回。
public ListNode removeNthFromEnd(ListNode head, int n) {
        if(head == null || n == 0) return head;
        ListNode dummy = new ListNode(0);
        dummy.next = head;
        
        ListNode temp1 = head;
        while(n > 0) {
            temp1 = temp1.next;
            n--;
        } // since n is valid, there is no need to check here, but should discuss with interviewer. If n is invalid, terminate here
        ListNode temp = dummy, temp2 = head;
        while(temp1 != null) {
            temp1 = temp1.next;
            temp2 = temp2.next;
            temp = temp.next;
        }
        temp.next = temp2.next;
        
        return dummy.next;
    }

2014年9月29日星期一

[Leetcode] Partition List

Given a linked list and a value x, partition it such that all nodes less than x come before nodes greater than or equal to x.
You should preserve the original relative order of the nodes in each of the two partitions.
For example,
Given 1->4->3->2->5->2 and x = 3,
return 1->2->2->4->3->5.
思路很简单,就是merge的逆过程,用两个dummy节点来表示小于x和大于等于x的两部分,然后将两部分链接起来,尤其要注意的地方是要显示的将当前链表的尾部指向null以保证安全。
public ListNode partition(ListNode head, int x) {
        if(head == null || head.next == null) {
            return head;
        }
        ListNode dum1 = new ListNode(0);
        ListNode dum2 = new ListNode(0);
        ListNode temp1 = dum1, temp2 = dum2;
        
        while(head != null) {
            if(head.val < x) {
                temp1.next = head;
                temp1 = temp1.next;
                head = head.next;
                temp1.next = null; // safe
            }
            else {
                temp2.next = head;
                temp2 = temp2.next;
                head = head.next;
                temp2.next = null; // safe
            }
        }
        temp1.next = dum2.next;
        
        return dum1.next;
    }

[Leetcode] Reverse Linked List II

Reverse a linked list from position m to n. Do it in-place and in one-pass.
For example:
Given 1->2->3->4->5->NULLm = 2 and n = 4,
return 1->4->3->2->5->NULL.
Note:
Given mn satisfy the following condition:
1 ≤ m ≤ n ≤ length of list.
链表翻转题,找到第一个需要翻转的地方以及它的前一个节点,然后每往后面走一个,都有头插法插入。注意在插入的时候可能出现当m = n的时候,当前节点的next被赋给了自己而产生了一个环出来,对于这种情况根据不同的实现方式可以用不同的方法排除掉。最后注意m和n的大小关系别写反了。
public ListNode reverseBetween(ListNode head, int m, int n) {
        ListNode dummy = new ListNode(0);
        dummy.next = head;
        ListNode station = dummy, temp1 = head;
        int cnt = m;
        
        while(cnt > 1) { // find the first element to reverse
            temp1 = temp1.next;
            station = station.next;
            cnt--;
        }
        n = n - m;
        ListNode temp2 = temp1.next;
        while(n > 0) {
            temp1.next = temp2.next;
            temp2.next = station.next;
            station.next = temp2;
            temp2 = temp1.next;
            n--;
        }
        
        return dummy.next;
    }

[Leetcode] Swap Nodes in Pairs

Given a linked list, swap every two adjacent nodes and return its head.
For example,
Given 1->2->3->4, you should return the list as 2->1->4->3.
Your algorithm should use only constant space. You may not modify the values in the list, only nodes itself can be changed.
比较简单的链表题,主要考察的是链表翻转,但是是翻转的最简单情况。使用一个dummy节点可以比较方便的管理链表头,在处理链表的尾部的时候,由于我使用了双指针操作,所以尤其要注意到达尾部的时候节点为空或者只有一个节点的情况,这些情况可以看做是corner case和在程序一开始就进行判断的情况一样。
public ListNode swapPairs(ListNode head) {
        if(head == null || head.next == null) {
            return head;
        }
        ListNode dummy = new ListNode(0);
        dummy.next = head;
        ListNode tail = dummy, temp1 = head, temp2 = head.next;
        
        while(temp1 != null) {
            tail.next = temp2;
            temp1.next = temp2.next;
            temp2.next = temp1;
            tail = temp1;
            temp1 = temp1.next;
            if(temp1 == null || temp1.next == null) break;
            temp2 = temp1.next;
        }
        
        return dummy.next;
    }

[Leetcode] Remove Duplicates from Sorted List

Given a sorted linked list, delete all duplicates such that each element appear only once.
For example,
Given 1->1->2, return 1->2.
Given 1->1->2->3->3, return 1->2->3.
比较简单的链表操作题,使用双指针的方法,一个扫描,一个保持当前没有重复的链表的结尾。
public ListNode deleteDuplicates(ListNode head) {
        if(head == null || head.next == null) {
            return head;
        }
        ListNode temp1 = head, temp2 = head.next;
        
        while(temp1 != null) {
            while(temp2 != null && temp1.val == temp2.val) {
                temp2 = temp2.next;
            }
            temp1.next = temp2;
            temp1 = temp2;
            if(temp2 != null) temp2 = temp2.next;
        }
        
        return head;
    }

[Leetcode] Linked List Cycle 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?
要求找到链表中,环开始的地方,如果没有环则直接返回null。使用Hashset可以很方便的找到环开始的地方,即第一次在Hashset中找到的节点。
public ListNode detectCycle(ListNode head) {
        HashSet set = new HashSet();
        ListNode temp = head;
        
        while(temp != null) {
            if(set.contains(temp) == true) {
                return temp;
            }
            else {
                set.add(temp);
                temp = temp.next;
            }
        }
        
        return null;
    }
上面的方法空间复杂度为O(n),可以再次使用快慢指针使得空间复杂度降为O(1)。可以证明,快慢指针相遇的地方到环开始的距离等于链表头到环开始的距离,利用改性质可以找到环开始的地方。

public ListNode detectCycle(ListNode head) {
        if(head == null || head.next == null) {
            return null;
        }
        ListNode slow = head, fast = head;
        
        do {
            slow = slow.next;
            fast = fast.next.next;
            if(slow == fast) break;
        }while(slow != null && fast != null && fast.next != null);
        if(fast == null || fast.next == null) {
            return null;
        }
        ListNode temp = head;
        while(temp != slow) {
            slow = slow.next;
            temp = temp.next;
        }
        
        return temp;
    }

[Leetcode] Linked List Cycle

Given a linked list, determine if it has a cycle in it.
Follow up:
Can you solve it without using extra space?
链表题,要求判断链表中是否存在一个环。最简单的思想就是用一个hashset来将所有的链表节点存储起来,在遍历链表的时候如果找到一个存在于hashset的节点,说明存在一个环,否则遍历完链表,说明没有环存在。
public boolean hasCycle(ListNode head) {
        HashSet set = new HashSet();
        ListNode temp = head;
        while(temp != null) {
            if(set.contains(temp) == true) {
                return true;
            }
            else {
                set.add(temp);
                temp = temp.next;
            }
        }
        return false;
}
但是上面的解法空间复杂度为O(n)。另一种解法只需要O(1)的空间复杂度,即使用快慢指针的方法,如果链表中有环,那么快指针必然会在环内追上慢指针,否则快指针必然先变为null。

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

[Leetcode]Merge Two Sorted Lists

Merge two sorted linked lists and return it as a new list. The new list should be made by splicing together the nodes of the first two lists.
比较基础的链表题,精髓在于使用dummy节点来方便的维护链表头。


public class Solution {
    public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        ListNode dummy = new ListNode(0);
        ListNode temp = dummy;
        while(l1 != null && l2 != null) {
            if(l1.val < l2.val) {
                temp.next = l1;
                temp = temp.next;
                l1 = l1.next;
            }
            else {
                temp.next = l2;
                temp = temp.next;
                l2 = l2.next;
            }
        }
        temp.next = l1 == null ? l2 : l1;
        return dummy.next;
 }
}