1. 程式人生 > >無序數組中找出最大的兩個(K)數

無序數組中找出最大的兩個(K)數

數組;查找;最大;K個

**給你一個整型數組,我想找出來最大的兩個數,能幫我寫一個算法嗎?


**
在上一遍我們已經解讀過這道題目了,包括我們能想到的問題。這裏我們按照解決算法題的一般步驟再來一起分析一下這道題:

一、確保我們理解了問題,並且嘗試一個例子,確認理解無誤。
那現在我們澄清一下問題,我們需要從這樣的數組中{ 4, 5,2,3,1}中找出最大的兩個數字返回{4,5},當數組為空的時候我們也返回空的數組。
二、想想你可以用什麽方法解決問題,你會選擇哪一種,為什麽?
我們不想先對他進行排序,原因有兩個,數組是引用類型,排序會改變原來的數據結果,我相信你調用別人SDK的時候,肯定也不想自己的數據莫名其妙的被改變了對吧,即使我們能保證不改變原來的次序,我們也會引入額外的存儲空間,另外,題目當中沒有要求將所有的數字排序返回,排序似乎有些浪費。而且,一般的排序最好能做到n log(n), 而這道題其實只需要o(n)就能解決。

三、 解釋你的算法和實現的方法
解釋我們的算法,我們申明兩個臨時變量, max, secondmax,然後開始掃描數組。若數組中當前的數字比max還大,我們就替換到max,當然替換之前,先將max賦值到secondmax裏去,若數組中當前的數字比max小,我們再比較是否比secondmax大,倘若大,我們將直接將其賦值到secondmax上去,其他情況我們就不用處理啦。
四、寫代碼的時候,記住,一定要解釋你現在在幹什麽


public static int[] FindMaxTwo(int[] array)  
       {  
           // 如果輸入的數組是空,或者數組的長度只有一個,我們也就沒有必要處理了,直接返回  
           if (array == null || array.Length == 1) return array;  
           // 定義兩個臨時變量,想一想我們為什麽要賦值為int.minvalue  
           int max = int.MinValue;  
           int secondMax = int.MinValue;  
           // 開始掃描數組  
           for (int index = 0; index < array.Length; index++)  
           {  

               // 如果當前的數字比max還大,我們將改變secondmax的值,然後該表max  
               if (array[index] >= max)  
               {  
                   secondMax = max;  
                   max = array[index];  
               }  
               // 如果當前數字沒有比max大,但是比secondmax大,我們直接改變secondmax的值  
               else if (array[index] >= secondMax)  
               {  
                   secondMax = array[index];  
               }  
           }  

           // 好了,我們將得到的這兩個值放到數組裏,準備返回吧  
           int[] result = new int[2];  

           result[0] = secondMax;  
           result[1] = max;  

           return result;  
       }  

五、拿一個實例,Work Through你的代碼(省略了哈)
六、修復缺陷,保證異常情況都被考慮到。我們需要列出來這裏的一些特殊情況,如數組中如果存在相同的數字而且都是最大的數,我們返回什麽呢?如{5,3,4,1,5},返回{4,5}還是{5,5}呢,不一樣的要求會對處理的細節有所差別。如果數組中全是負數我們的代碼還能返回期待的結果嗎?

嘗試一下: 大家可以把intmax= int.MinValue; int secondMax = int.MinValue;改成intmax;int secondmax;再試試 {-5,-2,-4,-3,-1}試試看,這裏會出現一個bug。其根本原因在於,int 的默認值是0。

再嘗試一下:我們先定義了兩個臨時變量,之後又申明了一個數組,是不是有點浪費。不簡潔有沒有?好的,我們給它優化一下:

public static int[] FindMaxTwoImproved2(int[] array)  
       {  
           if (array == null || array.Length == 1) return array;  

           // Here is a bug when the max value is negative since the initial value in the int array is 0  
           int[] result = new int[2];  

           // This is to fix the bug above  
           for (var index = 0; index < result.Length; index++)  
           {  
               result[index] = int.MinValue;  
           }  

           Console.WriteLine(result[1]);  
           for (int index = 0; index < array.Length; index++)  
           {  
               if (array[index] >= result[0])  
               {  
                   result[0] = result[1];  
                   result[1] = array[index];  
               }  
               else if (array[index] >= result[1])  
               {  
                   result[0] = array[index];  
               }  
           }  

           return result;  
       }  

