算法训练营(day01)
算法训练营(day01)
数组理论基础
数组是存放在连续内存空间上的相同类型数据的集合。
数组可以方便的通过下标索引的方式获取到下标对应的数据。
- 数组下标都是从0开始的。
- 数组内存空间的地址是连续的
- 因为数组的在内存空间的地址是连续的,所以我们在删除或者增添元素的时候,只能覆盖原有地址的元素进行修改
704.二分查找
题目链接:https://leetcode.cn/problems/binary-search/description/
给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。
二分查找理论基础
使用二分查找需要具备两个条件:
- 数组有序
- 无重复数组
需要满足这两个条件,使用二分法才能找到 唯一 的下标地址,得出结果。当然,并不是说不满足这两个条件就不能使用二分法,可以适当根据题意对二分法进行调整,比如下面的 34.在排序数组中查找元素的第一个和最后一个位置
,就使用了二分法加中心扩散的思想。
使用二分法最大的优点就是可以降低时间复杂度,达到**O(nlogN)**,对于笔试刷题,能大大提高AC效率。
解题思路
首先,题目很明显是使用二分查找法解题,需要考虑的是二分查找区间定义: 左闭右开[left, right) 还是 左闭右闭[left, right]
解题过程:
- 设定左右指针left,right
- 找出中间位置mid,并判断该位置值
num[mid]
是否等于 target- nums[mid] == target 则返回该位置下标
- nums[mid] > target 则右侧指针移到中间
- nums[mid] < target 则左侧指针移到中间
- 时间复杂度:O(logN)
详细代码
一般我个人更喜欢使用 左闭右闭,个人感觉更易理解,下面将给出两种解决方式。
左闭右闭:right = nums.length - 1
1 | class Solution { |
左闭右开:right = nums.length
1 | class Solution { |
35.搜索插入位置
题目链接:https://leetcode.cn/problems/search-insert-position/
给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
请必须使用时间复杂度为 O(log n) 的算法。
解题思路
看到题目有要求时间复杂度为 O(log n),可以考虑使用二分法。题目有个插入的要求,简单理解就是 插队,如果这个数据插入数组中,那么后面的数组都要向右延一个下标,所以插入的目标值返回的下标是左侧指针
解题过程:
- 设定左右指针left,right
- 找出中间位置mid,并判断该位置值
num[mid]
是否等于 target- nums[mid] == target 则返回该位置下标
- nums[mid] > target 则右侧指针移到中间
- nums[mid] < target 则左侧指针移到中间
- 如果全局都没找到target,则返回左侧指针下标
- 时间复杂度:O(logN)
详细代码
暴力解法:直接遍历,如果数组中有比目标值大的数, 则目标值 插队 在该数前面,否则排在数组最后面
1 | class Solution { |
二分法:左闭右闭
1 | class Solution { |
34.在排序数组中查找元素的第一个和最后一个位置
题目链接:https://leetcode.cn/problems/find-first-and-last-position-of-element-in-sorted-array/
给你一个按照非递减顺序排列的整数数组 nums
,和一个目标值 target
。请你找出给定目标值在数组中的开始位置和结束位置。如果数组中不存在目标值 target
,返回 [-1, -1]
。
你必须设计并实现时间复杂度为 O(log n)
的算法解决此问题。
解题思路
题目给定了有序,所以可以试试暴力解法,但若要满足时间复杂度,要考虑使用二分法+中心扩散
思路一:暴力遍历
解题过程:
设定左右指针left,right
位置值
num[left]
和num[right]
是否等于 target- nums[left] != target 则左侧指针右移
- nums[right] != target 则右侧指针左移
- nums[left] == target ||nums[right] != target 则指针停止移动
返回新数组
new int[]{left, right}
如果全局都不满足,则返回
new int[]{-1, -1}
思路二:二分法+中心扩散
解题过程:
- 设定左右指针left,right
- 找出中间位置mid,并判断该位置值
num[mid]
是否等于 target- nums[mid] == target 则以 mid 为初始值,向左向右遍历,找到边界值
- nums[mid] > target 则右侧指针移到中间
- nums[mid] < target 则左侧指针移到中间
- 返回新数组
new int[]{left, right}
- 如果全局都不满足,则返回
new int[]{-1, -1}
详细代码
暴力解法:
1 | class Solution { |
二分法+中心扩散
1 | class Solution { |
27.移除元素
题目链接:https://leetcode.cn/problems/remove-element/
给你一个数组 nums
和一个值 val
,你需要原地移除所有数值等于 val
的元素,并返回移除后数组的新长度。不要使用额外的数组空间,你必须仅使用 O(1)
额外空间并原地修改输入数组。
元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
快慢指针理论基础
双指针法(快慢指针法): 通过一个快指针和慢指针在一个for循环下完成两个for循环的工作。
- 快指针:寻找新数组的元素 ,新数组就是不含有目标元素的数组
- 慢指针:指向更新 新数组下标的位置
解题思路
思路一:暴力遍历
简单直接的遍历数组,只要和val不一样的就保留下来,一样的就不管。
解题过程:
- 定义一个常量res
- 遍历数组,判断
nums[i]
值是否等于val- nums[i] == val 则不管
- nums[i] != val 则nums[res++] = nums[i]
- 返回res
思路二:快慢指针(双指针法)
其实和暴力遍历没什么区别,理解起来就是 快指针 检查数组,慢指针 整合数组
解题过程:
设定快慢指针fast,slow
让 fast 遍历数组
- nums[fast] != val 则 nums[slow] 记录当前的 nums[fast]
- 否则不计
返回 slow
详细代码
暴力遍历:
1 | class Solution { |
快慢指针:
1 | class Solution { |