704.二分查找
这个题做过不止一次了。
思路就是用左右指针定界,判断中间值和目标值是否相等,然后再不断缩小范围,直到两指针相遇。
但是!思路我都懂,每次都会被边界条件绕晕!
今天做完以后整理了一下容易疑惑的地方:
1.关于“左开右闭,左开右开”
为什么会有这个问题呢,重点在于数组长度:numsSize
众所周知,数组下标是从0开始的,所以最后一个元素下标其实是numsSize-1
设置左右指针的时候,左指针无异议是0,右指针在这里就分出了两种思路:选numsSize或者numsSize-1。前者就是左闭右闭,后者就是左闭右开。左闭右闭定的区间是从left到right的所有元素(包含right),而左闭右开定的区间是从left到right的所有元素(不包含right)。
2.两种思路的区别:
1>while循环判断的条件不同:
在左闭右闭的情况下,是while(left<=right),因为right指向的元素包含在区间里,所以如果两个指针相遇了,需要再判断一次才能结束。
而左闭右开的情况下,是while(left<right),right指向的元素不包含在区间里,指针即使相遇了,也无需判断。
2>第二轮定界时指针的位置不同:
在左闭右闭的情况下,若target>mid,那么left=mid+1;若target<mid,那么right=mid-1;因为mid的值已经经过一次检查了,不必再包含进下一个检查区间了。
而左闭右开的情况下,若target>mid,那么left=mid+1;若target<mid,那么right=mid;因为right本来就不包含在检查区间里。
3.可以优化的地方:
1>整数溢出避免:在取mid的时候,最容易想到的就是(left+right)/2了,但是在n很大的情况下,left+right可能会溢出,所以可以采用left+(right-left)/2来避免溢出,提高代码健壮性。
2>在开头可以判断一下numsSize的特殊情况0和1,
每次进入循环前判断一下target是否等于left或者right,就不用循环找了,可以提高效率。
27.移除元素
经典的考察数组增删改查的题目。
数组删除元素的时候不能像链表一样方便可以直接删掉改一下指针就行了,必须要把后面的元素一个一个往前挪,就像排队的时候有一个人离开了,后面的人都要依次往前。有很多人离开了的话,这个往前挪的操作就要执行很多次,非常繁琐。
那么,有没有更好的办法呢?当然有。
在遍历的过程中,当数组中一个元素被删除的时候,可以先不急着把后面的元素前移补上。可以用一个指针记录下这个位置,等后面有不需要被删掉的元素的时候,直接把它移过来,然后指针再后移一位,持续这个操作。到最后,因为指针总是比当前的元素前一位,此时指针指向的位置下标,刚好就是删完以后的元素个数。