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程式碼的時候一定要加以實踐測試,說不定我的程式碼也有很多問題。