算法练习DAY1 DAY1 704二分算法 35搜索插入位置 27移除元素

1、二分算法 升序 无重复 力扣704

/**
 * DAY1 704二分算法 35搜索插入位置 27移除元素
 */
public class day1 {
    public static void main(String[] args) {

        int[] nums = {-1,0,3,5,9,12};
        //int[] nums = {12,9,5,3,0,-1};
        int target = 9;
        //int i = searchL(nums, target);
        int i = searchLR(nums, target);
        System.out.println(i);
    }
    //704二分算法

    /**
     * 数组基础知识回顾
     * 使用数组的步骤 1. 声明数组并【开辟空间】 2 给数组各个元素赋值 3 使用数组
     * 不进行开辟空间数组就无法使用!int[] temp = new int[5];
     */

    /**
     *正确解法
     * 核心:用改变数组下标的方式改变遍历区间 并且要抓住题意:升序 无重复
     *      左下标一定是小于或者小于等于右下标的
     */

    //左闭右闭
    public static int searchLR(int[] nums, int target){
        // 避免当 target 小于nums[0] nums[nums.length - 1]时多次循环运算
        if (target < nums[0] || target > nums[nums.length - 1]) {
            return -1;
        }
        int left = 0, right = nums.length - 1;
        while (left <= right) {
            int mid = left + ((right - left) >> 1);
            //这样设计取中间下标,就会避免数组越界的问题
            //比如数组 0 1 2 3  目标值是3  第一次l=0 r=3 m=1 第二次l=2 r=3 m=2 在取数组中间下标总是靠右一些
            //如果靠左一些 int mid = (left+1) + ((right - left) >> 1);
            // 第一次 l=0 r=3 m=2 第二次 l=3 r=3 m=4 数组越界
            if (nums[mid] == target)
                return mid;
            else if (nums[mid] < target)
                left = mid + 1;
            else if (nums[mid] > target)
                right = mid - 1;
        }
        return -1;
    }

    //左闭右开
    public static int searchL(int[] nums, int target){
        if(target<nums[0] || target>nums[nums.length-2]){
            return -1;
        }
        int left = 0;
        //int right = nums.length -1;
        int right = nums.length; //因为已经采用了开区间,不会让区间里含有nums[nums.length]
        while (left < right){ //因为这个时候 left=right 搜索区间就相当于 [3,3) 没有意义
            int middle = left + ((right - left)>>1);
            if(target == nums[middle]){
                return middle;
            }else if(target > nums[middle]){
                left = middle + 1;
            }else if(target < nums[middle]){
                right = middle;  //因为排除掉mid处的值 其实就是[left,mid),[mid+1,right)
            }
        }
        return -1;
    }
    /*左闭右开例子:
    * 有数组[0 1 2 3 4 5]  target = 1
    * 第一次 l=0 r=6 m=3
    * 第二次 l=0 r=m=3 m=1
    * (3位置这个数其实已经比较过,可以不用再比,由于是右开区间 所以r=m 若是r=m-1 会丢失比较数据
    * 
    *)
    * */
    

    /**
     * 初始解法
     * 考虑到二分 也考虑到用中间值去比较
     * 但是去动原数组显然是不明智的 因为要考虑引用的替换以及新数组的长度
     * 远不如变换遍历的下标轻松快捷
     * 在尝试更该引用的时候要注意数组越界以及 int[] temp;这样定义数组 temp实际为 null 在调用temo[0]时就会报空指针异常
     */
    /*public static int search(int[] nums, int target) {
        //采用左闭右开
        for(int i = nums.length/2;i<nums.length;){
            if(target < nums[i]){
                int[] temp = new int[i];
                for(int j = 0;j<i;j++){
                    temp[j] = nums[j];
                }
                nums = temp;
            }else if(target > nums[i]){
                int[] temp = new int[i];
                for(int j = 0 ; (j+i+1) < nums.length ; j++){
                    temp[j] = nums[j+i+1];
                }
            }else if(target == nums[i]){
                return i;
            }
        }
        return -1;
    }*/
}

2、35 搜索插入位置

/**
 * 35 搜索插入位置
 */
public class DAY1_2 {
    public static void main(String[] args) {
        //int[] nums = {-1,0,3,5,9,12};
        int[] nums = {12,9,5,3,0,-1};
        int target = -19;
        int i = searchA(nums, target);
        //int i = searchL(nums, target);
        //int i = searchLR(nums, target);
        System.out.println(i);
    }



