Compare commits

..

10 commits

Author SHA1 Message Date
e1b702657c add license 2025-12-28 12:54:31 +09:00
1433bf4850 add new solutions 2025-12-28 12:54:21 +09:00
475d438db4 chore: add pp for pretty printing 2025-12-25 13:08:52 +09:00
d7aba91105 add new solutions 2025-12-25 13:07:43 +09:00
14cd11f1c7
add new solutions 2025-12-24 18:51:27 +09:00
f960020cb4 add gods (Go data structures) as dependency
Which is allowed by leetcode
2025-12-24 18:51:12 +09:00
3a433aef4f chore: add makefile 2025-12-24 13:06:00 +09:00
58527849b2
add new solutions 2025-12-23 19:00:11 +09:00
ccb8b5673b restructure solutions dir 2025-12-23 18:39:11 +09:00
f9ddad5f88 add script for creating template 2025-12-23 18:39:11 +09:00
44 changed files with 936 additions and 0 deletions

24
LICENSE Normal file
View file

@ -0,0 +1,24 @@
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <https://unlicense.org/>

4
Makefile Normal file
View file

@ -0,0 +1,4 @@
lint:
golangci-lint run ./...
.PHONY: lint

21
create.sh Executable file
View file

@ -0,0 +1,21 @@
#!/bin/bash
num=$1
echo "$num" | grep -q "^[1-9][0-9]\{,4\}$" || {
echo "Err: no valid number given" >&2
exit 1
}
parent=$((num / 100))
pdir="solutions/${parent}/q${num}"
sol_file="${pdir}/solution.go"
if [ -f "$sol_file" ]; then
exit 0
fi
echo "Creating template for question No. $num" >&2
mkdir -pv "$pdir"
echo "package q$num" > "$sol_file"
echo "Created $sol_file"

12
go.mod
View file

@ -1,3 +1,15 @@
module leetcode-go module leetcode-go
go 1.25.5 go 1.25.5
require (
github.com/emirpasic/gods/v2 v2.0.0-alpha
github.com/k0kubun/pp/v3 v3.5.0
)
require (
github.com/mattn/go-colorable v0.1.14 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
golang.org/x/sys v0.29.0 // indirect
golang.org/x/text v0.26.0 // indirect
)

13
go.sum Normal file
View file

@ -0,0 +1,13 @@
github.com/emirpasic/gods/v2 v2.0.0-alpha h1:dwFlh8pBg1VMOXWGipNMRt8v96dKAIvBehtCt6OtunU=
github.com/emirpasic/gods/v2 v2.0.0-alpha/go.mod h1:W0y4M2dtBB9U5z3YlghmpuUhiaZT2h6yoeE+C1sCp6A=
github.com/k0kubun/pp/v3 v3.5.0 h1:iYNlYA5HJAJvkD4ibuf9c8y6SHM0QFhaBuCqm1zHp0w=
github.com/k0kubun/pp/v3 v3.5.0/go.mod h1:5lzno5ZZeEeTV/Ky6vs3g6d1U3WarDrH8k240vMtGro=
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=

View file

@ -0,0 +1,65 @@
package q15
import "slices"
func sortAndDedup3(nums []int) []int {
slices.Sort(nums)
head, dup := 0, 0
for i := 1; i < len(nums); i++ {
if nums[i] == nums[head] {
dup++
} else {
dup = 0
}
if dup < 3 {
head++
nums[head] = nums[i]
}
}
return nums[:head+1]
}
func threeSum(nums []int) [][]int {
nums = sortAndDedup3(nums)
ret := [][]int{}
appendResult := func(a, b, c int) {
triplet := []int{a, b, c}
if len(ret) > 0 && slices.Equal(triplet, ret[len(ret)-1]) {
return
}
ret = append(ret, triplet)
}
for i := 0; i < len(nums)-2; i++ {
if nums[i] > 0 {
break
}
wants := -nums[i]
l, r := i+1, len(nums)-1
for l < r {
for l < r && nums[l]+nums[r] > wants {
r--
}
if l < r && nums[l]+nums[r] == wants {
appendResult(nums[i], nums[l], nums[r])
r--
}
for l < r && nums[l]+nums[r] < wants {
l++
}
if l < r && nums[l]+nums[r] == wants {
appendResult(nums[i], nums[l], nums[r])
l++
}
}
// skip same numbers
for ; i < len(nums)-2 && nums[i] == nums[i+1]; i++ {
}
}
return ret
}
var _ = threeSum

