如何在O(n)运行时间内重新组织数组& O(1)空间复杂度?

时间:2016-05-13 19:38:35

标签: arrays algorithm sorting space-complexity swift2.2

我是一个空间复杂性'新手,并给了一个问题。

假设我有一个任意整数数组:
var transDetails = new { Account_num = accountNumber // in parm Trans_amount = 0 // out parm }; Db.Execute("get_details", transDetails); // Does not work Db.Query("get_details", transDetails); // Does not work Db.Execute("get_details", transDetails, outputParameters: transDetails); // Does not work either // transDetails.Trans_amount is unchanged.

如何重新排序此数组以使一端具有所有零:
[1,0,4,2,1,0,5]

...并计算非零整数的计数(在这种情况下:5)?

...在[1,4,2,1,5,0,0]运行时O(n)空间复杂度?

我不擅长这个。
我的背景是环境工程而不是计算机科学,所以我通常会抽象地思考。

我以为我可以做一个排序,然后计算非零整数。
然后我想我只能在重新排列数组时执行元素元素复制。
然后我想到了类似于冒泡的东西,切换相邻的元素,直到我用零结束。
我以为我可以节省空间复杂度'通过移位阵列成员'地址,即数组点指向数组,具有对其成员的偏移量。

我要么以牺牲空间复杂性为代价来增强运行时,反之亦然。

解决方案是什么?

7 个答案:

答案 0 :(得分:4)

Two-pointer approach将解决此任务并保持时间和内存限制。

首先将一个指针放在末尾,另一个指针放在数组的开头。然后递减结束指针,直到看到第一个非零元素。

现在是主循环:

  • 如果开始指针指向零,则将其与指向的值交换 由指针结束;然后递减结束指针。
  • 始终递增开始指针。
  • 当开始指针变得大于或等于结束时结束 指针。

最后,返回开始指针的位置 - 这是非零元素的数量。

答案 1 :(得分:2)

这是 @kfx 提供的智能答案的Swift代码

func putZeroesToLeft(inout nums: [Int]) {
    guard var firstNonZeroIndex: Int = (nums.enumerate().filter { $0.element != 0 }).first?.index else { return }

    for index in firstNonZeroIndex..<nums.count {
        if nums[index] == 0 {
            swap(&nums[firstNonZeroIndex], &nums[index])
            firstNonZeroIndex += 1
        }
    }
}

时间复杂度

有两个简单(非嵌套)循环重复最多n次(其中n是输入数组的长度)。所以时间是O(n)

空间复杂性

在输入数组旁边,我们只使用firstAvailableSlot int var。所以空间绝对是一个常数:O(1)。

答案 2 :(得分:1)

使用2个指针和交换的建议答案正在改变非零数组元素的顺序,这与提供的示例相冲突。 (虽然他没有明确指出这个限制,所以也许它是无关紧要的)

相反,从左到右遍历列表并跟踪到目前为止遇到的0的数量。

设置counter = 0(到目前为止遇到零)。

在每个步骤中,执行以下操作:

  1. 检查当前元素是否为0。
    1. 如果当前元素为0,则递增counter
    2. 否则,将当前元素向左移动counter
  2. 转到下一个元素。
  3. 当您到达列表末尾时,将值从array[end-counter]覆盖到数组的末尾,并使用0。 非零整数的数量是数组的大小减去计数的零。

    该算法具有O(n)时间复杂度,因为我们在整个数组中最多两次(所有0的数组;我们可以稍微修改更新方案,但最多只能修改一次)。它只使用一个额外的变量进行计数,它满足O(1)空间约束。

答案 3 :(得分:0)

  1. 开始迭代数组(比如i)并保持遇到的零数(比如说zero_count)直到现在。
  2. 当前元素为0时,不要递增迭代计数器。而是递增zero_count
  3. i + zero_count索引中的值复制到当前索引i
  4. i + zero_count大于数组长度时终止循环。
  5. 将剩余的数组元素设置为0
  6. 伪代码:

    zero_count = 0;
    i = 0;
    while i + zero_count < arr.length 
      if (arr[i] == 0) {
        zero_count++;
        if (i + zero_count < arr.length)
          arr[i] = arr[i+zero_count]
      } else {
        i++;
      }
    
    while i < arr.length
      arr[i] = 0;
      i++;
    

    此外,这还保留了数组中非零元素的顺序,

答案 4 :(得分:0)

正如其他答案所示,我们的想法是有两个指针,pq,一个指向数组的末尾(特别是在后面的第一个非零条目)和其他指向数组的开头。每次点击q0p指向的交换元素,递增q并递减p时,使用q扫描数组(具体来说,让它指向后面的下一个非零条目);迭代只要p < q

在C ++中,您可以这样做:

void rearrange(std::vector<int>& v) {
    int p = 0, q = v.size()-1;
    // make q point to the right position
    while (q >= 0 && !v[q]) --q;
    while (p < q) {
        if (!v[p]) { // found a zero element
            std::swap(v[p], v[q]);
            while (q >= 0 && !v[q]) --q; // make q point to the right position
        }
        ++p;
    }
}

答案 5 :(得分:0)

从数组的远端开始并向后工作。首先扫描,直到你达到非零(如果有的话)。跟踪此非零的位置。继续扫描。每当你遇到零交换。否则增加非零数。

Python实现:

def consolidateAndCount(nums):
    count = 0
    #first locate last nonzero
    i = len(nums)-1
    while nums[i] == 0:
        i -=1
        if i < 0:
            #no nonzeros encountered
            return 0
    count = 1 #since a nonzero was encountered
    for j in range(i-1,-1,-1):
        if nums[j] == 0:
            #move to end
            nums[j], nums[i] = nums[i],nums[j] #swap is constant space
            i -=1
        else:
            count += 1
    return count

例如:

>>> nums = [1,0,4,2,1,0,5]
>>> consolidateAndCount(nums)
5
>>> nums
[1, 5, 4, 2, 1, 0, 0]

答案 6 :(得分:0)

您实际上可以解决一个名为Dutch national flag problem的更通用的问题,它在Quicksort中使用。它根据给定的 mid 值将数组分为3个部分。首先,将所有数字设置为 mid ,然后将所有数字设置为 mid ,然后将所有数字设置为 mid

然后您可以选择 mid 值作为无穷大,并将 0 视为无穷大。

上述链接给出的伪代码:

procedure three-way-partition(A : array of values, mid : value):
    i ← 0
    j ← 0
    n ← size of A - 1

    while j ≤ n:
        if A[j] < mid:
            swap A[i] and A[j]
            i ← i + 1
            j ← j + 1
        else if A[j] > mid:
            swap A[j] and A[n]
            n ← n - 1
        else:
            j ← j + 1