Skip to content

Commit

Permalink
Add solution for av-find-broken
Browse files Browse the repository at this point in the history
  • Loading branch information
skosovsky committed Nov 29, 2023
1 parent 59f853a commit 121b543
Show file tree
Hide file tree
Showing 6 changed files with 349 additions and 0 deletions.
62 changes: 62 additions & 0 deletions av-find-broken/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# Avito Backend Weekend Offer

## 1. FindTheBrokenOne

| Показатель | значение |
|----------------------|----------------------------|
| Ограничение времени | 1 секунда |
| Ограничение памяти | 256Mb |
| Ввод | стандартный ввод (stdin) |
| Вывод | стандартный вывод (stdout) |

Вам предстоит найти хэш коммита, в котором время сборки приложения первый раз равнялось или превысило порог 'thresholdTime'.
Написать реализацию функции 'FindTheBrokenOne', которая возвращает хэш коммита, в котором время сборки приложения 'BuildTime' первый раз равнялось или превысило порог 'thresholdTime'. Если все 'BuildTime' меньше 'thresholdTime', то функция должна возвращать пустую строку.

### Ограничения

'0 <= len(commits) <= 1 000 000'
'commits[i+1].buildTime >= commits[i].buildTime >= 0'

### На вход подается

- список 'commits' истории коммитов в виде массива, где 'hash' - хэш коммита, а 'buildTime' - время сборки приложения с изменениями этого коммита
- 'thresholdTime', который показывает порог времени сборки

### На выходе вернуть

Выведите 'hash' коммита или пустое значение

#### Пример 1

'commits = [{"hash":"654ec593","buildTime":3},{"hash":"7ed9a3d6","buildTime":5},{"hash":"20c1be38","buildTime":7},{"hash":"6d9eb971","buildTime":9},{"hash":"4ed905e2","buildTime":10}]'
'thresholdTime = 4'

Ответ - '7ed9a3d6', так как именно этот коммит первым превышает порог '4'

Sample Input 1:
'[{"hash":"654ec593","buildTime":3},{"hash":"7ed9a3d6","buildTime":5},{"hash":"20c1be38","buildTime":7},{"hash":"6d9eb971","buildTime":9},{"hash":"4ed905e2","buildTime":10}]'
'4'

'Sample Output 1:'
'7ed9a3d6'

#### Пример 2

'commits = [{"hash":"654ec593","buildTime":3},{"hash":"7ed9a3d6","buildTime":5}]'
'thresholdTime = 7'

Ответ - '""', так 'thresholdTime' больше любого представленного значения

##### Среда

// CommitInfo - это вспомогательная структура,
// которая уже существует в рантайме.
// Её не нужно раскомментировать, код будет работать и так.
// type CommitInfo struct {
// Hash string
// BuildTime int
// }

func FindTheBrokenOne(commits []CommitInfo, thresholdTime int) string {
// Напишите вашу реализацию здесь
}
3 changes: 3 additions & 0 deletions av-find-broken/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module github.com/skosovsky/algo/av-find-broken

go 1.21.4
160 changes: 160 additions & 0 deletions av-find-broken/golangci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
linters-settings:
depguard:
# new configuration
rules:
logger:
deny:
# logging is allowed only by logutils.Log,
# logrus is allowed to use only in logutils package.
- pkg: "github.com/sirupsen/logrus"
desc: logging is allowed only by logutils.Log
dupl:
threshold: 100
funlen:
lines: -1 # the number of lines (code + empty lines) is not a right metric and leads to code without empty line or one-liner.
statements: 50
goconst:
min-len: 2
min-occurrences: 3
gocritic:
enabled-tags:
- diagnostic
- experimental
- opinionated
- performance
- style
disabled-checks:
- dupImport # https://github.com/go-critic/go-critic/issues/845
- ifElseChain
- octalLiteral
- whyNoLint
gocyclo:
min-complexity: 15
gofmt:
rewrite-rules:
- pattern: 'interface{}'
replacement: 'any'
goimports:
local-prefixes: github.com/golangci/golangci-lint
gomnd:
# don't include the "operation" and "assign"
checks:
- argument
- case
- condition
- return
ignored-numbers:
- '0'
- '1'
- '2'
- '3'
ignored-functions:
- strings.SplitN

govet:
check-shadowing: true
settings:
printf:
funcs:
- (github.com/golangci/golangci-lint/pkg/logutils.Log).Infof
- (github.com/golangci/golangci-lint/pkg/logutils.Log).Warnf
- (github.com/golangci/golangci-lint/pkg/logutils.Log).Errorf
- (github.com/golangci/golangci-lint/pkg/logutils.Log).Fatalf
lll:
line-length: 140
misspell:
locale: US
nolintlint:
allow-unused: false # report any unused nolint directives
require-explanation: false # don't require an explanation for nolint directives
require-specific: false # don't require nolint directives to be specific about which linter is being skipped
revive:
rules:
- name: unexported-return
disabled: true
- name: unused-parameter

linters:
disable-all: true
enable:
- bodyclose
- depguard
- dogsled
- dupl
- errcheck
- exportloopref
- funlen
- gocheckcompilerdirectives
- gochecknoinits
- goconst
- gocritic
- gocyclo
- gofmt
- goimports
- gomnd
- goprintffuncname
- gosec
- gosimple
- govet
- ineffassign
- lll
- misspell
- nakedret
- noctx
- nolintlint
- revive
- staticcheck
- stylecheck
- typecheck
- unconvert
- unparam
- unused
- whitespace

