Задачи на слайсы

1) Что выведет данная программа и почему?

package main
import ("fmt")

func main() {
	arr1 :=[...]int{1, 2,3, 4, 5}
	arr2 := arr1[:4]
	arr3 := append(arr2[:2], 9, 8, 7)

	fmt.Println(arr3)
	fmt.Println(arr2)
	fmt.Println(arr1)
}

Программа выведет следующие значения:

[1 2 9 8 7]
[1 2 9 8]
[1 2 9 8 7]

Давайте разберемся, почему так происходит:

  1. arr1 := [...]int{1, 2, 3, 4, 5} создает массив arr1 с пятью элементами.

  2. arr2 := arr1[:4] создает срез arr2, который ссылается на первые четыре элемента массива arr1. То есть arr2 это [1, 2, 3, 4].

  3. arr3 := append(arr2[:2], 9, 8, 7) берет срез первых двух элементов из arr2 (то есть [1, 2]), и добавляет к нему элементы 9, 8, 7. Важно отметить, что append может изменять исходный массив, если в нем достаточно места для новых элементов.

  4. В данном случае, начало слайсаarr2 ссылается на начало массива arr1, поэтому изменения в arr2 отражаются на arr1.

  5. После выполнения append, arr3 становится [1, 2, 9, 8, 7], arr2 изменяется на [1, 2, 9, 8], (7ка не влазит, так как слайс arr2 имеет фиксированную длину в 4 элемента), а arr1 теперь [1, 2, 9, 8, 7] (так как в нем достаточно места и для 7ки).

  6. Вывод fmt.Println(arr3), fmt.Println(arr2), fmt.Println(arr1) показывает измененные значения массивов и срезов после операций с append.

Это пример того, как операции с срезами и функцией append могут влиять на исходные массивы в Go, особенно когда срезы создаются на основе этих массивов.

2) Что выведет данная программа и почему?

3) Что будет выведено без аппенд и потом с ним?

Давайте разберем, что произойдет в коде с и без использования append.

Код без использования append

Объяснение:

  1. В функции main создается срез sl с элементами [1, 2, 3, 4].

  2. Срез sl передается в функцию mod.

  3. В функции mod происходит итерация по срезу a (который является ссылкой на sl), и каждый элемент заменяется на 5.

  4. После выполнения функции mod, срез sl в main будет изменен на [5, 5, 5, 5], так как срезы в Go передаются по ссылке.

Вывод:

Код с использованием append

Объяснение:

  1. В функции main создается срез sl с элементами [1, 2, 3, 4].

  2. Срез sl передается в функцию mod.

  3. В функции mod выполняется append(a, 125), что создает новый срез, который является копией a с добавленным элементом 125. Этот новый срез присваивается переменной a внутри функции mod.

  4. Затем происходит итерация по новому срезу a, и каждый элемент заменяется на 5. Однако это изменение не влияет на исходный срез sl, так как a теперь указывает на новый срез.

  5. После выполнения функции mod, срез sl в main останется неизменным, так как изменения были сделаны в копии среза.

Вывод:

Заключение

  • Без использования append: Срез sl будет изменен на [5, 5, 5, 5], так как изменения происходят непосредственно в переданном срезе.

  • С использованием append: Срез sl останется неизменным [1, 2, 3, 4], так как append создает новый срез, и изменения происходят в этом новом срезе, а не в исходном.

4) Написать функцию, которая на вход принимает 2 отсортированных слайса с типом Int в ответ выдает 1 отсортированный слайс, содержащий данные из входящих слайсов. Оценить сложность алгоритма.

Для объединения двух отсортированных слайсов в один отсортированный слайс можно использовать алгоритм слияния, который является частью алгоритма сортировки слиянием (merge sort). Этот алгоритм работает за линейное время, то есть его временная сложность составляет O(n + m), где n и m — размеры входных слайсов.

Реализация функции на Go

