4種解法 - 確定數組大小減半


題目

給你一個整數數組 arr。你可以從中選出一個整數集合,並刪除這些整數在數組中的每次出現。

返回 至少 能刪除數組中的一半整數的整數集合的最小大小。

示例 1:

輸入:arr = [3,3,3,3,5,5,5,2,2,7]
輸出:2
解釋:選擇 {3,7} 使得結果數組爲 [5,5,5,2,2]、長度爲 5(原數組長度的一半)。
大小爲 2 的可行集合有 {3,5},{3,2},{5,2}。
選擇 {2,7} 是不可行的,它的結果數組爲 [3,3,3,3,5,5,5],新數組長度大於原數組的二分之一。
示例 2:

輸入:arr = [7,7,7,7,7,7]
輸出:1
解釋:我們只能選擇集合 {7},結果數組爲空。
示例 3:

輸入:arr = [1,9]
輸出:1
示例 4:

輸入:arr = [1000,1000,3,7]
輸出:1
示例 5:

輸入:arr = [1,2,3,4,5,6,7,8,9,10]
輸出:5

提示:

1 <= arr.length <= 10^5
arr.length 爲偶數
1 <= arr[i] <= 10^5


解法一(順序求解)

思路:最簡單樸素的解法,首先進行元素統計,然後根據統計的結果按最大到小進行計算,不過當前解法C#會在數據大的情況下超時,而Python和GO不會,超時測試用例有6萬多個元素,源碼及超時用例如下:https://www.zhenxiangsimple.com/files/tech/testCase20200202.txt

  1. 統計元素個數
  2. 按個數進行最大值累計,相當於冒泡排序
  • 時間複雜度:O(n2)
  • 空間複雜度:O(n)
public class Solution {
    public int MinSetSize(int[] arr) {
        Dictionary<int,int> r = new Dictionary<int,int>();
        for(int i=0;i<arr.Length;i++)
        {//數據統計
            if(r.ContainsKey(arr[i]))
            {
                r[arr[i]] += 1;
            }
            else
            {
                r[arr[i]] = 1;
            }
        }
        int c = 0,ct = 0;
        while(c * 2<arr.Length)
        {//不需要全排序,只要滿足條件即退出
            int mx = 0,idx = -1;
            foreach(var item in r)
            {//取數據最大值
                if(item.Value > mx)
                {
                    idx = item.Key;
                    mx = item.Value;
                }
            }
            r.Remove(idx);//最大值記錄後退出循環
            c += mx;
            ct ++;
        }
        return ct;
    }
}

解法二(字典排序)

思路:基於解法一,先對結果進行排序,然後使用排序後的結果進行統計,按道理解法一更快,但可能.NET3.5對Dictionary的排序優化使得效率比自己查詢更高。

  • 時間複雜度:不算排序部分,O(n)
  • 空間複雜度:O(n)
public class Solution {
    public int MinSetSize(int[] arr) {
        Dictionary<int,int> r = new Dictionary<int,int>();
        for(int i=0;i<arr.Length;i++)
        {//數據統計
            if(r.ContainsKey(arr[i]))
            {
                r[arr[i]] += 1;
            }
            else
            {
                r[arr[i]] = 1;
            }
        }
        //排序
        var rOrder = from ro in r orderby ro.Value descending select ro;

        int c = 0,ct = 0;
        foreach(KeyValuePair<int,int> item in rOrder)
        {//累計
            c += item.Value;
            ct ++;
            if(c*2 >= arr.Length)
            {
                return ct;
            }            
        }
        return 0;//僅爲編譯
    }
}

解法三(分類統計)

思路:基於分類統計的思想,首先將元素進行統計,使用數組下標表示數組中元素個數,數組元素的值表示不同元素的個數,最後通過統計該數組下標即可。如果該元素爲0,則表示不存在當前個數(數組下標對應的個數)的元素。

  1. 統計數組中重複元素的個數放入數組r
  2. 對數組中元素個數進行統計判斷
  3. 如果已經超出半數,則減去重複的個數(比如,r[2]=3,表示有兩個相同元素的元素有3個,比如1,1,2,2,3,3,…,那麼不需要3個元素全部累計,只需要滿足超出半數即可,因此需要把多餘的元素減去。
  • 時間複雜度:O(n)
  • 空間複雜度:O(n)
public class Solution {
    public int MinSetSize(int[] arr) {
        int [] r = new int[arr.Length+1];//存儲元素個數
        r[0] = arr.Length;
        Dictionary<int,int> d = new Dictionary<int,int>();
        foreach(int item in arr)
        {
            if(d.ContainsKey(item))
            {//已有元素時累計
                d[item]++;
            }
            else
            {//新元素增加
                d.Add(item,1);
            }

            r[d[item]]++;
            r[d[item]-1]--;
        }
        int c = 0,t = 0,idx = 0;
        for(int i=arr.Length;i>0;i--)
        {//從最大值到最小值進行統計
            if(r[i]>0)
            {
                c+= i*r[i];
                t+=r[i];
                idx = i;
            }
            if(c*2 >= arr.Length)
            {//如果超出限制,則減去重複的部分
                t -= (c*2 - arr.Length)/(2*i);
                break;
            }
        }
        return t;
    }
}

解法四(空間換時間)

思路:基於解法三的思路做一個變化,將值和索引做個交換,即使用一個大的空間,索引即爲值本身,數組元素的值爲該值對應的個數。缺點就是如果本身的值很大,就比較耗費資源,可以先便利一邊找到最大值,也可以直接選擇一個相對大的值,本次選擇後者,一個相對大的值110000(開始選擇10萬,提示越界改爲11萬可以了)。

  1. 統計值的個數
  2. 對統計數組進行排序
  3. 將排序結果從大到小進行求解
  • 時間複雜度:O(n),不算排序部分
  • 空間複雜度:O(1),雖然是1,但其實最大
public class Solution {
    public int MinSetSize(int[] arr) {
        int[] r = new int[110000];
        foreach(int item in arr)
        {//元素個數統計
            r[item]++;
        }
        Array.Sort(r);//排序
        int c = 0,ct = 0;
        for(int i=r.Length-1;i>0;i--)
        {//個數計數
            c += r[i];
            ct ++;
            if(c*2 >= arr.Length)
            {
                return ct;
            }  
        }
        return 0;//僅爲編譯
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章