# don't enable:
# - asciicheck
# - scopelint
# - gochecknoglobals
# - gocognit
# - godot
# - godox
# - goerr113
# - interfacer
# - maligned
# - nestif
# - prealloc
# - testpackage
# - wsl

issues:
# Excluding configuration per-path, per-linter, per-text and per-source
exclude-rules:
- path: _test\.go
linters:
- gomnd

- path: pkg/golinters/errcheck.go
text: "SA1019: errCfg.Exclude is deprecated: use ExcludeFunctions instead"
- path: pkg/commands/run.go
text: "SA1019: lsc.Errcheck.Exclude is deprecated: use ExcludeFunctions instead"
- path: pkg/commands/run.go
text: "SA1019: e.cfg.Run.Deadline is deprecated: Deadline exists for historical compatibility and should not be used."

- path: pkg/golinters/gofumpt.go
text: "SA1019: settings.LangVersion is deprecated: use the global `run.go` instead."
- path: pkg/golinters/staticcheck_common.go
text: "SA1019: settings.GoVersion is deprecated: use the global `run.go` instead."
- path: pkg/lint/lintersdb/manager.go
text: "SA1019: (.+).(GoVersion|LangVersion) is deprecated: use the global `run.go` instead."
- path: pkg/golinters/unused.go
text: "rangeValCopy: each iteration copies 160 bytes \\(consider pointers or indexing\\)"
- path: test/(fix|linters)_test.go
text: "string `gocritic.go` has 3 occurrences, make it a constant"

run:
timeout: 5m
skip-dirs:
- test/testdata_etc # test files
- internal/cache # extracted from Go code
- internal/renameio # extracted from Go code
- internal/robustio # extracted from Go code
66 changes: 66 additions & 0 deletions av-find-broken/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package main

import "fmt"

type CommitInfo struct {
Hash string
BuildTime int
}

func main() {
commits := []CommitInfo{
{Hash: "654ec593", BuildTime: 3},
{Hash: "7ed9a3d6", BuildTime: 5},
{Hash: "20c1be38", BuildTime: 8},
{Hash: "6d9eb971", BuildTime: 9},
{Hash: "4ed905e2", BuildTime: 10},
{Hash: "654ec59", BuildTime: 16},
{Hash: "7ed9a3d", BuildTime: 18},
{Hash: "20c1be3", BuildTime: 20},
{Hash: "6d9eb97", BuildTime: 25},
{Hash: "4ed905e", BuildTime: 30},
}

thresholdTime := 16

fmt.Println(FindTheBrokenOne(commits, thresholdTime)) // standard output
}

func FindTheBrokenOne(commits []CommitInfo, thresholdTime int) string {
// Быстрая проверка, если вдруг последнее значение меньше
if len(commits) != 0 && commits[len(commits)-1].BuildTime < thresholdTime {
return ""
}

// Если длина меньше 20, то используем обычный перебор (20 вычислено тестами)
if len(commits) < 20 {
for i := 0; i <= len(commits)-1; i++ {
if commits[i].BuildTime >= thresholdTime {
return commits[i].Hash
}
if i == len(commits) {
return ""
}
}
}

// Если длина больше 20, то используем бинарный поиск
// Можно было заморочиться с другими видами поиска, но тут всего 1 млн значений
low, high := 0, len(commits)-1

for low < high {
mid := low + (high-low)/2

if commits[mid].BuildTime < thresholdTime {
low = mid + 1
} else {
high = mid
}
}

if low < len(commits) {
return commits[low].Hash
}

return ""
}
24 changes: 24 additions & 0 deletions av-find-broken/main_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package main

import "testing"

var commits = []CommitInfo{
{Hash: "654ec593", BuildTime: 1},
{Hash: "654ec593", BuildTime: 2},
{Hash: "654ec593", BuildTime: 3},
{Hash: "654ec593", BuildTime: 4},
{Hash: "654ec593", BuildTime: 5},
{Hash: "654ec593", BuildTime: 6},
{Hash: "654ec593", BuildTime: 7},
{Hash: "654ec593", BuildTime: 8},
{Hash: "654ec593", BuildTime: 9},
{Hash: "654ec593", BuildTime: 10},
}

var thresholdTime = 8

func BenchmarkFull(b *testing.B) {
for i := 0; i < b.N; i++ {
FindTheBrokenOne(commits, thresholdTime)
}
}
34 changes: 34 additions & 0 deletions av-find-broken/makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
APP=algo/ya-details
.PHONY: help
help: Makefile ## Show this help
@echo
@echo "Choose a command run in "$(APP)":"
@echo
@fgrep -h "##" $(MAKEFILE_LIST) | sed -e 's/\(\:.*\#\#\)/\:\ /' | fgrep -v fgrep | sed -e 's/\\$$//' | sed -e 's/##//'

.PHONY: build
build: ## Build an application
@echo "Building ${APP} ..."
mkdir -p build
go build -o build/${APP} main.go

run: ## Run an application
@echo "Starting ${APP} ..."
go run main.go

test: ## Run an application
@echo "Testing ${APP} ..."
go test

bench: ## Run an application
@echo "Benchmarking ${APP} ..."
go test -bench=. .

clean: ## Clean a garbage
@echo "Cleaning"
go clean
rm -rf build

lint: ## Check a code by golangci-lint
@echo "Linter checking..."
golangci-lint run -c golangci.yml ./...

0 comments on commit 121b543

Please sign in to comment.