View file

@ -0,0 +1,39 @@
package q17
var alphabets = [][]byte{
[]byte("abc"),
[]byte("def"),
[]byte("ghi"),
[]byte("jkl"),
[]byte("mno"),
[]byte("pqrs"),
[]byte("tuv"),
[]byte("wxyz"),
}
func combinations(offsets []int, i int, buffer []byte, strings []string) []string {
if buffer == nil {
buffer = make([]byte, len(offsets))
}
if i == len(offsets) {
return append(strings, string(buffer))
}
for _, c := range alphabets[offsets[i]] {
buffer[i] = c
strings = combinations(offsets, i+1, buffer, strings)
}
return strings
}
func letterCombinations(digits string) []string {
offsets := make([]int, len(digits))
for i := range len(digits) {
offsets[i] = int(digits[i] - '2')
}
return combinations(offsets, 0, nil, []string{})
}
var _ = letterCombinations

View file

@ -0,0 +1,32 @@
package q20
func isValid(s string) bool {
stack := make([]byte, 0, len(s)/2)
for i := range len(s) {
switch s[i] {
case '(', '[', '{':
stack = append(stack, s[i])
case ')':
if len(stack) == 0 || stack[len(stack)-1] != '(' {
return false
}
stack = stack[:len(stack)-1]
case ']':
if len(stack) == 0 || stack[len(stack)-1] != '[' {
return false
}
stack = stack[:len(stack)-1]
case '}':
if len(stack) == 0 || stack[len(stack)-1] != '{' {
return false
}
stack = stack[:len(stack)-1]
}
}
return len(stack) == 0
}
var _ = isValid

View file

@ -0,0 +1,19 @@
package q35
func searchInsert(nums []int, target int) int {
l, r := 0, len(nums)
for l < r {
mid := (l + r) / 2
switch {
case nums[mid] == target:
return mid
case nums[mid] > target:
r = mid
default:
l = mid + 1
}
}
return l
}
var _ = searchInsert

View file

@ -0,0 +1,18 @@
package q53
import "math"
func maxSubArray(nums []int) int {
sum := 0
minSubstract := 0
maxSubArr := math.MinInt
for i := range nums {
sum += nums[i]
nums[i] = sum
maxSubArr = max(maxSubArr, nums[i]-minSubstract)
minSubstract = min(minSubstract, nums[i])
}
return maxSubArr
}
var _ = maxSubArray

View file

@ -0,0 +1,22 @@
package q66
func plusOne(digits []int) []int {
digits[len(digits)-1]++
for i := len(digits) - 1; i > 0; i-- {
if digits[i] > 9 {
digits[i] -= 10
digits[i-1]++
} else {
break
}
}
if digits[0] > 9 {
digits[0] -= 10
ret := make([]int, 0, len(digits)+1)
ret = append(ret, 1)
return append(ret, digits...)
}
return digits
}
var _ = plusOne

View file

@ -0,0 +1,28 @@
package q67
func toDigit(byt byte) uint8 { return byt - '0' }
func toByte(digit uint8) byte { return '0' + digit }
func addBinary(a string, b string) string {
lenA, lenB := len(a), len(b)
ret := make([]byte, max(lenA, lenB)+1)
var carry uint8
for i := range ret {
d := carry
if i < lenA {
d += toDigit(a[lenA-i-1])
}
if i < lenB {
d += toDigit(b[lenB-i-1])
}
carry = d / 2
ret[len(ret)-i-1] = toByte(d % 2)
}
if ret[0] != '1' {
return string(ret[1:])
}
return string(ret)
}
var _ = addBinary

View file

@ -0,0 +1,26 @@
package q69
func mySqrt(x int) int {
if x < 2 {
return x
}
l, r := 0, x/2+1
for l < r {
m := (l + r) / 2
sq := m * m
switch {
case sq == x:
return m
case sq < x:
if (m+1)*(m+1) > x {
return m
}
l = m + 1
case sq > x:
r = m
}
}
return -1
}
var _ = mySqrt

