From 12b82c1271b75dc6571865e33936f9f42c4b5510 Mon Sep 17 00:00:00 2001 From: zikwall Date: Tue, 9 Feb 2021 15:24:03 +0300 Subject: [PATCH] Add popular tasks --- README.md | 8 +- docs/POPULAR _TASKS.md | 329 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 336 insertions(+), 1 deletion(-) create mode 100644 docs/POPULAR _TASKS.md diff --git a/README.md b/README.md index a697af3..04e1300 100644 --- a/README.md +++ b/README.md @@ -98,7 +98,13 @@ - Какие средства обобщенного программирования есть в Go? - Какие технологические преимущества языка Go вы можете назвать? - Какие технологические недостатки языка Go вы можете назвать? - +10. Популярные задачи на собеседованиях + - На вход подаются два неупорядоченных слайса любой длины. Надо написать функцию, которая возвращает их пересечение + - Написать генератор случайных чисел + - Слить N каналов в один + - Сделать конвейер чисел + - Написать WorkerPool с заданной функцией + - Сделать кастомную waitGroup на семафоре ## Как мне добавить свой вопрос-ответ? - [Ознакомтесь с шаблоном составления](TEMPLATE.md) diff --git a/docs/POPULAR _TASKS.md b/docs/POPULAR _TASKS.md new file mode 100644 index 0000000..ca0f515 --- /dev/null +++ b/docs/POPULAR _TASKS.md @@ -0,0 +1,329 @@ +### Популярные задачи на собеседованиях + +### 1. На вход подаются два неупорядоченных слайса любой длины. Надо написать функцию, которая возвращает их пересечение + +Можно решить сортировкой, за более долгое время, но без выделения дополнительной памяти. +А можно выделить дополнительную память и решить за линейное время. + +Надо посчитать количество появлений элементов первого массива (лучше брать тот, что покороче) — используем для этого словарь. +Потом пройтись по второму массиву и вычитать из словаря те элементы, которые есть в нем. +По ходу добавляем в результат те элементы, у которых частота появлений больше нуля. + + - [x] Советуем посетить [Математические операции над множествами](https://github.com/goavengers/go-datastructure#-point_right-%D0%BC%D0%BD%D0%BE%D0%B6%D0%B5%D1%81%D1%82%D0%B2%D0%B0-sets) + +```go +package main + +import ( + "fmt" +) + +// На вход подаются два неупорядоченных массива любой длины. +// Необходимо написать функцию, которая возвращает пересечение массивов +func intersection(a, b []int) []int { + counter := make(map[int]int) + var result []int + + for _, elem := range a { + counter[elem]++ + } + + for _, elem := range b { + if count, ok := counter[elem]; ok && count > 0 { + counter[elem] -= 1 + result = append(result, elem) + } + } + return result +} + +func main() { + + a := []int{23, 3, 1, 2} + b := []int{6, 2, 4, 23} + // [2, 23] + fmt.Printf("%v\n", intersection(a, b)) + a = []int{1, 1, 1} + b = []int{1, 1, 1, 1} + // [1, 1, 1] + fmt.Printf("%v\n", intersection(a, b)) +} +``` + +### 2. Написать генератор случайных чисел + +В принципе, легкая задача, на базовые знания по асинхронному взаимодействию в Go. +Для решения я бы использовал небуфферезированный канал. Будем асинхронно писать туда случайные числа и закроем его, когда закончим писать. + +Плюс ее можно использовать в немного измененном виде в задаче на [слияние N каналов](#3). + +```go +package main + +import ( + "fmt" + "math/rand" + "time" +) + +func randNumsGenerator(n int) <-chan int { + r := rand.New(rand.NewSource(time.Now().UnixNano())) + + out := make(chan int) + go func() { + for i := 0; i < n; i++ { + out <- r.Intn(n) + } + close(out) + }() + return out +} + +func main() { + for num := range randNumsGenerator(10) { + fmt.Println(num) + } +} +``` + +### 3. Слить N каналов в один + +Даны n каналов типа chan int. Надо написать функцию, которая смерджит все данные из этих каналов в один и вернет его. + +Мы хотим, чтобы результат работы функции выглядел примерно так: + +```go +for num := range joinChannels(a, b, c) { + fmt.Println(num) +} +``` + +Для этого напишем функцию, которая будет асинхронно читать из исходных каналов, которые ей передадут в качестве аргументов, и писать в результирующий канал, который вернется из функции. + +Создаем канал, куда будем сливать все данные. +Он будет небуферезированный, потому что мы не знаем, сколько данных придет из каналов. + +Дальше асинхронно прочитаем из исходных каналов и закроем результирующий канал для мерджа, когда все чтение закончится. +Чтобы дождаться конца чтения, просто обернем этот цикл по каналам в wait group. + +```go +package main + +import ( + "sync" +) + +func joinChannels(chs ...<-chan int) <-chan int { + mergedCh := make(chan int) + + go func() { + wg := &sync.WaitGroup{} + + wg.Add(len(chs)) + + for _, ch := range chs { + go func(ch <-chan int, wg *sync.WaitGroup) { + defer wg.Done() + + for id := range ch { + mergedCh <- id + } + }(ch, wg) + } + + wg.Wait() + close(mergedCh) + }() + + return mergedCh +} +``` + +```go +package main + +import ( + "fmt" +) + +func main() { + + a := make(chan int) + b := make(chan int) + c := make(chan int) + + go func() { + for _, num := range []int{1, 2, 3} { + a <- num + } + close(a) + }() + + go func() { + for _, num := range []int{20, 10, 30} { + b <- num + } + close(b) + }() + + go func() { + for _, num := range []int{300, 200, 100} { + c <- num + } + close(c) + }() + + for num := range joinChannels(a, b, c) { + fmt.Println(num) + } +} +``` + +### 4. Сделать конвейер чисел + +Даны два канала. +В первый пишутся числа. +Нужно, чтобы числа читались из первого по мере поступления, +что-то с ними происходило (допустим, возводились в квадрат) и результат записывался во второй канал. + +Довольно частая задача, более подробно можно почитать [тут](https://blog.golang.org/pipelines). + +Решается довольно прямолинейно — запускаем две горутины. +- В одной пишем в первый канал. +- Во второй читаем из первого канала и пишем во второй. + +Главное — не забыть закрыть каналы, чтобы ничего нигде не заблокировалось. + +```go +package main + +import ( + "fmt" +) + +func main() { + naturals := make(chan int) + squares := make(chan int) + + go func() { + for x := 0; x <= 10; x++ { + naturals <- x + } + + close(naturals) + }() + + go func() { + for x := range naturals { + squares <- x * x + } + + close(squares) + }() + + for x := range squares { + fmt.Println(x) + } +} +``` + +### 5. Написать WorkerPool с заданной функцией + +Довольно распространенная задача, плюс подобные задачи встречаются на практике. + +Нам нужно разбить процессы на несколько горутин — при этом не создавать новую горутину каждый раз, +а просто переиспользовать уже имеющиеся. +- Для этого создадим канал с джобами и результирующий канал. +- Для каждого воркера создадим горутину, который будет ждать новую джобу, применять к ней заданную функцию и пулять ответ в результирующий канал. + +```go +package main + +import ( + "fmt" +) + +func worker(id int, f func(int) int, jobs <-chan int, results chan<- int) { + for j := range jobs { + results <- f(j) + } +} + +func main() { + + const numJobs = 5 + jobs := make(chan int, numJobs) + results := make(chan int, numJobs) + + multiplier := func(x int) int { + return x * 10 + } + + for w := 1; w <= 3; w++ { + go worker(w, multiplier, jobs, results) + } + + for j := 1; j <= numJobs; j++ { + jobs <- j + } + + close(jobs) + + for i := 1; i <= numJobs; i++ { + fmt.Println(<-results) + } +} +``` + +Семафор можно легко получить из канала. +Чтоб не аллоцировать лишние данные, будем складывать туда пустые структуры. + +В нашем случае мы хотим сделать семафор, который будет ждать выполнения пяти горутин. +- Для этого просто добавим вместо обычного канала буфферизированный. +- И внутри каждой горутины положим в него значение. +- А в конце будем дожидаться, что все ок — мы вычитаем все значения из канала. + +```go +package main + +import ( + "fmt" +) + +type sema chan struct{} + +func New(n int) sema { + return make(sema, n) +} + +func (s sema) Inc(k int) { + for i := 0; i < k; i++ { + s <- struct{}{} + } +} + +func (s sema) Dec(k int) { + for i := 0; i < k; i++ { + <-s + } +} + +func main() { + numbers := []int{1, 2, 3, 4, 5} + n := len(numbers) + + sem := New(n) + + for _, num := range numbers { + go func(n int) { + fmt.Println(n) + sem.Inc(1) + }(num) + } + + sem.Dec(n) + +} +``` + +### 6. Сделать кастомную waitGroup на семафоре \ No newline at end of file