Leetcode解题笔记(Array)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/kelvinmao/article/details/52007595

源码见github https://github.com/Kelvinmao/Leetcode/tree/master/Array

  • [TODO]
    2016-08-12更新

    1. Sort Colors

Given an array with n objects colored red, white or blue, sort them so that objects of the same color are adjacent, with the colors in the order red, white and blue.

Here, we will use the integers 0, 1, and 2 to represent the color red, white, and blue respectively.

Note:
You are not suppose to use the library’s sort function for this problem.

Follow up:
A rather straight forward solution is a two-pass algorithm using counting sort.

First, iterate the array counting number of 0’s, 1’s, and 2’s, then overwrite array with total number of 0’s, then 1’s and followed by 2’s.

Could you come up with an one-pass algorithm using only constant space?

这道题最简单的方法就是采用计数排序,然后对原数组重新赋值,没什么难度,代码如下:

/*solution1 4ms use counting sort*/
class Solution {
public:
    void sortColors(vector<int>& nums) {
        int i=0,j=0,k=0;
        vector<int> zero;
        vector<int> one;
        vector<int> two;
        for(i=0;i<nums.size();i++){
            switch(nums[i]){
                case 0:
                    zero.push_back(nums[i]);
                    break;
                case 1:
                    one.push_back(nums[i]);
                    break;
                case 2:
                    two.push_back(nums[i]);
                    break;   
            }
        }
        for(i=0;i<zero.size();i++)
            nums[i]=zero[i];
        for(i,j=0;j<one.size();i++,j++)
            nums[i]=one[j];
        for(i,k=0;k<two.size();i++,k++)
            nums[i]=two[k];
    }
};

不过这个解法并不能算作完美的算法,因为申请了额外的空间,并且进行了多次的遍历。

现在考虑能不能用恒定的空间和one-pass来解决问题,考虑到只有三种元素,0 1 2,那么我们可以借鉴快排的思路,以1为pivot,然后把0放在1左边,2在1右边,这是大致的思路。

要实现这个算法,我们需要三个指针,low high和cur,其中low,high分别指向0和2组成的序列的边界,cur从最左端开始向后遍历,那么有以下几种情况:

  1. nums[cur]==0 应该将其与low指向的元素交换,因为nums[low]始终 ==1或low ==cur,所以交换后的nums[cur]已经到位,只需cur++,low++
  2. nums[cur]==1 已经就位,cur++即可
  3. nums[cur]==2 应该将其与high指向的元素作交换,但是high指向的元素的值不确定,所以只能让high–,cur不变

下面以一个数组为例进行演示

1 0 2 2 1 0
    ^         ^
    L         H
    M

    Mid != 0 || 2
    Mid++

    1 0 2 2 1 0
    ^ ^       ^
    L M       H

    Mid == 0
    Swap Low and Mid
    Mid++
    Low++

    0 1 2 2 1 0
      ^ ^     ^
      L M     H

    Mid == 2
    Swap High and Mid
    High--

    0 1 0 2 1 2
      ^ ^   ^
      L M   H

    Mid == 0
    Swap Low and Mid
    Mid++
    Low++

    0 0 1 2 1 2
        ^ ^ ^
        L M H

    Mid == 2
    Swap High and Mid
    High--

    0 0 1 1 2 2
        ^ ^
        L M
          H

由此可见,循环条件应该是mid<=high

代码如下:

class Solution {
public:
    void sortColors(vector<int>& nums) {
        int low=0,high=nums.size()-1,cur=0;
        while(cur<=high){
            if(nums[cur]==0)
                swap(nums[cur++],nums[low++]);
            else if(nums[cur]==1)
                cur++;
            else if(nums[cur]==2)
                swap(nums[cur],nums[high--]);
        }
    }
};

73.Set Matrix Zeroes

2016-08-11更新

238.Product of Array Except Self

Given an array of n integers where n > 1, nums, return an array output such that output[i] is equal to the product of all the elements of nums except nums[i].

Solve it without division and in O(n).

For example, given [1,2,3,4], return [24,12,8,6].

Follow up:
Could you solve it with constant space complexity? (Note: The output array does not count as extra space for the purpose of space complexity analysis.)

题目看起来很简单,但毕竟是一道medium的题,所以并不会像我们想的那么简单,我们首先粗略地考虑一般的情况:

  • 既然要算出某一元素以外其他元素的乘积,那么我们可以先算出所有元素的乘积,最后再除以不需要的元素即可

然而如果中间某个元素值为0呢?0做除数会引发异常,所以我们需要对Corner case进行讨论

  • 对于[0,1]这样的只有两个元素且其中一个元素的值为0的数组,我们发现只要交换一下两个元素然后返回即可。

修改之后,提交代码,发现自己对Corner case的讨论还不够完整,于是继续考虑。

  • 对于长度大于2的数组,又要再分两种情况:
    1. 长度大于2且只含有一个0,例如[9 0 3]这样的数组,我们发现只需要把0所在的位置赋值为其他元素的乘积,而其他位置依然为0,返回即可。
    2. 长度大于2但含有不止一个0,例如[9 0 0 3]这样的数组,我们容易证明对于这样的数组,要返回的数组的所有元素应该都为0。

Corner case讨论完毕,下面我们看代码:

/*solution1 60ms*/
class Solution {
public:
    vector<int> productExceptSelf(vector<int>& nums) {
        int i=0,res=1,count=0,zero_pos=0;
        vector<int> output(nums.size(),0);
        for(i=0;i<nums.size();i++)
            res=res*nums[i];
        if(!res){
            if(nums.size()==2){
                swap(nums[0],nums[1]);
                return nums;
            }
            else if(nums.size()>2){
                res=1;
                /*计算除0以外所有元素的乘积,并记录0的个数和位置*/
                for(i=0;i<nums.size();i++){
                    if(nums[i]==0){
                        count++;
                        zero_pos=i;
                        continue;
                    }
                    res=res*nums[i];
                }
                if(count==1){
                    output[zero_pos]=res;
                    return output;
                }
                if(count>1)
                    return output;
            }
        }
        for(i=0;i<nums.size();i++)
            output[i]=res/nums[i];
        return output;
    }
};

在参阅了讨论区之后,发现之前对题意的理解上有一点偏差。提示中说”without division”,我理解成了“不能对数组进行分割”,但这道题想表达的意思可能是“不能用除法”,所以上面的解法可能不够完美。

于是转换思路,假如有一个数组为[a,b,c,d],那么在经过这样一轮运算后得到的结果就是[bcd,acd,abd,abc],所以我们可以构造这样两个数组[1,a,ab,abc]和[bcd,cd,d,1].这样两个数组对应项相乘就可以得到结果。代码如下:

/*solution3 60ms*/
class Solution {
public:
    vector<int> productExceptSelf(vector<int>& nums) {
        vector<int> arr1(nums.size(),1);
        vector<int> arr2(nums.size(),1);
        for(int i=1;i<arr1.size();i++)
            arr1[i]=arr1[i-1]*nums[i-1];
        for(int i=arr2.size()-2;i>=0;i--)
            arr2[i]=arr2[i+1]*nums[i+1];
        for(int i=0;i<arr1.size();i++)
            arr1[i]=arr1[i]*arr2[i];
        return arr1;
    }
};

