情景要求:
(1)输入的数组元素为基本类型int。
(2)返回不含重复元素的数组,其类型为基本类型int。
(3)对最后的结果数组元素顺序没有要求,可乱序也可排序。
经过一系列的思考与实践,总结出了以下三种不同情形“删除数组重复元素”的解决办法。
1、通过原始数组删除重复元素
方法一:不重新开辟内存(不创建list、set等),可以在原始数组中原地删除重复元素。
由于数组的长度是固定的,所以没有像list那样的remove删除方法,只能通过简单的算法构造解决。
(1)简单过程算法描述:
(1)从i=0开始与nums[i+1]元素比较,即嵌套循环比较
(2)若元素相同,则记下相同元素的索引index,采用向前覆盖num[index]=nums[index+1],覆盖重复元素数据。
然后数组长度减1(去掉末尾元素)同时复位i=0,返回(1)
(3) 若不相同则继续(1)。
(4)直到i=nums.length成立,终止
(2)java代码:
public int[] removeDuplicates(int[] nums) {
int index = -1;
int i = 0;
if (nums.length == 0) {
return null;
}
loop: while (i != nums.length) {
if (i == nums.length - 1) {
break;
}
for (int k = i + 1; k < nums.length; k++) {
if (nums[i] == nums[k]) {
index = k; // 删除重复的第一个
for (int j = index; j < nums.length; j++) {
if (j != nums.length - 1) {
nums[j] = nums[j + 1];// 向前覆盖
} else {// 处理最后一位超出情况
nums[j] = nums[j];
}
}
nums = Arrays.copyOf(nums, nums.length - 1);// 覆盖之后长度减1
i = 0;
// 如果有重复的元素,则重新循环新数组nums(已经去掉了上一个重复元素的数组)
// 此处必须要用continue,不能用break。不然没法让i=0重新开始循环
continue loop;
}
}
i++;// 若没有再重复的元素就i++,继续循环
}
return nums;
}
(3)优缺点说明
- 优点:很明显地节约了空间,在此过程中没有任何创建多余存储空间,即创建新的数组,集合等操作;算法简单,容易理解。
- 缺点:循环次数多,时间复杂度高,牺牲了时间节约了空间;虽然保持原始的元素顺序,但无法同时排序
2、通过List删除数组重复元素
方法一:遍历数组比较,若相等则break中断,若不相等则加入list集合
(1)简单过程算法描述:
(1)循环遍历比较数组与list集合
(2)若相等则break中断本次循环,继续(1)下次循环
(3)若不相等则加入list结果集合
(4)list结果集合转为数组结果集
(2)java代码:
public int[] removeDuplicates(int[] nums) {
List<Integer> result = new ArrayList<>();
boolean flag; //是否重复标志位
for(int i=0;i<nums.length;i++){
flag = false;
for(int j=0;j<result.size();j++){
if(nums[i] == result.get(j)){
flag = true;
break;
}
}
if(!flag){
result.add(nums[i]);
}
}
//list元素转为新数组
int arr[] = new int[result.size()];
int j = 0;
for (int i : result) {
arr[j] = i;
j++;
}
return arr;
}
(3)优缺点说明:
- 优点:过程简单,容易理解;存储空间较少,节约内存;利用list集合操作元较为素方便,可以同时满足排序等情况。
- 缺点:嵌套循环,导致时间复杂度较高;
方法二:利用一次for循环,进行相邻数据的比较即可得到结果。
此方法是我再网上查的资料,然后经过实践测试总结到的,但是有个很明显的不足,最后我将以弥补得到相对完美的方法。
先将之前的不足方法进行介绍:
(1)简单过程算法描述:
(1)初始化集合list,加入数组第一个元素。
(2)循环遍历数组与集合中最后一个元素进行比较
(3)若相等则结束本次循环,继续(2)下一次循环
(4)若不相等则加入list结果集合,返回(2)继续
(5)list结果集合转为数组结果集
(2)java代码:
public int[] removeDuplicates(int[] nums) {
List<Integer> result = new ArrayList<>();
result.add(nums[0]);
for(int i=1;i<nums.length;i++){
if(nums[i] != (result.get(result.size()-1))){
result.add(nums[i]);
}
}
//list元素转入新数组
int arr[] = new int[result.size()];
int j = 0;
for (int i : result) {
arr[j] = i;
j++;
}
return arr;
}
(3)结果分析:
此方法重点是“相邻元素比较”。而忽略了如果出现[4,5,4]重复元素“隔空”情况时,就不能满足。
input: int [] nums = { 6, 6, 5, 6, 8, 8, 7 };
output: nums = [6, 5, 6, 8, 7]
显然这是明显错误的,网上有些的方法只是经过自己的推测猜想,并没有认真谨慎的实践。
(4)问题优化:
之所以出现以以上不正确的结果,是因为并没有考虑到不重复的元素已经在集合list中,下一次的比较只比较相邻的元素,并不能确定同样的元素是否已经在list集合中。那么我们只需要加一个条件即可解决。
!result.contains(nums[i])
代码如下:
public int[] removeDuplicates(int[] nums) {
List<Integer> result = new ArrayList<>();
result.add(nums[0]);
for(int i=1;i<nums.length;i++){
if(nums[i] != (result.get(result.size()-1)) && !result.contains(nums[i])){
result.add(nums[i]);
}
}
//list元素转入新数组
int arr[] = new int[result.size()];
int j = 0;
for (int i : result) {
arr[j] = i;
j++;
}
return arr;
}
经过测试结果是正确的:
input: int [] nums = { 6, 6, 5, 6, 8, 8, 7 };
output: nums = [6, 5, 8, 7]
(5)优缺点说明
- 优点:代码简单,容易理解;存储内存占用少,节约了空间;只有一次循环,时间复杂度相比较之下大大降低。
- 缺点:数组需要转为list集合,转的过程中需要注意基本类型与泛型的区别。
3、利用List/Set//HashSet/TreeSet删除数组重复元素
最简单最好用的应该就是集合自带的方法与特性操作元素,从而可以做到删除数组重复元素,那么就需要尽可能将数组转为集合。所以在此之前需要了解一下四个方面。
(1)数组转List集合,数组最好为Integer、String、Double等包装类型,如果为基本类型的话,要转为其对应的包装类型。也可以将数组中元素老老实实的取出放入list集合中。
(2)集合List转Set集合会同时解决自动去重、排序两个问题。
(3)List集合转Set集合的方式有:一是:Set<Integer> set= new HashSet(list);
,二是:Set<Integer> set = new HashSet<Integer>();set.addAll(list);
(4)Set集合转数组:第一是输出包装类Integer等,之后再转为基本类型。第二是直接Object array [] = set.toArray()
先转为Object
对象数组,再转为基本类型数组。
方法一:利用List、HashSet、TreeSet实现数组去重
(1)简单过程描述:
(1)若为基本类型数组,则转为其包装类型以便于转为List集合。
(2)list集合转为HashSet或者TreeSet(其实是加入填充到Set集合中)。
(3)HashSet/TreeSet转为Array数组。
(2)java代码:
public int[] removeDuplicates(int[] nums) {
//int 变Integer
Integer[] array = new Integer[nums.length];
for(int i=0; i<nums.length; i++)
{
Integer integer = new Integer(nums[i]);
array[i] = integer;
}
//Arrays.asList接受泛型。HashSet+TreeSet接受list
TreeSet<Integer> treeSet = new TreeSet<Integer>(Arrays.asList(array));
//也可以HashSet hashSet = new HashSet(Arrays.asList(array));
int arr[] = new int[treeSet.size()];
int i = 0;
for (Integer integer : treeSet) {
arr[i] = integer;
i++;
}
return arr;
}
(3)输出结果:
input: nums = {1,3,2,3,5}
output:nums=[1,2,3,5]
由结果我们可以知道,不但已经除去了重复项目而且已经升序排好了。
(4)优缺点说明:
- 优点:代码简单,容易理解;自动排序、去重;无需构造算法,只需转类型。
- 缺点:转类型的过程中可能会出现意想不到的错误,导致程序终止,比如TreeSet集合不允许元素为空;排好序的集合没法知道其元素在原始数组的索引。
方法二:利用List、Set解决数组去重问题
(1)简单过程描述:
(1)将array转为List
(2)将list加入set集合
(3)set集合变array数组返回结果
(2)java代码:
public int[] removeDuplicates(int[] nums) {
List<Integer> numList = new ArrayList<Integer>();
//数组变list
for (int i : nums)
numList.add(i);
//list变set
Set<Integer> numSet = new HashSet<Integer>();
numSet.addAll(numList);
//set变数组
Object[] arr = numSet.toArray();
int result[] = new int[arr.length];
for (int i = 0; i < arr.length; i++) {
result[i] = (int) arr[i];
}
return result;
}
备注说明:
在这里只是记录下本人的日常学习过程,当然也希望大神能提供更好的方法解决此类问题,特别是不利用其它存储空间,只能利用传入数组nums自身去重问题(我想了很长时间也没有想到比较好的方法)。
同时也希望大家学习copy代码的时候一定要加以实践测试,说不定我的代码也有很多问题。