我是一个空间复杂性'新手,并给了一个问题。
假设我有一个任意整数数组:
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)
空间复杂度?
我不擅长这个。
我的背景是环境工程而不是计算机科学,所以我通常会抽象地思考。
我以为我可以做一个排序,然后计算非零整数。
然后我想我只能在重新排列数组时执行元素元素复制。
然后我想到了类似于冒泡的东西,切换相邻的元素,直到我用零结束。
我以为我可以节省空间复杂度'通过移位阵列成员'地址,即数组点指向数组,具有对其成员的偏移量。
我要么以牺牲空间复杂性为代价来增强运行时,反之亦然。
解决方案是什么?
答案 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
(到目前为止遇到零)。
在每个步骤中,执行以下操作:
counter
。 counter
。当您到达列表末尾时,将值从array[end-counter]
覆盖到数组的末尾,并使用0。
非零整数的数量是数组的大小减去计数的零。
该算法具有O(n)时间复杂度,因为我们在整个数组中最多两次(所有0的数组;我们可以稍微修改更新方案,但最多只能修改一次)。它只使用一个额外的变量进行计数,它满足O(1)空间约束。
答案 3 :(得分:0)
i
)并保持遇到的零数(比如说zero_count
)直到现在。zero_count
。i + zero_count
索引中的值复制到当前索引i
。i + zero_count
大于数组长度时终止循环。0
。 伪代码:
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)
正如其他答案所示,我们的想法是有两个指针,p
和q
,一个指向数组的末尾(特别是在后面的第一个非零条目)和其他指向数组的开头。每次点击q
,0
和p
指向的交换元素,递增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