LeetCode 筆記27 Two Sum III
Design and implement a TwoSum class. It should support the following operations: add
and find
.
add
- Add the number to an internal data structure.find
- Find if there exists any pair of numbers which sum is equal to the value.
這個解法很容易想到,但很容易超時。
基本來說有這樣幾個途徑去解。
第一種,使用一個數組去存所有加入的元素,使用一個Set去存所有的和。在實現Find的時候,遍歷陣列,把能夠得到的和都加到set中去。這樣,find的時間複雜度是O(1)了,因為只需要判斷value是否在集合裡面,但是add就變成了O(n)。實踐證明這是行不通的。
第二種,還是使用一個數組去存元素,但是每次加入的時候保持陣列有序,這樣雖然可以通過二分查詢找到插入點,但是插入本身,由於是在陣列中,複雜度就變成O(n)了。在實現Find的時候,使用TwoSum標準演算法(用兩個指標,一前一後遍歷)。所以這個也沒有好多少,還是會超時。
第三種,不要使用陣列了。使用一個Search Tree來存放元素。這樣在add的時候可以基本保證O(logn)。
Find怎麼辦呢?
其實也可以採用TwoSum的那個標準演算法,但是需要在TreeNode裡面存放一個pre和next指標,分別是排序後的,該節點的前一個和後一個元素。這樣你遠看是一顆樹,近看是雙向連結串列。
在二叉排序樹中插入一個節點大家都會,要注意的是如何在插入的過程中,記錄pre和next。其實只要是向右搜尋,我們就給pre賦值,向左搜尋就給next賦值就可以了。
哦,對了,還要在插入過程中注意update 雙向連結串列的head和tail,這個也很容易,只要插入後發現pre是null,那麼這個節點肯定是head嘛,next是null就是tail咯。
程式碼是這個。
public class TwoSumDS { private TreeNode root; private TreeNode head; private TreeNode tail;//private Set<Integer> seenSum = new HashSet<>(); public void add(int number) { if (root == null) { root = new TreeNode(number); head = tail = root; } else { insertIntoTree(number); } } public boolean find(int value) { { if (head != null && tail != null && head != tail) { TreeNode p = head, q = tail; while (p != q) { int sum = p.val + q.val; //seenSum.add(sum); if (sum > value) { q = q.pre; } else if (sum < value) { p = p.next; } else { return true; } } } } return false; } private void insertIntoTree(int val) { TreeNode p = root; TreeNode pre = null; TreeNode next = null; while (true) { //seenSum.add(val + p.val); if (val > p.val) { pre = p; if (p.right != null) { p = p.right; } else { p.right = new TreeNode(val); insertInToChain(p.right, pre, next); break; } } else { next = p; if (p.left != null) { p = p.left; } else { p.left = new TreeNode(val); insertInToChain(p.left, pre, next); break; } } } } private void insertInToChain(TreeNode n, TreeNode pre, TreeNode next) { if (pre != null) { pre.next = n; } else { head = n; } if (next != null) { next.pre = n; } else { tail = n; } n.pre = pre; n.next = next; } private static class TreeNode { int val; TreeNode right; TreeNode left; TreeNode pre; TreeNode next; TreeNode(int val) { this.val = val; } } public static void main(String[] args) { TwoSumDS ts = new TwoSumDS(); ts.add(1); ts.add(3); ts.add(5); System.out.println(ts.find(4)); System.out.println(ts.find(7)); } }
注意到擼主註釋的部分,有一個優化。就是在插入和搜尋過程中,沿途找到的sum我們快取起來,下次說不定可以用。Find的之前可以先在seenset裡面看看。
不過這個優化導致了超時,所以就去掉了。看了一下Java doc,並沒有gurantee HashSet的存放操作是O(1)的。
看來Java還沒有很多人提交喲:>