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;
}
};
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)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的位置。
这两个函数的返回值如图所示:
![](https://i-blog.csdnimg.cn/blog_migrate/bba7df56f731aa233c40d72f3a3f2559.png)
//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;
//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;