update
This commit is contained in:
parent
962caebbb7
commit
87bf53d076
1825 changed files with 94929 additions and 0 deletions
|
@ -0,0 +1,16 @@
|
|||
Последний элемент имеет индекс на `1` меньший, чем длина массива.
|
||||
|
||||
Например:
|
||||
|
||||
```js
|
||||
var fruits = ["Яблоко", "Груша", "Слива"];
|
||||
```
|
||||
|
||||
Длина массива этого массива `fruits.length` равна `3`. Здесь "Яблоко" имеет индекс `0`, "Груша" -- индекс `1`, "Слива" -- индекс `2`.
|
||||
|
||||
То есть, для массива длины `goods`:
|
||||
|
||||
```js
|
||||
var lastItem = goods[goods.length-1]; // получить последний элемент
|
||||
```
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
# Получить последний элемент массива
|
||||
|
||||
[importance 5]
|
||||
|
||||
Как получить последний элемент из произвольного массива?
|
||||
|
||||
У нас есть массив `goods`. Сколько в нем элементов -- не знаем, но можем прочитать из `goods.length`.
|
||||
|
||||
Напишите код для получения последнего элемента `goods`.
|
|
@ -0,0 +1,10 @@
|
|||
function getMaxSubSum(arr) {
|
||||
var maxSum = 0, partialSum = 0;
|
||||
for (var i=0; i<arr.length; i++) {
|
||||
partialSum += arr[i];
|
||||
maxSum = Math.max(maxSum, partialSum);
|
||||
if (partialSum < 0) partialSum = 0;
|
||||
}
|
||||
return maxSum;
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
describe("getMaxSubSum", function() {
|
||||
it("максимальная подсумма [1, 2, 3] равна 6", function() {
|
||||
assert.equal( getMaxSubSum([1, 2, 3]), 6);
|
||||
});
|
||||
|
||||
it("максимальная подсумма [-1, 2, 3, -9] равна 5", function() {
|
||||
assert.equal( getMaxSubSum([-1, 2, 3, -9]), 5);
|
||||
});
|
||||
|
||||
it("максимальная подсумма [-1, 2, 3, -9, 11] равна 11", function() {
|
||||
assert.equal( getMaxSubSum([-1, 2, 3, -9, 11]), 11);
|
||||
});
|
||||
|
||||
it("максимальная подсумма [-2, -1, 1, 2] равна 3", function() {
|
||||
assert.equal( getMaxSubSum([-2, -1, 1, 2]), 3);
|
||||
});
|
||||
|
||||
it("максимальная подсумма [100, -9, 2, -3, 5] равна 100", function() {
|
||||
assert.equal( getMaxSubSum([100, -9, 2, -3, 5]), 100);
|
||||
});
|
||||
|
||||
it("максимальная подсумма [] равна 0", function() {
|
||||
assert.equal( getMaxSubSum([]), 0);
|
||||
});
|
||||
|
||||
it("максимальная подсумма [-1] равна 0", function() {
|
||||
assert.equal( getMaxSubSum([-1]), 0);
|
||||
});
|
||||
|
||||
it("максимальная подсумма [-1, -2] равна 0", function() {
|
||||
assert.equal( getMaxSubSum([-1, -2]), 0);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,99 @@
|
|||
# Подсказка (медленное решение)
|
||||
Можно просто посчитать для каждого элемента массива все суммы, которые с него начинаются.
|
||||
|
||||
Например, для `[-1, 2, 3, -9, 11]`:
|
||||
|
||||
```js
|
||||
// Начиная с -1:
|
||||
-1
|
||||
-1 + 2
|
||||
-1 + 2 + 3
|
||||
-1 + 2 + 3 + (-9)
|
||||
-1 + 2 + 3 + (-9) + 11
|
||||
|
||||
// Начиная с 2:
|
||||
2
|
||||
2 + 3
|
||||
2 + 3 + (-9)
|
||||
2 + 3 + (-9) + 11
|
||||
|
||||
// Начиная с 3:
|
||||
3
|
||||
3 + (-9)
|
||||
3 + (-9) + 11
|
||||
|
||||
// Начиная с -9
|
||||
-9
|
||||
-9 + 11
|
||||
|
||||
// Начиная с -11
|
||||
-11
|
||||
```
|
||||
|
||||
Сделайте вложенный цикл, который на внешнем уровне бегает по элементам массива, а на внутреннем -- формирует все суммы элементов, которые начинаются с текущей позиции.
|
||||
|
||||
# Медленное решение
|
||||
|
||||
Решение через вложенный цикл:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
function getMaxSubSum(arr) {
|
||||
var maxSum = 0; // если совсем не брать элементов, то сумма 0
|
||||
|
||||
for(var i=0; i<arr.length; i++) {
|
||||
var sumFixedStart = 0;
|
||||
for(var j=i; j<arr.length; j++) {
|
||||
sumFixedStart += arr[j];
|
||||
maxSum = Math.max(maxSum, sumFixedStart);
|
||||
}
|
||||
}
|
||||
|
||||
return maxSum;
|
||||
}
|
||||
|
||||
alert( getMaxSubSum([-1, 2, 3, -9]) ); // 5
|
||||
alert( getMaxSubSum([-1, 2, 3, -9, 11]) ); // 11
|
||||
alert( getMaxSubSum([-2, -1, 1, 2]) ); // 3
|
||||
alert( getMaxSubSum([1, 2, 3]) ); // 6
|
||||
alert( getMaxSubSum([100, -9, 2, -3, 5]) ); // 100
|
||||
```
|
||||
|
||||
Такое решение имеет [оценку сложности](http://ru.wikipedia.org/wiki/%C2%ABO%C2%BB_%D0%B1%D0%BE%D0%BB%D1%8C%D1%88%D0%BE%D0%B5_%D0%B8_%C2%ABo%C2%BB_%D0%BC%D0%B0%D0%BB%D0%BE%D0%B5) O(n<sup>2</sup>), то есть при увеличении массива в 2 раза алгоритм требует в 4 раза больше времени. На больших массивах (1000, 10000 и более элементов) такие алгоритмы могут приводить к серьёзным "тормозам".
|
||||
|
||||
# Подсказка (быстрое решение)
|
||||
|
||||
Будем идти по массиву и накапливать в некоторой переменной `s` текущую частичную сумму. Если в какой-то момент s окажется отрицательной, то мы просто присвоим `s=0`. Утверждается, что максимум из всех значений переменной s, случившихся за время работы, и будет ответом на задачу.
|
||||
|
||||
**Докажем этот алгоритм.**
|
||||
|
||||
В самом деле, рассмотрим первый момент времени, когда сумма `s` стала отрицательной. Это означает, что, стартовав с нулевой частичной суммы, мы в итоге пришли к отрицательной частичной сумме -- значит, и весь этот префикс массива, равно как и любой его суффикс имеют отрицательную сумму.
|
||||
|
||||
Следовательно, от всего этого префикса массива в дальнейшем не может быть никакой пользы: он может дать только отрицательную прибавку к ответу.
|
||||
|
||||
# Быстрое решение
|
||||
|
||||
```js
|
||||
//+ run
|
||||
function getMaxSubSum(arr) {
|
||||
var maxSum = 0, partialSum = 0;
|
||||
for (var i=0; i<arr.length; i++) {
|
||||
partialSum += arr[i];
|
||||
maxSum = Math.max(maxSum, partialSum);
|
||||
if (partialSum < 0) partialSum = 0;
|
||||
}
|
||||
return maxSum;
|
||||
}
|
||||
|
||||
|
||||
alert( getMaxSubSum([-1, 2, 3, -9]) ); // 5
|
||||
alert( getMaxSubSum([-1, 2, 3, -9, 11]) ); // 11
|
||||
alert( getMaxSubSum([-2, -1, 1, 2]) ); // 3
|
||||
alert( getMaxSubSum([100, -9, 2, -3, 5]) ); // 100
|
||||
alert( getMaxSubSum([1, 2, 3]) ); // 6
|
||||
alert( getMaxSubSum([-1, -2, -3]) ); // 0
|
||||
```
|
||||
|
||||
Информацию об алгоритме вы также можете прочитать здесь: [](http://e-maxx.ru/algo/maximum_average_segment) и здесь: [Maximum subarray problem](http://en.wikipedia.org/wiki/Maximum_subarray_problem).
|
||||
|
||||
Этот алгоритм требует ровно одного прохода по массиву, его сложность имеет оценку `O(n)`.
|
28
1-js/4-data-structures/6-array/10-maximal-subarray/task.md
Normal file
28
1-js/4-data-structures/6-array/10-maximal-subarray/task.md
Normal file
|
@ -0,0 +1,28 @@
|
|||
# Подмассив наибольшей суммы
|
||||
|
||||
[importance 2]
|
||||
|
||||
На входе массив чисел, например: `arr = [1, -2, 3, 4, -9, 6]`.
|
||||
|
||||
Задача -- найти непрерывный подмассив `arr`, сумма элементов которого максимальна.
|
||||
|
||||
Ваша функция должна возвращать только эту сумму.
|
||||
|
||||
Например:
|
||||
|
||||
```js
|
||||
getMaxSubSum([-1, *!*2, 3*/!*, -9]) = 5 (сумма выделенных)
|
||||
getMaxSubSum([*!*2, -1, 2, 3*/!*, -9]) = 6
|
||||
getMaxSubSum([-1, 2, 3, -9, *!*11*/!*]) = 11
|
||||
getMaxSubSum([-2, -1, *!*1, 2*/!*]) = 3
|
||||
getMaxSubSum([*!*100*/!*, -9, 2, -3, 5]) = 100
|
||||
getMaxSubSum([*!*1, 2, 3*/!*]) = 6 (неотрицательные - берем всех)
|
||||
```
|
||||
|
||||
Если все элементы отрицательные, то не берём ни одного элемента и считаем сумму равной нулю:
|
||||
|
||||
```js
|
||||
getMaxSubSum([-1, -2, -3]) = 0
|
||||
```
|
||||
|
||||
Постарайтесь придумать решение, которое работает за O(n<sup>2</sup>), а лучше за O(n) операций.
|
|
@ -0,0 +1,6 @@
|
|||
Текущий последний элемент имеет индекс `goods.length-1`. Значит, индексом нового элемента будет `goods.length`:
|
||||
|
||||
```js
|
||||
goods[goods.length] = 'Компьютер'
|
||||
```
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
# Добавить новый элемент в массив
|
||||
|
||||
[importance 5]
|
||||
|
||||
Как добавить элемент в конец произвольного массива?
|
||||
|
||||
У нас есть массив `goods`. Напишите код для добавления в его конец значения "Компьютер".
|
11
1-js/4-data-structures/6-array/3-create-array/solution.md
Normal file
11
1-js/4-data-structures/6-array/3-create-array/solution.md
Normal file
|
@ -0,0 +1,11 @@
|
|||
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var styles = ["Джаз", "Блюз"];
|
||||
styles.push("Рок-н-Ролл");
|
||||
styles[styles.length-2] = "Классика";
|
||||
alert( styles.shift() );
|
||||
styles.unshift( "Рэп", "Регги ");
|
||||
```
|
||||
|
23
1-js/4-data-structures/6-array/3-create-array/task.md
Normal file
23
1-js/4-data-structures/6-array/3-create-array/task.md
Normal file
|
@ -0,0 +1,23 @@
|
|||
# Создание массива
|
||||
|
||||
[importance 5]
|
||||
|
||||
Задача из 5 шагов-строк:
|
||||
<ol>
|
||||
<li>Создайте массив `styles` с элементами "Джаз", "Блюз".</li>
|
||||
<li>Добавьте в конец значение "Рок-н-Ролл"</li>
|
||||
<li>Замените предпоследнее значение с конца на "Классика". Код замены предпоследнего значения должен работать для массивов любой длины.</li>
|
||||
<li>Удалите первое значение массива и выведите его `alert`.</li>
|
||||
<li>Добавьте в начало значения "Рэп" и "Регги".</li>
|
||||
</ol>
|
||||
|
||||
Массив в результате каждого шага:
|
||||
|
||||
```js
|
||||
Джаз, Блюз
|
||||
Джаз, Блюз, Рок-н-Ролл
|
||||
Джаз, Классика, Рок-н-Ролл
|
||||
Классика, Рок-н-Ролл
|
||||
Рэп, Регги, Классика, Рок-н-Ролл
|
||||
```
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
Для вывода нужен случайный номер от `0` до `arr.length-1` включительно.
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var arr = ["Яблоко", "Апельсин", "Груша", "Лимон"];
|
||||
|
||||
var rand = Math.floor( Math.random() * arr.length );
|
||||
|
||||
alert(arr[rand]);
|
||||
```
|
||||
|
16
1-js/4-data-structures/6-array/4-random-from-array/task.md
Normal file
16
1-js/4-data-structures/6-array/4-random-from-array/task.md
Normal file
|
@ -0,0 +1,16 @@
|
|||
# Получить случайное значение из массива
|
||||
|
||||
[importance 3]
|
||||
|
||||
Напишите код для вывода `alert` случайного значения из массива:
|
||||
|
||||
```js
|
||||
var arr = ["Яблоко", "Апельсин", "Груша", "Лимон"];
|
||||
```
|
||||
|
||||
P.S. Код для генерации случайного целого от `min` to `max` включительно:
|
||||
|
||||
```js
|
||||
var rand = min + Math.floor( Math.random() * (max+1-min) );
|
||||
```
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
В решение ниже обратите внимание: мы не приводим `value` к числу сразу после `prompt`, так как если сделать `value = +value`, то после этого отличить пустую строку от нуля уже никак нельзя. А нам здесь нужно при пустой строке прекращать ввод, а при нуле -- продолжать.
|
||||
|
||||
```js
|
||||
//+ run demo
|
||||
var numbers = [];
|
||||
|
||||
while(true) {
|
||||
|
||||
var value = prompt("Введите число", 0);
|
||||
|
||||
if (value === "" || value === null || isNaN(value)) break;
|
||||
|
||||
numbers.push(+value);
|
||||
}
|
||||
|
||||
var sum = 0;
|
||||
for(var i=0; i<numbers.length; i++) {
|
||||
sum += numbers[i];
|
||||
}
|
||||
|
||||
alert(sum);
|
||||
```
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
# Создайте калькулятор для введённых значений
|
||||
|
||||
[importance 4]
|
||||
|
||||
Напишите код, который:
|
||||
<ul>
|
||||
<li>Запрашивает по очереди значения при помощи `prompt` и сохраняет их в массиве.</li>
|
||||
<li>Заканчивает ввод, как только посетитель введёт пустую строку, не число или нажмёт "Отмена".</li>
|
||||
<li>При этом ноль `0` не должен заканчивать ввод, это разрешённое число.</li>
|
||||
<li>Выводит сумму всех значений массива</li>
|
||||
</ul>
|
||||
|
||||
[demo /]
|
24
1-js/4-data-structures/6-array/6-item-value/solution.md
Normal file
24
1-js/4-data-structures/6-array/6-item-value/solution.md
Normal file
|
@ -0,0 +1,24 @@
|
|||
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var arr = [1,2,3];
|
||||
|
||||
var arr2 = arr; // (*)
|
||||
arr2[0] = 5;
|
||||
|
||||
alert(arr[0]);
|
||||
alert(arr2[0]);
|
||||
```
|
||||
|
||||
Код выведет `5` в обоих случаях, так как массив является объектом. В строке `(*)` в переменную `arr2` копируется ссылка на него, а сам объект в памяти по-прежнему один, в нём отражаются изменения, внесенные через `arr2` или `arr`.
|
||||
|
||||
В частности, сравнение `arr2 == arr` даст `true`.
|
||||
|
||||
Если нужно именно скопировать массив, то это можно сделать, например, так:
|
||||
|
||||
```js
|
||||
var arr2 = [];
|
||||
for(var i=0; i<arr.length; i++) arr2[i] = arr[i];
|
||||
```
|
||||
|
18
1-js/4-data-structures/6-array/6-item-value/task.md
Normal file
18
1-js/4-data-structures/6-array/6-item-value/task.md
Normal file
|
@ -0,0 +1,18 @@
|
|||
# Чему равен элемент массива?
|
||||
|
||||
[importance 3]
|
||||
|
||||
Что выведет этот код?
|
||||
|
||||
```js
|
||||
var arr = [1,2,3];
|
||||
|
||||
var arr2 = arr;
|
||||
arr2[0] = 5;
|
||||
|
||||
*!*
|
||||
alert(arr[0]);
|
||||
alert(arr2[0]);
|
||||
*/!*
|
||||
```
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
function find(array, value) {
|
||||
if (array.indexOf) { // если метод существует
|
||||
return array.indexOf(value);
|
||||
}
|
||||
|
||||
for(var i=0; i<array.length; i++) {
|
||||
if (array[i] === value) return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
26
1-js/4-data-structures/6-array/7-array-find/_js.view/test.js
Normal file
26
1-js/4-data-structures/6-array/7-array-find/_js.view/test.js
Normal file
|
@ -0,0 +1,26 @@
|
|||
describe("find", function() {
|
||||
|
||||
describe("возвращает позицию, на которой найден элемент", function() {
|
||||
it("в массиве [1,2,3] находит 1 на позиции 0", function() {
|
||||
assert.equal( find([1, 2, 3], 1), 0);
|
||||
});
|
||||
it("в массиве [1,2,3] находит 2 на позиции 1", function() {
|
||||
assert.equal( find([1, 2, 3], 2), 1);
|
||||
});
|
||||
it("в массиве [1,2,3] находит 3 на позиции 2", function() {
|
||||
assert.equal( find([1, 2, 3], 3), 2);
|
||||
});
|
||||
});
|
||||
|
||||
it("если элемент не найден, возвращает -1", function() {
|
||||
assert.equal( find([1,2,3], 0), -1);
|
||||
});
|
||||
|
||||
it("отличает false или null от 0", function() {
|
||||
assert.equal( find([false, true, null], 0), -1);
|
||||
});
|
||||
|
||||
it("отличает 1 от true", function() {
|
||||
assert.equal( find([1, 2, 3], true), -1);
|
||||
});
|
||||
});
|
61
1-js/4-data-structures/6-array/7-array-find/solution.md
Normal file
61
1-js/4-data-structures/6-array/7-array-find/solution.md
Normal file
|
@ -0,0 +1,61 @@
|
|||
Возможное решение:
|
||||
|
||||
```js
|
||||
function find(array, value) {
|
||||
|
||||
for(var i=0; i<array.length; i++) {
|
||||
if (array[i] == value) return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
```
|
||||
|
||||
Однако, в нем ошибка, т.к. сравнение `==` не различает `0` и `false`.
|
||||
|
||||
Поэтому лучше использовать `===`. Кроме того, в современном стандарте JavaScript существует встроенная фунция <a href="https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/indexOf">Array#indexOf</a>, которая работает именно таким образом. Имеет смысл ей воспользоваться, если браузер ее поддерживает.
|
||||
|
||||
```js
|
||||
//+ run
|
||||
function find(array, value) {
|
||||
if (array.indexOf) { // если метод существует
|
||||
return array.indexOf(value);
|
||||
}
|
||||
|
||||
for(var i=0; i<array.length; i++) {
|
||||
if (array[i] === value) return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
var arr = ["a", -1, 2, "b"];
|
||||
|
||||
var index = find(arr, 2);
|
||||
|
||||
alert(index);
|
||||
```
|
||||
|
||||
... Но еще лучшим вариантом было бы определить `find` по-разному в зависимости от поддержки браузером метода `indexOf`:
|
||||
|
||||
```js
|
||||
// создаем пустой массив и проверяем поддерживается ли indexOf
|
||||
if ( [].indexOf ) {
|
||||
|
||||
var find = function(array, value) {
|
||||
return array.indexOf(value);
|
||||
}
|
||||
|
||||
} else {
|
||||
var find = function(array, value) {
|
||||
for(var i=0; i<array.length; i++) {
|
||||
if (array[i] === value) return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
Этот способ - лучше всего, т.к. не требует при каждом запуске `find` проверять поддержку `indexOf`.
|
18
1-js/4-data-structures/6-array/7-array-find/task.md
Normal file
18
1-js/4-data-structures/6-array/7-array-find/task.md
Normal file
|
@ -0,0 +1,18 @@
|
|||
# Поиск в массиве
|
||||
|
||||
[importance 3]
|
||||
|
||||
Создайте функцию `find(arr, value)`, которая ищет в массиве `arr` значение `value` и возвращает его номер, если найдено, или `-1`, если не найдено.
|
||||
|
||||
Например:
|
||||
|
||||
```js
|
||||
arr = [ "test", 2, 1.5, false ];
|
||||
|
||||
find(arr, "test"); // 0
|
||||
find(arr, 2); // 1
|
||||
find(arr, 1.5); // 2
|
||||
|
||||
find(arr, 0); // -1
|
||||
```
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
function filterRange(arr, a, b) {
|
||||
var result = [];
|
||||
|
||||
for(var i=0; i<arr.length; i++) {
|
||||
if (arr[i] >= a && arr[i] <= b) {
|
||||
result.push(arr[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
describe("filterRange", function() {
|
||||
it("не меняет исходный массив", function() {
|
||||
var arr = [5, 4, 3, 8, 0];
|
||||
|
||||
filterRange(arr, 0, 10);
|
||||
assert.deepEqual(arr, [5,4,3,8,0]);
|
||||
});
|
||||
|
||||
it("оставляет только значения указанного интервала", function() {
|
||||
var arr = [5, 4, 3, 8, 0];
|
||||
|
||||
var result = filterRange(arr, 3, 5);
|
||||
assert.deepEqual(result, [5,4,3]);
|
||||
});
|
||||
});
|
29
1-js/4-data-structures/6-array/8-filter-range/solution.md
Normal file
29
1-js/4-data-structures/6-array/8-filter-range/solution.md
Normal file
|
@ -0,0 +1,29 @@
|
|||
# Алгоритм решения
|
||||
<ol>
|
||||
<li>Создайте временный пустой массив `var results = []`. </li>
|
||||
<li>Пройдите по элементам `arr` в цикле и заполните его.</li>
|
||||
<li>Возвратите `results`.</li>
|
||||
</ol>
|
||||
|
||||
# Решение
|
||||
|
||||
```js
|
||||
//+ run
|
||||
function filterRange(arr, a, b) {
|
||||
var result = [];
|
||||
|
||||
for(var i=0; i<arr.length; i++) {
|
||||
if (arr[i] >= a && arr[i] <= b) {
|
||||
result.push(arr[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
var arr = [5, 4, 3, 8, 0];
|
||||
|
||||
var filtered = filterRange(arr, 3, 5);
|
||||
alert(filtered);
|
||||
```
|
||||
|
17
1-js/4-data-structures/6-array/8-filter-range/task.md
Normal file
17
1-js/4-data-structures/6-array/8-filter-range/task.md
Normal file
|
@ -0,0 +1,17 @@
|
|||
# Фильтр диапазона
|
||||
|
||||
[importance 3]
|
||||
|
||||
Создайте фунцию `filterRange(arr, a, b)`, которая принимает массив чисел `arr` и возвращает новый массив, который содержит только числа из `arr` из диапазона от `a` до `b`. То есть, проверка имеет вид `a ≤ arr[i] ≤ b`.
|
||||
Функция не должна менять `arr`.
|
||||
|
||||
Пример работы:
|
||||
|
||||
```js
|
||||
var arr = [5, 4, 3, 8, 0];
|
||||
|
||||
var filtered = filterRange(arr, 3, 5);
|
||||
// теперь filtered = [5, 4, 3]
|
||||
// arr не изменился
|
||||
```
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
Их сумма равна `1060`.
|
||||
|
||||
```js
|
||||
//+ run
|
||||
// шаг 1
|
||||
var arr = [];
|
||||
|
||||
for (var i=2; i<100; i++) {
|
||||
arr[i] = true
|
||||
}
|
||||
|
||||
// шаг 2
|
||||
var p = 2;
|
||||
|
||||
do {
|
||||
// шаг 3
|
||||
for (i=2*p; i<100; i+=p) {
|
||||
arr[i] = false;
|
||||
}
|
||||
|
||||
// шаг 4
|
||||
for (i=p+1; i<100; i++) {
|
||||
if (arr[i]) break;
|
||||
}
|
||||
|
||||
p = i;
|
||||
} while (p*p < 100); // шаг 5
|
||||
|
||||
// шаг 6 (готово)
|
||||
// посчитать сумму
|
||||
var sum = 0;
|
||||
for (i=0; i<arr.length; i++) {
|
||||
if (arr[i]) {
|
||||
sum += i;
|
||||
}
|
||||
}
|
||||
|
||||
alert(sum);
|
||||
```
|
||||
|
25
1-js/4-data-structures/6-array/9-eratosthenes-sieve/task.md
Normal file
25
1-js/4-data-structures/6-array/9-eratosthenes-sieve/task.md
Normal file
|
@ -0,0 +1,25 @@
|
|||
# Решето Эратосфена
|
||||
|
||||
[importance 3]
|
||||
|
||||
Целое число, большее `1`, называется *простым*, если оно не делится нацело ни на какое другое, кроме себя и `1`.
|
||||
|
||||
Древний алгоритм "Решето Эратосфена" для поиска всех простых чисел до `n` выглядит так:
|
||||
|
||||
<ol>
|
||||
<li>Создать список последовательных чисел от `2` до `n`: `2, 3, 4, ..., n`.</li>
|
||||
<li>Пусть `p=2`, это первое простое число.</li>
|
||||
<li>Зачеркнуть все последующие числа в списке с разницей в `p`, т.е. `2*p, 3*p, 4*p` и т.д. В случае `p=2` это будут `4,6,8...`.</li>
|
||||
<li>Поменять значение `p` на первое незачеркнутое число после `p`.</li>
|
||||
<li>Повторить шаги 3-4 пока <code>p<sup>2</sup> < n</code>.</li>
|
||||
<li>Все оставшиеся незачеркнутыми числа -- простые.</li>
|
||||
</ol>
|
||||
|
||||
Посмотрите также <a href="/files/tutorial/intro/array/sieve.gif">анимацию алгоритма</a>.
|
||||
|
||||
Реализуйте "Решето Эратосфена" в JavaScript, используя массив.
|
||||
|
||||
Найдите все простые числа до `100` и выведите их сумму.
|
||||
|
||||
|
||||
|
440
1-js/4-data-structures/6-array/article.md
Normal file
440
1-js/4-data-structures/6-array/article.md
Normal file
|
@ -0,0 +1,440 @@
|
|||
# Массивы c числовыми индексами
|
||||
|
||||
*Массив с числовыми индексами* -- это коллекция данных, которая хранит сколько угодно значений, причем у каждого значения -- свой уникальный номер.
|
||||
|
||||
Если переменная -- это *коробка для данных*, то массив -- это *шкаф с нумерованными ячейками*, в каждой из которых могут быть свои данные.
|
||||
[cut]
|
||||
Например, при создании электронного магазина нужно хранить список товаров -- для таких задач и придуман массив.
|
||||
|
||||
## Объявление
|
||||
|
||||
Синтаксис для создания нового массива -- квадратные скобки со списком элементов внутри.
|
||||
|
||||
Пустой массив:
|
||||
|
||||
```js
|
||||
var arr = [];
|
||||
```
|
||||
|
||||
Массив `fruits` с тремя элементами:
|
||||
|
||||
```js
|
||||
var fruits = ["Яблоко", "Апельсин", "Слива"];
|
||||
```
|
||||
|
||||
**Элементы нумеруются, начиная с нуля.** Чтобы получить нужный элемент из массива -- указывается его номер в квадратных скобках:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var fruits = ["Яблоко", "Апельсин", "Слива"];
|
||||
|
||||
alert(fruits[0]); // Яблоко
|
||||
alert(fruits[1]); // Апельсин
|
||||
alert(fruits[2]); // Слива
|
||||
```
|
||||
|
||||
Элемент можно всегда заменить:
|
||||
|
||||
```js
|
||||
fruits[2] = 'Груша'; // теперь ["Яблоко", "Апельсин", "Груша"]
|
||||
```
|
||||
|
||||
... Или добавить:
|
||||
|
||||
```js
|
||||
fruits[3] = 'Лимон'; // теперь ["Яблоко", "Апельсин", "Груша", "Лимон"]
|
||||
```
|
||||
|
||||
Общее число элементов, хранимых в массиве, содержится в его свойстве `length`:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var fruits = ["Яблоко", "Апельсин", "Груша"];
|
||||
|
||||
alert(fruits.length); // 3
|
||||
```
|
||||
|
||||
**Через `alert` можно вывести и массив целиком.** При этом его элементы будут перечислены через запятую:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var fruits = ["Яблоко", "Апельсин", "Груша"];
|
||||
|
||||
alert(fruits); // Яблоко,Апельсин,Груша
|
||||
```
|
||||
|
||||
**В массиве может храниться любое число элементов любого типа.** В том числе, строки, числа, объекты и т.п.:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
// микс значений
|
||||
var arr = [ 1, 'Имя', { name: 'Петя' }, true ];
|
||||
|
||||
// получить объект из массива и тут же -- его свойство
|
||||
alert( arr[2].name ); // Петя
|
||||
```
|
||||
|
||||
## Методы pop/push, shift/unshift
|
||||
|
||||
Одно из применений массива -- это [очередь](http://ru.wikipedia.org/wiki/%D0%9E%D1%87%D0%B5%D1%80%D0%B5%D0%B4%D1%8C_%28%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%29). В классическом программировании так называют упорядоченную коллекцию элементов, такую что элементы добавляются в конец, а обрабатываются -- с начала.
|
||||
|
||||
В реальной жизни эта структура данных встречается очень часто. Например, очередь сообщений, которые надо отослать.
|
||||
|
||||
Очень близка к очереди еще одна структура данных: [стек](http://ru.wikipedia.org/wiki/%D0%A1%D1%82%D0%B5%D0%BA). Это такая коллекция элементов, в которой новые элементы добавляются в конец и берутся с конца.
|
||||
|
||||
Например, стеком является колода карт, в которую новые карты кладутся сверху, и берутся -- тоже сверху.
|
||||
|
||||
Для того, чтобы реализовывать эти структуры данных, и просто для более удобной работы с началом и концом массива существуют специальные методы.
|
||||
|
||||
### Конец массива
|
||||
|
||||
<dl>
|
||||
<dt>`pop`</dt>
|
||||
<dd>Удаляет *последний* элемент из массива и возвращает его:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var fruits = ["Яблоко", "Апельсин", "Груша"];
|
||||
|
||||
alert( fruits.pop() ); // удалили "Груша"
|
||||
|
||||
alert(fruits); // Яблоко, Апельсин
|
||||
```
|
||||
|
||||
</dd>
|
||||
<dt>`push`</dt>
|
||||
<dd>Добавляет элемент *в конец* массива:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var fruits = ["Яблоко", "Апельсин"];
|
||||
|
||||
fruits.push("Груша");
|
||||
|
||||
alert(fruits); // Яблоко, Апельсин, Груша
|
||||
```
|
||||
|
||||
Является полным аналогом `fruits[fruits.length] = ...`.
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
### Начало массива
|
||||
|
||||
<dl>
|
||||
<dt>`shift`</dt>
|
||||
<dd>Удаляет из массива *первый* элемент и возвращает его:
|
||||
|
||||
```js
|
||||
var fruits = ["Яблоко", "Апельсин", "Груша"];
|
||||
|
||||
alert( fruits.shift() ); // удалили Яблоко
|
||||
|
||||
alert(fruits); // Апельсин, Груша
|
||||
```
|
||||
|
||||
</dd>
|
||||
<dt>`unshift`</dt>
|
||||
<dd>Добавляет элемент *в начало* массива:
|
||||
|
||||
```js
|
||||
var fruits = ["Апельсин", "Груша"];
|
||||
|
||||
fruits.unshift('Яблоко');
|
||||
|
||||
alert(fruits); // Яблоко, Апельсин, Груша
|
||||
```
|
||||
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
<img src="shiftpush.png">
|
||||
|
||||
Методы `push` и `unshift` могут добавлять сразу по несколько элементов:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var fruits = ["Яблоко"];
|
||||
|
||||
fruits.push("Апельсин", "Персик");
|
||||
fruits.unshift("Ананас", "Лимон");
|
||||
|
||||
// результат: ["Ананас", "Лимон", "Яблоко", "Апельсин", "Персик"]
|
||||
alert(fruits);
|
||||
```
|
||||
|
||||
## Внутреннее устройство массива
|
||||
|
||||
Массив -- это объект, где в качестве ключей выбраны цифры, с дополнительными методами и свойством `length`.
|
||||
|
||||
Так как это объект, то в функцию он передаётся по ссылке:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
function eat(arr) {
|
||||
arr.pop();
|
||||
}
|
||||
|
||||
var arr = ["нам", "не", "страшен", "серый", "волк"]
|
||||
|
||||
alert(arr.length); // 5
|
||||
eat(arr);
|
||||
eat(arr);
|
||||
alert(arr.length); // 3, в функцию массив не скопирован, а передана ссылка
|
||||
```
|
||||
|
||||
**Ещё одно следствие -- можно присваивать в массив любые свойства.**
|
||||
|
||||
Например:
|
||||
|
||||
```js
|
||||
var fruits = []; // создать массив
|
||||
|
||||
fruits[99999] = 5; // присвоить свойство с любым номером
|
||||
|
||||
fruits.age = 25; // назначить свойство со строковым именем
|
||||
```
|
||||
|
||||
.. Но массивы для того и придуманы в JavaScript, чтобы удобно работать именно *с упорядоченными, нумерованными данными*. Для этого в них существуют специальные методы и свойство `length`.
|
||||
|
||||
Как правило, нет причин использовать массив как обычный объект, хотя технически это и возможно.
|
||||
|
||||
[warn header="Вывод массива с \"дырами\""]
|
||||
Если в массиве есть пропущенные индексы, то при выводе в большинстве браузеров появляются "лишние" запятые, например:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var a = [];
|
||||
a[0] = 0;
|
||||
a[5] = 5;
|
||||
|
||||
alert(a); // 0,,,,,5
|
||||
```
|
||||
|
||||
Эти запятые появляются потому, что алгоритм вывода массива идёт от `0` до `arr.length` и выводит всё через запятую. Отсутствие значений даёт несколько запятых подряд.
|
||||
[/warn]
|
||||
|
||||
### Влияние на быстродействие
|
||||
|
||||
Методы `push/pop` выполняются быстро, а `shift/unshift` -- медленно.
|
||||
|
||||
Чтобы понять, почему работать с концом массива -- быстрее, чем с его началом, разберём происходящее подробнее.
|
||||
|
||||
Операция `shift` выполняет два действия:
|
||||
<ol>
|
||||
<li>Удалить элемент в начале.</li>
|
||||
<li>Обновить внутреннее свойство `length`.</li>
|
||||
</ol>
|
||||
|
||||
При этом, так как все элементы находятся в своих ячейках, просто очистить ячейку с номером `0` недостаточно. Нужно еще и переместить все ячейки на `1` вниз (красным на рисунке подсвечены изменения):
|
||||
|
||||
```js
|
||||
*!*fruits.shift();*/!* // убрать 1 элемент с начала
|
||||
```
|
||||
|
||||
<img src="shift.png">
|
||||
|
||||
Чем больше элементов в массиве, тем дольше их перемещать.
|
||||
|
||||
Аналогично работает `unshift`: чтобы добавить элемент в начало массива, нужно сначала перенести все существующие.
|
||||
|
||||
У методов `push/pop` таких проблем нет. Для того, чтобы удалить элемент, метод `pop` очищает ячейку и укорачивает `length`.
|
||||
|
||||
```js
|
||||
*!*fruits.pop();*/!* // убрать 1 элемент с конца
|
||||
```
|
||||
|
||||
<img src="pop.png">
|
||||
|
||||
Аналогично работает `push`.
|
||||
|
||||
|
||||
## Перебор элементов
|
||||
|
||||
Для перебора элементов обычно используется цикл:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var arr = ["Яблоко", "Апельсин", "Груша"];
|
||||
|
||||
*!*
|
||||
for (var i=0; i<arr.length; i++) {
|
||||
alert( arr[i] );
|
||||
}
|
||||
*/!*
|
||||
```
|
||||
|
||||
[warn header="Не используйте `for..in` для массивов"]
|
||||
Так как массив является объектом, то возможен и вариант `for..in`:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var arr = ["Яблоко", "Апельсин", "Груша"];
|
||||
|
||||
*!*
|
||||
for (var key in arr) {
|
||||
*/!*
|
||||
alert( arr[key] ); // Яблоко, Апельсин, Груша
|
||||
}
|
||||
```
|
||||
|
||||
Недостатки этого способа:
|
||||
|
||||
<ul>
|
||||
<li>Цикл `for..in` выведет *все свойства* объекта, а не только цифровые.
|
||||
|
||||
В браузере, при работе с объектами страницы, встречаются коллекции элементов, которые по виду как массивы, но имеют дополнительные нецифровые свойства, которые будут видны в цикле `for..in`.
|
||||
|
||||
Бывают и библиотеки, которые предоставляют такие коллекции. Например jQuery. С виду -- массив, но есть дополнительные свойства. Для перебора только цифровых свойств нужен цикл `for(var i=0; i<arr.length...)`
|
||||
</li>
|
||||
<li>Цикл `for (var i=0; i<arr.length; i++)` в современных браузерах выполняется в 10-100 раз быстрее. Казалось бы, по виду он сложнее, но браузер особым образом оптимизирует такие циклы.</li>
|
||||
</ul>
|
||||
|
||||
**Если кратко: цикл `for(var i=0; i<arr.length...)` надёжнее и быстрее.**
|
||||
[/warn]
|
||||
|
||||
|
||||
## Особенности работы length
|
||||
|
||||
Встроенные методы для работы с массивом автоматически обновляют его длину `length`.
|
||||
|
||||
**Длина `length` -- не количество элементов массива, а `последний индекс + 1`**. Так уж оно устроено.
|
||||
|
||||
Это легко увидеть на следующем примере:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var arr = [];
|
||||
arr[1000] = true;
|
||||
|
||||
alert(arr.length); // *!*1001*/!*
|
||||
```
|
||||
|
||||
Вообще, если у вас элементы массива нумеруются случайно или с большими пропусками, то стоит подумать о том, чтобы использовать обычный объект.
|
||||
|
||||
Массивы предназначены именно для работы с непрерывной упорядоченной коллекцией элементов.
|
||||
|
||||
### Используем length для укорачивания массива
|
||||
|
||||
Обычно нам не нужно самостоятельно менять `length`... Но есть один фокус, который можно провернуть.
|
||||
|
||||
**При уменьшении `length` массив укорачивается.** Причем этот процесс необратимый, т.е. даже если потом вернуть `length` обратно -- значения не восстановятся:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var arr = [1, 2, 3, 4, 5];
|
||||
|
||||
arr.length = 2; // укоротить до 2 элементов
|
||||
alert(arr); // [1, 2]
|
||||
|
||||
arr.length = 5; // вернуть length обратно, как было
|
||||
alert(arr[3]); // undefined: значения не вернулись
|
||||
```
|
||||
|
||||
Самый простой способ очистить массив -- это `arr.length=0`.
|
||||
|
||||
## Создание вызовом new Array [#new-array]
|
||||
|
||||
### new Array()
|
||||
Существует еще один синтаксис для создания массива:
|
||||
|
||||
```js
|
||||
var arr = *!*new Array*/!*("Яблоко", "Груша", "и т.п.");
|
||||
```
|
||||
|
||||
Он редко используется, т.к. квадратные скобки `[]` короче.
|
||||
|
||||
Кроме того, у него есть одна особенность. Обычно `new Array(элементы, ...)` создаёт массив из данных элементов, но если у него один аргумент, и это число --то он создает массив *без элементов, но с заданной длиной*.
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var arr = new Array(2,3); // создает массив [2, 3]
|
||||
|
||||
arr = new Array(2); // создаст массив [2] ?
|
||||
|
||||
alert(arr[0]); // нет! у нас массив без элементов, длины 2
|
||||
```
|
||||
|
||||
Что же такое этот "массив без элементов, но с длиной"? Как такое возможно?
|
||||
|
||||
Оказывается, очень даже возможно и соответствует объекту `{length: 2}`. Получившийся массив ведёт себя так, как будто его элементы равны `undefined`.
|
||||
|
||||
Это может быть неожиданным сюрпризом, поэтому обычно используют квадратные скобки.
|
||||
|
||||
### Многомерные массивы
|
||||
|
||||
Массивы в JavaScript могут содержать в качестве элементов другие массивы. Это можно использовать для создания многомерных массивов, например матриц:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var matrix = [
|
||||
[1, 2, 3],
|
||||
[4, 5, 6],
|
||||
[7, 8, 9]
|
||||
];
|
||||
|
||||
alert(matrix[1][1]); // центральный элемент
|
||||
```
|
||||
|
||||
## Внутреннее представление массивов
|
||||
|
||||
[warn header="Hardcore coders only"]
|
||||
Эта секция относится ко внутреннему устройству структуры данных и требует специальных знаний. Она не обязательна к прочтению.
|
||||
[/warn]
|
||||
|
||||
Числовые массивы, согласно спецификации, являются объектами, в которые добавили ряд свойств, методов и автоматическую длину `length`. Но внутри они, как правило, устроены по-другому.
|
||||
|
||||
**Современные интерпретаторы стараются оптимизировать их и хранить в памяти не в виде хэш-таблицы, а в виде непрерывной области памяти, по которой легко пробежаться от начала до конца.**
|
||||
|
||||
Операции с массивами также оптимизируются, особенно если массив хранит только один тип данных, например только числа. Порождаемый набор инструкций для процессора получается очень эффективным.
|
||||
|
||||
Чтобы у интерпретатора получались эти оптимизации, программист не должен мешать.
|
||||
|
||||
В частности:
|
||||
<ul>
|
||||
<li>Не ставить массиву произвольные свойства, такие как `arr.test = 5`. То есть, работать именно как с массивом, а не как с объектом.</li>
|
||||
<li>Заполнять массив непрерывно. Как только браузер встречает необычное поведение массива, например устанавливается значение `arr[0]`, а потом сразу `arr[1000]`, то он начинает работать с ним, как с обычным объектом. Как правило, это влечёт преобразование его в хэш-таблицу.</li>
|
||||
</ul>
|
||||
|
||||
|
||||
Если следовать этим принципам, то массивы будут занимать меньше памяти и быстрее работать.
|
||||
|
||||
## Итого
|
||||
|
||||
Массивы существуют для работы с упорядоченным набором элементов.
|
||||
|
||||
**Объявление:**
|
||||
|
||||
```js
|
||||
// предпочтительное
|
||||
var arr = [ элемент1, элемент2... ];
|
||||
|
||||
// new Array
|
||||
var arr = new Array( элемент1, элемент2...);
|
||||
```
|
||||
|
||||
При этом `new Array(число)` создаёт массив заданной длины, *без элементов*. Чтобы избежать ошибок, предпочтителен первый синтаксис.
|
||||
|
||||
**Свойство `length`** -- длина массива. Если точнее, то последний индекс массива плюс `1`. Если её уменьшить вручную, то массив укоротится. Если `length` больше реального количества элементов, то отсутствующие элементы равны `undefined`.
|
||||
|
||||
|
||||
Массив можно использовать как очередь или стек.
|
||||
|
||||
**Операции с концом массива:**
|
||||
<ul>
|
||||
<li>`arr.push(элемент1, элемент2...)` добавляет элементы в конец.</li>
|
||||
<li>`var elem = arr.pop()` удаляет и возвращает последний элемент.</li>
|
||||
</ul>
|
||||
|
||||
**Операции с началом массива:**
|
||||
<ul>
|
||||
<li>`arr.unshift(элемент1, элемент2...)` добавляет элементы в начало.</li>
|
||||
<li>`var elem = arr.shift()` удаляет и возвращает первый элемент.</li>
|
||||
</ul>
|
||||
|
||||
Эти операции перенумеровывают все элементы, поэтому работают медленно.
|
||||
|
||||
В следующей главе мы рассмотрим другие методы для работы с массивами.
|
||||
|
||||
|
||||
|
||||
|
BIN
1-js/4-data-structures/6-array/pop.png
Executable file
BIN
1-js/4-data-structures/6-array/pop.png
Executable file
Binary file not shown.
After Width: | Height: | Size: 9.2 KiB |
BIN
1-js/4-data-structures/6-array/shift.png
Executable file
BIN
1-js/4-data-structures/6-array/shift.png
Executable file
Binary file not shown.
After Width: | Height: | Size: 13 KiB |
BIN
1-js/4-data-structures/6-array/shiftpush.png
Executable file
BIN
1-js/4-data-structures/6-array/shiftpush.png
Executable file
Binary file not shown.
After Width: | Height: | Size: 7.7 KiB |
Loading…
Add table
Add a link
Reference in a new issue