    /**
     * 正确解法 左闭右闭
     */
    public static int searchLR(int[] nums, int target) {
        int left = 0;
        int right = nums.length - 1;
        while (left <= right) {
            int middle = left + ((right - left) >> 1);
            if (nums[middle] == target) {
                return middle;
            } else if (nums[middle] > target) {
                right = middle - 1;
            } else if (nums[middle] < target) {
                left = middle + 1;
            }
        }
        return left; //right+1也行 因为到这一步区间就是[nums[right],nums[left]],插入值就值这两个之间
    }
    /**
     * 左闭右开
     */
    public static int searchL(int[] nums, int target) {
        int left = 0;
        int right = nums.length; //因为已经采用了开区间,不会让区间里含有nums[nums.length]
        while (left < right){
            int middle = left + ((right - left) >> 1);
            if(nums[middle] == target){
                return middle;
            }else if(nums[middle] < target){
                left = middle + 1;
            }else if(nums[middle] > target){
                right = middle;
            }
        }
        //退出while的情况只会是 left = right [left,right)
        //又由于 right始终是开的 也就是始终比搜索区间高出一位
        return right;
    }


    //降序 左闭右闭
    public static int search(int[] nums, int target) {
        int left = 0;
        int right = nums.length-1;
        while (left<=right){
            int middle = left + ((right - left) >> 1);
            if(nums[middle] == target){
                return target;
            }else if(nums[middle] < target){
                right = middle -1;
            }else if(nums[middle] > target){
                left = middle + 1;
            }
        }
        return left;
    }

    //降序 左闭右开
    public static int searchA(int[] nums, int target) {
        int left = 0;
        int right = nums.length;
        while (left < right){
            int middle = left + ((right - left) >> 1);
            if(nums[middle] == target){
                return target;
            }else if(nums[middle] < target){
                right = middle ;
            }else if(nums[middle] > target){
                left = middle + 1;
            }
        }
        return right;
    }

/**
 *自己写的 考虑了插入的四种情况 但是显得笨拙
     */
    //左闭右闭 分四种情况讨论  可行 但显得有点笨拙
    /*public static int searchLR(int[] nums, int target) {
        int left = 0;
        int right = nums.length - 1;
        while (left <= right) {
            int middle = left + ((right - left) >> 1);
            if (nums[middle] == target) {
                return middle;
            } else if (nums[middle] > target) {
                right = middle - 1;
            } else if (nums[middle] < target) {
                left = middle + 1;
            }
        }
        if (left == right) {
            if (nums[left] < target) {
                return left + 1;
            } else if (nums[left] > target) {
                return left;
            }
        } else if (left > right) {
            if (right > 0) {
                return nums.length;
            } else if (right < 0) {
                return 0;
            }
        }
        return -1;
    }*/
}

3、27 移除元素

/**
 * 27 移除元素
 */
public class DAY1_27 {
    public static void main(String[] args) {
        int[] nums = {0,1,2,2,3,0,4,2};
        int val = 2;
        //int i = removeElementSF(nums, val);
        int i = removeElementD(nums, val);
        for (int j = 0; j < i; j++) {
            System.out.println(nums[j]);
        }
    }



    /**
     *    快指针:寻找新数组的元素 ,新数组就是不含有目标元素的数组
     *    慢指针:指向更新 新数组下标的位置
     *    其实就是快指针按照数组顺序依次遍历,
     *    把遍历到不等于val的值从数组第0位开始存放,
     *    慢指针用于记录一共存放了多少个数字 返回慢指针就行
     */
    public static int removeElementSF(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;
    }

    /**
     * 双向指针
     * 就是左右都开始查找
     * 先找到右边第一个不等于val的值
     * 然后从左边开始遍历 要是有一个等于val的 就用右边遍历到的值替换他
     * 依次进行直到 left>right
     */
    public static int removeElementD(int[] nums, int val) {
        int left = 0;
        int right = nums.length - 1;
        while (right>=0 && nums[right] == val) right--;
        while (left <= right){
            if(nums[left] == val){
                nums[left] = nums[right];
                right--;
                while (right>=0 && nums[right] == val) right--;
            }
            left++;
        }
        return left;
    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值