代码随想录算法训练营第一天| 704. 二分查找、27. 移除元素 、977.有序数组的平方

704. 二分查找

给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。

public class BinarySearch {

    public int binarysearch(int[] nums,int target){
        int left=0;
        int right = nums.length-1;
        while(left<=right){
            int mid = (left+right)/2;
            if(target < nums[mid]){
                right = mid-1;
            } else if (target > nums[mid]) {
                left = mid+1;
            } else if (target == nums[mid]) {
                return mid;
            }
        }
        return -1;
    }


    public static void main(String[] args) {
        BinarySearch binarysearch = new BinarySearch();
        int[] nums = {1, 2, 3, 4, 5, 6, 7, 8};
        int target = 5;
        int res = binarysearch.binarysearch(nums,target);
        System.out.println(res);

    }
}

写错了,left = mid;导致程序卡死;

解题思路:

初始化左右指针
我们定义两个指针,left 指向数组的开头,right 指向数组的末尾,表示当前要查找的区间。

  1. 计算中间位置
    在每次迭代中,我们计算中间位置 mid = (left + right) / 2,即取当前查找范围的中间元素。

  2. 比较中间元素与目标值

    • 如果 nums[mid] 等于 target,则说明找到了目标值,返回中间位置 mid
    • 如果 target 小于 nums[mid],则目标值只可能出现在左半部分,将 right 移动到 mid - 1
    • 如果 target 大于 nums[mid],则目标值只可能出现在右半部分,将 left 移动到 mid + 1
  3. 循环条件
    继续重复上述过程,直到 left 超过 right,表示整个数组已经遍历完毕且未找到目标值,此时返回 -1

  4. 代码简述

    • 通过 while (left <= right) 循环控制查找范围,逐步缩小查找的区间。
    • 每次循环中通过 mid 找到中间元素,进行比较并移动 leftright,从而实现查找范围的缩小。
    • 如果找到目标值则返回其下标,否则返回 -1

27. 移除元素

题目:给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。

不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并原地修改输入数组。

元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。

示例 1: 给定 nums = [3,2,2,3], val = 3, 函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。 你不需要考虑数组中超出新长度后面的元素。

示例 2: 给定 nums = [0,1,2,2,3,0,4,2], val = 2, 函数应该返回新的长度 5, 并且 nums 中的前五个元素为 0, 1, 3, 0, 4。

你不需要考虑数组中超出新长度后面的元素。

public class Delete {

    public int  deletenum_for(int[] nums, int target){
        int size = nums.length;
        for(int i=0;i<size;i++){
            if(nums[i] == target){
                for(int j=i;j<size-1;j++){
                    nums[j] = nums[j+1];
                }
                size--;
                i--;
            }
        }
        return size;
    }
//(版本一)快慢指针法
    public int  removeElement(int[] nums, int target){
        int low = 0;
        for(int i=0;i<nums.length;i++){
            if(nums[i] != target){
                nums[low] = nums[i];
                low ++;
            }
        }
        return low;
    }

//    # 相向双指针法
//# 时间复杂度 O(n)
//# 空间复杂度 O(1)
    public int  removeElement_two(int[] nums, int target){
        int left =0;
        int right = nums.length-1;
        while(left<=right){
            if(nums[left]!=target)
            {
                left++;
            } else if (nums[right]==target) {
                right--;
            }else {
                nums[left] = nums[right];
                left++;
                right--;
            }


        }
        return left;

    }
    public static void main(String[] args) {
        Delete bs = new Delete();
        int[] nums = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
        System.out.println(bs.removeElement_two(nums, 5));
    }
}

出现问题:

方法 1:deletenum_for(嵌套循环删除法)

问题

  • 使用了两个嵌套的 for 循环来删除元素。内层循环会将删除的元素后面的所有元素向前移动。虽然逻辑是正确的,但返回的是 size(即 nums.length - 1),这实际上会导致删除后的数组长度错误。

  • 该方法的时间复杂度是 O(n²),因为每次删除元素都需要移动后面的元素,效率较低。


方法 2:removeElement(快慢指针法)

问题

  • 存在边界条件问题。在 for 循环中,应该遍历整个数组,即 i < nums.length,而不是 i < nums.length - 1

  • 指针 low 的递增应该在赋值之后进行,而不是在之前,否则会导致未正确覆盖数组元素。


