Leetcode算法入门学习记录

Day1 BinarySearch

  1. BinarySearch
  2. 标准的二分法模板
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;//亦可以使用返回时的判断,达到相同目的
      
  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 向左趋近,最终得到的结果为目标区间的最左端点
  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)>>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 反复向右趋近,最终结果值为目标区间的右端点
  1. First Bad Version
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 的形式,只加差值的一半,避免溢出
  1. Search Insert Position
  • 又一个二分查找的典型问题
  • 插入位置应当为大于目标值区间的最左侧,即大于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 双指针

  • 双指针更多的是一种编程思想和技巧,仅使用两个变量动态存储不同的位置节点,常常用在数组、链表等线性的数据结构当中,链表应用得更多且更容易感受到双指针的巨大作用。由于链表是树形和图形数据结构的基础,故有时在树和图的算法中也会用到双指针。
  • 固定套路:
    • 快慢指针:
      1. 寻找链表的中点
      2. 判断链表是否有环
      3. 判断链表中环的起点
      4. 求链表中环的长度
      5. 求链表中倒数第K个元素
    • 碰撞指针
      1. 二分查找问题(l & r)
      2. n数之和问题
  1. Squares of a Sorted Array
  • 双指针+归并排序
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;
        
    }
}
  • 本题应用双指针结合归并排序的思想方法,双指针将nums分割成待归并的两个子数组

  • 复杂度为On

  • 碰撞指针且不需要边界判断(优化)
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;
    }
}
  • 碰撞指针,减少了两个子数组剩余部分的处理,将条件简化为两个指针的相遇,也更好理解
  1. Rotate Array
  • 双指针——碰撞指针(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--;
        }
    }
}
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值