但是题目要求除了output这个要返回的数组外,空间复杂度必须为O(1),所以我们不能开两个数组,必须采用滚动计算的方法来替换其中的一个数组。代码如下:

/*solution3 60ms*/
class Solution {
public:
    vector<int> productExceptSelf(vector<int>& nums) {
        vector<int> arr1(nums.size(),1);
        int tmp=1;
        for(int i=1;i<arr1.size();i++)
            arr1[i]=arr1[i-1]*nums[i-1];
        for(int i=nums.size()-1;i>=0;i--){
            arr1[i]=arr1[i]*tmp;
            tmp=tmp*nums[i];
        }
        return arr1;
    }
};

228.Summary Ranges

Given a sorted integer array without duplicates, return the summary of its ranges.

For example, given [0,1,2,4,5,7], return [“0->2”,”4->5”,”7”].

这道题可以借鉴之前解决最短子数组问题的方案,使用滑动窗口在数组上划定范围,具体思路如下:

用指针i指向数组第一个元素,j指向第二个元素。当后一元素与前一元素之间满足差为1的关系时,j继续向后移动;否则,判断j的值,j的值为1时,则说明只找到一个数而不是一个序列,这个时候就直接将int转换为string即可;若大于1,则说明是一个序列,那么就需要将首尾两个数分别转为string之后用”->”连接;

执行完这步操作后,i的值应该被更新为序列末端的后一位。代码如下:

/*solution1 0ms*/
class Solution {
public:
    vector<string> summaryRanges(vector<int>& nums) {
        int i=0,size=nums.size();
        vector<string> res;
        if(nums.empty())
            return res;
        while(i<size){
            int j=1;
            while(i+j<size&&nums[i+j]-nums[i]==j)
                j++;
            res.push_back(j<=1?to_string(nums[i]):to_string(nums[i])+"->"+to_string(nums[i+j-1]));
            i+=j;
        }
        return res;
    }
};

2016-08-10更新

229.Majority Element II

Given an integer array of size n, find all elements that appear more than ⌊ n/3 ⌋ times. The algorithm should run in linear time and in O(1) space.

Hint:

How many majority elements could it possibly have?

这道题是Majority Element系列的第二题,要求O(n)的时间复杂度和O(1)的空间复杂度,题目中给出了暗示,要求先考虑一个序列中最多有多少个出现次数能超过1/3的元素,根据反证法,如果有三个符合要求的元素,那么数组的长度就不可能是n了,所以符合要求的元素最多有两个,但是最开始我并没能够从这个暗示里读出有价值的信息。

直到后来看了讨论区的答案才受到了启发,之前用过的majority voting algorithm是用来寻找出现次数过半的元素的,由于过半的元素只能有一个,所以只需要有一个count来统计投票就够了,这道题只不过是把可能符合条件的数变为2个,再加上一个count不就可以统计两个候选元素的得票状况了吗?其余步骤完全和之前相同。 代码如下:

class Solution {
public:
    vector<int> majorityElement(vector<int>& nums) {
        int curIndex1=0,curIndex2=0,count1=0,count2=0;
        int size=nums.size(),i=0,tri=size/3;
        vector<int>res;
        for(i=0;i<nums.size();i++){
            if(nums[i]==nums[curIndex1])
                count1++;
            else if(nums[i]==nums[curIndex2])
                count2++;
            else if(!count1){
                count1=1;
                curIndex1=i;
            }
            else if(!count2){
                count2=1;
                curIndex2=i;
            }
            else{
                count1--;
                count2--;
            }
        }
        count1=0;
        count2=0;
        for(i=0;i<nums.size();i++){
            if(nums[i]==nums[curIndex1])
                count1++;
            else if(nums[i]==nums[curIndex2])
                count2++;
        }
        if(count1>tri)
            res.push_back(nums[curIndex1]);
        if(count2>tri)
            res.push_back(nums[curIndex2]);
        return res;
    }
};

2016-08-09更新

169.Majority Element

Given an array of size n, find the majority element. The majority element is the element that appears more than ⌊ n/2 ⌋ times.

You may assume that the array is non-empty and the majority element always exist in the array.

这道题要求寻找数组中的多数元素,也就是出现次数超过数组长度一半的元素,最容易想到的方式就是用两个指针对其进行扫描,再用一个count变量进行计数,遇到count超过数组长度一半的情况就返回,思路非常简单,代码如下:

class Solution {
public:
    int majorityElement(vector<int>& nums) {
        if(nums.size()<2)
            return nums[0];
        int i=0,j=0,size=nums.size(),count=0;
        int half=size/2;;
        for(i=0;i<size;i++){
            count=0;/*外循环每进行一次count计数变量应该重新置0*/
            for(j=0;j<size;j++){
                if(nums[j]==nums[i]){
                    count++;
                    if(count>half)
                        return nums[i];
                }
            }
        }
        return -1;
    }
};

这并不能称得上是一个高效的算法,因为就是在反复的遍历,不出所料这个算法提交之后超时了,对于小数组来说没什么问题,但对于有大量重复元素的数组而言这样去遍历就显得太多余了,于是我用了之前做题时学到的一个技巧对算法进行了优化,代码如下:

/*solution1 O(n^2) 20ms*/
class Solution {
public:
    int majorityElement(vector<int>& nums) {
        if(nums.size()<2)
            return nums[0];
        int i=0,j=0,size=nums.size(),count=0;
        int half=size/2;;
        for(i=0;i<size;i++){
            count=0;/*外循环每进行一次count计数变量应该重新置0*/
            /*遇到有重复元素的,我们仅以最后一个元素做参照,来对该元素出现次数进行统计*/
            while(nums[i]==nums[i+1])
                i++;
            for(j=0;j<size;j++){
                if(nums[j]==nums[i]){
                    count++;
                    if(count>half)
                        return nums[i];
                }
            }
        }
        return -1;
    }
};

这样一个简单的操作,让一个TLE的算法击败了百分之五十以上的提交,运行时间为20ms。

之后又思考了别的方法,主要是以下三种:

1.sort

既然是majority,在经过排序后,如果比较小,从第一个元素开始,那么最后一个majority必然位于中间位置;如果比较大,导致最后一个元素也为majority,那么中间位置也依然是majority;综上所述,在两种极端情况下,都可以保证中间是majority,那么在其他情况下一定可以保证最中间的元素是majority。

既然如此,代码也就十分容易写出了;

/*solution2 sort 40ms*/
class Solution {
public:
    int majorityElement(vector<int>& nums) {
        sort(nums.begin(),nums.end());
        return nums[nums.size()/2];
    }
};

时间复杂度为O(nlog n),速度并不是很快,那么有没有办法将时间复杂度降到O(n)呢?

2.hash-table

hash-table的思路比较直接,维护一个unodered-map

/*solution4 use hash-table time-complexity O(n) memory-complexity O(n)*/
class Solution {
public:
    int majorityElement(vector<int>& nums) {
        if(nums.size()<2)
            return nums[0];
        unordered_map<int,int> num_count;
        int i=0,size=nums.size();
        for(i=0;i<size;i++){
            if(num_count.find(nums[i])==num_count.end())
                num_count[nums[i]]=0;
            else
                num_count[nums[i]]++;
            if(num_count[nums[i]]>=size/2)
                return nums[i];
        }
        return -1;
    }
};