方法 3:removeElement_two(相向双指针法)

问题

  1. 循环条件应该是 while (left <= right),否则可能会漏掉最后一个元素的比较。
  2. 在元素交换后,左指针 left 应该递增。

方法 1:嵌套循环删除法(deletenum_for

思路:
  • 我们通过两层循环,第一层遍历数组中的每一个元素,当遇到需要删除的目标值时,通过内层循环将后续所有元素向前移动。

方法 2:快慢指针法(removeElement

思路:
  • 通过快慢指针来实现删除的操作。快指针负责遍历整个数组,慢指针则用于记录不等于 val 的元素。
  • 当快指针遍历到不等于 val 的元素时,将该元素赋值给慢指针所指向的位置,并且慢指针递增。
  • 最终,慢指针的值即为移除元素后数组的新长度。
复杂度分析:
  • 时间复杂度:O(n),遍历数组一次。
  • 空间复杂度:O(1),只使用常量空间。
  • 代码简洁性:逻辑清晰,操作简便。

方法 3:相向双指针法(removeElement_two

思路:
  • 通过双指针从数组两端向中间收缩。左指针 left 从数组的起始位置开始,右指针 right 从数组的末尾开始。
  • 当左指针遇到目标元素时,我们将右指针的元素替换到左指针的位置,并同时移动两个指针,直到左右指针相遇。
  • 这样实现了在遍历过程中对数组元素的原地移除,最终返回左指针的值作为新的数组长度。
复杂度分析:
  • 时间复杂度:O(n),每个元素最多遍历一次。
  • 空间复杂度:O(1),不使用额外空间。

977.有序数组的平方

给你一个按 非递减顺序 排序的整数数组 nums,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。

示例 1:

输入:nums = [-4,-1,0,3,10]
输出:[0,1,9,16,100]
解释:平方后,数组变为 [16,1,0,9,100],排序后,数组变为 [0,1,9,16,100]
示例 2:

输入:nums = [-7,-3,2,3,11]
输出:[4,9,9,49,121]

import java.util.Arrays;

public class SortedSquares {
    public int[]  sortedSquares(int[] nums){
        for(int i=0;i<nums.length;i++){
            nums[i] = nums[i]*nums[i];
        }
        int size = nums.length;
        for(int i=0;i<nums.length;i++){
            int max = 0;
            for(int j=0;j<size;j++){
                if(max<nums[j]){
                    max = nums[j];
                }
            }
            size--;
            nums[size] = max;
        }
        return nums;
    }
    public int[]  sortedSquares_two(int[] nums){
        int left = 0;
        int right = nums.length-1;
        int[] result = new int[nums.length];
        int j = nums.length-1;
        for(int i=0;i<nums.length;i++){
            if(nums[left]*nums[left]<nums[right]*nums[right]){
                result[j] = nums[right]*nums[right];
                right--;
            }else{
                result[j] = nums[left]*nums[left];
                left++;
            }
            j--;
        }
        return result;
    }
    public static void main(String[] args) {
        SortedSquares bs = new SortedSquares();
        int[] nums = {-10, 2, 3, 4, 5, 6, 7, 8, 9, 10};
        System.out.println(bs.sortedSquares(nums));
    }
}

方法 1:直接排序法
平方所有元素:我们首先对数组中的每个元素进行平方操作。由于数组中的负数平方后会变成正数,导致平方后的数组顺序不再有序。
排序数组:对平方后的数组使用排序算法(如 Java 提供的 Arrays.sort())进行排序。
返回结果:返回排序后的平方数组。
时间复杂度:
平方操作:O(n),其中 n 是数组的长度。
排序操作:O(n log n)。
总时间复杂度:O(n log n)。

方法 2:双指针法(推荐)
分析平方特性:由于数组是非递减排序的,所以数组的负数部分平方后可能会变成比正数部分的元素还大的值。因此,平方后的最大值要么出现在数组的最左边,要么出现在最右边。
双指针遍历:我们可以设置两个指针,一个从数组的左边(left),一个从右边(right)开始。每次比较两个指针对应元素的平方,将较大的平方值放入新数组的末尾。
填充结果数组:从结果数组的最后一个位置开始填充,直到左右指针相遇。
时间复杂度:
平方操作与数组遍历:O(n),只需要遍历数组一次,进行平方操作并填充结果数组。
总时间复杂度:O(n)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值