算法训练第一天|704.二分查找|27.移除元素|977.有序数组的平方

LeetCode 704.二分查找

题目链接:704.二分查找

题目讲解:代码随想录

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

分析这道题目的前提是数组为有序数组,同时题目还强调数组中无重复元素,可以考虑使用二分法。

二分法的第一种写法(左闭右闭)

  • while (left <= right) 要使用 <= ,因为left == right是有意义的,所以使用 <=
  • if (nums[middle] > target) right 要赋值为 middle - 1,因为当前这个nums[middle]一定不是target,那么接下来要查找的左区间结束下标位置就是 middle - 1
//时间复杂度:O(log n)
//空间复杂度:O(1)
func search(nums []int, target int) int {
    // 初始化左右边界
    left := 0
    right := len(nums) - 1

    // 循环逐步缩小区间范围
    for left <= right{
        // 求区间中点
        middle := left + (right - left) / 2

        // 根据 nums[mid] 和 target 的大小关系
		// 调整区间范围
        if nums[middle] > target{
            right = middle - 1
        }else if nums[middle] < target {
            left = middle + 1
        }else{ 
            return middle
        }
    }
    
    // 在输入数组内没有找到值等于 target 的元素
    return -1
}

 二分法的第二种写法(左闭右开)

  • while (left < right),这里使用 < ,因为left == right在区间[left, right)是没有意义的
  • if (nums[middle] > target) right 更新为 middle,因为当前nums[middle]不等于target,去左区间继续寻找,而寻找区间是左闭右开区间,所以right更新为middle,即:下一个查询区间不会去比较nums[middle]
//时间复杂度:O(log n)
//空间复杂度:O(1)
func search(nums []int, target int) int {
    left := 0
    right := len(nums)

    for left < right {
        middle := left + (right - left) / 2

        if nums[middle] > target{
            right = middle
        }else if nums[middle] < target{
            left = middle + 1
        }else{
            return middle
        }
    }
    return -1
}

 LeetCode 35.搜索插入位置

题目链接:35.搜索插入位置

题目讲解:代码随想录

题目描述:给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。

使用二分法(左闭右开)

搜索插入位置,通过找规律,发现在left <= right的循环条件下,除了能直接在数组中找到对应的target,其余情况,都是在跳出left <= right的循环条件,即left > right时才满足条件。

func searchInsert(nums []int, target int) int {
    left := 0
    right := len(nums) - 1

    for left <= right{
        mid := left + (right - left) / 2
        if nums[mid] > target{
            right = mid - 1
        }else if nums[mid] < target{
            left = mid + 1
        }else{ // 恰好能找到对应的元素
            return mid
        }
    }
    return left
}

 LeetCode 34.在排序数组中查找元素的第一个和最后一个位置

题目链接:34.在排序数组中查找元素的第一个和最后一个位置

题目讲解:代码随想录

题目描述:给你一个按照非递减顺序排列的整数数组 nums,和一个目标值 target。请你找出给定目标值在数组中的开始位置和结束位置

寻找target在数组里的左右边界,有如下三种情况:

  • 情况一:target 在数组范围的右边或者左边,例如数组{3, 4, 5},target为2或者数组{3, 4, 5},target为6,此时应该返回{-1, -1}
  • 情况二:target 在数组范围中,且数组中不存在target,例如数组{3,6,7},target为5,此时应该返回{-1, -1}
  • 情况三:target 在数组范围中,且数组中存在target,例如数组{3,6,7},target为6,此时应该返回{1, 1}

自己的理解就是:利用二分查找,分别去找target(可能有多个)位置的前一个位置和后一个位置,不包括target元素本身(即找到边界)。然后统一处理以上三种情况。

// 时间复杂度O(log n)
func searchRange(nums []int, target int) []int {
    leftBorder := getLeft(nums, target)
    rightBorder := getRight(nums, target)
    // 情况一,target 在数组范围的右边或者左边
    if leftBorder == -2 || rightBorder == -2 {
        return []int{-1, -1}
    }
    // 情况三,target 在数组范围中,且数组中存在target
    if rightBorder - leftBorder > 1 {
        return []int{leftBorder + 1, rightBorder - 1}
    }
    // 情况二,target 在数组范围中,且数组中不存在target
    return []int{-1, -1}
}

// 获得左边界的位置(不包括target)
func getLeft(nums []int, target int) int {
    left, right := 0, len(nums)-1
    border := -2 // 记录border没有被赋值的情况
    for left <= right { // []闭区间
	    mid := left + ((right - left) >> 1)
	    if nums[mid] > target { 
	        right = mid - 1
	    } else if nums[mid] < target{
	        left =  mid + 1
	    }else{ // 找到等于target的位置,后续逐步更新,标记第一个target的前一个位置
            right = mid - 1 
            border = right 
        }
    }
    return border
}

// 获取右边界的位置(不包括target)
func getRight(nums []int, target int) int {
    left, right := 0, len(nums) - 1
    border := -2
    for left <= right {
	    mid := left + ((right - left) >> 1)
	    if nums[mid] > target { 
		    right = mid - 1
	    } else if nums[mid] < target{
            left = mid + 1
        }else { // 找到等于target的位置,后续逐步更新,标记最后一个target的后一个位置
            left = mid + 1
            border = left
	    }
    }
    return border

}

LeetCode 27.移除元素

题目链接:27.移除元素

题目讲解:代码随想录

题目描述:给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素。元素的顺序可能发生改变。然后返回 nums 中与 val 不同的元素的数量。

使用快慢指针实现 

双指针法(快慢指针法): 通过一个快指针和慢指针在一个for循环下完成两个for循环的工作。

定义快慢指针

  • 快指针:寻找新数组的元素 ,新数组就是不含有目标元素的数组(fast指针对应数组为target值时,跳过该值的相应操作,这样fast指针会比slow指针多走一步)
  • 慢指针:指向更新新数组下标的位置
// 时间复杂度 O(n)
// 空间复杂度 O(1)
func removeElement(nums []int, val int) int {
    slow := 0
    for fast := 0; fast < len(nums); fast++{
        if nums[fast] == val{
            continue
        }
        nums[slow] = nums[fast]
		slow++
    } 
    return slow
}

LeetCode 977.有序数组的平方

题目链接:997.有序数组的平方

题目讲解:代码随想录

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

数组其实是有序的, 只不过负数平方之后可能成为最大数了。

那么数组平方的最大值就在数组的两端,不是最左边就是最右边,不可能是中间。

此时可以考虑双指针法

  1. i指向起始位置,j指向终止位置。
  2. 新定义一个新数组result,和A数组一样的大小,让k指向result数组终止位置。
  3. 比较起始位置和最终位置元素平方的大小,较大的放入新数组中。
//时间复杂度为O(n)
func sortedSquares(nums []int) []int {
    n := len(nums)
    i, j, k := 0, n-1, n-1
    ans := make([]int, n)
    for i <= j{
        lm, rm := nums[i]*nums[i], nums[j]*nums[j]
        if lm > rm{
            ans[k] = lm
            i++
        }else{
            ans[k] = rm
            j--
        }
        k--
    }
    return ans
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值