【问题标题】:Find the length of minimum sub array with maximum degree in an array求数组中度数最大的最小子数组的长度
【发布时间】:2017-10-04 08:17:21
【问题描述】:

我尝试解决hackerrank中的一个问题,即在数组中找到具有最大度数的最小子数组的长度。数组的最大度数是具有最大频率的元素的计数。例如,考虑示例 {2, 2, 1, 2, 3, 1, 1},最小子数组长度为 4,因为 2 的度数最大,而度数为 3 的最小子数组是 {2, 2, 1, 2}

下面是我的解决方案

public class FindingMinSubArrayWithDegree {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int[] arr = new int[n];
        for (int i = 0; i < n; i++) {
            arr[i] = sc.nextInt();
        }
        System.out.println(degreeOfArray(arr));
        sc.close();
    }

    static int degreeOfArray(int[] arr) {
        HashMap<Integer, Integer> numbersByDegree = new HashMap<Integer, Integer>();
        for (int i = 0; i < arr.length; i++) {
            int degree = numbersByDegree.getOrDefault(arr[i], 0);
            numbersByDegree.put(arr[i], degree + 1);
        }
        List<Map.Entry<Integer, Integer>> sortedEntries = sortByValue(numbersByDegree);
        int maxDegree = sortedEntries.get(0).getValue();

        int[] degreeArr = new int[arr.length] ;
        int minSubArrayLength = arr.length;
        for (Map.Entry<Integer, Integer> entry : sortedEntries) {
            if (entry.getValue() < maxDegree) {
                break;
            }
            boolean startIndexFound = false, endIndexFound = false;
            int startIndex = 0, endIndex = 0;
            for (int i = 0; i < arr.length; i++) {
                if (entry.getKey() == arr[i]) {
                    if (i - 1 >= 0)
                        degreeArr[i] = degreeArr[i - 1] + 1;
                    else
                        degreeArr[i] = 1;
                } else {
                    if (i - 1 >= 0)
                        degreeArr[i] = degreeArr[i - 1];
                }
                if (!startIndexFound && degreeArr[i] == 1) {
                    startIndex = i;
                    startIndexFound = true;
                }
                if (!endIndexFound && degreeArr[i] == entry.getValue()) {
                    endIndex = i;
                    endIndexFound = true;
                }
                if (startIndexFound && endIndexFound)
                    break;
            }
            startIndexFound = false; endIndexFound = false;
            if ((endIndex - startIndex) < minSubArrayLength) {
                minSubArrayLength = endIndex - startIndex;
            }
            for (int i = 0; i < degreeArr.length; i++)
                degreeArr[i] = 0;
        }
        return minSubArrayLength + 1;
    }

    private static <K, V extends Comparable<? super V>> List<Map.Entry<K, V>> 
    sortByValue(Map<K, V> map) {
        List<Map.Entry<K, V>> list = new LinkedList<Map.Entry<K, V>>(map.entrySet());
        Collections.sort( list, new Comparator<Map.Entry<K, V>>() {
            public int compare(Map.Entry<K, V> o1, Map.Entry<K, V> o2) {
                return (o2.getValue()).compareTo( o1.getValue() );
            }
        });
        return list;
    }
}

对于 { 1, 1, 2, 2, 3, 3, 4, 4} 等输入,这种方法的最坏情况运行时间为 O(N ^ 2)。有没有更好的算法来解决这个问题?

PS - 我尝试在代码审查中提出这个问题,但没有得到任何回应,这就是为什么搬到这里

【问题讨论】:

  • 什么是数组的degree?
  • 数组的度数是频率最高的元素个数

标签: java arrays algorithm time-complexity


【解决方案1】:

要找到数组的度数,我们只需要跟踪数组中每个不同元素的频率,那些频率最高的元素就是度数。

所以,要找到度数最大的子数组,我们只需要关心包含具有最大计数的元素的子数组,这意味着所有[start , end]的子数组都是开始和结束出现那个元素。

因此,我们需要做的是跟踪每个元素的频率、开始和结束位置。

伪代码:

int max = 0;
Map<Integer, Integer> map = new HashMap<>();
Map<Integer, Integer> startIndex = new HashMap<>();
Map<Integer, Integer> endIndex = new HashMap<>();
for(int i = 0; i < data.length; i++){
   int value = data[i];
   if(map.containsKey(value)){
      map.put(value, map.get(value) + 1);
   }else{
      startIndex.put(value, i);
      map.put(value, 1);
   }
   endIndex.put(value, i);
   max = Integer.max(max, map.get(value));//Calculate the degree of the array
}
int result = data.length;
for(int i : map.keySet()){
   if(map.get(i) == max){
      int len = endIndex.get(i) - startIndex.get(i) + 1;
      result = Integer.min(result, len);
   }
}
return result;

