随笔- 40  评论- 45  文章- 0 

LeetCode 笔记27 Two Sum III - Data structure design

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还没有很多人提交哟:>

posted on 2015-03-18 20:34  lichen782  阅读(...)  评论(...编辑  收藏