求陣列中未出現的最小正整數

zxc123e發表於2015-10-23

【題目】: 給定一個無序整型陣列arr,找到陣列中未出現的最小正整數。要求時間複雜度為O(N),空間複雜度為O(1)。

例如:

arr=[-1,2,3,4]。返回1。

arr=[1,2,3,4]。返回5。

【解法】
一、窮舉查詢
最小正整數是1,所以常規的方法就是在陣列中找1,然後是2,依次找下去…。一直找到第一個沒有出現的正整數,這個數就是未出現的最小的正整數。

for i = 1 to n

    do if i not in A 

         then return i

      return n+1

顯然,這裡的演算法複雜度是O(n^2)。

二、先排序再查詢
第一種方法,每次查詢都是線性查詢,要改進最先想到的自然是二分查詢, 所以:
先排序,用O(nlgn)的快速排序、歸併排序或者堆排序;因為陣列中的元素是一些自然數,我們甚至可以使用O(n) 的基數排序,當然,需要更多的記憶體。
對1..n進行判斷,複雜度也為O(nlgn)
所以,整體的演算法複雜度為O(nlgn)

三、利用這種陣列的特性

  1. 先排序
  2. 再利用該特性搜尋
for i = 1 to n

    do if A[i] > i

         then return i

return n+1

注意到,如果我們使用基數排序,可以將複雜度降低到O(n)。

四、所有可能出現的情況。
(1) arr為整數1,2,3…N的一個隨機排列,那個未出現的最小正整數就是N+1。

(2) arr中有小於1或者大於N的數出現(我們稱之為“不合法”的數),則未出現的最小正整數一定在1到N中間(因為陣列一共只有N個數,如果出現不合法的數,則出現的1到N之間的數的個數一定小於N,故一定有沒有出現的數)。

int missMinNum(int arr[])
    {
        int l = 0; //l表示已經從1到L已經出現(左邊界),l的初值為0。

        int r = arr.length; //如果一個數字過大(不合法),就會被扔掉,用r表示這個右邊界,r初始值為arr長度。

//        int temp;

        while(l < r)
        {
            if(arr[l] == l +1)
            {
                l++;
            }
            else if(arr[l]> r || arr[l] <= l || arr[arr[l] - 1] == arr[l])//不合法
            {

                arr[l] =arr[--r];

            }
            else//合法但是沒有在理想的位置上
            {
                temp = arr[l];
                arr[l] =arr[arr[l] - 1];
                arr[temp - 1] = temp;

            }
        }
        return l + 1;
    }

時間複雜度O(n),改變了原陣列。
  

相關文章