时间复杂度为 O(n)

【讨论】:

    【解决方案2】:

    给定一个由非负整数 nums 组成的非空数组,该数组的度数定义为其任何一个元素的最大频率。

    我们将创建 3 个 HashMap 。

    • 记住每个元素的频率——HashMap Count
    • 每个元素第一次出现的索引 -- HashMap left
    • 每个元素最后出现的索引 -- HashMap Right。

    然后我们将遍历 Count(HashMap) 并比较最大频率,如果等于最大频率,那么我们将找到一个(连续)子数组的长度。

    代码:-

    class Solution {
    public int findShortestSubArray(int[] nums) {
        int n = nums.length;
        int degree = 0;
        HashMap<Integer,Integer> count = new HashMap<Integer,Integer>();
        HashMap<Integer,Integer> left = new HashMap<Integer,Integer>();
        HashMap<Integer,Integer> right = new HashMap<Integer,Integer>();
        for(int i = 0;i<n;i++){
            count.put(nums[i],count.getOrDefault(nums[i],0)+1);
            if(!left.containsKey(nums[i]))left.put(nums[i],i);
            right.put(nums[i],i);
            degree = Math.max(degree,count.get(nums[i]));
        }
        int len ;
        int result = n;
        for(int c : count.keySet()){
            if(count.get(c)==degree){
                len = right.get(c)-left.get(c)+1;
                if(len < result)
                    result = len;
            }
    
        }
        return result;
    
    }
    

    }

    时间复杂度 O(N) 空间复杂度 O(N)

    【讨论】:

      【解决方案3】:

      JavaScript 解决方案:

      function findShortestSubArray(nums) {
      
        // Elements is a map of key => elementInfo
        // with key being each of the elements in the array
        // and elementInfo being the object with properties count, leftIndex, rightIndex for 
        // one particular element in the array
      
        let degree = 0
        const elementsInfoHighestCount = new Map()
        let subArray = []
      
        const elements = nums.reduce((acc, num, index) => {
          let count
          let leftIndex
          let rightIndex
      
          if (acc.has(num)) {
            const existing = acc.get(num)
            count = existing.count + 1
            leftIndex = existing.leftIndex
            rightIndex = index
      
          } else {
            count = 1
            leftIndex = index
            rightIndex = index
          }
      
          return acc.set(num, { count, leftIndex, rightIndex })
        }, new Map())
      
      
        // Determine the degree by looping through elements map
        elements.forEach((element, uniqueNum) => {
          if (element.count === degree) {
            elementsInfoHighestCount.set(uniqueNum, element)
          } else if (element.count > degree) {
            elementsInfoHighestCount.clear()
            elementsInfoHighestCount.set(uniqueNum, element)
            degree = element.count
          }
        })
      
        // Get the shortest subarray array by looping through the elementInfoHighestCount map
        let result = elementsInfoHighestCount.values().next().value
        if (elementsInfoHighestCount.size === 1) {
          subArray = nums.slice(result.leftIndex, result.rightIndex + 1)
        } else if (elementsInfoHighestCount.size > 1) {
          elementsInfoHighestCount.forEach((element, num) => {
      
            const thisElementDiff = element.rightIndex - element.leftIndex
            const previousElementDiff = result.rightIndex - result.leftIndex
      
            if (thisElementDiff - previousElementDiff < 0) {
              result = elementsInfoHighestCount.get(num)
            }
          })
      
          subArray = nums.slice(result.leftIndex, result.rightIndex + 1)
        }
      
        return subArray.length
      };
      
      // Time complexity: O(N)
      
      // Testcases - [1, 2, 2, 3, 1], [1,2,2,3,1,4,2]
      

      【讨论】:

        【解决方案4】:

        在 Python 中:

        我们将这个数组的度数定义为它的任何元素的最大频率。请注意,您需要三个字典来保存计数和位置

        def codeHere(arrayinput):
          numItems = len(arrayinput)
        
          degree = 0
        
          left = dict()
          count = dict()
          right = dict()
          lenght = 0
          result = numItems
        
          for i in range(numItems):
              x = arrayinput[i]
        
              if (x not in count):
                  count[x] = 1
                  left[x] = i
              else:
                  count[x] += 1
                  right[x] = i
              if (count[x] > degree):
                  degree = count[x]
        
          for i in count.keys():
              if(count[i] == degree):
                  lenght=right[i] - left[i] + 1
                  if(lenght<result):
                      result=lenght
        
          return (result)
        

        【讨论】:

          猜你喜欢
          • 2016-06-16
          • 2020-11-22
          • 1970-01-01
          • 1970-01-01
          • 2014-08-26
          • 2020-04-18
          • 2019-12-25
          • 2020-09-24
          相关资源
          最近更新 更多