Charmve Coding | the smallest positive integer that does not occur in Array A

Perface

Since last week, Apr 17. 2020, I had been starting to hold a Data Structs & Algorithms series training course. So I am going to make coding tests as a push study as much as possible, so that more excelent skills or methods can be relized and be meet your guys’ need.


Hereby, the following articles are all writen originally by myself, named Charmve Coding. This is a try for me to write contents in English, I hopefully wanna bring some special reading matters to you, my friends, to improve our English technolog reading ability.


To be honest, if those content appeals to you, a small click, themb-up, would make me happy. If you have any questions, feel free to reach out to me.

Good greeting to every Maiwei(邁微)members, I’m a FRIEND of yours, Charmve.

As start, posting a interesting Codility test, and then I will share FIVE different ways to solve this question. To be specific, it’s worth mentioning that a value-fulled summary will be shared to you.

Let’s begin! The test direction is followed.

Write a function:


int solution(vector<int> &A);


that, given an array A of N integers, returns the smallest positive integer (greater than 0) that does not occur in A.

For example, given A = [1, 3, 6, 4, 1, 2], the function should return 5.

    Given A = [1, 2, 3], the function should return 4.

    Given A = [−1, −3], the function should return 1.

Write an efficient algorithm for the following assumptions:

  • N is an integer within the range [1…100,000];
  • each element of array A is an integer within the range [−1,000,000…1,000,000].
// you can use includes, for example:
// #include <algorithm>

// you can write to stdout for debugging purposes, e.g.
// cout << "this is a debug message" << endl;

int solution(vector<int> &A) {
    // write your code in C++14 (g++ 6.2.0)
    
}

Suppose the array is A, the size is n, and the index starts from 1. The following is a series of gradually improved algorithms:

First,

     Exhaustive Search

The general problem can be done in this very violent way, from 1 to n to determine whether it is in the array one by one:

MIN-AVAILABLE-NUM(A, n)
  for i = 1 to n
    do if i not in A 
         then return i
      return n+1

Obviously, the algorithm complexity here is O(n2)O (n ^ 2).

A C++ code version can be referred:

int solution(vector<int> &A) {
    // write your code in C++14 (g++ 6.2.0)
    int n = A.size;
    for(int i= 1; i < n; i++)
    {
    	 for(int j=0; j < n; j++)
   		 {
    		if(A[j] == i)
    			break;
    		if(A[j] != i && j == n)
    			return i;
    		if(A[j] != i && i == n)	
    			return n+1;
    	 }
    }
}

ATTENTION: the index of array starts from 1.

Second,

     Binary Search

Last method uses line-time-cost to find the goal supposed, so we natually think of binary search with algorithm complexity O(nlgn)O (nlgn). Before we use Binary Search, we should sort the array.

The processing:

  • firstly SORT: Quick sort, merge sort and cardinality sort (with O(n)O(n) space complexity)
  • then Search: Binary Search(with O(nlgn)O(nlgn) algorithm complexity)

Therefore, the overall algorithm complexity is O(nlgn)O (nlgn) in this way.

Third,

     Math Method

In fact, carefully observing the array A[1]..A[n]A [1] .. A [n], we can draw a conclusion: if there are unused numbers in the array, then Max(A)>nMax (A) > n   (Drawer principle)

MIN-AVAILABLE-NUM(A, n)
  for i = 1 to n
    do A[i] > i
         then return i
      return n+1

Note that if we use cardinality sorting, we can reduce the complexity to $ O(n)$ .

Fouth,

     Linear time & Linear space

Although the third algorithm can achieve O(n)O (n) in the theoretical sense, the constant factor implied by cardinality sorting is large, and it is not sorted in place. Here is an algorithm that does not require sorting:

MIN-AVAILABLE-NUM(A, n)
  for i = 1 to n
    B[i] = 0
  for i = 1 to n
    do if A[i] < n
    		then B[A[i]] = 1
    for  i = 1 to n
    if(B[i] == 0) return i;
  return n+1;

Here we use an auxiliary array B to indicate whether the numbers 1 to n exist in the array A, and mark it as 0 as long as it does not exist. Finally, the first value found in B is the element we are looking for. ; If the elements in B are all 1, it means that A uses all the numbers 1 to n, then the next n + 1 is returned.

No sorting is required here, and the complexity is O (n), but an additional array of O (n) is required.

Fifth,

     Linear time & constant space

Using the principle of quick sort, we can achieve O (n) efficiency without using additional arrays. The principle is:

Take the intermediate value of 1 to n, m=(1+n)/2m = (1 + n) / 2, use m to divide the array into two parts A1 and A2, all the elements in A1 are less than or equal to m, all the elements in A2 are greater than m (note that use here Is the subscript, not A [m]). If the size of A1 is m, the free element is in A2, which was proved before, and then the same method is applied in A2.

MIN-AVAILABLE-NUM(A, low, up)
  if(low == up) return low
  m = (low + up) / 2
  split = partition(A, low, up, m)
  if a[split] == m 
  	then return MIN-AVAILABLE-NUM(A, low, split)
    else 
    	return MIN-AVAILABLE-NUM(A, split+1, up)

The recursive formula here is: T(n)=T(n/2)+O(n)T (n) = T (n / 2) + O (n). According to the third case of the main theorem, the complexity is O (n), which is actually a proportional sequence: n+n/2+n/4...n + n / 2 + n / 4 ...

However, because recursion is used here, the space complexity is actually O (Lgn), so you can use a loop instead:

MIN-AVAILABLE-NUM(A, low, up)
  while low != up
    m = (low + up) / 2
    split = partition(low, up, m)
    if A[split] == m 
      then low = split + 1
    else up = split - 1
   return low

A C++ code version can be referred:

void swap(int *a, int *b){
    int temp = *a;
    *a = *b;
    *b = temp;
}

int partition(int data[], int length, int start, int end, int key){
    if(length<=0||start<0||end>=length) return -1;
    int index;
    int small = start-1;
    for(index=start; index<=end; ++index){
        if(data[index]<=key){
            ++small;
            if(small!=index){
                swap(&data[index], &data[small]);
            }
        }
    }
    return small;
} 

int solution(int A[], int low, int up){
    while(low < up){
        int m = (low + up)/2;
        int split = partition(A, N, low, up, m);
        if (split == m) { //can't use A[split] 
            low = split +1;
        }else {
            up = split;
        }
    }
    if(low==A[low]){
        return low+1;
    }
    if(A[low]>low){
        return low;
    }
}

THANKS for your attention!

Worthly Recommended:

[1] 數據結構與算法 | 你知道快速排序,那你知道它的衍生應用嗎?Partition函數

[2] 數據結構與算法 | 二分查找:劍指offer53 在排序數組中查找數字

[3] 數據結構與算法 | 數據結構中到底有多少種“樹”?一文告訴你


Follow my WeChat Office Account 邁微電子研發社,to get more exciting content. First published in the personal public account.
在這裏插入圖片描述

△Scan the QC to follow MaiweiE-com

Knowledge Planet (in Charge):The community aims to share the autumn / spring preparation strategies (including brush questions), face-to-face and internal referral opportunities, learning routes, knowledge question banks, etc.

在這裏插入圖片描述

△Scan the QC to entry the community of MaiweiE-com

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章