【Leetcode】-代码随想录算法训练营Day1 | 704. 二分查找,27. 移除元素

二分查找

Leetcode题目-Binary Search

链接: 704. Binary Search

特殊条件

  1. 有序数组
  2. 元素unique
    遇到上述限定条件可以考虑用二分法。

思路关键

二分法中,两个左右指针划定的区间的边界定义与指针的移动规则息息相关。

一般常用的写法思路有两种

  1. 左闭右闭区间
  2. 左闭右开区间

根据区间的定义,处理每次循环时边界变更的操作。

关键注意

起始的左右边界是0, nums.length-1?还是0, nums.length-1?,循环的条件是left<right还是left<=right,以及调整边界时左右边界是mid?mid+1?还是mid-1?

另在处理过程中注意防止溢出。

代码实现

1. 左闭右闭区间 [left, right]

 class Solution {
   public int search(int[] nums, int target) {
   		int left = 0, right = nums.length-1;  // 右闭区间,所以右边界为nums.length-1
   		// 因为left=right时,[left, right]即[left, left]无效空间,所以left可取=right
   		while (left <= right) {
   			int mid = left + (right - left) / 2;    // 防止溢出
   			// 因为mid所指值小于target,且区间为左闭右闭,所以新区间不包含mid,新区间左边界调整为mid+1
   			if (nums[mid] < target) left = mid + 1; 
   			// 因为mid所指值大于target,且区间为左闭右闭,所以新区间不包含mid,新区间右边界调整为mid-1
   			if (nums[mid] > target) right = mid - 1;
   			// mid所指值等于target时,已找到,直接返回
   			if (nums[mid] == target) return mid;
   		}
   		// 未找到,返回-1
   		return -1;
   }
}

2. 左闭右开区间 [left, right)

class Solution {
   public int search(int[] nums, int target) {
   		int left = 0, right = nums.length;  // 右开区间,所以右边界为nums.length
   		// 因为left=right时,[left, right)即[left, left)无效空间,所以left不取=right
   		while (left < right) {
   			int mid = left + (right - left) / 2;    // 防止溢出
   			// 因为mid所指值小于target,且区间为左闭右开,所以新区间不包含mid,新区间左边界调整为mid+1
   			if (nums[mid] < target) left = mid + 1; 
   			// 因为mid所指值大于target,且区间为左闭右开,所以新区间右边界直接为mid即可
   			if (nums[mid] > target) right = mid;
   			// mid所指值等于target时,已找到,直接返回
   			if (nums[mid] == target) return mid;
   		}
   		// 未找到,返回-1
   		return -1;
   }
}

总结

时间复杂度:O(log n)
空间复杂度:O(1)
根据二分法的使用条件,看到有序,元素唯一,可以考虑二分法查找。
二分法的关键是理清所操作的边界的定义,并根据边界的定义决定区间移动时的操作。
代码随想录文章讲解:传送门
代码随想录视频讲解:传送门

双指针

Leetcode题目-Remove Element

链接: 27. Remove Element

特殊条件

不使用额外的数组空间,在原数组上操作。

思路关键

数组中的元素时不能删除只能覆盖的,所谓的“删除”即用后面的元素覆盖要删除的元素,所以执行List.remove等库函数时,其实际复杂度为O(n)
由于java的int数组类型长度固定,所以实际返回的长度后面会有多余元素。不过此题讲了,只检验前面的元素。

两个实现思路

  1. 暴力解法
  2. 双指针(快慢指针)

代码实现

1. 暴力解法

class Solution {
    public int removeElement(int[] nums, int val) {
        // 暴力解法
        int i = 0, count = 0;
        while (i < nums.length) {
            if (nums[i] == val) {
                count++;
                int j = i;
                while (j < nums.length - 1) {
                    nums[j] = nums[j + 1];
                    j++;
                }
                nums[j] = 0;
            } else {
                i++;
            }
        }

        return count;
    }
}

haha此方法没过,超时了

2. 双指针(快慢指针)

class Solution {
    public int removeElement(int[] nums, int val) {
        int fast = 0, slow = 0;
        for (; fast < nums.length; fast++) {
            if (nums[fast] != val) {
                nums[slow++] = nums[fast];
            }
        }
        return slow;
    }
}

总结

暴力解法的时间复杂度:O(n^2)
暴力解法的空间复杂度:O(1)
双指针法的时间复杂度:O(n)
双指针法的空间复杂度:O(1)
显然双指针法优,且双指针法的思路有很多可扩展移植之处,如链表、字符串、数组等。
代码随想录文章讲解:传送门
代码随想录视频讲解:传送门

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值