如何从O(N ^ 2)开始执行时间O(N)

时间:2012-03-10 16:12:38

标签: optimization

我尝试在线解决一个编程问题(我的作业或我的任何面试/测试,但可能是一个很好的候选人)。 问题如下。我的代码在功能上是正确的,但它具有运行时复杂度O(N 2 ),其中预期的解决方案应该是O(N)。

我尝试了其他一些方法来优化它们 - 1)对数组进行排序,然后尝试我是否可以使用它,但由于排序导致原始数字的索引丢失,我甚至将它们保存在一个单独的数组中并且确实进行了调整,但即使最终也是O(N 2 )。我确信数组的排序有助于达到O(N),但却无法确定它。

使用任何方法在O(N)中完成此问题的任何帮助都会很有用。

(长篇道歉)

考虑N个整数的零索引数组A.该数组的指数是从0到N-1的整数。取索引K.如果A [J]>,则将索引J称为K的上升。 A [K]。注意,如果A [K]是数组A中的最大值,则K没有上升。

如果abs(K-J)是最小可能值(即,如果J和K之间的距离最小),则K的Ascender J被称为K的最近上升者。请注意,K最多可以有两个最接近的上升部分:一个较小,一个比K大。

例如,让我们考虑以下数组A:

A [0] = 4 A [1] = 3 A [2] = 1 A [3] = 4 A [4] = -1 A [5] = 2 A [6] = 1 A [7] = 5 A [8] = 7

如果K = 3,那么K有两个上升器:7和8.它最近的上升器是7,K和7之间的距离等于abs(K-7)= 4.

写一个函数:

struct Results {
  int * R;
  int N;
};

struct Results array_closest_ascenders(int A[], int N);

给定N个整数的零索引数组A,返回N个整数的零索引数组R,这样(对于K = 0,...,N-1):

    if K has the closest ascender J, then R[K] = abs(K−J); that is, R[K] is equal to the distance between J and K,
    if K has no ascenders then R[K] = 0.

例如,给定以下数组A:

A [0] = 4 A [1] = 3 A [2] = 1 A [3] = 4 A [4] = -1 A [5] = 2 A [6] = 1 A [7] = 5 A [8] = 7

该函数应返回以下数组R:

R [0] = 7 R [1] = 1 R [2] = 1 R [3] = 4 R [4] = 1 R [5] = 2 R [6] = 1 R [7] = 1 R [8] = 0

数组R应该返回为:

    a structure Results (in C), or
    a vector of integers (in C++), or
    a record Results (in Pascal), or
    an array of integers (in any other programming language).

假设:

    N is an integer within the range [0..50,000];
    each element of array A is an integer within the range [−1,000,000,000..1,000,000,000].

复杂度:

    expected worst-case time complexity is O(N);
    expected worst-case space complexity is O(N), beyond input storage (not counting the storage required for input arguments).

可以修改输入数组的元素。

我的解决方案(O(N 2 )):

#include <math.h>
#include "stdio.h"
#include "stdlib.h"

struct Results
{
    int *R;
    int N;
};

struct Results array_closest_ascenders ( int A[], int N )
{
    struct Results result;


     int i,j,asc_found=0;

     result.R = (int*)malloc(sizeof(int)*N);

     for(i=0;i<N;i++)
       result.R[i] = N;

     result.N = N;

     for(i=0;i<N;i++)
     {
       asc_found = 0;
       for(j=0;j<N;j++)
       {
         if(A[i] < A[j])
         {
           //if(result.R[i] == 0)
           {
               if(result.R[i] > abs(i-j))
               {
                    result.R[i] = abs(i-j);
                    asc_found = 1;
               }
           }
         }
       }
       if(asc_found == 0)
           result.R[i] = 0;
     }


    return result;
}

void main()
{

    //int A[] = {4, 3, 1, 4, -1, 2, 1, 5, 7};
    int A[] = {691446939, -241956306, 485954938, 604054438, 383714185, -656099986, -357341170, -255988102, -139683363, -463281394, -382925609, 712727854};
    struct Results tmp;

    tmp = array_closest_ascenders(A,sizeof(A)/sizeof(A[0]));

}

6 个答案:

答案 0 :(得分:2)

左侧最近的上升者最近的上升者可以单独考虑。我们经历了一次数组,计算出最近的上升点;再一次在相反的方向,计算最近的上升。最近的上升者是两者中最接近的。

在下面的算法中,只考虑左上方。一堆索引跟踪当前元素的左上升(所有这些)。最近的上升者始终位于堆栈的顶部。

for i in 0 .. N
  while (!stack.empty) && (A[stack.top] <= A[i])
    stack.pop
  if stack.empty
    then R[i] = 0
    else R[i] = i - stack.top
  stack.push(i)

答案 1 :(得分:0)

我的意思是将其添加为评论但未显示任何评论链接。 R []不应该跟随吗?

R[0]=abs(0-7)=7;
R[1]=abs(1-0)=1;
R[2]=abs(2-5)=3;
R[3]=abs(3-7)=4;
R[4]=abs(4-2)=2;/or 4-6
R[5]=abs(5-1)=4;
R[6]=abs(6-5)=1;
R[7]=abs(7-8)=1;
R[8]=abs(8-8)=0;

