数组的元素的删除
一.第26题·.有序数组删除重复项
1.题目要求
给你一个 升序排列 的数组 nums ,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。然后返回 nums 中唯一元素的个数。
2.实现效果展示
- 输入数字:nums为0 0 1 1 2 2 3 4 4
- 调用方法后,输出长度为5
- 新数组的前五位为0 1 2 3 4
3.方法1:双指针法
- 使用两个指针
第一个指针index=1,索引从1开始,因为第一个数字不可能重复。
第二个指针在for循环中,j从第二个元素开始检验。 - 注意:因为题目说是有序的数组,重复项是贴在一起的。
- 逻辑:
index来标记不重复数组的长度, 其中index-1表示当前不重复数组的最后一个数的索引位置。
如果j不等于nums[index-1],那它不是重复项,将index此时所在位置的值设置为nums[j];index的长度加1,继续检验。
如果遇到重复项了,index长度不变,j进入下一次循环。
代码实现如下:
//方法1;双指针
public static int removeDuplicates(int[] nums) {
if(nums.length==0){
return 0;
}
int index=1;
for (int j=1;j<nums.length;j++){
if (nums[j]!=nums[index-1]){
nums[index]=nums[j];
index++;
}
}
return index;
}
4.方法2:哈希表实现
用哈希表实现,因为它有一个强大的功能,就是主键key不会重复。
步骤:
- 先创建一个Integer型的哈希表,将数组的元素通过foreach循环插入主键。
- if-else判断,使用containsKey方法来判断哈希表中是否已经有这个主键。
- 没有使用put方法添加主键,value记录次数为1。 如果有的话,使用get方法获取之前的次数,在之前的次数上+1。
- 将哈希表中的key集合以数组的形式输出即可,并记录大小。
涉及到的方法:
containsKey(key):用于判断哈希表中是否有主键key
get(i):获取哈希表中主键为i的对应value值
keyset():获取主键的集合
toArray():将集合转化为数组形式
实现代码:
//方法2:哈希表,key-value键值对,因为key不会重复,最后返回哈希表的长度就行
public static int removeDuplicates2(int []num1){
HashMap<Integer,Integer> map=new HashMap<>();
for (int i:num1){
//判断哈希表中没有这个值
if(!map.containsKey(i)){
map.put(i,1);
}
else map.put(i,map.get(i)+1);
}
int a=map.size();
//获取key值的集合对象
Set<Integer> set=map.keySet();
//将哈希表中的键集合,转化为数组
Object[] keyArray = set.toArray();
for (int i=0;i<a;i++){
//强制转化为int型
num1[i]= (int) keyArray[i];
}
return a;
}
二.第27题.数组移除目标元素
1.题目要求
- 给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。
- 不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。
- 元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
2.实现效果
输入:nums = [3,2,2,3], val = 3 (val为移除元素)
输出:2, nums = [2,2]
解释:函数返回新的长度 2, 并且 nums 中的前两个元素均为 2。
注意:不需要考虑数组中超出新长度后面的元素。
例如:函数返回的新长度为 2 ,而 nums = [2,2,3,3] 或 nums = [2,2,0,0],也会被视作正确答案。
3.方法1:暴力求解
暴力算法,熟悉的两层for循环,把时间复杂度拉得满满的,O(n^2)
解析:
- 一开始用size来记录数组大小
- 第一层for循环,用来遍历数组,一个个检查元素nums[i]
- if语句用来判断遍历的元素是否为要删除的元素
- 找到了要删除的元素,通过for循环,将此时i位置后面的元素整体向前移一位。并且每找到一个移除元素,size减少1。
- 同时i坐标要减1,一位如果找到了,需要向前移项,移完项后,i位置的元素变了,还需要重新检验。
public static int removeElement1(int []nums, int val) {
int size = nums.length;
for (int i = 0; i < size; i++) {
// 发现需要移除的元素,就将数组集体向前移动一位
if (nums[i] == val) {
for (int j = i + 1; j < size; j++) {
nums[j - 1] = nums[j];
}
// 因为下标i以后的数值都向前移动了一位,所以i也向前移动一位
i--;
size--; // 此时数组的大小-1
}
}
return size;
}
4.方法2:双指针法
- time:用来记录数组的更新长度,初始化为0
- for循环遍历数组,来寻找
- 如果nums[i]!=移除元素,就将元素放入更新数组的对应位置,也就是time索引的位置。同时将更新数组的长度time+1。
示例代码如下:
public static int removeElement(int[] nums, int val) {
int time=0; // 记录不等于删除值的数
for (int i=0;i<nums.length;i++){
if (nums[i]!=val){
nums[time]=nums[i]; //如果找到
time++;
}
}
return time;
}
三.第1题.两数之和
1.题目要求
给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。
你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。
你可以按任意顺序返回答案。
示例:
输入:nums = [2,7,11,15], target = 9
输出:[0,1]
解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1]
输入:nums = [3,3], target = 6
输出:[0,1]
2.方法一.暴力求解
解析:
- 双循环for,时间复杂度O(N^2)
- 外层循环,遍历从0到nums.length-2,是两个数的第一个数字的遍历
- 内存循环,从第一个数字后面遍历到数组结束,是第二个数字的遍历
- if用来判断两个数字之和是否等于目标值,找到就返回
示例代码如下:
public static int[] twoSum(int[] nums, int target) {
int a=nums.length;
for(int i=0;i<=a-2;i++){
for (int j=i+1;j<a;j++){
if(nums[i]+nums[j]==target)
return new int[]{i,j};
}
}
throw new IllegalArgumentException("没有该匹配值");
}
3.方法二.哈希表求解
特点:
- 边存,边检验是否找到。
- 时间复杂度变低了。
思路:
先将数组中的第一个元素放入哈希表中,从索引1开始遍历,在放入前,先检验map集合中是否有差值target-nums[i](其中nums[i]为待插值)。
如果找到了,就返回下标。没有找到就将nums[i]插入集合中。
示例代码如下:
public static int[] twoSum2(int[] nums, int target) {
int a=nums.length;
Map<Integer,Integer> map=new HashMap<>();
map.put(nums[0],0);
for (int i=1;i<a;i++){
//判断目前哈希表中有没有差键
if (map.containsKey(target-nums[i])){
//有的话,则返回当前下标,并返回哈希表中的差键下标
return new int []{i,map.get(target-nums[i])};
}
//没有存进去
map.put(nums[i],i);
}
4.方法三.双指针
- 使用这个方法,需要实现给数组排好序,时间复杂度也和这有关。
- start指针从0索引开始; end指针在数组最后,length-1开始。
- 当nums[start]+nums[end]<target时,start++,起始指针前移。
- 当nums[start]+nums[end]>target时,end–,尾指针移动。
- 知道中间两数之和等于target时,返回结果。
示例代码如下:
public static int[] twoSum3(int[] nums, int target) {
// 先对数组进行排序
Arrays.sort(nums);
int start = 0;
int end = nums.length - 1;
// 使用双指针法进行查找
while (start < end) {
int sum = nums[start] + nums[end];
if (sum == target) {
// 找到目标数对
return new int[]{nums[start], nums[end]};
} else if (sum < target) {
// 当前和小于目标值,增加起始指针
start++;
} else {
// 当前和大于目标值,减少尾指针
end--;
}
}
// 没有找到符合条件的数对
return new int[]{};
}
这是我第一次开始认真准备刷算法题,以前也刷过,但是坚持了一星期就放弃了,我想这次我能将这几个月坚持下来。
准备每天发布一下自己的算法刷题心得吧,每天一篇,一篇两道三个算法题,难的话,就一篇,坚持。