问题
I want to create an array of type number with length n. All values inside the array should be 0 except the one which index matches a condition.
Thats how i currently do it:
const data: number[] = [];
for (let i = 0; i < n; i++) {
if (i === someIndex) {
data.push(someNumber);
} else {
data.push(0);
}
}
So lets say n = 4
, someIndex = 2
, someNumber = 4
would result in the array [0, 0, 4, 0]
.
Is there a way to do it in O(1) instead of O(n)?
回答1:
You need to assign n values, and so there is that amount of work to do. The work increases linearly with increasing n.
Having said that, you can hope to make your code a bit faster by making use of .fill
:
const data: number[] = Array(n).fill(0);
data[someIndex] = someNumber;
But don't be mistaken; this is still O(n): .fill
may be faster, but it still requires to fill the whole array with zeroes, which means a corresponding size of memory needs to be initialised, so that operation has linear time complexity.
If however you drop the requirement that zeroes need to be assigned, then you can only store the someNumber
:
const data: number[] = Array(n);
data[someIndex] = someNumber;
This way you actually do not allocate the memory for the whole array, so this code snippet runs in constant time. Any access to an index different from someIndex
will give you a value of undefined
. You may trap that condition and translate that to a zero on-the-fly:
let value = i in data ? data[i] : 0;
Obviously, if you are going to access all indices of the array like that, you'll have again a linear time complexity.
回答2:
Creating an array of size n in O(1) time is theoretically possible depending on implementation details - in principle, if an array is implemented as a hashtable then its length
property can be set without allocating or initialising space for all of its elements. The ECMAScript specification for the Array(n)
constructor doesn't mandate that Array(n)
should do anything which necessarily takes more than O(1) time, although it also doesn't mandate that the time complexity is O(1).
In practice, Array(n)
's time complexity depends on the browser, though verifying this is a bit tricky. The performance.now() function can be used to measure the time elapsed between the start and end of a computation, but the precision of this function is artificially reduced in many browsers to protect against CPU-timing attacks like Spectre. To get around this, we can call the constructor repetitions
times, and then divide the time elapsed by repetitions
to get a more precise measurement per constructor call.
My timing code is below:
function timeArray(n, repetitions=100000) {
var startTime = performance.now();
for(var i = 0; i < repetitions; ++i) {
var arr = Array(n);
arr[n-1] = 'foo';
}
var endTime = performance.now();
return (endTime - startTime) / repetitions;
}
for(var n = 10000; n <= 1000000; n += 10000) {
console.log(n, timeArray(n));
}
Here's my results from Google Chrome (version 74) and Firefox (version 72); on Chrome the performance is clearly O(n) and on Firefox it's clearly O(1) with a quite consistent time of about 0.01ms on my machine.
I measured using repetitions = 1000
on Chrome, and repetitions = 100000
on Firefox, to get accurate enough results within a reasonable time.
Another option proposed by @M.Dietz in the comments is to declare the array like var arr = [];
and then assign at some index (e.g. arr[n-1] = 'foo';
). This turns out to take O(1) time on both Chrome and Firefox, both consistently under one nanosecond:
That suggests the version using []
is better to use than the version using Array(n)
, but still the specification doesn't mandate that this should take O(1) time, so there may be other browsers where this version takes O(n) time. If anybody gets different results on another browser (or another version of one of these browsers) then please do add a comment.
来源:https://stackoverflow.com/questions/59753659/create-array-with-length-n-initialize-all-values-with-0-besides-one-which-index