How to remove duplicate elements of an ordered array

We know that for arrays, inserting and deleting elements at the end is more efficient, and the time complexity is O(1), but if you insert or delete elements in the middle or at the beginning, it will involve data movement, and the time complexity is O (N), the efficiency is low.

So the last article  O(1) time to delete/find any element in the array  talked about a technique, swapping the element to be deleted to the last one, and then deleting it, can avoid data movement.

PS: I have written more than 100 original articles carefully , and I have hand-in-hand brushed with 200 buckle questions, all of which are published in  labuladong's algorithm cheat sheet , which is continuously updated . It is recommended to collect, brush the questions in the order of my articles , master various algorithm routines, and then cast them into the sea of ​​questions.

Ordered array/linked list de-duplication

Let me first talk about how to de-duplicate an ordered array, first look at the following topic:

1cbe3f0948d6824aa7dafcd1f1380b7a.jpg

The function signature is as follows:

int removeDuplicates(int[] nums);

Obviously, since the array has been sorted, the duplicate elements must be connected together. It is not difficult to find them, but if every duplicate element is found, it will be deleted immediately, that is, the deletion operation is performed in the middle of the array. The overall time complexity will reach O(N^2).

PS: I have written more than 100 original articles carefully , and I have hand-in-hand brushed with 200 buckle questions, all of which are published in  labuladong's algorithm cheat sheet , which is continuously updated . It is recommended to collect, brush the questions in the order of my articles , master various algorithm routines, and then cast them into the sea of ​​questions.

Briefly explain what is in-situ modification:

If it is not modified in place, we directly new an  int[] array, put the elements after deduplication into this new array, and then return to this new array.

But in-situ deletion does not allow us to new the new array, we can only operate on the original array, and then return a length, so that we can get the elements after deduplication through the returned length and the original array.

This requirement is very common in array-related algorithmic problems. The general solution is  the fast and slow pointer technique in the double pointer technique in the previous article  .

We let the slow pointer  slow go behind, and the fast pointer  fast walks in front to explore the way, slow and tell it if we find a non-repetitive element  and let us move  slow forward. In this way, when the  fast pointer traverses the entire array  nums , the nums[0..slow] elements are not repeated .

int removeDuplicates(int[] nums) {
    if (nums.length == 0) {
        return 0;
    }
    int slow = 0, fast = 0;
    while (fast < nums.length) {
        if (nums[fast] != nums[slow]) {
            slow++;
            // 维护 nums[0..slow] 无重复
            nums[slow] = nums[fast];
        }
        fast++;
    }
    // 数组长度为索引 + 1
    return slow + 1;
}

Look at the process of algorithm execution:

04f34a959ab6f013c90028c8d11241dd.gif

Simply expand, if you give you an ordered linked list, how to remove the duplication? This is the 83rd question. In fact, it is exactly the same as the array deduplication. The only difference is that the array assignment operation becomes an operation pointer:

ListNode deleteDuplicates(ListNode head) {
    if (head == null) return null;
    ListNode slow = head, fast = head;
    while (fast != null) {
        if (fast.val != slow.val) {
            // nums[slow] = nums[fast];
            slow.next = fast;
            // slow++;
            slow = slow.next;
        }
        // fast++
        fast = fast.next;
    }
    // 断开与后面重复元素的连接
    slow.next = null;
    return head;
}

3bcd836dd59a37b1fe4dfb97325b5820.gif

8306e7ebbef26302521befe437ec97a2.jpg

The function signature is as follows:

int removeElement(int[] nums, int val);

The title requires us to   delete nums all valthe elements with values  in situ, and we still need to use   the speed pointer in the double pointer technique :

If you  fast encounter an element that needs to be removed, skip it directly, otherwise tell the  slow pointer and let it  slow go one step forward.

This is exactly the same as the solution to the array deduplication problem mentioned earlier, so I won’t draw GIF, just look at the code:

int removeElement(int[] nums, int val) {
    int fast = 0, slow = 0;
    while (fast < nums.length) {
        if (nums[fast] != val) {
            nums[slow] = nums[fast];
            slow++;
        }
        fast++;
    }
    return slow;
}

Note that there is an important difference between this and the deduplication of an ordered array . Here we nums[slow] assign the value first  and then give it  slow++, so that we can ensure  nums[0..slow-1] that it does not contain the value  val element, and the final result array length is  slow.

PS: I have written more than 100 original articles carefully , and I have hand-in-hand brushed with 200 buckle questions, all of which are published in  labuladong's algorithm cheat sheet , which is continuously updated . It is recommended to collect, brush the questions in the order of my articles , master various algorithm routines, and then cast them into the sea of ​​questions.

Move zero

This is Leikou question 283. Let me describe the following question:

Enter an array  numsfor you, please modify it in place , move all the elements with value 0 in the array to the end of the array, the function signature is as follows:

void moveZeroes(int[] nums);

For example nums = [0,1,4,0,2], if you give you input  , your algorithm does not return a value, but it will  nums modify the array in place  [1,4,2,0,0].

Combining the several questions mentioned before, do you already have the answers?

The title lets us move all 0s to the end. In fact, it is equivalent to removing  nums all 0s, and then assign the following elements to 0.

So we can reuse the removeElement function of the previous question  :

void moveZeroes(int[] nums) { 
    // Remove all 0 in nums 
    // Return the length of the array after removing 0 
    int p = removeElement(nums, 0); 
    // Assign all elements after p to 0 
    for (; p <nums.length; p++) { 
        nums[p] = 0; 
    } 
} 

// See the code above to achieve 
int removeElement(int[] nums, int val);

At this point, the four "modification in place" algorithm problems are finished. In fact, the core is still the speed pointer technique. Have you learned it?


Guess you like

Origin blog.51cto.com/15064450/2571028