第一题:合并有序数组
给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2,另有两个整数 m 和 n ,分别表示 nums1 和 nums2
中的元素数目。请你 合并 nums2 到 nums1 中,使合并后的数组同样按 非递减顺序 排列。
注意:最终,合并后数组不应由函数返回,而是存储在数组 nums1 中。为了应对这种情况,nums1 的初始长度为 m + n,其中前 m
个元素表示应合并的元素,后 n 个元素为 0 ,应忽略。nums2 的长度为 n 。
解法一: 放入同一数组,再快速排序。
int comp(int *a,int*b)
{
return *a - *b;
}
void merge(int* nums1, int nums1Size, int m, int* nums2, int nums2Size, int n){
int s=m;
for(int i=0;i<n;i++)
{
nums1[s]=nums2[i];
s+=1;
}
qsort(nums1,m+n,sizeof(nums1[0]),comp);
}
解法2:双指针
void merge(int* nums1, int nums1Size, int m, int* nums2, int nums2Size, int n){
int s1=0,s2=0;
int *brr=(int *)malloc(sizeof(int)*nums1Size);
assert (brr!=NULL);
int i=0;
while(s1<m && s2<n)
{
if(nums1[s1]<=nums2[s2])
{
brr[i]=nums1[s1];
s1+=1;
i++;
}
else
{
brr[i]=nums2[s2];
s2+=1;
i++;
}
}
while(s1<m)
{
brr[i]=nums1[s1];
s1+=1;
i++;
}
while(s2<n)
{
brr[i]=nums2[s2];
s2+=1;
i++;
}
for(int j=0;j<nums1Size;j++)
{
nums1[j]=brr[j];
}
free(brr);
}
这里注意int s1 ,s2=0 // 错误 因为s1没有被初始化!!!!!
第二题:二分查找
int search(int* nums, int numsSize, int target){
int left=0,right=numsSize-1;
int mid=(left+right+1)/2;
while(left<=right)
{
if(nums[mid]==target)
{
return mid;
}
if(nums[mid]>target)
{
right=mid-1;
mid=(left+right)/2;
}
else
{
left=mid+1;
mid=(left+right+1)/2;
}
}
return -1;
}
这里要注意的是left 和right的取值,每次mid都已经不等于target,所以left和right要+1或-1。
class Solution {
public:
vector<int> searchRange(vector<int>& nums, int target) {
int len=nums.size();
int l=0,r=len;
int lval=lower_bound(nums,target);
int rval=upper_bound(nums,target);
if(lval==rval){return {-1,-1};}
else
return {lval,rval-1};
}
int lower_bound(vector<int>&nums,int target)
{
const int n=nums.size();
int l=0,r=n;
while(l<r)
{
int mid=l+(r-l)/2;
if(nums[mid]>=target)
{r=mid;}
else{
l=mid+1;
}
}
return l;
}
int upper_bound(vector<int>&nums,int target)
{
const int n=nums.size();//和while(l<r)一起配合使得不会出现数组越界,就不用数组越界的检查。
int l=0,r=n;
while(l<r) //难点
{
int mid=l+(r-l)/2;//防止数组过大时l+r出现内存最大区间的越界
if(nums[mid]<=target)
{l=mid+1;}
else{
r=mid;//难点是怎么和没找到target时做到统一代码。
}
}
return l;
}
};
重点:怎么找到左侧的边界?
可⻅,找到 target 时不要⽴即返回,⽽是缩⼩「搜索区间」的上界 right,在区间 [left, mid) 中继续搜索,即不断向左收缩,达到锁定左侧边界的⽬的(如果值不等于target,就将left减小以达到退出条件l<r)。
难点:是怎么和没找到target时(或者说普通的二分查找时)做到统一代码。
class Solution {
public:
int search(vector<int>& nums, int target) {
int n = (int)nums.size();
if (!n) {
return -1;
}
if (n == 1) {
return nums[0] == target ? 0 : -1;
}
int l = 0, r = n - 1;
while(l<=r)
{
int mid=(l+r)/2;
if(nums[mid]==target){return mid;}
if(nums[0]<=nums[mid])//说明mid左侧是有序数组,则可以查找
{
if(nums[0]<=target&&target<nums[mid])
{
r=mid-1;
}
else{
l=mid+1;
}
}
else
{
if(nums[mid]<target&&target<=nums[n-1])//如果mid左半部不有序,那么mid右侧一定有序,我们在mid右侧操作。范围则继续缩小到target所在的那个区域,下个循环继续找。
{
l=mid+1;
}
else{
r=mid-1;
}
}
}
return -1;
}
};
总结:只要那个区间是有序的就可以二分查找。我们只需要不断的在可以查找的区间去缩小target所在的范围。直到找到。