非科班,之前只是学过数据结构课,基础较差,写给自己的碎碎念
day1: 34题2分法的方法没做出来,69、367有int,long的坑,27题没做出来快慢指针的做法。
704:力扣题目链接
首先是二分查找法的使用前提条件:
1.数组为有序数组
2.数组中无重复元素
确定可以使用后,有两种写法
1.左闭右闭:这是我之前一直用的写法,其中需要注意的是int mid = start + (end - start)/2或者是int mid = start + (end - start)>>1。这样相比于常见的(start = end)/2,可以防止溢出。其实这个也很好理解,当成向量理解就觉得更合理了。
class Solution {
public int search(int[] nums, int target) {
int start = 0;
int end = nums.length-1;
while(start <= end){
int mid = start + ((end - start) / 2) ;
if(nums[mid] < target){
//target在右边
start = mid + 1;
}else if(nums[mid] > target){
end = mid - 1;
}else if(nums[mid] == target){
return mid;
}
}
return -1;
}
}
2.左闭右开:注意左闭右开的话,[start,end),要想让他是一个合法的区间,那么start是不可能等于end的,比如[3,3)是错的。所以while里的条件应该是start < end。同时注意,在比较时,右边的那个数始终是不参与比较的,所以end = mid就可以,如果是mid - 1的话,就漏掉了一个数。左边因为是开区间所以不变。故完整代码是:
class Solution {
public int search(int[] nums, int target) {
int start = 0;
int end = nums.length;
while(start < end){
int mid = start + ((end - start) / 2) ;
if(nums[mid] < target){
//target在右边
start = mid + 1;
}else if(nums[mid] > target){
end = mid;
}else if(nums[mid] == target){
return mid;
}
}
return -1;
}
}
35.力扣题目链接
我个人不考虑二分法的做法是:
因为这是一个排序数组,那么只需要从头开始遍历,得到的第一个大于或者等于这个target的数的索引就是该target输入的位置。
class Solution {
public int searchInsert(int[] nums, int target) {
for(int i = 0; i < nums.length; i++){
if(target <= nums[i]){
return i;
}
}
return nums.length;
}
}
如果要用二分查找法的方式做,那么:
class Solution {
public int searchInsert(int[] nums, int target) {
// for(int i = 0; i < nums.length; i++){
// if(target <= nums[i]){
// return i;
// }
// }
// return nums.length;
int start = 0;
int end = nums.length - 1;
while(start <= end){
int mid = start + (end - start)/2;
if(nums[mid] < target){
start = mid + 1;
}
else if(nums[mid] > target){
end = mid - 1;
}
else{
return mid;
}
}
return start;
}
}
这个题的中心思想和704差不多,当target不存在的数时,始终返回的是start的索引(也可以认为是end+1,因为跳出循环的时候始终是start = end - 1)。
尝试用第二种方法做:
class Solution {
public int searchInsert(int[] nums, int target) {
// for(int i = 0; i < nums.length; i++){
// if(target <= nums[i]){
// return i;
// }
// }
// return nums.length;
int start = 0;
int end = nums.length;
while(start < end){
int mid = start + (end - start)/2;
if(nums[mid] < target){
start = mid + 1;
}
else if(nums[mid] > target){
end = mid;
}
else{
return mid;
}
}
return start;
}
}
这里也可以返回end,因为此时跳出循环的end = start
34.力扣链接
这道题我一开始想用基于数组的暴力解题法,结果自己逻辑水平太有限实在是弄不出来,多举几个例子就错了,最后我采用了集合arraylist的方法,如下:
class Solution {
public int[] searchRange(int[] nums, int target) {
ArrayList<Integer> list = new ArrayList();
int[] arr = new int[2];
for(int i = 0; i< nums.length; i++){
if(nums[i] == target){
list.add(i);
}
}
if(list.size() == 0){
arr[0] = arr[1] = -1;
}else {
arr[0] = list.get(0);
arr[1] = list.get(list.size()-1);
}
return arr;
}
}
如果要用二分法的话,重点是要找到左边界和右边界,随想录提供的第一种方法我还需要消化一下,这里用第二种方法,觉得更好解释:
首先用二分法确定是否存在该target,若不存在则直接输出{-1,1};
若存在,则在数组范围内(0,nums.length -1)逐步查找left和right的范围。
class Solution {
public int[] searchRange(int[] nums, int target) {
int num = binarySearch(nums, target);
if(num == -1){
return new int[] {-1,-1};
}else{
int left = num;
int right = num;
while(left >= 0 && nums[left] == target){
left --;
}
while(right <= nums.length - 1 && nums[right] == target){
right ++;
}
return new int[] {left+1,right-1};
}
}
public int binarySearch(int[] nums, int target){
int start = 0;
int end = nums.length - 1;
while(start <= end){
int mid = start + (end - start)/2;
if(nums[mid] < target){
start = mid + 1;
}else if(nums[mid] > target){
end = mid - 1;
}else{
return mid;
}
}
return -1;
}
}
69.力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台
中心思想是求左边界,以为很快就做完了,结果有一个int和long的坑,卡了很久才反应过来.注意当x为整数类型时,x/2的平方是非常有可能溢出的。
class Solution {
public int mySqrt(int x) {
int start = 0;
int end = x;
int bound = -1;
while(start <= end){
int mid = start + (end - start)/2;
long result = (long)mid*mid;
if(result <= x){
bound = mid;
start = mid + 1;
}else{
end = mid - 1;
}
}
return bound;
}
}
367. 力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台
同理。求左边界
class Solution {
public boolean isPerfectSquare(int num) {
int start = 0;
int end = num;
while(start <= end){
int mid = start + (end - start)/2;
long result = (long)mid*mid;
if(result == num){
return true;
}else if(result < num){
start = mid + 1;
}else{
end = mid - 1;
}
}
return false;
}
}
27 .力扣题目链接
这个题要求原地移除元素并返回移除后的长度,其中移除的含义是指通过返回的数组长度遍历数组,得到剩下的数组。这里我一开始是想到了先把要移除的元素变成-1,之后遍历得到数组中非负数的长度,最后逆冒泡算法,使负数都排到数组后面,得到结果。
class Solution {
public int removeElement(int[] nums, int val) {
for(int i = 0; i<nums.length;i++){
if(nums[i] == val){
nums[i] = -1;
}
}
int len = 0;
for(int i = 0; i<nums.length;i++){
if(nums[i] != -1){
len ++;
}
}
for(int i = 0; i<nums.length-1;i++){
for(int j = i+1; j<nums.length;j++){
if(nums[i] < nums[j]){
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
}
}
return len;
}
}
这种方法执行时间长。
用快慢指针法,则
- 快指针:寻找新数组的元素 ,新数组就是不含有目标元素的数组
- 慢指针:指向更新 新数组下标的位置
也就是说快指针起到遍历的作用一直往前走,边走便判断,慢指针则主要起到了确定新数组下标的作用,当不是val时它也跟着遍历,当是val时,它这一轮就停住了,等下一轮去覆盖它。
class Solution {
public int removeElement(int[] nums, int val) {
int slowIndex = 0;
for(int fastIndex = 0; fastIndex<nums.length;fastIndex++){
if(nums[fastIndex] != val){
nums[slowIndex] = nums[fastIndex];
slowIndex++;
}
}
return slowIndex;
}
}