View file

@ -0,0 +1,24 @@
package q70
func nWays(n, i int, cache []int) int {
if i == n {
return 1
}
if i > n {
return 0
}
if cache == nil {
cache = make([]int, n)
} else if cache[i] != 0 {
return cache[i]
}
ret := nWays(n, i+1, cache) + nWays(n, i+2, cache)
cache[i] = ret
return ret
}
func climbStairs(n int) int {
return nWays(n, 0, nil)
}
var _ = climbStairs

View file

@ -0,0 +1,18 @@
package q9
import "strconv"
func isPalindrome(x int) bool {
if x < 0 {
return false
}
str := strconv.FormatInt(int64(x), 10)
for i := range len(str) / 2 {
if str[i] != str[len(str)-1-i] {
return false
}
}
return true
}
var _ = isPalindrome

View file

@ -0,0 +1,20 @@
package q104
type TreeNode struct {
Val int
Left *TreeNode
Right *TreeNode
}
func md(d int, node *TreeNode) int {
if node == nil {
return d - 1
}
return max(md(d+1, node.Left), md(d+1, node.Right))
}
func maxDepth(root *TreeNode) int {
return md(1, root)
}
var _ = maxDepth

View file

@ -0,0 +1,26 @@
package q108
type TreeNode struct {
Val int
Left *TreeNode
Right *TreeNode
}
func sortedArrayToBST(nums []int) *TreeNode {
if len(nums) == 0 {
return nil
}
if len(nums) == 1 {
return &TreeNode{Val: nums[0]}
}
l, r := nums[:len(nums)/2], nums[len(nums)/2+1:]
m := nums[len(nums)/2]
return &TreeNode{
Val: m,
Left: sortedArrayToBST(l),
Right: sortedArrayToBST(r),
}
}
var _ = sortedArrayToBST

View file

@ -0,0 +1,14 @@
package q120
func minimumTotal(triangle [][]int) int {
size := len(triangle)
for row := size - 2; row >= 0; row-- {
for i := range triangle[row] {
triangle[row][i] += min(triangle[row+1][i], triangle[row+1][i+1])
}
}
return triangle[0][0]
}
var _ = minimumTotal

View file

@ -0,0 +1,11 @@
package q136
func singleNumber(nums []int) int {
x := 0
for _, num := range nums {
x ^= num
}
return x
}
var _ = singleNumber

View file

@ -0,0 +1,28 @@
package q141
type ListNode struct {
Val int
Next *ListNode
}
func hasCycle(head *ListNode) bool {
if head == nil {
return false
}
p1, p2 := head, head
for {
for range 2 {
p2 = p2.Next
switch p2 {
case nil:
return false
case p1:
return true
}
}
p1 = p1.Next
}
}
var _ = hasCycle

View file

@ -0,0 +1,13 @@
package q190
func reverseBits(n int) int {
ret := 0
for i := range 32 {
if n|(1<<i) == n {
ret |= 1 << (31 - i)
}
}
return ret
}
var _ = reverseBits

View file

@ -0,0 +1,13 @@
package q191
func hammingWeight(n int) int {
num := 0
for ; n > 0; n >>= 1 {
if n|1 == n {
num++
}
}
return num
}
var _ = hammingWeight

View file

@ -0,0 +1,37 @@
package q199
type TreeNode struct {
Val int
Left *TreeNode
Right *TreeNode
}
func rightSideView(root *TreeNode) []int {
ret := []int{}
type qElem struct {
height int
node *TreeNode
}
queue := []qElem{{0, root}}
var last *qElem
for ; len(queue) > 0; queue = queue[1:] {
cur := &queue[0]
if cur.node == nil {
continue
}
if last != nil && last.height < cur.height {
ret = append(ret, last.node.Val)
}
last = cur
queue = append(queue, qElem{cur.height + 1, cur.node.Left}, qElem{cur.height + 1, cur.node.Right})
}
if last != nil {
ret = append(ret, last.node.Val)
}
return ret
}
var _ = rightSideView

View file

