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

135 - Candy

Candy

There are N children standing in a line. Each child is assigned a rating value.

You are giving candies to these children subjected to the following requirements:

  • Each child must have at least one candy.
  • Children with a higher rating get more candies than their neighbors.

What is the minimum candies you must give?

Hide Tags

Greedy

这道题很早以前看了一眼,没大明白问的啥东东,后来就一直放着没动。
刚刚仔细读了一下,好像知道了,就在纸上比划了几个数组,猜想应该是这么回事:
首先每人至少应该有一颗糖,所以基数是总共小孩的个数。

然后从头向尾逐个检查,是不是比前一个rating value大?大的话,就要追加糖果了。
     追加的时候要注意,如果是连续出现递增的情况,后一个小孩追加的糖果的个数,要比前一个更多一个,
     因此,需要弄一个临时变量表示当前这个小孩应该追加几个糖果,并让它随着 rating value 递增而递增;
     另外,一旦出现下降,这个临时变量就要清零,从下一个递增的地方重新累积。

再然后就从尾向头检查,看是不是前一个比后一个大,是的话,也要增追加糖果。(也需要一个临时变量来累加)
    但此时有一个特别的情况,就是对于处于波峰位置的局部最大值:
          前面的扫描过程中,它已经被追加了糖果了,此时如果重复追加,可能就给多了;
          但前面的扫描追加的值,可能不能完全体现它与自己后面的小孩的 rating value 之间的关系。
          例如 [3,1,9,8,7,2],从前向后检查的话,[9] 只会被追加一个;单从后向前检查的话,它应该被追加3个
          因此,这时候需要知道前一次扫描追加了多少,并且跟当前的临时变量比较。
          如果新的临时变量大,就取新的临时变量的值,但是需要减去之间已经追加过的值;
          如果之前追加的值更大,则无需再追加。
     这也就意味着,还需要一个辅助数组来记录之前追加过的内容,以便比较,因此额外使用了一个数组 buf。

按照这个逻辑试了一下,确实没有问题,两轮扫描之后即可直到最少需要多少颗糖果。
第二次扫描的时候,最初的逻辑如下:
        if(!ii)
            added++, rst += added;
        else
        {
            if(ratings[ii-1]<ratings[ii])
                added++, rst += (added>buf[ii])?(added-buf[ii]):0;
            else
                added++, rst += added;
        }
合并代码之后的逻辑如下面蓝色部分所示。

class Solution {
public:
    int candy(vector<int>& ratings) {
        int sz=ratings.size();
       
        int rst=sz, added=0, buf[sz]={0};
        for(int ii=1; ii<sz; ++ii)
        {
            if(ratings[ii]>ratings[ii-1])
                added++, rst += added, buf[ii]=added;
            else
                added=0;
        }
       
        added=0;
        for(int ii=sz-2; ii>=0; ii—)
        {
            if(ratings[ii]>ratings[ii+1])
            {
                added++;
                rst += (ii && ratings[ii-1]<ratings[ii]) ?
                            ((added>buf[ii])?(added-buf[ii]):0) : added;

            }
            else
                added=0;
        }
        return rst;
    }
};

36 ms.

Thursday, September 24, 2015

278 - First Bad Version

First Bad Version

You are a product manager and currently leading a team to develop a new product. Unfortunately, the latest version of your product fails the quality check. Since each version is developed based on the previous version, all the versions after a bad version are also bad.

Suppose you have n versions [1, 2, ..., n] and you want to find out the first bad one, which causes all the following ones to be bad.

You are given an API bool isBadVersion(version) which will return whether version is bad. Implement a function to find the first bad version. You should minimize the number of calls to the API.

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

Hide Tags

Binary Search

Hide Similar Problems

(M) Search for a Range (M) Search Insert Position

二分查找,视中点结果来移动起点终点,两者相邻时查找结束。

// Forward declaration of isBadVersion API.
bool isBadVersion(int version);

class Solution {
public:
    int firstBadVersion(int n) {
        long low=1, high=n, mid=0;

        if(isBadVersion(low))
            return low;
       
        while(low<high-1)
        {
            mid = (low+high)/2;
            (isBadVersion(mid)) ? high=mid : low=mid;
        }
        return (int)high;
    }
};

0 ms.

各家公司常考题目

各家公司常考题目

原帖:http://www.1point3acres.com/bbs/thread-139979-1-1.html




LinkedIn


#
Title
Acceptance
Difficulty

127
19.5%
Medium

65
11.5%
Hard

170
24.9%
Easy

243
37.2%
Easy

245
40.9%
Medium

244
34.4%
Medium

33
28.7%
Hard

187
20.3%
Medium

238
34.4%
Medium

50
26.7%
Medium

46
31.9%
Medium

47
25.8%
Hard

256
37.3%
Medium

76
19.0%
Hard

21
32.5%
Easy

23
21.0%
Hard

56
22.5%
Hard

53
34.4%
Medium

152
19.6%
Medium

149
12.8%
Hard

236
26.5%
Medium

205
24.7%
Easy

57
21.6%
Hard

198
29.2%
Easy

254
28.7%
Medium

150
21.4%
Medium

102
29.2%
Easy

173

Facebook


#
Title
Acceptance
Difficulty

79
20.5%
Medium

139
23.1%
Medium

125
22.0%
Easy

1
17.7%
Medium

78
28.2%
Medium

69
23.2%
Medium

75
32.6%
Medium

33
28.7%
Hard

206
32.3%
Easy

26
31.0%
Easy

80
30.4%
Medium

10
20.7%
Hard

158
22.6%
Hard

238
34.4%
Medium

50
26.7%
Medium

161
24.5%
Medium

200
22.6%
Medium

43
21.1%
Medium

76
19.0%
Hard

209
23.2%
Medium

88
29.1%
Easy

23
21.0%
Hard

252
33.3%
Easy

253
26.6%
Medium

221
19.7%
Medium

236
26.5%
Medium

235
37.5%
Easy

17
25.6%
Medium

215
27.3%
Medium

208
24.8%
Medium

28
22.4%
Easy

261
25.2%
Medium

168
18.3%
Easy

91
16.3%
Medium

38
25.3%
Easy

133
24.2%
Medium

102
29.2%
Easy

173
29.7%
Medium

121
32.9%
Medium

49
24.3%
Medium

67
24.6%
Easy

211
20.3%
Medium

15



Amazon


#
Title
Acceptance
Difficulty

98
20.2%
Medium

20
26.5%
Easy

242
34.1%
Easy

1
17.7%
Medium

167
43.3%
Medium

78
28.2%
Medium

8
12.8%
Easy

48
32.0%
Medium

186
31.8%
Medium

206
32.3%
Easy

200
22.6%
Medium

155
19.4%
Easy

21
32.5%
Easy

23
21.0%
Hard

146
15.1%
Hard

235
37.5%
Easy

5
20.7%
Medium

160
28.9%
Easy

89
32.9%
Medium

204
19.8%
Easy

199
27.9%
Medium

102
29.2%
Easy

49
24.3%
Medium

Screen Shot 2015-08-20 at 12.28.06 AM.png

Screen Shot 2015-08-20 at 12.28.12 AM.png

Screen Shot 2015-08-20 at 12.28.19 AM.png

Screen Shot 2015-08-20 at 12.28.50 AM.png