时间复杂度O(n),空间复杂度O(n),但leetcode给出的运行时间为48ms,看来还需要再优化

3.majority voting algorithm

这个算法专门用来解决数组中多数元素的问题,只需要one-pass就可以解决问题

先来看看这个算法的描述:

The algorithm is carried out in two steps:

Eliminate all elements except one.
Iterating through the array of numbers, maintain a current candidate and a counter initialized to 0. With the current element x in iteration, update the counter and (possibly) the candidate: if the counter is 0, set the current candidate to x and the counter to 1. If the counter is not 0, increment or decrement the counter based on whether x is the current candidate.
Determine if the remaining element is a valid majority element.
With the candidate acquired in step 1, iterate through the array of numbers and count its occurrences. Determine if the result is more than half of the sequence’s length. If so, the candidate is the majority. Otherwise, the sequence doesn’t contain a majority.

主要思想是:
1. 不断删除数组中的元素直到仅剩下一个
2. 判断剩下的那一个是否真的是majority

关于这个算法的演示,请见:http://www.cs.utexas.edu/~moore/best-ideas/mjrty/example.html#step13

下面贴出代码:

class Solution {
public:
    int majorityElement(vector<int>& nums) {
        int curIndex=0,count=0,i=0;
        for(i=0;i<nums.size();i++){
            nums[curIndex]==nums[i]?count++:count--;
            if(!count){
                curIndex=i;
                count=1;
            }
        }
        return nums[curIndex];
    }
};

2016-08-08更新

  1. Find Minimum in Rotated Sorted Array II

Follow up for “Find Minimum in Rotated Sorted Array”:
What if duplicates are allowed?

Would this affect the run-time complexity? How and why?

Suppose a sorted array is rotated at some pivot unknown to you beforehand.

(i.e., 0 1 2 4 5 6 7 might become 4 5 6 7 0 1 2).

Find the minimum element.

The array may contain duplicates.

这是Find Minimum in Rotated Sorted Array系列的第二题,和这个系列的第一题的思路相近,就是寻找在rotate之后依然有序的部分,但是这次有可能会出现nums[mid]==nums[high]的情况,之前处理过Search in Rotated Sorted Array II问题,当出现这种情况时,应该对high的值-1,然后继续判断。

Find Minimum in Rotated Sorted Array I算法的平均时间复杂度为O(log n),而在Find Minimum in Rotated Sorted Array II中,由于有重复元素的存在,那么会对时间复杂度造成影响,在最坏的情况下,比如整个数组由同一个元素组成时,这个算法就不得不一直对high的值进行调整,所以最坏的情况下时间复杂度为O(n)

代码如下:

class Solution {
public:
    int findMin(vector<int>& nums) {
        int low=0,size=nums.size(),high=size-1,mid=0;
        while(low<high){
            mid=low+(high-low)/2;
            if(nums[low]<nums[high])
                return nums[low];
            if(nums[mid]>nums[high])
                low=mid+1;
            else if(nums[mid]<nums[high])
                high=mid;
            else
                high--;
        }
        return nums[low];
    }
};

2016-08-07更新

  1. Minimum Size Subarray Sum

Given an array of n positive integers and a positive integer s, find the minimal length of a subarray of which the sum ≥ s. If there isn’t one, return 0 instead.

For example, given the array [2,3,1,2,4,3] and s = 7,
the subarray [4,3] has the minimal length under the problem constraint.

More practice:
If you have figured out the O(n) solution, try coding another solution of which the time complexity is O(n log n).

求和等于给定值的最短子数组,最开始想用用双重循环把所有大于等于给定值的子数组都穷举出来然后寻找最短子数组,但是考虑到时间复杂度为O(n^2),于是进一步考虑优化方案。

最终,在参考了讨论区之后,我打算引入滑动窗口的思想来解决问题。

image

初始窗口为[low,high],low,high初始值均为0,窗口内部不包含元素;因为此时窗口sum==0< s,所以上界high向右移动,直到sum大于等于s时,我们先计算长度并进行比较,之后尝试舍弃窗口的第一个元素,如果还大于等于s,则继续舍弃,直到找到和等于给定值的最短子数组。代码如下:

class Solution {
public:
    int minSubArrayLen(int s, vector<int>& nums) {
        if(nums.empty())
            return 0;
        int i=0,j=0,size=nums.size(),sum=0,min_size=INT_MAX;
        for(i=0;i<size;i++){
            while(sum<s){
                sum+=nums[++j];
            }
            min_size=min(min_size,j-i+1);
        }
        return min_size==INT_MAX ? 0:min_size;
    }
};

这里需要注意的是,虽然for循环内部还有一个while循环,但并不意味着时间复杂度为O(n^2),因为我们考虑最坏情况,sum每加上一个nums[i],就要进入内循环执行一次sum-=nums[low++],那么也是执行n次减法运算,总时间复杂度为O(2n),也就是说这个算法依然是线性阶的时间复杂度。

2016-08-06更新

  1. Search a 2D Matrix II

Write an efficient algorithm that searches for a value in an m x n matrix. This matrix has the following properties:

  • Integers in each row are sorted in ascending from left to right.
  • Integers in each column are sorted in ascending from top to bottom.

For example,

Consider the following matrix:

[
  [1,   4,  7, 11, 15],
  [2,   5,  8, 12, 19],
  [3,   6,  9, 16, 22],
  [10, 13, 14, 17, 24],
  [18, 21, 23, 26, 30]
]

Given target = 5, return true.

Given target = 20, return false.

这道题是 Search a 2D Matrix系列中的第二题,由于不能保证每行第一个数比上一行最后一个数大,所以我们不能采用第一题的先确定行后确定列的思路。

不过遍历每行每列,以及遍历行之后在行内部进行二分的思路还是可以用的,但不够efficient

于是在参照讨论区之后,我采用了这样的算法:

从右上角开始,如果target大于右上角的值,因为右上角值是第一行最大值,最后一列最小值,所以target应该在之后的行中;若小于,则其毕存在于之前的列当中,代码如下:

/*solution1 264ms*/
class Solution {
public:
    bool searchMatrix(vector<vector<int>>& matrix, int target) {
        if(matrix.empty()||matrix[0].empty())
            return false;
        int row=0,col=matrix[0].size()-1,size=matrix.size();
        while(row<size&&col>=0){
            if(matrix[row][col]==target)
                return true;
            else if(matrix[row][col]>target)
                col--;
            else if(matrix[row][col]<target)
                row++;
        }
        return false;
    }
};

运行时发现时间太长,觉得每次进入循环都要对是否相等进行判断,太过浪费时间,于是做出如下优化:

/*solution2 164ms*/
class Solution {
public:
    bool searchMatrix(vector<vector<int>>& matrix, int target) {
        int row = matrix.size();
        int col = matrix[0].size();
        int i = row-1,j =0;
        while(i>=0&&j<col){
            if(target>matrix[i][j])
                j++;
            else if(target<matrix[i][j])
                i--;
            else
                return true;
        }
        return false;
    }
};
  1. 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.

