Wednesday, September 30, 2015

287 - Find the Duplicate Number

Find the Duplicate Number

Given an array nums containing n + 1 integers where each integer is between 1 and n (inclusive), prove that at least one duplicate number must exist. Assume that there is only one duplicate number, find the duplicate one.

Note:

  1. You must not modify the array (assume the array is read only).
  2. You must use only constant, O(1) extra space.
  3. Your runtime complexity should be less than O(n2).
  4. There is only one duplicate number in the array, but it could be repeated more than once.

Credits:
Special thanks to @jianchao.li.fighter for adding this problem and creating all test cases.

Hide Tags

Array Two Pointers Binary Search

Hide Similar Problems

(H) First Missing Positive (M) Single Number (M) Linked List Cycle II (M) Missing Number

好题。
刚开始完全木有思路,只好试着写了一个暴力的,果不其然耗时惊人。

后来忘了只读的限制,写了一个线性的,每次把到达当前元素的下标的相反数放入该位置,
这样重复访问的时候就会遇到所使用的下标的相反数,从而可以找到重复的元素。
虽然可以找到答案,耗时也不多,但违反了只读的要求。

要只读,还要线性,还要常量空间,实在是有难度。
提示里有双下标,二分查找。但数组是随机的,二分查找实在是想不出怎么做。
双下标通常有几种用法,一种是首尾收拢,例如2sum;另一种是新旧位置,例如数组去重;
还有一种,就是链表里的快慢指针。

逐个比划了一下,原来奥秘在于最后一种,快慢指针。
实际上重复的元素就好比是链表里圈的起点,当我们用它的值作为新的下标去访问的时候,就会“绕圈”了!
果然,按照这个思路,快慢下标,直到重合,说明有圈,
再让快的回到起点,快慢同步移动,重合的时候,就是答案了。

这个题目设计的精巧之处在于,
首先要利用数值的范围,他们的值不会超过个数-1,这就意味着,可以用这些值,充当下一步的索引,
于是逐步推进变成了走跳棋,(不知道这个是不是提示里的二分查找。。。)
然后跳棋实际上又被当成了链表,只不过不是用指针,而是用val当作新下标,也就相当于 next 指针,
从而利用链表里快慢指针的路子,最终既不改变数组内容,又不需要更多空间开销,还速度解决问题

绝对的多快好省,相当精妙的设计!!

class Solution {
public:
    //at most O(2n) solution without modify the original array
    int findDuplicate(vector<int>& nums) {
        int sz=nums.size(), idx_slow=0, idx_fast=0, rst=0;
        if(sz)
        {
            do
            {
                idx_fast = nums[nums[idx_fast]];
                idx_slow = nums[idx_slow];
            }while(idx_fast<sz && idx_slow<sz && idx_slow!=idx_fast);


            idx_fast = 0;
            while(idx_fast<sz && idx_slow<sz && idx_slow!=idx_fast)
                idx_slow=nums[idx_slow], idx_fast=nums[idx_fast];


            rst = idx_fast;
        }
        return rst;
    }

    //quick, at least O(n), 20 ms, but modified the original array
    int findDuplicate1(vector<int>& nums) {
        int sz=nums.size(), idx=0, rst=0;
        if(sz)
        {
            while(nums[idx]!=0-idx)     //ends if met negative of idx
            {
                int tmp = nums[idx];
                nums[idx] = 0-idx;      //rewrite data as -idx
                idx = tmp;
            }
            rst = 0-nums[idx];
        }
        return rst;
    }
   
    //naive way, O(n^2), 1748 ms, terrible
    int findDuplicate0(vector<int>& nums) {
        int sz=nums.size();
        for(int ii=sz-1; ii>0; ii--)
            for(int jj=ii-1; jj>=0; jj--)
                if(nums[ii]==nums[jj])
                    return nums[ii];
        return 0;
    }
};

12 ms, 16 ms, 1748 ms.

Sunday, September 27, 2015

106 - Construct Binary Tree from Inorder and Postorder Traversal

Construct Binary Tree from Inorder and Postorder Traversal

Given inorder and postorder traversal of a tree, construct the binary tree.

Note:
You may assume that duplicates do not exist in the tree.

Hide Tags

Tree Array Depth-first Search

Hide Similar Problems

(M) Construct Binary Tree from Preorder and Inorder Traversal

