该代码实际上具有二次复杂度吗?

时间:2019-04-14 15:28:31

标签: python arrays algorithm list time-complexity

最近,我看到了以下算法问题的一种解决方案:

  

给出一个整数数组,返回一个新数组,其中的每个元素   数组是该数组右边较小元素的数量   原始输入数组中的元素。

     

例如,给定数组[3,4,9,6,1],返回[1,1,2,1,0]

这是python中的解决方案:

import bisect

    def smaller_counts(lst):
        result = []
        seen = []
        for num in reversed(lst) :
            i = bisect.bisect_left(seen, num)
            result.append(i)
            bisect.insort(seen, num)
    return list(reversed(result))

问题代码。

此解决方案的作者声称它具有O(nlogn)复杂性,但对我来说似乎不正确。

这是我对复杂度的计算:

在函数 bisect_right 上方的代码中,仅执行二进制搜索,因此其复杂度为 O(logn)。 让我们看看insort的source codes

def insort_right(a, x, lo=0, hi=None):
    """Insert item x in list a, and keep it sorted assuming a is sorted.
    If x is already in a, insert it to the right of the rightmost x.
    Optional args lo (default 0) and hi (default len(a)) bound the
    slice of a to be searched.
    """

    lo = bisect_right(a, x, lo, hi)
    a.insert(lo, x)

def bisect_right(a, x, lo=0, hi=None):
    """Return the index where to insert item x in list a, assuming a is sorted.
    The return value i is such that all e in a[:i] have e <= x, and all e in
    a[i:] have e > x.  So if x already appears in the list, a.insert(x) will
    insert just after the rightmost x already there.
    Optional args lo (default 0) and hi (default len(a)) bound the
    slice of a to be searched.
    """

    if lo < 0:
        raise ValueError('lo must be non-negative')
    if hi is None:
        hi = len(a)
    while lo < hi:
        mid = (lo+hi)//2
        if x < a[mid]: hi = mid
        else: lo = mid+1
    return lo

代码1。

位置:

bisect = bisect_right
insort = insort_right

代码2。

很明显, insort_right 花费了 O(n)时间,因为它在开始时执行了二进制搜索,花费了 O(logn)然后插入一个需要花费 O(n)时间的元素。

现在让我们看一下python codes插入函数的实现:

static PyObject *
list_insert_impl(PyListObject *self, Py_ssize_t index, PyObject *object)
/*[clinic end generated code: output=7f35e32f60c8cb78 input=858514cf894c7eab]*/
{
    if (ins1(self, index, object) == 0)
        Py_RETURN_NONE;
    return NULL;
}

static int
ins1(PyListObject *self, Py_ssize_t where, PyObject *v)
{
    Py_ssize_t i, n = Py_SIZE(self);
    PyObject **items;
    if (v == NULL) {
        PyErr_BadInternalCall();
        return -1;
    }
    if (n == PY_SSIZE_T_MAX) {
        PyErr_SetString(PyExc_OverflowError,
            "cannot add more objects to list");
        return -1;
    }

    if (list_resize(self, n+1) < 0)
        return -1;

    if (where < 0) {
        where += n;
        if (where < 0)
            where = 0;
    }
    if (where > n)
        where = n;
    items = self->ob_item;
    for (i = n; --i >= where; )
        items[i+1] = items[i];
    Py_INCREF(v);
    items[where] = v;
    return 0;
}

代码3

因此我们可以注意到,由于静态int ins1(...)中的元素移位,所以插入需要O(n)时间。 现在,我们来计算问题代码中的情况。 首先,它调用 bisect.insort(seen,num),列表 seen 仅包含一个元素。在第二次迭代中,已见包含两个元素。 在第n次迭代中,列表 seen 已包含n个元素,因此可以这样计算操作计数: 1 + 2 + 3 + ... + n-1 + n ,即 n(n + 1)/ 2 ,其为 O(n ^ 2) 。 因此,对于第i次迭代,二进制搜索要花费 O(logn)时间,而插入要花费 O(n)时间(在main for循环主体中)。 因此最后,整个问题的复杂度为 O(n ^ 2)。 对于这个问题的复杂性,我的计算是否正确?


0 个答案:

没有答案