問題
給定一個有序數組,要删除數組重複出現的元素,使得每個元素隻出現一次,然後傳回移除重複數組後的新長度;
示例:
假設給定一個數組
nums = [1,2,4,4]
,删除重複出現的元素 4 後,原數組變成
nums = [1, 2, 4]
,此時新的數組長度為 3;
解決思路
數組原地操作
數組原地操作,此時無需建立新的數組,隻需要在原來的數組上操作即可。相當于首先要找到數組中重複的元素,然後将重複的元素移除,此時就涉及到數組中的删除操作,相關知識點可以看我的另一篇文章
數組的增删改查。
這是一種時間換空間的方法,此時的空間複雜度為 O(1),時間複雜度為 O(n^2),具體實作可以參考如下代碼,其中也詳細注釋了每一步操作。
/**
* 去除有序數組中重複元素并傳回數組的新長度
* @param nums
* @return 删除重複元素後數組的新長度
*/
public int removeDuplicates(int[] nums) {
// 數組初始容量
int length = nums.length;
// 我們假定數組最後一個元素是唯一的,然後對于其他的每個元素,如果自身與它後邊的數相同,那麼就删除這個相同的元素
for(int i = length - 2; i >= 0; i++){
// 比較目前元素與其後一個元素是否相等
if(nums[i] == nums[i + 1]){
// 若相等,則移除後一位,并将所有元素向前移動一位
for(int j = i + 1; j < length; j++){
num[j - 1] = nums[j];
}
length--;
}
}
// 傳回數組的新長度
return length;
}
普通方法
針對數組原地操作算法時間複雜度為 O(n^2),為降低時間複雜度提高算法效率,可以通過空間換時間的做法,通過定義新的數組,進而實作去除重複元素的目的,此時的時間複雜度為 O(n),而空間複雜度也由 O(1) 變成了 O(n)。但是有幾點需要注意:
- 臨界情況(即數組為空);
- 建立新數組時,需要指定其容量,是以需要先求出原數組中無重複元素時的元素個數;
- 最後則是将原數組中未重複的元素指派給新數組;
* @return 删除重複元素後的新數組
public int[] removeDuplicates(int[] nums) {
// 臨界情況
if(nums.length == 0){
return nums;
// 先求出數組中無重複時的元素個數
int size = 0;
for(int i = 0; i < nums.length; i++){
if(i == 0 || nums[i] != nums[i - 1]){
size++;
// 用于存放不含重複元素的有序數組
int[] resultArr = new int[size];
int index = 0;
if(i == 0 || nums[i] != nums[i + 1]){
resultArr[index++] = nums[i];
// 傳回新的不含重複元素的有序數組
return resultArr;
雙指針
以上的兩種方法要麼是以時間換空間,要麼是以空間換時間,那我們有沒有一種折中的辦法,既能保證時間複雜度很低,也能保證空間複雜度呢?答案是:當然有!
利用雙指針的思想,既可以将空間複雜度控制在 O(1),也可以将時間複雜度控制在 O(n)。
// 臨界情況
reutrn 0;
for(int i = 1; i < nums.length; i++){
if(nums[size] != nums[i]){
nums[++size] = nums[i];
}
// 傳回新長度
return size + 1;
總結
以上就是 3 種去除有序數組中重複元素的三種算法,其中既有以時間換空間的數組原地操作法,也有空間換時間的普通方法,最後的話則是有一種綜合前兩種方法優點的方法 - 雙指針。通過雙指針方法,既能保證空間複雜度為 O(1),也将時間複雜度限制在了 O(n)。
想不到連簡單的數組去重都有這麼大的學問,我們在日常學習時,大多可能隻關注于如何實作功能即可。但如果要應用到工作場景中,可能就需要考慮效率問題,此時則需要根據我們的具體需求來進行選擇了。
好了,以上就是今天的内容了,如果你還有其他更好的方法,歡迎留言交流呀!