大家好,今天我們來研究一個比較常見的編碼問題。 假如現在給我們一個對象數組,它可以是整數數組和字符串數組,也可以是實現 Comparable 接口的任何對象。
帶着以下問題,我們來開始今天的文章:
- 我們如何從數組中找到重複的元素?
- 你能用 O(n) 複雜度來解決這個問題嗎?
不論在日常工作中,或者在面試中,這都是經常遇到的問題;
其實有多種方法可以解決這個問題,在這裏我們將討論兩種比較常見的方法,首先是常規方法,這種方法指將每個元素與其他元素進行比較,其次是使用類似哈希表的數據結構來將問題的時間複雜度從二次降低到線性,當然要增加一些空間複雜度。這也說明通過使用合理的數據結構,我們可以想出更優時間複雜度的算法來解決問題,所以說數據結構和算法的相關知識對程序員非常重要;
方案1- 在 O(n^2)中尋找重複項
在第一種解決方案中,我們將數組中的每個元素與其他每個元素進行比較。 如果它們相同,那麼就有重複項,如果不相同,那麼就沒有重複項,通常把這種方法稱爲:暴力破解算法
當我們使用這種方案從數組中尋找重複項時,它的時間複雜度就是O (n ^ 2)
public static Set<Integer> findDuplicates(int[] input) {
Set<Integer> duplicates = new HashSet<Integer>();
for (int i = 0; i < input.length; i++) {
for (int j = 1; j < input.length; j++) {
if (input[i] == input[j] && i != j) {
// duplicate element found
duplicates.add(input[i]);
break;
}
}
}
return duplicates;
}
我們將最後的重複項放入到Set集合返回,但是如果面試官問你還有其他優化方案嗎?將它的時間複雜度降爲O(n);
我們接着往下看
方案2 - 在 O(n) 中尋找重複項
第二個解決方案演示瞭如何使用合適的數據結構編寫更好的算法來解決同樣的問題。 我們知道,在 Java 中,由於Set 集合底層是基於散列表數據結構所以不允許重複元素,因此平均情況下插入需要 O(1)
通過HashSet
集合來解決這個問題,我們可以在O(n)
時間內完成,我們在for循環
中將每個元素插入HashSet
中,因爲它只允許唯一的元素,所以當我們嘗試添加重複元素時候,add()
方法會返回false
;
最後,我們將重複下打印出來,看看是不是可以實現我們的需求;
public static <T extends Comparable<T>> void getDuplicates(T[] array) {
Set<T> dupes = new HashSet<T>();
for (T i : array) {
if (!dupes.add(i)) {
System.out.println("Duplicate element in array is : " + i);
}
}
}
這個方法適用於Java
中任何類型的 Java 數組,比如 Array with Integer
,Array with String
或者任何實現 Comparable
接口的對象,但是不適用於原語數組,因爲它們在 Java
中不是對象
代碼清單
爲了方便大家測試,提供了代碼清單,大家可以直接跑一跑
package com.milo.collection.list;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
/**
* 過濾數組中重複的元素
* @author milogenius
* @date 2020/4/22 23:03
*/
public class DuplicatesFromArray {
public static void main(String args[]) {
int[] withDuplicates = { 1, 2, 3, 1, 2, 3, 4, 5, 3, 6 };
//調用常規方法
Set<Integer> duplicates = findDuplicates(withDuplicates);
System.out.println("input array is : " + Arrays.toString(withDuplicates));
System.out.println("Duplicate elements found in array are : " + duplicates);
// 調用泛型方法
String[] myArray = { "ab", "cd", "ab", "de", "cd" };
System.out.println("input string array is : " + Arrays.toString(myArray));
getDuplicates(myArray);
}
/**
* 時間複雜度是O(n²)
*
* @param input
* @return
*/
public static Set<Integer> findDuplicates(int[] input) {
Set<Integer> duplicates = new HashSet<Integer>();
for (int i = 0; i < input.length; i++) {
for (int j = 1; j < input.length; j++) {
if (input[i] == input[j] && i != j) {
// 發現重複元素
duplicates.add(input[i]);
break;
}
}
}
return duplicates;
}
/**
* 時間複雜度爲O(n) ,因爲我們使用了HashSet數據結構
*
* @param array
* @return
*/
public static <T extends Comparable<T>> void getDuplicates(T[] array) {
Set<T> dupes = new HashSet<T>();
for (T i : array) {
if (!dupes.add(i)) {
System.out.println("Duplicate element in array is : " + i);
}
}
}
}
Output :
input array is : [1, 2, 3, 1, 2, 3, 4, 5, 3, 6]
Duplicate elements found in array are : [1, 2, 3]
input string array is : [ab, cd, ab, de, cd]
Duplicate element in array is : ab
Duplicate element in array is : cd
總結
我們學習了兩種解決如何在數組中找到重複元素的方法,第一個解決方案是暴力破解算法,第二個解決方案是我們使用HashSet
數據結構將第一種方案的時間複雜度從O(n^2)
降爲O (n)
,同時也展示了利用泛型實現方法的通用性;