@ -0,0 +1,31 @@
package q200
const (
VISITED byte = '.'
LAND byte = '1'
)
func traverse(grid [][]byte, x, y int) int {
if x < 0 || y < 0 || x >= len(grid[0]) || y >= len(grid) || grid[y][x] != LAND {
return 0
}
grid[y][x] = VISITED
traverse(grid, x+1, y)
traverse(grid, x-1, y)
traverse(grid, x, y+1)
traverse(grid, x, y-1)
return 1
}
func numIslands(grid [][]byte) int {
count := 0
for y := range grid {
for x := range grid[y] {
count += traverse(grid, x, y)
}
}
return count
}
var _ = numIslands

View file

@ -0,0 +1,105 @@
package q208
type tNode struct {
str string
childs [26]*tNode
hasWord bool
}
func (n *tNode) index() int { return index(n.str) }
func (n *tNode) split(prefixLen int) (*tNode, *tNode) {
if len(n.str) < prefixLen {
panic("cannot split node")
}
if len(n.str) == prefixLen {
return n, nil
}
prefix := n.str[:prefixLen]
n.str = n.str[prefixLen:]
p := &tNode{str: prefix}
p.childs[n.index()] = n
return p, n
}
func commonPrefix(a, b string) int {
minLen := min(len(a), len(b))
for i := range minLen {
if a[i] != b[i] {
return i
}
}
return minLen
}
func index(str string) int {
if len(str) > 0 {
return int(str[0] - 'a')
}
return -1 // root
}
type Trie struct {
root *tNode
}
func Constructor() Trie {
return Trie{
root: &tNode{},
}
}
func (t *Trie) Insert(word string) {
node := t.root
for len(word) > 0 {
idx := index(word)
next := node.childs[idx]
if next != nil {
pflen := commonPrefix(word, next.str)
next, _ = next.split(pflen)
node.childs[next.index()] = next
word = word[pflen:]
} else {
next = &tNode{str: word}
word = ""
node.childs[idx] = next
}
node = next
}
node.hasWord = true
}
func (t *Trie) Search(word string) bool {
node := t.root
for len(word) > 0 {
next := node.childs[index(word)]
if next == nil {
return false
}
pflen := commonPrefix(word, next.str)
if pflen != len(next.str) {
return false
}
word = word[pflen:]
node = next
}
return node.hasWord
}
func (t *Trie) StartsWith(prefix string) bool {
node := t.root
for len(prefix) > 0 {
next := node.childs[index(prefix)]
if next == nil {
return false
}
pflen := commonPrefix(prefix, next.str)
if pflen != len(next.str) && pflen != len(prefix) {
return false
}
prefix = prefix[pflen:]
node = next
}
return true
}

View file

@ -0,0 +1,37 @@
package q215
func findKthLargest(nums []int, k int) int {
// build max-heap
for i := 1; i < len(nums); i++ {
for j := i; j > 0; j = (j - 1) / 2 {
parent := (j - 1) / 2
if nums[j] > nums[parent] {
nums[j], nums[parent] = nums[parent], nums[j]
}
}
}
for range k - 1 {
// pop the heap
nums[0] = nums[len(nums)-1]
nums = nums[:len(nums)-1]
i := 0
for i*2+1 < len(nums) {
l, r := i*2+1, i*2+2
next := l
if r < len(nums) && nums[r] > nums[l] {
next = r
}
if nums[i] >= nums[next] {
break
}
nums[i], nums[next] = nums[next], nums[i]
i = next
}
}
return nums[0]
}
var _ = findKthLargest

View file

@ -0,0 +1,32 @@
package q228
import (
"fmt"
"strconv"
)
func addToRanges(output []string, a, b int) []string {
if a == b {
return append(output, strconv.FormatInt(int64(a), 10))
}
return append(output, fmt.Sprintf("%d->%d", a, b))
}
func summaryRanges(nums []int) []string {
if len(nums) == 0 {
return nil
}
output := []string{}
l := 0
for r := 1; r < len(nums); r++ {
if nums[r]-nums[r-1] != 1 {
output = addToRanges(output, nums[l], nums[r-1])
l = r
}
}
output = addToRanges(output, nums[l], nums[len(nums)-1])
return output
}
var _ = summaryRanges

View file