跟前一题一样,唯一的不同是段首变成了段尾,Postorder 的各级root,就在vector 中该子树段落的段尾
定位了root之后,回到 Inorder 里找到相应的位置,就可以把它的左子右子 Inorder 分开了
进而根据Inorder的长度,将 Postorder 也分开,这样就可以递归进入下一层了。
当 Preorder 递归到首末重叠,就到了叶子节点的左右子了,返回 NULL 即可。

一个有趣的现象是,在 Inorder 里查找当前 root 的时候,++ (36 ms) 向上找的效率明显低于 – (12 ms),
有可能是测试 case 里的分布不平均,右子残缺的情况比左子多。(for 循环 ++/-- 之间的效率差别应该没这么大)


在讨论区看到一个直接用 stack 迭代实现的,有点意思:
https://leetcode.com/discuss/6334/here-is-my-o-n-solution-is-it-neat

/**
* Definition for a binary tree node.
* struct TreeNode {
*     int val;
*     TreeNode *left;
*     TreeNode *right;
*     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/

class Solution {
public:
    TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
        return build(inorder, 0, inorder.size(),
                        postorder, 0, postorder.size());
    }
   
    TreeNode* build(vector<int>& inorder, int is, int ie,
                    vector<int>& postorder, int ps, int pe) {
        TreeNode* root = NULL;
        if(ps<pe)
        {
            int val = postorder[pe-1];              //last is root
            root = new TreeNode(val);
   
            //faster than doing ++ from is to ie, but why??
            int ii=ie-1;
            while(ii>=is && val != inorder[ii])
                ii--;

            root->left = build(inorder, is, ii, postorder, ps, ps+(ii-is));
            root->right = build(inorder, ii+1, ie, postorder, ps+(ii-is), pe-1);
        }
        return root;
    }
};
12 ms.

105 - Construct Binary Tree from Preorder and Inorder Traversal

Construct Binary Tree from Preorder and Inorder Traversal

Given preorder and inorder traversal of a tree, construct the binary tree.

Note:
You may assume that duplicates do not exist in the tree.

Hide Tags

Tree Array Depth-first Search

Hide Similar Problems

(M) Construct Binary Tree from Inorder and Postorder Traversal

关键在于,Preorder 的各级root,就在vector 中该子树段落的段首。
定位了root之后,回到 Inorder 里找到相应的位置,就可以把它的左子右子 Inorder 分开了
进而根据Inorder的长度,将 Preorder 也分开,这样就可以递归进入下一层了。
当 Preorder 递归到首末重叠,就到了叶子节点的左右子了,返回 NULL 即可。


一个有趣的现象是,在 Inorder 里查找当前 root 的时候,++ (36 ms) 向上找的效率明显低于 – (12 ms),
有可能是测试 case 里的分布不平均,右子残缺的情况比左子多。(for 循环 ++/-- 之间的效率差别应该没这么大)

/**
* Definition for a binary tree node.
* struct TreeNode {
*     int val;
*     TreeNode *left;
*     TreeNode *right;
*     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/

class Solution {
public:
    TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
        return build(inorder, 0, inorder.size(),
                        preorder, 0, preorder.size());
    }
   
    TreeNode* build(vector<int>& inorder, int is, int ie,
                    vector<int>& preorder, int ps, int pe) {
        TreeNode* root = NULL;
        if(ps<pe)
        {
            int val = preorder[ps];                 //first is root
            root = new TreeNode(val);
   
            //faster than doing ++ from is to ie, but why??
            int ii=ie-1;
            while(ii>=is && val != inorder[ii])
                ii--;

            root->left = build(inorder, is, ii, preorder, ps+1, ps+1+(ii-is));
            root->right = build(inorder, ii+1, ie, preorder, ps+1+(ii-is), pe);
        }
        return root;
    }
};

12 ms.

三分地上网友自己总结的 Leetcode 和 Lintcode

原帖地址:http://www.1point3acres.com/bbs/thread-140867-1-1.html

找工作找了几个月,自己精心总结了Leetcode和Lintcode的截止目前为止所有题目(包含**题目)Java解法答案,有的是自己写的,有的是看到网上有了更好的解法(比如某些博客)然后总结的(提供了答案来源地址链接),几乎所有题都是已知的最优解。答案包含题目,答案,出处来源和自己的笔记(比如容易写错的地方注解)。
欢迎大家pull request提供更优解法。
地址:
https://github.com/stevenlordiam/Leetcode
https://github.com/stevenlordiam/Lintcode