根据您在下面的评论,这是一个有效的程序。该算法基于计数排序(如http://geekviewpoint.com/Sort_Algorithms_in_java/所示)。 “偏移”是允许负值,如“-1”

注意:虽然有一个用于填充R []的嵌套循环,但算法不是N ^ 2,因为第二个循环正在迭代桶的数量。

答案:[7,1,1,4,1,2,1,1,0]

import java.util.ArrayList;
import java.util.Arrays;

public class Ascender {
public static int[] ascenderArray(int[] A) {
    int max = A[0], min = A[0];
    for (int i : A) {
        if (max < i)
            max = i;
        if (min > i)
            min = i;
    }
    int offset = min < 0 ? -min : 0;
    ArrayList<Integer> buckets[] = new ArrayList[max + 1 + offset];
    for (int i = 0; i < buckets.length; i++)
        buckets[i] = new ArrayList<Integer>();
    for (int i = 0; i < A.length; i++)
        buckets[A[i] + offset].add(i);

    int[] R = new int[A.length];
    for (int i = 0; i < R.length; i++) {
        try {
            min = A.length;
            int x = A[i] + 1 + offset;
            for (int n = x; n < buckets.length; n++) {
                x=n;
                while (buckets[x].isEmpty())
                    x++;
                n=x;
                int tmp = Math.abs(i - buckets[n].get(0));
                if (min > tmp)
                    min = tmp;
            }
            R[i] = min==A.length?0:min;
        } catch (Exception e) {
            R[i] = 0;
        }
    }
    return R;
}//

public static void main(String... args) {
    int A[] = { 4, 3, 1, 4, -1, 2, 1, 5, 7 };
    int R[] = ascenderArray(A);
    System.out.println(Arrays.toString(R));
}
}

答案 2 :(得分:0)

我不确定是否有Ο( N )解决方案,但这里有一个Ο( N ·log N ) JavaScript中的解决方案:

var A = [4,3,1,4,-1,2,1,5,7],
    N = A.length;

// build an array of index:value pairs and sort them in ascending order
var sorted = [];
for (var i=0; i<N; i++) {
    sorted.push({index:i, value:A[i]});
}
sorted.sort(function(a, b) { return a.value - b.value; });

// iterate the sorted array in descending order
var ascenders = [],
    closest,            // closest ascender
    curr = sorted[N-1]; // initiate curr with largest item
ascenders[N-1] = 0;
for (var i=N-2; i>=0; i--) {
    // compare curr from the previous iteration with the future
    // curr of the current iteration
    if (sorted[i].value !== curr.value) {
        // update closest ascender if their values differ
        closest = curr;
    }
    curr = sorted[i];
    ascenders[i] = Math.abs(curr.index-closest.index);
}

// rearrange ascenders in the original order
var ret = [];
for (var i=0; i<N; i++) {
    ret[sorted[i].index] = ascenders[i];
}

在这里排序值是Ο( N ·log N )的最大努力。

答案 3 :(得分:0)

import java.util.Arrays;
import java.util.TreeMap;

public class AsenderUtil {

    public static int[] getClosestAsenderIndices(int[] arr, int indx) {

        if (indx < 0 || arr.length <= indx)
            throw new RuntimeException("wrong input");

        int baseVal = arr[indx];

        TreeMap<Integer,Integer> ts = new TreeMap<Integer,Integer>();

        for(int i=0;i<arr.length;i++){
            if(arr[i]>baseVal) {
                ts.put(Math.abs(i-indx),i);
            }
        }

        int[] toReturn = new int[2];
        toReturn[0] = ts.remove(ts.firstKey());
        toReturn[1] = ts.remove(ts.firstKey());

        return toReturn;
    }

    public static void main(String... args) {
        int A[] = { 4, 3, 1, 4, -1, 2, 1, 5, 7 };
        int R[] = getClosestAsenderIndices(A,3);
        System.out.println(Arrays.toString(R));
    }
}

答案 4 :(得分:0)

class Program
{
    static void Main(string[] args)
    {
        int[] A = new int[] { 4, 3, 1, 4, -1, 2, 1, 5, 7 };
         int[] B = new int[A.Length];
        int[] R = new int[A.Length];
        Program obj = new Program();
        obj.ABC(A,B, R);
    }

    public void ABC(int[] A,int[]B, int[] R)
    {
        int i, j, m,k;
        // int temp = 0;
        int n = A.Length - 1;
        for (i = 0; i < n; i++)
        {
            for (j = 0; j <= n; j++)
            {
                if (A[i] < A[j])
                {
                    m = Math.Abs(j - i);
                    R[i] = m;
                    break;

                }

            }
            for (j = i-1; j > 0; j--)
            {
                if (A[i] < A[j])
                {
                    k = Math.Abs(j - i);
                    B[i] = k;
                    break;

                }

            }

        }
        for (i = 0; i < n; i++)
        {
            if (R[i] > B[i] && (B[i] == 0))
            {

                R[i] = R[i];
                //Console.WriteLine(R[i]);
                //Console.ReadLine();
            }

            else { R[i] = B[i]; }
        }


    }
}

答案 5 :(得分:0)

我的解决方案是O(N):

class ArrayClosestAscendent {
 public int[] solution(int[] A) {
     int i;
     int r[] = new int[A.length];
     for(i=0;i<A.length;i++){
         r[i] = search(A, i);
     }
     return r;
 }

public int search(int[] A, int i) {
    int j,k;
    j=i+1;
    k=i-1;
    int result = 0;
    if(j <= A.length-1 && (A[j]>A[i]))
            return Math.abs(j-i);
    j++;
     while(k>=0 || j < A.length){
         if(k >= 0 && A[k] > A[i]){
             return Math.abs(i-k);
         }else if(j < A.length && A[j] > A[i]){
             return Math.abs(i-j);
         }else{
             j++;
             k--;
         }
     }
     return result;
}    
}

基本上在搜索功能中,我将数组的第一个元素与右边的元素进行比较,如果它更大,这意味着它是第一个最接近的上升元素。对于其他元素,我比较左边的一个元素,然后紧接着右边的元素。第一个更大的是最接近的上升,我继续这样迭代,直到我找不到比我正在考虑的元素大或我返回0的元素。