Объяснение алгоритма

  1. Инициализация:

    • Создаем результирующий слайс result с начальной емкостью, равной сумме длин входных слайсов a и b.

    • Инициализируем индексы i и j для итерации по слайсам a и b соответственно.

  2. Основной цикл слияния:

    • Пока оба индекса i и j находятся в пределах длин своих слайсов, сравниваем текущие элементы a[i] и b[j].

    • Добавляем меньший элемент в результирующий слайс и увеличиваем соответствующий индекс.

  3. Добавление оставшихся элементов:

    • После завершения основного цикла, один из слайсов может содержать оставшиеся элементы. Добавляем их в результирующий слайс.

Оценка сложности

  • Временная сложность: O(n + m), где n и m — размеры входных слайсов. Это обусловлено тем, что каждый элемент из обоих слайсов обрабатывается ровно один раз.

  • Пространственная сложность: O(n + m), так как создается новый слайс, содержащий все элементы из входных слайсов.

Этот алгоритм является оптимальным для задачи слияния двух отсортированных слайсов и работает эффективно даже для больших входных данных.

5) Что выведет данная программа и почему?

Давайте разберем, что происходит в этой программе и что она выведет.

Разбор кода

  1. Создание и заполнение слайса x:

    После этих операций x будет содержать [0, 1, 2].

  2. Создание слайсов y и z:

    Здесь начинается интересное. Важно понимать, что append может изменять базовый массив слайса, если его емкость позволяет. Если емкость недостаточна, append создаст новый массив и скопирует в него данные.

    • y := append(x, 3) добавляет 3 к x, так что y становится [0, 1, 2, 3].

    • z := append(x, 4) добавляет 4 к x, так что z становится [0, 1, 2, 4].

    Однако, так как x и y ссылаются на один и тот же базовый массив, изменение x через append может повлиять на y.

Вывод программы

Когда мы вызываем fmt.Println(y, z), мы получаем:

Почему так происходит?

  • Когда y := append(x, 3) выполняется, y ссылается на тот же базовый массив, что и x, но с дополнительным элементом 3.

  • Когда z := append(x, 4) выполняется, z также ссылается на тот же базовый массив, что и x, но с дополнительным элементом 4.

Так как x и y ссылаются на один и тот же базовый массив, изменение x через append влияет на y. В результате, оба слайса y и z содержат [0, 1, 2, 4].

Заключение

Программа выводит [0 1 2 4] [0 1 2 4] из-за того, что append изменяет базовый массив слайса, если его емкость позволяет. В данном случае, x, y и z ссылаются на один и тот же базовый массив, что приводит к такому результату.

6) Задача: Требуется реализовать функцию uniqRandn, которая генерирует слайс длины n уникальных рандомных чисел:

Для реализации функции uniqRandn, которая генерирует слайс длины n уникальных случайных чисел, можно использовать несколько подходов. Один из них — это использование карты (map) для отслеживания уже сгенерированных чисел, чтобы избежать дубликатов.

Реализация функции uniqRandn

  1. Импорт необходимых пакетов:

    • math/rand для генерации случайных чисел.

    • time для инициализации генератора случайных чисел.

  2. Инициализация генератора случайных чисел:

    • Используем текущее время в наносекундах для инициализации генератора случайных чисел.

  3. Использование карты для отслеживания уникальных чисел:

    • Генерируем случайные числа и добавляем их в карту до тех пор, пока не наберем нужное количество уникальных чисел.

Код

Объяснение кода

  1. Инициализация генератора случайных чисел:

    Это гарантирует, что генератор случайных чисел будет инициализирован текущим временем, что делает последовательность случайных чисел уникальной при каждом запуске программы.

  2. Создание карты и слайса:

    Карта nums используется для отслеживания уникальных чисел, а слайс result для хранения результата.

  3. Генерация уникальных случайных чисел:

    Цикл продолжается до тех пор, пока количество уникальных чисел в карте nums не достигнет n. Внутри цикла генерируется случайное число num. Если это число еще не присутствует в карте nums, оно добавляется в карту и слайс result.

Заключение

Этот код генерирует слайс длины n уникальных случайных чисел. Если требуется другой диапазон случайных чисел, можно изменить аргумент функции rand.Intn. Например, для генерации чисел в диапазоне от 0 до 999, используйте rand.Intn(1000).

Last updated