leetcode 刷题_day01_数组01_二分查找&同向快慢指针

本文介绍了在有序数组中使用二分查找算法的两种情况(左闭右闭和左闭右开),提供了Rust和Java的代码示例,并讨论了它们的时间复杂度(O(logn))和空间复杂度(O(1))。同时,针对移除元素问题,通过双指针法在原数组上进行O(1)空间复杂度的操作。
摘要由CSDN通过智能技术生成

704. 二分查找

思路

给定的数组有序且不重复,因此可以使用二分法解题

解题方法

二分法通过不断拆分数组来定位 targe,因此数组的定义就十分重要,这里有两种情况

  1. 左闭右开
  2. 左闭右闭

不管哪种情况,都需要确保两件事

  1. 遍历时,不能遗漏数组合法区间内的元素
  2. 构建数组时,不能遗漏也不能多添加元素

因此,对于左闭右闭的情况

  1. while 循环时,while定义了当前数组的所有元素,且右边界合法,因此 left <= right
  2. 构建下一个子区间时,需要保证左右边界合法且不能囊括已排除的元素

对于左闭右开的情况

  1. while 循环时,while定义了当前数组的所有元素,且右边界不合法,因此 left < right
  2. 构建下一个子区间时,需要保证右边界不合法,因此右边界可以为已排除元素,左边界则不能

复杂度

  • 时间复杂度:
    O ( l o g n ) O(logn) O(logn)

  • 空间复杂度:
    O ( 1 ) O(1) O(1)

Code

Rust

fn main() {
    println!("============= 左闭右闭 =================");
    let nums = vec![5];
    let target_1 = -5;
    // let target_2 = 12;
    let result = search_1(&nums, target_1);
    // let result_2 = search_1(&nums, target_2);
    println!("{}", result);
    // println!("{}", result_2);
    // println!("============= 左闭右开 =================");
    let result_3 = search_2(&nums, target_1);
    // let result_4 = search_2(&nums, target_2);
    println!("{}", result_3);
    // println!("{}", result_4);
}

/**
 * 左闭右闭
 */
fn search_1(nums: &Vec<i32>, target: i32) -> i32 {
    let mut left: i32 = 0;
    let mut right: i32 = (nums.len() - 1) as i32;
    // 遍历需要遍历到合法区间的每个数值
    while right >= left {
        let middle = (left + right) / 2;
        // 右半边
        if nums.get(middle as usize).unwrap() < &target {
            // 新区间不能包括旧区间的数值
            left = middle + 1;
        }
        // 左半边
        else if nums.get(middle as usize).unwrap() > &target {
            // 新区间不能包括旧区间的数值
            // 注意,如果 middle 的类型为 usize,如果 middle = 0 - 1,则会导致数值溢出
            right = middle - 1;
        }
        // 找到
        else {
            return middle as i32;
        }
    }
    -1
}

/**
 * 左闭右开
 */
fn search_2(nums: &Vec<i32>, target: i32) -> i32 {
    let mut left: usize = 0;
    let mut right: usize = nums.len() - 1;

    // 左闭右开,因此最右边的数值不是合法区间,不能遍历到右极值
    while right > left {
        let middle = (left + right) / 2;
        if nums.get(middle).unwrap() < &target {
            left = middle + 1;
        } else if nums.get(middle).unwrap() > &target {
            // 构建左半边时要特别注意,合法区间内不能漏值,同时最右极值不在区间内
            // 因此,让右节点为之前的 middle 即可
            // 注意,这里不会有负数情况,因此可以用 usize
            right = middle;
        } else {
            return middle as i32;
        }
    }
    -1
}

Java

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

27. 移除元素

思路

  1. 要求O(1),因此就不能使用另一个数组来暂存,只能使用指针在原数组中操作
  2. 删除操作,在数组中实际上就是覆盖操作

解题方法

使用双指针法进行解题
快指针负责遍历数组,慢指针负责组装(覆盖)目标数组;如果是非删除数据,则将快指针的数据赋值给慢指针

复杂度

  • 时间复杂度:
    O ( n ) O(n) O(n)

  • 空间复杂度:
    O ( 1 ) O(1) O(1)

Code

Rust

fn main() {
    let mut nums:Vec<i32> = vec![3, 2, 2, 3];
    let val = 3;
    let length = remove_element(&mut nums, val);
    println!("{}", length);
}

fn remove_element(nums: &mut Vec<i32>, val: i32) -> i32 {
    let mut slow_index = 0;
    for pos in 0..nums.len() {
        if nums[pos] != val {
            nums[slow_index] = nums[pos];
            slow_index += 1;
        }
    }
    return slow_index as i32;
}

Java

class Solution {
    public int removeElement(int[] nums, int val) {
        // o1 的空间
        int slowIndex = 0;
        // 快指针遍历数组
        for(int fastIndex = 0; fastIndex < nums.length; fastIndex++) {
            // 如果是非删除数据,则将快指针的数据赋值给慢指针
            if (nums[fastIndex] != val) {
                nums[slowIndex++] = nums[fastIndex];
            }
        }
        return slowIndex;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值