2016-08-07

补充solution2:

我们采用一个之前未曾见过的思路:映射找环法

先看这样一个数组[1,2,3],每个元素的下标和元素之间存在一种映射关系,可以写为0->1 1->2 2->3 ,稍加变化可写为 0->1->2->3 ,是一个类似于链表的结构。

再看这样一个数组[1,2,2,3],它的元素与下标的映射关系可以写为: 0->1->2->2->2…………这说明链表中出现了环,所以只要链表中有环,就说明有重复元素,且环入口为重复元素。

在Leetcode 142题Linked List Cycle II 中,我们采用快慢指针法来寻找环的入口,思路是让fast指针为slow指针速度的二倍,当fast slow指针相遇时,从链表头结点释放entry指针,当entry与slow指针相等时,entry的位置就是环入口。

之前也有过与duplicate相关的Array类题目,大多是删除元素,而这次要求找出其中的duplicates,本来不是什么很难的要求,但由于有了题目上的种种限制,比如不能改变Array(也就不能通过排序的方法判断相邻元素是否相等),时间复杂度低于O(n2),这道题目就有了难度。

对于这样的数组,我们很难将其与二分查找联系起来,不过它们之间确实有着练习

既然是从1~n的n+1个数,那么根据抽屉原理就必然有一个重复的数,那么先计算mid,算出mid之后计算数组中比mid小的数有几个,如果多于mid个,根据抽屉原理,则说明区间[low,mid-1]必存在重复,否则调整区间到[mid+1,high]。代码如下:

class Solution {
public:
    int findDuplicate(vector<int>& nums) {
        int low=0,size=nums.size(),high=size-1,mid=0,count=0,i=0;
        while(low<=high){
            count=0;
            mid=low+(high-low)/2;
            for(i=0;i<size;i++){
                if(nums[i]<=mid)
                    count++;
            }
            if(count>mid)
                high=mid-1;
            else
                low=mid+1;
        }
        return low;
    }
};

2016-08-05更新

  1. Search a 2D Matrix

Write an efficient algorithm that searches for a value in an m x n matrix. This matrix has the following properties:

Integers in each row are sorted from left to right.
The first integer of each row is greater than the last integer of the previous row.
For example,

Consider the following matrix:

[
  [1,   3,  5,  7],
  [10, 11, 16, 20],
  [23, 30, 34, 50]
]

Given target = 3, return true.

首先是最容易想到的解法,对这个m*n阶的矩阵进行遍历,时间复杂度为O(m *n),Leetcode给出的运行时间最快为12ms,最慢为20ms,性能不够稳定,代码如下:

/*solution1 
Traverse the 2D matrix*/
class Solution {
public:
    bool searchMatrix(vector<vector<int>>& matrix, int target) {
        if(matrix.empty()||matrix[0].empty())
            return false;
        int i=0,j=0;
        int row=matrix.size(),col=matrix[0].size();
        for(i=0;i<row;i++)
            for(j=0;j<col;j++)
                if(matrix[i][j]==target)
                    return true;
        return false;
    }
};

下面开始对算法进行优化:

既然是查找,而且元素又按照一定的顺序进行排列,那就要考虑二分算法,我们发现每一行的元素都是有序的,那么对每一行进行遍历,每一行内部使用二分查找,时间复杂度为O(mlogn),见代码:

/*solution2
use binary-search in rows*/
class Solution {
public:
    bool searchMatrix(vector<vector<int>>& matrix, int target) {
        if(matrix.empty()||matrix[0].empty())
            return false;
        int row=matrix.size(),i=0;
        int low=0,size=matrix[0].size(),high=size-1,mid=0;
        for(i=0;i<row;i++){
            low=0;
            high=size-1;
            while(low<=high){
                mid=low+(high-low)/2;
                if(matrix[i][mid]==target)
                    return true;
                if(matrix[i][mid]<target)
                    low=mid+1;
                else
                    high=mid-1;
            }
        }
        return false;
    }
};

提交之后发现这个算法与最快的算法还有一定差距,于是继续思考如何优化。

上面的算法是对每行进行二分查找,既然都能想到对行进行二分查找,那为何不先用二分锁定所在行,之后用二分锁定所在列,这样时间复杂度是O(log m+log n),比刚才的两个算法时间性能上有了很大的提升。
代码如下:

class Solution {
public:
    bool searchMatrix(vector<vector<int>>& matrix, int target) {
        if(matrix.empty()||matrix[0].empty())
            return false;
        int low=0,size=matrix.size(),high=size-1,mid=0;
        while(low<=high){
            mid=low+(high-low)/2;
            if(matrix[mid][0]==target)
                return true;
            if(matrix[mid][0]>target)
                high=mid-1;
            else
                low=mid+1;
        }
        int row=high;

        if(row<0)
            return false;
        low=0;
        high=matrix[0].size()-1;
        mid=0;
        while(low<=high){
            mid=low+(high-low)/2;
            if(matrix[row][mid]==target)
                return true;
            if(matrix[row][mid]>target)
                high=mid-1;
            else
                low=mid+1;
        }
        return false;
    }
};

2016-08-03更新

  1. Plus One

Given a non-negative number represented as an array of digits, plus one to the number.

The digits are stored such that the most significant digit is at the head of the list.

一个数组代表了一个数字,现在要将这个数字+1,然后将结果以数组的形式返回。这道题看似简单,但实际处理的时候还是会有很多问题,首先,要维护一个进位,难点在于要处理好如果出现了连续进位的情况。我的思路请见代码:

/*solution1*/
class Solution {
public:
    vector<int> plusOne(vector<int>& digits) {
        int i=0,size=digits.size(),carry=1;
        for(i=size-1;i>=0;i--){
            int digit=(digits[i]+carry)%10;
            carry=(digits[i]+carry)/10;
            digits[i]=digit;
            if(carry==0)
                return digits;
        }
        vector<int> res(size+1,0);
        res[0]=1;
        return res;
    }
};

第一版代码很重要,可以说是这类问题的一个通解,carry的初始值随题目所需要加上的值相等,在这道题中当然就是1了。进入循环之后,首先要计算的是末位+1之后对10取模的值,这是加法计算的原则,之后carry的值被更新为”’carry=(digits[i]+carry)/10;”’,如果其大于0,则说明需要进位,因为int类型是向下取整的,需要进位,则向左移动一位,将进位值加到这一位上,以相同的方法继续判断是否需要进位;如果不需要进位则carry==0,直接返回即可。还有一种情况,就是循环结束后发现还是需要进位,例如999这种情况,那么就需要申请一个长度比原来大1 的vector,因为是+1,所以只有可能得到以1开头之后全为0的值,所以很好处理,看代码就好了。

之后看了一下讨论区,发现这道题还有一种钻空子的解法,只适用于plus one的情况,如果不是plus one而是plus two之类的情况就不能用这种方法了。

思路是从最后一位开始判断,如果这一位是9,将其赋值为0,如果不是9,则将其+1并返回;循环结束后如果还没有返回(也就是全为9的情况),那么将其首位赋值为1,调用push_back方法追加一个0即可,代码也十分优雅,请看代码:

