[Leetcode] 41, 75, 34

41. First Missing Positive

Given an unsorted integer array, find the first missing positive integer.

For example,
Given [1,2,0] return 3,
and [3,4,-1,1] return 2.

Your algorithm should run in O(n) time and uses constant space.

Solution: 桶排序的变体,如果数组中存在i+1则令nums[i]放入i+1,完成重新排序后,遍历数组查找第一个不是i+1的位,则说明i+1是第一个不存在在数组中的正数。重新排序的做法是,如果nums[i]!=i+1,则将nums[i]与nums[nums[i]-1]交换,对新的nums[i]一直重复此步骤直到成功找到i+1或者数值是非法下标(小于等于0或者大于length),时间复杂度为O(n)。

图片摘自:http://www.cnblogs.com/AnnieKim/archive/2013/04/21/3034631.html


证明:根据题意我们可以知道,令length为nums数组的大小,这个数的范围是[1,length+1],因为如果从1到length每一位都存在在数组中时,则length+1是要求的数,否则如果存在任何一个[1,length]之外的数,这个范围中的数据就会有缺失,因此目标数据必定在[1,length]这个范围之中。因此我们只需要利用原nums数组,空间大小就足够用于进行桶排序。

Code:

class Solution {
public:
    int firstMissingPositive(vector<int>& nums) {
        for(int i=0; i<nums.size(); i++){
            while(nums[i]!=i+1 && nums[i]>0 && nums[i]<=nums.size() && nums[i]!=nums[nums[i]-1]) 
                swap(nums[i], nums[nums[i]-1]);//nums[i]!=nums[nums[i]-1]判断条件用于避免进入死循环
        }
        for(int i=0; i<nums.size(); i++){
            if(nums[i]!=i+1) return i+1;
        }
        return nums.size()+1;
    }
};



75. 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 anone-pass algorithm using only constant space?

Solution(1): 计数排序。因为数据只有0,1,2,非常紧凑,这是最直观的做法,但是需要遍历两次不符合one-pass algorithm的要求。

Code:

class Solution {
public:
    void sortColors(vector<int>& nums) {
        vector<int> count(3, 0);
        for(int i=0; i<nums.size(); i++) count[nums[i]]++;
        int t=0;
        while(t<count[0]){
            nums[t] = 0;
            t++;
        }
        while(t<count[0]+count[1]){
            nums[t] = 1;
            t++;
        }
        while(t<nums.size()){
            nums[t] = 2;
            t++;
        }
    }
};

Solution(2): 鉴于此题只有三个数,因此三路快排的partion算法一次就能够完成排序,恰好满足one-pass algorithm要求。

关于快排的partition的分析,详见http://www.imooc.com/article/16141

普通partition算法图解:


代码(来自维基百科):

// arr[]为数组,start、end分别为数组第一个元素和最后一个元素的索引
 // povitIndex为数组中任意选中的数的索引
int partition(int arr[], int start, int end, int pivotIndex)
{
    int pivot = arr[pivotIndex];
    swap(arr[pivotIndex], arr[end]);
    int storeIndex = start;
    for(int i = start; i < end; ++i) {
        if(arr[i] < pivot) {
            swap(arr[i], arr[storeIndex]);
            ++storeIndex;
        }
    }
    swap(arr[storeIndex], arr[end]);
    return storeIndex;
}

三路快排的partition图解:

Code:

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



34. Search for a Range

Given an array of integers sorted in ascending order, 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].

Solution: 二分查找。对于范围可以在找到值了之后向两头搜索,也可以进行两次二分查找,一次upper_bound,一次lower_bound。后一种做法比较好,因为和target相等的范围可能比较大。

Code(1): 

class Solution {
public:
    vector<int> searchRange(vector<int>& nums, int target) {
        auto b = nums.begin();
        auto e = nums.end();
        auto mid = b;
        while(b<e){
            mid = b + (e-b)/2;
            if(*mid == target) break;
            else if(*mid < target){
                b = mid+1;
            }else if(*mid > target){
                e = mid;//切记e在范围外
            }
        }
        vector<int> ans(2,-1);
        if(b<e){
            //确定起点和终点
            ans[0] = mid-nums.begin();
            while(ans[0]>0 && nums[ans[0]-1]==target) ans[0]--;
            ans[1] = mid-nums.begin();
            while(ans[1]<nums.size()-1 && nums[ans[1]+1]==target) ans[1]++;
        }
        return ans;
    }
};

Code(2):
class Solution {
public:
    vector<int> searchRange(vector<int>& nums, int target) {
        //lower_bound
        auto first = nums.begin();
        auto last = nums.end();
        while(first < last){
            auto mid = first+(last-first)/2;
            if(target <= *mid)
                last = mid;
            else
                first = mid+1;
        }
        auto lb = first;
        
        //upper_bound;
        first = nums.begin();
        last = nums.end();
        while(first < last){
            auto mid = first+(last-first)/2;
            if(target < *mid)//与lower_bound的区别只有这里
                last = mid;
            else
                first = mid+1;
        }
        auto ub = first;
        
        
        if(lb==nums.end() || *lb!=target)
            return vector<int> {-1,-1};
        else
            return vector<int> {lb-nums.begin(), ub-nums.begin()-1};
        
    }
};


有关二分查找的总结:

1. 二分查找的库函数(来源:http://www.cnblogs.com/cobbliu/archive/2012/05/21/2512249.html

(1)ForwardIter lower_bound(ForwardIter first, ForwardIter last,const _Tp& val),算法返回一个非递减序列[first, last)中的第一个大于等于值val的位置。

(2)ForwardIter upper_bound(ForwardIter first, ForwardIter last, const _Tp& val),算法返回一个非递减序列[first, last)中第一大于val的位置。

这两个函数的返回值如图所示:




2.关于 lower_bound和upper_bound的实现:

(1)lower_bound:对于 middle位置的数,大于或小于的情况很容易分析,比较特殊的是等于的情况。对于一般的二分查找,等于的情况已经可以直接输出了,但是lower_bound需要找到第一个等于值val的位置,所以还需要继续往下进行二分,此时我们需要将last指针指向这个middle的位 置,虽然这会导致这一位暂时到范围之外,但是如果前面都没有等于val的值,first指针会继续向后走指向原本在范围外的那个值。
Code:
//lower_bound
auto first = nums.begin();
auto last = nums.end();
while(first < last){
    auto mid = first+(last-first)/2;
    if(target <= *mid)
        last = mid;
    else
        first = mid+1;
}
auto lower_bound = first;

(2)upper_bound:对于*middle等于val的情况,将first指向middle的后一个,因为需要找到第一个大于middle的数。
Code:
//upper_bound;
first = nums.begin();
last = nums.end();
while(first < last){
    auto mid = first+(last-first)/2;
    if(target < *mid)//与lower_bound的区别只有这里
        last = mid;
    else
        first = mid+1;
}
auto upper_bound = first;


3. 计算middle的位置: auto mid = first + (last - first)/2 。 样例结果:奇数情况-{2, 6 ,9}, 偶数情况-{2,6, 8 ,9}。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值