【问题标题】:How to find maximum number of groups needed to sort an Array?如何找到对数组进行排序所需的最大组数?
【发布时间】:2018-08-11 05:31:53
【问题描述】:

假设我有一个数组:

[1, 8, 5, 6, 10, 9, 11, 12];

我想按升序对其进行排序,但找出我需要排序的最大组数。在这个例子中,答案是:

[1], [8,5,6], [10,9], [11], [12]: so 5

[3, 2, 1] 会变成 1,因为整个数组都需要排序。

我完全不知道如何做到这一点,我们将不胜感激。

【问题讨论】:

  • 你可以遍历数组并计算 n[i+1] > n[i] 的次数
  • 是需要排序的最大连续组吗?
  • 看起来是 Tarjan 的强连通分量算法的一个很好的例子。
  • @GrumpyWelshGit 这不起作用,因为一个更大的数字可能在一个组的中间
  • @Dvorog 只是可以创建所有组。因此,即使某些数字被排序,例如 [1] 和 [11],它们仍然算作一组 1。像 [8,5,6] 这样的组是因​​为在该组中,您可以对所有数字进行排序到它们的适当位置。如果我在 9 之后添加 2,那么整个部分将改为 1 组。

标签: java arrays algorithm sorting grouping


【解决方案1】:

我的解决方案使用插入排序算法,该算法将已排序的元素保持在原位,并将未排序的元素移向数组的开头,直到它们到达原位。我们可以使用这种行为来检测需要排序的组。

在每次迭代中,我们检查当前元素是否大于或等于前一个元素。如果是这样,我们可能遇到了一个新组。我们将当前索引压入堆栈。

如果当前元素小于前一个元素,那么我们仍然在同一个组中。我们开始将此元素与之前的元素交换,直到它到位。在每一步,我们检查我们是否已经越过了前一组的边界。如果越界了,说明这两组其实是一组,所以我们从栈中弹出最后一个值。

最后,组数等于栈大小。

这里是实现:

public static int countGroups(int[] a) {
    if (a.length < 2) return a.length;
    Stack<Integer> stack = new Stack<>();
    stack.push(0);
    for (int i = 1; i < a.length; i++) {
        if (a[i] >= a[i - 1]) stack.push(i);
        for (int j = i; j > 0 && a[j] < a[j - 1]; j--) {
            swap(a, j, j - 1);
            if (j <= stack.peek()) stack.pop();
        }
    }
    return stack.size();
}

private static void swap(int[] a, int i, int j) {
    int t = a[i];
    a[i] = a[j];
    a[j] = t;
}

这是一个带有一些示例的 JavaScript sn-p:

console.log(countGroups([1, 8, 5, 6, 10, 9, 11, 12]));    //5 - [1], [8, 5, 6], [10, 9], [11], [12]
console.log(countGroups([1, 8, 5, 6, 10, 9, 2, 11, 12])); //4 - [1], [8, 5, 6, 10, 9, 2], [11], [12]
console.log(countGroups([3, 8, 5, 6, 10, 9, 2, 11, 1]));  //1 - [3, 8, 5, 6, 10, 9, 2, 11, 1]
console.log(countGroups([1, 2, 8, 6, 10, 9, 11]));        //5 - [1], [2], [8, 6], [10, 9], [11]
console.log(countGroups([1, 2, 1, 1, 10, 9, 10]));        //4 - [1], [2, 1, 1], [10, 9], [10]

function countGroups(a) {
    if (a.length < 2) return a.length;
    let stack = [0];
    for (let i = 1; i < a.length; i++) {
        if (a[i] >= a[i - 1]) stack.push(i);
        for (let j = i; j > 0 && a[j] < a[j - 1]; j--) {
            swap(a, j, j - 1);
            if (j <= stack[stack.length - 1]) stack.pop();
        }
    }
    return stack.length;
}

function swap(a, i, j) {
   let t = a[i];
   a[i] = a[j];
   a[j] = t;
}

更新:如果不需要实际对数组进行排序,看来问题可以在线性时间内解决:

public static int countGroupsLinear(int[] a) {
    Stack<Integer> stack = new Stack<>();
    stack.push(a[0]);
    for (int i = 1; i < a.length; i++) {
        if (a[i] >= stack.peek()) stack.push(a[i]);
        else {
            int last = stack.pop();
            while (stack.size() > 0 && a[i] < stack.peek()) stack.pop();
            stack.push(last);
        }
    }
    return stack.size();
}

【讨论】:

  • 堆栈解决方案不是线性时间,如果数组是反向排序的,在最坏的情况下它是二次的。
  • @thebenman 您的陈述不正确;堆栈解决方案是线性时间,因为每个数字都被压入堆栈一次并且最多弹出一次。
  • 你是对的。我第一次去没有注意到它。它看起来很像插入排序。这也是一个很好的解决方案
【解决方案2】:

这可以通过搭载 MergeSort 例程来完成。

真正的动作发生在合并排序例程的合并阶段。

这里的想法是针对合并数组的任何索引i

  1. 如果要添加的元素与插入的索引具有相同的索引,则我们知道它形成了一个组,我们将组计数增加 1。

假设i/p = [1, 7, 5, 4, 2, 12]

我们创建一个新数组来存储元素[0, 1, 2, 3, 4, 5, 6]的索引

对于给定的输入,将调用第一个合并函数 左子数组 => 0 右子数组 => 1

对于新合并数组的第 0 个元素,我们查看可以使用哪个元素。

所以第 0 个新元素来自索引 0,即 1,它们是相同的,所以我们将组数增加 1。索引 1 也是如此

  1. 假设合并数组中的索引 i 是索引 j 中的元素,其中 j > i 必须插入,我们现在知道从 i 到 j 的子数组可以是一个组,如果范围 [i , j] 在合并数组中属于这个范围。如果我们遇到另一个要添加的索引k,它大于j,我们将更新我们正在寻找的索引以包含k,即[i, k]。因为,如果我们仅考虑范围 [i, j],我们将在 k 处有一个元素,其中 input[k] &gt; input[j] 从而不会形成有效组。

