文章目录
今天学习数组的一些基本操作,包括数组的删除、查找、增加(妥妥增删改查),核心是使用二分查找的方法来减低时间复杂度,总体的时间复杂度为log(n)
题目传送门
LeetCode:704.二分查找;27.
消除元素;34.查找元素位置;35.搜索插入位置
一、二分查找
1.题目简介
给定一个 n
个元素有序的(升序)整型数组 nums
和一个目标值 target
,写一个函数搜索 nums
中的 target
,如果目标值存在返回下标,否则返回 -1
。
2.解题思路
数组是一个升序数组,利用二分查找的思想,每次只需要找到中间元素与目标进行比较,根据比较结果选择原区间长度一半的区间继续搜索,最终得到答案。
具体做法:每一次对于区间[start,end],先找到中间位置mid_index=(start+end)/2(这里是int自动向下取整),然后得到区间值mid。通过比较,选取合适的二分区间[start,mid_index-1],[mid_index+1,end],再递归进行,直到区间达到临界条件。
3.细节处理
- 数组的起始下标
因为使用的是Java,每次对区间取一半这个操作如果是对数组进行的话会浪费很多数组空间,所以使用两个变量start,end来记录区间左右端的下标用于区间划分 - 区间划分
每一次通过比较mid,发现不相同之后会选择出那一半区间是有可能有目标值target的。
不妨设mid<target,则原本[start,end]改为[mid_index,end],但是因为已经知道mid不等于target了,所以直接把mid_index抛弃,变为[mid_index+1,end] - 临界处理
这里主要考虑几种情况:
start=end,也就是你二分的区间只有一个元素了,只需要比较这最后一个元素和target是否相同就可以了;
start>end,这是因为最后一次比较出现mid>target的情况,根据区间划分就得到这种情况了,直接返回-1即可
4.题解代码
class Solution {
public int find(int[] nums, int a, int b, int target){
int flag = -1;
if(a>=b)flag = target == nums[a] ? a:-1;
else {
int mid_index = (a + b) / 2;
int mid = nums[mid_index];
if (target == mid) flag = mid_index;
else if(target < mid) flag = find(nums, a, mid_index - 1, target);
else flag = find(nums, mid_index + 1, b, target);
}
return flag;
}
public int search(int[] nums, int target) {
return find(nums,0,nums.length-1,target);
}
}
二、移除元素
1.题目简介
给你一个数组 nums
和一个值 val
,你需要原地移除所有数值等于 val
的元素,并返回移除后数组的新长度。
不要使用额外的数组空间,你必须仅使用 O(1)
额外空间并原地修改输入数组。
元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
2.解题思路
删除元素嘛,自然而然的就想到,扫描数组,如果遇到target值就将这个元素删除,直到扫描完所有元素。
在Java中没办法对数组元素直接删除,只能够使后面元素整体往前移动一格。
在python中,用in操作就可以知道数组中是否有target值,如果有,用remove删除即可,再检查数组,直到数组没有为止。
3.细节处理
- 记录删除次数
因为没有减少数组长度,所以每删除一次,数组尾部就有一位失去意义,用cnt记录删除次数,就可以知道删除之后实际数组长度。 - 检查&删除操作
这里需要注意的是当检查到当前下标的值等于target时,用当前元素位置,后面元素都往前移动一格(不需要移动细节1中提到的失去意义的格子)。移动完之后,当前位置变成没有检查的状态,所以需要再检查一次。 - 循环长度
因为每次都会删除,所以扫描的时候需要到达的理论最后一个元素并不是数组的最后位置,需要减去删除次数。
4.题解代码
class Solution {
public int removeElement(int[] nums, int val) {
int cnt = 0;
for(int i = 0;i<nums.length-cnt;i++){
if(nums[i]==val){
for(int j=i;j<nums.length-cnt-1;j++){
nums[j] = nums[j+1];
}
cnt++;
i--;
}
}
return nums.length - cnt;
}
}
当然python来就很简单了
class Solution(object):
def removeElement(self, nums, val):
while (1):
if val in nums:
nums.remove(val)
else:
break
三、查找元素位置
1.题目简介
给你一个按照非递减顺序排列的整数数组 nums
,和一个目标值 target
。请你找出给定目标值在数组中的开始位置和结束位置。
如果数组中不存在目标值 target
,返回 [-1, -1]
。
你必须设计并实现时间复杂度为 O(log n)
的算法解决此问题。
2.解题思路
利用二分查找找到数组中的target,再向前向后延伸找到都为traget的连续序列
3.题解代码
class Solution {
public int find(int[] nums, int a, int b, int target){
int flag = -1;
if(a>=b)flag = target == nums[a] ? a:-1;
else {
int mid_index = (a + b) / 2;
int mid = nums[mid_index];
if (target == mid) flag = mid_index;
else if(target < mid) flag = find(nums, a, mid_index - 1, target);
else flag = find(nums, mid_index + 1, b, target);
}
return flag;
}
public int[] searchRange(int[] nums, int target) {
int start = -1;
int end = -1;
int[] re = new int[2];
if(nums.length==0){
re[0] = start;
re[1] = end;
return re;
}
int flag = find(nums,0,nums.length-1,target);
if(flag!=-1){
start = flag;
end = flag;
for(int i=flag-1;i>=0;i--){
if (nums[i]!=target)break;
start--;
}
for(int i=flag+1;i<nums.length;i++){
if (nums[i]!=target)break;
end++;
}
}
re[0] = start;
re[1] = end;
return re;
}
四、搜索插入的位置
1.题目简介
给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
请必须使用时间复杂度为 O(log n)
的算法。
2.解题思路
利用二分查找找到数组中的target,如果没有就从头找到第一个大于target的元素位置,即插入位置
3.题解代码
class Solution {
public int find(int[] nums, int a, int b, int target) {
int flag = -1;
if (a >= b) flag = target == nums[a] ? a : -1;
else {
int mid_index = (a + b) / 2;
int mid = nums[mid_index];
if (target == mid) flag = mid_index;
else if (target < mid) flag = find(nums, a, mid_index - 1, target);
else flag = find(nums, mid_index + 1, b, target);
}
return flag;
}
public int searchInsert(int[] nums, int target) {
if(nums.length==0)return 0;
else{
int flag = find(nums,0,nums.length-1,target);
if(flag==-1){
for(int i = 0;i<nums.length;i++) if(nums[i]>target)return i;
return nums.length;
}
return flag;
}
}
}
总结
今天主要考察数组的运用,二分查找很重要啊,需要熟读并背诵才行噢,一会去看看别人写的快慢指针的效果。