Проверить что заданная строка является палиндромом.
Пример 1:
Input: s = "A man, a plan, a canal: Panama"
Output: true
Объяснение: "amanaplanacanalpanama" – палиндром.
Пример 2:
Input: s = "race a car"
Output: false
Объяснение: "raceacar" – не палиндром.
Пример 3:
Input: s = " "
Output: true
Объяснение: "" – пустая строка это палиндром.
Пример 1:
Input: s = "A man, a plan, a canal: Panama"
Output: true
Объяснение: "amanaplanacanalpanama" – палиндром.
Пример 2:
Input: s = "race a car"
Output: false
Объяснение: "raceacar" – не палиндром.
Пример 3:
Input: s = " "
Output: true
Объяснение: "" – пустая строка это палиндром.
package main
import (
"fmt"
)
func main() {
fmt.Println(isPalindrome("A man, a plan, a canal: Panama")) // true
fmt.Println(isPalindrome("race a car")) // false
fmt.Println(isPalindrome(" ")) // true
}
func isPalindrome(s string) bool {
if s == "" {
return true
}
begin, end := 0, len(s) - 1
for begin < end {
for begin < end && !isAlphanumericChar(s[begin]) {
begin++
}
for begin < end && !isAlphanumericChar(s[end]) {
end--
}
if begin < end && charToLower(s[begin]) != charToLower(s[end]) {
return false
}
begin++
end--
}
return true
}
func charToLower(c byte) byte {
if c >= 'A' && c <= 'Z' {
c += 32
}
return c
}
func isAlphanumericChar(c byte) bool {
return (c >= '0' && c <= '9') ||
(c >= 'a' && c <= 'z') ||
(c >= 'A' && c <= 'Z')
}
В массиве чисел, переместить все нули в конец массива.
Пример 1:
Input: nums = [0,1,0,3,12]
Output: [1,3,12,0,0]
Пример 2:
Input: nums = [0]
Output: [0]
Пример 1:
Input: nums = [0,1,0,3,12]
Output: [1,3,12,0,0]
Пример 2:
Input: nums = [0]
Output: [0]
package main
import (
"fmt"
)
func main() {
fmt.Println(moveZeroes([]int{0,1,0,3,12})) // 1,3,12,0,0
fmt.Println(moveZeroes([]int{0})) // 0
}
// Вариант решения с дополнительной памятью
func moveZeroes(nums []int) []int {
ans := make([]int, len(nums), len(nums))
numZeroes := 0
for i := 0; i < len(nums); i++ {
if nums[i] != 0 {
ans[numZeroes] = nums[i]
numZeroes++
}
}
return nums
}
// Вариант решения без дополнительной памяти,
// очень хороший и больше для мидла
func moveZeroes(nums []int) []int {
nonZeroIndex := 0
for i := 0; i < len(nums); i++ {
if nums[i] != 0 {
nums[i], nums[nonZeroIndex] = nums[nonZeroIndex], nums[i]
nonZeroIndex++
}
}
return nums
}
Удалить дубликаты из отсортированного массива. Функция должна вернуть число, означающее
количество уникальных элементов в массиве.
⚠️ Важно: решение не должно использовать дополнительную память.
Пример 1:
Input: nums = [1,1,2]
Output: 2, nums = [1,2,_]
Пример 2:
Input: nums = [0,0,1,1,1,2,2,3,3,4]
Output: 5, nums = [0,1,2,3,4,_,_,_,_,_]
⚠️ Важно: решение не должно использовать дополнительную память.
Пример 1:
Input: nums = [1,1,2]
Output: 2, nums = [1,2,_]
Пример 2:
Input: nums = [0,0,1,1,1,2,2,3,3,4]
Output: 5, nums = [0,1,2,3,4,_,_,_,_,_]
package main
import (
"fmt"
)
func main() {
fmt.Println(removeDuplicates([]int{1,1,2})) // 2
fmt.Println(removeDuplicates([]int{0,0,1,1,1,2,2,3,3,4})) // 5
}
func removeDuplicates(nums []int) int {
ln := len(nums)
if ln <= 1 {
return ln
}
j := 0
for i := 1; i < ln; i++ {
if nums[j] != nums[i] {
j++
nums[j] = nums[i]
}
}
return j + 1
}
Есть односвязный список, нужно определить есть ли в этом списке цикл.
Пример 1:
Input: 3 → 2 → 0 → -4 → 2 (из -4 список возвращается в 2)
Output: true
Пример 2:
Input: 1 → 2 → 1 (из 2 возвращается в 1)
Output: true
Пример 3:
Input: 0 → 1 → 2 → 3
Output: false
Пример 1:
Input: 3 → 2 → 0 → -4 → 2 (из -4 список возвращается в 2)
Output: true
Пример 2:
Input: 1 → 2 → 1 (из 2 возвращается в 1)
Output: true
Пример 3:
Input: 0 → 1 → 2 → 3
Output: false
package main
import (
"fmt"
)
func main() {
head := &ListNode{3, nil}
head.Next = &ListNode{2, &ListNode{0, &ListNode{-4, head}}}
head2 := &ListNode{1, nil}
head2.Next = &ListNode{2, &ListNode{0, head2}}
head3 := &ListNode{0, nil}
head2.Next = &ListNode{1, &ListNode{2, &ListNode{3, head2}}}
fmt.Println(hasCycle(head)) // true
fmt.Println(hasCycle(head2)) // true
fmt.Println(hasCycle(head3)) // false
}
type ListNode struct {
Val int
Next *ListNode
}
// hashmap solution
func hasCycle(head *ListNode) bool {
seen := make(map[*ListNode]int)
for head!=nil {
if _, ok := seen[head]; ok {
return true
} else {
// create an entry
seen[head] = 1
}
head = head.Next
}
return false
}
// two pointers solution
func hasCycle(head *ListNode) bool {
first, second := head, head
for first != nil && first.Next != nil {
first = first.Next.Next
second = second.Next
if first == second {
return true
}
}
return false
}
Написать алгоритм сжатия строки по следующему принципу:
Пример 1:
Input: aaaaabbbbbbbbcd
Output: a5b8c1d1
Пример 2:
Input: abcaaaaaaaaaaaaaaaaaabbc
Output: a1b1c1a18b2c1
Пример 1:
Input: aaaaabbbbbbbbcd
Output: a5b8c1d1
Пример 2:
Input: abcaaaaaaaaaaaaaaaaaabbc
Output: a1b1c1a18b2c1
package main
import (
"fmt"
)
func main() {
fmt.Println(squeeze("aaaaabbbbbbbbcd")) // a5b8c1d1
fmt.Println(squeeze("abcaaaaaaaaaaaaaaaaaabbc")) // a1b1c1a18b2c1
fmt.Println(squeeze("aaaa")) // a4
fmt.Println(squeeze("abcde")) // a1b1c1d1e1
}
func squeeze(data string) string {
var result strings.Builder
repeated := 1
for i := 0; i < len(data); i++ {
if i < len(data) - 1 && data[i] == data[i+1] {
repeated++
} else {
result.WriteString(fmt.Sprintf("%c%d", data[i], repeated))
repeated = 1
}
}
return result.String()
}
У нас имеются сведения о поездках пассажира, в виде начальной и конечной точек.
Реализовать функцию построения маршрута, по которому проехал пассажир.
Input: []Trip[{"x", "d"}, {"c", "e"}, {"e", "b"}, {"d", "c"}]
Output: [x d c e b]
Реализовать функцию построения маршрута, по которому проехал пассажир.
Input: []Trip[{"x", "d"}, {"c", "e"}, {"e", "b"}, {"d", "c"}]
Output: [x d c e b]
package main
import (
"fmt"
)
type Trip struct {
from string
to string
}
func main() {
trips := []Trip{
{"c", "e"},
{"x", "d"},
{"d", "c"},
{"e", "b"},
}
fmt.Println(route(trips)) // [x d c e b]
}
func route(trips []Trip) []string {
result := make([]string, 0, len(trips) + 1)
head := ""
tos := make(map[string]bool)
adjList := make(map[string]string)
// transform array to adjacency list
for _, part := range trips {
adjList[part.from] = part.to
tos[part.to] = true
}
// find the head
for _, part := range trips {
if _, ok := tos[part.from]; !ok {
head = part.from
break
}
}
if head == "" {
panic("Wrong data set, head is not defined")
}
result = append(result, head)
// traverse from the head and collect a result
for {
result = append(result, adjList[head])
if _, ok := adjList[head]; !ok {
break
}
head = adjList[head]
}
return result
}
Дан массив интервалов, нужно объединить пересекающиеся интервалы и вернуть результирующий
массив.
Пример 1:
Input: intervals = [[1,3],[2,6],[8,10],[15,18]]
Output: [[1,6],[8,10],[15,18]]
Пример 2:
Input: intervals = [[1,4],[4,5]]
Output: [[1,5]]
Пример 1:
Input: intervals = [[1,3],[2,6],[8,10],[15,18]]
Output: [[1,6],[8,10],[15,18]]
Пример 2:
Input: intervals = [[1,4],[4,5]]
Output: [[1,5]]
package main
import (
"fmt"
)
func main() {
fmt.Println(merge([][]int{{1,3},{2,6},{8,10},{15,18}})) // [[1,6],[8,10],[15,18]]
fmt.Println(merge([][]int{{1,4},{4,5}})) // [[1,5]]
}
func merge(s [][]int) [][]int {
// sort intervals by start value
sort.Slice(s, func(i,j int) bool {
return s[i][0] < s[j][0]
})
res := [][]int{s[0]} // its guaranteed that there will be at least one interval.
i := 0
for idx := 1; idx < len(s); idx++ {
switch {
case s[idx][0] >= res[i][0] && s[idx][0] <= res[i][1]:
// start of the second interval lays inside first
res[i][0], res[i][1] = Min(res[i][0], s[idx][0]), Max(res[i][1], s[idx][1])
case s[idx][1] >= res[i][0] && s[idx][1] <= res[i][1]:
// the end of the second interval lays inside first
res[i][0], res[i][1] = Min(res[i][0], s[idx][0]), Max(res[i][1], s[idx][1])
default:
res = append(res, s[idx])
i++
}
}
return res
}
func Min(x,y int) int {
if x < y {
return x
}
return y
}
func Max(x,y int) int {
if x < y {
return y
}
return x
}
На вход подаются два неупорядоченных слайса любой длины. Надо написать функцию, которая
возвращает их пересечение
Можно решить сортировкой, за более долгое время, но без выделения дополнительной памяти.
А можно выделить дополнительную память и решить за линейное время.
Надо посчитать количество появлений элементов первого массива (лучше брать тот, что покороче) — используем для этого словарь.
Потом пройтись по второму массиву и вычитать из словаря те элементы, которые есть в нем.
По ходу добавляем в результат те элементы, у которых частота появлений больше нуля.
Можно решить сортировкой, за более долгое время, но без выделения дополнительной памяти.
А можно выделить дополнительную память и решить за линейное время.
Надо посчитать количество появлений элементов первого массива (лучше брать тот, что покороче) — используем для этого словарь.
Потом пройтись по второму массиву и вычитать из словаря те элементы, которые есть в нем.
По ходу добавляем в результат те элементы, у которых частота появлений больше нуля.
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))
}
Для решения я бы использовал небуфферезированный канал.
Будем асинхронно писать туда случайные числа и закроем его, когда закончим писать.
Будем асинхронно писать туда случайные числа и закроем его, когда закончим писать.
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)
}
}
Даны n каналов типа chan int. Надо написать функцию, которая смерджит все данные из этих каналов
в один и вернет его.
Для этого напишем функцию, которая будет асинхронно читать из исходных каналов, которые ей передадут в качестве аргументов, и писать в результирующий канал, который вернется из функции.
- Создаем канал, куда будем сливать все данные. Он будет небуферезированный, потому что мы не знаем, сколько данных придет из каналов.
- Дальше асинхронно прочитаем из исходных каналов и закроем результирующий канал для мерджа, когда все чтение закончится.
- Чтобы дождаться конца чтения, просто обернем этот цикл по каналам в wait group.
Для этого напишем функцию, которая будет асинхронно читать из исходных каналов, которые ей передадут в качестве аргументов, и писать в результирующий канал, который вернется из функции.
- Создаем канал, куда будем сливать все данные. Он будет небуферезированный, потому что мы не знаем, сколько данных придет из каналов.
- Дальше асинхронно прочитаем из исходных каналов и закроем результирующий канал для мерджа, когда все чтение закончится.
- Чтобы дождаться конца чтения, просто обернем этот цикл по каналам в wait group.
package main
import (
"sync"
"fmt"
)
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
}
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)
}
}
Даны два канала.
В первый пишутся числа.
Нужно, чтобы числа читались из первого по мере поступления,
что-то с ними происходило (допустим, возводились в квадрат) и результат записывался во второй
канал.
Решается довольно прямолинейно — запускаем две горутины.
- В одной пишем в первый канал.
- Во второй читаем из первого канала и пишем во второй.
Главное — не забыть закрыть каналы, чтобы ничего нигде не заблокировалось.
Решается довольно прямолинейно — запускаем две горутины.
- В одной пишем в первый канал.
- Во второй читаем из первого канала и пишем во второй.
Главное — не забыть закрыть каналы, чтобы ничего нигде не заблокировалось.
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)
}
}
Нам нужно разбить процессы на несколько горутин — при этом не создавать новую горутину каждый
раз,
а просто переиспользовать уже имеющиеся.
- Для этого создадим канал с джобами и результирующий канал.
- Для каждого воркера создадим горутину, который будет ждать новую джобу, применять к ней заданную функцию и пулять ответ в результирующий канал.
- Для этого создадим канал с джобами и результирующий канал.
- Для каждого воркера создадим горутину, который будет ждать новую джобу, применять к ней заданную функцию и пулять ответ в результирующий канал.
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)
}
}
Семафор можно легко получить из канала.
Чтоб не аллоцировать лишние данные, будем складывать туда пустые структуры.
В нашем случае мы хотим сделать семафор, который будет ждать выполнения пяти горутин.
- Для этого просто добавим вместо обычного канала буфферизированный.
- И внутри каждой горутины положим в него значение.
- А в конце будем дожидаться, что все ок — мы вычитаем все значения из канала.
В нашем случае мы хотим сделать семафор, который будет ждать выполнения пяти горутин.
- Для этого просто добавим вместо обычного канала буфферизированный.
- И внутри каждой горутины положим в него значение.
- А в конце будем дожидаться, что все ок — мы вычитаем все значения из канала.
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)
}
Реализуйте функцию perm(), принимающую срез или строку и выводящую все возможные комбинации его
(ее) символов.
Мы используем типы rune для обработки и срезов, и строк. Runes являются кодовыми точками из Unicode, а значит могут парсить строки и срезы одинаково.
Мы используем типы rune для обработки и срезов, и строк. Runes являются кодовыми точками из Unicode, а значит могут парсить строки и срезы одинаково.
package main
import "fmt"
// Perm вызвает f с каждой пермутацией a.
func Perm(a []rune, f func([]rune)) {
perm(a, f, 0)
}
// Пермутируем значения в индексе i на len(a)-1.
func perm(a []rune, f func([]rune), i int) {
if i > len(a) {
f(a)
return
}
perm(a, f, i+1)
for j := i + 1; j < len(a); j++ {
a[i], a[j] = a[j], a[i]
perm(a, f, i+1)
a[i], a[j] = a[j], a[i]
}
}
func main() {
Perm([]rune("abc"), func(a []rune) {
fmt.Println(string(a))
})
}
Реализуйте функцию reverse, получающую срез целых чисел и разворачивающую его без использования
временного среза.
Цикл for меняет местами значения каждого элемента среза. Значения будут следовать слева направо, и в итоге все элементы будут развернуты.
Цикл for меняет местами значения каждого элемента среза. Значения будут следовать слева направо, и в итоге все элементы будут развернуты.
package main
import "fmt"
func reverse(sw []int) {
for a, b := 0, len(sw)-1; a < b; a, b = a+1, b-1 {
sw[a], sw[b] = sw[b], sw[a]
}
}
func main() {
x := []int{3, 2, 1}
reverse(x)
fmt.Println(x)
}
package main
import (
"fmt"
"sort"
"strconv"
)
// Задача: из массива неповторяющихся чисел вывести диапазоны.
// Массив может быть неотсорирован
// 8 1 5 3 2 7 -> "1-3,5,7-8"
// 1 2 -> "1-2"
// 1 4 -> "1,4"
func main() {
cases := [][]int{
{1, 2, 3, 5, 7, 8, 11},
{1, 3, 5, 7, 9},
{1, 3, 4, 9, 6, 2},
{1, 2},
{1, 4},
}
for i, _ := range cases {
fmt.Println(cases[i], "->", buildRange(cases[i]))
}
}
func buildRange(nums []int) string {
if len(nums) == 0 {
return ""
}
if len(nums) == 1 {
return strconv.Itoa(nums[0])
}
sort.Slice(nums, func(i, j int) bool {
return nums[i] < nums[j]
})
chars := []byte{}
rangeStart, rangeEnd := nums[0], nums[0]
current, next := 0, 0
for i := 0; i < len(nums); i++ {
current = nums[i]
if i < len(nums)-1 {
next = nums[i+1]
}
if current+1 == next {
rangeEnd = next
continue
}
chars = append(chars, strconv.Itoa(rangeStart)...)
if rangeStart != rangeEnd {
chars = append(chars, '-')
chars = append(chars, strconv.Itoa(rangeEnd)...)
}
chars = append(chars, ',')
rangeStart = next
rangeEnd = next
}
return string(chars[:len(chars)-1])
}