/*solution2*/
class Solution {
public:
    vector<int> plusOne(vector<int>& digits) {
        int i=0,size=digits.size();
        for(i=size-1;i>=0;i--){
            if(digits[i]==9)
                digits[i]=0;
            else{
                digits[i]++;
                return digits;
            }
        }
        digits[0]=1;
        digits.push_back(0);
        return digits;
    }
};

2016-08-01更新

  1. Search for a Range

Given a sorted array of integers, find the starting and ending position of a given target value.

Your algorithm’s runtime complexity must be in the order of O(log n).

If the target is not found in the array, return [-1, -1].

For example,
Given [5, 7, 7, 8, 8, 10] and target value 8,
return [3, 4].

这道题要求查找操作的时间复杂度为对数级,那毫无疑问就是Binary-search了,但是不同于一般的B-search,这道题要求返回的是元素索引组成的向量,而不是一个索引,所以就有些麻烦了。

不过回想一下Search Insert Position这道题目,当B-search找不到目标元素时,low指针指向比target稍大的元素;High指针指向稍小的元素。所以借助这个性质,我们有了大致的思路,就是连续用两次二分查找,一次找下界,另一次找上界。这样time-complexity为O(log n),memory-complexity为O(1),符合题目要求。思路简单,但是边界条件判断比较麻烦,代码如下:

class Solution {
public:
    vector<int> searchRange(vector<int>& nums, int target) {
        vector<int> res(2,-1);
        if(nums.empty())
            return res;
        int low=0,size=nums.size(),high=nums.size()-1,mid=0;
        while(low<high){
            mid=low+(high-low)/2;
            if(nums[mid]>target)
                high=mid-1;
            else if(nums[mid]==target)
                high=mid;
            else
                low=mid+1;
        }
        if(nums[low]==target)
            res[0]=low;

        low=0,size=nums.size(),high=size-1,mid=0;
        while(low+1<high){
            mid=low+(high-low)/2;
            if(nums[mid]<target)
                low=mid+1;
            else if(nums[mid]==target)
                low=mid;
            else
                high=mid-1;
        }
        if(nums[low+1]==target&&size>1)
            res[1]=low+1;
        else if(nums[low]==target)
            res[1]=low;
        else
            res[1]=-1;
        return res;
    }
};
  1. 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.

版本序列已经sorted,又要求尽可能少调用API,所以最好的的办法就是Binary-Search,不过和常规的B-search相比,这里要做一些变化,当检测到mid版本有问题时,则说明应该去左半区继续查找;否则应该去右半区。最后返回low指针指向的版本号即可,代码如下:

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

class Solution {
public:
    int firstBadVersion(int n) {
        int low=0,high=n-1,mid=0;
        while(low<=high){
            mid=low+(high-low)/2;
            if(isBadVersion(mid))
                high=mid-1;
            else
                low=mid+1;
        }
        return low;
    }
};

2016-07-30更新

  1. Search Insert Position

Given a sorted array and a target value, return the index if the target is found. If not, return the index where it would be if it were inserted in order.

You may assume no duplicates in the array.

Here are few examples.

[1,3,5,6], 5 → 2
[1,3,5,6], 2 → 1
[1,3,5,6], 7 → 4
[1,3,5,6], 0 → 0

典型的Binary-Search,第一版思路是先用Binary-Search,如果没找到,再从最后出发向前遍历直到找到恰好比target大的元素并返回,代码如下:

/*solution1*/
class Solution {
public:
    int searchInsert(vector<int>& nums, int target) {
        if(nums.empty()||target<nums[0])
            return 0;
        int low=0,size=nums.size(),high=size-1,mid=0,i=0;
        while(low<=high){
            mid=low+(high-low)/2;
            if(nums[mid]==target)
                return mid;
            if(nums[mid]>target)
                high=mid-1;
            else
                low=mid+1;
        }
        // if not found,let varible i go back to find an exact position 
        for(i=size;i>=0&&nums[i-1]>target;i--);
        return i;
    }
};

之后又看了一些别人写的代码,发现最后一步向前查找是不必要的,因为Binary-Search的性质是如果最后找不到目标元素,则low指针指向恰好比target大的元素;high指针指向恰好比target小的元素,所以代码可以优化如下:

/*solution2*/
class Solution {
public:
    int searchInsert(vector<int>& nums, int target) {
        if(nums.empty()||target<nums[0])
            return 0;
        int low=0,size=nums.size(),high=size-1,mid=0,i=0;
        while(low<=high){
            mid=low+(high-low)/2;
            if(nums[mid]==target)
                return mid;
            if(nums[mid]>target)
                high=mid-1;
            else
                low=mid+1;
        }
        // if not found
        return low;
    }
};
  1. Search in Rotated Sorted Array

Suppose a sorted array is rotated at some pivot unknown to you beforehand.

(i.e., 0 1 2 4 5 6 7 might become 4 5 6 7 0 1 2).

You are given a target value to search. If found in the array return its index, otherwise return -1.

You may assume no duplicate exists in the array.

这道题是典型的二分查找,由于没有重复元素,所以我们可以通过中间值和边缘值的大小关系来确定左右两半哪一半是有序的(或者说没有受到rotate的影响),确定之后,我们可以通过判断target是否存在于这个有序区间,若存在于其中,则在该区间上进行二分查找即可;若不存在,则在另一区间上进行二分查找,简而言之:
1. 若A[mid]

class Solution {
public:
    int search(vector<int>& nums, int target) {
        int size=nums.size(),low=0,high=size-1,mid=0;
        while(low<=high){
            mid=low+(high-low)/2;
            if(target==nums[mid])
                return mid;
            if(nums[mid]<nums[high]){
                if(target>nums[mid]&&target<=nums[high])
                    low=mid+1;
                else
                    high=mid-1;
            }
            else{
                if(target>=nums[low]&&target<nums[mid])
                    high=mid-1;
                else
                    low=mid+1;
            }
        }
        return -1;
    }
};
  1. Search in Rotated Sorted Array II

Follow up for “Search in Rotated Sorted Array”:
What if duplicates are allowed?

Would this affect the run-time complexity? How and why?

Write a function to determine if a given target is in the array.

这是上面那道题的后续题目,这次出现了重复元素,不过出现了重复元素,主要思路没有变化,还是通过寻找有序区间并判断target是否存在于这个区间来调整low,high两个指针的位置。

但是这次有重复元素了,所以A[mid]和A[high]的大小关系并不能成为我们判断左半边还是右半边有序的条件了,比如给出数组{1,2,3,3,3,3,3},旋转之后可能是{3,3,3,3,3,1,2}或者{3,1,2,3,3,3,3},这样,我们仅凭中间值和边缘值的大小关系是不能判断出哪边有序的,所以解决方案是如果中间值和边缘值相等,则high–然后判断中间值和新的边缘值的关系。

注意这里移动high指针会造成算法时间复杂度的变化,最坏的情况就是整个数组都由一个数字组成,那就要移动到low=high为止,也就是算法时间复杂度从O(lg n)变成了O(n)

代码如下:

class Solution {
public:
    bool search(vector<int>& nums, int target) {
        int size=nums.size(),low=0,high=size-1,mid=0;
        while(low<=high){
            mid=low+(high-low)/2;
            if(target==nums[mid])
                return true;
            if(nums[mid]<nums[high]){
                if(target>nums[mid]&&target<=nums[high])
                    low=mid+1;
                else
                    high=mid-1;
            }
            else if(nums[mid]>nums[high]){
                if(target>=nums[low]&&target<nums[mid])
                    high=mid-1;
                else
                    low=mid+1;
            }
            else
                high--;
        }
        return false;
    }
};

2016-07-28更新
11. Container With Most Water

Given n non-negative integers a1, a2, …, an, where each represents a point at coordinate (i, ai). n vertical lines are drawn such that the two endpoints of line i is at (i, ai) and (i, 0). Find two lines, which together with x-axis forms a container, such that the container contains the most water.

Note: You may not slant the container.

给定一个数组,每个元素构成坐标(i,ai),假设i为木棒x轴坐标,ai为木棒高度,寻找两根木棒使其与x轴形成的容器能够盛装最多的水。

这道题第一种方法是暴力破解,也就是把所有可能都算一遍然后进行比较,我自己没有尝试但最后查阅答案时获知这种方法的结果是time limit exceed,所以这种方法被pass

第二种方法是双指针法,因为能够盛装的水的量为min(height[start],height[end])*(end-start),也就是取决于最短的那根木棒,所以可以用start和end两个指针从前后向中间扫描,遇到较大值就进行记录,代码如下:

/*solution1,36ms*/
class Solution {
public:
    int maxArea(vector<int>& height) {
        int size=height.size();
        int start=0,end=size-1;
        int container=0,max_value=0;
        while(start<end){
            container=min(height[start],height[end])*(end-start);
            max_value=max(container,max_value);
            if(height[start]<height[end])
                start++;
            else
                end--;
        }
        return max_value;
    }
};

在查看讨论区之后,我发现这种方法还有优化的空间,因为随着两个指针的靠近,(end-start)越来越小,所以我们需要让height尽可能大,所以我们如果仅仅让start和end简单地向一定的方向移动,就会让一些比当前小的height进入循环,然后增加了无意义的比较,所以我们加入两个循环:

/*solution2,24ms*/
class Solution {
public:
    int maxArea(vector<int>& height) {
        int size=height.size();
        int start=0,end=size-1;
        int container=0,max_value=0,het=0;
        while(start<end){
            het=min(height[start],height[end]);
            container=het*(end-start);
            max_value=max(container,max_value);
            /*如果不大于当前height,则start继续向前移动*/
            while(height[start]<=het&&start<end)
                start++;
            while(height[end]<=het&&start<end)
                end--;
        }
        return max_value;
    }
};

第一种方法用时36ms,第二种减少到24ms,性能上有了极大的改善。

  1. Find Minimum in Rotated Sorted Array

Suppose a sorted array is rotated at some pivot unknown to you beforehand.

(i.e., 0 1 2 4 5 6 7 might become 4 5 6 7 0 1 2).

Find the minimum element.

You may assume no duplicate exists in the array.

有一个排好序的数组,可能从中间某个元素开始有环形存在,假设不存在重复元素,请找出其中的最小值。

第一眼看到这道题的时候我直接对数组进行了遍历,然后维护一个min指针指向数组中的最小元素,AC之后看了一下时间只用了4ms,觉得时间性能还不错,代码如下:

/*solution1*/
class Solution {
public:
    int findMin(vector<int>& nums) {
        int size=nums.size();
        int i=0,min=0;
        for(i=0;i<size;i++){
            if(nums[i]<nums[min])
                min=i;
        }
        return nums[min];
    }
};

不过看了讨论区之后觉得自己的答案和大牛们比起来简直太小学生了,上面的算法时间复杂度为O(n),而大牛们通过使用binary_search将时间复杂度降低到了O(lgN).下面我来简述一下binary search解决此题的思路。

因为是排好序的数组,所以只需要分为三种情况
1. [0,1,2,3,4,5,6,7]这种情况的特征是最左边的值小于最右边,所以最左边的一定是minimum,直接返回。
2. [4,5,6,7,0,1,2,3]这种情况的特征是中间的数大于最右边的数,所以minimum一定出现在右半区中,调整到右半区继续查找。
3. [5,6,7,0,1,2,3,4]这种情况的特征是中间的数小于最左边的数,所以minimum一定出现在左半区中,调整到左半区继续查找。
代码如下:

/*solution2*/
class Solution {
public:
    int findMin(vector<int>& nums) {
        int size=nums.size(),low=0,high=size-1,mid=0;
        while(low<high){
            if(nums[low]<nums[high])
                return nums[low];
            mid=(low+high)/2;
            if(nums[mid]>nums[high])
                low=mid+1;
            else
                high=mid;
        }
        return nums[low];
    }
};

代码AC之后,我查看了运行时间,还是4ms,时间上没有太大的改变,应该是和测试用例有关,但时间复杂度降低到了对数级,在数据量较大的时候应该就能显示出来差异了。

2016-07-27更新
16. 3Sum Closest

Given an array S of n integers, find three integers in S such that the sum is closest to a given number, target.

Return the sum of the three integers. You may assume that each input would have exactly one solution.

 For example, given array S = {-1 2 1 -4}, and target = 1.

The sum that is closest to the target is 2. (-1 + 2 + 1 = 2).

这道题要求从给定数组中找出三个数使其和与target的差的绝对值最小。

和3Sum的思路基本一样,不同的是:
1. 这道题要求的是closet而不是equal,所以left要把所有可能试一遍。
2. 题目明确说明答案是唯一确定的,不需要考虑重复问题
3. 需要多维护一个min变量来记录每次循环时的最小值

具体请见代码:

class Solution {
public:
    int threeSumClosest(vector<int>& nums, int target) {
        int size=nums.size(),left=0,min=nums[0]+nums[1]+nums[2],mid=0,right=0;
        if(size<3)
            return 0;
        sort(nums.begin(),nums.end());
        for(left=0;left<size-2;left++){
            int tmp=target-nums[left];
            mid=left+1;
            right=size-1;
            while(mid<right){
                if(abs(tmp-nums[mid]-nums[right])<abs(target-min))
                    min=nums[left]+nums[mid]+nums[right];
                if(tmp==nums[mid]+nums[right])
                    return min;
                else if(nums[mid]+nums[right]<tmp)
                    mid++;
                else
                    right--;
            }
        }
        return min;
    }
};
  1. 4Sum

Given an array S of n integers, are there elements a, b, c, and d in S such that a + b + c + d = target? Find all unique quadruplets in the array which gives the sum of target.

Note: The solution set must not contain duplicate quadruplets.

For example, given array S = [1, 0, -1, 0, -2, 2], and target = 0.

A solution set is:
[
  [-1,  0, 0, 1],
  [-2, -1, 1, 2],
  [-2,  0, 0, 2]
]

给出n个元素的数组S,寻找是否存在四个数组中的元素a,b,c,d且a+b+c+d=target,如果有,寻找出所有这样的不重复的四元组。

这道题如果有一个数是确定的,那么就转化为了3Sum问题,思路完全相同,代码上稍作改动即可

solution1 用时92ms
- [ TODO To be faster]

