数组理论基础
文章链接:link
704.二分查找
一、做题感受&第一想法
轻松。(408学了二分查找,比较轻松地用了左闭右闭的思想写出来了。)
二、学习文章后收获
1.二分法整体思路
- 两种思路:①左闭右闭 ②左闭右开
- 两个易错点:①while循环条件:考虑区间合法性! ②更新low和high时需不需要加一减一:考虑区间定义!
- mid的计算方法一直不变,一直是
mid = (low+high)/2
或者mid = low +(high-low)/2
(防溢出)。 - 适用范围:①有序表 ②无重复元素(因为二分法只能找到其中一个,返回不了所有)(只要看到面试题里给出的数组是有序数组,都可以想一想是否可以使用二分法。)
2.如何解决“两int型相加可能越界”的问题
mid = (left + right)/2; //error,可能越界
mid = left + (right - left)/2; //ok,防止越界
三、实现过程中的困难
1.Leedcode错误 error:control reaches end of non-void function[-Werror=return-type]
原因在于,某些编译器要求函数的结尾必须保证有返回值。如果代码中某个分支情况导致了没有经过return语句(或者看似没有经过),那么有的编译器就会报warning或error。
解决方法:在函数结尾加一个显式的return(虽然可能永远都不会经过它)。
四、代码
1.左闭右闭
int search(int* nums, int numsSize, int target) {
int low = 0, high = numsSize - 1, mid;
while(low<=high) //左闭右闭
{
mid = low + (high - low)/2; //防溢出
if(nums[mid]==target) return mid;
else if(nums[mid]>target) high = mid -1;
else if(nums[mid]<target) low = mid +1;
}
return -1; //不用if判断了。只要走到这儿就一定是low>high了。
}
2.左闭右开
int search(int* nums, int numsSize, int target) {
int low = 0,mid,high = numsSize;//左闭右开
while(low<high)
{
mid = low + (high - low)/2; //防溢出
if(nums[mid]==target) return mid;
else if(nums[mid]>target) high = mid;
else if(nums[mid]<target) low = mid +1;
}
return -1;
}
27.移除元素
一、做题感受&第一想法
有思路但难以用代码表示。弄巧成拙写了相向双指针,但是边界条件出错了几次,最后算是坎坎坷坷写出了。
二、学习文章后收获
1.思路一:暴力解法
int removeElement(int* nums, int numsSize, int val) {
int i = 0, j = 0, temp = 0, k = 0;
for( i = 0; i < numsSize - k; ){
if(nums[i] == val){
k++;
for(j = i; j < numsSize - 1; j++){
nums[j] = nums[j+1];
}
}
else{
i++;
}
}
return numsSize-k;
}
2.思路二:双指针法之“快慢指针”:用快指针从前往后遍历,最后移动到慢指针处。
int removeElement(int* nums, int numsSize, int val) {
int fast = 0, slow = 0;
while(fast < numsSize){ //用fast指针来遍历
if( nums[fast] != val){
nums[slow] = nums[fast]; //如果找到不为val的元素,则搬动到slow的位置。
slow++;
}
fast++;
}
return slow;
}
3.思路三:双指针法之“相向指针”:右指针从右往左寻找合法元素,移动给左指针
int removeElement(int* nums, int numsSize, int val) {
int leftIndex = 0, rightIndex = numsSize - 1;
while(leftIndex <= rightIndex){ //等号:考虑整个数组没有val的情况
while( leftIndex <= rightIndex && nums[leftIndex] != val ){ //两个判断条件的顺序不能调换!否则数组越界
leftIndex ++;
}
while( leftIndex <= rightIndex && nums[rightIndex] == val){
rightIndex --;
}
if( leftIndex < rightIndex ){
nums[leftIndex] = nums[rightIndex];
leftIndex++;
rightIndex--;
}
}
return leftIndex;
}
三、实现过程中的困难
1.&&运算符的执行顺序问题(“相向指针”中的一个bug)
上述的代码中,while循环的两个判断条件不能调换顺序!
错误写法:
while( nums[leftIndex] != val && leftIndex <= rightIndex ){ //error,数组越界
leftIndex ++;
}
while( nums[rightIndex] == val && leftIndex <= rightIndex ){ //error,数组越界
rightIndex --;
}
错误原因:
&&
和||
运算符可能会导致某些判断并不会做。a && b
如果条件a已经为假,则b不会再执行。a || b
如果条件a已经为真,则b不会再执行。
所以,如果先判断nums[leftIndex] != val
再判断leftIndex <= rightIndex
,则可能会导致在前者nums[leftIndex] != val
时数组越界!(举个例子:数组中所有元素都不是val,那么最后leftIndex不断加加,直到leftIndex为numsSize。此时数组越界!)
【The END】
【另:今日份“相关题目”记录】
“704.二分查找”相关题目
35.搜索插入位置
一、二分查找如果查找失败元素插入位置——找low
左闭右闭时,二分查找如果查找失败(low>high),则被查找元素大小在high和low之间!故应该取代low的位置。
左闭右开时,二分查找如果查找失败(low=high),则被查找元素大小就在high和low上!故应该取代low的位置。
二、暴力解法有时也很ok
int searchInsert(int* nums, int numsSize, int target) {
int i=0;
for(i=0;i<numsSize;i++)
{
if(nums[i]==target) return i;
else if(nums[i]>target) return i;
}
return numsSize; //注意数组边界的处理!
}
1.在最后一句return上卡住了,因为一开始并没有考虑数组元素全部小于target的情况。
这种情况下,i = numsSize,会跳出for循环,此时应插在所有数组元素的后面,所以需要添补return numsSize
。
2.暴力解法有时不一定比其他方法慢。
三、代码记录:
1.左闭右闭:
int searchInsert(int* nums, int numsSize, int target) {
int low = 0,high = numsSize-1, mid;
while(low<=high)
{
mid = low + (high-low)/2;
if(nums[mid]==target) return mid;
else if(nums[mid]>target) high = mid -1;
else low = mid +1;
}
//没有找到target,此时low>high
//target应该插在high和low中间!即:取代low的位置!
return low;
2.左闭右开:
int searchInsert(int* nums, int numsSize, int target) {
int low = 0,high = numsSize, mid;
while(low<high)
{
mid = low + (high-low)/2;
if(nums[mid]==target) return mid;
else if(nums[mid]>target) high = mid;
else low = mid +1;
}
//没有找到target,此时low=high
//target应该放在high和low的位置!即:取代low的位置!
return low;
}
34. 在排序数组中查找元素的第一个和最后一个位置
一、二分法如何寻找左边界和右边界:两次二分
二、我一开始的思路:二分法找到一个target,再直接遍历左右元素,寻找左右边界(比较暴力的解法)
int* searchRange(int* nums, int numsSize, int target, int* returnSize) {
int low=0 , high = numsSize-1 , mid, i=0;
*returnSize = 2;
int* p = (int*)malloc(sizeof(int)*2);
while(low<=high)
{
mid = low +(high - low)/2;
if(nums[mid] == target) break;
else if(nums[mid] >target) high = mid -1;
else low = mid +1;
}
if(low>high){
p[0]=-1;
p[1]=-1;
}
else //找到了target
{
for(i=mid;i>=0;i--){
if(nums[i]!=target) break;
}
p[0] = i+1;
for(i=mid;i<numsSize;i++){
if(nums[i]!=target) break;
}
p[1] = i-1;
}
return p;
}