示例 input = [3, 2, 0, 1] 将变为 [0, 1, 2, 3] for the last merge the left subarray will be[1, 0] the right subarray will be[2, 3 ]`

对于合并数组的索引 0,我们查看最小值 input[1]input[2],即 input[2] 并且索引为 2,因此我们现在知道该数组的最小值来自索引 2。我们继续查看合并数组中的索引 3 并更新贡献组的最高索引,并在两个索引相同时停止

在索引 1 处,我们在索引 1、3 之间进行选择,我们发现索引 [3] 是下一个最小值,我们将最高索引更新为 3 并继续直到合并索引的索引 i 等于被插入元素的索引。

它在O(N log N) 中运行,我们得到索引数组的结果是input 数组的排序索引

【讨论】:

    【解决方案3】:

    这个解是 O(N)。但它仅适用于具有不同数字的数组。

    public static List<List<Integer>> countGroups(int[] arr) {
    
        List<List<Integer>> result = new ArrayList<List<Integer>>();
    
        if (arr.length < 1)
            return result;
    
        // create mins from right to left
        int[] mins = new int[arr.length];
        int tempMin = arr[arr.length - 1];
        for (int i = arr.length - 1; i >= 0; i--) {
            tempMin = Math.min(tempMin, arr[i]);
            mins[i] = tempMin;
        }
    
        // create max from left to right
        int[] maxs = new int[arr.length];
        int tempMax = arr[0];
        for (int i = 0; i < arr.length; i++) {
            tempMax = Math.max(tempMax, arr[i]);
            maxs[i] = tempMax;
        }
    
        // now that you have the start of intervals (mins) and end of intervals, you can
        // simply check if you are
        // still in the interval
    
        List<Integer> list = new ArrayList<Integer>();
        for (int i = 0; i < arr.length - 1; i++) {
            list.add(arr[i]);
            if (mins[i] != mins[i + 1] && maxs[i] != maxs[i + 1]) {
                result.add(new ArrayList<Integer>(list));
                list.clear();
            }
        }
    
        list.add(arr[arr.length - 1]);
        result.add(new ArrayList<Integer>(list));
    
        return result;
    }
    

    `

    【讨论】:

      【解决方案4】:

      这是我能想出的解决方案,想法是保持数组的活动索引,其值在当前最大值和最小值之间,棘手的部分是保持当前最大值不在上下文中就像currentCheckableMaxIndex 一样,只要它落在上下文中就会变成currentMaxIndex

      public static int countSubArray(int[] A) {
      
          int len = A.length;
          int currentActiveIndex = 0;
          int count = 0;
          for(int i = 0; i < len;){
              int currentCheckableMaxIndex = i;
              int currentMinIndex = i, currentMaxIndex = i;
              for(int j = i; j < len; j++){
                  // if there is a new min value, set its index,
                  // also check if there is a checkable max that we can change to the new max
                  if(A[currentMinIndex] > A[j]) {
                      currentMinIndex = j;
      
                      if(A[currentMaxIndex] < A[currentCheckableMaxIndex])
                          currentMaxIndex = currentCheckableMaxIndex;
                  }
                  // setting only the current checkable max to avoid including a max whose index is higher than current minimum
                  // will only set it to the max index if there exists a new minimum whose index is > currentCheckableMaxIndex
                  if(A[currentCheckableMaxIndex] < A[j]) {
                      currentCheckableMaxIndex = j;
                  }
                  // save the current valid index only if the value in the current index is in between the current min and max
                  if(A[currentMaxIndex] >= A[j] && A[j] >= A[currentMinIndex]) currentActiveIndex = j;
              }
              // i should continue from the current valid index + 1
              i = currentActiveIndex + 1;
      
              count++;
      
              // loop will not get here again if i == len - 1, so we count the last group that would have be omitted
              if(i == len - 1) {
                  count++;
                  break;
              }
          }
      
          return count;
      
      }
      

      【讨论】:

        【解决方案5】:

        这里的代码可以很好地处理一个由 n 个不同整数组成的数组。这是在 C 中。

        #include <stdio.h>
        //code for the case where each element of the array is distinct 
        //and greater than 0
        //finding index of the maximum value in array
        int maximum(int *a,int n)
        {
            int i,max;
            max=0;
            for(i=0;i<n;i++)
            {
                if(a[i]>a[max])
                {
                    max=i;
                }
            }
            return max;
        }
        
        //finding index of the minimum value in array(excluding zeros)
        int minimum(int *a,int n)
        {
            int i,min,index;
            min=100000;
            for(i=0;i<n;i++)
            {
                if(a[i]!=0 && a[i]<min)
                {
                    min=a[i];
                    index=i;
                }
            }
            return index;
        }
        
        //checks for the presence of a non-zero element in array
        int check(int *a,int n)
        {
            for(int i=0;i<n;i++)
            {
                if(a[i]!=0)
                return 1;
            }
            return 0;
        }
        
        //main function
        int main()
        {
            int n,j,k,max,min,slices;
            slices=0;
            scanf("%d",&n);
            int a[n];
            for(int j=0;j<n;j++)
            {
                scanf("%d",&a[j]);
            }
            //until all the elements of the array become 0
            //continue iterating to find slices
            while(check(a,n))
            {
                max=maximum(a,n);
                min=minimum(a,n);
                //if index of minimum value is greater than 
                //index of maximum value
                if(max<min)
                {
                    slices=slices+1;
                    for(j=0;j<n;j++)
                    a[j]=0;
                }
                else
                {
                    for(j=0;j<=min;j++)
                    {
                        a[j]=0;
                    }
                    slices=slices+1;
                    if(check(a,n))
                    {
                        for(j=max;j<n;j++)
                        {
                            a[j]=0;
                        }
                        slices=slices+1;
                    }
                }
            }
            printf("slices %d",slices);
            return 0;
        }
        

        【讨论】:

          【解决方案6】:

          与上面提出的解决方案相同,但在 Python 3 中实现:

          def solution(A) -> int:
              if len(A) < 2:
                  return len(A)
          
              stack = [A[0]]
              for num in A[1:]:
                  if num >= stack[-1]:
                      stack.append(num)
                  else:
                      smallest = stack.pop()
                      while len(stack) > 0 and num < stack[-1]:
                          stack.pop()
                      stack.append(smallest)
              
              return len(stack)
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2015-01-06
            • 1970-01-01
            • 1970-01-01
            • 2021-11-30
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多