/*solution 1*/
class Solution {
public:
    vector<vector<int>> fourSum(vector<int>& nums, int target) {
        vector<vector<int>>res;
        int left=0,mid1=0,mid2=0,right=0,tmp_mid2=0,tmp_right=0,tmp_mid1=0,tmp_left=0;
        int size=nums.size();
        sort(nums.begin(),nums.end());
        if(size<4)
            return res;
        for(left=0;left<size-3;left++){
            int tmp_left=target-nums[left];
            /*去除left造成的重复*/
            if(left > 0 && nums[left] == nums[left-1])
                continue;
            for(mid1=left+1;mid1<size-2;mid1++){
                int tmp=tmp_left-nums[mid1];
                mid2=mid1+1;
                right=size-1;
                /*去除mid1造成的重复*/
                if(mid1 > left+1 && nums[mid1] == nums[mid1-1])
                    continue;
                while(mid2<right){
                    if(nums[mid2]+nums[right]==tmp){
                        tmp_mid2=nums[mid2];
                        tmp_right=nums[right];
                        vector<int>quadruplets(4,0);
                        quadruplets[0]=nums[left];
                        quadruplets[1]=nums[mid1];
                        quadruplets[2]=nums[mid2];
                        quadruplets[3]=nums[right];
                        res.push_back(quadruplets);
                        while(mid2 < right && nums[++mid2] == tmp_mid2);/*去除mid2造成的重复*/
                        while(mid2 < right && nums[--right] == tmp_right);
                    }
                    else if(nums[mid2]+nums[right]<tmp)
                        mid2++;
                    else
                        right--;
                }
            }
        }
        return res;
    }
};

2016-07-26更新

15.3Sum

Given an array S of n integers, are there elements a, b, c in S such that a + b + c = 0? Find all unique triplets in the array which gives the sum of zero.

Note: (Elements in a triplet (a,b,c) must be in non-descending order. (ie, a ≤ b ≤ c))The solution set must not contain duplicate triplets.

For example, given array S = [-1, 0, 1, 2, -1, -4],

A solution set is:
[
  [-1, 0, 1],
  [-1, -1, 2]
]

从数组中计算出和与给定值相等的元素组成的新数组,以数组形式返回。Leetcode上的题目似乎有变化,原先还有一个条件是返回的数组中元素必须以升序排列。

最笨的方法就是三重循环,O(n^3)的时间复杂度,想想都可怕。

再来分析题目要求:
1. 以数组形式返回
2. 返回结果不能重复
3. 返回结果以升序排列

以上我们用一大堆if语句都可以完成,不过如果数组在最初是有序的,问题就简化了许多。声明left,mid,right作为待返回数组的三个数的下标,其中left最小,right最大。

那么三个数之和等于0,只要确定一个数,不就转化为2Sum问题了吗?所以我们需要一层外循环

for(left=0;left<size&&nums[left]<=0;left++)

之后mid和right两个指针从两边向中间扫描,发现符合条件的进行记录

while(mid<right){
                if(nums[mid]+nums[right]==tmp){
                    int tmp_left=nums[left],tmp_right=nums[right],tmp_mid=nums[mid];
                    vector<int>triplet(3,0);
                    triplet[0]=nums[left];
                    triplet[1]=nums[mid];
                    triplet[2]=nums[right];
                    res.push_back(triplet);
                }
                else if(nums[mid]+nums[right]<tmp)
                    mid++;
                else
                    right--;
            }

这样,似乎我们的算法可以解决这个问题了,然而还需要处理重复的问题。于是修改代码如下:

for(left=0;left<size&&nums[left]<=0;left++){
            tmp=0-nums[left];
            mid=left+1;
            right=size-1;
            while(mid<right){
                if(nums[mid]+nums[right]==tmp){
                    int tmp_left=nums[left],tmp_right=nums[right],tmp_mid=nums[mid];
                    vector<int>triplet(3,0);
                    triplet[0]=nums[left];
                    triplet[1]=nums[mid];
                    triplet[2]=nums[right];
                    res.push_back(triplet);
                    while(mid<right&&nums[++mid]==tmp_mid);/*防止mid造成重复*/
                    while(mid<right&&nums[--right]==tmp_right);/*防止right造成重复*/
                }
                else if(nums[mid]+nums[right]<tmp)
                    mid++;
                else
                    right--;
            }
            while(left+1<size&&nums[left]==nums[left+1])/*防止*/
                left++;
        }

问题就得到了解决。

2016-07-25更新

189.Rotate Array
- [ TODO 189 use constant space to finish it]
Rotate an array of n elements to the right by k steps.

For example, with n = 7 and k = 3, the array [1,2,3,4,5,6,7] is rotated to [5,6,7,1,2,3,4].

Note:

Try to come up as many solutions as you can, there are at least 3 different ways to solve this problem.

旋转数组,旋转方法就是不断向右移动元素,末尾移至开头。暂时只成功了通过复制新数组进而将合适的元素填入原数组这一种方法,time complexity,space complexity均为O(n),原理见代码:

class Solution {
public:
    void rotate(vector<int>& nums, int k) {
        if(k<=0)
            return;
        int size=nums.size(),i=0;
        vector<int>nums2(nums.begin(),nums.end());
        for(i=0;i<size;i++){
            nums[(i+k)%size]=nums2[i];
        }
    }
};

solution2:逆置
首先将前size-k个元素逆置,再将size-k+1到末尾的所有元素逆置,最后将全部元素逆置也可以得到要求的结果,时间复杂度O(n),空间复杂度O(1),见代码:

class Solution {
public:
    void rotate(vector<int>& nums, int k) {
        int size=nums.size();
        k=k%size;
        reverse(nums.begin(),nums.begin()+size-k);/*reverse的两个参数只包含左不包含右*/
        reverse(nums.begin()+size-k,nums.end());
        reverse(nums.begin(),nums.end());
    }
};

217.Contains Duplicate I

Given an array of integers, find if the array contains any duplicates.

Your function should return true if any value appears at least twice in the array, and it should return false if every element is distinct.

判断数组中是否有重复元素系列中的第一题,最容易想到的办法是双指针法,也就是双重循环,O(n^2)的时间复杂度。不过通过排序,可以将时间复杂度降低到O(n),代码如下:

class Solution {
public:
    bool containsDuplicate(vector<int>& nums) {
        if(nums.size()==0||nums.size()==1)
            return false;
        sort(nums.begin(),nums.end());
        int i=0;
        for(i;i<nums.size();i++){
            if(nums[i]==nums[i+1])
                return true;
        }
        return false;
    }
};

这道题也可以用hash_table来完成,不过还是需要一次循环,意义不大。

219.Contains Duplicate II

Given an array of integers and an integer k, find out whether there are two distinct indices i and j in the array such that nums[i] = nums[j] and the difference between i and j is at most k.

判断重复元素系列的第二题,这次不仅要找到是否存在重复元素,而且要求两元素下标之差不大于k,这个题用hash_table就比较合适了,见代码:

class Solution {
public:
    bool containsNearbyDuplicate(vector<int>& nums, int k) {
        unordered_map<int,int> hash_table;
        int i=0;
        for(i=0;i<nums.size();i++){
            if(hash_table.find(nums[i])!=hash_table.end()&&i-hash_table[nums[i]]<=k)
                return true;
            hash_table[nums[i]]=i;
        }
        return false;
    }
};
  • [ TODO Contains Duplicate III]