回到上一篇的問題中來,我們曾今問過K是否會變,能不能做到以不變應萬變,動態一些,你讓我們返回幾個我們就返回幾個呢?當然可以,這次我們申明一個長度為K的數組,直接來看代碼:

 // Let‘s extend the array to get K fliexable  
        public static int[] FindMaxTwoImproved3(int[] array, int k)  
        {  
            if (k < 0) throw new ArgumentException("The K should not be negative.");  
            if(array != null && array.Length < k) throw new ArgumentException("The K should less than the length of target array.");  
            if (array == null || array.Length == 1) return array;  

            // Here is a bug when the max value is negative since the initial value in the int array is 0  
            int[] result = new int[k];  

            // This is to fix the bug above  
            for (var index = 0; index < result.Length; index++)  
            {  
                result[index] = int.MinValue;  
            }  

            for (int index = 0; index < array.Length; index++)  
            {  
                if (array[index] >= result[k - 1])  
                {  
                    <span style="background-color:rgb(51,204,0);">for (int fallback = 0; fallback < k - 1; fallback++)</span>  
<span style="background-color:rgb(51,204,0);">                    {</span>  
<span style="background-color:rgb(51,204,0);">                        result[fallback] = result[fallback + 1];</span>  
<span style="background-color:rgb(51,204,0);">                    }</span>  
<span style="background-color:rgb(51,204,0);"></span>  
                    result[k - 1] = array[index];  

                }  
                else  
                {  
                    for (int fallback = k - 1; fallback >= 0; fallback--)  
                    {  
                        if (array[index] >= result[fallback])  
                        {  
                            <span style="background-color:rgb(51,204,0);">for (int fallback2 = 0; fallback2 < fallback; fallback2++)</span>  
<span style="background-color:rgb(51,204,0);">                            {</span>  
<span style="background-color:rgb(51,204,0);">                                result[fallback2] = result[fallback2 + 1];</span>  
<span style="background-color:rgb(51,204,0);">                            }</span>  
                            result[fallback] = array[index];  
                        }  

                    }  
                }  
            }  

            return result;  
        }  

在這段代碼中,大家有沒有發現有代碼重復的嫌疑?我把他們高亮起來了。我們再定義一個公共方法,

/// <summary>  
      /// 改方法會負責將當前數組array從前往後的K-1個數字依次賦值成後面的數組,  
      /// 而最後一個值我們將替換成target  
      /// </summary>  
      /// <param name="array"></param>  
      /// <param name="target"></param>  
      /// <param name="k"></param>  
      public static void Fallback(int[] array, int target, int k)  
      {  
          if (k > 0)  
          {  
              for (int fallback2 = 0; fallback2 < k - 1; fallback2++)  
              {  
                  array[fallback2] = array[fallback2 + 1];  
              }  
              array[k - 1] = target;  
          }  
      }  

將高亮部分也替換一下,看最後的代碼:

// Let‘s remove the duplication code  
       public static int[] FindMaxTwoImproved4(int[] array, int k)  
       {  
           if (k < 0) throw new ArgumentException("The K should not be negative.");  

           if (array == null || array.Length == 1) return array;  

           // Here is a bug when the max value is negative since the initial value in the int array is 0  
           int[] result = new int[k];  

           // This is to fix the bug above  
           for (var index = 0; index < result.Length; index++)  
           {  
               result[index] = int.MinValue;  
           }  

           for (int index = 0; index < array.Length; index++)  
           {  
               if (array[index] >= result[k - 1])  
               {  
                   Fallback(result, array[index], k);  

                   result[k - 1] = array[index];  

               }  
               else  
               {  
                   for (int fallback = k - 1; fallback >= 0; fallback--)  
                   {  
                       if (array[index] >= result[fallback])  
                       {  
                           Fallback(result, array[index], fallback);  

                           result[fallback] = array[index];  
                       }  
                   }  
               }  
           }  
           return result;  
       }  

大家發現了嗎?這是不是我們見過的一種排序算法。沒錯,當K = N 時,這就是開辟了新存儲空間的插入排序了。有沒有?
接下來我們就可以定義各種不同的輸入去測試我們的算法啦,這裏是一個例子。供大家參考:

static void Main(string[] args)  
      {  
          int[] array = new int[5] { -5, -3, -4, -5, 10 };  
          var result = FindMaxTwo(array);  

          PrintArray(result);  

          result = FindMaxTwoImproved4(array, 2);  
          PrintArray(result);  

      }  

      public static void PrintArray(int[] array)  
      {  
          if (array == null)  
          { Console.WriteLine("The array is null or empty"); }  

          else  
          {  
              foreach (var i in array)  
              {  
                  Console.Write(i + "  ");  
              }  
              Console.WriteLine();  
          }  
      }  

歡迎大家關註我的公眾號,還有我的系列視頻教程, 數據結構與算法經典算法面試題輔導, 排序專題講解鏈表專題講解

大家有什麽更好的解法,也歡迎討論哈。

技術分享圖片

無序數組中找出最大的兩個(K)數