LeetCode——解决删除数组中重复元素问题三种方式

情景要求:
(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代码的时候一定要加以实践测试,说不定我的代码也有很多问题。

猜你喜欢

转载自blog.csdn.net/mynewclass/article/details/79805367