Day1 BinarySearch
class Solution {
public int search(int[] nums, int target) {
if(target > nums[nums.length-1] || target < nums[0])
return -1;
int l = 0, r = nums.length-1;
while(l <= r){
int mid = (l+r)/2;
if(nums[mid] == target){
return mid;
}else if(nums[mid] > target){
r = mid-1;
}else{
l = mid+1;
}
}
return -1;
}
}
-
需要注意的是 边界条件的确定
-
while(l <= r) //用于处理当数组中只有一个元素的情况,避免不进入循环直接输出-1
-
return nums[l] == target? l:-1;//亦可以使用返回时的判断,达到相同目的
-
class Solution {
public int search(int[] nums, int target) {
int n = nums.length;
if(target > nums[n-1] || target < nums[0])
return -1;
int l = 0, r = n-1;
while(l < r){
int mid = l+r>>1;
if(target <= nums[mid]){ // 当mid比target大时,目标区间在mid左侧
r = mid; // 所以向左趋近,r = mid
// 最终的值是目标区间里最左的值,最小结果
}else{
l = mid+1; // 不满足,目标区间在右侧,直接mid+1
}
}
return target == nums[l]? l : -1;
}
}
- 当目标区间在mid左侧时,反复用 r = mid 向左趋近,最终得到的结果为目标区间的最左端点
class Solution {
public int search(int[] nums, int target) {
int n = nums.length;
if(target > nums[n-1] || target < nums[0])
return -1;
int l = 0, r = n-1;
while(l<r){
int mid = (l+r+1)>>1; // 此处处理+1
if(nums[mid] <= target){
l = mid; // 向右趋近,目标值在目标区间的右端点
}else{
r = mid-1;
}
}
return nums[l] == target? l : -1;
}
}
- 注意 (l+r+1)>>1 ,加1是为了当找到结果值时可以使mid正常更新,进而更新 l 或者 r ,否则会使得 l=mid反复执行,陷入死循环
- l=mid 反复向右趋近,最终结果值为目标区间的右端点
public class Solution extends VersionControl {
public int firstBadVersion(int n) {
int l = 1,r = n;
while(l<r){
int mid = l+r>>>1;
if(isBadVersion(mid)){
r = mid;
}else{
l = mid+1;
}
}
return l;
}
}
- 典型的寻找左边界问题,要寻找最小的错误版本,及在目标区间内取得左端点值
- 需要注意的是 定点数运算溢出的问题
- 不能用(left+right)/2 形式,两个值的初始值超过int限定大小的一半,则会发生溢出,更正办法为:
- (left + right) >>> 1 改算术右移为逻辑右移,当无符号数处理
- 或是 left+(right-left)/2 的形式,只加差值的一半,避免溢出
- 不能用(left+right)/2 形式,两个值的初始值超过int限定大小的一半,则会发生溢出,更正办法为:
- 又一个二分查找的典型问题
- 插入位置应当为大于目标值区间的最左侧,即大于target区间的左边界就是插入位置(替换位置)
class Solution {
public int searchInsert(int[] nums, int target) {
int n = nums.length;
if(n == 0 || target<nums[0]) return 0;
if(target > nums[n-1]) return n;
int l = 0, r = n-1;
while(l<r){
int mid = l+r>>>1;
if(nums[mid] >= target){
r = mid;
}else{
l = mid+1;
}
}
return l;
}
}
- 插入位置应当为小于目标值区间的最右侧,正确位置应当为小于目标值区间的最右侧+1(紧随其后)
class Solution {
public int searchInsert(int[] nums, int target) {
int n = nums.length;
if(n == 0 || target<nums[0]) return 0;
if(target > nums[n-1]) return n;
int l = 0, r = n-1;
while(l<r){
int mid = (l+r+1)>>>1;
if(nums[mid] <= target){
l = mid;
}else{
r = mid-1;
}
}
return nums[l] == target? l : l+1;//重要改动
}
}
-
插入位置为区间的末尾,故返回时判断若为插入则需要 l+1
-
一个简单的遍历方法
class Solution {
public int searchInsert(int[] nums, int target) {
int n = nums.length;
for(int i = 0;i<n;i++){
if(nums[i] >= target){
return i;
}
}
return n;
}
}
- 关键在于及时跳出循环,避免超时
Day2 双指针
- 双指针更多的是一种编程思想和技巧,仅使用两个变量动态存储不同的位置节点,常常用在数组、链表等线性的数据结构当中,链表应用得更多且更容易感受到双指针的巨大作用。由于链表是树形和图形数据结构的基础,故有时在树和图的算法中也会用到双指针。
- 固定套路:
- 快慢指针:
- 寻找链表的中点
- 判断链表是否有环
- 判断链表中环的起点
- 求链表中环的长度
- 求链表中倒数第K个元素
- 碰撞指针
- 二分查找问题(l & r)
- n数之和问题
class Solution {
public int[] sortedSquares(int[] nums) {
int index = 0;
int n = nums.length;
//预处理,确定正负元素分界点
while(index < n && nums[index] < 0){
index++;
}
//处理数组得到平方
for(int i =0;i<n;i++){
nums[i] = nums[i]*nums[i];
}
int i = index, j = index-1, k = 0;
//结果数组
int[] ans = new int[n];
//归并过程
while(i<n && j>=0){
if(nums[i] < nums[j]){
ans[k++] = nums[i++];
}else{
ans[k++] = nums[j--];
}
}
//对nums剩余部分的处理
while(i<n){
ans[k++] = nums[i++];
}
while(j>=0){
ans[k++] = nums[j--];
}
return ans;
}
}
class Solution {
public int[] sortedSquares(int[] nums) {
int n = nums.length;
int index = 0;
for(int i = 0;i<n;i++){
nums[i] = nums[i]*nums[i];
}
int i = 0, j = n-1, k = n-1;
int[] ans = new int[n];
//倒序将较大的元素放入新数组中
while(i <= j){
//此处等于号=的作用为将最后一个元素置于新数组中
if(nums[j] > nums[i]){
ans[k--] = nums[j--];
}else{
ans[k--] = nums[i++];
}
}
return ans;
}
}
- 碰撞指针,减少了两个子数组剩余部分的处理,将条件简化为两个指针的相遇,也更好理解
- 双指针——碰撞指针(reverse中的)
- reverse函数写法熟悉
class Solution {
public void rotate(int[] nums, int k) {
//重要的预处理,减少了很多的reverse次数并且避免了在reverse函数中的边界判断
k %= nums.length;
reverse(nums,0,nums.length-1);
reverse(nums,0,k-1);
reverse(nums,k,nums.length-1);
}
private void reverse(int[] nums, int left, int right){
while(left < right){
int tmp = nums[left];
nums[left] = nums[right];
nums[right] = tmp;
left++;right--;
}
}
}