@ -0,0 +1,29 @@
package q2483
func bestClosingTime(customers string) int {
numY := 0
for i := range len(customers) {
if customers[i] == 'Y' {
numY++
}
}
minCloseHour := 0
minPenalty := numY
penalty := numY
for i := range len(customers) {
// close at (i + 1)th hour
switch customers[i] {
case 'N':
penalty++
case 'Y':
penalty--
}
if penalty < minPenalty {
minPenalty = penalty
minCloseHour = i + 1
}
}
return minCloseHour
}
var _ = bestClosingTime

View file

@ -0,0 +1,25 @@
package q383
func canConstruct(ransomNote string, magazine string) bool {
counts := make([]int, 'z'-'a'+1)
for _, c := range magazine {
if c < 'a' || c > 'z' {
continue
}
counts[int(c)-int('a')]++
}
for _, c := range ransomNote {
if c < 'a' || c > 'z' {
continue
}
offset := int(c) - int('a')
counts[offset]--
if counts[offset] < 0 {
return false
}
}
return true
}
var _ = canConstruct

View file

@ -0,0 +1,14 @@
package q3075
import "slices"
func maximumHappinessSum(happiness []int, k int) int64 {
slices.Sort(happiness)
var sum int64
for i := range k {
sum += max(0, int64(happiness[len(happiness)-1-i]-i))
}
return sum
}
var _ = maximumHappinessSum

View file

@ -0,0 +1,37 @@
package q530
import "math"
type TreeNode struct {
Val int
Left *TreeNode
Right *TreeNode
}
func getMinimumDifference(root *TreeNode) int {
minDiff := math.MaxInt
var last *int
var check = func(num int) {
if last == nil {
last = &num
return
}
minDiff = min(minDiff, num-*last)
*last = num
}
var traverse func(node *TreeNode)
traverse = func(node *TreeNode) {
if node == nil {
return
}
traverse(node.Left)
check(node.Val)
traverse(node.Right)
}
traverse(root)
return minDiff
}
var _ = getMinimumDifference

View file

@ -0,0 +1,43 @@
package q637
type TreeNode struct {
Val int
Left *TreeNode
Right *TreeNode
}
func averageOfLevels(root *TreeNode) []float64 {
type qElem struct {
level int
node *TreeNode
}
ret := []float64{}
level := 0
n := 0 // number of nodes
var sum float64
queue := []qElem{{level: 0, node: root}}
for ; len(queue) > 0; queue = queue[1:] {
curr := &queue[0]
if curr.level == level {
sum += float64(curr.node.Val)
n++
} else {
ret = append(ret, sum/float64(n))
sum = float64(curr.node.Val)
n = 1
level = curr.level
}
if curr.node.Left != nil {
queue = append(queue, qElem{curr.level + 1, curr.node.Left})
}
if curr.node.Right != nil {
queue = append(queue, qElem{curr.level + 1, curr.node.Right})
}
}
ret = append(ret, sum/float64(n))
return ret
}
var _ = averageOfLevels

View file

@ -0,0 +1,50 @@
package q909
func coord(n, num int) (x, y int) {
num -= 1
row := num / n
y = n - row - 1
x = num % n
if row%2 == 1 {
x = n - 1 - x
}
return
}
func snakesAndLadders(board [][]int) int {
n, sqN := len(board), len(board)*len(board)
leastRolls := make([]int, sqN)
if len(leastRolls) == 1 {
return 0
}
queue := make([]int, 0, sqN)
queue = append(queue, 1)
for ; len(queue) > 0; queue = queue[1:] {
cur := queue[0]
for dist := 1; dist <= 6 && cur+dist <= sqN; dist++ {
next := cur + dist
x, y := coord(n, next)
if board[y][x] != -1 {
next = board[y][x]
}
rolls := leastRolls[cur-1] + 1
if (next > 1 && leastRolls[next-1] == 0) || rolls < leastRolls[next-1] {
leastRolls[next-1] = rolls
if next == sqN {
return rolls
}
queue = append(queue, next)
}
}
}
l := leastRolls[sqN-1]
if l == 0 {
return -1
}
return 0
}
var _ = snakesAndLadders

6
tools.go Normal file
View file

@ -0,0 +1,6 @@
package leetcodego
import (
_ "github.com/emirpasic/gods/v2/utils"
_ "github.com/k0kubun/pp/v3"
)