2016-07-24更新
27. Remove Element

Given an array and a value, remove all instances of that value in place and return the new length.

Do not allocate extra space for another array, you must do this in place with constant memory.

The order of elements can be changed. It doesn’t matter what you leave beyond the new length.

Example:
Given input array nums = [3,2,2,3], val = 3

Your function should return length = 2, with the first two elements of nums being 2.

删除数组中和给定值相等的元素,返回删除后的数组和数组长度。

这道题用遍历即可,见代码:

class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        int new_len=0,i=0,size=nums.size();
        for(i=0;i<nums.size();i++){
            if(nums[i]!=val){
                nums[new_len++]=nums[i];
            }
        }
        return new_len;
    }
};
  1. Pascal’s Triangle

Given numRows, generate the first numRows of Pascal’s triangle.

For example, given numRows = 5,

Return

[
     [1],
    [1,1],
   [1,2,1],
  [1,3,3,1],
 [1,4,6,4,1]
]

给出行数,要求生成杨辉三角。

这道题只要找到了从第二行开始的递推公式即可,我们发现最左和最右两个数都是1,中间的数等于其“肩上”两数之和。根据组合数公式
image
,对于第k(k > 2)层第n(n > 1 && n < k)个元素A[k][n]有

A[k][n] = A[k-1][n-1] + A[k-1][n]

image

由此我们可以写出代码如下:

class Solution {
public:
    vector<vector<int>> generate(int numRows) {
        vector<vector<int>> val;
        val.resize(numRows);
        int i=0,j=0;
        for(i=0;i<numRows;i++){
            val[i].resize(i+1);
            val[i][0]=1;
            val[i][val[i].size()-1]=1;
            for(j=1;j<val[i].size()-1;j++)
                val[i][j]=val[i-1][j-1]+val[i-1][j];
        }
        return val;
    }
};
  • [ TODO 119. Pascal’s Triangle II ]

2016-07-23更新

  1. Two Sum

    Given an array of integers, return indices of the two numbers such that they add up to a specific target.

You may assume that each input would have exactly one solution.

Example:Given nums = [2, 7, 11, 15], target = 9,

Because nums[0] + nums[1] = 2 + 7 = 9,
return [0, 1].

给定一个数组,要求返回元素之和等于特定值的两元素下标构成的数组。有两种解法:
1. 最基础最容易想到,也就是遍历数组,一个一个加起来看是否等于target,速度极慢,用C写的程序运行时间超过了200ms,代码如下:

/*first solution,very slow*/
/**
 * Note: The returned array must be malloced, assume caller calls free().
 */
int* twoSum(int* nums, int numsSize, int target) {
    int i=0,j=0,k=0;
    int *arr=(int *)malloc(2*sizeof(int));;
    for(i=0;i<numsSize;i++){
        for(j=i+1;j<numsSize;j++){
            if(nums[i]+nums[j]==target){
                arr[0]=i;
                arr[1]=j;
            }
        }
    }
    return arr;
}

2.数组中的元素与索引为一个一一对应的关系,为了提高查找效率,考虑引入了hash table,从头到尾对数组进行遍历,一旦有元素的值等于target-nums[i],则返回其下标即可,代码如下:

/*second solution,faster,using hash table*/
class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        vector<int> result;
        unordered_map<int,int> hash;
        int i=0,numToFind=0;
        for(i=0;i<nums.size();i++){
            numToFind=target-nums[i];
            if(hash.find(numToFind)!=hash.end()){
                result.push_back(hash[numToFind]);
                result.push_back(i);
            }
            hash[nums[i]]=i;
        }
        return result;
    }
};
  1. Remove Duplicates from Sorted Array
    Given a sorted array, remove the duplicates in place such that each element appear only once and return the new length.

Do not allocate extra space for another array, you must do this in place with constant memory.

For example,
Given input array nums = [1,1,2],

Your function should return length = 2, with the first two elements of nums being 1 and 2 respectively. It doesn’t matter what you leave beyond the new length.

删除已排序数组中的重复元素,要求常数级的空间复杂度,并返回最后的数组长度。
思路就是遍历,直接看代码吧:

class Solution {
public:
    int removeDuplicates(vector<int>& nums) {
        if(nums.size()==0)
            return 0;
        if(nums.size()<2)
            return 1;
        int i=0,new_len=1;
        for(i=1;i<nums.size();i++){
            if(nums[i]!=nums[i-1]){
                nums[new_len]=nums[i];
                new_len++;
            }
        }
        return new_len;
    }
};
  1. Merge Sorted Array

Given two sorted integer arrays nums1 and nums2, merge nums2 into nums1 as one sorted array.

Note:
You may assume that nums1 has enough space (size that is greater or equal to m + n) to hold additional elements from nums2. The number of elements initialized in nums1 and nums2 are m and n respectively.

合并两个排序后的数组,不能申请额外的空间。属于常见题目,其实就是归并排序的原理,看代码吧:

class Solution {
public:
    void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {
        int i=0,j=0,k=0;
        vector<int> temp;
        while(i<m&&j<n){
            if(nums1[i]<nums2[j]){
                temp.push_back(nums1[i]);
                i++;
            }
            else{
                temp.push_back(nums2[j]);
                j++;
            }
        }
        while(i<m){
            temp.push_back(nums1[i]);
            i++;
        }
        while(j<n){
            temp.push_back(nums2[j]);
            j++;
        }
        for(i=0;i<temp.size();i++)
            nums1[i]=temp[i];
    }
};
  1. Move Zeroes

Given an array nums, write a function to move all 0’s to the end of it while maintaining the relative order of the non-zero elements.

For example, given nums = [0, 1, 0, 3, 12], after calling your function, nums should be [1, 3, 12, 0, 0].

Note:
You must do this in-place without making a copy of the array.
Minimize the total number of operations.
要求将数组中的0放在数组末尾,并且其余元素不能改变相对顺序,不能用额外空间,尽可能减少操作。介绍两种解法:
1.遍历数组,将数组中所有非0值通过赋值放到前面来,并且记录0的个数,然后在最后把0补进去。见代码:

class Solution {
public:
    void moveZeroes(vector<int>& nums) {
        int j = 0;
        // move all the nonzero elements advance
        for (int i = 0; i < nums.size(); i++) {
            if (nums[i] != 0) {
                nums[j++] = nums[i];
            }
        }
        for (;j < nums.size(); j++) {
            nums[j] = 0;
        }
    }
};

2.利用vector内置函数
这是一个十分惊艳的解法,是我在讨论区看到的,只用了一行代码就解决了问题,使用了remove和fill函数。算法是遍历整个vector,发现等于c的元素就用后一个元素值来替代当前元素值。vector整体的size()不变。(比如对1234这个序列remove 2,返回的序列是 1344(3被复制到2的位置,4被复制到3的位置))。然后返回一个迭代器(第一个是c的位置的迭代器)。看代码:

class Solution {
public:
    void moveZeroes(vector<int>& nums) {
        fill(remove(nums.begin(),nums.end(),0),nums.end(),0);
    }
};
展开阅读全文

没有更多推荐了,返回首页