diff --git a/basics/main.go b/basics/main.go index d654573..8971abc 100644 --- a/basics/main.go +++ b/basics/main.go @@ -9,14 +9,12 @@ import ( "github.com/rihib/leetcode/basics/graph" "github.com/rihib/leetcode/basics/queue" "github.com/rihib/leetcode/basics/sort" - "github.com/rihib/leetcode/basics/tree" ) func main() { testSort() testPriorityQueue() testDijkstra() - testTree() } func testSort() { @@ -114,7 +112,7 @@ func testPriorityQueue() { node, l, isEmpty = pq.Peek(), pq.Len(), pq.IsEmpty() fmt.Printf("Peek returns nil: %t, Len: %d, IsEmpty: %t\n", node == nil, l, isEmpty) - fmt.Print("\n") + fmt.Println("") } func testDijkstra() { @@ -131,80 +129,4 @@ func testDijkstra() { for node, distance := range dist { fmt.Printf("Distance from node 0 to node %d is %d\n", node, distance) } - - fmt.Print("\n") -} - -func testTree() { - /* - 下記のツリーが構築される。 - 1 - / \ - 2 3 - / \ / \ - x 4 5 x - / \ / - x x 6 - */ - nums := []int{1, 2, 3, -1, 4, 5, -1, -1, -1, 6} - root := buildTree(nums) - fmt.Println("=====Tree=====") - - fmt.Print("Pre-order: ") - tree.PreOrderTraversal(root) // 1, 2, 4, 3, 5, 6 - fmt.Print("\n") - - fmt.Print("In-order: ") - tree.InOrderTraversal(root) // 2, 4, 1, 6, 5, 3 - fmt.Print("\n") - - fmt.Print("Post-order: ") - tree.PostOrderTraversal(root) // 4, 2, 6, 5, 3, 1 - fmt.Print("\n") - - fmt.Print("Pre-order Iterative: ") - tree.PreOrderTraversalIterative(root) // 1, 2, 4, 3, 5, 6 - fmt.Print("\n") - - fmt.Print("In-order Iterative: ") - tree.InOrderTraversalIterative(root) // 2, 4, 1, 6, 5, 3 - fmt.Print("\n") - - fmt.Print("Post-order Iterative1: ") - tree.PostOrderTraversalIterative1(root) // 4, 2, 6, 5, 3, 1 - fmt.Print("\n") - - fmt.Print("Post-order Iterative2: ") - tree.PostOrderTraversalIterative2(root) // 4, 2, 6, 5, 3, 1 - fmt.Print("\n") - - fmt.Print("Post-order Iterative3: ") - tree.PostOrderTraversalIterative3(root) // 4, 2, 6, 5, 3, 1 - fmt.Print("\n") - - fmt.Print("\n") -} - -func buildTree(nums []int) *tree.Node { - if len(nums) == 0 || nums[0] < 0 { - return nil - } - root := &tree.Node{Val: nums[0]} - queue := []*tree.Node{root} - index := 1 - for len(queue) > 0 && index < len(nums) { - node := queue[0] - queue = queue[1:] - if nums[index] >= 0 { - node.Left = &tree.Node{Val: nums[index]} - queue = append(queue, node.Left) - } - index++ - if index < len(nums) && nums[index] >= 0 { - node.Right = &tree.Node{Val: nums[index]} - queue = append(queue, node.Right) - } - index++ - } - return root } diff --git a/basics/tree/orders.go b/basics/tree/orders.go deleted file mode 100644 index 40df100..0000000 --- a/basics/tree/orders.go +++ /dev/null @@ -1,147 +0,0 @@ -package tree - -import "fmt" - -type Node struct { - Val int - Left *Node - Right *Node -} - -// Root -> Left -> Right -func PreOrderTraversal(root *Node) { - if root == nil { - return - } - fmt.Printf("%d ", root.Val) - PreOrderTraversal(root.Left) - PreOrderTraversal(root.Right) -} - -// Left -> Root -> Right -func InOrderTraversal(root *Node) { - if root == nil { - return - } - InOrderTraversal(root.Left) - fmt.Printf("%d ", root.Val) - InOrderTraversal(root.Right) -} - -// Left -> Right -> Root -func PostOrderTraversal(root *Node) { - if root == nil { - return - } - PostOrderTraversal(root.Left) - PostOrderTraversal(root.Right) - fmt.Printf("%d ", root.Val) -} - -func PreOrderTraversalIterative(root *Node) { - if root == nil { - return - } - stack := []*Node{root} - for len(stack) > 0 { - node := stack[len(stack)-1] - stack = stack[:len(stack)-1] - fmt.Printf("%d ", node.Val) - // 厳密にpre-orderにするなら、まず右の子ノードを追加してから左の子ノードを追加する - if node.Right != nil { - stack = append(stack, node.Right) - } - if node.Left != nil { - stack = append(stack, node.Left) - } - } -} - -func InOrderTraversalIterative(root *Node) { - stack := []*Node{} - node := root - for len(stack) > 0 || node != nil { - for node != nil { - stack = append(stack, node) - node = node.Left - } - node = stack[len(stack)-1] - stack = stack[:len(stack)-1] - fmt.Printf("%d ", node.Val) - node = node.Right - } -} - -func PostOrderTraversalIterative1(root *Node) { - if root == nil { - return - } - stack := []*Node{root} - var lastVisited *Node - for len(stack) > 0 { - node := stack[len(stack)-1] - if node == nil { - stack = stack[:len(stack)-1] - continue - } - // 左右の子が処理済み、または子がない場合にノードを取り出す - if (node.Left == nil && node.Right == nil) || - (lastVisited != nil && - (lastVisited == node.Left || lastVisited == node.Right)) { - stack = stack[:len(stack)-1] - fmt.Printf("%d ", node.Val) - lastVisited = node - } else { - stack = append(stack, node.Right) - stack = append(stack, node.Left) - } - } -} - -func PostOrderTraversalIterative2(root *Node) { - if root == nil { - return - } - stack := []*Node{} - var lastVisited *Node - node := root - for len(stack) > 0 || node != nil { - if node != nil { - stack = append(stack, node) - node = node.Left - continue - } - peekNode := stack[len(stack)-1] - if peekNode.Right != nil && lastVisited != peekNode.Right { - node = peekNode.Right - } else { - fmt.Printf("%d ", peekNode.Val) - lastVisited = stack[len(stack)-1] - stack = stack[:len(stack)-1] - } - } -} - -func PostOrderTraversalIterative3(root *Node) { - if root == nil { - return - } - stack1 := []*Node{root} - stack2 := []*Node{} - for len(stack1) > 0 { - node := stack1[len(stack1)-1] - stack1 = stack1[:len(stack1)-1] - stack2 = append(stack2, node) - if node.Left != nil { - stack1 = append(stack1, node.Left) - } - if node.Right != nil { - stack1 = append(stack1, node.Right) - } - } - for len(stack2) > 0 { - node := stack2[len(stack2)-1] - stack2 = stack2[:len(stack2)-1] - fmt.Printf("%d ", node.Val) - } -} diff --git a/go/add_binary.go b/go/add_binary.go index 9ae2bbd..959efc0 100644 --- a/go/add_binary.go +++ b/go/add_binary.go @@ -3,27 +3,27 @@ package main import ( "slices" - "strconv" "strings" ) func addBinary(a string, b string) string { var reversed strings.Builder + maxLength := max(len(a), len(b)) carry := 0 - for i := 1; i <= len(a) || i <= len(b); i++ { + for i := 1; i <= maxLength; i++ { bitA, bitB := 0, 0 if i <= len(a) { - bitA, _ = strconv.Atoi(string(a[len(a)-i])) + bitA = int(a[len(a)-i] - '0') // a[len(a)-i]がbyte型なので'0'もbyte型と解釈される } if i <= len(b) { - bitB, _ = strconv.Atoi(string(b[len(b)-i])) + bitB = int(b[len(b)-i] - '0') } sum := bitA + bitB + carry carry = sum / 2 - reversed.WriteString(strconv.Itoa(sum % 2)) + reversed.WriteByte(byte(sum%2 + '0')) // sum%2がint型なので'0'もint型として扱われる } if carry == 1 { - reversed.WriteByte('1') + reversed.WriteByte(byte(carry + '0')) } result := []rune(reversed.String()) slices.Reverse(result) diff --git a/go/add_two_numbers.go b/go/add_two_numbers.go index b56315e..ce0e5dd 100644 --- a/go/add_two_numbers.go +++ b/go/add_two_numbers.go @@ -1,46 +1,23 @@ //lint:file-ignore U1000 Ignore all unused code package main -// dummyを使うことで最後に値0を持つノードを作らずに済む -// 単純にl1とl2の数を求めて足し算する方法だと、64個以上のノード数を持つリスト同士の足し算ができない -func addTwoNumbersIteratice(l1 *ListNode, l2 *ListNode) *ListNode { +func addTwoNumbers(l1 *ListNode, l2 *ListNode) *ListNode { dummy := new(ListNode) - current, node1, node2 := dummy, l1, l2 + node := dummy carry := 0 - for node1 != nil || node2 != nil || carry != 0 { + for l1 != nil || l2 != nil || carry != 0 { sum := carry - if node1 != nil { - sum += node1.Val - node1 = node1.Next + if l1 != nil { + sum += l1.Val + l1 = l1.Next } - if node2 != nil { - sum += node2.Val - node2 = node2.Next + if l2 != nil { + sum += l2.Val + l2 = l2.Next } - current.Next = &ListNode{sum % 10, nil} - current = current.Next + node.Next = &ListNode{Val: sum % 10} carry = sum / 10 + node = node.Next } return dummy.Next } - -func addTwoNumbersRecursive(l1 *ListNode, l2 *ListNode) *ListNode { - return addTwoNumbersHelper(l1, l2, 0) -} - -func addTwoNumbersHelper(l1 *ListNode, l2 *ListNode, carry int) *ListNode { - if l1 == nil && l2 == nil && carry == 0 { - return nil - } - sum := carry - if l1 != nil { - sum += l1.Val - l1 = l1.Next - } - if l2 != nil { - sum += l2.Val - l2 = l2.Next - } - next := addTwoNumbersHelper(l1, l2, sum/10) - return &ListNode{sum % 10, next} -} diff --git a/go/climbing_stairs.go b/go/climbing_stairs.go index 298af33..9b075cc 100644 --- a/go/climbing_stairs.go +++ b/go/climbing_stairs.go @@ -1,22 +1,22 @@ //lint:file-ignore U1000 Ignore all unused code package main -func climbStairsDP(n int) int { - prev, curr := 1, 1 - for i := 1; i < n; i++ { +func climbStairsFibonacci(n int) int { + prev, curr := 0, 1 + for i := 1; i <= n; i++ { prev, curr = curr, prev+curr } return curr } -func climbStairsmemorizedRecursion(n int) int { +func climbStairsDP(n int) int { m := make(map[int]int, n) return climbStairsHelper(n, m) } func climbStairsHelper(n int, m map[int]int) int { - if n == 0 || n == 1 { - return 1 + if n == 1 || n == 2 { + return n } if ways, ok := m[n]; ok { return ways diff --git a/go/combination_sum.go b/go/combination_sum.go index 8464a56..65c3316 100644 --- a/go/combination_sum.go +++ b/go/combination_sum.go @@ -1,73 +1,67 @@ //lint:file-ignore U1000 Ignore all unused code package main -import "slices" - -// 時間計算量: 両者とも最悪で指数時間かかるが、バックトラッキングの方が剪定の効果があり実行時間は短くなりやすい。 -// 空間計算量: DPはすべての部分和の組み合わせを記録するためメモリ使用量が多くなる。バックトラッキングは再帰の深さ分だけを保持するためメモリ使用量は少なくなる。 - -func combinationSumBacktrackingRecursion(candidates []int, target int) [][]int { - var combinations [][]int - var combination []int - var generate func(int, int) - generate = func(sum, currentIndex int) { - if sum > target { - return - } - if sum == target { - combinations = append(combinations, slices.Clone(combination)) - return - } - for i := currentIndex; i < len(candidates); i++ { - combination = append(combination, candidates[i]) - generate(sum+candidates[i], i) - combination = combination[:len(combination)-1] +func combinationSumDP(candidates []int, target int) [][]int { + combinationsGroups := make([][][]int, target+1) + combinationsGroups[0] = [][]int{{}} + for _, candidate := range candidates { + for i := candidate; i <= target; i++ { + for _, combination := range combinationsGroups[i-candidate] { + newCombination := append([]int{}, combination...) + newCombination = append(newCombination, candidate) + combinationsGroups[i] = append(combinationsGroups[i], newCombination) + } } } - generate(0, 0) - return combinations -} - -type combinationFrame struct { - combination []int - sum int - currentIndex int + return combinationsGroups[target] } func combinationSumBacktrackingIterative(candidates []int, target int) [][]int { - var combinations [][]int - stack := []combinationFrame{{[]int{}, 0, 0}} + combinations := [][]int{} + type state struct { + combination []int + sum int + index int + } + stack := []state{{[]int{}, 0, 0}} for len(stack) > 0 { - f := stack[len(stack)-1] + current := stack[len(stack)-1] stack = stack[:len(stack)-1] - if f.sum > target { - continue - } - if f.sum == target { - combinations = append(combinations, f.combination) + if current.sum == target { + combinations = append(combinations, current.combination) continue } - for i := f.currentIndex; i < len(candidates); i++ { - combination := slices.Clone(f.combination) - stack = append(stack, combinationFrame{append(combination, candidates[i]), f.sum + candidates[i], i}) + for i := current.index; i < len(candidates); i++ { + newSum := current.sum + candidates[i] + if newSum > target { + continue + } + newCombination := append([]int{}, current.combination...) + newCombination = append(newCombination, candidates[i]) + stack = append(stack, state{newCombination, newSum, i}) } } return combinations } -func combinationSumDP(candidates []int, target int) [][]int { - memo := make([][][]int, target+1) - memo[0] = [][]int{{}} - for _, candidate := range candidates { - for sum, combinations := range memo { - for _, combination := range combinations { - if sum+candidate > target { - continue - } - newCombination := append(slices.Clone(combination), candidate) - memo[sum+candidate] = append(memo[sum+candidate], newCombination) - } +func combinationSumBacktrackingRecursion(candidates []int, target int) [][]int { + var combinations [][]int + var combination []int + var generateCombinations func(int, int) + generateCombinations = func(currentIndex int, sum int) { + if sum == target { + combinations = append(combinations, append([]int{}, combination...)) + return + } + if sum > target { + return + } + for i := currentIndex; i < len(candidates); i++ { + combination = append(combination, candidates[i]) + generateCombinations(i, sum+candidates[i]) + combination = combination[:len(combination)-1] } } - return memo[target] + generateCombinations(0, 0) + return combinations } diff --git a/go/convert_sorted_array_to_binary_search_tree.go b/go/convert_sorted_array_to_binary_search_tree.go index b81b026..28ea874 100644 --- a/go/convert_sorted_array_to_binary_search_tree.go +++ b/go/convert_sorted_array_to_binary_search_tree.go @@ -1,142 +1,15 @@ //lint:file-ignore U1000 Ignore all unused code package main -import "container/list" - func sortedArrayToBST(nums []int) *TreeNode { if len(nums) == 0 { return nil } - midIndex := len(nums) / 2 - return &TreeNode{ - Val: nums[midIndex], - Left: sortedArrayToBST(nums[:midIndex]), - Right: sortedArrayToBST(nums[midIndex+1:]), - } -} - -func sortedArrayToBSTClosed(nums []int) *TreeNode { - if len(nums) == 0 { - return nil - } - return sortedArrayToBSTClosedHelper(nums, 0, len(nums)-1) -} -func sortedArrayToBSTClosedHelper(nums []int, left, right int) *TreeNode { - if left > right { - return nil - } - mid := left + (right-left)/2 - return &TreeNode{ - Val: nums[mid], - Left: sortedArrayToBSTClosedHelper(nums, left, mid-1), - Right: sortedArrayToBSTClosedHelper(nums, mid+1, right), - } -} - -func sortedArrayToBSTHalfOpen(nums []int) *TreeNode { - if len(nums) == 0 { - return nil - } - return sortedArrayToBSTHalfOpenHelper(nums, 0, len(nums)) -} - -func sortedArrayToBSTHalfOpenHelper(nums []int, left, right int) *TreeNode { - if left == right { - return nil - } - mid := left + (right-left)/2 + mid := len(nums) / 2 return &TreeNode{ Val: nums[mid], - Left: sortedArrayToBSTHalfOpenHelper(nums, left, mid), - Right: sortedArrayToBSTHalfOpenHelper(nums, mid+1, right), - } -} - -type bstElement struct { - root *TreeNode - left []int - right []int -} - -func sortedArrayToBSTDFS(nums []int) *TreeNode { - if len(nums) == 0 { - return nil - } - midIndex := len(nums) / 2 - root := &TreeNode{Val: nums[midIndex]} - stack := []bstElement{{root, nums[:midIndex], nums[midIndex+1:]}} - for len(stack) > 0 { - e := stack[len(stack)-1] - stack = stack[:len(stack)-1] - if len(e.left) > 0 { - leftMid := len(e.left) / 2 - e.root.Left = &TreeNode{Val: e.left[leftMid]} - stack = append(stack, bstElement{ - e.root.Left, e.left[:leftMid], e.left[leftMid+1:], - }) - } - if len(e.right) > 0 { - rightMid := len(e.right) / 2 - e.root.Right = &TreeNode{Val: e.right[rightMid]} - stack = append(stack, bstElement{ - e.root.Right, e.right[:rightMid], e.right[rightMid+1:], - }) - } - } - return root -} - -func sortedArrayToBSTBFS(nums []int) *TreeNode { - if len(nums) == 0 { - return nil - } - midIndex := len(nums) / 2 - root := &TreeNode{Val: nums[midIndex]} - queue := list.New() - queue.PushBack(bstElement{root, nums[:midIndex], nums[midIndex+1:]}) - for queue.Len() > 0 { - e := queue.Remove(queue.Front()).(bstElement) - if len(e.left) > 0 { - leftMid := len(e.left) / 2 - e.root.Left = &TreeNode{Val: e.left[leftMid]} - queue.PushBack(bstElement{ - e.root.Left, e.left[:leftMid], e.left[leftMid+1:], - }) - } - if len(e.right) > 0 { - rightMid := len(e.right) / 2 - e.root.Right = &TreeNode{Val: e.right[rightMid]} - queue.PushBack(bstElement{ - e.root.Right, e.right[:rightMid], e.right[rightMid+1:], - }) - } - } - return root -} - -func sortedArrayToBSTInorder(nums []int) *TreeNode { - root := dummyCompleteBinaryTree(0, len(nums)) - setValueInorder(root, nums) - return root -} - -func dummyCompleteBinaryTree(index, length int) *TreeNode { - if index >= length { - return nil - } - node := &TreeNode{} - node.Left = dummyCompleteBinaryTree(index*2+1, length) - node.Right = dummyCompleteBinaryTree(index*2+2, length) - return node -} - -func setValueInorder(root *TreeNode, nums []int) []int { - if root == nil || len(nums) == 0 { - return nums + Left: sortedArrayToBST(nums[:mid]), + Right: sortedArrayToBST(nums[mid+1:]), } - nums = setValueInorder(root.Left, nums) - root.Val = nums[0] - nums = setValueInorder(root.Right, nums[1:]) - return nums } diff --git a/go/counting_bits.go b/go/counting_bits.go index 3f642d3..e9e2c4d 100644 --- a/go/counting_bits.go +++ b/go/counting_bits.go @@ -1,10 +1,32 @@ //lint:file-ignore U1000 Ignore all unused code package main +// 2の冪乗の時はMSBのみが1になる +// それ以外の場合はMSB以外の部分はすでに前に計算していてメモ化されているのでそれを使えばいい func countBits(n int) []int { bitCounts := make([]int, n+1) + powerOfTwo := 1 for i := 1; i <= n; i++ { - bitCounts[i] = 1 + bitCounts[i&(i-1)] // i&(i-1) unset rightmost set bit + if i == powerOfTwo*2 { + powerOfTwo = i + } + bitCounts[i] = 1 + bitCounts[i-powerOfTwo] + } + return bitCounts +} + +// 内側のループはnを2で割り続けるのでlog n回 +// 外側のループはn回なので、全体でO(n log n)になる +func countBits2(n int) []int { + bitCounts := make([]int, n+1) + for i := 0; i <= n; i++ { + num := i + count := 0 + for num > 0 { + count += num % 2 + num /= 2 + } + bitCounts[i] = count } return bitCounts } diff --git a/go/first_unique_character_in_a_string.go b/go/first_unique_character_in_a_string.go index bb7ef1b..12f238c 100644 --- a/go/first_unique_character_in_a_string.go +++ b/go/first_unique_character_in_a_string.go @@ -2,12 +2,12 @@ package main func firstUniqChar(s string) int { - frequencies := make(map[rune]int) + frequency := make(map[rune]int) for _, r := range s { - frequencies[r]++ + frequency[r]++ } for i, r := range s { - if frequencies[r] == 1 { + if frequency[r] == 1 { return i } } @@ -15,12 +15,12 @@ func firstUniqChar(s string) int { } func firstUniqChar2(s string) int { - var frequencies [26]int + var frequency [26]int for _, r := range s { - frequencies[r-'a']++ + frequency[r-'a']++ } for i, r := range s { - if frequencies[r-'a'] == 1 { + if frequency[r-'a'] == 1 { return i } } diff --git a/go/generate_parentheses.go b/go/generate_parentheses.go index 83448cb..5bb6813 100644 --- a/go/generate_parentheses.go +++ b/go/generate_parentheses.go @@ -1,56 +1,55 @@ //lint:file-ignore U1000 Ignore all unused code package main -import "slices" - -func generateParenthesisRecursive(n int) []string { - var combinations []string - combination := make([]rune, 0, n*2) - var generate func(int, int) - generate = func(openNum, closeNum int) { - if openNum == n && closeNum == n { - combinations = append(combinations, string(combination)) - return +func generateParenthesisIterative(n int) []string { + var parentheses []string + type state struct { + parenthesis []byte + open int + closed int + } + stack := []state{{[]byte{}, 0, 0}} + for len(stack) > 0 { + current := stack[len(stack)-1] + stack = stack[:len(stack)-1] + if current.open == n && current.closed == n { + parentheses = append(parentheses, string(current.parenthesis)) + continue } - if openNum < n { - combination = append(combination, '(') - generate(openNum+1, closeNum) - combination = combination[:len(combination)-1] + if current.open < n { + newParenthesis := append([]byte{}, current.parenthesis...) + newParenthesis = append(newParenthesis, '(') + stack = append(stack, state{newParenthesis, current.open + 1, current.closed}) } - if closeNum < openNum { - combination = append(combination, ')') - generate(openNum, closeNum+1) - combination = combination[:len(combination)-1] + if current.open > current.closed { + newParenthesis := append([]byte{}, current.parenthesis...) + newParenthesis = append(newParenthesis, ')') + stack = append(stack, state{newParenthesis, current.open, current.closed + 1}) } } - generate(0, 0) - return combinations + return parentheses } -type frame struct { - combination []rune - openNum int - closeNum int -} - -func generateParenthesisIterative(n int) []string { - var combinations []string - stack := []frame{{[]rune{}, 0, 0}} - for len(stack) > 0 { - f := stack[len(stack)-1] - stack = stack[:len(stack)-1] - if f.openNum == n && f.closeNum == n { - combinations = append(combinations, string(f.combination)) - continue +func generateParenthesisRecursive(n int) []string { + var parentheses []string + parenthesis := make([]byte, 0, n*2) + var generate func(int, int) + generate = func(open int, closed int) { + if open == n && closed == n { + parentheses = append(parentheses, string(parenthesis)) + return } - if f.openNum < n { - newCombination := append(slices.Clone(f.combination), '(') - stack = append(stack, frame{newCombination, f.openNum + 1, f.closeNum}) + if open < n { + parenthesis = append(parenthesis, '(') + generate(open+1, closed) + parenthesis = parenthesis[:len(parenthesis)-1] } - if f.closeNum < f.openNum { - newCombination := append(slices.Clone(f.combination), ')') - stack = append(stack, frame{newCombination, f.openNum, f.closeNum + 1}) + if open > closed { + parenthesis = append(parenthesis, ')') + generate(open, closed+1) + parenthesis = parenthesis[:len(parenthesis)-1] } } - return combinations + generate(0, 0) + return parentheses } diff --git a/go/group_anagrams.go b/go/group_anagrams.go index cccea09..6ffaf84 100644 --- a/go/group_anagrams.go +++ b/go/group_anagrams.go @@ -2,7 +2,7 @@ package main func groupAnagrams(strs []string) [][]string { - anagramsMap := make(map[[26]int][]string, len(strs)) + anagramsMap := make(map[[26]int][]string) for _, word := range strs { var frequencies [26]int for _, r := range word { @@ -10,9 +10,12 @@ func groupAnagrams(strs []string) [][]string { } anagramsMap[frequencies] = append(anagramsMap[frequencies], word) } - anagrams := make([][]string, 0, len(anagramsMap)) - for _, group := range anagramsMap { - anagrams = append(anagrams, group) + + anagrams := make([][]string, len(anagramsMap)) + i := 0 + for _, words := range anagramsMap { + anagrams[i] = words + i++ } return anagrams } diff --git a/go/implement_queue_using_stacks.go b/go/implement_queue_using_stacks.go index 9d45b05..fa2c849 100644 --- a/go/implement_queue_using_stacks.go +++ b/go/implement_queue_using_stacks.go @@ -22,12 +22,11 @@ func (q *MyQueue) Pop() int { } func (q *MyQueue) Peek() int { - if len(q.popStack) > 0 { - return q.popStack[len(q.popStack)-1] - } - for len(q.pushStack) > 0 { - q.popStack = append(q.popStack, q.pushStack[len(q.pushStack)-1]) - q.pushStack = q.pushStack[:len(q.pushStack)-1] + if len(q.popStack) == 0 { + for len(q.pushStack) > 0 { + q.popStack = append(q.popStack, q.pushStack[len(q.pushStack)-1]) + q.pushStack = q.pushStack[:len(q.pushStack)-1] + } } return q.popStack[len(q.popStack)-1] } diff --git a/go/is_subsequence.go b/go/is_subsequence.go index 211bb09..306c2cb 100644 --- a/go/is_subsequence.go +++ b/go/is_subsequence.go @@ -2,23 +2,12 @@ package main func isSubsequence(s string, t string) bool { - sIndex, tIndex := 0, 0 - for sIndex < len(s) && tIndex < len(t) { - if s[sIndex] == t[tIndex] { - sIndex++ + i, j := 0, 0 + for i < len(s) && j < len(t) { + if s[i] == t[j] { + i++ } - tIndex++ + j++ } - return sIndex == len(s) -} - -func isSubsequence2(s string, t string) bool { - sIndex := 0 - runeS := []rune(s) - for _, r := range t { - if sIndex < len(runeS) && r == runeS[sIndex] { - sIndex++ - } - } - return sIndex == len(runeS) + return i == len(s) } diff --git a/go/linked_list_cycle.go b/go/linked_list_cycle.go index 9ab246c..e8cd286 100644 --- a/go/linked_list_cycle.go +++ b/go/linked_list_cycle.go @@ -2,13 +2,13 @@ package main func hasCycleMap(head *ListNode) bool { - visited := make(map[*ListNode]struct{}) + seen := make(map[*ListNode]struct{}) node := head for node != nil { - if _, ok := visited[node]; ok { + if _, ok := seen[node]; ok { return true } - visited[node] = struct{}{} + seen[node] = struct{}{} node = node.Next } return false diff --git a/go/longest_common_prefix.go b/go/longest_common_prefix.go index c87dd75..6efdfa1 100644 --- a/go/longest_common_prefix.go +++ b/go/longest_common_prefix.go @@ -1,73 +1,18 @@ //lint:file-ignore U1000 Ignore all unused code package main -import ( - "strings" -) +import "sort" func longestCommonPrefix(strs []string) string { - var prefix strings.Builder - for i := 0; ; i++ { - var curr rune - for _, word := range strs { - if i >= len(word) { - return prefix.String() - } - if curr == 0 { - curr = rune(word[i]) - continue - } - if rune(word[i]) != curr { - return prefix.String() - } - } - prefix.WriteRune(curr) + if len(strs) == 1 { + return strs[0] } -} - -/* -Trie -*/ -type Trie struct { - root *Node -} - -type Node struct { - children map[rune]*Node - isWordEnd bool -} - -func NewTrie() *Trie { - return &Trie{root: &Node{children: make(map[rune]*Node)}} -} -func (t *Trie) Insert(word string) { - node := t.root - for _, r := range word { - if _, ok := node.children[r]; !ok { - node.children[r] = &Node{children: make(map[rune]*Node)} + sort.Strings(strs) + for i := range strs[0] { + if strs[0][i] != strs[len(strs)-1][i] { + return strs[0][:i] } - node = node.children[r] - } - node.isWordEnd = true -} - -func (t *Trie) Prefix() string { - var prefix strings.Builder - node := t.root - for len(node.children) == 1 && !node.isWordEnd { - for r, child := range node.children { - prefix.WriteRune(r) - node = child // ここでnodeをchildで更新してもrangeは評価済みなのでループが続くことはない - } - } - return prefix.String() -} - -func longestCommonPrefixTrie(strs []string) string { - t := NewTrie() - for _, word := range strs { - t.Insert(word) } - return t.Prefix() + return strs[0] } diff --git a/go/longest_substring_without_repeating_characters.go b/go/longest_substring_without_repeating_characters.go index b2412b2..d72e441 100644 --- a/go/longest_substring_without_repeating_characters.go +++ b/go/longest_substring_without_repeating_characters.go @@ -1,21 +1,14 @@ //lint:file-ignore U1000 Ignore all unused code package main -// 各文字においてその段階でのSubstringの長さを求め、maxLengthを更新 -// rightは現在の文字のインデックスを表す -// 常にleftとrightの間には重複が存在しないようにleftを管理する -// なのでrightを更新した際にleftとrightの間を見て。rightと重複する文字が存在するならその文字を含まないようにleftを更新する -// そうすることで常にleftとrightの間には重複が存在しないことを保証できるので、rightにおけるSubstringの長さはright-left+1で求めることができる func lengthOfLongestSubstring(s string) int { - maxLength := 0 - left := 0 - seen := make(map[rune]int) + maxLength, left, seen := 0, 0, make(map[rune]int) for right, r := range s { - if i, ok := seen[r]; ok && left <= i { - left = i + 1 + if lastIndex, ok := seen[r]; ok && lastIndex >= left { + left = lastIndex + 1 } - maxLength = max(maxLength, right-left+1) seen[r] = right + maxLength = max(maxLength, right-left+1) } return maxLength } diff --git a/go/maximum_depth_of_binary_tree.go b/go/maximum_depth_of_binary_tree.go index 999afb8..508d913 100644 --- a/go/maximum_depth_of_binary_tree.go +++ b/go/maximum_depth_of_binary_tree.go @@ -1,11 +1,6 @@ //lint:file-ignore U1000 Ignore all unused code package main -type entry struct { - node *TreeNode - depth int -} - /* 再帰 */ @@ -27,7 +22,7 @@ func maxDepthRecursive(root *TreeNode) int { DFS */ // pushする前にnilノードを弾く -func maxDepthIterativePreOrder1(root *TreeNode) int { +func maxDepthIterativeDFS(root *TreeNode) int { if root == nil { return 0 } @@ -37,18 +32,18 @@ func maxDepthIterativePreOrder1(root *TreeNode) int { e := stack[len(stack)-1] stack = stack[:len(stack)-1] maximum = max(maximum, e.depth) - if e.node.Right != nil { - stack = append(stack, entry{e.node.Right, e.depth + 1}) - } if e.node.Left != nil { stack = append(stack, entry{e.node.Left, e.depth + 1}) } + if e.node.Right != nil { + stack = append(stack, entry{e.node.Right, e.depth + 1}) + } } return maximum } // popした後にnilノードを弾く -func maxDepthIterativePreOrder2(root *TreeNode) int { +func maxDepthIterativeDFS2(root *TreeNode) int { maximum := 0 stack := []entry{{root, 1}} for len(stack) > 0 { @@ -58,39 +53,62 @@ func maxDepthIterativePreOrder2(root *TreeNode) int { continue } maximum = max(maximum, e.depth) - stack = append(stack, entry{e.node.Right, e.depth + 1}) stack = append(stack, entry{e.node.Left, e.depth + 1}) + stack = append(stack, entry{e.node.Right, e.depth + 1}) } return maximum } -func maxDepthIterativePostOrder(root *TreeNode) int { - if root == nil { - return 0 - } +// 帰りがけにdepthを更新 +func maxDepthIterativeDFS3(root *TreeNode) int { maximum := 0 - stack := []entry{{root, 1}} - var lastVisited *TreeNode + stack := []*entry2{ + { + node: root, + isPreorder: true, + depth: &maximum, + leftDepth: new(int), + rightDepth: new(int), + }, + } for len(stack) > 0 { e := stack[len(stack)-1] - node := e.node - depth := e.depth - if node == nil { - stack = stack[:len(stack)-1] - continue - } - if (node.Left == nil && node.Right == nil) || (lastVisited != nil && (lastVisited == node.Left || lastVisited == node.Right)) { - stack = stack[:len(stack)-1] - maximum = max(maximum, depth) - lastVisited = node + stack = stack[:len(stack)-1] + if e.isPreorder { + if e.node == nil { + continue + } + e.isPreorder = false + stack = append(stack, e) + stack = append(stack, &entry2{ + node: e.node.Left, + isPreorder: true, + depth: e.leftDepth, + leftDepth: new(int), + rightDepth: new(int), + }) + stack = append(stack, &entry2{ + node: e.node.Right, + isPreorder: true, + depth: e.rightDepth, + leftDepth: new(int), + rightDepth: new(int), + }) } else { - stack = append(stack, entry{node.Right, depth + 1}) - stack = append(stack, entry{node.Left, depth + 1}) + *e.depth = max(*e.leftDepth, *e.rightDepth) + 1 } } return maximum } +type entry2 struct { + node *TreeNode + isPreorder bool + depth *int + leftDepth *int + rightDepth *int +} + /* BFS */ @@ -132,6 +150,11 @@ func maxDepthIterativeBFS2(root *TreeNode) int { return maximum } +type entry struct { + node *TreeNode + depth int +} + // depthを保持せずに処理する func maxDepthIterativeBFS3(root *TreeNode) int { if root == nil { diff --git a/go/merge_two_sorted_lists.go b/go/merge_two_sorted_lists.go index 27f9c79..d74998c 100644 --- a/go/merge_two_sorted_lists.go +++ b/go/merge_two_sorted_lists.go @@ -1,7 +1,7 @@ //lint:file-ignore U1000 Ignore all unused code package main -func mergeTwoListsIterative(list1 *ListNode, list2 *ListNode) *ListNode { +func mergeTwoLists(list1 *ListNode, list2 *ListNode) *ListNode { dummy := &ListNode{} tail := dummy for list1 != nil && list2 != nil { @@ -20,19 +20,3 @@ func mergeTwoListsIterative(list1 *ListNode, list2 *ListNode) *ListNode { } return dummy.Next } - -func mergeTwoListsRecursive(list1 *ListNode, list2 *ListNode) *ListNode { - if list1 == nil { - return list2 - } - if list2 == nil { - return list1 - } - if list1.Val < list2.Val { - list1.Next = mergeTwoListsRecursive(list1.Next, list2) - return list1 - } else { - list2.Next = mergeTwoListsRecursive(list1, list2.Next) - return list2 - } -} diff --git a/go/middle_of_the_linked_list.go b/go/middle_of_the_linked_list.go index 9dc7ade..3ec2eb4 100644 --- a/go/middle_of_the_linked_list.go +++ b/go/middle_of_the_linked_list.go @@ -2,17 +2,17 @@ package main func middleNode(head *ListNode) *ListNode { - length := 0 + count := 0 node := head for node != nil { - length++ node = node.Next + count++ } - node = head - for i := 0; i < length/2; i++ { - node = node.Next + middle := head + for i := 0; i < count/2; i++ { + middle = middle.Next } - return node + return middle } func middleNode2(head *ListNode) *ListNode { diff --git a/go/missing_number.go b/go/missing_number.go index f867acd..f5ea6e3 100644 --- a/go/missing_number.go +++ b/go/missing_number.go @@ -2,17 +2,18 @@ package main func missingNumber(nums []int) int { - var difference int - for i, n := range nums { - difference += i + 1 - n + sum := 0 + for _, n := range nums { + sum += n } - return difference + l := len(nums) + return l*(l+1)/2 - sum } func missingNumber2(nums []int) int { - var total int - for _, n := range nums { - total += n + res := len(nums) + for i, n := range nums { + res += i - n } - return (1+len(nums))*len(nums)/2 - total + return res } diff --git a/go/move_zeroes.go b/go/move_zeroes.go index f203edc..e4089a3 100644 --- a/go/move_zeroes.go +++ b/go/move_zeroes.go @@ -2,12 +2,10 @@ package main func moveZeroes(nums []int) { - zeroIndex := 0 - for i, n := range nums { - if n == 0 { - continue + for nonZero, cur := 0, 0; cur < len(nums); cur++ { + if nums[cur] != 0 { + nums[nonZero], nums[cur] = nums[cur], nums[nonZero] + nonZero++ } - nums[zeroIndex], nums[i] = nums[i], nums[zeroIndex] - zeroIndex++ } } diff --git a/go/next_permutation.go b/go/next_permutation.go index 817baab..cd62551 100644 --- a/go/next_permutation.go +++ b/go/next_permutation.go @@ -1,27 +1,26 @@ //lint:file-ignore U1000 Ignore all unused code package main -import "sort" - -// [1, 3, 5, 4, 2]という入力があるとき -// 3 < 5なので、3を変える必要がある -// 3より大きい最小の値は4なので、3と4を入れ替えると -// [1, 4, 5, 3, 2]となる -// 3以降の要素を逆順にすると、 -// next permutationの[1, 4, 2, 3, 5]となる。 -// nums[sortedUntil] >= nums[sortedUntil+1]は>ではなく、>=でないと -// [1, 1]などではOut of indexになる func nextPermutation(nums []int) { - sortedUntil := len(nums) - 2 // len(nums) <= 1だとsortedUntil=-1になる点に注意 - for sortedUntil >= 0 && nums[sortedUntil] >= nums[sortedUntil+1] { - sortedUntil-- + if len(nums) < 2 { + return + } + i := len(nums) - 2 + for i >= 0 && nums[i] >= nums[i+1] { + i-- } - if sortedUntil >= 0 { - swapTarget := len(nums) - 1 - for nums[sortedUntil] >= nums[swapTarget] { - swapTarget-- + if i >= 0 { + j := len(nums) - 1 + for nums[j] <= nums[i] { + j-- } - nums[sortedUntil], nums[swapTarget] = nums[swapTarget], nums[sortedUntil] + nums[i], nums[j] = nums[j], nums[i] + } + reverse(nums[i+1:]) +} + +func reverse(nums []int) { + for i, j := 0, len(nums)-1; i < j; i, j = i+1, j-1 { + nums[i], nums[j] = nums[j], nums[i] } - sort.Ints(nums[sortedUntil+1:]) // sortを使うとO(n log n)になるので、別途reverse関数を作るのもあり } diff --git a/go/number_of_1bits.go b/go/number_of_1bits.go index 55f364a..94bc4e2 100644 --- a/go/number_of_1bits.go +++ b/go/number_of_1bits.go @@ -1,11 +1,29 @@ //lint:file-ignore U1000 Ignore all unused code package main +func hammingWeightNaive1(n int) int { + count := 0 + for n > 0 { + count += n % 2 + n /= 2 + } + return count +} + +func hammingWeightNaive2(n int) int { + count := 0 + for n != 0 { + count += n & 1 + n >>= 1 + } + return count +} + func hammingWeightBitmanipulation(n int) int { count := 0 for n > 0 { n &= n - 1 - count++ + count += 1 } return count } @@ -13,8 +31,8 @@ func hammingWeightBitmanipulation(n int) int { func hammingWeight2Complement(n int) int { count := 0 for n > 0 { - n -= n & -n - count++ + n -= (n & -n) + count += 1 } return count } diff --git a/go/palindrome_linked_list.go b/go/palindrome_linked_list.go index 0708ad7..ebea2ab 100644 --- a/go/palindrome_linked_list.go +++ b/go/palindrome_linked_list.go @@ -2,19 +2,27 @@ package main func isPalindromeLinkedList(head *ListNode) bool { - var values []int - node := head - for node != nil { - values = append(values, node.Val) - node = node.Next + slow, fast := head, head + var rev *ListNode + + for fast != nil && fast.Next != nil { + fast = fast.Next.Next + tmp := slow.Next + slow.Next = rev + rev = slow + slow = tmp + } + + if fast != nil { + slow = slow.Next } - left, right := 0, len(values)-1 - for left < right { - if values[left] != values[right] { + + for slow != nil { + if slow.Val != rev.Val { return false } - left++ - right-- + slow, rev = slow.Next, rev.Next } + return true } diff --git a/go/palindrome_number.go b/go/palindrome_number.go index 4493ec7..a9285c8 100644 --- a/go/palindrome_number.go +++ b/go/palindrome_number.go @@ -1,12 +1,15 @@ //lint:file-ignore U1000 Ignore all unused code package main -func isPalindromeNumbver(x int) bool { - reversed := 0 - tmp := x - for tmp > 0 { - reversed = reversed*10 + tmp%10 - tmp /= 10 +func isPalindromeNumber(x int) bool { + if x < 0 { + return false } - return x == reversed + + original, reversed := x, 0 + for x > 0 { + reversed = reversed*10 + x%10 + x /= 10 + } + return original == reversed } diff --git a/go/permutations.go b/go/permutations.go index 6970057..e3e553d 100644 --- a/go/permutations.go +++ b/go/permutations.go @@ -1,95 +1,81 @@ //lint:file-ignore U1000 Ignore all unused code package main -import ( - "maps" - "slices" - "sort" -) +import "sort" -// 辞書順に生成していく -// [1, 2, 4, 3]の次は[1, 3, 2, 4] -// まず後ろから昇順になっている箇所を探す(2) -// そしてまた後ろから2までで、2よりも大きい一番小さい数を探す(3) -// そしてその2つを入れ替える([1, 3, 4, 2]) -// そしてその後ろの部分を逆順にする([1, 3, 2, 4]) func permuteLexicographically(nums []int) [][]int { + sort.Ints(nums) var permutations [][]int - permutation := slices.Clone(nums) - sort.Ints(permutation) for { - newPermutation := slices.Clone(permutation) - permutations = append(permutations, newPermutation) - sortedUntil := len(permutation) - 2 - for sortedUntil >= 0 && permutation[sortedUntil] > permutation[sortedUntil+1] { - sortedUntil-- + permutations = append(permutations, append([]int{}, nums...)) + i := len(nums) - 2 + for i >= 0 && nums[i] >= nums[i+1] { + i-- } - if sortedUntil < 0 { + if i < 0 { break } - swapTarget := len(permutation) - 1 - for permutation[sortedUntil] > permutation[swapTarget] { - swapTarget-- + j := len(nums) - 1 + for nums[j] <= nums[i] { + j-- } - permutation[sortedUntil], permutation[swapTarget] = permutation[swapTarget], permutation[sortedUntil] - sort.Ints(permutation[sortedUntil+1:]) + nums[i], nums[j] = nums[j], nums[i] + reverse(nums[i+1:]) } return permutations } -func permuteBacktrackingRecursion(nums []int) [][]int { +func permuteBacktrackingIterative(nums []int) [][]int { var permutations [][]int - permutation := make([]int, 0, len(nums)) - inUse := make(map[int]struct{}, len(nums)) - var generate func() - generate = func() { - if len(inUse) == len(nums) { - newPermutation := slices.Clone(permutation) - permutations = append(permutations, newPermutation) - return + type state struct { + permutation []int + inUse map[int]struct{} + } + stack := []state{{[]int{}, make(map[int]struct{})}} + for len(stack) > 0 { + current := stack[len(stack)-1] + stack = stack[:len(stack)-1] + if len(current.permutation) == len(nums) { + permutations = append(permutations, current.permutation) + continue } for _, n := range nums { - if _, ok := inUse[n]; ok { + if _, ok := current.inUse[n]; ok { continue } - permutation = append(permutation, n) - inUse[n] = struct{}{} - generate() - permutation = permutation[:len(permutation)-1] - delete(inUse, n) + newPermutation := append([]int{}, current.permutation...) + newPermutation = append(newPermutation, n) + newInUse := make(map[int]struct{}) + for k, v := range current.inUse { + newInUse[k] = v + } + newInUse[n] = struct{}{} + stack = append(stack, state{newPermutation, newInUse}) } } - generate() return permutations } -type permutationFrame struct { - permutation []int - inUse map[int]struct{} -} - -func permuteBacktrackingIterative(nums []int) [][]int { +func permuteBacktrackingRecursion(nums []int) [][]int { var permutations [][]int permutation := make([]int, 0, len(nums)) - inUse := make(map[int]struct{}, len(nums)) - stack := []permutationFrame{{permutation, inUse}} - for len(stack) > 0 { - f := stack[len(stack)-1] - stack = stack[:len(stack)-1] - if len(f.permutation) == len(nums) { - permutations = append(permutations, f.permutation) - continue + inUse := make(map[int]struct{}) + var generate func() + generate = func() { + if len(permutation) == len(nums) { + permutations = append(permutations, append([]int{}, permutation...)) + return } for _, n := range nums { - if _, ok := f.inUse[n]; ok { - continue + if _, ok := inUse[n]; !ok { + permutation = append(permutation, n) + inUse[n] = struct{}{} + generate() + permutation = permutation[:len(permutation)-1] + delete(inUse, n) } - newPermutation := slices.Clone(f.permutation) - newPermutation = append(newPermutation, n) - newInUse := maps.Clone(f.inUse) - newInUse[n] = struct{}{} - stack = append(stack, permutationFrame{newPermutation, newInUse}) } } + generate() return permutations } diff --git a/go/reverse_linked_list.go b/go/reverse_linked_list.go index 7559393..0bd0747 100644 --- a/go/reverse_linked_list.go +++ b/go/reverse_linked_list.go @@ -2,23 +2,21 @@ package main func reverseListIterative(head *ListNode) *ListNode { - var prev *ListNode curr := head + var prev *ListNode for curr != nil { - next := curr.Next - curr.Next = prev - prev, curr = curr, next + prev, curr, curr.Next = curr, curr.Next, prev } return prev } func reverseListRecurssive(head *ListNode) *ListNode { - if head == nil || head.Next == nil { - return head + curr := head + if curr == nil || curr.Next == nil { + return curr } - curr, next := head, head.Next - reversed := reverseListRecurssive(next) - next.Next = curr - curr.Next = nil - return reversed + next := curr.Next + reversedHead := reverseListRecurssive(next) + curr.Next, next.Next = nil, curr + return reversedHead } diff --git a/go/roman_to_integer.go b/go/roman_to_integer.go index 43ac89c..9cb10d0 100644 --- a/go/roman_to_integer.go +++ b/go/roman_to_integer.go @@ -2,6 +2,37 @@ package main func romanToInt(s string) int { + symbolToInt := map[rune]int{ + 'I': 1, + 'V': 5, + 'X': 10, + 'L': 50, + 'C': 100, + 'D': 500, + 'M': 1000, + } + total := 0 + runeS := []rune(s) + currIndex, nextIndex := 0, 1 + for currIndex < len(runeS) && nextIndex < len(runeS) { + curr, next := runeS[currIndex], runeS[nextIndex] + if (curr == 'I' && (next == 'V' || next == 'X')) || + (curr == 'X' && (next == 'L' || next == 'C')) || + (curr == 'C' && (next == 'D' || next == 'M')) { + total += symbolToInt[next] - symbolToInt[curr] + currIndex, nextIndex = currIndex+2, nextIndex+2 + continue + } + total += symbolToInt[curr] + currIndex, nextIndex = currIndex+1, nextIndex+1 + } + if currIndex < len(runeS) { + total += symbolToInt[runeS[currIndex]] + } + return total +} + +func romanToInt2(s string) int { symbolToInt := map[rune]int{ 'I': 1, 'V': 5, diff --git a/go/search_in_rotated_sorted_array.go b/go/search_in_rotated_sorted_array.go index 345120e..c8dac72 100644 --- a/go/search_in_rotated_sorted_array.go +++ b/go/search_in_rotated_sorted_array.go @@ -1,63 +1,48 @@ //lint:file-ignore U1000 Ignore all unused code package main -// 半閉区画ではleft~right-1が未探索で、rightは常に探索済み -// そのため、for文の条件はleft < rightとなる(left=1, right=2のときmid=1となり、未探索のleftのみが探索の対象となる) -func searchHalfClosed(nums []int, target int) int { - left, right := 0, len(nums) - for left < right { +func searchClosed(nums []int, target int) int { + left, right := 0, len(nums)-1 + for left <= right { mid := left + (right-left)/2 - if nums[mid] == target { + if target == nums[mid] { return mid } - // ここでは左側がソートされているかを判定しているが - // searchClosedとは違ってsearchHalfClosedの場合は - // nums[left] < nums[mid]でも、nums[left] <= nums[mid]でも問題なく動作する - // なぜならsearchHalfClosedでは、nums内にtargetが存在する場合において - // left == mid && nums[mid] != targetになることはないので - // (left==midになるにはlen(nums)==1である必要がある) - // left == midになってもバグらないためである - // 単にたまたまバグらないだけで、意味的にnums[left] <= nums[mid]の方が適切 if nums[left] <= nums[mid] { - if nums[left] <= target && target < nums[mid] { // leftがtargetの可能性もあるので<=とする - right = mid + if nums[left] <= target && target < nums[mid] { + right = mid - 1 } else { left = mid + 1 } } else { - if nums[mid] < target && target <= nums[right-1] { // rightは最初は範囲外なので-1をする。その場合right-1は未探索なので<=とする + if nums[mid] < target && target <= nums[right] { left = mid + 1 } else { - right = mid + right = mid - 1 } } } return -1 } -func searchClosed(nums []int, target int) int { - left, right := 0, len(nums)-1 - for left <= right { +func searchHalfClosed(nums []int, target int) int { + left, right := 0, len(nums) + for left < right { mid := left + (right-left)/2 - if nums[mid] == target { + if target == nums[mid] { return mid } - // ここでは左側がソートされているかを判定しているが - // nums[left] < nums[mid]としてしまうと - // left == midになるとき(例えばnums=[3, 1]) - // 左側がソートされていないと判断されて - // 誤った条件分岐に入ってしまう if nums[left] <= nums[mid] { if nums[left] <= target && target < nums[mid] { - right = mid - 1 + right = mid } else { left = mid + 1 } } else { - if nums[mid] < target && target <= nums[right] { + if nums[mid] < target && target <= nums[right-1] { left = mid + 1 } else { - right = mid - 1 + right = mid } } } diff --git a/go/search_insert_position.go b/go/search_insert_position.go index db36052..9fdfb80 100644 --- a/go/search_insert_position.go +++ b/go/search_insert_position.go @@ -1,12 +1,7 @@ //lint:file-ignore U1000 Ignore all unused code package main -// 長さ1のnums={1}があるとき、 -// left=0, right=1, mid=0になる。 -// target=2のとき、ループを抜ける直前にleft=mid+1されるので、ループを抜けたらleftを返せばいい。 -// target=0のとき、left=0のままなので、ループを抜けたらleftを返せばい(-1を返すわけではないことに注意。targetはleftよりも小さく、leftの左の値よりも大きいわけなので、leftを返せば良い) - -func searchInsertHalfClosed(nums []int, target int) int { +func searchInsert(nums []int, target int) int { left, right := 0, len(nums) for left < right { mid := left + (right-left)/2 @@ -18,19 +13,3 @@ func searchInsertHalfClosed(nums []int, target int) int { } return left } - -func searchInsertClosed(nums []int, target int) int { - left, right := 0, len(nums)-1 - for left <= right { - mid := left + (right-left)/2 - if nums[mid] == target { - return mid - } - if nums[mid] > target { - right = mid - 1 - } else { - left = mid + 1 - } - } - return left -} diff --git a/go/single_number.go b/go/single_number.go index 5365be6..86b2e65 100644 --- a/go/single_number.go +++ b/go/single_number.go @@ -2,9 +2,9 @@ package main func singleNumber(nums []int) int { - singleNum := 0 + res := 0 for _, n := range nums { - singleNum ^= n + res ^= n } - return singleNum + return res } diff --git a/go/string_to_integer_atoi.go b/go/string_to_integer_atoi.go index 036c89b..0151174 100644 --- a/go/string_to_integer_atoi.go +++ b/go/string_to_integer_atoi.go @@ -1,39 +1,38 @@ //lint:file-ignore U1000 Ignore all unused code package main -import ( - "math" - "unicode" -) +import "math" func myAtoi(s string) int { + const ( + intMax = int(math.MaxInt32) + intMin = int(math.MinInt32) + ) + i := 0 for i < len(s) && s[i] == ' ' { i++ } + sign := 1 - if i < len(s) { - if s[i] == '+' { - i++ - } else if s[i] == '-' { + if i < len(s) && (s[i] == '-' || s[i] == '+') { + if s[i] == '-' { sign = -1 - i++ } + i++ } - n := 0 - for i < len(s) && unicode.IsDigit(rune(s[i])) { + + num := 0 + for i < len(s) && '0' <= s[i] && s[i] <= '9' { digit := int(s[i] - '0') - if sign == 1 { - if n > math.MaxInt32/10 || (math.MaxInt32-n*10) <= digit { - return math.MaxInt32 - } - } else { - if -n < math.MinInt32/10 || (math.MinInt32- -n*10) >= -digit { - return math.MinInt32 - } + if sign == 1 && (num > intMax/10 || num == intMax/10 && digit >= intMax%10) { + return intMax + } + if sign == -1 && (-num < intMin/10 || -num == intMin/10 && -digit <= intMin%10) { + return intMin } - n = n*10 + digit + num = num*10 + digit i++ } - return n * sign + return sign * num } diff --git a/go/symmetric_tree.go b/go/symmetric_tree.go index cd9bbaf..42c1cd2 100644 --- a/go/symmetric_tree.go +++ b/go/symmetric_tree.go @@ -3,55 +3,49 @@ package main import "container/list" -func isSymmetric(root *TreeNode) bool { - return isSymmetricHelper(root.Left, root.Right) +func isSymmetricRecursive(root *TreeNode) bool { + if root == nil { + return true + } + return isMirror(root.Left, root.Right) } -func isSymmetricHelper(left, right *TreeNode) bool { - if left == nil && right == nil { +func isMirror(t1, t2 *TreeNode) bool { + if t1 == nil && t2 == nil { return true } - if left == nil || right == nil || left.Val != right.Val { + if t1 == nil || t2 == nil { return false } - return isSymmetricHelper(left.Left, right.Right) && isSymmetricHelper(left.Right, right.Left) + return (t1.Val == t2.Val) && + isMirror(t1.Left, t2.Right) && isMirror(t1.Right, t2.Left) } -type nodePair struct { - left *TreeNode - right *TreeNode -} +func isSymmetricIterative(root *TreeNode) bool { + if root == nil { + return true + } -func isSymmetricBFS(root *TreeNode) bool { queue := list.New() - queue.PushBack(nodePair{root.Left, root.Right}) + queue.PushBack(root.Left) + queue.PushBack(root.Right) + for queue.Len() > 0 { - f := queue.Remove(queue.Front()).(nodePair) - if f.left == nil && f.right == nil { - continue - } - if f.left == nil || f.right == nil || f.left.Val != f.right.Val { - return false - } - queue.PushBack(nodePair{f.left.Left, f.right.Right}) - queue.PushBack(nodePair{f.left.Right, f.right.Left}) - } - return true -} + t1 := queue.Remove(queue.Front()).(*TreeNode) + t2 := queue.Remove(queue.Front()).(*TreeNode) -func isSymmetricDFS(root *TreeNode) bool { - stack := []nodePair{{root.Left, root.Right}} - for len(stack) > 0 { - f := stack[len(stack)-1] - stack = stack[:len(stack)-1] - if f.left == nil && f.right == nil { + if t1 == nil && t2 == nil { continue } - if f.left == nil || f.right == nil || f.left.Val != f.right.Val { + if t1 == nil || t2 == nil || t1.Val != t2.Val { return false } - stack = append(stack, nodePair{f.left.Left, f.right.Right}) - stack = append(stack, nodePair{f.left.Right, f.right.Left}) + + queue.PushBack(t1.Left) + queue.PushBack(t2.Right) + queue.PushBack(t1.Right) + queue.PushBack(t2.Left) } + return true } diff --git a/go/top_k_frequent_elements.go b/go/top_k_frequent_elements.go index 64fa3e6..47369ca 100644 --- a/go/top_k_frequent_elements.go +++ b/go/top_k_frequent_elements.go @@ -7,14 +7,19 @@ import ( "sort" ) +type Element struct { + num int + count int +} + func topKFrequentBucketSort(nums []int, k int) []int { - frequencies := make(map[int]int, len(nums)) - for _, n := range nums { - frequencies[n]++ + frequency := make(map[int]int) + for _, num := range nums { + frequency[num]++ } countToNum := make([][]int, len(nums)+1) - for n, count := range frequencies { - countToNum[count] = append(countToNum[count], n) + for num, count := range frequency { + countToNum[count] = append(countToNum[count], num) } topK := make([]int, 0, k) for i := len(countToNum) - 1; i >= 0 && len(topK) < k; i-- { @@ -23,51 +28,72 @@ func topKFrequentBucketSort(nums []int, k int) []int { return topK } -type element struct { - num int - count int -} - -// see https://ja.wikipedia.org/wiki/クイックセレクト func topKFrequentQuickselect(nums []int, k int) []int { - frequencies := make(map[int]int, len(nums)) - for _, n := range nums { - frequencies[n]++ + frequency := make(map[int]int) + for _, num := range nums { + frequency[num]++ } - elements := make([]element, 0, len(frequencies)) - for n, count := range frequencies { - elements = append(elements, element{n, count}) + elements := make([]Element, 0, len(frequency)) + for num, count := range frequency { + elements = append(elements, Element{num: num, count: count}) } - quickselect(elements, 0, len(elements)-1, len(elements)-k) - topK := make([]int, 0, k) + quickselect(elements, 0, len(elements)-1, len(elements)-k, partitionRandom) + topK := make([]int, k) for i := 0; i < k; i++ { - topK = append(topK, elements[len(elements)-1-i].num) + topK[i] = elements[len(elements)-1-i].num } return topK } -func quickselect(elements []element, left, right, k int) { +func quickselect( + elements []Element, left, right, k int, + partition func([]Element, int, int) int) { for left < right { pivotIndex := partition(elements, left, right) - if k == pivotIndex { + if pivotIndex == k { return } - if k < pivotIndex { - right = pivotIndex - 1 - } else { + if pivotIndex < k { left = pivotIndex + 1 + } else { + right = pivotIndex - 1 } } } -func partition(elements []element, left, right int) int { +func partitionRandom(elements []Element, left, right int) int { pivotIndex := left + rand.IntN(right-left+1) - pivotValue := elements[pivotIndex].count elements[pivotIndex], elements[right] = elements[right], elements[pivotIndex] + pivot := elements[right].count + storeIndex := left + for i := left; i < right; i++ { + if elements[i].count < pivot { + elements[i], elements[storeIndex] = elements[storeIndex], elements[i] + storeIndex++ + } + } + elements[storeIndex], elements[right] = elements[right], elements[storeIndex] + return storeIndex +} + +func partitionMedianOf3(elements []Element, left, right int) int { + mid := left + (right-left)/2 + if elements[right].count < elements[left].count { + elements[right], elements[left] = elements[left], elements[right] + } + if elements[mid].count < elements[left].count { + elements[mid], elements[left] = elements[left], elements[mid] + } + if elements[right].count < elements[mid].count { + elements[right], elements[mid] = elements[mid], elements[right] + } + pivotIndex := mid + elements[pivotIndex], elements[right] = elements[right], elements[pivotIndex] + pivot := elements[right].count storeIndex := left for i := left; i < right; i++ { - if elements[i].count < pivotValue { - elements[storeIndex], elements[i] = elements[i], elements[storeIndex] + if elements[i].count < pivot { + elements[i], elements[storeIndex] = elements[storeIndex], elements[i] storeIndex++ } } @@ -76,17 +102,16 @@ func partition(elements []element, left, right int) int { } func topKFrequentPDQSort(nums []int, k int) []int { - frequencies := make(map[int]int, len(nums)) - for _, n := range nums { - frequencies[n]++ + frequency := make(map[int]int) + for _, num := range nums { + frequency[num]++ } - numsSet := make([]int, 0, len(frequencies)) - for n := range frequencies { - numsSet = append(numsSet, n) + numsSet := make([]int, 0, len(frequency)) + for num := range frequency { + numsSet = append(numsSet, num) } - // 第2引数の比較関数でtrueになる並び順にnumsSetをソートする sort.Slice(numsSet, func(i, j int) bool { - return frequencies[numsSet[i]] > frequencies[numsSet[j]] + return frequency[numsSet[i]] > frequency[numsSet[j]] }) return numsSet[:k] } @@ -99,22 +124,22 @@ func topKFrequentMinHeap(nums []int, k int) []int { h := &MinHeap{} heap.Init(h) for num, count := range frequency { - heap.Push(h, element{num: num, count: count}) + heap.Push(h, Element{num: num, count: count}) if h.Len() > k { heap.Pop(h) } } topK := make([]int, 0, k) for h.Len() > 0 { - topK = append(topK, heap.Pop(h).(element).num) + topK = append(topK, heap.Pop(h).(Element).num) } return topK } -type MinHeap []element +type MinHeap []Element func (h *MinHeap) Push(x interface{}) { - *h = append(*h, x.(element)) + *h = append(*h, x.(Element)) } func (h *MinHeap) Pop() interface{} { @@ -135,19 +160,19 @@ func topKFrequentMaxHeap(nums []int, k int) []int { h := &MaxHeap{} heap.Init(h) for num, count := range frequency { - heap.Push(h, element{num: num, count: count}) + heap.Push(h, Element{num: num, count: count}) } topK := make([]int, 0, k) for i := 0; i < k; i++ { - topK = append(topK, heap.Pop(h).(element).num) + topK = append(topK, heap.Pop(h).(Element).num) } return topK } -type MaxHeap []element +type MaxHeap []Element func (h *MaxHeap) Push(x interface{}) { - *h = append(*h, x.(element)) + *h = append(*h, x.(Element)) } func (h *MaxHeap) Pop() interface{} { diff --git a/go/two_sum.go b/go/two_sum.go index e884864..039cfb8 100644 --- a/go/two_sum.go +++ b/go/two_sum.go @@ -2,7 +2,7 @@ package main func twoSum(nums []int, target int) []int { - numToIndex := make(map[int]int, len(nums)) + numToIndex := make(map[int]int) for i, n := range nums { if j, ok := numToIndex[target-n]; ok { return []int{i, j} diff --git a/go/valid_palindrome.go b/go/valid_palindrome.go index 581e878..22a0315 100644 --- a/go/valid_palindrome.go +++ b/go/valid_palindrome.go @@ -5,51 +5,21 @@ import "unicode" func isPalindrome(s string) bool { runeS := []rune(s) - i, j := 0, len(s)-1 - for i < j { - if !unicode.IsDigit(runeS[i]) && !unicode.IsLetter(runeS[i]) { - i++ + left, right := 0, len(s)-1 + for left < right { + if !(unicode.IsDigit(runeS[left]) || unicode.IsLetter(runeS[left])) { + left++ continue } - if !unicode.IsDigit(runeS[j]) && !unicode.IsLetter(runeS[j]) { - j-- + if !(unicode.IsDigit(runeS[right]) || unicode.IsLetter(runeS[right])) { + right-- continue } - if unicode.ToLower(runeS[i]) != unicode.ToLower(runeS[j]) { + if unicode.ToLower(runeS[left]) != unicode.ToLower(runeS[right]) { return false } - i++ - j-- + left++ + right-- } return true } - -func isPalindrome2(s string) bool { - left := make(chan rune) - right := make(chan rune) - go filter(s, left, false) - go filter(s, right, true) - for { - l, lok := <-left - r, rok := <-right - if !lok || !rok { - return true - } - if l != r { - return false - } - } -} - -func filter(s string, c chan rune, reversed bool) { - for i := range s { - if reversed { - i = len(s) - 1 - i - } - r := rune(s[i]) - if unicode.IsDigit(r) || unicode.IsLetter(r) { - c <- unicode.ToLower(r) - } - } - close(c) -} diff --git a/go/valid_parentheses.go b/go/valid_parentheses.go index 27f215b..7231e5e 100644 --- a/go/valid_parentheses.go +++ b/go/valid_parentheses.go @@ -2,19 +2,15 @@ package main func isValid(s string) bool { - closeToOpen := map[rune]rune{ - ')': '(', - '}': '{', - ']': '[', - } var stack []rune - for _, r := range s { - p, ok := closeToOpen[r] + closeToOpens := map[rune]rune{')': '(', '}': '{', ']': '['} + for _, bracket := range s { + openBracket, ok := closeToOpens[bracket] if !ok { - stack = append(stack, r) + stack = append(stack, bracket) continue } - if len(stack) == 0 || stack[len(stack)-1] != p { + if len(stack) == 0 || stack[len(stack)-1] != openBracket { return false } stack = stack[:len(stack)-1] diff --git a/go/zigzag_conversion.go b/go/zigzag_conversion.go index 1f947a9..ec330f1 100644 --- a/go/zigzag_conversion.go +++ b/go/zigzag_conversion.go @@ -3,27 +3,45 @@ package main import "strings" -// ジグザグの行ごとに作成していき、最後に1つにする -// sの先頭から見ていくので、その文字の行番号さえわかればその行に追加していくだけ func convert(s string, numRows int) string { - if numRows <= 1 { + if numRows == 1 { return s } + rows := make([]strings.Builder, numRows) - blockSize := numRows*2 - 2 + cycleLength := 2*numRows - 2 for i, r := range s { - offset := i % blockSize - var rowIndex int - if offset < numRows { - rowIndex = offset - } else { - rowIndex = blockSize - offset + rowIndex := i % cycleLength + if rowIndex >= numRows { + rowIndex = cycleLength - rowIndex } rows[rowIndex].WriteRune(r) } - var converted strings.Builder + + var oneline strings.Builder for _, row := range rows { - converted.WriteString(row.String()) + oneline.WriteString(row.String()) + } + return oneline.String() +} + +func convert2(s string, numRows int) string { + if numRows == 1 { + return s + } + var oneline strings.Builder + cycleLength := 2*numRows - 2 + for rowIndex := 0; rowIndex < numRows; rowIndex++ { + for i := rowIndex; i < len(s); i += cycleLength { + oneline.WriteByte(s[i]) + if rowIndex == 0 || rowIndex == numRows-1 { + continue + } + diagIndex := i + cycleLength - rowIndex*2 + if diagIndex < len(s) { + oneline.WriteByte(s[diagIndex]) + } + } } - return converted.String() + return oneline.String() } diff --git a/pullrequests/add_binary/step1.go b/pullrequests/add_binary/step1.go deleted file mode 100644 index 303292c..0000000 --- a/pullrequests/add_binary/step1.go +++ /dev/null @@ -1,39 +0,0 @@ -//lint:file-ignore U1000 Ignore all unused code -package addbinary - -import ( - "slices" - "strings" -) - -/* -レビュワーの方へ: - - このコードは既にGoの標準のフォーマッタで整形済みです。演算子の周りにスペースがあったりなかったりしますが、これはGoのフォーマッタによるもので、優先順位の高い演算子の周りにはスペースが入らず、低い演算子の周りには入るようになっています。https://qiita.com/tchssk/items/77030b4271cd192d0347 -*/ - -/* -解法自体はすぐに思いついたが、文字列操作のメソッドの使い方の記憶が曖昧だったので、調べながら書いた。 -*/ -func addBinary(a string, b string) string { - var reversed strings.Builder - maxLength := max(len(a), len(b)) - carry := 0 - for i := 1; i <= maxLength; i++ { - bitA, bitB := 0, 0 - if i <= len(a) { - bitA = int(a[len(a)-i] - '0') - } - if i <= len(b) { - bitB = int(b[len(b)-i] - '0') - } - sum := bitA + bitB + carry - carry = sum / 2 - reversed.WriteByte(byte(sum%2 + '0')) - } - if carry == 1 { - reversed.WriteByte(byte(carry + '0')) - } - result := []rune(reversed.String()) - slices.Reverse(result) - return string(result) -} diff --git a/pullrequests/add_two_numbers/step1.go b/pullrequests/add_two_numbers/step1.go deleted file mode 100644 index 6e21428..0000000 --- a/pullrequests/add_two_numbers/step1.go +++ /dev/null @@ -1,48 +0,0 @@ -//lint:file-ignore U1000 Ignore all unused code -package addtwonumbers - -type ListNode struct { - Val int - Next *ListNode -} - -/* -時間:8分35秒 - -方針自体はすぐに決まり、繰り上げと2つの数の桁数の違いに気をつけて、足していけばいいと考えました。末端のノードの値が0になるときの対処に少し悩みました。 -とりあえず動くように書いたのでコードの重複などが多く、こなれていないコードだなと思います。 -*/ -func addTwoNumbers_step1(l1 *ListNode, l2 *ListNode) *ListNode { - dummy := new(ListNode) - dummy.Next = new(ListNode) - dummy.Next.Val = 0 - curr := dummy - for l1 != nil && l2 != nil { - curr = curr.Next - sum := l1.Val + l2.Val + curr.Val - curr.Val = sum % 10 - curr.Next = new(ListNode) - curr.Next.Val = sum / 10 - l1, l2 = l1.Next, l2.Next - } - for l1 != nil { - curr = curr.Next - sum := l1.Val + curr.Val - curr.Val = sum % 10 - curr.Next = new(ListNode) - curr.Next.Val = sum / 10 - l1 = l1.Next - } - for l2 != nil { - curr = curr.Next - sum := l2.Val + curr.Val - curr.Val = sum % 10 - curr.Next = new(ListNode) - curr.Next.Val = sum / 10 - l2 = l2.Next - } - if curr.Next.Val == 0 { - curr.Next = nil - } - return dummy.Next -} diff --git a/pullrequests/add_two_numbers/step2.go b/pullrequests/add_two_numbers/step2.go deleted file mode 100644 index 317506d..0000000 --- a/pullrequests/add_two_numbers/step2.go +++ /dev/null @@ -1,31 +0,0 @@ -//lint:file-ignore U1000 Ignore all unused code -package addtwonumbers - -/* -コードの重複を排除しました。ただまだ無駄な処理が多いです。 -*/ -func addTwoNumbers_step2(l1 *ListNode, l2 *ListNode) *ListNode { - dummy := new(ListNode) - dummy.Next = new(ListNode) - dummy.Next.Val = 0 - curr := dummy - for l1 != nil || l2 != nil { - curr = curr.Next - sum := curr.Val - if l1 != nil { - sum += l1.Val - l1 = l1.Next - } - if l2 != nil { - sum += l2.Val - l2 = l2.Next - } - curr.Val = sum % 10 - curr.Next = new(ListNode) - curr.Next.Val = sum / 10 - } - if curr.Next.Val == 0 { - curr.Next = nil - } - return dummy.Next -} diff --git a/pullrequests/add_two_numbers/step3.go b/pullrequests/add_two_numbers/step3.go deleted file mode 100644 index b1cb8f9..0000000 --- a/pullrequests/add_two_numbers/step3.go +++ /dev/null @@ -1,27 +0,0 @@ -//lint:file-ignore U1000 Ignore all unused code -package addtwonumbers - -/* -carryを導入した方がより明確でシンプルに書けると気づき変更しました。 -carryを導入したことにより、末端に余計なノード(値が0のノード)が発生しなくなりました。 -*/ -func addTwoNumbers_step3(l1 *ListNode, l2 *ListNode) *ListNode { - dummy := new(ListNode) - curr := dummy - carry := 0 - for l1 != nil || l2 != nil || carry != 0 { - sum := carry - if l1 != nil { - sum += l1.Val - l1 = l1.Next - } - if l2 != nil { - sum += l2.Val - l2 = l2.Next - } - curr.Next = &ListNode{Val: sum % 10} - carry = sum / 10 - curr = curr.Next - } - return dummy.Next -} diff --git a/pullrequests/best_time_to_buy_and_sell_stock/step1.go b/pullrequests/best_time_to_buy_and_sell_stock/step1.go new file mode 100644 index 0000000..ad300d9 --- /dev/null +++ b/pullrequests/best_time_to_buy_and_sell_stock/step1.go @@ -0,0 +1,18 @@ +//lint:file-ignore U1000 Ignore all unused code +package besttimetobuyandsellstock + +/* +時間:4分30秒 + +走査している中で現在の値に対して最大の利益を取り得るのは今まで走査してきた中の最小値であるため、最小値を更新した後に、現在の値に対する利益を計算し、それが最大値であれば更新するようにしている。 + +解いた後に中の変数名と関数名が一緒になってしまっていることに気づいた。 +*/ +func maxProfitStep1(prices []int) int { + minPrice, maxValue := prices[0], 0 + for _, price := range prices { + minPrice = min(minPrice, price) + maxValue = max(maxValue, price-minPrice) + } + return maxValue +} diff --git a/pullrequests/best_time_to_buy_and_sell_stock/step2.go b/pullrequests/best_time_to_buy_and_sell_stock/step2.go new file mode 100644 index 0000000..bb7f7c4 --- /dev/null +++ b/pullrequests/best_time_to_buy_and_sell_stock/step2.go @@ -0,0 +1,16 @@ +//lint:file-ignore U1000 Ignore all unused code +package besttimetobuyandsellstock + +import "math" + +/* +関数名と被らないように変数名を更新したのと、minPriceをmath.MaxIntで初期化することで、たとえ空の入力が渡されたとしてもエラーにならないようにした。 +*/ +func maxProfit(prices []int) int { + minPrice, maxValue := math.MaxInt, 0 + for _, price := range prices { + minPrice = min(minPrice, price) + maxValue = max(maxValue, price-minPrice) + } + return maxValue +} diff --git a/pullrequests/binary_search/step1.go b/pullrequests/binary_search/step1.go new file mode 100644 index 0000000..6c6300f --- /dev/null +++ b/pullrequests/binary_search/step1.go @@ -0,0 +1,38 @@ +//lint:file-ignore U1000 Ignore all unused code +package binarysearch + +/* +時間:5分 +単なる二分探索だが、まだ少し実装がもたつく部分があり、体に定着するほどにはなっていないなと思いました。 +*/ +func binarySearchHalfClosed(nums []int, target int) int { + left, right := 0, len(nums) + for left < right { + mid := left + (right-left)/2 + if nums[mid] == target { + return mid + } + if target < nums[mid] { + right = mid + } else { + left = mid + 1 + } + } + return -1 +} + +func binarySearchClosed(nums []int, target int) int { + left, right := 0, len(nums)-1 + for left <= right { + mid := left + (right-left)/2 + if nums[mid] == target { + return mid + } + if target < nums[mid] { + right = mid - 1 + } else { + left = mid + 1 + } + } + return -1 +} diff --git a/pullrequests/binary_search/step2.go b/pullrequests/binary_search/step2.go new file mode 100644 index 0000000..27e6a7d --- /dev/null +++ b/pullrequests/binary_search/step2.go @@ -0,0 +1,17 @@ +//lint:file-ignore U1000 Ignore all unused code +package binarysearch + +import "sort" + +/* +PythonのbisectみたいなものがGoにもないかと調べたところ、sort.Search関数が使えそうだったので実装してみました。 +*/ +func search(nums []int, target int) int { + index := sort.Search(len(nums), func(i int) bool { + return nums[i] >= target + }) + if index < len(nums) && nums[index] == target { + return index + } + return -1 +} diff --git a/pullrequests/combination_sum/step1.go b/pullrequests/combination_sum/step1.go new file mode 100644 index 0000000..31819b8 --- /dev/null +++ b/pullrequests/combination_sum/step1.go @@ -0,0 +1,30 @@ +//lint:file-ignore U1000 Ignore all unused code +package template + +/* +時間:24分 +少し前にバックトラッキングの問題を解いたので、ある条件を満たす全ての組み合わせを求めるためにバックトラッキングを使って解きました。 +*/ +func combinationSumStep1(candidates []int, target int) [][]int { + var combinations [][]int + var stack []int + var findCombinations func(int, int) + findCombinations = func(curr int, sum int) { + if sum == target { + combination := make([]int, len(stack)) + copy(combination, stack) + combinations = append(combinations, combination) + return + } + if sum > target { + return + } + for i := curr; i < len(candidates); i++ { + stack = append(stack, candidates[i]) + findCombinations(i, sum+candidates[i]) + stack = stack[:len(stack)-1] + } + } + findCombinations(0, 0) + return combinations +} diff --git a/pullrequests/combination_sum/step2.go b/pullrequests/combination_sum/step2.go new file mode 100644 index 0000000..28644ff --- /dev/null +++ b/pullrequests/combination_sum/step2.go @@ -0,0 +1,56 @@ +//lint:file-ignore U1000 Ignore all unused code +package template + +/* +Step1では再帰を使ってバックトラッキングを解いたので、スタックを使った方法も実装しました。 +また、Step1の実装のリファクタもしました。 +*/ +func combinationSumBacktrackingStack(candidates []int, target int) [][]int { + combinations := [][]int{} + type state struct { + combination []int + sum int + index int + } + stack := []state{{[]int{}, 0, 0}} + for len(stack) > 0 { + current := stack[len(stack)-1] + stack = stack[:len(stack)-1] + if current.sum == target { + combinations = append(combinations, append([]int{}, current.combination...)) + continue + } + for i := current.index; i < len(candidates); i++ { + newSum := current.sum + candidates[i] + if newSum > target { + continue + } + newCombination := append([]int{}, current.combination...) + newCombination = append(newCombination, candidates[i]) + stack = append(stack, state{newCombination, newSum, i}) + } + } + return combinations +} + +func combinationSumBacktrackingRecursion(candidates []int, target int) [][]int { + var combinations [][]int + var stack []int + var generateCombinations func(int, int) + generateCombinations = func(currentIndex int, sum int) { + if sum == target { + combinations = append(combinations, append([]int{}, stack...)) + return + } + if sum > target { + return + } + for i := currentIndex; i < len(candidates); i++ { + stack = append(stack, candidates[i]) + generateCombinations(i, sum+candidates[i]) + stack = stack[:len(stack)-1] + } + } + generateCombinations(0, 0) + return combinations +} diff --git a/pullrequests/combination_sum/step3.go b/pullrequests/combination_sum/step3.go new file mode 100644 index 0000000..13715f3 --- /dev/null +++ b/pullrequests/combination_sum/step3.go @@ -0,0 +1,21 @@ +//lint:file-ignore U1000 Ignore all unused code +package template + +/* +動的計画法を使った方法も実装しました。 +targetごとに組み合わせを求めると重複する組み合わせが生じてしまうので、candidateごとに組み合わせを求めるようにしました。 +*/ +func combinationSumDP(candidates []int, target int) [][]int { + combinationsGroups := make([][][]int, target+1) + combinationsGroups[0] = [][]int{{}} + for _, candidate := range candidates { + for i := candidate; i <= target; i++ { + for _, combination := range combinationsGroups[i-candidate] { + newCombination := append([]int{}, combination...) + newCombination = append(newCombination, candidate) + combinationsGroups[i] = append(combinationsGroups[i], newCombination) + } + } + } + return combinationsGroups[target] +} diff --git a/pullrequests/combination_sum/step4.go b/pullrequests/combination_sum/step4.go new file mode 100644 index 0000000..43e04ef --- /dev/null +++ b/pullrequests/combination_sum/step4.go @@ -0,0 +1,56 @@ +//lint:file-ignore U1000 Ignore all unused code +package template + +/* +Step1では再帰を使ってバックトラッキングを解いたので、スタックを使った方法も実装しました。 +また、Step1の実装のリファクタもしました。 +*/ +func combinationSumBacktrackingStackStep4(candidates []int, target int) [][]int { + combinations := [][]int{} + type state struct { + combination []int + sum int + index int + } + stack := []state{{[]int{}, 0, 0}} + for len(stack) > 0 { + current := stack[len(stack)-1] + stack = stack[:len(stack)-1] + if current.sum == target { + combinations = append(combinations, current.combination) + continue + } + for i := current.index; i < len(candidates); i++ { + newSum := current.sum + candidates[i] + if newSum > target { + continue + } + newCombination := append([]int{}, current.combination...) + newCombination = append(newCombination, candidates[i]) + stack = append(stack, state{newCombination, newSum, i}) + } + } + return combinations +} + +func combinationSumBacktrackingRecursionStep4(candidates []int, target int) [][]int { + var combinations [][]int + var stack []int + var generateCombinations func(int, int) + generateCombinations = func(currentIndex int, sum int) { + if sum == target { + combinations = append(combinations, append([]int{}, stack...)) + return + } + if sum > target { + return + } + for i := currentIndex; i < len(candidates); i++ { + stack = append(stack, candidates[i]) + generateCombinations(i, sum+candidates[i]) + stack = stack[:len(stack)-1] + } + } + generateCombinations(0, 0) + return combinations +} diff --git a/pullrequests/contains_duplicate/step1.go b/pullrequests/contains_duplicate/step1.go new file mode 100644 index 0000000..6db8956 --- /dev/null +++ b/pullrequests/contains_duplicate/step1.go @@ -0,0 +1,25 @@ +//lint:file-ignore U1000 Ignore all unused code +package containsduplicate + +/* +かなり前に解いたときのもの(コミット履歴からとってきました)なので +詳細は忘れてしまいました(すいません)。 +基本的にはマップを使って重複を見つけようとしています。 + +- Step1の要素数が1以下の場合の処理はなくても変わらないことに気づいたのでStep2ではなくしています。 +- valueの意味でvを使ったのですが、nのほうがnumsと対応していてより良いかなと思ったので同様にStep2ではnを使うようにしました。 +- nmもnumsMapのつもりで使ったのですが同様に他にマップが使われているわけではないのでStep2では単にmとしました。 +*/ +func containsDuplicateStep1(nums []int) bool { + if len(nums) <= 1 { + return false + } + nm := make(map[int]struct{}) + for _, v := range nums { + if _, ok := nm[v]; ok { + return true + } + nm[v] = struct{}{} + } + return false +} diff --git a/pullrequests/contains_duplicate/step2.go b/pullrequests/contains_duplicate/step2.go new file mode 100644 index 0000000..23c3c69 --- /dev/null +++ b/pullrequests/contains_duplicate/step2.go @@ -0,0 +1,30 @@ +//lint:file-ignore U1000 Ignore all unused code +package containsduplicate + +/* +時間:1分 +よりシンプルに書くようにしました。 +また他の解法として最後に要素数を比較する方法もあると思います。 + +質問 +- m, nという変数名をどう思いますか?(個人的にはこれぐらいの短いコードであれば逆に長い変数名をつけるのも良くないのではと思ったためあえて一文字の変数を使いました) +- 2つの解法のうちどちらのがほうが良いと思いますか?(個人的には下の解法はコード数が短くなるものの、必ずnumsを全て見ないといけないので、その点で若干パフォーマンスは劣るのかなと思っています。気にしなくても良いぐらいの話かもしれませんが、、) +*/ +func containsDuplicateStep2(nums []int) bool { + m := make(map[int]struct{}) + for _, n := range nums { + if _, ok := m[n]; ok { + return true + } + m[n] = struct{}{} + } + return false +} + +func containsDuplicate2Step2(nums []int) bool { + m := make(map[int]struct{}) + for _, n := range nums { + m[n] = struct{}{} + } + return len(nums) > len(m) +} diff --git a/pullrequests/contains_duplicate/step3.go b/pullrequests/contains_duplicate/step3.go new file mode 100644 index 0000000..ca54f69 --- /dev/null +++ b/pullrequests/contains_duplicate/step3.go @@ -0,0 +1,13 @@ +//lint:file-ignore U1000 Ignore all unused code +package containsduplicate + +func containsDuplicateStep3(nums []int) bool { + seen := make(map[int]struct{}) + for _, n := range nums { + if _, ok := seen[n]; ok { + return true + } + seen[n] = struct{}{} + } + return false +} diff --git a/pullrequests/contains_duplicate/step4.go b/pullrequests/contains_duplicate/step4.go new file mode 100644 index 0000000..15c6592 --- /dev/null +++ b/pullrequests/contains_duplicate/step4.go @@ -0,0 +1,22 @@ +//lint:file-ignore U1000 Ignore all unused code +package containsduplicate + +func containsDuplicateStep4(nums []int) bool { + seen := make(map[int]struct{}) + for _, n := range nums { + if _, ok := seen[n]; ok { + return true + } + seen[n] = struct{}{} + } + return false +} + +// 若干トリッキーで意図がわかりづらいので上の方がベター +func containsDuplicate2Step4(nums []int) bool { + numsMap := make(map[int]struct{}) + for _, n := range nums { + numsMap[n] = struct{}{} + } + return len(nums) > len(numsMap) +} diff --git a/pullrequests/counting_bits/step1.go b/pullrequests/counting_bits/step1.go deleted file mode 100644 index 17046ce..0000000 --- a/pullrequests/counting_bits/step1.go +++ /dev/null @@ -1,27 +0,0 @@ -//lint:file-ignore U1000 Ignore all unused code -package countingbits - -/* -レビュワーの方へ: - - このコードは既にGoの標準のフォーマッタで整形済みです。演算子の周りにスペースがあったりなかったりしますが、これはGoのフォーマッタによるもので、優先順位の高い演算子の周りにはスペースが入らず、低い演算子の周りには入るようになっています。https://qiita.com/tchssk/items/77030b4271cd192d0347 -*/ - -/* -時間:11分 -問題を理解するのに3分かかってしまった(問題文の英語が難しかった、、) -解法を思いつくのにも少し時間がかかってしまった。 -この方法だと内側のループでO(log n)、外側のループでO(n)かかるので、全体でO(n log n)かかる。 -*/ -func countBitsStep1(n int) []int { - bitCounts := make([]int, n+1) - for i := 0; i <= n; i++ { - num := i - count := 0 - for num > 0 { - count += num % 2 - num /= 2 - } - bitCounts[i] = count - } - return bitCounts -} diff --git a/pullrequests/counting_bits/step2.go b/pullrequests/counting_bits/step2.go deleted file mode 100644 index b2c4a4c..0000000 --- a/pullrequests/counting_bits/step2.go +++ /dev/null @@ -1,23 +0,0 @@ -//lint:file-ignore U1000 Ignore all unused code -package countingbits - -/* -レビュワーの方へ: - - このコードは既にGoの標準のフォーマッタで整形済みです。演算子の周りにスペースがあったりなかったりしますが、これはGoのフォーマッタによるもので、優先順位の高い演算子の周りにはスペースが入らず、低い演算子の周りには入るようになっています。https://qiita.com/tchssk/items/77030b4271cd192d0347 -*/ - -/* -DPのメモカを利用すればO(n)で解ける。 -2の冪乗のときは必ず1になるし、それ以外の場合は前に計算した結果+1すれば良い。 -*/ -func countBitsStep2(n int) []int { - bitCounts := make([]int, n+1) - powerOfTwo := 1 - for i := 1; i <= n; i++ { - if i == powerOfTwo*2 { - powerOfTwo = i - } - bitCounts[i] = 1 + bitCounts[i-powerOfTwo] - } - return bitCounts -} diff --git a/pullrequests/generate_parentheses/step1.go b/pullrequests/generate_parentheses/step1.go deleted file mode 100644 index 63dc348..0000000 --- a/pullrequests/generate_parentheses/step1.go +++ /dev/null @@ -1,34 +0,0 @@ -//lint:file-ignore U1000 Ignore all unused code -package generateparentheses - -/* -初見で解くことができず、他の人の回答を見ながら解く形になってしまいました。 -一応自分でもバックトラッキングをちゃんと理解して自力で下記のコードを書けるようにはなりました。 - -時間計算量と空間計算量はカタラン数になるという認識。 -https://github.com/SuperHotDogCat/coding-interview/pull/7#discussion_r1577988152 -https://github.com/Mike0121/LeetCode/pull/1#discussion_r1577919957 -*/ -func generateParenthesis_step1(n int) []string { - var parentheses []string - var stack []byte - var generate func(int, int) - generate = func(open int, closed int) { - if open == n && closed == n { - parentheses = append(parentheses, string(stack)) - return - } - if open < n { - stack = append(stack, '(') - generate(open+1, closed) - stack = stack[:len(stack)-1] - } - if open > closed { - stack = append(stack, ')') - generate(open, closed+1) - stack = stack[:len(stack)-1] - } - } - generate(0, 0) - return parentheses -} diff --git a/pullrequests/group_anagrams/step1.go b/pullrequests/group_anagrams/step1.go new file mode 100644 index 0000000..5607019 --- /dev/null +++ b/pullrequests/group_anagrams/step1.go @@ -0,0 +1,28 @@ +//lint:file-ignore U1000 Ignore all unused code +package groupanagrams + +/* + すぐに思いついたのはソートした文字列をkeyとしてハッシュテーブルを使ってグルーピングすること。 + ただ、クイックソートの平均計算量はO(n log n)なので、O(n)でできればやりたいと思い、前に似た + ような問題を解いたことがあったので、与えられた条件に小文字しか使われないと書いてあったので、 + 配列のインデックスを用いて頻度を取得し、keyにすることにした。 +*/ + +func groupAnagramsStep1(strs []string) [][]string { + m := make(map[[26]int][]string) + for _, s := range strs { + var freq [26]int + for _, r := range s { + freq[r-'a']++ + } + m[freq] = append(m[freq], s) + } + + result := make([][]string, len(m)) + i := 0 + for _, v := range m { + result[i] = v + i++ + } + return result +} diff --git a/pullrequests/group_anagrams/step2.go b/pullrequests/group_anagrams/step2.go new file mode 100644 index 0000000..0381a92 --- /dev/null +++ b/pullrequests/group_anagrams/step2.go @@ -0,0 +1,25 @@ +//lint:file-ignore U1000 Ignore all unused code +package groupanagrams + +/* + 変数名を改善した。 +*/ + +func groupAnagramsStep2(strs []string) [][]string { + m := make(map[[26]int][]string) + for _, s := range strs { + var freq [26]int + for _, r := range s { + freq[r-'a']++ + } + m[freq] = append(m[freq], s) + } + + res := make([][]string, len(m)) + i := 0 + for _, v := range m { + res[i] = v + i++ + } + return res +} diff --git a/pullrequests/group_anagrams/step3.go b/pullrequests/group_anagrams/step3.go new file mode 100644 index 0000000..9e3d95d --- /dev/null +++ b/pullrequests/group_anagrams/step3.go @@ -0,0 +1,31 @@ +//lint:file-ignore U1000 Ignore all unused code +package groupanagrams + +/* + 時間:3分 + 変数名をリファクタした。 + + 質問: + - resという変数名をどう思いますか? + - mという変数名をどう思いますか?(mapのmです) + - wordsという変数名をどう思いますか? +*/ + +func groupAnagramsStep3(strs []string) [][]string { + m := make(map[[26]int][]string) + for _, word := range strs { + var freq [26]int + for _, r := range word { + freq[r-'a']++ + } + m[freq] = append(m[freq], word) + } + + res := make([][]string, len(m)) + i := 0 + for _, words := range m { + res[i] = words + i++ + } + return res +} diff --git a/pullrequests/group_anagrams/step4.go b/pullrequests/group_anagrams/step4.go new file mode 100644 index 0000000..82ad9f5 --- /dev/null +++ b/pullrequests/group_anagrams/step4.go @@ -0,0 +1,21 @@ +//lint:file-ignore U1000 Ignore all unused code +package groupanagrams + +func groupAnagramsStep4(strs []string) [][]string { + anagramsMap := make(map[[26]int][]string) + for _, word := range strs { + var frequency [26]int + for _, r := range word { + frequency[r-'a']++ + } + anagramsMap[frequency] = append(anagramsMap[frequency], word) + } + + anagrams := make([][]string, len(anagramsMap)) + i := 0 + for _, words := range anagramsMap { + anagrams[i] = words + i++ + } + return anagrams +} diff --git a/pullrequests/group_anagrams/step5.go b/pullrequests/group_anagrams/step5.go new file mode 100644 index 0000000..8fd20a9 --- /dev/null +++ b/pullrequests/group_anagrams/step5.go @@ -0,0 +1,21 @@ +//lint:file-ignore U1000 Ignore all unused code +package groupanagrams + +func groupAnagramsStep5(strs []string) [][]string { + anagramsMap := make(map[[26]int][]string) + for _, word := range strs { + var frequencies [26]int + for _, r := range word { + frequencies[r-'a']++ + } + anagramsMap[frequencies] = append(anagramsMap[frequencies], word) + } + + anagrams := make([][]string, len(anagramsMap)) + i := 0 + for _, words := range anagramsMap { + anagrams[i] = words + i++ + } + return anagrams +} diff --git a/pullrequests/implement_queue_using_stacks/step1.go b/pullrequests/implement_queue_using_stacks/step1.go new file mode 100644 index 0000000..0ebd590 --- /dev/null +++ b/pullrequests/implement_queue_using_stacks/step1.go @@ -0,0 +1,57 @@ +//lint:file-ignore U1000 Ignore all unused code +package template + +/* +レビュワーの方へ: + - このコードは既にGoの標準のフォーマッタで整形済みです。演算子の周りにスペースがあったりなかったりしますが、これはGoのフォーマッタによるもので、優先順位の高い演算子の周りにはスペースが入らず、低い演算子の周りには入るようになっています。https://qiita.com/tchssk/items/77030b4271cd192d0347 +*/ + +/* +時間:15分 + +スタックを2つ使ってキューを実装する方法を知っていたので解法自体はすぐに思いついた。 +LeetCodeの問題としてはPopやPeekは必ず有効な時に呼ばれるとのことなので、エラー処理をする必要はなく、プロトタイプ宣言自体もerrorを返せるようになっていないが、個人的にエラー処理をしないと気持ち悪かったので、変なエラー処理っぽいことをした中途半端なコードになってしまった。 + +末尾についているStep1は提出用につけただけなので無視してください。 +*/ +type MyQueueStep1 struct { + pushStack []int + popStack []int +} + +func ConstructorStep1() MyQueueStep1 { + var q MyQueueStep1 + return q +} + +func (this *MyQueueStep1) Push(x int) { + this.pushStack = append(this.pushStack, x) +} + +func (this *MyQueueStep1) Pop() int { + this.Peek() + if len(this.popStack) > 0 { + x := this.popStack[len(this.popStack)-1] + this.popStack = this.popStack[:len(this.popStack)-1] + return x + } + return 0 +} + +func (this *MyQueueStep1) Peek() int { + if len(this.popStack) <= 0 { + for len(this.pushStack) > 0 { + x := this.pushStack[len(this.pushStack)-1] + this.pushStack = this.pushStack[:len(this.pushStack)-1] + this.popStack = append(this.popStack, x) + } + } + if len(this.popStack) > 0 { + return this.popStack[len(this.popStack)-1] + } + return 0 +} + +func (this *MyQueueStep1) Empty() bool { + return len(this.pushStack) <= 0 && len(this.popStack) <= 0 +} diff --git a/pullrequests/implement_queue_using_stacks/step2.go b/pullrequests/implement_queue_using_stacks/step2.go new file mode 100644 index 0000000..0a2be0e --- /dev/null +++ b/pullrequests/implement_queue_using_stacks/step2.go @@ -0,0 +1,43 @@ +//lint:file-ignore U1000 Ignore all unused code +package template + +/* +レビュワーの方へ: + - このコードは既にGoの標準のフォーマッタで整形済みです。演算子の周りにスペースがあったりなかったりしますが、これはGoのフォーマッタによるもので、優先順位の高い演算子の周りにはスペースが入らず、低い演算子の周りには入るようになっています。https://qiita.com/tchssk/items/77030b4271cd192d0347 +*/ + +/* +LeetCodeに通すことだけを考えてちゃんと綺麗に書いてみた。 +*/ +type MyQueueStep2 struct { + pushStack []int + popStack []int +} + +func ConstructorStep2() MyQueueStep2 { + return MyQueueStep2{} +} + +func (q *MyQueueStep2) Push(n int) { + q.pushStack = append(q.pushStack, n) +} + +func (q *MyQueueStep2) Pop() int { + n := q.Peek() + q.popStack = q.popStack[:len(q.popStack)-1] + return n +} + +func (q *MyQueueStep2) Peek() int { + if len(q.popStack) == 0 { + for len(q.pushStack) > 0 { + q.popStack = append(q.popStack, q.pushStack[len(q.pushStack)-1]) + q.pushStack = q.pushStack[:len(q.pushStack)-1] + } + } + return q.popStack[len(q.popStack)-1] +} + +func (q *MyQueueStep2) Empty() bool { + return len(q.pushStack) == 0 && len(q.popStack) == 0 +} diff --git a/pullrequests/implement_queue_using_stacks/step3.go b/pullrequests/implement_queue_using_stacks/step3.go new file mode 100644 index 0000000..f149e83 --- /dev/null +++ b/pullrequests/implement_queue_using_stacks/step3.go @@ -0,0 +1,47 @@ +//lint:file-ignore U1000 Ignore all unused code +package template + +import "fmt" + +/* +レビュワーの方へ: + - このコードは既にGoの標準のフォーマッタで整形済みです。演算子の周りにスペースがあったりなかったりしますが、これはGoのフォーマッタによるもので、優先順位の高い演算子の周りにはスペースが入らず、低い演算子の周りには入るようになっています。https://qiita.com/tchssk/items/77030b4271cd192d0347 +*/ + +/* +実際にLeetCodeの制約を無視できる場合のコードも書いてみた(本来であれば`container/list`を使えば良いという話だが、そこは一応LeetCodeで提示されている形から極力変えずに書いた)。 +*/ +type Queue struct { + pushStack []int + popStack []int +} + +func (q *Queue) Push(n int) { + q.pushStack = append(q.pushStack, n) +} + +func (q *Queue) Pop() (int, error) { + n, err := q.Peek() + if err != nil { + return 0, fmt.Errorf("queue is empty, cannot pop") + } + q.popStack = q.popStack[:len(q.popStack)-1] + return n, nil +} + +func (q *Queue) Peek() (int, error) { + if q.Empty() { + return 0, fmt.Errorf("queue is empty, cannot peek") + } + if len(q.popStack) == 0 { + for len(q.pushStack) > 0 { + q.popStack = append(q.popStack, q.pushStack[len(q.pushStack)-1]) + q.pushStack = q.pushStack[:len(q.pushStack)-1] + } + } + return q.popStack[len(q.popStack)-1], nil +} + +func (q *Queue) Empty() bool { + return len(q.pushStack) == 0 && len(q.popStack) == 0 +} diff --git a/pullrequests/invert_binary_tree/step1.go b/pullrequests/invert_binary_tree/step1.go new file mode 100644 index 0000000..baae19d --- /dev/null +++ b/pullrequests/invert_binary_tree/step1.go @@ -0,0 +1,23 @@ +//lint:file-ignore U1000 Ignore all unused code +package invertbinarytree + +type TreeNode struct { + Val int + Left *TreeNode + Right *TreeNode +} + +/* +時間:3分30秒 + +再帰的に左右のノードを入れ替えていけば良いというのにはすぐに気づき、すんなり実装することができた。 +*/ +func invertTreeStep1(root *TreeNode) *TreeNode { + if root == nil { + return nil + } + invertTreeStep1(root.Left) + invertTreeStep1(root.Right) + root.Left, root.Right = root.Right, root.Left + return root +} diff --git a/pullrequests/invert_binary_tree/step2.go b/pullrequests/invert_binary_tree/step2.go new file mode 100644 index 0000000..8b4171c --- /dev/null +++ b/pullrequests/invert_binary_tree/step2.go @@ -0,0 +1,33 @@ +//lint:file-ignore U1000 Ignore all unused code +package invertbinarytree + +/* +Step1は冗長だったのでリファクタした。 +これぐらい短いと早期リターンしなくても良いかなと思い下記のようにしました。 +またイテレーティブに解く方法も実装しました。 +*/ +func invertTreeStep2(root *TreeNode) *TreeNode { + if root != nil { + root.Left, root.Right = invertTreeStep2(root.Right), invertTreeStep2(root.Left) + } + return root +} + +func invertTreeIterative(root *TreeNode) *TreeNode { + if root == nil { + return nil + } + stack := []*TreeNode{root} + for len(stack) > 0 { + node := stack[len(stack)-1] + stack = stack[:len(stack)-1] + node.Left, node.Right = node.Right, node.Left + if node.Left != nil { + stack = append(stack, node.Left) + } + if node.Right != nil { + stack = append(stack, node.Right) + } + } + return root +} diff --git a/pullrequests/invert_binary_tree/step3.go b/pullrequests/invert_binary_tree/step3.go new file mode 100644 index 0000000..e5d894e --- /dev/null +++ b/pullrequests/invert_binary_tree/step3.go @@ -0,0 +1,10 @@ +//lint:file-ignore U1000 Ignore all unused code +package invertbinarytree + +func invertTreeRecursive(root *TreeNode) *TreeNode { + if root == nil { + return nil + } + root.Left, root.Right = invertTreeRecursive(root.Right), invertTreeRecursive(root.Left) + return root +} diff --git a/pullrequests/middle_of_the_linked_list/step1.go b/pullrequests/linked_list_cycle/step1.go similarity index 62% rename from pullrequests/middle_of_the_linked_list/step1.go rename to pullrequests/linked_list_cycle/step1.go index 4cc642f..3ac8934 100644 --- a/pullrequests/middle_of_the_linked_list/step1.go +++ b/pullrequests/linked_list_cycle/step1.go @@ -1,5 +1,5 @@ //lint:file-ignore U1000 Ignore all unused code -package middleofthelinkedlist +package linkedlistcycle type ListNode struct { Val int @@ -12,17 +12,18 @@ type ListNode struct { */ /* -時間:3分 +時間:5分 -しばらく解法が思いつかなかったが、フロイドの循環検出法を思いだし、それと似た方法で解くことができると思いついた。 +この問題を見たことがあり、フロイドの循環検出法を知っていたため、その方法で解いてみた。 +`slow == fast`ではポインタ同士を比較しているため、同一のオブジェクトであることを確認できる。 */ -func middleNodeStep1(head *ListNode) *ListNode { - if head == nil { - return nil - } +func hasCycleFloyd(head *ListNode) bool { slow, fast := head, head for fast != nil && fast.Next != nil { slow, fast = slow.Next, fast.Next.Next + if slow == fast { + return true + } } - return slow + return false } diff --git a/pullrequests/middle_of_the_linked_list/step3.go b/pullrequests/linked_list_cycle/step2.go similarity index 60% rename from pullrequests/middle_of_the_linked_list/step3.go rename to pullrequests/linked_list_cycle/step2.go index eae00e1..8bfb50b 100644 --- a/pullrequests/middle_of_the_linked_list/step3.go +++ b/pullrequests/linked_list_cycle/step2.go @@ -1,5 +1,5 @@ //lint:file-ignore U1000 Ignore all unused code -package middleofthelinkedlist +package linkedlistcycle /* レビュワーの方へ: @@ -7,18 +7,18 @@ package middleofthelinkedlist */ /* -他の方の解法を見て実装してみた。この方法を先に思いつかなかったのが逆に良くない気がした。 +他に既に訪ねたノードのリストを保持しておく方法も考えられる。 +ノードのポインタ(メモリアドレス)をキーとしている。 */ -func middleNodeStep3(head *ListNode) *ListNode { - count := 0 +func hasCycleMap(head *ListNode) bool { + seen := make(map[*ListNode]struct{}) node := head for node != nil { + if _, ok := seen[node]; ok { + return true + } + seen[node] = struct{}{} node = node.Next - count++ } - middle := head - for i := 0; i < count/2; i++ { - middle = middle.Next - } - return middle + return false } diff --git a/pullrequests/longest_substring_without_repeating_characters/step1.go b/pullrequests/longest_substring_without_repeating_characters/step1.go new file mode 100644 index 0000000..fe11637 --- /dev/null +++ b/pullrequests/longest_substring_without_repeating_characters/step1.go @@ -0,0 +1,26 @@ +//lint:file-ignore U1000 Ignore all unused code +package longestsubstringwithoutrepeatingcharacters + +/* +時間:34分 +最初に下記のやり方でやることを思いついたが、もう少し賢いやり方があるのではないかと10分ぐらい考えてしまい、思いつかなかったので、とりあえず書こうと思い、書きました。結果的には他の人のコードも見たりする限り、この方法で良かったのだとわかりました。 +*/ +func lengthOfLongestSubstring_step1(s string) int { + maxLen := 0 + head := 0 + m := make(map[rune]struct{}) + for tail := 1; tail <= len(s); tail++ { + if _, ok := m[rune(s[tail-1])]; ok { + for s[head] != s[tail-1] { + delete(m, rune(s[head])) + head++ + } + head++ + } else { + m[rune(s[tail-1])] = struct{}{} + } + substring := s[head:tail] + maxLen = max(maxLen, len(substring)) + } + return maxLen +} diff --git a/pullrequests/longest_substring_without_repeating_characters/step2.go b/pullrequests/longest_substring_without_repeating_characters/step2.go new file mode 100644 index 0000000..3641277 --- /dev/null +++ b/pullrequests/longest_substring_without_repeating_characters/step2.go @@ -0,0 +1,23 @@ +//lint:file-ignore U1000 Ignore all unused code +package longestsubstringwithoutrepeatingcharacters + +/* +より変数名をわかりやすくし、無駄な部分を消しました。 +*/ +func lengthOfLongestSubstring(s string) int { + maxLen, head := 0, 0 + inUse := make(map[byte]struct{}) + for tail := range s { + if _, ok := inUse[s[tail]]; ok { + for s[head] != s[tail] { + delete(inUse, s[head]) + head++ + } + head++ + } else { + inUse[s[tail]] = struct{}{} + } + maxLen = max(maxLen, tail-head+1) + } + return maxLen +} diff --git a/pullrequests/longest_substring_without_repeating_characters/step3.go b/pullrequests/longest_substring_without_repeating_characters/step3.go new file mode 100644 index 0000000..9f7f40f --- /dev/null +++ b/pullrequests/longest_substring_without_repeating_characters/step3.go @@ -0,0 +1,24 @@ +//lint:file-ignore U1000 Ignore all unused code +package longestsubstringwithoutrepeatingcharacters + +/* +inUseの値にbool値を入れればよりシンプルに書けると思ったので、書き換えました。 + + - inUseの値に最後に見たインデックスを入れることも考えたが、どちらにせよ`delete(inUse, s[head])`でそこまでの文字を消していく必要があるので、意味がないのでは? + - head, tailか、left, rightのどちらの方がベターか。substringの先頭・末尾と捉えるのであれば前者、Two Pointersと捉えるのであれば後者?head, tailの方がinclusiveだとわかるのでより良いのでは?(https://github.com/sakzk/leetcode/pull/3#discussion_r1591191532) + - inUseという変数名はどうか? + ー 確かにsliding windowと捉えることもできる(https://github.com/sakzk/leetcode/pull/3#discussion_r1591194013) +*/ +func lengthOfLongestSubstring_step3(s string) int { + maxLen, head := 0, 0 + inUse := make(map[byte]bool) + for tail := range s { + for inUse[s[tail]] { + delete(inUse, s[head]) + head++ + } + inUse[s[tail]] = true + maxLen = max(maxLen, tail-head+1) + } + return maxLen +} diff --git a/pullrequests/longest_substring_without_repeating_characters/step4.go b/pullrequests/longest_substring_without_repeating_characters/step4.go new file mode 100644 index 0000000..300d2e3 --- /dev/null +++ b/pullrequests/longest_substring_without_repeating_characters/step4.go @@ -0,0 +1,14 @@ +//lint:file-ignore U1000 Ignore all unused code +package longestsubstringwithoutrepeatingcharacters + +func lengthOfLongestSubstring_step4(s string) int { + maxLength, left, seen := 0, 0, make(map[rune]int) + for right, r := range s { + if lastIndex, ok := seen[r]; ok && lastIndex >= left { + left = lastIndex + 1 + } + seen[r] = right + maxLength = max(maxLength, right-left+1) + } + return maxLength +} diff --git a/pullrequests/lowest_common_ancester_of_a_binary_search_tree/step1.go b/pullrequests/lowest_common_ancester_of_a_binary_search_tree/step1.go new file mode 100644 index 0000000..109c01b --- /dev/null +++ b/pullrequests/lowest_common_ancester_of_a_binary_search_tree/step1.go @@ -0,0 +1,36 @@ +//lint:file-ignore U1000 Ignore all unused code +package lowestcommonancesterofabinarysearchtree + +type TreeNode struct { + Val int + Left *TreeNode + Right *TreeNode +} + +/* +時間:6分30秒 + +ルートから見ていく時、共通の祖先になっていないときは必ず、pとqは左か右のどちらかの同じ部分木にいるはず。pとqの間の値になったら共通の祖先になったとわかる。 + +本来は見つからなかった場合は返り値としてerrorを返したかったのですが、LeetCodeの制約上変えられないのでnilを返すようにしています。 + +該当ノードが見つからなかったからといってプログラムの実行の継続が困難になるわけではないと思うので、panicやlog.Panicを使うのはやり過ぎだと思います。 +場合によっては絶対に見つからないことが起こる入力はしないはずだと言える状況であればlog.Fatalを使ってログに書き込んだ後にos.Exit(1)を呼び出してプログラムを終了させるのが良い可能性もありますが、それも通常の場合やりすぎな気がします。 +他にはlog.Printなどを使ってログに見つからなかったことを記録しても良いかもしれませんが、見つからないことが起こる入力があり得るのであれば単にerrorを返り値として返して呼び出し側で処理するのが良いと思っています。 +他には単に見つからなかった場合はnilを返すなども手としてはあると思いますが、そうする場合は呼び出し側にもわかるようにコメント等で書いておいて欲しいなと思います。 +*/ +func lowestCommonAncestorIterativeStep1(root, p, q *TreeNode) *TreeNode { + node := root + for node != nil { + if p.Val <= node.Val && node.Val <= q.Val || q.Val <= node.Val && node.Val <= p.Val { + return node + } + if p.Val < node.Val && q.Val < node.Val { + node = node.Left + } + if node.Val < p.Val && node.Val < q.Val { + node = node.Right + } + } + return nil +} diff --git a/pullrequests/lowest_common_ancester_of_a_binary_search_tree/step2.go b/pullrequests/lowest_common_ancester_of_a_binary_search_tree/step2.go new file mode 100644 index 0000000..9c72e0b --- /dev/null +++ b/pullrequests/lowest_common_ancester_of_a_binary_search_tree/step2.go @@ -0,0 +1,32 @@ +//lint:file-ignore U1000 Ignore all unused code +package lowestcommonancesterofabinarysearchtree + +/* +より見やすくなるようにリファクタしました。また、再帰を使った実装もしてみました。 +エラー処理についてはStep1と同様です。 +*/ +func lowestCommonAncestorIterative(root, p, q *TreeNode) *TreeNode { + node := root + for node != nil { + if p.Val < node.Val && q.Val < node.Val { + node = node.Left + continue + } + if node.Val < p.Val && node.Val < q.Val { + node = node.Right + continue + } + return node + } + return nil +} + +func lowestCommonAncestorRecursive(root, p, q *TreeNode) *TreeNode { + if p.Val < root.Val && q.Val < root.Val { + return lowestCommonAncestorRecursive(root.Left, p, q) + } + if root.Val < p.Val && root.Val < q.Val { + return lowestCommonAncestorRecursive(root.Right, p, q) + } + return root +} diff --git a/pullrequests/majority_element/step1.go b/pullrequests/majority_element/step1.go new file mode 100644 index 0000000..5b97467 --- /dev/null +++ b/pullrequests/majority_element/step1.go @@ -0,0 +1,24 @@ +//lint:file-ignore U1000 Ignore all unused code +package majorityelement + +/* +レビュワーの方へ: + - このコードは既にGoの標準のフォーマッタで整形済みです。演算子の周りにスペースがあったりなかったりしますが、これはGoのフォーマッタによるもので、優先順位の高い演算子の周りにはスペースが入らず、低い演算子の周りには入るようになっています。https://qiita.com/tchssk/items/77030b4271cd192d0347 +*/ + +/* +時間:3分 + +一番自然な方法で解くことにした。 +最後にLeetCodeの制約上しょうがなく-1を返しているが、本当はerrorを返したい。 +*/ +func majorityElement(nums []int) int { + frequencies := make(map[int]int, len(nums)) + for _, n := range nums { + frequencies[n]++ + if frequencies[n] > len(nums)/2 { + return n + } + } + return -1 +} diff --git a/pullrequests/single_number/step2.go b/pullrequests/majority_element/step2.go similarity index 52% rename from pullrequests/single_number/step2.go rename to pullrequests/majority_element/step2.go index 35834a2..b984784 100644 --- a/pullrequests/single_number/step2.go +++ b/pullrequests/majority_element/step2.go @@ -1,5 +1,5 @@ //lint:file-ignore U1000 Ignore all unused code -package singlenumber +package majorityelement /* レビュワーの方へ: @@ -7,13 +7,22 @@ package singlenumber */ /* -調べたところ下記のようにすれば空間計算量がO(1)になる。 -ただし、このXORを使った方法は1つの数以外は全て偶数回必ず出現するという条件を満たさなければ成立しないので、実際にこのように書くのが良い方法だとは思わなかった(ただしビット演算の練習にはなる)。 +時間:6分 + +Boyer-Moore Voteアルゴリズムでも解いてみた。このアルゴリズムもたまたま知っていたが、思い出しつつ書いたので少し時間がかかってしまった。 */ -func singleNumberStep2(nums []int) int { - singleNum := 0 +func majorityElementBoyerMooreMajorityVote(nums []int) int { + candidate, count := nums[0], 0 for _, n := range nums { - singleNum ^= n + if n == candidate { + count++ + } else { + count-- + } + if count < 0 { + candidate = n + count = 0 + } } - return singleNum + return candidate } diff --git a/pullrequests/merge_two_sorted_lists/step1.go b/pullrequests/merge_two_sorted_lists/step1.go new file mode 100644 index 0000000..1bbe056 --- /dev/null +++ b/pullrequests/merge_two_sorted_lists/step1.go @@ -0,0 +1,60 @@ +//lint:file-ignore U1000 Ignore all unused code +package mergetwosortedlists + +/* + 個人的にLeetCodeを進めていて、2月ごろに解いたもの。GitHubのコミット履歴から引っ張ってきま + した。 + + 解いた時間を記録していなかったのですが、恐らく20~30分ぐらいかかった気がします。解いている途 + 中でポインタの向きがよくわからなくなってしまい、時間がかかってしまった記憶があります。また、 + Goの勉強も兼ねて書いたので、言語自体の知識も少ない状態で書きました。 + + 綺麗に書いている余裕がなかったので、とりあえず動くコードを書いたという感じです。とりあえず値 + の小さいノードから順番にポインタの向きを変えてマージしていけば良いとはわかったのですが、実装 + 力に余裕がなく、コードが冗長になってしまったという印象です。 +*/ + +type ListNode struct { + Val int + Next *ListNode +} + +func mergeTwoListsStep1(list1 *ListNode, list2 *ListNode) *ListNode { + if list1 == nil { + return list2 + } + if list2 == nil { + return list1 + } + + var res *ListNode + if list1.Val <= list2.Val { + res = list1 + list1 = list1.Next + } else { + res = list2 + list2 = list2.Next + } + + curNode := res + for { + if list1 == nil { + curNode.Next = list2 + return res + } + if list2 == nil { + curNode.Next = list1 + return res + } + + if list1.Val <= list2.Val { + curNode.Next = list1 + curNode = curNode.Next + list1 = list1.Next + } else { + curNode.Next = list2 + curNode = curNode.Next + list2 = list2.Next + } + } +} diff --git a/pullrequests/merge_two_sorted_lists/step2.go b/pullrequests/merge_two_sorted_lists/step2.go new file mode 100644 index 0000000..8d0ec46 --- /dev/null +++ b/pullrequests/merge_two_sorted_lists/step2.go @@ -0,0 +1,30 @@ +//lint:file-ignore U1000 Ignore all unused code +package mergetwosortedlists + +/* + 他の人のコードを色々見て、冗長な部分を消し、リファクタをしました。 +*/ + +func mergeTwoListsStep2(list1 *ListNode, list2 *ListNode) *ListNode { + dummy := new(ListNode) + cur := dummy + + for list1 != nil && list2 != nil { + if list1.Val <= list2.Val { + cur.Next = list1 + list1 = list1.Next + } else { + cur.Next = list2 + list2 = list2.Next + } + cur = cur.Next + } + + if list1 != nil { + cur.Next = list1 + } else { + cur.Next = list2 + } + + return dummy.Next +} diff --git a/pullrequests/merge_two_sorted_lists/step3.go b/pullrequests/merge_two_sorted_lists/step3.go new file mode 100644 index 0000000..e29d0ac --- /dev/null +++ b/pullrequests/merge_two_sorted_lists/step3.go @@ -0,0 +1,73 @@ +//lint:file-ignore U1000 Ignore all unused code +package mergetwosortedlists + +/* + 感想: + 現在(6月)解いたコードになります。大分余裕を持って書けるようになってきました。一番初めはポ + インタの向きがよくわからなくなるということがあったのですが、そういったこともなくなり、ポイ + ンタの操作を余裕を持って書けるようになってきました。暗記ではなく、自然と同じようなコードが + 出てくるようになりました。またGo自体も初期に比べて無理なく自然に書けるようになりました。 + + Step2に比べて変数名が改善されています(curではなくtailに変更)。また個人的にシンプルで見 + やすいと思っているので多重代入をよく使うようになりました。 + + また、個人的な感覚として、list1またはlist2のどちらか片方がnilの場合の処理をfor文の中に入 + れるか、外に出すかをその時の気分で意図的に変えるようになりました。 + + 質問したいこと: + - tailという変数名についてどう思いますか? + - 多重代入は使った方がシンプルで見やすいと個人的に思ってるのですがどう思いますか?もちろん + 互いに影響を与え合ってしまうような変数同士の場合は挙動がundefinedだと思うので避けるべき + だとは思いますが。 + - 個人的には上と下の回答はどちらも大差ないと思っていてどちらでも良いと思っているのですが、 + どう思いますか? +*/ + +func mergeTwoListsStep3(list1 *ListNode, list2 *ListNode) *ListNode { + dummy := new(ListNode) + tail := dummy + + for list1 != nil && list2 != nil { + if list1.Val < list2.Val { + tail.Next, list1 = list1, list1.Next + } else { + tail.Next, list2 = list2, list2.Next + } + tail = tail.Next + } + + if list1 != nil { + tail.Next = list1 + } + if list2 != nil { + tail.Next = list2 + } + + return dummy.Next +} + +func mergeTwoLists_step3_anothersolution(list1 *ListNode, list2 *ListNode) *ListNode { + dummy := new(ListNode) + tail := dummy + + for list1 != nil || list2 != nil { + if list1 == nil { + tail.Next = list2 + break + } + if list2 == nil { + tail.Next = list1 + break + } + + if list1.Val < list2.Val { + tail.Next, list1 = list1, list1.Next + } else { + tail.Next, list2 = list2, list2.Next + } + tail = tail.Next + tail.Next = nil + } + + return dummy.Next +} diff --git a/pullrequests/merge_two_sorted_lists/step4.go b/pullrequests/merge_two_sorted_lists/step4.go new file mode 100644 index 0000000..e4c865c --- /dev/null +++ b/pullrequests/merge_two_sorted_lists/step4.go @@ -0,0 +1,22 @@ +//lint:file-ignore U1000 Ignore all unused code +package mergetwosortedlists + +func mergeTwoListsStep4(list1 *ListNode, list2 *ListNode) *ListNode { + dummy := new(ListNode) + tail := dummy + for list1 != nil && list2 != nil { + if list1.Val < list2.Val { + tail.Next, list1 = list1, list1.Next + } else { + tail.Next, list2 = list2, list2.Next + } + tail = tail.Next + } + if list1 != nil { + tail.Next = list1 + } + if list2 != nil { + tail.Next = list2 + } + return dummy.Next +} diff --git a/pullrequests/merge_two_sorted_lists/step5.go b/pullrequests/merge_two_sorted_lists/step5.go new file mode 100644 index 0000000..b2a267a --- /dev/null +++ b/pullrequests/merge_two_sorted_lists/step5.go @@ -0,0 +1,22 @@ +//lint:file-ignore U1000 Ignore all unused code +package mergetwosortedlists + +func mergeTwoListsStep5(list1 *ListNode, list2 *ListNode) *ListNode { + dummy := &ListNode{} + tail := dummy + for list1 != nil && list2 != nil { + if list1.Val < list2.Val { + tail.Next, list1 = list1, list1.Next + } else { + tail.Next, list2 = list2, list2.Next + } + tail = tail.Next + } + if list1 != nil { + tail.Next = list1 + } + if list2 != nil { + tail.Next = list2 + } + return dummy.Next +} diff --git a/pullrequests/middle_of_the_linked_list/step2.go b/pullrequests/middle_of_the_linked_list/step2.go deleted file mode 100644 index dc5b30d..0000000 --- a/pullrequests/middle_of_the_linked_list/step2.go +++ /dev/null @@ -1,18 +0,0 @@ -//lint:file-ignore U1000 Ignore all unused code -package middleofthelinkedlist - -/* -レビュワーの方へ: - - このコードは既にGoの標準のフォーマッタで整形済みです。演算子の周りにスペースがあったりなかったりしますが、これはGoのフォーマッタによるもので、優先順位の高い演算子の周りにはスペースが入らず、低い演算子の周りには入るようになっています。https://qiita.com/tchssk/items/77030b4271cd192d0347 -*/ - -/* -最初の早期リターンはなくても問題ないことに気づき、削除した。 -*/ -func middleNodeStep2(head *ListNode) *ListNode { - slow, fast := head, head - for fast != nil && fast.Next != nil { - slow, fast = slow.Next, fast.Next.Next - } - return slow -} diff --git a/pullrequests/move_zeroes/step1.go b/pullrequests/move_zeroes/step1.go deleted file mode 100644 index 27346fd..0000000 --- a/pullrequests/move_zeroes/step1.go +++ /dev/null @@ -1,27 +0,0 @@ -//lint:file-ignore U1000 Ignore all unused code -package movezeroes - -/* -レビュワーの方へ: - - このコードは既にGoの標準のフォーマッタで整形済みです。演算子の周りにスペースがあったりなかったりしますが、これはGoのフォーマッタによるもので、優先順位の高い演算子の周りにはスペースが入らず、低い演算子の周りには入るようになっています。https://qiita.com/tchssk/items/77030b4271cd192d0347 -*/ - -/* -左側に非ゼロのエリア、右側にゼロのエリアを作れば良い。 -イメージとしてはzeroIndexはゼロのエリアのはじまり(境界)のインデックスを持ち、 -for文で探索していって非ゼロの要素を見つけたらzeroIndexの要素とスワップして、 -zeroIndexをインクリメントすることで、zeroIndexの左側に非ゼロの要素を集め、 -右側にゼロを集めるという感じ。 - -境界というのを明確にしたいのであればstartZeroIndexとかでもいいかも。またはzeroIndexだとインデックスの値自体が0なのかという誤解を引き起こす恐れがあるので、indexOfStartZeroIndexとかでもいいかもしれない。ただ全体の行数に対して変数名が冗長すぎる気がしたので今回はzeroIndexにした。 -*/ -func moveZeroes(nums []int) { - zeroIndex := 0 - for i, n := range nums { - if n == 0 { - continue - } - nums[zeroIndex], nums[i] = nums[i], nums[zeroIndex] - zeroIndex++ - } -} diff --git a/pullrequests/next_permutation/step1.go b/pullrequests/next_permutation/step1.go deleted file mode 100644 index 1b86702..0000000 --- a/pullrequests/next_permutation/step1.go +++ /dev/null @@ -1,32 +0,0 @@ -//lint:file-ignore U1000 Ignore all unused code -package nextpermutation - -import "sort" - -/* -時間:36分 -解法を思いつくのに時間がかかってしまいました(15分ぐらい)。 -後ろから見ていって昇順になっていない箇所を見つけたら、nums[i-1]よりも大きい中で最小の値を見つけてスワップし、後ろの要素をソートするというやり方を取りました。 -*/ -func nextPermutation_step1(nums []int) { - for i := len(nums) - 1; i > 0; i-- { - if nums[i-1] < nums[i] { - minN := nums[i] - idx := i - for j := i; j < len(nums); j++ { - if nums[j] < minN && nums[i-1] < nums[j] { - idx = j - minN = nums[j] - } - } - nums[i-1], nums[idx] = nums[idx], nums[i-1] - sort.Slice(nums[i:], func(a, b int) bool { - return nums[i+a] < nums[i+b] - }) - return - } - } - sort.Slice(nums, func(a, b int) bool { - return nums[a] < nums[b] - }) -} diff --git a/pullrequests/next_permutation/step2.go b/pullrequests/next_permutation/step2.go deleted file mode 100644 index 60ffc4d..0000000 --- a/pullrequests/next_permutation/step2.go +++ /dev/null @@ -1,28 +0,0 @@ -//lint:file-ignore U1000 Ignore all unused code -package nextpermutation - -import "sort" - -/* -変数名などをリファクタしました。 -*/ -func nextPermutation_step2(nums []int) { - for i := len(nums) - 1; i > 0; i-- { - if nums[i-1] < nums[i] { - candidate, candidateIndex := nums[i], i - for j := i; j < len(nums); j++ { - if nums[j] < candidate && nums[i-1] < nums[j] { - candidate, candidateIndex = nums[j], j - } - } - nums[i-1], nums[candidateIndex] = nums[candidateIndex], nums[i-1] - sort.Slice(nums[i:], func(a, b int) bool { - return nums[i+a] < nums[i+b] - }) - return - } - } - sort.Slice(nums, func(a, b int) bool { - return nums[a] < nums[b] - }) -} diff --git a/pullrequests/next_permutation/step3.go b/pullrequests/next_permutation/step3.go deleted file mode 100644 index e503255..0000000 --- a/pullrequests/next_permutation/step3.go +++ /dev/null @@ -1,30 +0,0 @@ -//lint:file-ignore U1000 Ignore all unused code -package nextpermutation - -/* -これまでの解法がソートを使っていたために時間計算量がO(n log n)になってしまったわけですが、他の人の回答を見てO(n)で解ける方法に変えました。 -nums[i-1]よりも後ろは昇順になっているのを利用し、リバースすればソートできるということに気づきませんでした。 -*/ -func nextPermutation_step3(nums []int) { - if len(nums) < 2 { - return - } - i := len(nums) - 2 - for i >= 0 && nums[i] >= nums[i+1] { - i-- - } - if i >= 0 { - j := len(nums) - 1 - for nums[j] <= nums[i] { - j-- - } - nums[i], nums[j] = nums[j], nums[i] - } - reverse(nums[i+1:]) -} - -func reverse(nums []int) { - for i, j := 0, len(nums)-1; i < j; i, j = i+1, j-1 { - nums[i], nums[j] = nums[j], nums[i] - } -} diff --git a/pullrequests/missing_number/step1.go b/pullrequests/number_of_1bits/step1.go similarity index 65% rename from pullrequests/missing_number/step1.go rename to pullrequests/number_of_1bits/step1.go index 67ecd4f..623f75f 100644 --- a/pullrequests/missing_number/step1.go +++ b/pullrequests/number_of_1bits/step1.go @@ -1,5 +1,5 @@ //lint:file-ignore U1000 Ignore all unused code -package template +package numberof1bits /* レビュワーの方へ: @@ -7,12 +7,15 @@ package template */ /* -方針としては0~len(nums)の和と実際の和の差分を取ればいい。 +時間:3分 + +rightmost set bitは n & (n - 1) で求めることができるので、効率的にcountしていくことができる。 */ -func missingNumberStep1(nums []int) int { - var difference int - for i, n := range nums { - difference += i + 1 - n +func hammingWeightStep1(n int) int { + count := 0 + for n > 0 { + n &= n - 1 + count += 1 } - return difference + return count } diff --git a/pullrequests/missing_number/step2.go b/pullrequests/number_of_1bits/step2.go similarity index 59% rename from pullrequests/missing_number/step2.go rename to pullrequests/number_of_1bits/step2.go index 1f46968..1ef4a31 100644 --- a/pullrequests/missing_number/step2.go +++ b/pullrequests/number_of_1bits/step2.go @@ -1,5 +1,5 @@ //lint:file-ignore U1000 Ignore all unused code -package template +package numberof1bits /* レビュワーの方へ: @@ -7,12 +7,13 @@ package template */ /* -0~len(nums)の和を求めるには算数的に求める方法も取れる。 +またはnの2の補数(-n)を使って、n & -n でrightmost set bitのみがsetされているビット列を取得し、最後にnから引くことでrightmost set bitをunsetすることもできる。 */ -func missingNumberStep2(nums []int) int { - var total int - for _, n := range nums { - total += n +func hammingWeightStep2(n int) int { + count := 0 + for n > 0 { + n -= (n & -n) + count += 1 } - return (1+len(nums))*len(nums)/2 - total + return count } diff --git a/pullrequests/palindrome_number/step2.go b/pullrequests/number_of_1bits/step3.go similarity index 59% rename from pullrequests/palindrome_number/step2.go rename to pullrequests/number_of_1bits/step3.go index cedc9d5..c97f5c3 100644 --- a/pullrequests/palindrome_number/step2.go +++ b/pullrequests/number_of_1bits/step3.go @@ -1,7 +1,5 @@ //lint:file-ignore U1000 Ignore all unused code -package palindromenumber - -import "strconv" +package numberof1bits /* レビュワーの方へ: @@ -9,17 +7,22 @@ import "strconv" */ /* -一応、文字列に変換する方法でも解いてみた。 +ナイーブに解くこともできる。この場合はO(n)の時間計算量になり、効率が悪い。 */ -func isPalindromeStep2(x int) bool { - strX := strconv.Itoa(x) - i, j := 0, len(strX)-1 - for i < j { - if strX[i] != strX[j] { - return false - } - i++ - j-- +func hammingWeightStep3_1(n int) int { + count := 0 + for n > 0 { + count += n % 2 + n /= 2 + } + return count +} + +func hammingWeightStep3_2(n int) int { + count := 0 + for n != 0 { + count += n & 1 + n >>= 1 } - return true + return count } diff --git a/pullrequests/palindrome_number/step1.go b/pullrequests/palindrome_number/step1.go deleted file mode 100644 index f35ae4c..0000000 --- a/pullrequests/palindrome_number/step1.go +++ /dev/null @@ -1,22 +0,0 @@ -//lint:file-ignore U1000 Ignore all unused code -package palindromenumber - -/* -レビュワーの方へ: - - このコードは既にGoの標準のフォーマッタで整形済みです。演算子の周りにスペースがあったりなかったりしますが、これはGoのフォーマッタによるもので、優先順位の高い演算子の周りにはスペースが入らず、低い演算子の周りには入るようになっています。https://qiita.com/tchssk/items/77030b4271cd192d0347 -*/ - -/* -時間:8分50秒 -最初に思いついたのは文字列に変換してチェックする方法だが、LeetCode上に文字列に変換しないで求めてみよと書かれていたのでまずは下記のようにして解いた。 -*/ -func isPalindromeStep1(x int) bool { - var reversed int - tmp := x - for tmp > 0 { - n := tmp % 10 - reversed = reversed*10 + n - tmp /= 10 - } - return x == reversed -} diff --git a/pullrequests/product_of_array_except_self/step1.go b/pullrequests/product_of_array_except_self/step1.go deleted file mode 100644 index dd80caa..0000000 --- a/pullrequests/product_of_array_except_self/step1.go +++ /dev/null @@ -1,35 +0,0 @@ -//lint:file-ignore U1000 Ignore all unused code -package template - -/* -時間:6分 -全体の積を求め、個々の数字で割ることで求める。 -解いた後に割り算を使ってはいけないということに気づいた。 -*/ -func productExceptSelfStep1(nums []int) []int { - allProduct, zeroCount := 0, 0 - for _, n := range nums { - if n == 0 { - zeroCount++ - continue - } - if allProduct == 0 { - allProduct = n - } else { - allProduct *= n - } - } - products := make([]int, len(nums)) - for i := range products { - if zeroCount > 0 { - if nums[i] == 0 && zeroCount == 1 { - products[i] = allProduct - } else { - products[i] = 0 - } - } else { - products[i] = allProduct / nums[i] - } - } - return products -} diff --git a/pullrequests/product_of_array_except_self/step2.go b/pullrequests/product_of_array_except_self/step2.go deleted file mode 100644 index 864039e..0000000 --- a/pullrequests/product_of_array_except_self/step2.go +++ /dev/null @@ -1,20 +0,0 @@ -//lint:file-ignore U1000 Ignore all unused code -package template - -/* -他の人の解法を見て書いた。 -*/ -func productExceptSelfStep2(nums []int) []int { - products := make([]int, len(nums)) - leftProduct := 1 - for i := 0; i < len(nums); i++ { - products[i] = leftProduct - leftProduct *= nums[i] - } - rightProduct := 1 - for i := len(nums) - 1; i >= 0; i-- { - products[i] *= rightProduct - rightProduct *= nums[i] - } - return products -} diff --git a/pullrequests/ransom_note/step1.go b/pullrequests/ransom_note/step1.go new file mode 100644 index 0000000..c54d387 --- /dev/null +++ b/pullrequests/ransom_note/step1.go @@ -0,0 +1,27 @@ +//lint:file-ignore U1000 Ignore all unused code +package ransomnote + +/* +レビュワーの方へ: + - このコードは既にGoの標準のフォーマッタで整形済みです。演算子の周りにスペースがあったりなかったりしますが、これはGoのフォーマッタによるもので、優先順位の高い演算子の周りにはスペースが入らず、低い演算子の周りには入るようになっています。https://qiita.com/tchssk/items/77030b4271cd192d0347 +*/ + +/* +時間:2分 +似たような問題を何回か解いたことがあるので特に困らなかった。 +入力が小文字に限らない場合は、runeをkeyとするmapを使えば良い。その際、Unicodeの扱いには気をつける必要がある。 +https://github.com/rihib/leetcode/pull/5#issue-2446890745 +*/ +func canConstructStep1(ransomNote string, magazine string) bool { + var frequency [26]int + for _, r := range magazine { + frequency[r-'a']++ + } + for _, r := range ransomNote { + frequency[r-'a']-- + if frequency[r-'a'] < 0 { + return false + } + } + return true +} diff --git a/pullrequests/ransom_note/step2.go b/pullrequests/ransom_note/step2.go new file mode 100644 index 0000000..e531624 --- /dev/null +++ b/pullrequests/ransom_note/step2.go @@ -0,0 +1,20 @@ +//lint:file-ignore U1000 Ignore all unused code +package ransomnote + +/* +レビュワーの方へ: + - このコードは既にGoの標準のフォーマッタで整形済みです。演算子の周りにスペースがあったりなかったりしますが、これはGoのフォーマッタによるもので、優先順位の高い演算子の周りにはスペースが入らず、低い演算子の周りには入るようになっています。https://qiita.com/tchssk/items/77030b4271cd192d0347 +*/ +func canConstructStep2(ransomNote string, magazine string) bool { + var frequencies [26]int + for _, r := range magazine { + frequencies[r-'a']++ + } + for _, r := range ransomNote { + frequencies[r-'a']-- + if frequencies[r-'a'] < 0 { + return false + } + } + return true +} diff --git a/pullrequests/reverse_linked_list/step1.go b/pullrequests/reverse_linked_list/step1.go new file mode 100644 index 0000000..817bbea --- /dev/null +++ b/pullrequests/reverse_linked_list/step1.go @@ -0,0 +1,32 @@ +//lint:file-ignore U1000 Ignore all unused code +package reverselinkedlist + +/* +COMMENT +*/ + +type ListNode struct { + Val int + Next *ListNode +} + +func reverseList_step1(head *ListNode) *ListNode { + if head == nil { + return head + } + if head.Next == nil { + return head + } + + prev := head + next := head.Next + head.Next = nil + + for next != nil { + nextNext := next.Next + next.Next = prev + prev = next + next = nextNext + } + return prev +} diff --git a/pullrequests/reverse_linked_list/step2.go b/pullrequests/reverse_linked_list/step2.go new file mode 100644 index 0000000..d484257 --- /dev/null +++ b/pullrequests/reverse_linked_list/step2.go @@ -0,0 +1,27 @@ +//lint:file-ignore U1000 Ignore all unused code +package reverselinkedlist + +/* +COMMENT +*/ + +func reverseList_step2(head *ListNode) *ListNode { + if head == nil { + return head + } + if head.Next == nil { + return head + } + + prev := head + curr := head.Next + head.Next = nil + + for curr != nil { + next := curr.Next + curr.Next = prev + prev = curr + curr = next + } + return prev +} diff --git a/pullrequests/reverse_linked_list/step3.go b/pullrequests/reverse_linked_list/step3.go new file mode 100644 index 0000000..1262387 --- /dev/null +++ b/pullrequests/reverse_linked_list/step3.go @@ -0,0 +1,47 @@ +//lint:file-ignore U1000 Ignore all unused code +package reverselinkedlist + +/* + 時間: 2分、2分 + イテレーティブな方法と再帰を使った方法の2通りで解きました。 + + 質問: + - 20行目について、1行にまとめているのですが、読みやすさなどの点ではどう思いますか? + + - 与えられたhead自体を操作するのは避けた方が良いか?(例えばcurrなどの別の変数に代入して + から操作した方が良いか) + + - 変数名としてreversedHeadか、reversedListHeadのどちらの方が良いか? + - reversedListHeadだとこれぐらいのコードの短さで使うにしては少し長すぎるかなと思った + のでここではreversedHeadでも十分通じると思い、これにしました + + 他の人のPRを見て思ったこと + - スタックを使って解く方法も確かにあるなと思いました。パフォーマンス的には再帰を使って解く + 方法とほぼ同じ?(再帰もスタックを使っていると言えば使っている) + + - スタックとヒープ + https://github.com/Ryotaro25/leetcode_first60/pull/8/files#r1617095525 + https://github.com/Ryotaro25/leetcode_first60/pull/8/files#r1616548694 + + - 再帰について + https://github.com/seal-azarashi/leetcode/pull/7/files#r1640157920 +*/ + +func reverseList_iterative_step3(head *ListNode) *ListNode { + var prev *ListNode + for head != nil { + prev, head, head.Next = head, head.Next, prev + } + return prev +} + +func reverseList_recurssive_step3(head *ListNode) *ListNode { + if head == nil || head.Next == nil { + return head + } + + next := head.Next + reversedHead := reverseList_recurssive_step3(next) + head.Next, next.Next = nil, head + return reversedHead +} diff --git a/pullrequests/reverse_linked_list/step4.go b/pullrequests/reverse_linked_list/step4.go new file mode 100644 index 0000000..bff0146 --- /dev/null +++ b/pullrequests/reverse_linked_list/step4.go @@ -0,0 +1,22 @@ +//lint:file-ignore U1000 Ignore all unused code +package reverselinkedlist + +func reverseList_iterative_step4(head *ListNode) *ListNode { + curr := head + var prev *ListNode + for curr != nil { + prev, curr, curr.Next = curr, curr.Next, prev + } + return prev +} + +func reverseList_recurssive_step4(head *ListNode) *ListNode { + curr := head + if curr == nil || curr.Next == nil { + return curr + } + next := curr.Next + reversedHead := reverseList_recurssive_step4(next) + curr.Next, next.Next = nil, curr + return reversedHead +} diff --git a/pullrequests/roman_to_integer/step1.go b/pullrequests/roman_to_integer/step1.go deleted file mode 100644 index 4bfbfc4..0000000 --- a/pullrequests/roman_to_integer/step1.go +++ /dev/null @@ -1,42 +0,0 @@ -//lint:file-ignore U1000 Ignore all unused code -package romantointeger - -/* -レビュワーの方へ: - - このコードは既にGoの標準のフォーマッタで整形済みです。演算子の周りにスペースがあったりなかったりしますが、これはGoのフォーマッタによるもので、優先順位の高い演算子の周りにはスペースが入らず、低い演算子の周りには入るようになっています。https://qiita.com/tchssk/items/77030b4271cd192d0347 -*/ - -/* -時間:25分 -方針自体はすぐに思いついたが、単純に実装に時間がかかってしまった。 -*/ -func romanToIntStep1(s string) int { - symbolToInt := map[rune]int{ - 'I': 1, - 'V': 5, - 'X': 10, - 'L': 50, - 'C': 100, - 'D': 500, - 'M': 1000, - } - total := 0 - runeS := []rune(s) - currIndex, nextIndex := 0, 1 - for currIndex < len(runeS) && nextIndex < len(runeS) { - curr, next := runeS[currIndex], runeS[nextIndex] - if (curr == 'I' && (next == 'V' || next == 'X')) || - (curr == 'X' && (next == 'L' || next == 'C')) || - (curr == 'C' && (next == 'D' || next == 'M')) { - total += symbolToInt[next] - symbolToInt[curr] - currIndex, nextIndex = currIndex+2, nextIndex+2 - continue - } - total += symbolToInt[curr] - currIndex, nextIndex = currIndex+1, nextIndex+1 - } - if currIndex < len(runeS) { - total += symbolToInt[runeS[currIndex]] - } - return total -} diff --git a/pullrequests/roman_to_integer/step2.go b/pullrequests/roman_to_integer/step2.go deleted file mode 100644 index 1634c56..0000000 --- a/pullrequests/roman_to_integer/step2.go +++ /dev/null @@ -1,33 +0,0 @@ -//lint:file-ignore U1000 Ignore all unused code -package romantointeger - -/* -レビュワーの方へ: - - このコードは既にGoの標準のフォーマッタで整形済みです。演算子の周りにスペースがあったりなかったりしますが、これはGoのフォーマッタによるもので、優先順位の高い演算子の周りにはスペースが入らず、低い演算子の周りには入るようになっています。https://qiita.com/tchssk/items/77030b4271cd192d0347 -*/ - -/* -前のシンボルの方が後ろのシンボルよりも小さい場合として考えることができる。 -ただ個人的にはStep1の方が要件に明確に沿っているので良いのではと思っている。 -*/ -func romanToIntStep2(s string) int { - symbolToInt := map[rune]int{ - 'I': 1, - 'V': 5, - 'X': 10, - 'L': 50, - 'C': 100, - 'D': 500, - 'M': 1000, - } - total := 0 - runeS := []rune(s) - for i, r := range runeS { - if i+1 < len(runeS) && symbolToInt[r] < symbolToInt[runeS[i+1]] { - total -= symbolToInt[r] - } else { - total += symbolToInt[r] - } - } - return total -} diff --git a/pullrequests/search_in_rotated_sorted_array/step1.go b/pullrequests/search_in_rotated_sorted_array/step1.go deleted file mode 100644 index f54e0f3..0000000 --- a/pullrequests/search_in_rotated_sorted_array/step1.go +++ /dev/null @@ -1,31 +0,0 @@ -//lint:file-ignore U1000 Ignore all unused code -package template - -/* -O(log n)で解くことを期待されていたので、何らかの形で二分探索を使うのだろうとは思っていたのですが、初見では解けなかったので、他の人の回答を参考にしました。 -どちらか片方は必ず必ず昇順にならんでいるということを利用して二分探索を行っています。 -下記は閉区画で解きました。 -*/ -func search_closed(nums []int, target int) int { - left, right := 0, len(nums)-1 - for left <= right { - mid := (left + right) / 2 - if target == nums[mid] { - return mid - } - if nums[left] <= nums[mid] { - if nums[left] <= target && target < nums[mid] { - right = mid - 1 - } else { - left = mid + 1 - } - } else { - if nums[mid] < target && target <= nums[right] { - left = mid + 1 - } else { - right = mid - 1 - } - } - } - return -1 -} diff --git a/pullrequests/search_in_rotated_sorted_array/step2.go b/pullrequests/search_in_rotated_sorted_array/step2.go deleted file mode 100644 index 59d3105..0000000 --- a/pullrequests/search_in_rotated_sorted_array/step2.go +++ /dev/null @@ -1,29 +0,0 @@ -//lint:file-ignore U1000 Ignore all unused code -package template - -/* -半閉区画でも解いてみました。 -*/ -func search_half_closed(nums []int, target int) int { - left, right := 0, len(nums) - for left < right { - mid := (left + right) / 2 - if target == nums[mid] { - return mid - } - if nums[left] <= nums[mid] { - if nums[left] <= target && target < nums[mid] { - right = mid - } else { - left = mid + 1 - } - } else { - if nums[mid] < target && target <= nums[right-1] { - left = mid + 1 - } else { - right = mid - } - } - } - return -1 -} diff --git a/pullrequests/search_insert_position/step1.go b/pullrequests/search_insert_position/step1.go new file mode 100644 index 0000000..f500b57 --- /dev/null +++ b/pullrequests/search_insert_position/step1.go @@ -0,0 +1,27 @@ +//lint:file-ignore U1000 Ignore all unused code +package searchinsertposition + +/* +時間:12分 +解法自体はすぐに浮かんで解くことができた。 +その割に二分探索を書くのがまだ慣れていなくて少し時間がかかってしまった。 +*/ +func searchInsert_step1(nums []int, target int) int { + left, right := 0, len(nums) + var mid int + for left < right { + mid = (left + right) / 2 + if target == nums[mid] { + return mid + } + if target < nums[mid] { + right = mid + } else { + left = mid + 1 + } + } + if target < nums[mid] { + return mid + } + return mid + 1 +} diff --git a/pullrequests/search_insert_position/step2.go b/pullrequests/search_insert_position/step2.go new file mode 100644 index 0000000..63236c2 --- /dev/null +++ b/pullrequests/search_insert_position/step2.go @@ -0,0 +1,22 @@ +//lint:file-ignore U1000 Ignore all unused code +package searchinsertposition + +/* +最後の部分はleftを返せ馬良いと気づき変更した。 +その他もより読みやすいように直した。 +*/ +func searchInsert_step2(nums []int, target int) int { + left, right := 0, len(nums) + for left < right { + mid := (left + right) / 2 + if nums[mid] == target { + return mid + } + if nums[mid] < target { + left = mid + 1 + } else { + right = mid + } + } + return left +} diff --git a/pullrequests/search_insert_position/step3.go b/pullrequests/search_insert_position/step3.go new file mode 100644 index 0000000..86f7164 --- /dev/null +++ b/pullrequests/search_insert_position/step3.go @@ -0,0 +1,22 @@ +//lint:file-ignore U1000 Ignore all unused code +package searchinsertposition + +/* +オーバーフローを起こさないようにmidの計算方法を修正した +https://github.com/hayashi-ay/leetcode/blob/064fca989bc4ecf9c7bce70237524a3e7ab1a21a/35.%20Search%20Insert%20Position.md?plain=1#L6 +*/ +func searchInsert_step3(nums []int, target int) int { + left, right := 0, len(nums) + for left < right { + mid := left + (right-left)/2 + if nums[mid] == target { + return mid + } + if nums[mid] < target { + left = mid + 1 + } else { + right = mid + } + } + return left +} diff --git a/pullrequests/search_insert_position/step4.go b/pullrequests/search_insert_position/step4.go new file mode 100644 index 0000000..345396d --- /dev/null +++ b/pullrequests/search_insert_position/step4.go @@ -0,0 +1,15 @@ +//lint:file-ignore U1000 Ignore all unused code +package searchinsertposition + +func searchInsert(nums []int, target int) int { + left, right := 0, len(nums) + for left < right { + mid := left + (right-left)/2 + if nums[mid] < target { + left = mid + 1 + } else { + right = mid + } + } + return left +} diff --git a/pullrequests/single_number/step1.go b/pullrequests/single_number/step1.go deleted file mode 100644 index b61f0be..0000000 --- a/pullrequests/single_number/step1.go +++ /dev/null @@ -1,26 +0,0 @@ -//lint:file-ignore U1000 Ignore all unused code -package singlenumber - -/* -レビュワーの方へ: - - このコードは既にGoの標準のフォーマッタで整形済みです。演算子の周りにスペースがあったりなかったりしますが、これはGoのフォーマッタによるもので、優先順位の高い演算子の周りにはスペースが入らず、低い演算子の周りには入るようになっています。https://qiita.com/tchssk/items/77030b4271cd192d0347 -*/ - -/* -時間:3分 -LeetCodeには空間計算量がO(1)になるように実装しろと書かれていたが、方法が思いつかなかったので、まずは普通に実装してみた。 -*/ -func singleNumberStep1(nums []int) int { - singleNums := make(map[int]int, len(nums)) - for _, n := range nums { - singleNums[n]++ - if singleNums[n] > 1 { - delete(singleNums, n) - } - } - // 必要に応じてlen(singleNums) != 1のときはエラーを返すという処理も追加しても良いかも - for n := range singleNums { - return n - } - return 0 // 本来は見つからなかった場合はエラーを返したい -} diff --git a/pullrequests/string_to_integer_atoi/step1.go b/pullrequests/string_to_integer_atoi/step1.go new file mode 100644 index 0000000..5aeee4a --- /dev/null +++ b/pullrequests/string_to_integer_atoi/step1.go @@ -0,0 +1,41 @@ +//lint:file-ignore U1000 Ignore all unused code +package stringtointegeratoi + +import "math" + +/* +時間:20分 +解法自体はすぐに思いついたのですが、色々とバグを取り除くのに時間がかかってしまいました。 +*/ +func myAtoi_step1(s string) int { + const ( + intMax = int(math.MaxInt32) + intMin = int(math.MinInt32) + ) + sign := 1 + i := 0 + num := 0 + for i < len(s) && s[i] == ' ' { + i++ + } + if i < len(s) && (s[i] == '-' || s[i] == '+') { + if s[i] == '-' { + sign = -1 + } + i++ + } + for i < len(s) && s[i] == '0' { + i++ + } + for i < len(s) && '0' <= rune(s[i]) && rune(s[i]) <= '9' { + num = num*10 + int(rune(s[i])-'0') + if sign == 1 && num > intMax { + return intMax + } + if sign == -1 && -num < intMin { + return intMin + } + i++ + } + return num * sign +} diff --git a/pullrequests/string_to_integer_atoi/step2.go b/pullrequests/string_to_integer_atoi/step2.go new file mode 100644 index 0000000..73bc1fe --- /dev/null +++ b/pullrequests/string_to_integer_atoi/step2.go @@ -0,0 +1,40 @@ +//lint:file-ignore U1000 Ignore all unused code +package stringtointegeratoi + +import "math" + +/* +Goではrune型はint32、byte型はint8のエイリアスで、明示的にキャストしなくても内部でキャストしてくれるので、そのまま比較できるため、rune型にキャストするのをやめました。 +*/ +func myAtoi_step2(s string) int { + const ( + intMax = int(math.MaxInt32) + intMin = int(math.MinInt32) + ) + + i := 0 + for i < len(s) && s[i] == ' ' { + i++ + } + + sign := 1 + if i < len(s) && (s[i] == '-' || s[i] == '+') { + if s[i] == '-' { + sign = -1 + } + i++ + } + + num := 0 + for i < len(s) && '0' <= s[i] && s[i] <= '9' { + num = num*10 + int(s[i]-'0') + if sign == 1 && num > intMax { + return intMax + } + if sign == -1 && -num < intMin { + return intMin + } + i++ + } + return sign * num +} diff --git a/pullrequests/string_to_integer_atoi/step3.go b/pullrequests/string_to_integer_atoi/step3.go new file mode 100644 index 0000000..a34066a --- /dev/null +++ b/pullrequests/string_to_integer_atoi/step3.go @@ -0,0 +1,41 @@ +//lint:file-ignore U1000 Ignore all unused code +package stringtointegeratoi + +import "math" + +/* +32bit環境でもオーバーフローしないようにコードを変えてみました。 +*/ +func myAtoi(s string) int { + const ( + intMax = int(math.MaxInt32) + intMin = int(math.MinInt32) + ) + + i := 0 + for i < len(s) && s[i] == ' ' { + i++ + } + + sign := 1 + if i < len(s) && (s[i] == '-' || s[i] == '+') { + if s[i] == '-' { + sign = -1 + } + i++ + } + + num := 0 + for i < len(s) && '0' <= s[i] && s[i] <= '9' { + next := int(s[i] - '0') + if sign != -1 && num > (intMax-next)/10 { + return intMax + } + if sign == -1 && -num < (intMin+next)/10 { + return intMin + } + num = num*10 + next + i++ + } + return sign * num +} diff --git a/pullrequests/string_to_integer_atoi/step4.go b/pullrequests/string_to_integer_atoi/step4.go new file mode 100644 index 0000000..74aa35c --- /dev/null +++ b/pullrequests/string_to_integer_atoi/step4.go @@ -0,0 +1,41 @@ +//lint:file-ignore U1000 Ignore all unused code +package stringtointegeratoi + +import "math" + +/* +Step3のオーバーフローしてしまうバグを直しました。 +*/ +func myAtoi_step4(s string) int { + const ( + intMax = int(math.MaxInt32) + intMin = int(math.MinInt32) + ) + + i := 0 + for i < len(s) && s[i] == ' ' { + i++ + } + + sign := 1 + if i < len(s) && (s[i] == '-' || s[i] == '+') { + if s[i] == '-' { + sign = -1 + } + i++ + } + + num := 0 + for i < len(s) && '0' <= s[i] && s[i] <= '9' { + digit := int(s[i] - '0') + if sign == 1 && (num > intMax/10 || num == intMax/10 && digit >= intMax%10) { + return intMax + } + if sign == -1 && (-num < intMin/10 || -num == intMin/10 && -digit <= intMin%10) { + return intMin + } + num = num*10 + digit + i++ + } + return sign * num +} diff --git a/pullrequests/symmetric_tree/step1.go b/pullrequests/symmetric_tree/step1.go deleted file mode 100644 index ad88730..0000000 --- a/pullrequests/symmetric_tree/step1.go +++ /dev/null @@ -1,35 +0,0 @@ -//lint:file-ignore U1000 Ignore all unused code -package symmetrictree - -type TreeNode struct { - Val int - Left *TreeNode - Right *TreeNode -} - -/* -レビュワーの方へ: - - このコードは既にGoの標準のフォーマッタで整形済みです。演算子の周りにスペースがあったりなかったりしますが、これはGoのフォーマッタによるもので、優先順位の高い演算子の周りにはスペースが入らず、低い演算子の周りには入るようになっています。https://qiita.com/tchssk/items/77030b4271cd192d0347 -*/ - -/* -初見で解くことができなかったので、他の解法を見て解いた。再帰の練習が足りてなさそう。 -*/ -func isSymmetricRecursive(root *TreeNode) bool { - // 問題的にはrootはnilにならないのでこの処理は必要ないが - // 一応nilのときはtrue扱いにすると決めて書いた - if root == nil { - return true - } - return isMirror(root.Left, root.Right) -} - -func isMirror(left, right *TreeNode) bool { - if left == nil && right == nil { - return true - } - if left == nil || right == nil { - return false - } - return left.Val == right.Val && isMirror(left.Left, right.Right) && isMirror(left.Right, right.Left) -} diff --git a/pullrequests/symmetric_tree/step2.go b/pullrequests/symmetric_tree/step2.go deleted file mode 100644 index 37ab6b1..0000000 --- a/pullrequests/symmetric_tree/step2.go +++ /dev/null @@ -1,36 +0,0 @@ -//lint:file-ignore U1000 Ignore all unused code -package symmetrictree - -import "container/list" - -/* -レビュワーの方へ: - - このコードは既にGoの標準のフォーマッタで整形済みです。演算子の周りにスペースがあったりなかったりしますが、これはGoのフォーマッタによるもので、優先順位の高い演算子の周りにはスペースが入らず、低い演算子の周りには入るようになっています。https://qiita.com/tchssk/items/77030b4271cd192d0347 -*/ - -/* -イテレーティブにも解いてみた。BFSなのでキューを使って実装した。 -*/ -func isSymmetric(root *TreeNode) bool { - if root == nil { - return true - } - queue := list.New() - queue.PushBack(root.Left) - queue.PushBack(root.Right) - for queue.Len() > 0 { - left := queue.Remove(queue.Front()).(*TreeNode) - right := queue.Remove(queue.Front()).(*TreeNode) - if left == nil && right == nil { - continue - } - if left == nil || right == nil || left.Val != right.Val { - return false - } - queue.PushBack(left.Left) - queue.PushBack(right.Right) - queue.PushBack(left.Right) - queue.PushBack(right.Left) - } - return true -} diff --git a/pullrequests/two_sum/step1.go b/pullrequests/two_sum/step1.go new file mode 100644 index 0000000..ba27643 --- /dev/null +++ b/pullrequests/two_sum/step1.go @@ -0,0 +1,18 @@ +//lint:file-ignore U1000 Ignore all unused code +package twosum + +/* +かなり前に解いたものなので詳細は忘れてしまいましたが、ナイーブにやる方法では各文字ごとに毎回リストを走査してしまうと時間計算量がO(n^2)になってしまうので、オーバーヘッドはありますが、ハッシュ化するのが良いと考えました。 + +また同じ要素を2回使うのを避けるために、毎回追加する前に対応する要素がないかを確認してから追加するようにしました。 +*/ +func twoSumStep1(nums []int, target int) []int { + m := make(map[int]int) + for i, n := range nums { + if j, ok := m[target-n]; ok { + return []int{i, j} + } + m[n] = i + } + return nil +} diff --git a/pullrequests/two_sum/step2.go b/pullrequests/two_sum/step2.go new file mode 100644 index 0000000..7a8ea9e --- /dev/null +++ b/pullrequests/two_sum/step2.go @@ -0,0 +1,25 @@ +//lint:file-ignore U1000 Ignore all unused code +package twosum + +/* +mではなく、よりわかりやすいようにnumsMapとしました。 +GoogleのGoスタイルガイドには変数名に型名を使うのは良くないと書かれていますが、同時に下記のようにも書かれています。今回はnumsという配列をマップに変換したもの(配列もインデックスと値を情報としてもつ)と捉えることができるため、対応していることを示すためにnumsMapとしました。 + +`It is acceptable to include a type-like qualifier if there are two versions of a value in scope, for example you might have an input stored in ageString and use age for the parsed value.` + +`numToIdx`というのもありそう(https://github.com/aoshi2025s/leetcode-review/pull/1#discussion_r1666780953)。 + +対応する組み合わせが見つからなかった際にどうするのかは難しいところ。 + - https://github.com/seal-azarashi/leetcode/pull/11#discussion_r1672537855 + - https://github.com/sendahuang14/leetcode/pull/11#discussion_r1702393602 +*/ +func twoSumStep2(nums []int, target int) []int { + numsMap := make(map[int]int) + for i, n := range nums { + if j, ok := numsMap[target-n]; ok { + return []int{i, j} + } + numsMap[n] = i + } + return nil +} diff --git a/pullrequests/two_sum/step3.go b/pullrequests/two_sum/step3.go new file mode 100644 index 0000000..dff0f34 --- /dev/null +++ b/pullrequests/two_sum/step3.go @@ -0,0 +1,13 @@ +//lint:file-ignore U1000 Ignore all unused code +package twosum + +func twoSumStep3(nums []int, target int) []int { + numToIndex := make(map[int]int) + for i, n := range nums { + if j, ok := numToIndex[target-n]; ok { + return []int{i, j} + } + numToIndex[n] = i + } + return nil // 本来ならerrorを返したい +} diff --git a/pullrequests/valid_anagram/step1.go b/pullrequests/valid_anagram/step1.go new file mode 100644 index 0000000..5f67ef2 --- /dev/null +++ b/pullrequests/valid_anagram/step1.go @@ -0,0 +1,45 @@ +//lint:file-ignore U1000 Ignore all unused code +package validanagram + +/* +かなり前に解いたものなので、詳細については忘れてしましました。 +小文字のアルファベットのみが入力として与えられる場合と、Unicode文字も含まれる場合の2つの解法が含まれています。 + +基本的にはそれぞれの文字列に含まれる文字の種類と数が一致しているかを見ています。 +下の解法について、40行目で0未満になった場合のみfalseを返すのは一見おかしいように見えますが、30行目で文字数が等しいことを確認しているため、一方より多い文字があれば何かの文字は少なくなるはずであるため、ロジック的には問題ないと思います。 +*/ +func isAnagramStep1(s string, t string) bool { + if len(s) != len(t) { + return false + } + + var freq [26]int + for i := 0; i < len(s); i++ { + freq[s[i]-'a']++ + freq[t[i]-'a']-- + } + for _, v := range freq { + if v != 0 { + return false + } + } + return true +} + +func isAnagramUnicodeStep1(s string, t string) bool { + if len(s) != len(t) { + return false + } + + freq := make(map[rune]int) + for _, r := range s { + freq[r]++ + } + for _, r := range t { + freq[r]-- + if freq[r] < 0 { + return false + } + } + return true +} diff --git a/pullrequests/valid_anagram/step2.go b/pullrequests/valid_anagram/step2.go new file mode 100644 index 0000000..09f0307 --- /dev/null +++ b/pullrequests/valid_anagram/step2.go @@ -0,0 +1,41 @@ +//lint:file-ignore U1000 Ignore all unused code +package validanagram + +/* +よりシンプルにわかりやすく書くようにしました。例えばfreqのように省略するのではなくfrequencyにしました。 + +上については、Goの場合、s[i]のようにアクセスするとバイト列にアクセスすることになるので、今回の場合はたまたまascii文字しか入力として与えられないので問題ないのですが、あまりよろしくないと思ったので文字としてアクセスするように変更しました。 + +下の解法についてはStep1のロジックがわかりづらかったので、より明確になるように変更しました。 +*/ +func isAnagramStep2(s string, t string) bool { + var frequency [26]int + for _, r := range s { + frequency[r-'a']++ + } + for _, r := range t { + frequency[r-'a']-- + } + for _, n := range frequency { + if n != 0 { + return false + } + } + return true +} + +func isAnagramUnicodeStep2(s string, t string) bool { + frequency := make(map[rune]int) + for _, r := range s { + frequency[r]++ + } + for _, r := range t { + frequency[r]-- + } + for _, n := range frequency { + if n != 0 { + return false + } + } + return true +} diff --git a/pullrequests/valid_anagram/step3.go b/pullrequests/valid_anagram/step3.go new file mode 100644 index 0000000..d781c83 --- /dev/null +++ b/pullrequests/valid_anagram/step3.go @@ -0,0 +1,32 @@ +//lint:file-ignore U1000 Ignore all unused code +package validanagram + +func isAnagramStep3(s string, t string) bool { + return frequency(s) == frequency(t) +} + +func frequency(s string) [26]int { + var f [26]int + for _, r := range s { + f[r-'a']++ + } + return f +} + +// ちゃんとUnicodeに対応させるなら結合文字などを考慮する必要がある +// https://github.com/rihib/leetcode/pull/5#discussion_r1706198268 +func isAnagramUnicodeStep3(s string, t string) bool { + frequency := make(map[rune]int) + for _, r := range s { + frequency[r]++ + } + for _, r := range t { + frequency[r]-- + } + for _, n := range frequency { + if n != 0 { + return false + } + } + return true +} diff --git a/pullrequests/valid_anagram/step4.go b/pullrequests/valid_anagram/step4.go new file mode 100644 index 0000000..b63c510 --- /dev/null +++ b/pullrequests/valid_anagram/step4.go @@ -0,0 +1,32 @@ +//lint:file-ignore U1000 Ignore all unused code +package validanagram + +func isAnagramStep4(s string, t string) bool { + return frequencies(s) == frequencies(t) +} + +func frequencies(s string) [26]int { + var f [26]int + for _, r := range s { + f[r-'a']++ + } + return f +} + +// ちゃんとUnicodeに対応させるなら結合文字などを考慮する必要がある +// https://github.com/rihib/leetcode/pull/5#discussion_r1706198268 +func isAnagramUnicodeStep4(s string, t string) bool { + frequencies := make(map[rune]int) + for _, r := range s { + frequencies[r]++ + } + for _, r := range t { + frequencies[r]-- + } + for _, n := range frequencies { + if n != 0 { + return false + } + } + return true +} diff --git a/pullrequests/zigzag_conversion/step1.go b/pullrequests/zigzag_conversion/step1.go new file mode 100644 index 0000000..4888418 --- /dev/null +++ b/pullrequests/zigzag_conversion/step1.go @@ -0,0 +1,27 @@ +//lint:file-ignore U1000 Ignore all unused code +package zigzagconversion + +/* +時間:34分 +解法を思いつくのに時間がかかってしまいました(10分ぐらい?)。 +規則性を持っており、インデックスからどの行になるかを求めることができると思ったのでこの解法になりました。 +*/ +func convert_step1(s string, numRows int) string { + if numRows == 1 { + return s + } + var rows = make([][]rune, numRows) + for i, r := range s { + tmp := i % (numRows*2 - 2) + if tmp < numRows { + rows[tmp] = append(rows[tmp], r) + } else { + rows[2*numRows-tmp-2] = append(rows[2*numRows-tmp-2], r) + } + } + var res []rune + for _, row := range rows { + res = append(res, row...) + } + return string(res) +} diff --git a/pullrequests/zigzag_conversion/step2.go b/pullrequests/zigzag_conversion/step2.go new file mode 100644 index 0000000..7a8bc45 --- /dev/null +++ b/pullrequests/zigzag_conversion/step2.go @@ -0,0 +1,27 @@ +//lint:file-ignore U1000 Ignore all unused code +package zigzagconversion + +/* +より読みやすくなるようにリファクタしました。 +*/ +func convert_step2(s string, numRows int) string { + if numRows == 1 { + return s + } + + rows := make([][]rune, numRows) + cycleLen := 2*numRows - 2 + for i, r := range s { + rowNum := i % cycleLen + if rowNum >= numRows { + rowNum = cycleLen - rowNum + } + rows[rowNum] = append(rows[rowNum], r) + } + + var oneline []rune + for _, row := range rows { + oneline = append(oneline, row...) + } + return string(oneline) +} diff --git a/pullrequests/zigzag_conversion/step3.go b/pullrequests/zigzag_conversion/step3.go new file mode 100644 index 0000000..34152a3 --- /dev/null +++ b/pullrequests/zigzag_conversion/step3.go @@ -0,0 +1,50 @@ +//lint:file-ignore U1000 Ignore all unused code +package zigzagconversion + +import "strings" + +/* +文字列結合のパフォーマンスが良くなるように変更しました。 +また、空間計算量がO(1)になる解法も追加しました。 +*/ +func convert_step3(s string, numRows int) string { + if numRows == 1 { + return s + } + + rows := make([]strings.Builder, numRows) + cycleLen := 2*numRows - 2 + for i, r := range s { + rowNum := i % cycleLen + if rowNum >= numRows { + rowNum = cycleLen - rowNum + } + rows[rowNum].WriteRune(r) + } + + var oneline strings.Builder + for _, row := range rows { + oneline.WriteString(row.String()) + } + return oneline.String() +} + +func convert_step3_anothersolution(s string, numRows int) string { + if numRows == 1 { + return s + } + var oneline strings.Builder + cycleLen := 2*numRows - 2 + for rowNum := 0; rowNum < numRows; rowNum++ { + for i := rowNum; i < len(s); i += cycleLen { + oneline.WriteByte(s[i]) + if 0 < rowNum && rowNum < numRows-1 { + diagIdx := i + cycleLen - rowNum*2 + if diagIdx < len(s) { + oneline.WriteByte(s[diagIdx]) + } + } + } + } + return oneline.String() +} diff --git a/pullrequests/zigzag_conversion/step4.go b/pullrequests/zigzag_conversion/step4.go new file mode 100644 index 0000000..eeaa1f6 --- /dev/null +++ b/pullrequests/zigzag_conversion/step4.go @@ -0,0 +1,47 @@ +//lint:file-ignore U1000 Ignore all unused code +package zigzagconversion + +import "strings" + +func convert(s string, numRows int) string { + if numRows == 1 { + return s + } + + rows := make([]strings.Builder, numRows) + cycleLength := 2*numRows - 2 + for i, r := range s { + rowIndex := i % cycleLength + if rowIndex >= numRows { + rowIndex = cycleLength - rowIndex + } + rows[rowIndex].WriteRune(r) + } + + var oneline strings.Builder + for _, row := range rows { + oneline.WriteString(row.String()) + } + return oneline.String() +} + +func convert_anothersolution(s string, numRows int) string { + if numRows == 1 { + return s + } + var oneline strings.Builder + cycleLength := 2*numRows - 2 + for rowIndex := 0; rowIndex < numRows; rowIndex++ { + for i := rowIndex; i < len(s); i += cycleLength { + oneline.WriteByte(s[i]) + if rowIndex == 0 || rowIndex == numRows-1 { + continue + } + diagIndex := i + cycleLength - rowIndex*2 + if diagIndex < len(s) { + oneline.WriteByte(s[diagIndex]) + } + } + } + return oneline.String() +}