add two numbers
golangで60 questions to solveを説いてみるシリーズ。
今回の問題はAdd Two Numbers。
Add Two Numbers
まずは自分の限界。
package AddTwoNumbers
import (
"strconv"
)
type ListNode struct {
Val int
Next *ListNode
}
func addTwoNumbers(l1 *ListNode, l2 *ListNode) *ListNode {
connectedL1 := nodeConnect(l1)
connectedL2 := nodeConnect(l2)
ansval := connectedL1 + connectedL2
ansstr := strconv.Itoa(ansval)
ans := toList(Reverse(ansstr))
return ans
}
func Reverse(s string) string {
runes := []rune(s)
for i, j := 0, len(runes)-1; i < j; i, j = i+1, j-1 {
runes[i], runes[j] = runes[j], runes[i]
}
return string(runes)
}
func toList(s string) *ListNode {
l := &ListNode{}
for i, sRune := range s {
val, err := strconv.Atoi(string(sRune))
if err != nil {
panic(err)
}
if i == 0 {
l.Val = val
} else {
tmpl := l
for {
if tmpl.Next != nil {
tmpl = tmpl.Next
continue
}
tmpl.Next = &ListNode{
Val: val,
}
break
}
}
}
return l
}
func nodeConnect(l *ListNode) int {
conStr := ""
for {
conStr += strconv.Itoa(l.Val)
l = l.Next
if l == nil {
val, err := strconv.Atoi(Reverse(conStr))
if err != nil {
panic(err)
}
return val
}
}
}
大体正解するけど、[1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1]
でパニックが出る。31桁なんてどうやればいいんだ?
考え方を変えないとだめだな。
linked list
このようなデータ構造をlinked listと呼ぶ。スタックやキューの実装に便利だが、n番目の要素にすぐにたどり着いつくのが難しい。
上のlistNodeから順番に足していって、繰り上げていく。 下記の図のように。
このあとも、golangのpointerの仕組みを使ってなんとかしようとしたりしたけど、難しかった。 そして試行錯誤してなんとか正解にたどり着いたのがこちら
func addTwoNumbers(l1 *ListNode, l2 *ListNode) *ListNode {
ans := &ListNode{}
result := ans
carry := 0
for {
if carry == 1 && l1 == nil && l2 == nil {
ans.Val = 1
break
} else if l1 != nil && l2 != nil {
sum := l1.Val + l2.Val + carry
ans.Val = sum % 10
carry = sum / 10
l1 = l1.Next
l2 = l2.Next
if l1 != nil || l2 != nil || carry == 1 {
ans.Next = &ListNode{}
ans = ans.Next
}
} else if l1 == nil && l2 != nil {
sum := l2.Val + carry
carry = sum / 10
ans.Val = sum % 10
l2 = l2.Next
if l2 != nil || carry == 1 {
ans.Next = &ListNode{}
ans = ans.Next
}
} else if l1 != nil && l2 == nil {
sum := l1.Val + carry
carry = sum / 10
ans.Val = sum % 10
l1 = l1.Next
if l1 != nil || carry == 1 {
ans.Next = &ListNode{}
ans = ans.Next
}
} else {
break
}
}
return result
}
if条件がむちゃくちゃだし、ポインタをトリッキーに使っているしなんとも不安になるコードだ。
ということで、むちゃくちゃシンプルな解法がこれ
func addTwoNumbersRecursive(l1 *ListNode, l2 *ListNode) *ListNode {
if l1 == nil && l2 == nil {
return nil
} else if l1 == nil && l2 != nil {
return l2
} else if l1 != nil && l2 == nil {
return l1
}
sum := l1.Val + l2.Val
next := addTwoNumbersRecursive(l1.Next, l2.Next)
if sum >= 10 {
carry := sum / 10
sum %= 10
next = addTwoNumbersRecursive(next, &ListNode{Val: carry})
}
return &ListNode{Val: sum, Next: next}
}
再帰を全然書いていないからこのコードを見せられてもサクッと読めない・・・
時間計算量
O(max(m,n))
となる。maxってなんだ。ちょっとググったところ、このmax
はexcelとかのそれと変わらない。
m, nのうち最大の最大の数ということになる。
O(n) + O(m) = O(n + m) = O(max(n,m))
となる。
空間計算量
O(1)
かな。ちがうか。計算結果を格納するのに最大必要になる要素数はO(n) + O(m) + 1
か。