diff --git a/Week_01/README.md b/Week_01/README.md index 50de304..db568b0 100644 --- a/Week_01/README.md +++ b/Week_01/README.md @@ -1 +1,7 @@ -学习笔记 \ No newline at end of file +学习笔记 + +1、基础数据结构平时工作中用的比较少,但是面试过程中被问很多,几乎都忘了,通过第一周的学习对数组、链表、跳表、队列、栈有了一定的了解,但是在leetcode刷题过程中还有很多不足,不少题想不到思路,还需要进一步通过刷题来加深理解。 + +2、通过第一周老师讲的相关数据结构,在刷题中感觉滑动窗口,单调栈等相关题目较难,还要通过相关题目来进一步加强训练。老师说的题目朝着if、else、for、递归那个方向进行分解的方式很实用,双指针法在解题过程中感觉很妙,能解决不少看起来没啥思路的难题。 + +3、这一周还是有点追求题量,第一天做了的题有些第二天都会想不起来怎么做,需要更进一步贯彻老师说的五毒神掌,多刷老题,把做过的题做到熟练掌握。 diff --git a/Week_01/design-circular-deque/myCircularDeque.go b/Week_01/design-circular-deque/myCircularDeque.go new file mode 100644 index 0000000..607d816 --- /dev/null +++ b/Week_01/design-circular-deque/myCircularDeque.go @@ -0,0 +1,128 @@ +package design_circular_deque + +type MyCircularDeque struct { + // 存储数据的slice + nums []int + // 队列大小, 方便队列空、满判断,为传入的k+1,多出的这个位置不放元素 + k int + // 头指针, 队列头即第一个元素所在位置 + head int + // 尾指针,队列尾,执行最后一个元素的下一个位置 + tail int +} + + +/** Initialize your data structure here. Set the size of the deque to be k. */ +func Constructor(k int) MyCircularDeque { + return MyCircularDeque{ + nums: make([]int, k+1), + k: k+1, + head: 0, + tail: 0, + } +} + + +/** Adds an item at the front of Deque. Return true if the operation is successful. */ +func (this *MyCircularDeque) InsertFront(value int) bool { + // 要插入元素先判满 + if this.IsFull() { + return false + } + // 防止越界 + this.head = (this.head - 1 + this.k) % this.k + this.nums[this.head] = value + return true +} + + +/** Adds an item at the rear of Deque. Return true if the operation is successful. */ +func (this *MyCircularDeque) InsertLast(value int) bool { + // 要插入元素先判满 + if this.IsFull() { + return false + } + // 尾部插入元素后,尾指针要加一 + this.nums[this.tail] = value + // 防止越界 + this.tail = (this.tail + 1) % this.k + return true +} + + +/** Deletes an item from the front of Deque. Return true if the operation is successful. */ +func (this *MyCircularDeque) DeleteFront() bool { + // 要删除元素先判空 + if this.IsEmpty() { + return false + } + // 防止越界 + this.head = (this.head + 1) % this.k + return true +} + + +/** Deletes an item from the rear of Deque. Return true if the operation is successful. */ +func (this *MyCircularDeque) DeleteLast() bool { + // 要删除元素先判空 + if this.IsEmpty() { + return false + } + // 防止越界 + this.tail = (this.tail - 1 + this.k) % this.k + return true +} + + +/** Get the front item from the deque. */ +func (this *MyCircularDeque) GetFront() int { + // 获取元素先判空 + if this.IsEmpty() { + return -1 + } + return this.nums[this.head] +} + + +/** Get the last item from the deque. */ +func (this *MyCircularDeque) GetRear() int { + // 获取元素先判空 + if this.IsEmpty() { + return -1 + } + // 防止越界 + return this.nums[(this.tail - 1 + this.k) % this.k] +} + + +/** Checks whether the circular deque is empty or not. */ +func (this *MyCircularDeque) IsEmpty() bool { + if this.head == this.tail { + return true + }else{ + return false + } +} + + +/** Checks whether the circular deque is full or not. */ +func (this *MyCircularDeque) IsFull() bool { + if (this.tail + 1) % this.k == this.head { + return true + }else{ + return false + } +} + +/** + * Your MyCircularDeque object will be instantiated and called as such: + * obj := Constructor(k); + * param_1 := obj.InsertFront(value); + * param_2 := obj.InsertLast(value); + * param_3 := obj.DeleteFront(); + * param_4 := obj.DeleteLast(); + * param_5 := obj.GetFront(); + * param_6 := obj.GetRear(); + * param_7 := obj.IsEmpty(); + * param_8 := obj.IsFull(); + */ diff --git a/Week_01/design-circular-deque/myCircularDeque_test.go b/Week_01/design-circular-deque/myCircularDeque_test.go new file mode 100644 index 0000000..d48d97f --- /dev/null +++ b/Week_01/design-circular-deque/myCircularDeque_test.go @@ -0,0 +1,52 @@ +package design_circular_deque + +import "testing" + +func TestConstructor(t *testing.T) { + deque := Constructor(3) + want := true + got := deque.InsertLast(1) + if got != want { + t.Errorf("want:%t got:%t", want, got) + } + want = true + got = deque.InsertLast(2) + if got != want { + t.Errorf("want:%t got:%t", want, got) + } + want = true + got = deque.InsertFront(3) + if got != want { + t.Errorf("want:%t got:%t", want, got) + } + want = false + got = deque.InsertFront(4) + if got != want { + t.Errorf("want:%t got:%t", want, got) + } + want2 := 2 + got2 := deque.GetRear() + if got2 != want2 { + t.Errorf("want:%d got:%d", want2, got2) + } + want = true + got = deque.IsFull() + if got != want { + t.Errorf("want:%t got:%t", want, got) + } + want = true + got = deque.DeleteLast() + if got != want { + t.Errorf("want:%t got:%t", want, got) + } + want = true + got = deque.InsertFront(4) + if got != want { + t.Errorf("want:%t got:%t", want, got) + } + want2 = 4 + got2 = deque.GetFront() + if got2 != want2 { + t.Errorf("want:%d got:%d", want2, got2) + } +} diff --git a/Week_01/merge-sorted-array/merge.go b/Week_01/merge-sorted-array/merge.go new file mode 100644 index 0000000..046210b --- /dev/null +++ b/Week_01/merge-sorted-array/merge.go @@ -0,0 +1,27 @@ +package merge_sorted_array + +func Merge(nums1 []int, m int, nums2 []int, n int) { + // 从尾部插入 + // 用于记录数据放入的位置, + tail, p1, p2 := m + n - 1, m - 1, n - 1 + // nums1和nums2都没有处理完,则一直处理 + for p1 >= 0 && p2 >= 0 { + // 插入较大值 + if nums1[p1] > nums2[p2] { + nums1[tail] = nums1[p1] + p1-- + }else{ + nums1[tail] = nums2[p2] + p2-- + } + tail-- + } + // nums1的元素先处理完,需要继续把p2剩余的元素插入nums1中 + if p1 < 0 { + for i := p2; i >=0; i-- { + nums1[i] = nums2[i] + } + } + // nums2的元素先处理完,nums1中的其他元素不用再处理 + // if p2 < 0 +} diff --git a/Week_01/merge-sorted-array/merge_test.go b/Week_01/merge-sorted-array/merge_test.go new file mode 100644 index 0000000..2300a75 --- /dev/null +++ b/Week_01/merge-sorted-array/merge_test.go @@ -0,0 +1,15 @@ +package merge_sorted_array + +import "testing" + +func TestMerge(t *testing.T) { + nums1 := []int{1, 2, 3, 0, 0, 0} + nums2 := []int{2, 5, 6} + Merge(nums1,3, nums2, 3) + want := []int{1,2,2,3,5,6} + for idx, n := range want { + if nums1[idx] != n { + t.Errorf("want:%d got:%d", n, nums1[idx]) + } + } +} diff --git a/Week_01/merge-two-sorted-lists/mergeTwoLists.go b/Week_01/merge-two-sorted-lists/mergeTwoLists.go new file mode 100644 index 0000000..cdfae76 --- /dev/null +++ b/Week_01/merge-two-sorted-lists/mergeTwoLists.go @@ -0,0 +1,36 @@ +package merge_two_sorted_lists + +type ListNode struct { + Val int + Next *ListNode +} + +func MergeTwoLists(l1 *ListNode, l2 *ListNode) *ListNode { + // 记录新链表头节点,为哑节点, newHead.Next为实际链表 + newHead := &ListNode{} + // 用于遍历用 + pre := newHead + // 只要有一个链表没遍历完就继续处理 + for l1 != nil || l2 != nil { + // 如果两个链表都没有遍历完,则比较大小,较小链表为所求,并向后移动 + if l1 != nil && l2 != nil { + if l1.Val < l2.Val { + pre.Next = l1 + l1 = l1.Next + pre = pre.Next + }else{ + pre.Next = l2 + l2 = l2.Next + pre = pre.Next + } + // 已经有一个链表遍历完成,只需将未遍历完的拼接在新链表的尾部就可以结束了 + }else if l1 != nil { + pre.Next = l1 + break + }else if l2 != nil { + pre.Next = l2 + break + } + } + return newHead.Next +} diff --git a/Week_01/merge-two-sorted-lists/mergeTwoLists_test.go b/Week_01/merge-two-sorted-lists/mergeTwoLists_test.go new file mode 100644 index 0000000..a945bde --- /dev/null +++ b/Week_01/merge-two-sorted-lists/mergeTwoLists_test.go @@ -0,0 +1,29 @@ +package merge_two_sorted_lists + +import "testing" + +func createList(arr []int) *ListNode { + head := &ListNode{} + pre := head + for _, item := range arr { + pre.Next = &ListNode{Val: item} + pre = pre.Next + } + return head.Next +} + +func TestMergeTwoLists(t *testing.T) { + l1 := createList([]int{1,2,4}) + l2 := createList([]int{1,3,4}) + got := MergeTwoLists(l1, l2) + want := []int{1,1,2,3,4,4} + idx := 0 + for got != nil { + if got.Val != want[idx] { + t.Errorf("want:%d got:%d", want[idx], got.Val) + break + } + idx++ + got = got.Next + } +} diff --git a/Week_01/move-zeroes/moveZeroes.go b/Week_01/move-zeroes/moveZeroes.go new file mode 100644 index 0000000..c5776df --- /dev/null +++ b/Week_01/move-zeroes/moveZeroes.go @@ -0,0 +1,13 @@ +package move_zeroes + +func MoveZeroes(nums []int) { + // 用于记录当前非0元素的插入位置 + left := 0 + for right := 0; right < len(nums); right++ { + // 遍历到的非0元素插入left标记的位置并更新left都下一个位置 + if nums[right] != 0 { + nums[left], nums[right] = nums[right], nums[left] + left++ + } + } +} diff --git a/Week_01/move-zeroes/moveZeroes_test.go b/Week_01/move-zeroes/moveZeroes_test.go new file mode 100644 index 0000000..db53099 --- /dev/null +++ b/Week_01/move-zeroes/moveZeroes_test.go @@ -0,0 +1,14 @@ +package move_zeroes + +import "testing" + +func TestMoveZeroes(t *testing.T) { + nums := []int{0,1,0,3,12} + want := []int{1,3,12,0,0} + MoveZeroes(nums) + for idx, num := range nums { + if num != want[idx] { + t.Errorf("want:%d got:%d", want[idx], num) + } + } +} diff --git a/Week_01/plus-one/plusOne.go b/Week_01/plus-one/plusOne.go new file mode 100644 index 0000000..b59df0d --- /dev/null +++ b/Week_01/plus-one/plusOne.go @@ -0,0 +1,24 @@ +package plus_one + +func PlusOne(digits []int) []int { + if len(digits) <= 0 { + return []int{} + } + // 用于记录进位, 初始为1,当做加1 + plus := 1 + for idx := len(digits)-1; idx >= 0; idx-- { + // 没有进位了,直接返回digits即为所求 + if plus == 0 { + return digits + } + // 当前位加上进位 + digits[idx] = digits[idx] + plus + // 可能会产生进位,进行处理 + plus, digits[idx] = digits[idx] / 10, digits[idx] % 10 + } + // 有向最高位的进位,进行处理 + if plus == 1 { + digits = append([]int{plus}, digits...) + } + return digits +} diff --git a/Week_01/plus-one/plusOne_test.go b/Week_01/plus-one/plusOne_test.go new file mode 100644 index 0000000..7b2b8ad --- /dev/null +++ b/Week_01/plus-one/plusOne_test.go @@ -0,0 +1,14 @@ +package plus_one + +import "testing" + +func TestPlusOne(t *testing.T) { + nums := []int{1,2,3} + want := []int{1,2,4} + got := PlusOne(nums) + for idx, g := range got { + if g != want[idx] { + t.Errorf("want:%d got:%d", want[idx], g) + } + } +} diff --git a/Week_01/remove-duplicates-from-sorted-array/removeDuplicates.go b/Week_01/remove-duplicates-from-sorted-array/removeDuplicates.go new file mode 100644 index 0000000..768c8cd --- /dev/null +++ b/Week_01/remove-duplicates-from-sorted-array/removeDuplicates.go @@ -0,0 +1,21 @@ +package remove_duplicates_from_sorted_array + +func RemoveDuplicates(nums []int) int { + if len(nums) <= 0 { + return 0 + } + // 双指针处理 + // 左指针 + left := 0 + for right := 1; right < len(nums); right++ { + // 相等,则不处理,右指针继续右移 + if nums[right] == nums[left] { + continue + }else{ + // 不相等, 则将元素放入left+1的位置,并更新left + left++ + nums[left] = nums[right] + } + } + return left + 1 +} diff --git a/Week_01/remove-duplicates-from-sorted-array/removeDuplicates_test.go b/Week_01/remove-duplicates-from-sorted-array/removeDuplicates_test.go new file mode 100644 index 0000000..f279162 --- /dev/null +++ b/Week_01/remove-duplicates-from-sorted-array/removeDuplicates_test.go @@ -0,0 +1,18 @@ +package remove_duplicates_from_sorted_array + +import "testing" + +func TestRemoveDuplicates(t *testing.T) { + nums := []int{1,1,2} + want := 2 + got := RemoveDuplicates(nums) + if got != want { + t.Errorf("want:%d got:%d", want, got) + } + nums = []int{0,0,1,1,1,2,2,3,3,4} + want = 5 + got = RemoveDuplicates(nums) + if got != want { + t.Errorf("want:%d got:%d", want, got) + } +} diff --git a/Week_01/rotate-array/rotate.go b/Week_01/rotate-array/rotate.go new file mode 100644 index 0000000..660f7bb --- /dev/null +++ b/Week_01/rotate-array/rotate.go @@ -0,0 +1,37 @@ +package rotate_array + +//暴力 +func Rotate(nums []int, k int) { + // k可能是nums的倍数 + k = k % len(nums) + n := len(nums) + // 每次右移一位,右移k次 + for i := k; i > 0; i-- { + // 记录下最后一位数值 + tmp := nums[n-1] + // 其余元素依次后移 + for j := n -2 ; j >= 0; j-- { + nums[j+1] = nums[j] + } + // 第一位放置最后一位元素 + nums[0] = tmp + } +} + +// 一次移动k个元素,先记录下这k个元素,把其他的n-k个依次后移k个位置 +// 再将记录的k个元素放在头部,申请O(k)的新空间 +func Rotate2(nums []int, k int) { + k = k % len(nums) + n := len(nums) + tmp := make([]int, k) + // 记录后k个元素,深拷贝,不然后移前n-k个时会被覆盖 + copy(tmp, nums[n-k:]) + // 后移前n-k个元素 + for i := n - k - 1; i >= 0; i-- { + nums[i+k] = nums[i] + } + // 将记录k个元素放在数组头 + for i := 0; i< k ; i++ { + nums[i] = tmp[i] + } +} \ No newline at end of file diff --git a/Week_01/rotate-array/rotate_test.go b/Week_01/rotate-array/rotate_test.go new file mode 100644 index 0000000..90f5007 --- /dev/null +++ b/Week_01/rotate-array/rotate_test.go @@ -0,0 +1,26 @@ +package rotate_array + +import ( + "testing" +) + +func TestRotate(t *testing.T) { + nums := []int{1,2,3,4,5,6,7} + k := 3 + want := []int{5,6,7,1,2,3,4} + Rotate(nums, k) + for idx, num := range nums { + if num != want[idx] { + t.Errorf("want:%d got:%d", want[idx], num) + } + } + nums = []int{1,2,3,4,5,6,7} + k = 3 + want = []int{5,6,7,1,2,3,4} + Rotate2(nums, k) + for idx, num := range nums { + if num != want[idx] { + t.Errorf("want:%d got:%d", want[idx], num) + } + } +} diff --git a/Week_01/trapping-rain-water/trap.go b/Week_01/trapping-rain-water/trap.go new file mode 100644 index 0000000..18261a0 --- /dev/null +++ b/Week_01/trapping-rain-water/trap.go @@ -0,0 +1,36 @@ +package trapping_rain_water + +func max(a, b int) int { + if a > b { + return a + }else{ + return b + } +} + +// 左右指针法 +func Trap(height []int) int { + if len(height) <= 0 { + return 0 + } + n := len(height) + // 用于记录可存储的水 + sum := 0 + // 设置左、右指针, 左右水高的最大值 + left, right, leftMax, rightMax := 0, n-1, height[0], height[n-1] + for left < right { + // 更新左水高最大值 + leftMax = max(height[left], leftMax) + // 更新右水高最大值 + rightMax = max(height[right],rightMax) + // 当前位置能盛的水已左右最大水高的较小值来确定 + if leftMax < rightMax { + sum += max(leftMax, height[left]) - height[left] + left++ + }else{ + sum += max(rightMax, height[right]) - height[right] + right-- + } + } + return sum +} diff --git a/Week_01/trapping-rain-water/trap_test.go b/Week_01/trapping-rain-water/trap_test.go new file mode 100644 index 0000000..fde682b --- /dev/null +++ b/Week_01/trapping-rain-water/trap_test.go @@ -0,0 +1,18 @@ +package trapping_rain_water + +import "testing" + +func TestTrap(t *testing.T) { + height := []int{0,1,0,2,1,0,1,3,2,1,2,1} + want := 6 + got := Trap(height) + if got != want { + t.Errorf("want:%d got:%d", want, got) + } + height = []int{4,2,0,3,2,5} + want = 9 + got = Trap(height) + if got != want { + t.Errorf("want:%d got:%d", want, got) + } +} diff --git a/Week_01/two-sum/twoSum.go b/Week_01/two-sum/twoSum.go new file mode 100644 index 0000000..886f303 --- /dev/null +++ b/Week_01/two-sum/twoSum.go @@ -0,0 +1,15 @@ +package two_sum + +func TwoSum(nums []int, target int) []int { + // 用于记录出现过的元素及下标 + m := make(map[int]int) + for idx, num := range nums { + // 有出现过值为target-num的元素,则直接返回下标 + if v, ok := m[target-num]; ok { + return []int{v, idx} + } + // 记录元素及下标 + m[num] = idx + } + return []int{} +} diff --git a/Week_01/two-sum/twoSum_test.go b/Week_01/two-sum/twoSum_test.go new file mode 100644 index 0000000..1cbd3f8 --- /dev/null +++ b/Week_01/two-sum/twoSum_test.go @@ -0,0 +1,15 @@ +package two_sum + +import "testing" + +func TestTwoSum(t *testing.T) { + nums := []int{2,7,11,15} + target := 9 + want := []int{0,1} + got := TwoSum(nums, target) + for idx, g := range got { + if g != want[idx] { + t.Errorf("want:%d got:%d", want[idx], g) + } + } +} diff --git a/Week_02/README.md b/Week_02/README.md index 50de304..fef872e 100644 --- a/Week_02/README.md +++ b/Week_02/README.md @@ -1 +1,9 @@ -学习笔记 \ No newline at end of file +学习笔记 + +1、对哈希表有了进一步的理解,查看了golang中map的实现机制,了解了map查找、插入、删除、扩容、遍历的机制。 + +2、熟悉了二叉树的前序、中序、后序遍历,掌握了递归和迭代两种模板的实现。 + +3、对堆的知识进行了复习,能建立大顶堆和小顶堆解决一些算法题目。 + +4、对深度优先和广度优先算法有了更清晰的认识。 \ No newline at end of file diff --git a/Week_02/binary-tree-inorder-traversal/inorderTraversal.go b/Week_02/binary-tree-inorder-traversal/inorderTraversal.go new file mode 100644 index 0000000..95ac006 --- /dev/null +++ b/Week_02/binary-tree-inorder-traversal/inorderTraversal.go @@ -0,0 +1,46 @@ +package binary_tree_inorder_traversal + +type TreeNode struct { + Val int + Left *TreeNode + Right *TreeNode +} + +// 递归法 +func InorderTraversal(root *TreeNode) []int { + ans := []int{} + if root == nil { + return ans + } + // 左子树 + ans = append(ans, InorderTraversal(root.Left)...) + // 根节点 + ans = append(ans, root.Val) + // 右子树 + ans = append(ans, InorderTraversal(root.Right)...) + return ans +} + +// 迭代法 +func InorderTraversal2(root *TreeNode) []int { + ans := []int{} + if root == nil { + return ans + } + stack := []*TreeNode{} + p := root + for p != nil || len(stack) > 0 { + // 节点非空,则入栈,继续处理左孩子 + if p != nil { + stack = append(stack, p) + p = p.Left + }else{ + // 节点是空,则弹出栈顶,栈顶值记录进结果,然后处理右孩子 + p = stack[len(stack)-1] + stack = stack[:len(stack)-1] + ans = append(ans, p.Val) + p = p.Right + } + } + return ans +} diff --git a/Week_02/binary-tree-inorder-traversal/inorderTraversal_test.go b/Week_02/binary-tree-inorder-traversal/inorderTraversal_test.go new file mode 100644 index 0000000..0de9e7b --- /dev/null +++ b/Week_02/binary-tree-inorder-traversal/inorderTraversal_test.go @@ -0,0 +1,33 @@ +package binary_tree_inorder_traversal + +import "testing" + +func TestInorderTraversal1(t *testing.T) { + root := &TreeNode{Val:1} + right1 := &TreeNode{Val: 2} + left2 := &TreeNode{Val: 3} + root.Right = right1 + right1.Left = left2 + want := []int{1,3,2} + got := InorderTraversal(root) + for idx, g := range got { + if g != want[idx] { + t.Errorf("want:%d g:%d", want[idx], g) + } + } +} + +func TestInorderTraversal2(t *testing.T) { + root := &TreeNode{Val:1} + right1 := &TreeNode{Val: 2} + left2 := &TreeNode{Val: 3} + root.Right = right1 + right1.Left = left2 + want := []int{1,3,2} + got := InorderTraversal2(root) + for idx, g := range got { + if g != want[idx] { + t.Errorf("want:%d g:%d", want[idx], g) + } + } +} diff --git a/Week_02/binary-tree-preorder-traversal/preorderTraversal.go b/Week_02/binary-tree-preorder-traversal/preorderTraversal.go new file mode 100644 index 0000000..d88fc03 --- /dev/null +++ b/Week_02/binary-tree-preorder-traversal/preorderTraversal.go @@ -0,0 +1,46 @@ +package binary_tree_preorder_traversal + +type TreeNode struct { + Val int + Left *TreeNode + Right *TreeNode +} + +// 递归法 +func PreorderTraversal(root *TreeNode) []int { + ans := []int{} + if root == nil { + return ans + } + // 根节点 + ans = append(ans, root.Val) + // 左子树 + ans = append(ans, PreorderTraversal(root.Left)...) + // 右子树 + ans = append(ans, PreorderTraversal(root.Right)...) + return ans +} + +// 迭代法 +func PreorderTraversal2(root *TreeNode) []int { + ans := []int{} + if root == nil { + return ans + } + stack := []*TreeNode{} + p := root + for p != nil || len(stack) > 0 { + // 节点值先放入结果,将节点入栈,然后处理左子树 + if p != nil { + ans = append(ans, p.Val) + stack = append(stack, p) + p = p.Left + }else{ + // 弹出节点,然后处理右子树 + p = stack[len(stack)-1] + stack = stack[:len(stack)-1] + p = p.Right + } + } + return ans +} diff --git a/Week_02/binary-tree-preorder-traversal/preorderTraversal_test.go b/Week_02/binary-tree-preorder-traversal/preorderTraversal_test.go new file mode 100644 index 0000000..92fdb6e --- /dev/null +++ b/Week_02/binary-tree-preorder-traversal/preorderTraversal_test.go @@ -0,0 +1,33 @@ +package binary_tree_preorder_traversal + +import "testing" + +func TestPreorderTraversal(t *testing.T) { + root := &TreeNode{Val:1} + right1 := &TreeNode{Val: 2} + left2 := &TreeNode{Val: 3} + root.Right = right1 + right1.Left = left2 + want := []int{1,2,3} + got := PreorderTraversal(root) + for idx, g := range got { + if g != want[idx] { + t.Errorf("want:%d g:%d", want[idx], g) + } + } +} + +func TestPreorderTraversal2(t *testing.T) { + root := &TreeNode{Val:1} + right1 := &TreeNode{Val: 2} + left2 := &TreeNode{Val: 3} + root.Right = right1 + right1.Left = left2 + want := []int{1,2,3} + got := PreorderTraversal2(root) + for idx, g := range got { + if g != want[idx] { + t.Errorf("want:%d g:%d", want[idx], g) + } + } +} diff --git a/Week_02/chou-shu-lcof/nthUglyNumber.go b/Week_02/chou-shu-lcof/nthUglyNumber.go new file mode 100644 index 0000000..26f3853 --- /dev/null +++ b/Week_02/chou-shu-lcof/nthUglyNumber.go @@ -0,0 +1,56 @@ +package chou_shu_lcof + +func min(a, b, c int) int { + if a < b { + if a < c { + return a + }else{ + return c + } + }else{ + if b < c { + return b + }else{ + return c + } + } +} + +func NthUglyNumber(n int) int { + if n <= 0 { + return 0 + } + // 用于存放n个丑数 + dp := make([]int, n) + // 记录不同丑数因子的丑数指针,初始化都指向第一个丑数 + two, three, five := 0, 0, 0 + // 第一个丑数 + dp[0] = 1 + // 生成剩余的n-1个丑数 + for i := 1; i < n; i++ { + // 获取下一个最小的丑数 + next := min(dp[two]*2, dp[three]*3, dp[five]*5) + dp[i] = next + // 更新当前2、3、5丑数指针的位置 + /* + // 2、3、5倍数会有重复的,一次不能只更新一个,而必须全部更新 + switch next { + case dp[two]*2: + two++ + case dp[three]*3: + three++ + case dp[five]*5: + five++ + }*/ + if next == dp[two] * 2 { + two++ + } + if next == dp[three] * 3 { + three++ + } + if next == dp[five] * 5 { + five++ + } + } + return dp[n-1] +} diff --git a/Week_02/chou-shu-lcof/nthUglyNumber_test.go b/Week_02/chou-shu-lcof/nthUglyNumber_test.go new file mode 100644 index 0000000..7dbbb75 --- /dev/null +++ b/Week_02/chou-shu-lcof/nthUglyNumber_test.go @@ -0,0 +1,12 @@ +package chou_shu_lcof + +import "testing" + +func TestNthUglyNumber(t *testing.T) { + n := 10 + want := 12 + got := NthUglyNumber(n) + if got != want { + t.Errorf("want:%d got:%d", want, got) + } +} diff --git a/Week_02/group-anagrams/groupAnagrams.go b/Week_02/group-anagrams/groupAnagrams.go new file mode 100644 index 0000000..11f2123 --- /dev/null +++ b/Week_02/group-anagrams/groupAnagrams.go @@ -0,0 +1,22 @@ +package group_anagrams + +func GroupAnagrams(strs []string) [][]string { + ans := [][]string{} + if len(strs) == 0 { + return ans + } + // 记录每个异位词数据 + m := make(map[[26]byte][]string) + for _, str := range strs { + chars := [26]byte{0} + for i := range str { + chars[str[i] - 'a']++ + } + // 为异位词的字符串chars数组值相同,将异位词字符串按chars数组值组合在一起 + m[chars] = append(m[chars], str) + } + for _, v := range m { + ans = append(ans, v) + } + return ans +} \ No newline at end of file diff --git a/Week_02/group-anagrams/groupAnagrams_test.go b/Week_02/group-anagrams/groupAnagrams_test.go new file mode 100644 index 0000000..014d38e --- /dev/null +++ b/Week_02/group-anagrams/groupAnagrams_test.go @@ -0,0 +1,16 @@ +package group_anagrams + +import "testing" + +func TestGroupAnagrams(t *testing.T) { + strs := []string{"eat", "tea", "tan", "ate", "nat", "bat"} + want := [][]string{{"eat","tea","ate"},{"tan", "nat"}, {"bat"} } + got := GroupAnagrams(strs) + for out := range got { + for inner := range got[out] { + if got[out][inner] != want[out][inner] { + t.Errorf("want:%s got:%s", want[out][inner], got[out][inner]) + } + } + } +} diff --git a/Week_02/n-ary-tree-level-order-traversal/levelOrder.go b/Week_02/n-ary-tree-level-order-traversal/levelOrder.go new file mode 100644 index 0000000..c759036 --- /dev/null +++ b/Week_02/n-ary-tree-level-order-traversal/levelOrder.go @@ -0,0 +1,57 @@ +package n_ary_tree_level_order_traversal + +type Node struct { + Val int + Children []*Node +} + +// dfs解法 +func LevelOrder(root *Node) [][]int { + ans := [][]int{} + dfs(&ans, root, 0) + return ans +} + +func dfs(ans *[][]int, root *Node, level int) { + if root == nil { + return + } + // 需要建立新层 + if level >= len(*ans) { + *ans = append(*ans, []int{}) + } + // 将节点值放入当前层 + (*ans)[level] = append((*ans)[level], root.Val) + // 继续处理孩子节点 + for _, child := range root.Children { + dfs(ans, child, level+1) + } +} + +// bfs解法 +func LevelOrder2(root *Node) [][]int { + ans := [][]int{} + if root == nil { + return ans + } + queue := []*Node{root} + for len(queue) > 0 { + // 存放当前层结果 + sub := []int{} + // 当前层节点个数 + size := len(queue) + for i := 0; i < size; i++ { + // 将节点出队列并记录进当前层结果 + node := queue[0] + queue = queue[1:] + sub = append(sub, node.Val) + // 将节点孩子入队列,用于下一层遍历 + for _, child := range node.Children { + queue = append(queue, child) + } + } + // 一层处理完,将当前层结果放入结果集中 + ans = append(ans, sub) + } + return ans +} \ No newline at end of file diff --git a/Week_02/n-ary-tree-level-order-traversal/levelOrder_test.go b/Week_02/n-ary-tree-level-order-traversal/levelOrder_test.go new file mode 100644 index 0000000..69ff0b6 --- /dev/null +++ b/Week_02/n-ary-tree-level-order-traversal/levelOrder_test.go @@ -0,0 +1,41 @@ +package n_ary_tree_level_order_traversal + +import "testing" + +func creatTree(arr []int) *Node { + root := &Node{Val: 1} + c1 := &Node{Val: 3} + c2 := &Node{Val: 2} + c3 := &Node{Val: 4} + root.Children = []*Node{c1,c2,c3} + cc1 := &Node{Val: 5} + cc2 := &Node{Val: 6} + c1.Children = []*Node{cc1,cc2} + return root +} + +func TestLevelOrder(t *testing.T) { + root := creatTree([]int{1,3,2,4,5,6}) + want := [][]int{{1},{3,2,4},{5,6}} + got := LevelOrder(root) + for idx, g := range got { + for i := range g { + if got[idx][i] != want[idx][i] { + t.Errorf("want:%d got:%d idx:%d, i:%d", want[idx], got[idx], idx, i) + } + } + } +} + +func TestLevelOrder2(t *testing.T) { + root := creatTree([]int{1,3,2,4,5,6}) + want := [][]int{{1},{3,2,4},{5,6}} + got := LevelOrder2(root) + for idx, g := range got { + for i := range g { + if got[idx][i] != want[idx][i] { + t.Errorf("want:%d got:%d idx:%d, i:%d", want[idx], got[idx], idx, i) + } + } + } +} diff --git a/Week_02/n-ary-tree-preorder-traversal/preorder.go b/Week_02/n-ary-tree-preorder-traversal/preorder.go new file mode 100644 index 0000000..602c39f --- /dev/null +++ b/Week_02/n-ary-tree-preorder-traversal/preorder.go @@ -0,0 +1,44 @@ +package n_ary_tree_preorder_traversal + +type Node struct { + Val int + Children []*Node +} + +// 递归解法 +func Preorder(root *Node) []int { + ans := []int{} + if root == nil { + return ans + } + // 先遍历根节点 + ans = append(ans, root.Val) + // 然后递归遍历各孩子节点 + for _, child := range root.Children { + ans = append(ans, Preorder(child)...) + } + return ans +} + +// 迭代解法 +func Preorder2(root *Node) []int { + ans := []int{} + if root == nil { + return ans + } + stack := []*Node{root} + // 栈中还有节点,继续处理 + for len(stack) > 0 { + // 弹出节点 + node := stack[len(stack)-1] + stack = stack[:len(stack)-1] + ans = append(ans, node.Val) + // 结果是先左后右,所以孩子从右到左入栈 + for i := len(node.Children) -1; i >=0; i-- { + stack = append(stack, node.Children[i]) + } + } + return ans +} + + diff --git a/Week_02/n-ary-tree-preorder-traversal/preorder_test.go b/Week_02/n-ary-tree-preorder-traversal/preorder_test.go new file mode 100644 index 0000000..a623e05 --- /dev/null +++ b/Week_02/n-ary-tree-preorder-traversal/preorder_test.go @@ -0,0 +1,37 @@ +package n_ary_tree_preorder_traversal + +import "testing" + +func creatTree(arr []int) *Node { + root := &Node{Val: 1} + c1 := &Node{Val: 3} + c2 := &Node{Val: 2} + c3 := &Node{Val: 4} + root.Children = []*Node{c1,c2,c3} + cc1 := &Node{Val: 5} + cc2 := &Node{Val: 6} + c1.Children = []*Node{cc1,cc2} + return root +} + +func TestPreorder(t *testing.T) { + root := creatTree([]int{1,3,2,4,5,6}) + want := []int{1,3,5,6,2,4} + got := Preorder(root) + for idx := range got { + if got[idx] != want[idx] { + t.Errorf("want:%d got:%d idx:%d", want[idx], got[idx], idx) + } + } +} + +func TestPreorder2(t *testing.T) { + root := creatTree([]int{1,3,2,4,5,6}) + want := []int{1,3,5,6,2,4} + got := Preorder2(root) + for idx := range got { + if got[idx] != want[idx] { + t.Errorf("want:%d got:%d idx:%d", want[idx], got[idx], idx) + } + } +} diff --git a/Week_02/top-k-frequent-elements/topKFrequent.go b/Week_02/top-k-frequent-elements/topKFrequent.go new file mode 100644 index 0000000..17d02fc --- /dev/null +++ b/Week_02/top-k-frequent-elements/topKFrequent.go @@ -0,0 +1,60 @@ +package top_k_frequent_elements + +import "container/heap" + +// 堆中元素内容, key为统计的元素, count为频次 +type item struct { + key int + count int +} + +type hp []item + +func (h hp) Len() int { + return len(h) +} + +// 小顶堆 +func (h hp) Less(i, j int) bool { + return h[i].count < h[j].count +} + +func (h hp) Swap(i, j int) { + h[i], h[j] = h[j],h[i] +} + +func (h *hp) Push(ele interface{}) { + *h = append(*h, ele.(item)) +} + +func (h *hp) Pop() interface{} { + ele := (*h)[len(*h)-1] + *h = (*h)[:len(*h)-1] + return ele +} + +func TopKFrequent(nums []int, k int) []int { + if len(nums) == 0 { + return []int{} + } + m := make(map[int]int) + // 统计频次 + for _, num := range nums { + m[num]++ + } + h := &hp{} + heap.Init(h) + // 将map中的数据记录入堆,堆大小限制为k + for key, val := range m { + heap.Push(h, item{key: key, count:val}) + if h.Len() > k { + heap.Pop(h) + } + } + // 入堆完成后,堆中的k个数据即为出现频次最高的k个数 + ans := make([]int, k) + for i := 0; i < k; i++ { + ans[k-i-1] = heap.Pop(h).(item).key + } + return ans +} diff --git a/Week_02/top-k-frequent-elements/topKFrequent_test.go b/Week_02/top-k-frequent-elements/topKFrequent_test.go new file mode 100644 index 0000000..d68b682 --- /dev/null +++ b/Week_02/top-k-frequent-elements/topKFrequent_test.go @@ -0,0 +1,15 @@ +package top_k_frequent_elements + +import "testing" + +func TestTopKFrequent(t *testing.T) { + nums := []int{1,1,1,2,2,3} + k := 2 + want := []int{1,2} + got := TopKFrequent(nums, k) + for idx := range want { + if got[idx] != want[idx] { + t.Errorf("want:%d got:%d", want[idx], got[idx]) + } + } +} diff --git a/Week_02/two-sum/twoSum.go b/Week_02/two-sum/twoSum.go new file mode 100644 index 0000000..270922c --- /dev/null +++ b/Week_02/two-sum/twoSum.go @@ -0,0 +1,15 @@ +package two_sum + +func TwoSum(nums []int, target int) []int { + // 用于记录出现过的元素及下标 + m := make(map[int]int) + for idx, num := range nums { + // 有出现过值为target-num的元素,则直接返回下标 + if v, ok := m[target-num]; ok { + return []int{v, idx} + } + // 记录元素及下标 + m[num] = idx + } + return []int{} +} \ No newline at end of file diff --git a/Week_02/two-sum/twoSum_test.go b/Week_02/two-sum/twoSum_test.go new file mode 100644 index 0000000..4e59a48 --- /dev/null +++ b/Week_02/two-sum/twoSum_test.go @@ -0,0 +1,33 @@ +package two_sum + +import "testing" + +func TestTwoSum(t *testing.T) { + nums := []int{2,7,11,15} + target := 9 + want := []int{0,1} + got := TwoSum(nums, target) + for idx := range got { + if got[idx] != want[idx] { + t.Errorf("want:%d got:%d", want[idx], got[idx]) + } + } + nums = []int{3,2,4} + target = 6 + want = []int{1,2} + got = TwoSum(nums, target) + for idx := range got { + if got[idx] != want[idx] { + t.Errorf("want:%d got:%d", want[idx], got[idx]) + } + } + nums = []int{3,3} + target = 6 + want = []int{0,1} + got = TwoSum(nums, target) + for idx := range got { + if got[idx] != want[idx] { + t.Errorf("want:%d got:%d", want[idx], got[idx]) + } + } +} diff --git a/Week_02/valid-anagram/isAnagram.go b/Week_02/valid-anagram/isAnagram.go new file mode 100644 index 0000000..be8ac43 --- /dev/null +++ b/Week_02/valid-anagram/isAnagram.go @@ -0,0 +1,29 @@ +package valid_anagram + +func IsAnagram(s string, t string) bool { + // 如果长度不等,肯定不是字母异位词 + if len(s) != len(t) { + return false + } + // 记录各字符次数 + m := make(map[rune]int) + // 遍历s时各次数+1 + for _, ch := range s { + m[ch]++ + } + // 遍历t时各字符次数-1,如果次数<0, 则说明s的次数小于t中次数,可以提前结束 + for _, ch := range t { + m[ch]-- + if m[ch] < 0 { + return false + } + } + /* 不需要,最开始先判断了两个字符串长度相等与否,如果还有不为0的,肯定在遍历t时已经提前结束了 + // 如果遍历完两个字符串,还有次数不是0的,说明肯定不是字母异位词 + for _, v := range m { + if v != 0 { + return false + } + }*/ + return true +} diff --git a/Week_02/valid-anagram/isAnagram_test.go b/Week_02/valid-anagram/isAnagram_test.go new file mode 100644 index 0000000..298c48e --- /dev/null +++ b/Week_02/valid-anagram/isAnagram_test.go @@ -0,0 +1,20 @@ +package valid_anagram + +import "testing" + +func TestIsAnagram(t *testing.T) { + s1 := "anagram" + s2 := "nagaram" + want := true + got := IsAnagram(s1,s2) + if got != want { + t.Errorf("want:%t got:%t", want, got) + } + s1 = "rat" + s2 = "car" + want = false + got = IsAnagram(s1,s2) + if got != want { + t.Errorf("want:%t got:%t", want, got) + } +} diff --git a/Week_03/README.md b/Week_03/README.md index 50de304..2a14ce7 100644 --- a/Week_03/README.md +++ b/Week_03/README.md @@ -1 +1,7 @@ -学习笔记 \ No newline at end of file +学习笔记 + +1、对递归有了进一步的熟悉,通过刷题进一步巩固了递归的知识。 + +2、对分治法有了一定了解,但还没做相关题目,需要做相关题目进一步巩固。 + +3、回溯算法,刚开始对回溯算法不是很理解,通过反复看排列、组合、N皇后的相关题目及题解,后边有了一定理解,总体感觉相关题目有点难,还需要通过反复刷相关题目来加深理解。 \ No newline at end of file diff --git a/Week_03/combinations/combine.go b/Week_03/combinations/combine.go new file mode 100644 index 0000000..590922c --- /dev/null +++ b/Week_03/combinations/combine.go @@ -0,0 +1,27 @@ +package combinations + +func Combine(n int, k int) [][]int { + ans := [][]int{} + if n <= 0 || k <= 0 { + return ans + } + backtrack(1, n, k, []int{}, &ans) + return ans +} + +// 回溯解法框架 +func backtrack(idx, n, k int, path []int, ans *[][]int){ + // 递归终止条件,已经选出k个数 + if len(path) == k { + *ans = append(*ans, append([]int{}, path...)) + return + } + for i := idx; i <= n; i++ { + // 选择i + path = append(path, i) + // 选其他数 + backtrack(i + 1, n, k, path, ans) + // 不选i + path = path[0:len(path)-1] + } +} diff --git a/Week_03/combinations/combine_test.go b/Week_03/combinations/combine_test.go new file mode 100644 index 0000000..fa25b87 --- /dev/null +++ b/Week_03/combinations/combine_test.go @@ -0,0 +1,16 @@ +package combinations + +import "testing" + +func TestCombine(t *testing.T) { + n, k := 4, 2 + want := [][]int{{1,2},{1,3},{1,4},{2,3},{2,4},{3,4}} + got := Combine(n,k) + for idx := range got { + for i := range got[idx] { + if got[idx][i] != want[idx][i] { + t.Errorf("want:%d got:%d", want[idx][i], got[idx][i]) + } + } + } +} diff --git a/Week_03/construct-binary-tree-from-preorder-and-inorder-traversal/buildTree.go b/Week_03/construct-binary-tree-from-preorder-and-inorder-traversal/buildTree.go new file mode 100644 index 0000000..4a3154a --- /dev/null +++ b/Week_03/construct-binary-tree-from-preorder-and-inorder-traversal/buildTree.go @@ -0,0 +1,37 @@ +package construct_binary_tree_from_preorder_and_inorder_traversal + +type TreeNode struct { + Val int + Left *TreeNode + Right *TreeNode +} + +// 前序遍历 preorder = [[3],[9],[20,15,7]] +// 中序遍历 inorder = [[9],[3],[15,20,7]] +/* + 3 + / \ + 9 20 + / \ + 15 7 + */ +func BuildTree(preorder []int, inorder []int) *TreeNode { + // 递归终止条件 + if len(preorder) == 0 { + return nil + } + // 根节点为前序遍历的第一个节点 + root := &TreeNode{Val: preorder[0]} + // 根据前序序列中根节点在中序序列中的位置将树分为左、右子树 + splitIdx := 0 + for i := 0 ; i < len(inorder); i++ { + if inorder[i] == preorder[0] { + splitIdx = i + break + } + } + // 递归处理左右子树 + root.Left = BuildTree(preorder[1:splitIdx+1], inorder[:splitIdx]) + root.Right = BuildTree(preorder[splitIdx+1:], inorder[splitIdx+1:]) + return root +} \ No newline at end of file diff --git a/Week_03/construct-binary-tree-from-preorder-and-inorder-traversal/buildTree_test.go b/Week_03/construct-binary-tree-from-preorder-and-inorder-traversal/buildTree_test.go new file mode 100644 index 0000000..86151b8 --- /dev/null +++ b/Week_03/construct-binary-tree-from-preorder-and-inorder-traversal/buildTree_test.go @@ -0,0 +1,43 @@ +package construct_binary_tree_from_preorder_and_inorder_traversal + +import "testing" + +func preOrderTravel(root *TreeNode) []int { + ans := []int{} + if root == nil { + return ans + } + ans = append(ans, root.Val) + ans = append(ans, preOrderTravel(root.Left)...) + ans = append(ans, preOrderTravel(root.Right)...) + return ans +} + +func inOrderTravel(root *TreeNode) []int { + ans := []int{} + if root == nil { + return ans + } + ans = append(ans, inOrderTravel(root.Left)...) + ans = append(ans, root.Val) + ans = append(ans, inOrderTravel(root.Right)...) + return ans +} + +func TestBuildTree(t *testing.T) { + preOrder := []int{3,9,20,15,7} + inOrder := []int{9,3,15,20,7} + got := BuildTree(preOrder, inOrder) + preGot := preOrderTravel(got) + inGot := inOrderTravel(got) + for idx := range preOrder { + if preGot[idx] != preOrder[idx] { + t.Errorf("pre want:%d got:%d", preOrder[idx], preGot[idx]) + } + } + for idx := range inOrder { + if inGot[idx] != inOrder[idx] { + t.Errorf("in want:%d got:%d", inOrder[idx], inGot[idx]) + } + } +} diff --git a/Week_03/lowest-common-ancestor-of-a-binary-tree/lowestCommonAncestor.go b/Week_03/lowest-common-ancestor-of-a-binary-tree/lowestCommonAncestor.go new file mode 100644 index 0000000..52fb86b --- /dev/null +++ b/Week_03/lowest-common-ancestor-of-a-binary-tree/lowestCommonAncestor.go @@ -0,0 +1,27 @@ +package lowest_common_ancestor_of_a_binary_tree + +type TreeNode struct { + Val int + Left *TreeNode + Right *TreeNode +} + +func LowestCommonAncestor(root, p, q *TreeNode) *TreeNode { + // 到叶子节点或者p、q中任意节点,递归终止 + if root == nil || root == p || root == q { + return root + } + // 左子树中查找p、q + left := LowestCommonAncestor(root.Left, p, q) + // 右子树中查找p、q + right := LowestCommonAncestor(root.Right, p, q) + // p、q分别在左右子树中,则当前节点即为所求 + if left != nil && right != nil { + return root + } + // 在子树的一边,则非nil的left或right为所求 + if left == nil { + return right + } + return left +} \ No newline at end of file diff --git a/Week_03/lowest-common-ancestor-of-a-binary-tree/lowestCommonAncestor_test.go b/Week_03/lowest-common-ancestor-of-a-binary-tree/lowestCommonAncestor_test.go new file mode 100644 index 0000000..b7c43a4 --- /dev/null +++ b/Week_03/lowest-common-ancestor-of-a-binary-tree/lowestCommonAncestor_test.go @@ -0,0 +1,34 @@ +package lowest_common_ancestor_of_a_binary_tree + +import "testing" + +func createTree() (*TreeNode, *TreeNode, *TreeNode) { + root := &TreeNode{Val: 3} + l1 := &TreeNode{Val: 5} + r1 := &TreeNode{Val: 1} + root.Left = l1 + root.Right = r1 + l1l := &TreeNode{Val: 6} + l1r := &TreeNode{Val: 2} + l1.Left = l1l + l1.Right = l1r + l2l := &TreeNode{Val: 7} + l2r := &TreeNode{Val: 4} + l1r.Left = l2l + l1r.Right = l2r + r1l := &TreeNode{Val: 0} + r1r := &TreeNode{Val: 8} + r1.Left = r1l + r1.Right = r1r + return root, l1, r1 +} + +func TestLowestCommonAncestor(t *testing.T) { + root, p , q := createTree() + want := 3 + got := LowestCommonAncestor(root, p, q) + if got.Val != want { + t.Errorf("want:%d got:%d", 3, got.Val) + } +} + diff --git a/Week_03/permutations-ii/permuteUnique.go b/Week_03/permutations-ii/permuteUnique.go new file mode 100644 index 0000000..eae4b73 --- /dev/null +++ b/Week_03/permutations-ii/permuteUnique.go @@ -0,0 +1,39 @@ +package permutations_ii + +import "sort" + +func PermuteUnique(nums []int) [][]int { + ans := [][]int{} + if len(nums) <= 0 { + return ans + } + // 先进数组排序,方便排列处理 + sort.Ints(nums) + // 记录已经选过的元素 + used := make([]bool, len(nums)) + backtrack([]int{}, nums, used, &ans) + return ans +} + +// 回溯解法框架 +func backtrack(path, nums []int, used []bool, ans *[][]int){ + // 递归终止条件 + if len(path) == len(nums) { + *ans = append(*ans, append([]int{}, path...)) + return + } + for i := 0; i < len(nums); i++ { + // 当前位置的数已经选过或者当前数和前一个数相等,但是前一个数还没选,确保相同的数先选前边的再放后边的,不引起重复 + if used[i] || i > 0 && nums[i] == nums[i-1] && !used[i-1] { + continue + } + // 选当前数并标记为已选 + path = append(path, nums[i]) + used[i] = true + backtrack(path, nums, used, ans) + // 不选当前数并标记为未选 + path = path[:len(path)-1] + used[i] = false + } +} + diff --git a/Week_03/permutations-ii/permuteUnique_test.go b/Week_03/permutations-ii/permuteUnique_test.go new file mode 100644 index 0000000..ad71ffc --- /dev/null +++ b/Week_03/permutations-ii/permuteUnique_test.go @@ -0,0 +1,16 @@ +package permutations_ii + +import "testing" + +func TestPermuteUnique(t *testing.T) { + nums := []int{1,1,2} + got := PermuteUnique(nums) + want := [][]int{{1,1,2},{1,2,1}, {2,1,1}} + for idx := range got { + for i := range got[idx] { + if got[idx][i] != want[idx][i] { + t.Errorf("want:%d got:%d", want[idx][i], got[idx][i]) + } + } + } +} diff --git a/Week_03/permutations/permute.go b/Week_03/permutations/permute.go new file mode 100644 index 0000000..6cc0802 --- /dev/null +++ b/Week_03/permutations/permute.go @@ -0,0 +1,29 @@ +package permutations + +func Permute(nums []int) [][]int { + ans := [][]int{} + if len(nums) <= 0 { + return ans + } + backtrack(0, len(nums) - 1, nums, &ans) + return ans +} + +//回溯解法框架 +func backtrack(p, q int, nums []int, ans *[][]int) { + // 递归终止条件 + if p == q { + *ans = append(*ans, append([]int{}, nums...)) + return + } + for i := p; i <= q; i++ { + // 交换两个数位置 + nums[p], nums[i] = nums[i], nums[p] + // 继续处理其他数 + backtrack(p + 1, q, nums, ans) + // 还原两个数位置 + nums[p], nums[i] = nums[i], nums[p] + } +} + + diff --git a/Week_03/permutations/permute_test.go b/Week_03/permutations/permute_test.go new file mode 100644 index 0000000..49f6f9d --- /dev/null +++ b/Week_03/permutations/permute_test.go @@ -0,0 +1,16 @@ +package permutations + +import "testing" + +func TestPermute(t *testing.T) { + nums := []int{1,2,3} + want := [][]int{{1,2,3},{1,3,2},{2,1,3},{2,3,1},{3,2,1},{3,1,2}} + got := Permute(nums) + for idx := range got { + for i := range got[idx] { + if got[idx][i] != want[idx][i] { + t.Errorf("want:%d got:%d", want[idx][i], got[idx][i]) + } + } + } +} diff --git a/Week_04/README.md b/Week_04/README.md index 50de304..6a7958f 100644 --- a/Week_04/README.md +++ b/Week_04/README.md @@ -1 +1,11 @@ -学习笔记 \ No newline at end of file +学习笔记 + +1、对贪心算法有了一定的了解,贪心算法每一步都查找最优算法,然后不进行回溯,是否达到最终的最优解要看情况,如果能达到最优解则采用贪心算法,代码相对简洁。 + +2、对二分查找有了进一步的学习,二分查找必须满足几个条件:有序、有界、能索引,二分查找还有一定的变形格式,如作业中的旋转数组,有序矩阵。 + +思考题:使用二分查找,寻找一个半有序数组 [4, 5, 6, 7, 0, 1, 2] 中间无序的地方 + +无序的地方就是最小值所在的地方,它把数组分成两个有序部分,具体解法和homework中查找旋转数组的最小值相同。 + +3、dfs和bfs:这周的dfs和bfs比树那章的dfs和bfs虽然模板差不多,但在做题的过程中感觉复杂了不少,还需要通过刷题进一步掌握。 diff --git a/Week_04/assign-cookies/findContentChildren.go b/Week_04/assign-cookies/findContentChildren.go new file mode 100644 index 0000000..97339ba --- /dev/null +++ b/Week_04/assign-cookies/findContentChildren.go @@ -0,0 +1,23 @@ +package assign_cookies + +import "sort" + +func FindContentChildren(g []int, s []int) int { + // 胃口和饼干先排序 + sort.Ints(g) + sort.Ints(s) + ans := 0 + // 贪心算法 + for i, j := 0, 0; i < len(g) && j < len(s);{ + // 能够满足胃口,结果+1, 继续判断下一个孩子和胃口 + if s[j] >= g[i] { + ans++ + i++ + j++ + // 不能满足当前孩子,则看下一块饼干是否能够满足 + }else{ + j++ + } + } + return ans +} diff --git a/Week_04/assign-cookies/findContentChildren_test.go b/Week_04/assign-cookies/findContentChildren_test.go new file mode 100644 index 0000000..207c586 --- /dev/null +++ b/Week_04/assign-cookies/findContentChildren_test.go @@ -0,0 +1,27 @@ +package assign_cookies + +import "testing" + +func TestFindContentChildren(t *testing.T) { + g := []int{1,2,3} + s := []int{1, 1} + want := 1 + got := FindContentChildren(g, s) + if got != want { + t.Errorf("want:%d got:%d", want, got) + } + g = []int{1,2} + s = []int{1, 2, 3} + want = 2 + got = FindContentChildren(g, s) + if got != want { + t.Errorf("want:%d got:%d", want, got) + } + g = []int{10, 9 ,8 , 7} + s = []int{5, 6, 7, 8} + want = 2 + got = FindContentChildren(g, s) + if got != want { + t.Errorf("want:%d got:%d", want, got) + } +} \ No newline at end of file diff --git a/Week_04/best-time-to-buy-and-sell-stock-ii/maxProfit.go b/Week_04/best-time-to-buy-and-sell-stock-ii/maxProfit.go new file mode 100644 index 0000000..1216da0 --- /dev/null +++ b/Week_04/best-time-to-buy-and-sell-stock-ii/maxProfit.go @@ -0,0 +1,13 @@ +package best_time_to_buy_and_sell_stock_ii + +func MaxProfit(prices []int) int { + // 贪心算法 + ans := 0 + for i := range prices { + // 如果当天比前一天大,则前一天买入,当天抛出 + if i > 0 && prices[i] - prices[i-1] > 0 { + ans += prices[i] - prices[i-1] + } + } + return ans +} diff --git a/Week_04/best-time-to-buy-and-sell-stock-ii/maxProfit_test.go b/Week_04/best-time-to-buy-and-sell-stock-ii/maxProfit_test.go new file mode 100644 index 0000000..fbb7763 --- /dev/null +++ b/Week_04/best-time-to-buy-and-sell-stock-ii/maxProfit_test.go @@ -0,0 +1,24 @@ +package best_time_to_buy_and_sell_stock_ii + +import "testing" + +func TestMaxProfit(t *testing.T) { + prices := []int{7,1,5,3,6,4} + want := 7 + got := MaxProfit(prices) + if got != want { + t.Errorf("want:%d got:%d", want, got) + } + prices = []int{1,2,3,4,5} + want = 4 + got = MaxProfit(prices) + if got != want { + t.Errorf("want:%d got:%d", want, got) + } + prices = []int{7,6,4,3,1} + want = 0 + got = MaxProfit(prices) + if got != want { + t.Errorf("want:%d got:%d", want, got) + } +} diff --git a/Week_04/find-minimum-in-rotated-sorted-array/findMin.go b/Week_04/find-minimum-in-rotated-sorted-array/findMin.go new file mode 100644 index 0000000..347542e --- /dev/null +++ b/Week_04/find-minimum-in-rotated-sorted-array/findMin.go @@ -0,0 +1,20 @@ +package find_minimum_in_rotated_sorted_array + +func FindMin(nums []int) int { + if len(nums) <= 0 { + return 0 + } + left, right := 0, len(nums) -1 + for left < right { + mid := (left + right) / 2 + // 中间比最右边的还大,则最小值在右边,继续查找右边 + // 需要注意数组已经是升序的特殊情况 + if nums[mid] > nums[len(nums)-1] { + left = mid + 1 + }else{ + // 否则查找左边 + right = mid + } + } + return nums[left] +} diff --git a/Week_04/find-minimum-in-rotated-sorted-array/findMin_test.go b/Week_04/find-minimum-in-rotated-sorted-array/findMin_test.go new file mode 100644 index 0000000..3b33495 --- /dev/null +++ b/Week_04/find-minimum-in-rotated-sorted-array/findMin_test.go @@ -0,0 +1,31 @@ +package find_minimum_in_rotated_sorted_array + +import "testing" + +func TestFindMin(t *testing.T) { + nums := []int{3, 4, 5, 1, 2} + want := 1 + got := FindMin(nums) + if got != want { + t.Errorf("want:%d got:%d", want, got) + } + nums = []int{4, 5, 6, 7, 0, 1, 2} + want = 0 + got = FindMin(nums) + if got != want { + t.Errorf("want:%d got:%d", want, got) + } + nums = []int{1} + want = 1 + got = FindMin(nums) + if got != want { + t.Errorf("want:%d got:%d", want, got) + } + + nums = []int{11, 13, 15, 17} + want = 11 + got = FindMin(nums) + if got != want { + t.Errorf("want:%d got:%d", want, got) + } +} diff --git a/Week_04/jump-game-ii/jump.go b/Week_04/jump-game-ii/jump.go new file mode 100644 index 0000000..a18cd6b --- /dev/null +++ b/Week_04/jump-game-ii/jump.go @@ -0,0 +1,24 @@ +package jump_game_ii + +func max (a, b int) int { + if a > b { + return a + }else{ + return b + } +} +func Jump(nums []int) int { + end := 0 + maxPosition := 0 + steps := 0 + for i := 0; i < len(nums) - 1; i++ { + // 找能跳的最远的 + maxPosition = max(maxPosition, nums[i] + i) + // 遇到边界,就更新边界,并且步数加一 + if i == end { + end = maxPosition + steps++ + } + } + return steps +} diff --git a/Week_04/jump-game-ii/jump_test.go b/Week_04/jump-game-ii/jump_test.go new file mode 100644 index 0000000..71e2f76 --- /dev/null +++ b/Week_04/jump-game-ii/jump_test.go @@ -0,0 +1,12 @@ +package jump_game_ii + +import "testing" + +func TestJump(t *testing.T) { + nums := []int{2,3,1,1,4} + want := 2 + got := Jump(nums) + if got != want { + t.Errorf("want:%d got:%d", want, got) + } +} diff --git a/Week_04/jump-game/canJump.go b/Week_04/jump-game/canJump.go new file mode 100644 index 0000000..c5b5cf7 --- /dev/null +++ b/Week_04/jump-game/canJump.go @@ -0,0 +1,21 @@ +package jump_game + +func max(a, b int) int { + if a > b { + return a + } + return b +} + +func CanJump(nums []int) bool { + curMax := 0 + for i := 0; i < len(nums); i++ { + // 当前位置比上次能跳到的最大位置还大,则不能跳到位置i + if i > curMax { + return false + } + // 更新当前能跳到的最大位置 + curMax = max(curMax, i + nums[i]) + } + return true +} diff --git a/Week_04/jump-game/canJump_test.go b/Week_04/jump-game/canJump_test.go new file mode 100644 index 0000000..4c94e19 --- /dev/null +++ b/Week_04/jump-game/canJump_test.go @@ -0,0 +1,12 @@ +package jump_game + +import "testing" + +func TestCanJump(t *testing.T) { + nums := []int{2,3,1,1,4} + want := true + got := CanJump(nums) + if got != want { + t.Errorf("want:%t got:%t", want, got) + } +} diff --git a/Week_04/lemonade-change/lemonadeChange.go b/Week_04/lemonade-change/lemonadeChange.go new file mode 100644 index 0000000..f562436 --- /dev/null +++ b/Week_04/lemonade-change/lemonadeChange.go @@ -0,0 +1,33 @@ +package lemonade_change + +func LemonadeChange(bills []int) bool { + // leetcode上bills是空数组时返回true, 不加判断也一致 + // if len(bills) <= 0 { + // return true + // } + // 贪心算法 + bill5, bill10 := 0, 0 + for _, bill := range bills { + // 5元,则不用找,5元余额+1 + if bill == 5 { + bill5++ + // 10元,需要找5元,直接减掉5元,然后判断5元是否小于0,小于0则不满足条件 + }else if bill == 10 { + bill10++ + bill5-- + }else{ + // 否则是20元, 则可以先找10元再找5元,没有10元则需要找3个5元,减了后如果5元小于则不满足条件 + if bill10 > 0 { + bill10-- + bill5-- + }else{ + bill5 -=3 + } + } + // 前边直接扣的,如果扣了后5元小于0,说明不满足条件 + if bill5 < 0 { + return false + } + } + return true +} diff --git a/Week_04/lemonade-change/lemonadeChange_test.go b/Week_04/lemonade-change/lemonadeChange_test.go new file mode 100644 index 0000000..52a561c --- /dev/null +++ b/Week_04/lemonade-change/lemonadeChange_test.go @@ -0,0 +1,36 @@ +package lemonade_change + +import "testing" + +func TestLemonadeChange(t *testing.T) { + bills := []int{5,5,5,10,20} + got := LemonadeChange(bills) + want := true + if got != want { + t.Errorf("want:%t got:%t", want, got) + } + bills = []int{5,5,10} + got = LemonadeChange(bills) + want = true + if got != want { + t.Errorf("want:%t got:%t", want, got) + } + bills = []int{10,10} + got = LemonadeChange(bills) + want = false + if got != want { + t.Errorf("want:%t got:%t", want, got) + } + bills = []int{5,5,10,10,20} + got = LemonadeChange(bills) + want = false + if got != want { + t.Errorf("want:%t got:%t", want, got) + } + bills = []int{} + got = LemonadeChange(bills) + want = true + if got != want { + t.Errorf("want:%t got:%t", want, got) + } +} diff --git a/Week_04/minesweeper/updateBoard.go b/Week_04/minesweeper/updateBoard.go new file mode 100644 index 0000000..c101625 --- /dev/null +++ b/Week_04/minesweeper/updateBoard.go @@ -0,0 +1,46 @@ +package minesweeper + +func UpdateBoard(board [][]byte, click []int) [][]byte { + m, n := len(board), len(board[0]) + row, col := click[0], click[1] + // 挖到雷 + if board[row][col] == 'M' { + board[row][col] = 'X' + } else { + count := 0 + for i := -1; i < 2; i++ { + for j := -1; j < 2; j++ { + if i == 0 && j == 0 { + continue + } + r, c := row + i, col + j + if r < 0 || r >= m || c < 0 || c >= n { + continue + } + if board[r][c] == 'M' || board[r][c] == 'X' { + count++ + } + } + } + if count > 0 { + board[row][col] = byte(count + int('0')) + } else { + board[row][col] = 'B' + for i := -1; i < 2; i++ { + for j := -1; j < 2; j++ { + if i == 0 && j == 0 { + continue + } + r, c := row + i, col + j + if r < 0 || r >= m || c < 0 || c >= n { + continue + } + if board[r][c] == 'E' { + UpdateBoard(board, []int{r, c}) + } + } + } + } + } + return board +} diff --git a/Week_04/minesweeper/updateBoard_test.go b/Week_04/minesweeper/updateBoard_test.go new file mode 100644 index 0000000..5214365 --- /dev/null +++ b/Week_04/minesweeper/updateBoard_test.go @@ -0,0 +1,27 @@ +package minesweeper + +import "testing" + +func TestUpdateBoard(t *testing.T) { + board := [][]byte{ + {'E', 'E', 'E', 'E', 'E'}, + {'E', 'E', 'M', 'E', 'E'}, + {'E', 'E', 'E', 'E', 'E'}, + {'E', 'E', 'E', 'E', 'E'}, + } + click := []int{3, 0} + got := [][]byte{ + {'B', '1', 'E', '1', 'B'}, + {'B', '1', 'M', '1', 'B'}, + {'B', '1', '1', '1', 'B'}, + {'B', 'B', 'B', 'B', 'B'}, + } + want := UpdateBoard(board, click) + for idx := range want { + for i := range want[idx] { + if got[idx][i] != want[idx][i] { + t.Errorf("got:%c got:%c", got[idx][i], want[idx][i]) + } + } + } +} diff --git a/Week_04/number-of-islands/numIslands.go b/Week_04/number-of-islands/numIslands.go new file mode 100644 index 0000000..ea133cc --- /dev/null +++ b/Week_04/number-of-islands/numIslands.go @@ -0,0 +1,28 @@ +package number_of_islands + +func NumIslands(grid [][]byte) int { + ans := 0 + for i := 0; i < len(grid); i++ { + for j := 0; j < len(grid[0]); j++ { + // 是陆地 + if grid[i][j] == '1' { + ans++ + dfs(i, j, grid) + } + } + } + return ans +} + +func dfs(row, col int, grid [][]byte) { + // 越界或者不是陆地 + if row < 0 || row >= len(grid) || col < 0 || col >= len(grid[0]) || grid[row][col] != '1' { + return + } + grid[row][col] = '0' + // 四个方向继续处理 + dfs(row+1, col, grid) + dfs(row-1, col, grid) + dfs(row, col+1, grid) + dfs(row, col-1, grid) +} diff --git a/Week_04/number-of-islands/numIslands_test.go b/Week_04/number-of-islands/numIslands_test.go new file mode 100644 index 0000000..4daa187 --- /dev/null +++ b/Week_04/number-of-islands/numIslands_test.go @@ -0,0 +1,17 @@ +package number_of_islands + +import "testing" + +func TestNumIslands(t *testing.T) { + grid := [][]byte{ + {'1', '1', '1', '1', '0'}, + {'1', '1', '0', '1', '0'}, + {'1', '1', '0', '0', '0'}, + {'0', '0', '0', '0', '0'}, + } + want := 1 + got := NumIslands(grid) + if got != want { + t.Errorf("want:%d got:%d", want, got) + } +} diff --git a/Week_04/search-a-2d-matrix/searchMatrix.go b/Week_04/search-a-2d-matrix/searchMatrix.go new file mode 100644 index 0000000..4692e54 --- /dev/null +++ b/Week_04/search-a-2d-matrix/searchMatrix.go @@ -0,0 +1,22 @@ +package search_a_2d_matrix + +func SearchMatrix(matrix [][]int, target int) bool { + if len(matrix) <= 0 || len(matrix[0]) <= 0 { + return false + } + row, col := len(matrix), len(matrix[0]) + // 将矩阵转化为一维数组求解 + left, right := 0, row * col - 1 + for left <= right { + mid := (left + right) / 2 + // 将mid位置转化为矩阵的行,列 + if matrix[mid/col][mid%col] == target { + return true + }else if matrix[mid/col][mid%col] > target { + right = mid - 1 + } else{ + left = mid + 1 + } + } + return false +} diff --git a/Week_04/search-a-2d-matrix/searchMatrix_test.go b/Week_04/search-a-2d-matrix/searchMatrix_test.go new file mode 100644 index 0000000..f772b84 --- /dev/null +++ b/Week_04/search-a-2d-matrix/searchMatrix_test.go @@ -0,0 +1,20 @@ +package search_a_2d_matrix + +import "testing" + +func TestSearchMatrix(t *testing.T) { + matrix := [][]int{{1, 3, 5, 7}, {10, 11, 16, 20}, {23, 30, 34, 60}} + target := 3 + want := true + got := SearchMatrix(matrix, target) + if got != want { + t.Errorf("want:%t got:%t", want, got) + } + matrix = [][]int{{1, 3, 5, 7}, {10, 11, 16, 20}, {23, 30, 34, 60}} + target = 13 + want = false + got = SearchMatrix(matrix, target) + if got != want { + t.Errorf("want:%t got:%t", want, got) + } +} diff --git a/Week_04/search-in-rotated-sorted-array/search.go b/Week_04/search-in-rotated-sorted-array/search.go new file mode 100644 index 0000000..b17f918 --- /dev/null +++ b/Week_04/search-in-rotated-sorted-array/search.go @@ -0,0 +1,33 @@ +package search_in_rotated_sorted_array + +func Search(nums []int, target int) int { + if len(nums) <= 0 { + return -1 + } + left, right := 0, len(nums) -1 + for left <= right { + mid := (left + right) / 2 + if nums[mid] == target { + return mid + } else if nums[0] <= nums[mid] { + // 左半部分有序 + // 目标在左半部分 + if nums[0] <= target && target < nums[mid] { + right = mid - 1 + }else{ + // 否则目标在右半部分 + left = mid + 1 + } + }else{ + // 右半部分有序 + // 目标在右半部分 + if nums[mid] < target && target <= nums[len(nums)-1] { + left = mid + 1 + }else{ + // 否则目标在左半部分 + right = mid - 1 + } + } + } + return -1 +} diff --git a/Week_04/search-in-rotated-sorted-array/search_test.go b/Week_04/search-in-rotated-sorted-array/search_test.go new file mode 100644 index 0000000..fe9b8c6 --- /dev/null +++ b/Week_04/search-in-rotated-sorted-array/search_test.go @@ -0,0 +1,41 @@ +package search_in_rotated_sorted_array + +import "testing" + +func TestSearch(t *testing.T) { + nums := []int{4,5,6,7,0,1,2} + target := 0 + want := 4 + got := Search(nums, target) + if got != want { + t.Errorf("want:%d got:%d", want, got) + } + nums = []int{4,5,6,7,0,1,2} + target = 3 + want = -1 + got = Search(nums, target) + if got != want { + t.Errorf("want:%d got:%d", want, got) + } + nums = []int{1} + target = 0 + want = -1 + got = Search(nums, target) + if got != want { + t.Errorf("want:%d got:%d", want, got) + } + nums = []int{1} + target = 1 + want = 0 + got = Search(nums, target) + if got != want { + t.Errorf("want:%d got:%d", want, got) + } + nums = []int{3, 1} + target = 1 + want = 1 + got = Search(nums, target) + if got != want { + t.Errorf("want:%d got:%d", want, got) + } +} diff --git a/Week_04/walking-robot-simulation/robotSim.go b/Week_04/walking-robot-simulation/robotSim.go new file mode 100644 index 0000000..2e20af1 --- /dev/null +++ b/Week_04/walking-robot-simulation/robotSim.go @@ -0,0 +1,43 @@ +package walking_robot_simulation + +func RobotSim(commands []int, obstacles [][]int) int { + // ans为结果,x,y为坐标轴上的位置 + ans, x, y := 0, 0, 0 + // 当前方向 + direct := 0 + // 北: 向Y+方向移动1格, 东:向X+方向移动1格, 南:向Y-方向移动1格, 西:向X-方向移动1格 + dirs := [4][2]int{{0, 1}, {1, 0}, {0, -1}, {-1,0}} + // 记录障碍 + obstacleMap := make(map[[2]int]bool) + for _, obstacle := range obstacles { + obstacleMap[[2]int{obstacle[0], obstacle[1]}] = true + } + for _, command := range commands { + switch command { + // 右转90度 + case -1: + direct++ + if direct >= 4 { + direct = 0 + } + // 左转90度 + case -2: + direct-- + if direct < 0 { + direct = 3 + } + // 在当前方向直接前进 + default: + // 在当前方向移动,直到遇到障碍 + for ; command > 0 && !obstacleMap[[2]int{x+dirs[direct][0], y+dirs[direct][1]}]; command-- { + x += dirs[direct][0] + y += dirs[direct][1] + } + // 更新最大值 + if ans < x * x + y * y { + ans = x * x + y * y + } + } + } + return ans +} diff --git a/Week_04/walking-robot-simulation/robotSim_test.go b/Week_04/walking-robot-simulation/robotSim_test.go new file mode 100644 index 0000000..451d4d3 --- /dev/null +++ b/Week_04/walking-robot-simulation/robotSim_test.go @@ -0,0 +1,20 @@ +package walking_robot_simulation + +import "testing" + +func TestRobotSim(t *testing.T) { + commands := []int{4,-1,3} + obstacles := [][]int{} + want := 25 + got := RobotSim(commands, obstacles) + if got != want { + t.Errorf("want:%d got:%d", want, got) + } + commands = []int{4,-1,4,-2,4} + obstacles = [][]int{{2,4}} + want = 65 + got = RobotSim(commands, obstacles) + if got != want { + t.Errorf("want:%d got:%d", want, got) + } +} diff --git a/Week_04/word-ladder-ii/findLadders.go b/Week_04/word-ladder-ii/findLadders.go new file mode 100644 index 0000000..c2bab46 --- /dev/null +++ b/Week_04/word-ladder-ii/findLadders.go @@ -0,0 +1,98 @@ +package word_ladder_ii + +func FindLadders(beginWord string, endWord string, wordList []string) [][]string { + //字典表(将wordList中的单词放入hash表中,方便查找) + dict:=make(map[string]bool,0) + for _,v:=range wordList{ + dict[v]=true + } + //如果endWord不在hash表中,表示不存在转换列表,返回空集合 + if !dict[endWord]{ + return [][]string{} + } + //将第一个单词放入hash表中,方便实现邻接表(构建图) + dict[beginWord]=true + //构建邻接表 + graph:=make(map[string][]string,0) + //执行bfs搜索,找到每个点到endWord的距离 + distance:=bfs(endWord,dict,graph) + res:=make([][]string,0)//保存结果 + //执行dfs操作 + dfs(beginWord,endWord,&res,[]string{},distance,graph) + return res +} + +func dfs(beginWord string,endWord string,res *[][]string,path []string,distance map[string]int,graph map[string][]string){ + //出递归条件 + if beginWord==endWord{ + path=append(path,beginWord) //加入endWord节点 + tmp:=make([]string,len(path)) + copy(tmp,path) + (*res)=append((*res),tmp) + path=path[:len(path)-1] //移除endWord节点 + return + } + //否则遍历图 + for _,v:=range graph[beginWord]{ + //遍历图时,朝着距离与终点越来越近的方向进行(当前节点的距离肯定要比下一个距离大1) + if distance[beginWord]==distance[v]+1{ + path=append(path,beginWord) + dfs(v,endWord,res,path,distance,graph) + //回溯(执行完上述的所有时,将其回溯回去) + path=path[:len(path)-1] + } + } +} + +//从终点出发,进行bfs,计算每一个点到达终点的距离 +func bfs(endWord string,dict map[string]bool,graph map[string][]string)map[string]int{ + distance:=make(map[string]int,0) //每个点到终点的距离 + queue:=make([]string,0) + queue=append(queue,endWord) + distance[endWord]=0 //初始值 + for len(queue)!=0{ + cursize:=len(queue) + for i:=0;i 0 { + ans++ + size := len(queue) + for i := 0; i < size; i++ { + // 出队列 + word := queue[0] + queue = queue[1:] + // 已经和最终单词相同,则返回值最小转换次数 + if word == endWord { + return ans + } + // 已经变换过的从字典中删除 + delete(wm, word) + // 单词的每位依次替换为:a~z + for j := 0; j < len(word); j++ { + bw := []byte(word) + c := bw[j] + for k := 0; k < 26; k++ { + bw[j] = byte(int('a') + k) + // 替换后的单词在字典列表中才入队 + if wm[string(bw)] { + queue = append(queue, string(bw)) + } + } + bw[j] = c + } + } + } + return 0 +} + +// 优化:双向bfs, 优化后耗时几十ms +func LadderLength2(beginWord string, endWord string, wordList []string) int { + ans := 0 + // 存放字典列表 + wm := make(map[string]bool) + for _, word := range wordList { + wm[word] = true + } + // 字典中不包括最后的单词,则肯定达不到目标,提前结束 + if !wm[endWord] { + return ans + } + // 头部、尾部、已经访问的元素map + beginMap, endMap, visited := make(map[string]bool), make(map[string]bool), make(map[string]bool) + beginMap[beginWord] = true + endMap[endWord] = true + for len(beginMap) > 0 && len(endMap) > 0 { + ans++ + // 元素少的一端开始处理 + if len(beginMap) > len(endMap) { + beginMap, endMap = endMap, beginMap + } + tmp := make(map[string]bool) + // 替换并判断 + for word, _ := range beginMap { + bw := []byte(word) + for i := 0; i < len(bw); i++ { + old := bw[i] + for j := 0; j < 26; j++ { + bw[i] = byte(int('a') + j) + sw := string(bw) + if endMap[sw] { + return ans + 1 + } + if !visited[sw] && wm[sw] { + tmp[sw] = true + visited[sw] = true + } + } + bw[i] = old + } + } + beginMap = tmp + } + return 0 +} \ No newline at end of file diff --git a/Week_04/word-ladder/ladderLength_test.go b/Week_04/word-ladder/ladderLength_test.go new file mode 100644 index 0000000..0922fd1 --- /dev/null +++ b/Week_04/word-ladder/ladderLength_test.go @@ -0,0 +1,41 @@ +package word_ladder + +import "testing" + +func TestLadderLength(t *testing.T) { + beginWord := "hit" + endWord := "cog" + wordList:= []string{"hot","dot","dog","lot","log","cog"} + want := 5 + got := LadderLength(beginWord, endWord, wordList) + if got != want { + t.Errorf("want:%d got:%d", want, got) + } + beginWord = "hit" + endWord = "cog" + wordList = []string{"hot","dot","dog","lot","log"} + want = 0 + got = LadderLength(beginWord, endWord, wordList) + if got != want { + t.Errorf("want:%d got:%d", want, got) + } +} + +func TestLadderLength2(t *testing.T) { + beginWord := "hit" + endWord := "cog" + wordList:= []string{"hot","dot","dog","lot","log","cog"} + want := 5 + got := LadderLength2(beginWord, endWord, wordList) + if got != want { + t.Errorf("want:%d got:%d", want, got) + } + beginWord = "hit" + endWord = "cog" + wordList = []string{"hot","dot","dog","lot","log"} + want = 0 + got = LadderLength2(beginWord, endWord, wordList) + if got != want { + t.Errorf("want:%d got:%d", want, got) + } +} diff --git a/Week_06/README.md b/Week_06/README.md index 50de304..4985c0d 100644 --- a/Week_06/README.md +++ b/Week_06/README.md @@ -1 +1,5 @@ -学习笔记 \ No newline at end of file +学习笔记 + +1、 这两周主要学习了动态规划,主要找状态转移方程,单感觉很多时候不是那么好找。 + +2、在做homework时,里边很多困难的题目,做着比较吃力,还需要进一步刷题。 diff --git a/Week_06/burst-balloons/maxCoins.go b/Week_06/burst-balloons/maxCoins.go new file mode 100644 index 0000000..ca141ed --- /dev/null +++ b/Week_06/burst-balloons/maxCoins.go @@ -0,0 +1,33 @@ +package burst_balloons + +// 动态转移方程:dp[i][j] = dp[i][k] + dp[k][j] +func MaxCoins(nums []int) int { + n := len(nums) + ans := make([][]int, n + 2) + for i := 0; i < n + 2; i++ { + ans[i] = make([]int, n + 2) + } + // 前后增加一个元素,简化处理 + val := make([]int, n + 2) + val[0], val[n+1] = 1, 1 + for i := 1; i <= n; i++ { + val[i] = nums[i-1] + } + for i := n - 1; i >= 0; i-- { + for j := i + 2; j <= n + 1; j++ { + for k := i + 1; k < j; k++ { + sum := val[i] * val[k] * val[j] + sum += ans[i][k] + ans[k][j] + ans[i][j] = max(ans[i][j], sum) + } + } + } + return ans[0][n+1] +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} diff --git a/Week_06/burst-balloons/maxCoins_test.go b/Week_06/burst-balloons/maxCoins_test.go new file mode 100644 index 0000000..5968692 --- /dev/null +++ b/Week_06/burst-balloons/maxCoins_test.go @@ -0,0 +1,12 @@ +package burst_balloons + +import "testing" + +func TestMaxCoins(t *testing.T) { + nums := []int{3,1,5,8} + want := 167 + got := MaxCoins(nums) + if got != want { + t.Errorf("want:%d got:%d", want, got) + } +} \ No newline at end of file diff --git a/Week_06/decode-ways/numDecodings.go b/Week_06/decode-ways/numDecodings.go new file mode 100644 index 0000000..608c0ba --- /dev/null +++ b/Week_06/decode-ways/numDecodings.go @@ -0,0 +1,32 @@ +package decode_ways + +func NumDecodings(s string) int { + if len(s) <= 0 || s[0] == '0' { + return 0 + } + n := len(s) + dp := make([]int, n + 1) + dp[0], dp[1] = 1, 1 + for i := 1; i < n; i++ { + // 当前为'0',则前一个值需要时'1'或'2' + if s[i] == '0' { + if s[i-1] == '1' || s[i-1] == '2' { + // 此种情况s[i]s[i-1]被唯一编码,不增加情况 + dp[i+1] = dp[i-1] + }else{ + // s[i-1]不是'1'或'2',则不能编码 + return 0 + } + }else{ + // 当前位和前一位要小于26,则当前为和前一位可以单独编码也可以连起来编码 + if s[i-1] == '1' || (s[i-1] == '2' && s[i] <= '6') { + // 分开编码为dp[i], 合并编码为dp[i-1] + dp[i+1] = dp[i] + dp[i-1] + }else{ + // 否则只能分开编码,则为dp[i] + dp[i+1] = dp[i] + } + } + } + return dp[n] +} diff --git a/Week_06/decode-ways/numDecodings_test.go b/Week_06/decode-ways/numDecodings_test.go new file mode 100644 index 0000000..d708d22 --- /dev/null +++ b/Week_06/decode-ways/numDecodings_test.go @@ -0,0 +1,18 @@ +package decode_ways + +import "testing" + +func TestNumDecodings(t *testing.T) { + s := "12" + want := 2 + got := NumDecodings(s) + if got != want { + t.Errorf("want:%d got:%d", want, got) + } + s = "226" + want = 3 + got = NumDecodings(s) + if got != want { + t.Errorf("want:%d got:%d", want, got) + } +} diff --git a/Week_06/edit-distance/minDistance.go b/Week_06/edit-distance/minDistance.go new file mode 100644 index 0000000..5a404a6 --- /dev/null +++ b/Week_06/edit-distance/minDistance.go @@ -0,0 +1,47 @@ +package edit_distance + +func min(a, b int) int { + if a < b { + return a + } + return b +} + +/* + 增,dp[i][j] = dp[i][j - 1] + 1 + 删,dp[i][j] = dp[i - 1][j] + 1 + 改,dp[i][j] = dp[i - 1][j - 1] + 1 + 状态转移方程: + dp[i][j] = min(dp[i - 1][j] , dp[i][j - 1] , dp[i - 1][j - 1]) + 1 + 如果刚好这两个字母相同 word1[i - 1] = word2[j - 1] ,那么可以直接参考 dp[i - 1][j - 1] ,操作不用加一 +*/ + +func MinDistance(word1 string, word2 string) int { + n1, n2 := len(word1), len(word2) + if n1 == 0 && n2 == 0 { + return 0 + } + dp := make([][]int, n1 + 1) + for i := range dp { + dp[i] = make([]int, n2 + 1) + } + dp[0][0] = 0 + // 第0列 + for i := 1; i <= n2; i++ { + dp[0][i] = i + } + // 第0行 + for i := 1; i <= n1; i++ { + dp[i][0] = i + } + for i := 1; i <= n1; i ++ { + for j := 1; j <= n2; j++ { + dp[i][j] = min(min(dp[i-1][j], dp[i][j-1]), dp[i-1][j-1]) + 1 + // 两个字符刚好一样,那么可以直接参考 dp[i - 1][j - 1] + if word1[i-1] == word2[j-1] { + dp[i][j] = min(dp[i][j],dp[i-1][j-1]) + } + } + } + return dp[n1][n2] +} diff --git a/Week_06/edit-distance/minDistance_test.go b/Week_06/edit-distance/minDistance_test.go new file mode 100644 index 0000000..02e8830 --- /dev/null +++ b/Week_06/edit-distance/minDistance_test.go @@ -0,0 +1,13 @@ +package edit_distance + +import "testing" + +func TestMinDistance(t *testing.T) { + word1 := "horse" + word2 := "ros" + want := 3 + got := MinDistance(word1, word2) + if got != want { + t.Errorf("want:%d got:%d", want, got) + } +} diff --git a/Week_06/frog-jump/canCross.go b/Week_06/frog-jump/canCross.go new file mode 100644 index 0000000..1e41670 --- /dev/null +++ b/Week_06/frog-jump/canCross.go @@ -0,0 +1,28 @@ +package frog_jump + +func CanCross(stones []int) bool { + if len(stones) <= 0 { + return false + } + dp := make(map[int]map[int]struct{}, len(stones)) + // 初始化每块石头及跳到的次数 + for _, i := range stones { + dp[i] = make(map[int]struct{}) + } + // 第0块石头初始化跳数0 + dp[0][0] = struct{}{} + for i := 0; i < len(stones); i++ { + for k, _ := range dp[stones[i]] { + // 从当前石头跳k-1~k+1到另一块石头 + for step := k - 1; step <= k + 1; step++ { + if step > 0 { + if _, ok := dp[stones[i] + step]; ok { + // 能够跳到另一块石头,记录下跳的步数step + dp[stones[i] + step][step] = struct{}{} + } + } + } + } + } + return len(dp[stones[len(stones)-1]]) != 0 +} diff --git a/Week_06/frog-jump/canCross_test.go b/Week_06/frog-jump/canCross_test.go new file mode 100644 index 0000000..34ec678 --- /dev/null +++ b/Week_06/frog-jump/canCross_test.go @@ -0,0 +1,18 @@ +package frog_jump + +import "testing" + +func TestCanCross(t *testing.T) { + stones := []int{0,1,3,5,6,8,12,17} + want := true + got := CanCross(stones) + if got != want { + t.Errorf("want:%t got:%t", want, got) + } + stones = []int{0,1,2,3,4,8,9,11} + want = false + got = CanCross(stones) + if got != want { + t.Errorf("want:%t got:%t", want, got) + } +} diff --git a/Week_06/longest-valid-parentheses/longestValidParentheses.go b/Week_06/longest-valid-parentheses/longestValidParentheses.go new file mode 100644 index 0000000..7898689 --- /dev/null +++ b/Week_06/longest-valid-parentheses/longestValidParentheses.go @@ -0,0 +1,70 @@ +package longest_valid_parentheses + +// dp解法 +func LongestValidParentheses(s string) int { + if len(s) <= 1 { + return 0 + } + n, max := len(s), 0 + dp := make([]int, n) + // 第0个字符,无配对 + dp[0] = 0 + // 第1个字符,为2或者0 + if s[0] == '(' && s[1] == ')' { + dp[1] = 2 + max = 2 + }else{ + dp[1] = 0 + } + // dp方程 + // dp[i]= dp[i−2]+2; s[i] = ')', s[i-1] = '(' 的情况 + // dp[i] = dp[i - 1] + 2 + dp[i - dp[i - 1] - 2]; s[i]=')', s[i-1] = ')'的情况 + for i := 2; i < n; i++ { + // 为左括号,则不会配对 + if s[i] == '(' { + dp[i] = 0 + }else{ + // 前一个为左括号,则直接配对 + if s[i-1] == '(' { + dp[i] = dp[i-2] + 2 + }else if dp[i-1] > 0 { + // 找到配对的地方进行判断 + if (i - dp[i - 1] - 1) >= 0 && s[i - dp[i - 1] - 1] == '(' { + dp[i] = dp[i - 1] + 2 + if (i - dp[i - 1] - 2) >= 0 { + dp[i] = dp[i] + dp[i - dp[i - 1] - 2] + } + } + }else{ + dp[i] = 0 + } + } + if dp[i] > max { + max = dp[i] + } + } + return max +} + +// 栈解法 +func LongestValidParentheses2(s string) int { + if len(s) <= 1 { + return 0 + } + ans, stack := 0, []int{-1} + for i := range s { + if s[i] == '(' { + stack = append(stack, i) + }else{ + stack = stack[0:len(stack)-1] + if len(stack) == 0 { + stack = append(stack, i) + } + top := stack[len(stack)-1] + if ans < i - top { + ans = i - top + } + } + } + return ans +} \ No newline at end of file diff --git a/Week_06/longest-valid-parentheses/longestValidParentheses_test.go b/Week_06/longest-valid-parentheses/longestValidParentheses_test.go new file mode 100644 index 0000000..c31d074 --- /dev/null +++ b/Week_06/longest-valid-parentheses/longestValidParentheses_test.go @@ -0,0 +1,21 @@ +package longest_valid_parentheses + +import "testing" + +func TestLongestValidParentheses(t *testing.T) { + s := ")()())" + want := 4 + got := LongestValidParentheses(s) + if got != want { + t.Errorf("want:%d got:%d", want, got) + } +} + +func TestLongestValidParentheses2(t *testing.T) { + s := ")()())" + want := 4 + got := LongestValidParentheses2(s) + if got != want { + t.Errorf("want:%d got:%d", want, got) + } +} diff --git a/Week_06/max-sum-of-rectangle-no-larger-than-k/maxSumSubmatrix.go b/Week_06/max-sum-of-rectangle-no-larger-than-k/maxSumSubmatrix.go new file mode 100644 index 0000000..af4ab64 --- /dev/null +++ b/Week_06/max-sum-of-rectangle-no-larger-than-k/maxSumSubmatrix.go @@ -0,0 +1,79 @@ +package max_sum_of_rectangle_no_larger_than_k + +import "math" + +func MaxSumSubmatrix(matrix [][]int, k int) int { + for len(matrix) == 0 || len(matrix[0]) == 0 { + return 0 + } + maxSum, rows, cols := math.MinInt32, len(matrix), len(matrix[0]) + for left := 0; left < cols; left++ { + rowSums := make([]int,rows) + // 扩展一列求一次值,并更新小于k的最大值 + for right := left; right < cols; right++ { + // 逐行扩展列 + for row := 0; row < rows; row++ { + rowSums[row] += matrix[row][right] + } + // 更新新值 + maxSum = max(maxSum, maxKSubArray(rowSums, k)) + // 提前结束 + if maxSum == k { + return maxSum + } + } + } + return maxSum +} + +func maxKSubArray(nums []int, k int) int { + if len(nums) <= 0 { + return 0 + } + // O(n)求小于k的子数组 + cur, maxValue := math.MinInt32, 0 + for _, num := range nums { + if cur > 0 { + cur = cur + num + }else{ + cur = num + } + // 提前结束 + if cur == k { + return k + } + if cur > maxValue { + maxValue = cur + } + } + if maxValue < k { + return maxValue + } + // 求得的值比k大,则用暴力方式重新求值O(n^2) + cur = math.MinInt32 + for i := 0; i < len(nums); i++ { + sum := 0 + for j := i; j < len(nums); j++ { + sum += nums[j] + // 提前结束 + if sum == k { + return sum + } + // 更新小于k的最大值 + if sum < k && sum > cur { + cur = sum + } + } + } + return cur +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} + + + diff --git a/Week_06/max-sum-of-rectangle-no-larger-than-k/maxSumSubmatrix_test.go b/Week_06/max-sum-of-rectangle-no-larger-than-k/maxSumSubmatrix_test.go new file mode 100644 index 0000000..12c759d --- /dev/null +++ b/Week_06/max-sum-of-rectangle-no-larger-than-k/maxSumSubmatrix_test.go @@ -0,0 +1,13 @@ +package max_sum_of_rectangle_no_larger_than_k + +import "testing" + +func TestMaxSumSubmatrix(t *testing.T) { + matrix := [][]int{{1, 0, 1}, {0, -2, 3}} + k := 2 + want := 2 + got := MaxSumSubmatrix(matrix, k) + if got != want { + t.Errorf("want:%d got:%d", want, got) + } +} diff --git a/Week_06/maximal-square/maximalSquare.go b/Week_06/maximal-square/maximalSquare.go new file mode 100644 index 0000000..066a9fc --- /dev/null +++ b/Week_06/maximal-square/maximalSquare.go @@ -0,0 +1,43 @@ +package maximal_square + +func min(a, b int) int { + if a < b { + return a + } + return b +} + +func MaximalSquare(matrix [][]byte) int { + if len(matrix) == 0 || len(matrix[0]) == 0 { + return 0 + } + // 边长 + ans := 0 + // 二维dp数组 + m, n := len(matrix),len(matrix[0]) + dp := make([][]int, m) + for idx := range dp { + dp[idx] = make([]int, n) + } + // dp方程 + // dp[row][col] = min(dp[row-1][col],min(dp[row][col-1], dp[row-1][col-1])) + 1 + for row := range matrix { + for col := range matrix[row] { + // 当前cell值为'0'时,则dp值则为0 + if matrix[row][col] == '0' { + dp[row][col] = 0 + } else if row == 0 || col == 0 { + // 在第0行或第0列,dp值为1,为'0'的情况已经在前边处理了 + dp[row][col] = 1 + }else{ + // 其他情况直接应用dp方程 + dp[row][col] = min(dp[row-1][col],min(dp[row][col-1], dp[row-1][col-1])) + 1 + } + // 更新最大边长 + if dp[row][col] > ans { + ans =dp[row][col] + } + } + } + return ans * ans +} diff --git a/Week_06/maximal-square/maximalSquare_test.go b/Week_06/maximal-square/maximalSquare_test.go new file mode 100644 index 0000000..7dbfab7 --- /dev/null +++ b/Week_06/maximal-square/maximalSquare_test.go @@ -0,0 +1,12 @@ +package maximal_square + +import "testing" + +func TestMaximalSquare(t *testing.T) { + matrix := [][]byte{{'1','0','1','0','0'},{'1','0','1','1','1'},{'1','1','1','1','1'},{'1','0','0','1','0'}} + want := 4 + got := MaximalSquare(matrix) + if got != want { + t.Errorf("want:%d got:%d", want, got) + } +} diff --git a/Week_06/minimum-path-sum/minPathSum.go b/Week_06/minimum-path-sum/minPathSum.go new file mode 100644 index 0000000..f4459f5 --- /dev/null +++ b/Week_06/minimum-path-sum/minPathSum.go @@ -0,0 +1,34 @@ +package minimum_path_sum + +func MinPathSum(grid [][]int) int { + if len(grid) <= 0 || len(grid[0]) <= 0 { + return 0 + } + // 行、列 + m, n := len(grid), len(grid[0]) + // dp问题, dp公式为: dp[i][j] = min(dp[i][j-1],dp[i-1][j]) + grid[i][j] + // 可通过一维数组简化 + dp := make([]int, n) + dp[0] = grid[0][0] + // dp数组初始化, 初始化第0行的处理 + for i := 1; i < n; i++ { + dp[i] = dp[i-1] + grid[0][i] + } + // 第1行开始 + for i := 1; i < m; i++ { + // 第0列为上一行此列值加上当前表格的值 + dp[0] += grid[i][0] + for j := 1; j < n; j++ { + // 当前值为上一行列值或当前行前一列值中较小值加上当前表格的值 + dp[j] = min(dp[j-1], dp[j]) + grid[i][j] + } + } + return dp[n-1] +} + +func min(a, b int) int { + if a < b { + return a + } + return b +} diff --git a/Week_06/minimum-path-sum/minPathSum_test.go b/Week_06/minimum-path-sum/minPathSum_test.go new file mode 100644 index 0000000..188143d --- /dev/null +++ b/Week_06/minimum-path-sum/minPathSum_test.go @@ -0,0 +1,12 @@ +package minimum_path_sum + +import "testing" + +func TestMinPathSum(t *testing.T) { + grid := [][]int{{1,3,1},{1,5,1},{4,2,1}} + want := 7 + got := MinPathSum(grid) + if got != want { + t.Errorf("want:%d got:%d", want, got) + } +} diff --git a/Week_06/minimum-window-substring/minWindow.go b/Week_06/minimum-window-substring/minWindow.go new file mode 100644 index 0000000..183620c --- /dev/null +++ b/Week_06/minimum-window-substring/minWindow.go @@ -0,0 +1,37 @@ +package minimum_window_substring + +import "math" + +func MinWindow(s string, t string) string { + ans, cnt := "", math.MaxInt32 + // 用于记录字符出现次数 + hm := make(map[byte]int) + for idx := range t { + hm[t[idx]]++ + } + l, r := 0, 0 + for ; r < len(s); r++ { + hm[s[r]]-- + // s中已经包含全部t,则缩小窗口,找最小子串 + for check(hm) { + if r - l + 1 < cnt { + cnt = r -l + 1 + ans = s[l: r+1] + } + hm[s[l]]++ + l++ + } + } + return ans +} + +// 检查s中是否完全包含t +func check(hm map[byte]int) bool { + for _, v := range hm { + if v > 0 { + return false + } + } + return true +} + diff --git a/Week_06/minimum-window-substring/minWindow_test.go b/Week_06/minimum-window-substring/minWindow_test.go new file mode 100644 index 0000000..4076ab8 --- /dev/null +++ b/Week_06/minimum-window-substring/minWindow_test.go @@ -0,0 +1,13 @@ +package minimum_window_substring + +import "testing" + +func TestMinWindow(t *testing.T) { + s := "ADOBECODEBANC" + ts := "ABC" + want := "BANC" + got := MinWindow(s, ts) + if got != want { + t.Errorf("want:%s got:%s", want, got) + } +} diff --git a/Week_06/palindromic-substrings/countSubstrings.go b/Week_06/palindromic-substrings/countSubstrings.go new file mode 100644 index 0000000..b67033f --- /dev/null +++ b/Week_06/palindromic-substrings/countSubstrings.go @@ -0,0 +1,27 @@ +package palindromic_substrings + +/* +dp状态转移分析: +当s[i]与s[j]相等时,有如下三种情况 +情况一:下标i 与 j相同,同一个字符例如a,是回文子串 +情况二:下标i 与 j相差为1,例如aa,也是文子串 +情况三:下标:i 与 j相差大于1的时候,此时s[i]与s[j]已经相同了,看dp[i + 1][j - 1]是否为true。 +情况一和情况二统一为:j - i < 2 +*/ + +func CountSubstrings(s string) int { + n, count := len(s), 0 + dp := make([][]bool,n) + for idx := range dp { + dp[idx] = make([]bool, n) + } + for i := n -1; i >= 0; i-- { + for j := i;j < n; j++ { + if s[i] == s[j] && (j -i < 2 || dp[i+1][j-1]) { + dp[i][j] = true + count++ + } + } + } + return count +} diff --git a/Week_06/palindromic-substrings/countSubstrings_test.go b/Week_06/palindromic-substrings/countSubstrings_test.go new file mode 100644 index 0000000..442eb28 --- /dev/null +++ b/Week_06/palindromic-substrings/countSubstrings_test.go @@ -0,0 +1,18 @@ +package palindromic_substrings + +import "testing" + +func TestCountSubstrings(t *testing.T) { + s := "abc" + want := 3 + got := CountSubstrings(s) + if got != want { + t.Errorf("want:%d got:%d", want, got) + } + s = "aaa" + want = 6 + got = CountSubstrings(s) + if got != want { + t.Errorf("want:%d got:%d", want, got) + } +} diff --git a/Week_06/split-array-largest-sum/splitArray.go b/Week_06/split-array-largest-sum/splitArray.go new file mode 100644 index 0000000..0e811fa --- /dev/null +++ b/Week_06/split-array-largest-sum/splitArray.go @@ -0,0 +1,41 @@ +package split_array_largest_sum + +func SplitArray(nums []int, m int) int { + // 将nums分子数组,各子数组和的值的范围 + max, sum := 0, 0 + for _, num := range nums { + sum += num + if num > max { + max = num + } + } + left, right := max, sum + for left < right { + mid := (right + left) / 2 + // 保证各子数组的值不大于mid,划分子数组 + count := split(nums, mid) + // 划分出的子数组数量大于m,则mid值小了,在右区间继续处理 + if count > m { + left = mid + 1 + }else{ + // mid值大了,在左区间继续处理 + right = mid + } + } + return left +} + +// 将数组分成count个子数组,每个子数组和小于value +func split(nums []int, value int) int { + count := 1 + cur := 0 + for _, num := range nums { + if cur + num > value { + count++ + cur = num + }else{ + cur += num + } + } + return count +} diff --git a/Week_06/split-array-largest-sum/splitArray_test.go b/Week_06/split-array-largest-sum/splitArray_test.go new file mode 100644 index 0000000..3ad5f9a --- /dev/null +++ b/Week_06/split-array-largest-sum/splitArray_test.go @@ -0,0 +1,13 @@ +package split_array_largest_sum + +import "testing" + +func TestSplitArray(t *testing.T) { + nums := []int{7,2,5,10,8} + m := 2 + want := 18 + got := SplitArray(nums, m) + if got != want { + t.Errorf("want:%d got:%d", want, got) + } +} diff --git a/Week_06/student-attendance-record-ii/checkRecord.go b/Week_06/student-attendance-record-ii/checkRecord.go new file mode 100644 index 0000000..0e67d9f --- /dev/null +++ b/Week_06/student-attendance-record-ii/checkRecord.go @@ -0,0 +1,27 @@ +package student_attendance_record_ii + +func CheckRecord(n int) int { + MOD := 1000000007 + // 一维用于遍历数组, 二维记录A的数量,三维记录L的数量 + // 满足条件A的取值是0,1;满足条件的L的取值是0,1,2 + dp := make([][2][3]int, n + 1) + // 为了简化,dp下标从1开始,对应第N个字符 + // 初始化dp方程 + dp[1][1][0] = 1 + dp[1][0][1] = 1 + dp[1][0][0] = 1 + for i := 2; i <= n; i++ { + // +P + dp[i][0][0] = (dp[i-1][0][0] + dp[i-1][0][1] + dp[i-1][0][2]) % MOD + dp[i][1][0] = (dp[i-1][1][0] + dp[i-1][1][1] + dp[i-1][1][2]) % MOD + // +L + dp[i][0][1] = dp[i-1][0][0] + dp[i][1][1] = dp[i-1][1][0] + dp[i][0][2] = dp[i-1][0][1] + dp[i][1][2] = dp[i-1][1][1] + // +A + // 此处的 dp[i][1][0] 在上面 +P 时已经开始出现,故此处应 += + dp[i][1][0] += (dp[i-1][0][0] + dp[i-1][0][1] + dp[i-1][0][2]) % MOD + } + return (dp[n][0][0] + dp[n][0][1] + dp[n][0][2] + dp[n][1][0] + dp[n][1][1] + dp[n][1][2]) % MOD +} diff --git a/Week_06/student-attendance-record-ii/checkRecord_test.go b/Week_06/student-attendance-record-ii/checkRecord_test.go new file mode 100644 index 0000000..908cc44 --- /dev/null +++ b/Week_06/student-attendance-record-ii/checkRecord_test.go @@ -0,0 +1,12 @@ +package student_attendance_record_ii + +import "testing" + +func TestCheckRecord(t *testing.T) { + n := 2 + want := 8 + got := CheckRecord(n) + if got != want { + t.Errorf("want:%d got:%d", want, got) + } +} diff --git a/Week_06/task-scheduler/leastInterval.go b/Week_06/task-scheduler/leastInterval.go new file mode 100644 index 0000000..5ab94fa --- /dev/null +++ b/Week_06/task-scheduler/leastInterval.go @@ -0,0 +1,55 @@ +package task_scheduler + +import "sort" + +/* + 任务分桶 + 数量最多的任务先入桶 + + n + 1 : 冷却时间 + 1个单位执行时间 + A B 冷却 + A B + A B + A B + + 第一种情况: + maxTimes:最多任务的次数 + maxCount:与最多任务相同的数量 + sum = (maxTimes−1)∗(n+1)+maxCount + + 第二种情况: + 任务多,全部可以填冷却时间 + sum = len(tasks) + + 取两个中的较大值 + */ + + +func LeastInterval(tasks []byte, n int) int { + if len(tasks) == 0 { + return 0 + } + // 任务桶 + buckets := make([]int, 26) + for _, task := range tasks { + buckets[task-'A']++ + } + sort.Ints(buckets) + // 最大次数的任务及最大次数任务的个数 + maxTimes, maxCount := buckets[25], 1 + for i := 25; i > 0; i-- { + if buckets[i-1] == buckets[i] { + maxCount++ + }else{ + break + } + } + return max(len(tasks), (maxTimes - 1)*(n + 1) + maxCount) +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} diff --git a/Week_06/task-scheduler/leastInterval_test.go b/Week_06/task-scheduler/leastInterval_test.go new file mode 100644 index 0000000..5f30671 --- /dev/null +++ b/Week_06/task-scheduler/leastInterval_test.go @@ -0,0 +1,13 @@ +package task_scheduler + +import "testing" + +func TestLeastInterval(t *testing.T) { + tasks := []byte{'A','A','A','B','B','B'} + n := 2 + want := 8 + got := LeastInterval(tasks, n) + if got != want { + t.Errorf("want:%d got:%d", want, got) + } +} diff --git a/Week_07/README.md b/Week_07/README.md index 50de304..98bcbe2 100644 --- a/Week_07/README.md +++ b/Week_07/README.md @@ -1 +1,7 @@ -学习笔记 \ No newline at end of file +学习笔记 + +1、通过这周的学习,学习了Trie树,并查集,感觉有一定难度。 + +2、学习了高级搜索算法,双端bfs解单词接龙,基因变化,感觉比较巧妙,启发式搜索没看懂,还需要多看几次视频。 + +3、对avl,红黑数,B树有了一定了解。 diff --git a/Week_07/climbing-stairs/climbStairs.go b/Week_07/climbing-stairs/climbStairs.go new file mode 100644 index 0000000..a55d584 --- /dev/null +++ b/Week_07/climbing-stairs/climbStairs.go @@ -0,0 +1,14 @@ +package climbing_stairs + +func ClimbStairs(n int) int { + if n < 2 { + return 1 + } + f0, f1, f2 := 1, 1, 2 + for i := 3; i <= n; i++ { + f0 = f1 + f1 = f2 + f2 = f0 + f1 + } + return f2 +} diff --git a/Week_07/climbing-stairs/climbStairs_test.go b/Week_07/climbing-stairs/climbStairs_test.go new file mode 100644 index 0000000..c81677a --- /dev/null +++ b/Week_07/climbing-stairs/climbStairs_test.go @@ -0,0 +1,18 @@ +package climbing_stairs + +import "testing" + +func TestClimbStairs(t *testing.T) { + n := 2 + want := 2 + got := ClimbStairs(n) + if got != want { + t.Errorf("want:%d got:%d", want, got) + } + n = 3 + want = 3 + got = ClimbStairs(n) + if got != want { + t.Errorf("want:%d got:%d", want, got) + } +} \ No newline at end of file diff --git a/Week_07/generate-parentheses/generateParenthesis.go b/Week_07/generate-parentheses/generateParenthesis.go new file mode 100644 index 0000000..0ead05e --- /dev/null +++ b/Week_07/generate-parentheses/generateParenthesis.go @@ -0,0 +1,26 @@ +package generate_parentheses + +func GenerateParenthesis(n int) []string { + ans := []string{} + if n <= 0 { + return ans + } + dfs(&ans, n, 0, 0, "") + return ans +} + +func dfs(ans *[]string, n, left, right int, s string){ + // 左、右括号都到达n, 则是一个符合条件的括号对 + if left == n && right == n { + *ans = append(*ans, s) + return + } + // 左括号数量 < n, 放一个左括号 + if left < n { + dfs(ans, n, left + 1, right, s + "(") + } + // 右括号数量 < 左括号数量, 放一个右括号 + if right < left { + dfs(ans, n, left, right + 1, s + ")") + } +} diff --git a/Week_07/generate-parentheses/generateParenthesis_test.go b/Week_07/generate-parentheses/generateParenthesis_test.go new file mode 100644 index 0000000..3f343aa --- /dev/null +++ b/Week_07/generate-parentheses/generateParenthesis_test.go @@ -0,0 +1,15 @@ +package generate_parentheses + +import ( + "reflect" + "testing" +) + +func TestGenerateParenthesis(t *testing.T) { + n := 3 + want := []string{"((()))","(()())","(())()","()(())","()()()"} + got := GenerateParenthesis(n) + if !reflect.DeepEqual(want, got) { + t.Errorf("want:%+v, got:%+v", want, got) + } +} \ No newline at end of file diff --git a/Week_07/implement-trie-prefix-tree/trie.go b/Week_07/implement-trie-prefix-tree/trie.go new file mode 100644 index 0000000..1cd7c2a --- /dev/null +++ b/Week_07/implement-trie-prefix-tree/trie.go @@ -0,0 +1,72 @@ +package implement_trie_prefix_tree + +type TrieNode struct { + // 孩子 + children []*TrieNode + // 是否是单词 + IsWord bool +} + +type Trie struct { + root *TrieNode +} + +/** Initialize your data structure here. */ +func Constructor() Trie { + return Trie{ + root: &TrieNode{ + children: make([]*TrieNode, 26), + }, + } +} + + +/** Inserts a word into the trie. */ +func (this *Trie) Insert(word string) { + p := this.root + for _, ch := range word { + // 没有孩子则创建 + if p.children[ch-'a'] == nil { + p.children[ch-'a'] = &TrieNode{children: make([]*TrieNode, 26)} + } + p = p.children[ch-'a'] + } + // 插入单词后标记单词 + p.IsWord = true +} + + +/** Returns if the word is in the trie. */ +func (this *Trie) Search(word string) bool { + p := this.root + for _, ch := range word { + if p.children[ch-'a'] == nil { + return false + } + p = p.children[ch-'a'] + } + // 最后是单词结尾则已经查到,否则只是一个前缀 + return p.IsWord +} + + +/** Returns if there is any word in the trie that starts with the given prefix. */ +func (this *Trie) StartsWith(prefix string) bool { + p := this.root + for _, ch := range prefix { + if p.children[ch-'a'] == nil { + return false + } + p = p.children[ch-'a'] + } + return true +} + + +/** + * Your Trie object will be instantiated and called as such: + * obj := Constructor(); + * obj.Insert(word); + * param_2 := obj.Search(word); + * param_3 := obj.StartsWith(prefix); + */ diff --git a/Week_07/implement-trie-prefix-tree/trie_test.go b/Week_07/implement-trie-prefix-tree/trie_test.go new file mode 100644 index 0000000..c525387 --- /dev/null +++ b/Week_07/implement-trie-prefix-tree/trie_test.go @@ -0,0 +1,29 @@ +package implement_trie_prefix_tree + +import "testing" + +func TestConstructor(t *testing.T) { + tree := Constructor() + tree.Insert("apple") + want := true + got := tree.Search("apple") + if got != want { + t.Errorf("want:%t got:%t", want, got) + } + want = false + got = tree.Search("app") + if got != want { + t.Errorf("want:%t got:%t", want, got) + } + want = true + got = tree.StartsWith("app") + if got != want { + t.Errorf("want:%t got:%t", want, got) + } + tree.Insert("app") + want = true + got = tree.Search("app") + if got != want { + t.Errorf("want:%t got:%t", want, got) + } +} diff --git a/Week_07/minimum-genetic-mutation/minMutation.go b/Week_07/minimum-genetic-mutation/minMutation.go new file mode 100644 index 0000000..af6f142 --- /dev/null +++ b/Week_07/minimum-genetic-mutation/minMutation.go @@ -0,0 +1,51 @@ +package minimum_genetic_mutation + +// 和单词接龙一样双端bfs +func MinMutation(start string, end string, bank []string) int { + if len(start) != len(end) { + return -1 + } + bankMap := make(map[string]bool) + for _, b := range bank { + bankMap[b] = true + } + // 如果基因序列不存在最后的目标基因,则提前退出 + if !bankMap[end] { + return -1 + } + ans := 0 + // 双端bfs + beginMap, endMap, visited := make(map[string]bool), make(map[string]bool), make(map[string]bool) + beginMap[start] = true + endMap[end] = true + changeChs := []byte{'A','C','G','T'} + for len(beginMap) > 0 && len(endMap) > 0 { + if len(beginMap) > len(endMap) { + beginMap, endMap = endMap, beginMap + } + tmpMap := make(map[string]bool) + for word, _ := range beginMap { + bw := []byte(word) + // 一次替换序列的每一位 + for i := range bw { + old := bw[i] + for _, ch := range changeChs { + bw[i] = ch + sw := string(bw) + // 提前找到结果 + if endMap[sw] { + return ans + 1 + } + if !visited[sw] && bankMap[sw] { + visited[sw] = true + tmpMap[sw] = true + } + } + bw[i] = old + } + } + beginMap = tmpMap + ans++ + } + return -1 +} diff --git a/Week_07/minimum-genetic-mutation/minMutation_test.go b/Week_07/minimum-genetic-mutation/minMutation_test.go new file mode 100644 index 0000000..31a149c --- /dev/null +++ b/Week_07/minimum-genetic-mutation/minMutation_test.go @@ -0,0 +1,33 @@ +package minimum_genetic_mutation + +import "testing" + +func TestMinMutation(t *testing.T) { + start := "AACCGGTT" + end := "AACCGGTA" + bank := []string{"AACCGGTA"} + want := 1 + got := MinMutation(start, end, bank) + if got != want { + t.Errorf("want:%d got:%d", want, got) + } + start = "AACCGGTT" + end = "AAACGGTA" + bank = []string{"AACCGGTA", "AACCGCTA", "AAACGGTA"} + want = 2 + got = MinMutation(start, end, bank) + if got != want { + t.Errorf("want:%d got:%d", want, got) + } + /*"AACCGGTT" + "AAACGGTA" + ["AACCGATT","AACCGATA","AAACGATA","AAACGGTA"]*/ + start = "AACCGGTT" + end = "AAACGGTA" + bank = []string{"AACCGATT","AACCGATA","AAACGATA","AAACGGTA"} + want = 4 + got = MinMutation(start, end, bank) + if got != want { + t.Errorf("want:%d got:%d", want, got) + } +} diff --git a/Week_07/n-queens/solveNQueens.go b/Week_07/n-queens/solveNQueens.go new file mode 100644 index 0000000..a65dfb2 --- /dev/null +++ b/Week_07/n-queens/solveNQueens.go @@ -0,0 +1,62 @@ +package n_queens + +import "strings" + +func SolveNQueens(n int) [][]string { + ans := [][]string{} + board := make([]string, n) + // 初始时棋牌全是'.' + for i := 0; i < n; i++ { + board[i] = strings.Repeat(".", n) + } + backtrack(&ans, board, 0) + return ans +} + +func backtrack(ans *[][]string, board []string, row int) { + // 已经放满,记录一个结果 + if row == len(board) { + *ans = append(*ans, append([]string{}, board...)) + return + } + // 在当前行每一列放置一个皇后试探 + for col := 0; col < len(board); col ++ { + if !isValid(board, row, col) { + continue + } + // 放置皇后 + stringAt(&board[row], col, 'Q') + // 继续放置下一行 + backtrack(ans,board, row + 1) + // 恢复状态 + stringAt(&board[row], col, '.') + } +} + +func stringAt(s *string, col int, v rune) { + rs := []rune(*s) + rs[col] = v + *s = string(rs) +} + +func isValid(board []string, row, col int) bool { + // 检查列是否有冲突 + for i := 0; i < row; i++ { + if board[i][col] == 'Q' { + return false + } + } + // 左上对角线 + for i, j := row - 1, col - 1; i >= 0 && j >= 0 ; i, j = i - 1, j - 1 { + if board[i][j] == 'Q' { + return false + } + } + // 右上对角线 + for i, j := row - 1, col + 1; i >= 0 && j < len(board); i, j = i - 1, j + 1 { + if board[i][j] == 'Q' { + return false + } + } + return true +} diff --git a/Week_07/n-queens/solveNQueens_test.go b/Week_07/n-queens/solveNQueens_test.go new file mode 100644 index 0000000..8c89055 --- /dev/null +++ b/Week_07/n-queens/solveNQueens_test.go @@ -0,0 +1,9 @@ +package n_queens + +import "testing" + +func TestSolveNQueens(t *testing.T) { + n := 4 + got := SolveNQueens(n) + t.Logf("got:%+v", got) +} diff --git a/Week_07/number-of-islands/numIslands.go b/Week_07/number-of-islands/numIslands.go new file mode 100644 index 0000000..aca5f3e --- /dev/null +++ b/Week_07/number-of-islands/numIslands.go @@ -0,0 +1,77 @@ +package number_of_islands + +func NumIslands(grid [][]byte) int { + ans := 0 + if len(grid) <= 0 || len(grid[0]) <= 0 { + return ans + } + // 水格子数量 + spaces := 0 + directs := [][]int{{1,0},{0,1},{-1,0},{0,-1}} + // 初始时,连通量 union.count = rows * cols, 处理过程中记下水格子数量,最后用连通量-水格子数量就是岛屿 + union := NewUnionFind(len(grid)*len(grid[0])) + for i := 0; i < len(grid); i++ { + for j := 0; j < len(grid[0]); j++ { + if grid[i][j] == '1' { + // 向四个方向连通 + for _, dr := range directs { + dx := i + dr[0] + dy := j + dr[1] + if dx >= 0 && dx < len(grid) && dy >= 0 && dy < len(grid[0]) && grid[dx][dy] == '1' { + union.Union(i * len(grid[0]) + j, dx * len(grid[0]) + dy) + } + } + }else{ + // 是水,数量+1 + spaces += 1 + } + } + } + // 岛屿=连通量-水格子 + return union.count - spaces +} + +// 构造并查集结构 +type UnionFind struct { + // 连通分量 + count int + // 节点的父节点 + parent []int +} + +// 初始化 +func NewUnionFind(n int) *UnionFind { + u := &UnionFind{ + count: n, + parent: make([]int, n), + } + for i := 0; i < n; i++ { + u.parent[i] = i + } + return u +} + +// 合并p,q +func (u *UnionFind) Union(p, q int){ + rootP := u.Find(p) + rootQ := u.Find(q) + if rootP == rootQ { + return + } + u.parent[rootQ] = rootP + u.count-- +} + +// 查找x的祖先 +func (u *UnionFind) Find(x int) int { + for u.parent[x] != x { + u.parent[x] = u.parent[u.parent[x]] + x = u.parent[x] + } + return x +} + +// 返回连通分量个数 +func (u *UnionFind) Count() int { + return u.count +} \ No newline at end of file diff --git a/Week_07/number-of-islands/numIslands_test.go b/Week_07/number-of-islands/numIslands_test.go new file mode 100644 index 0000000..273c2b5 --- /dev/null +++ b/Week_07/number-of-islands/numIslands_test.go @@ -0,0 +1,26 @@ +package number_of_islands + +import "testing" + +func TestNumIslands(t *testing.T) { + grid := [][]byte{ + {'1', '1', '1', '1', '0'}, + {'1', '1', '0', '1', '0'}, + {'1', '1', '0', '0', '0'}, + {'0', '0', '0', '0', '0'}, + } + want := 1 + got := NumIslands(grid) + if got != want { + t.Errorf("want:%d got:%d", want, got) + } + + grid = [][]byte{ + {'1','0','1','1','0','1','1'}, + } + want = 3 + got = NumIslands(grid) + if got != want { + t.Errorf("want:%d got:%d", want, got) + } +} diff --git a/Week_07/number-of-provinces/findCircleNum.go b/Week_07/number-of-provinces/findCircleNum.go new file mode 100644 index 0000000..bc938e1 --- /dev/null +++ b/Week_07/number-of-provinces/findCircleNum.go @@ -0,0 +1,66 @@ +package number_of_provinces + +func FindCircleNum(isConnected [][]int) int { + if len(isConnected) <= 0 { + return 0 + } + n := len(isConnected) + // 初始化并查集 + union := NewUnionFind(n) + for i := 0; i < n; i++ { + for j := 0; j < n; j++ { + if isConnected[i][j] == 1 { + // 连通并查集 + union.Union(i,j) + } + } + } + return union.Count() +} + +// 构造并查集结构 +type UnionFind struct { + // 连通分量 + count int + // 节点的父节点 + parent []int +} + +// 初始化 +func NewUnionFind(n int) *UnionFind { + u := &UnionFind{ + count: n, + parent: make([]int, n), + } + for i := 0; i < n; i++ { + u.parent[i] = i + } + return u +} + +// 合并p,q +func (u *UnionFind) Union(p, q int){ + rootP := u.Find(p) + rootQ := u.Find(q) + if rootP == rootQ { + return + } + u.parent[rootQ] = rootP + u.count-- +} + +// 查找x的祖先 +func (u *UnionFind) Find(x int) int { + for u.parent[x] != x { + u.parent[x] = u.parent[u.parent[x]] + x = u.parent[x] + } + return x +} + +// 返回连通分量个数 +func (u *UnionFind) Count() int { + return u.count +} + + diff --git a/Week_07/number-of-provinces/findCircleNum_test.go b/Week_07/number-of-provinces/findCircleNum_test.go new file mode 100644 index 0000000..e37077e --- /dev/null +++ b/Week_07/number-of-provinces/findCircleNum_test.go @@ -0,0 +1,12 @@ +package number_of_provinces + +import "testing" + +func TestFindCircleNum(t *testing.T) { + isConnected := [][]int{{1,1,0},{1,1,0},{0,0,1}} + want := 2 + got := FindCircleNum(isConnected) + if got != want { + t.Errorf("want:%d got:%d", want, got) + } +} diff --git a/Week_07/sudoku-solver/solveSudoku.go b/Week_07/sudoku-solver/solveSudoku.go new file mode 100644 index 0000000..758ebb7 --- /dev/null +++ b/Week_07/sudoku-solver/solveSudoku.go @@ -0,0 +1,51 @@ +package sudoku_solver + +func SolveSudoku(board [][]byte) { + if len(board) != 9 || len(board[0]) != 9 { + return + } + solve(board) +} + +func solve(board [][]byte) bool { + for i := 0; i < 9; i++ { + for j := 0; j < 9; j++ { + if board[i][j] != '.' { + continue + } + // 用'1' ~ '9'试探 + for ch := '1'; ch <= '9'; ch++ { + bc := byte(ch) + // 判断字符有效性 + if isValid(board, i, j, bc) { + // 替换 + board[i][j] = bc + // 已经满足条件,提前返回 + if solve(board) { + return true + } else { + // 恢复状态 + board[i][j] = '.' + } + } + } + return false + } + } + return true +} + +// 判断字符填入是否是有效 +func isValid(board [][]byte, row, col int, ch byte) bool { + for i := 0; i < 9; i++ { + // 行或列已经存在次字符 + if board[i][col] == ch || board[row][i] == ch { + return false + } + // 3 * 3 单元格内有次字符 + if board[3 * (row / 3) + i / 3][ 3 * (col / 3) + i % 3] == ch { + return false + } + } + return true +} diff --git a/Week_07/sudoku-solver/solveSudoku_test.go b/Week_07/sudoku-solver/solveSudoku_test.go new file mode 100644 index 0000000..f06b91a --- /dev/null +++ b/Week_07/sudoku-solver/solveSudoku_test.go @@ -0,0 +1,19 @@ +package sudoku_solver + +import "testing" + +func TestSolveSudoku(t *testing.T) { + board := [][]byte{ + {'5','3','.','.','7','.','.','.','.'}, + {'6','.','.','1','9','5','.','.','.'}, + {'.','9','8','.','.','.','.','6','.'}, + {'8','.','.','.','6','.','.','.','3'}, + {'4','.','.','8','.','3','.','.','1'}, + {'7','.','.','.','2','.','.','.','6'}, + {'.','6','.','.','.','.','2','8','.'}, + {'.','.','.','4','1','9','.','.','5'}, + {'.','.','.','.','8','.','.','7','9'}, + } + SolveSudoku(board) + t.Logf("got:%+v", board) +} diff --git a/Week_07/surrounded-regions/solve.go b/Week_07/surrounded-regions/solve.go new file mode 100644 index 0000000..94160c5 --- /dev/null +++ b/Week_07/surrounded-regions/solve.go @@ -0,0 +1,42 @@ +package surrounded_regions + +func Solve(board [][]byte) { + m := len(board) + if m <= 0 { + return + } + n := len(board[0]) + // 处理边缘列上的'O' + for i := 0; i < m; i++ { + dfs(board, i, 0) + dfs(board, i, n - 1) + } + // 处理边缘行上的'O' + for j := 0; j < n; j++ { + dfs(board, 0, j) + dfs(board, m - 1, j) + } + for i := 0; i < m; i++ { + for j := 0; j < n; j++ { + // 将改过的'A'变为'O' + if board[i][j] == 'A' { + board[i][j] = 'O' + }else if board[i][j] == 'O' { + // 没变过的'O',被'X'包围 + board[i][j] = 'X' + } + } + } +} + +// 将于边缘上的'O'相连的字符变为'A' +func dfs(board [][]byte, row, col int) { + if row < 0 || col < 0 || row >= len(board) || col >= len(board[0]) || board[row][col] != 'O' { + return + } + board[row][col] = 'A' + dfs(board, row + 1, col) + dfs(board, row - 1, col) + dfs(board, row, col + 1) + dfs(board, row, col - 1) +} diff --git a/Week_07/surrounded-regions/solve_test.go b/Week_07/surrounded-regions/solve_test.go new file mode 100644 index 0000000..6e7bf53 --- /dev/null +++ b/Week_07/surrounded-regions/solve_test.go @@ -0,0 +1,25 @@ +package surrounded_regions + +import ( + "reflect" + "testing" +) + +func TestSolve(t *testing.T) { + board := [][]byte{ + {'X', 'X', 'X', 'X'}, + {'X', 'O', 'O', 'X'}, + {'X', 'X', 'O', 'X'}, + {'X', 'O', 'X', 'X'}, + } + want := [][]byte{ + {'X', 'X', 'X', 'X'}, + {'X', 'X', 'X', 'X'}, + {'X', 'X', 'X', 'X'}, + {'X', 'O', 'X', 'X'}, + } + Solve(board) + if !reflect.DeepEqual(board, want) { + t.Errorf("want:%+v got:%+v", want, board) + } +} diff --git a/Week_07/valid-sudoku/isValidSudoku.go b/Week_07/valid-sudoku/isValidSudoku.go new file mode 100644 index 0000000..78f0f4a --- /dev/null +++ b/Week_07/valid-sudoku/isValidSudoku.go @@ -0,0 +1,33 @@ +package valid_sudoku + +func IsValidSudoku(board [][]byte) bool { + if len(board) != 9 || len(board[0]) != 9 { + return false + } + // 行、列、3*3格子 + rows, cols, boxs := make([]map[int]int, 9), make([]map[int]int, 9), make([]map[int]int, 9) + for i := 0; i < 9; i++ { + rows[i] = make(map[int]int) + cols[i] = make(map[int]int) + boxs[i] = make(map[int]int) + } + for i := 0; i < len(board); i++ { + for j := 0; j < len(board[0]); j++ { + if board[i][j] == '.' { + continue + } + // 格子里的数值 + num := int(board[i][j] - '0') + // 3 * 3 单元格的位置 + bx := (i / 3 ) * 3 + j / 3 + // 更新行、列、3*3格子对应数值的个数 + rows[i][num]++ + cols[j][num]++ + boxs[bx][num]++ + if rows[i][num] > 1 || cols[j][num] > 1 || boxs[bx][num] > 1 { + return false + } + } + } + return true +} diff --git a/Week_07/valid-sudoku/isValidSudoku_test.go b/Week_07/valid-sudoku/isValidSudoku_test.go new file mode 100644 index 0000000..6748db4 --- /dev/null +++ b/Week_07/valid-sudoku/isValidSudoku_test.go @@ -0,0 +1,23 @@ +package valid_sudoku + +import "testing" + +func TestIsValidSudoku(t *testing.T) { + board := [][]byte{ + {'5','3','.','.','7','.','.','.','.'}, + {'6','.','.','1','9','5','.','.','.'}, + {'.','9','8','.','.','.','.','6','.'}, + {'8','.','.','.','6','.','.','.','3'}, + {'4','.','.','8','.','3','.','.','1'}, + {'7','.','.','.','2','.','.','.','6'}, + {'.','6','.','.','.','.','2','8','.'}, + {'.','.','.','4','1','9','.','.','5'}, + {'.','.','.','.','8','.','.','7','9'}, + } + want := true + got := IsValidSudoku(board) + if got != want { + t.Errorf("want:%t got:%t", want, got) + } +} + diff --git a/Week_07/word-ladder/ladderLength.go b/Week_07/word-ladder/ladderLength.go new file mode 100644 index 0000000..3a4442f --- /dev/null +++ b/Week_07/word-ladder/ladderLength.go @@ -0,0 +1,53 @@ +package word_ladder + +func LadderLength(beginWord string, endWord string, wordList []string) int { + ans := 0 + wordMap := make(map[string]bool) + for _, word := range wordList { + wordMap[word] = true + } + // 字符串中不包括最后的单词,则肯定达不到目标,提前结束 + if !wordMap[endWord] { + return ans + } + beginMap, endMap, visitedMap := make(map[string]bool), make(map[string]bool), make(map[string]bool) + beginMap[beginWord] = true + endMap[endWord] = true + for len(beginMap) > 0 && len(endMap) > 0 { + ans++ + // 元素少的一端开始处理 + if len(beginMap) > len(endMap) { + beginMap, endMap = endMap, beginMap + } + // 记录下一层bfs元素 + tmp := make(map[string]bool) + for word, _ := range beginMap { + bw := []byte(word) + // 依次变单词的每一位 + for i := 0; i < len(bw); i++ { + old := bw[i] + // 字符替换,从 a ~ z + for j := 0; j < 26; j++ { + bw[i] = byte('a' + j) + sw := string(bw) + // 两端碰头,则满足条件 + if endMap[sw] { + return ans + 1 + } + // 如果这个单词还没处理,并且变到的当前单词是字典里的词 + if !visitedMap[sw] && wordMap[sw] { + tmp[sw] = true + // 标记已访问 + visitedMap[sw] = true + } + } + // 恢复被替换的字符 + bw[i] = old + } + // 下一层bfs处理元素 + beginMap = tmp + } + } + // 不能变换 + return 0 +} diff --git a/Week_07/word-ladder/ladderLength_test.go b/Week_07/word-ladder/ladderLength_test.go new file mode 100644 index 0000000..343416e --- /dev/null +++ b/Week_07/word-ladder/ladderLength_test.go @@ -0,0 +1,13 @@ +package word_ladder + +import "testing" + +func TestLadderLength(t *testing.T) { + beginWord, endWord := "hit", "cog" + wordList := []string{"hot","dot","dog","lot","log","cog"} + want := 5 + got := LadderLength(beginWord, endWord, wordList) + if got != want { + t.Errorf("want:%d got:%d", want, got) + } +} diff --git a/Week_07/word-search-ii/findWords.go b/Week_07/word-search-ii/findWords.go new file mode 100644 index 0000000..2bb9642 --- /dev/null +++ b/Week_07/word-search-ii/findWords.go @@ -0,0 +1,83 @@ +package word_search_ii + +func FindWords(board [][]byte, words []string) []string { + ans := []string{} + if len(board) <= 0 || len(board[0]) <= 0 || len(words) <= 0 { + return ans + } + // 向四个方向扩展 + directs := [][]int{{0,-1},{1,0},{0,1},{-1,0}} + // 构建trie树 + trie := Constructor() + for _, word := range words { + trie.Insert(word) + } + for i := 0; i < len(board); i++ { + for j := 0; j < len(board[0]); j++ { + dfs(trie.root,i, j, directs, board, &ans) + } + } + return ans +} + +func dfs(node *TrieNode, row, col int, directs [][]int, board [][]byte, ans *[]string) { + // 越界 + if row < 0 || row >= len(board) || col < 0 || col >= len(board[0]) { + return + } + c := board[row][col] + // 已经遍历过或者trie树中不存在此字符 + if c == '#' || node.Children[c-'a'] == nil { + return + } + node = node.Children[c-'a'] + // 已经匹配到一个单词的结尾 + if node.Word != "" { + *ans = append(*ans, node.Word) + // 防重 + node.Word = "" + } + // 标记为已遍历 + board[row][col] = '#' + // 向四个方向扩展下一个字符 + for _, dr := range directs { + dfs(node,row + dr[0], col + dr[1], directs, board, ans) + } + // 标记清除 + board[row][col] = c +} + +type TrieNode struct { + // 孩子 + Children []*TrieNode + // 单词 + Word string +} + +type Trie struct { + root *TrieNode +} + +/** Initialize your data structure here. */ +func Constructor() Trie { + return Trie{ + root: &TrieNode{ + Children: make([]*TrieNode, 26), + }, + } +} + + +/** Inserts a word into the trie. */ +func (this *Trie) Insert(word string) { + p := this.root + for _, ch := range word { + // 没有孩子则创建 + if p.Children[ch-'a'] == nil { + p.Children[ch-'a'] = &TrieNode{Children: make([]*TrieNode, 26)} + } + p = p.Children[ch-'a'] + } + // 插入单词后标记单词 + p.Word = word +} diff --git a/Week_07/word-search-ii/findWords_test.go b/Week_07/word-search-ii/findWords_test.go new file mode 100644 index 0000000..e6cf929 --- /dev/null +++ b/Week_07/word-search-ii/findWords_test.go @@ -0,0 +1,36 @@ +package word_search_ii + +import "testing" + +func TestFindWords(t *testing.T) { + board := [][]byte{ + {'o', 'a', 'a', 'n'}, + {'e', 't', 'a', 'e'}, + {'i', 'h', 'k', 'r'}, + {'i', 'f', 'l', 'v'}, + } + words := []string{"oath", "pea", "eat", "rain"} + want := []string{"oath", "eat"} + got := FindWords(board, words) + for idx := range want { + if got[idx] != want[idx] { + t.Errorf("want:%s got:%s", want[idx], got[idx]) + } + } + + board = [][]byte{ + {'o', 'a', 'a', 'n'}, + {'e', 't', 'a', 'e'}, + {'i', 'h', 'k', 'r'}, + {'i', 'f', 'l', 'v'}, + } + words = []string{"oath", "pea", "eat", "rain", "oathi", "oathk", "oathf", "oate", "oathii", "oathfi", "oathfii"} + want = []string{"oath", "oathk", "oathf", "oathfi", "oathfii", "oathi", "oathii", "oate", "eat"} + got = FindWords(board, words) + /*for idx := range want { + if got[idx] != want[idx] { + t.Errorf("want:%s got:%s", want[idx], got[idx]) + } + }*/ + t.Logf("want:%+v got:%+v", want, got) +} diff --git a/Week_08/README.md b/Week_08/README.md index 50de304..5ee2592 100644 --- a/Week_08/README.md +++ b/Week_08/README.md @@ -1 +1,181 @@ -学习笔记 \ No newline at end of file +学习笔记 + +1、通过本周学习了解了位运算,能够用位运算解决N皇后问题。 + +2、熟悉了布隆过滤器及LRU的实现。 + +3、熟悉了几种常用排序算法的实现。知道了常用几种排序算法的时间复杂度和空间复杂度。 + +(1)选择排序:基本思想为每一趟从待排序的数据元素中选择最小(或最大)的一个元素作为首元素,直到所有元素排完为止。时间复杂度为O(n^2) + +``` +func SelectSort(nums []int) []int { + for i := 0; i < len(nums)-1; i++ { + min := i + for j := i + 1; j < len(nums); j++ { + if nums[j] < nums[min] { + min = j + } + } + if min != i { + nums[min], nums[i] = nums[i], nums[min] + } + } + return nums +} +``` + +(2)插入排序: 基本思想是每一步将一个待排序的记录,插入到前面已经排好序的有序序列中去,直到插完所有元素为止。时间复杂度为O(n^2) + +``` +func InsertSort(nums []int) []int { + for i := 1; i < len(nums); i++ { + for j := i; j > 0 && nums[j] < nums[j-1]; j-- { + nums[j], nums[j-1] = nums[j-1], nums[j] + } + } + return nums +} +``` + +(3)冒泡排序: 基本思想是对相邻的元素进行两两比较,顺序相反则进行交换,这样,每一趟会将最小或最大的元素“浮”到顶端,最终达到完全有序。时间复杂度为O(n^2) +``` +func BubbleSort(nums []int) []int { + for i := 0; i < len(nums)-1; i++ { + flag := true + for j := 1; j < len(nums)-i; j++ { + if nums[j] < nums[j-1] { + nums[j], nums[j-1] = nums[j-1], nums[j] + flag = false + } + } + if flag { + break + } + } + return nums +} +``` + +(4)快速排序:基本思想选择一个基准,通过一趟排序将要排序的数据分割成两部分,其中一部分的所有数据都比基准小,另外一部分的所有数据多比基准大,然后再按此方法对这两部分数据递归进行,最后变成有序序列。时间复杂度为O(nlogn) +``` +func QuickSort(nums []int) []int { + left, right := 0, len(nums)-1 + quickSort(left, right, nums) + return nums +} +func quickSort(left, right int, nums []int) { + if left < right { + pivot := partition(left, right, nums) + quickSort(left, pivot-1, nums) + quickSort(pivot+1, right, nums) + } +} +func partition(left, right int, nums []int) int { + pivot := nums[left] + for left < right { + for left < right && pivot <= nums[right] { + right-- + } + nums[left] = nums[right] + for left < right && pivot >= nums[left] { + left++ + } + nums[right] = nums[left] + } + nums[left] = pivot + return left +} +``` + +(5)归并排序:基本思想是分治,分将数组拆分多个子序列,治将子序列合并成较大有序子序列。时间复杂度为O(nlogn) +``` +func MergeSort(nums []int) []int { + if len(nums) <= 1 { + return nums + } + left, right := 0, len(nums)-1 + mergeSort(left, right, nums) + return nums +} + +func mergeSort(left, right int, nums []int) { + if left < right { + mid := (left + right) >> 1 + mergeSort(left, mid, nums) + mergeSort(mid+1, right, nums) + merge(left, mid, right, nums) + } +} + +func merge(left, mid, right int, nums []int) { + tmp := make([]int, right-left+1) + i, j, k := left, mid+1, 0 + for ; i <= mid && j <= right; k++ { + if nums[i] <= nums[j] { + tmp[k] = nums[i] + i++ + } else { + tmp[k] = nums[j] + j++ + } + } + for ; i <= mid; i, k = i+1, k+1 { + tmp[k] = nums[i] + } + for ; j <= right; j, k = j+1, k+1 { + tmp[k] = nums[j] + } + for i, num := range tmp { + nums[left+i] = num + } +} +``` +(6)堆排序: 基本思想是利用堆设计的一种排序算法,时间复杂度均为O(nlogn)。 +``` +func HeapSort(nums []int) []int { + length := len(nums) + if length <= 1 { + return nums + } + for i := len(nums)>>1 - 1; i >= 0; i-- { + heapify(nums, length, i) + } + for i := length - 1; i >= 0; i-- { + nums[0], nums[i] = nums[i], nums[0] + heapify(nums, i, 0) + } + return nums +} + +func heapify(nums []int, length, i int) { + left, right, largest := 2*i+1, 2*i+2, i + if left < length && nums[left] > nums[largest] { + largest = left + } + if right < length && nums[right] > nums[largest] { + largest = right + } + if largest != i { + nums[i], nums[largest] = nums[largest], nums[i] + heapify(nums, length, largest) + } +} +``` +(7) 希尔排序:基本思想是把数组按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个数组恰被分成一组,算法便终止。时间复杂度O(n^1.3) +``` +func ShellSort(nums []int) []int { + // 数组长度 + n := len(nums) + // 每次减半,直到步长为 1 + for step := n / 2; step > 0; step /= 2 { + // 开始插入排序,每一轮的步长为 step + for i := step; i < n; i++ { + for j := i; j-step >= 0 && nums[j] < nums[j-step]; j = j - step { + nums[j], nums[j-step] = nums[j-step], nums[j] + } + } + } + return nums +} +``` diff --git a/Week_08/design-a-leaderboard/leaderBoard.go b/Week_08/design-a-leaderboard/leaderBoard.go new file mode 100644 index 0000000..875676d --- /dev/null +++ b/Week_08/design-a-leaderboard/leaderBoard.go @@ -0,0 +1,49 @@ +package design_a_leaderboard + +import "sort" + +type Leaderboard struct { + // 记录参赛者分数 + count []int +} + + +func Constructor() Leaderboard { + return Leaderboard { + count: make([]int, 10000), + } +} + +// 添加分数 +func (this *Leaderboard) AddScore(playerId int, score int) { + this.count[playerId] += score +} + +// 求最大K个数的和 +func (this *Leaderboard) Top(K int) int { + tmp := make([]int, len(this.count)) + copy(tmp, this.count) + // 按分数从大到小排 + sort.Slice(tmp, func(i, j int) bool { + return tmp[i] > tmp[j] + }) + sum := 0 + for i := 0; i < K; i++ { + sum += tmp[i] + } + return sum +} + +// 重置指定参赛者分数 +func (this *Leaderboard) Reset(playerId int) { + this.count[playerId] = 0 +} + + +/** + * Your Leaderboard object will be instantiated and called as such: + * obj := Constructor(); + * obj.AddScore(playerId,score); + * param_2 := obj.Top(K); + * obj.Reset(playerId); + */ diff --git a/Week_08/design-a-leaderboard/leaderBoard_test.go b/Week_08/design-a-leaderboard/leaderBoard_test.go new file mode 100644 index 0000000..4464755 --- /dev/null +++ b/Week_08/design-a-leaderboard/leaderBoard_test.go @@ -0,0 +1,27 @@ +package design_a_leaderboard + +import "testing" + +func TestConstructor(t *testing.T) { + leaderboard := Constructor() + leaderboard.AddScore(1,73) // leaderboard = [[1,73]]; + leaderboard.AddScore(2,56) // leaderboard = [[1,73],[2,56]]; + leaderboard.AddScore(3,39) // leaderboard = [[1,73],[2,56],[3,39]]; + leaderboard.AddScore(4,51) // leaderboard = [[1,73],[2,56],[3,39],[4,51]]; + leaderboard.AddScore(5,4) // leaderboard = [[1,73],[2,56],[3,39],[4,51],[5,4]]; + k := 1 + want := 73 + got := leaderboard.Top(k) // returns 73; + if got != want { + t.Errorf("want:%d got:%d", want, got) + } + leaderboard.Reset(1) // leaderboard = [[2,56],[3,39],[4,51],[5,4]]; + leaderboard.Reset(2) // leaderboard = [[3,39],[4,51],[5,4]]; + leaderboard.AddScore(2,51) // leaderboard = [[2,51],[3,39],[4,51],[5,4]]; + k = 3 + want = 141 + got = leaderboard.Top(3) // returns 141 = 51 + 51 + 39; + if got != want { + t.Errorf("want:%d got:%d", want, got) + } +} diff --git a/Week_08/lru-cache/lruCache.go b/Week_08/lru-cache/lruCache.go new file mode 100644 index 0000000..bb9fb9f --- /dev/null +++ b/Week_08/lru-cache/lruCache.go @@ -0,0 +1,96 @@ +package lru_cache + +// 双链表节点定义 +type DLinkedNode struct { + key, value int + prev, next *DLinkedNode +} + +type LRUCache struct { + size int + capacity int + cache map[int]*DLinkedNode + head, tail *DLinkedNode +} + +func Constructor(capacity int) LRUCache { + c := LRUCache{ + size: 0, + capacity: capacity, + cache: make(map[int]*DLinkedNode), + head: &DLinkedNode{key: 0,value: 0}, + tail: &DLinkedNode{key: 0, value: 0}, + } + c.head.next = c.tail + c.tail.prev = c.head + return c +} + +// 获取元素 +func (this *LRUCache) Get(key int) int { + // 不存在,返回-1 + if node, ok := this.cache[key]; !ok { + return -1 + }else{ + // 存在,移到头部 + this.moveToHead(node) + return node.value + } +} + +// 插入元素 +func (this *LRUCache) Put(key int, value int) { + // 已经存在 + if node, ok := this.cache[key]; ok { + node.value = value + // 将节点移到首部 + this.moveToHead(node) + }else{ + // 已经满了 + if this.size == this.capacity { + //把尾部节点删了 + remove := this.removeTail() + delete(this.cache, remove.key) + this.size-- + } + node = &DLinkedNode{key: key, value: value} + this.addToHead(node) + this.size++ + this.cache[key] = node + } +} + +// 将节点插入双链表头部 +func (this *LRUCache) addToHead(node *DLinkedNode) { + node.prev = this.head + node.next = this.head.next + this.head.next.prev = node + this.head.next = node +} + +// 删除节点 +func (this *LRUCache) removeNode(node *DLinkedNode) { + node.prev.next = node.next + node.next.prev = node.prev +} + +// 将节点移到链表头部 +func (this *LRUCache) moveToHead(node *DLinkedNode) { + this.removeNode(node) + this.addToHead(node) +} + +// 删除尾部节点 +func (this *LRUCache) removeTail() *DLinkedNode { + node := this.tail.prev + this.removeNode(node) + return node +} + + +/** + * Your LRUCache object will be instantiated and called as such: + * obj := Constructor(capacity); + * param_1 := obj.Get(key); + * obj.Put(key,value); + */ \ No newline at end of file diff --git a/Week_08/lru-cache/lruCache_test.go b/Week_08/lru-cache/lruCache_test.go new file mode 100644 index 0000000..e4b1b88 --- /dev/null +++ b/Week_08/lru-cache/lruCache_test.go @@ -0,0 +1,36 @@ +package lru_cache + +import "testing" + +func TestConstructor(t *testing.T) { + lRUCache := Constructor(2) + lRUCache.Put(1, 1) // 缓存是 {1=1} + lRUCache.Put(2, 2) // 缓存是 {2=2, 1=1} + want := 1 + got := lRUCache.Get(1) // 返回 1 + if got != want { + t.Errorf("want:%d got:%d", want, got) + } + lRUCache.Put(3, 3) // 该操作会使得关键字 2 作废,缓存是 {3=3, 1=1} + want = -1 + got = lRUCache.Get(2) // 返回 -1 (未找到) + if got != want { + t.Errorf("want:%d got:%d", want, got) + } + lRUCache.Put(4, 4) // 该操作会使得关键字 1 作废,缓存是 {4=4, 3=3} + want = -1 + got = lRUCache.Get(1) // 返回 -1 (未找到) + if got != want { + t.Errorf("want:%d got:%d", want, got) + } + want = 3 + got = lRUCache.Get(3) // 返回 3 + if got != want { + t.Errorf("want:%d got:%d", want, got) + } + want = 4 + got = lRUCache.Get(4) // 返回 4 + if got != want { + t.Errorf("want:%d got:%d", want, got) + } +} diff --git a/Week_08/merge-intervals/merge.go b/Week_08/merge-intervals/merge.go new file mode 100644 index 0000000..f73a4f5 --- /dev/null +++ b/Week_08/merge-intervals/merge.go @@ -0,0 +1,29 @@ +package merge_intervals + +import "sort" + +func max(a, b int) int { + if a > b { + return a + } + return b +} + +func Merge(intervals [][]int) [][]int { + ans := [][]int{} + if len(intervals) <= 0 { + return ans + } + sort.Slice(intervals, func(i, j int) bool { + return intervals[i][0] < intervals[j][0] + }) + ans = append(ans, intervals[0]) + for i := 1; i < len(intervals); i++ { + if ans[len(ans)-1][1] >= intervals[i][0] { + ans[len(ans)-1][1] = max(ans[len(ans)-1][1], intervals[i][1]) + }else{ + ans = append(ans, intervals[i]) + } + } + return ans +} diff --git a/Week_08/merge-intervals/merge_test.go b/Week_08/merge-intervals/merge_test.go new file mode 100644 index 0000000..1754fd6 --- /dev/null +++ b/Week_08/merge-intervals/merge_test.go @@ -0,0 +1,21 @@ +package merge_intervals + +import ( + "reflect" + "testing" +) + +func TestMerge(t *testing.T) { + intervals := [][]int{{1,3},{2,6},{8,10},{15,18}} + want := [][]int{{1,6},{8,10},{15,18}} + got := Merge(intervals) + if !reflect.DeepEqual(got, want) { + t.Errorf("want:%+v got:%+v", want, got) + } + intervals = [][]int{{1,4},{4,5}} + want = [][]int{{1,5}} + got = Merge(intervals) + if !reflect.DeepEqual(got, want) { + t.Errorf("want:%+v got:%+v", want, got) + } +} diff --git a/Week_08/n-queens-ii/totalNQueens.go b/Week_08/n-queens-ii/totalNQueens.go new file mode 100644 index 0000000..5e62546 --- /dev/null +++ b/Week_08/n-queens-ii/totalNQueens.go @@ -0,0 +1,25 @@ +package n_queens_ii + +func TotalNQueens(n int) int { + ans := 0 + size := (1 << n) - 1 + dfs(0, 0, 0, size, &ans) + return ans +} + +func dfs(row, ld, rd, size int, ans *int){ + if row == size { + *ans++ + return + } + //取得现在可以填充的位置,1表示可以填充的,0表示已经填充过了 + pos := size &^ (row | ld | rd) + for pos != 0 { + //取得pos的最后一位1,也就是这轮需要填充的位置 + p := pos & -pos + //将pos的最后一位1置为0,表明我们将这个位置填充了 + pos &= pos - 1 + //下探到下一层 + dfs(row | p, (ld | p) << 1, (rd | p) >> 1, size, ans) + } +} diff --git a/Week_08/n-queens-ii/totalNQueens_test.go b/Week_08/n-queens-ii/totalNQueens_test.go new file mode 100644 index 0000000..74218c8 --- /dev/null +++ b/Week_08/n-queens-ii/totalNQueens_test.go @@ -0,0 +1,12 @@ +package n_queens_ii + +import "testing" + +func TestTotalNQueens(t *testing.T) { + n := 4 + want := 2 + got := TotalNQueens(n) + if got != want { + t.Errorf("want:%d got:%d", want, got) + } +} diff --git a/Week_08/n-queens/solveNQueens.go b/Week_08/n-queens/solveNQueens.go new file mode 100644 index 0000000..ad48519 --- /dev/null +++ b/Week_08/n-queens/solveNQueens.go @@ -0,0 +1,49 @@ +package n_queens + +import "math/bits" + +func SolveNQueens(n int) [][]string { + ans := [][]string{} + size := (1 << n) - 1 + queens := make([]int, n) + for i := 0; i < n; i++ { + queens[i] = -1 + } + dfs(queens,0, 0, 0, 0, n, size, &ans) + return ans +} + +func dfs(queens []int, row, cols, ld, rd, n, size int, ans *[][]string){ + if row == n { + board := generateBoard(queens, n) + *ans = append(*ans, board) + return + } + //取得现在可以填充的位置,1表示可以填充的,0表示已经填充过了 + pos := size &(^(cols | ld | rd)) + for pos != 0 { + //取得pos的最后一位1,也就是这轮需要填充的位置 + p := pos & -pos + //将pos的最后一位1置为0,表明我们将这个位置填充了 + pos &= pos - 1 + //二进制中一的位数 + column := bits.OnesCount(uint(p - 1)) + queens[row] = column + //下探到下一层 + dfs(queens, row+1, cols | p, (ld | p) << 1, (rd | p) >> 1, n, size, ans) + queens[row] = -1 + } +} + +func generateBoard(queens []int, n int) []string { + board := []string{} + for i := 0; i < n; i++ { + row := make([]byte, n) + for j := 0; j < n; j++ { + row[j] = '.' + } + row[queens[i]] = 'Q' + board = append(board, string(row)) + } + return board +} diff --git a/Week_08/n-queens/solveNQueens_test.go b/Week_08/n-queens/solveNQueens_test.go new file mode 100644 index 0000000..8c89055 --- /dev/null +++ b/Week_08/n-queens/solveNQueens_test.go @@ -0,0 +1,9 @@ +package n_queens + +import "testing" + +func TestSolveNQueens(t *testing.T) { + n := 4 + got := SolveNQueens(n) + t.Logf("got:%+v", got) +} diff --git a/Week_08/number-of-1-bits/hammingWeight.go b/Week_08/number-of-1-bits/hammingWeight.go new file mode 100644 index 0000000..a07762f --- /dev/null +++ b/Week_08/number-of-1-bits/hammingWeight.go @@ -0,0 +1,11 @@ +package number_of_1_bits + +func HammingWeight(num uint32) int { + ans := 0 + // 利用位运算性质,num & (num -1) 能把最后一位1消掉 + for num != 0 { + ans++ + num = num & (num - 1) + } + return ans +} diff --git a/Week_08/number-of-1-bits/hammingWeight_test.go b/Week_08/number-of-1-bits/hammingWeight_test.go new file mode 100644 index 0000000..dcc535b --- /dev/null +++ b/Week_08/number-of-1-bits/hammingWeight_test.go @@ -0,0 +1,12 @@ +package number_of_1_bits + +import "testing" + +func TestHammingWeight(t *testing.T) { + num := 0b00000000000000000000000000001011 + want := 3 + got := HammingWeight(uint32(num)) + if got != want { + t.Errorf("want:%d got:%d", want, got) + } +} diff --git a/Week_08/power-of-two/isPowerOfTwo.go b/Week_08/power-of-two/isPowerOfTwo.go new file mode 100644 index 0000000..231def6 --- /dev/null +++ b/Week_08/power-of-two/isPowerOfTwo.go @@ -0,0 +1,6 @@ +package power_of_two + +func IsPowerOfTwo(n int) bool { + // 二进制中只有一个1,则说明是2的幂 + return n > 0 && n & (n-1) == 0 +} diff --git a/Week_08/power-of-two/isPowerOfTwo_test.go b/Week_08/power-of-two/isPowerOfTwo_test.go new file mode 100644 index 0000000..5faf9d6 --- /dev/null +++ b/Week_08/power-of-two/isPowerOfTwo_test.go @@ -0,0 +1,12 @@ +package power_of_two + +import "testing" + +func TestIsPowerOfTwo(t *testing.T) { + n := 16 + want := true + got := IsPowerOfTwo(n) + if got != want { + t.Errorf("want:%t got:%t", want, got) + } +} diff --git a/Week_08/relative-sort-array/relativeSortArray.go b/Week_08/relative-sort-array/relativeSortArray.go new file mode 100644 index 0000000..a3ceba6 --- /dev/null +++ b/Week_08/relative-sort-array/relativeSortArray.go @@ -0,0 +1,54 @@ +package relative_sort_array + +import "sort" + +func RelativeSortArray(arr1 []int, arr2 []int) []int { + ans := []int{} + // 获取最大值 + upper := 0 + for _, num := range arr1 { + if num > upper { + upper = num + } + } + // 计数排序 + freq := make([]int, upper + 1) + for _, num := range arr1 { + freq[num]++ + } + // arr2中的元素按照arr2的顺序放在头部 + for _, num := range arr2 { + for ;freq[num] > 0; freq[num]-- { + ans = append(ans, num) + } + } + // 其他元素按照大小放入结果 + for num, count := range freq { + for ; count > 0; count-- { + ans = append(ans, num) + } + } + return ans +} + +// 自定义排序 +func RelativeSortArray2(arr1 []int, arr2 []int) []int { + // 记录arr2中数值的序号 + rankMap := make(map[int]int) + for i, num := range arr2 { + rankMap[num] = i + } + // 自定义排序,如果arr2中存在,按照arr2中顺序排序,如果不存在,则按数值排序 + sort.Slice(arr1, func(i, j int) bool { + rankI, hasI := rankMap[arr1[i]] + rankJ, hasJ := rankMap[arr1[j]] + if hasI && hasJ { + return rankI < rankJ + }else if hasI || hasJ { + return hasI + }else { + return arr1[i] < arr1[j] + } + }) + return arr1 +} diff --git a/Week_08/relative-sort-array/relativeSortArray_test.go b/Week_08/relative-sort-array/relativeSortArray_test.go new file mode 100644 index 0000000..20b47ab --- /dev/null +++ b/Week_08/relative-sort-array/relativeSortArray_test.go @@ -0,0 +1,26 @@ +package relative_sort_array + +import ( + "reflect" + "testing" +) + +func TestRelativeSortArray(t *testing.T){ + arr1 := []int{2,3,1,3,2,4,6,7,9,2,19} + arr2 := []int{2,1,4,3,9,6} + want := []int{2,2,2,1,4,3,3,9,6,7,19} + got := RelativeSortArray(arr1, arr2) + if !reflect.DeepEqual(want, got){ + t.Errorf("want:%+v got:%+v", want, got) + } +} + +func TestRelativeSortArray2(t *testing.T){ + arr1 := []int{2,3,1,3,2,4,6,7,9,2,19} + arr2 := []int{2,1,4,3,9,6} + want := []int{2,2,2,1,4,3,3,9,6,7,19} + got := RelativeSortArray2(arr1, arr2) + if !reflect.DeepEqual(want, got){ + t.Errorf("want:%+v got:%+v", want, got) + } +} diff --git a/Week_08/reverse-bits/reverseBits.go b/Week_08/reverse-bits/reverseBits.go new file mode 100644 index 0000000..690fddf --- /dev/null +++ b/Week_08/reverse-bits/reverseBits.go @@ -0,0 +1,11 @@ +package reverse_bits + +func ReverseBits(num uint32) uint32 { + ans := uint32(0) + // 依次获取最低位然后左移 + for i:=0; i < 32; i++ { + ans = ans << 1 + num & 1 + num = num >> 1 + } + return ans +} diff --git a/Week_08/reverse-bits/reverseBits_test.go b/Week_08/reverse-bits/reverseBits_test.go new file mode 100644 index 0000000..3b16c41 --- /dev/null +++ b/Week_08/reverse-bits/reverseBits_test.go @@ -0,0 +1,12 @@ +package reverse_bits + +import "testing" + +func TestReverseBits(t *testing.T) { + num := 0b00000010100101000001111010011100 + want := 0b00111001011110000010100101000000 + got := ReverseBits(uint32(num)) + if got != uint32(want) { + t.Errorf("want:%d got:%d", want, got) + } +} diff --git a/Week_08/reverse-pairs/reversePairs.go b/Week_08/reverse-pairs/reversePairs.go new file mode 100644 index 0000000..04485ef --- /dev/null +++ b/Week_08/reverse-pairs/reversePairs.go @@ -0,0 +1,46 @@ +package reverse_pairs + +func ReversePairs(nums []int) int { + if len(nums) <= 0 { + return 0 + } + return mergeSort(nums, 0, len(nums)-1) +} + +func mergeSort(nums []int, left, right int) int { + if left >= right { + return 0 + } + // 分界点 + mid := (left + right) >> 1 + // 分为两部分求和 + count := mergeSort(nums, left, mid) + mergeSort(nums, mid+1, right) + tmp := make([]int, right - left + 1) + // 合并并计算重要翻转对 + i, t, c := left, left, 0 + for j := mid + 1; j <= right; j, c = j + 1, c + 1 { + // 不是重要翻转对,i++ + for i <= mid && nums[i] <= 2 * nums[j] { + i++ + } + // 归并 + for t <= mid && nums[t] <= nums[j] { + tmp[c] = nums[t] + c++ + t++ + } + tmp[c] = nums[j] + // 计算重要翻转对 + count += mid - i + 1 + } + // 将没归并完的进一步归并 + for t <= mid { + tmp[c] = nums[t] + c++ + t++ + } + for i, num := range tmp { + nums[left+i] = num + } + return count +} diff --git a/Week_08/reverse-pairs/reversePairs_test.go b/Week_08/reverse-pairs/reversePairs_test.go new file mode 100644 index 0000000..35f1647 --- /dev/null +++ b/Week_08/reverse-pairs/reversePairs_test.go @@ -0,0 +1,18 @@ +package reverse_pairs + +import "testing" + +func TestReversePairs(t *testing.T) { + nums := []int{1,3,2,3,1} + want := 2 + got := ReversePairs(nums) + if got != want { + t.Errorf("want:%d got:%d", want, got) + } + nums = []int{2,4,3,5,1} + want = 3 + got = ReversePairs(nums) + if got != want { + t.Errorf("want:%d got:%d", want, got) + } +} diff --git a/Week_08/valid-anagram/isAnagram.go b/Week_08/valid-anagram/isAnagram.go new file mode 100644 index 0000000..658ee34 --- /dev/null +++ b/Week_08/valid-anagram/isAnagram.go @@ -0,0 +1,22 @@ +package valid_anagram + +func IsAnagram(s string, t string) bool { + // 长度不等,肯定不是异位词 + if len(s) != len(t) { + return false + } + // 使用map统计各字符数量 + hash := make(map[byte]int) + // s中出现,则相应字符+1,t中出现,则相应字符-1 + for i := 0; i < len(s); i++ { + hash[s[i]]++ + hash[t[i]]-- + } + // 经过上述处理,如果所有字符个数都是0,则才是有效的字母异位词 + for _, v := range hash { + if v != 0 { + return false + } + } + return true +} diff --git a/Week_08/valid-anagram/isAnagram_test.go b/Week_08/valid-anagram/isAnagram_test.go new file mode 100644 index 0000000..28acd1c --- /dev/null +++ b/Week_08/valid-anagram/isAnagram_test.go @@ -0,0 +1,13 @@ +package valid_anagram + +import "testing" + +func TestIsAnagram(t *testing.T) { + s := "anagram" + s2 := "nagaram" + want := true + got := IsAnagram(s, s2) + if got != want { + t.Errorf("want:%t got:%t", want, got) + } +} \ No newline at end of file diff --git a/Week_09/README.md b/Week_09/README.md index 50de304..36f1c08 100644 --- a/Week_09/README.md +++ b/Week_09/README.md @@ -1 +1,18 @@ -学习笔记 \ No newline at end of file +学习笔记 + +1、通过本周学习,对动态规划有了进一步了的熟悉,特别是字符串与动态规划相结合的题目,一般动态方程都需要升维,变成二维。 + +2、对字符串算法有了一定了解,熟悉了字符串的替换、反转的方法,会处理常见的回文,子串等问题。 + +3、不同路径2状态转移方程: + +``` +(1)、状态定义: +dp[i][j] 表示走到格子(i,j) 的方法数。 +(2)、状态转移分析: +如果网格 (i, j)上有障碍物,则 dp[i][j]值为0,表示走到该格子的方法数为 0; +否则网格 (i, j)可以从网格 (i - 1, j) 或者 网格 (i, j - 1)走过来,因此走到该格子的方法数为走到网格 (i - 1, j)和网格 (i, j - 1)的方法数之和,即 dp[i, j] = dp[i - 1, j] + dp[i, j - 1]。 +(3)、状态转移方程: +dp[i][j] = 0; (i,j)位置有障碍物时 +dp[i[[j] = dp[i-1][j] + dp[i][j-1]; (i,j)位置没有障碍物时 +``` \ No newline at end of file diff --git a/Week_09/decode-ways/numDecodings.go b/Week_09/decode-ways/numDecodings.go new file mode 100644 index 0000000..fd72e99 --- /dev/null +++ b/Week_09/decode-ways/numDecodings.go @@ -0,0 +1,33 @@ +package decode_ways + +func NumDecodings(s string) int { + if len(s) <= 0 || s[0] == '0' { + return 0 + } + n := len(s) + //dp[n]代表以s[n-1]为结尾的解码数 + dp := make([]int, n + 1) + dp[0], dp[1] = 1, 1 + for i := 1; i < n; i++ { + // 当前为'0',则前一个值需要时'1'或'2' + if s[i] == '0' { + if s[i-1] == '1' || s[i-1] == '2' { + // 此种情况s[i]s[i-1]被唯一编码,不增加情况 + dp[i+1] = dp[i-1] + }else{ + // s[i-1]不是'1'或'2',则不能编码 + return 0 + } + }else{ + // 当前位和前一位要小于26,则当前为和前一位可以单独编码也可以连起来编码 + if s[i-1] == '1' || (s[i-1] == '2' && s[i] <= '6') { + // 分开编码为dp[i], 合并编码为dp[i-1] + dp[i+1] = dp[i] + dp[i-1] + }else{ + // 否则只能分开编码,则为dp[i] + dp[i+1] = dp[i] + } + } + } + return dp[n] +} diff --git a/Week_09/decode-ways/numDecodings_test.go b/Week_09/decode-ways/numDecodings_test.go new file mode 100644 index 0000000..d708d22 --- /dev/null +++ b/Week_09/decode-ways/numDecodings_test.go @@ -0,0 +1,18 @@ +package decode_ways + +import "testing" + +func TestNumDecodings(t *testing.T) { + s := "12" + want := 2 + got := NumDecodings(s) + if got != want { + t.Errorf("want:%d got:%d", want, got) + } + s = "226" + want = 3 + got = NumDecodings(s) + if got != want { + t.Errorf("want:%d got:%d", want, got) + } +} diff --git a/Week_09/distinct-subsequences/numDistinct.go b/Week_09/distinct-subsequences/numDistinct.go new file mode 100644 index 0000000..26b0aba --- /dev/null +++ b/Week_09/distinct-subsequences/numDistinct.go @@ -0,0 +1,35 @@ +package distinct_subsequences + +func NumDistinct(s string, t string) int { + m, n := len(s), len(t) + if m < n { + return 0 + } + dp := make([][]int, m + 1) + for i := 0; i <= m; i++ { + dp[i] = make([]int, n + 1) + } + // 两个都是空字符串 + dp[0][0] = 1 + // t为空串 + for i := 1; i <= m; i++ { + dp[i][0] = 1 + } + // s为空串 + for j := 1; j <= n; j++ { + dp[0][j] = 0 + } + for i := 1; i <= m; i++ { + for j := 1; j <= n; j++ { + if s[i-1] == t[j-1] { + // dp[i-1][j-1]: 用s[i]来匹配 + // dp[i-1][j]: 不用si[i]来匹配 + dp[i][j] = dp[i-1][j-1] + dp[i-1][j] + }else{ + // 不用si[i]来匹配 + dp[i][j] = dp[i-1][j] + } + } + } + return dp[m][n] +} diff --git a/Week_09/distinct-subsequences/numDistinct_test.go b/Week_09/distinct-subsequences/numDistinct_test.go new file mode 100644 index 0000000..771c46b --- /dev/null +++ b/Week_09/distinct-subsequences/numDistinct_test.go @@ -0,0 +1,13 @@ +package distinct_subsequences + +import "testing" + +func TestNumDistinct(t *testing.T) { + s := "rabbbit" + ts := "rabbit" + want := 3 + got := NumDistinct(s, ts) + if got != want { + t.Errorf("want:%d got:%d", want, got) + } +} diff --git a/Week_09/find-all-anagrams-in-a-string/findAnagrams.go b/Week_09/find-all-anagrams-in-a-string/findAnagrams.go new file mode 100644 index 0000000..50ffd35 --- /dev/null +++ b/Week_09/find-all-anagrams-in-a-string/findAnagrams.go @@ -0,0 +1,41 @@ +package find_all_anagrams_in_a_string + +func FindAnagrams(s string, p string) []int { + ans := []int{} + // win: 窗口字符串的统计, pattern:p字符串的统计 + win, pattern := make(map[byte]int), make(map[byte]int) + for i := range p { + pattern[p[i]]++ + } + left, right, valid := 0, 0, 0 + for right < len(s) { + ch := s[right] + // 字符是p中字符 + if _, ok := pattern[ch]; ok { + win[ch]++ + // 个数相同,则有效字符加1 + if win[ch] == pattern[ch] { + valid++ + } + } + // 窗口长度达到p的长度,需要收缩 + if right - left + 1 >= len(p) { + // 完全匹配,记录结果 + if valid == len(pattern) { + ans = append(ans, left) + } + // 将左边字符移除窗口 + ch := s[left] + left++ + // 移除的字符是p中的一个字符,需要进一步更新匹配记录 + if _, ok := pattern[ch]; ok { + if win[ch] == pattern[ch] { + valid-- + } + win[ch]-- + } + } + right++ + } + return ans +} diff --git a/Week_09/find-all-anagrams-in-a-string/findAnagrams_test.go b/Week_09/find-all-anagrams-in-a-string/findAnagrams_test.go new file mode 100644 index 0000000..47c52e2 --- /dev/null +++ b/Week_09/find-all-anagrams-in-a-string/findAnagrams_test.go @@ -0,0 +1,16 @@ +package find_all_anagrams_in_a_string + +import ( + "reflect" + "testing" +) + +func TestFindAnagrams(t *testing.T) { + s := "cbaebabacd" + p := "abc" + want := []int{0,6} + got := FindAnagrams(s, p) + if !reflect.DeepEqual(got, want) { + t.Errorf("want:%+v got:%+v", want, got) + } +} diff --git a/Week_09/first-unique-character-in-a-string/firstUniqChar.go b/Week_09/first-unique-character-in-a-string/firstUniqChar.go new file mode 100644 index 0000000..6d89cc6 --- /dev/null +++ b/Week_09/first-unique-character-in-a-string/firstUniqChar.go @@ -0,0 +1,16 @@ +package first_unique_character_in_a_string + +func FirstUniqChar(s string) int { + ms := make(map[byte]int) + // 统计各字符次数 + for i := range s { + ms[s[i]]++ + } + // 再次遍历字符串,找到第一个唯一字符返回其位置 + for i := range s { + if ms[s[i]] == 1 { + return i + } + } + return -1 +} diff --git a/Week_09/first-unique-character-in-a-string/firstUniqChar_test.go b/Week_09/first-unique-character-in-a-string/firstUniqChar_test.go new file mode 100644 index 0000000..a870bd1 --- /dev/null +++ b/Week_09/first-unique-character-in-a-string/firstUniqChar_test.go @@ -0,0 +1,18 @@ +package first_unique_character_in_a_string + +import "testing" + +func TestFirstUniqChar(t *testing.T) { + s := "leetcode" + want := 0 + got := FirstUniqChar(s) + if got != want { + t.Errorf("want:%d got:%d", want, got) + } + s = "loveleetcode" + want = 2 + got = FirstUniqChar(s) + if got != want { + t.Errorf("want:%d got:%d", want, got) + } +} diff --git a/Week_09/isomorphic-strings/isIsomorphic.go b/Week_09/isomorphic-strings/isIsomorphic.go new file mode 100644 index 0000000..946a240 --- /dev/null +++ b/Week_09/isomorphic-strings/isIsomorphic.go @@ -0,0 +1,26 @@ +package isomorphic_strings + +func IsIsomorphic(s string, t string) bool { + // 长度不同,肯定不是同构字符串 + if len(s) != len(t) { + return false + } + // 存放s对应的t + s2t := make(map[byte]byte) + // 存放t对应的s + t2s := make(map[byte]byte) + for i := 0; i < len(s); i++ { + // 记录两个map的对应关系 + if _, ok := s2t[s[i]]; !ok { + s2t[s[i]] = t[i] + } + if _, ok := t2s[t[i]]; !ok { + t2s[t[i]] = s[i] + } + // 不匹配则直接返回 + if s2t[s[i]] != t[i] || t2s[t[i]] != s[i] { + return false + } + } + return true +} diff --git a/Week_09/isomorphic-strings/isIsomorphic_test.go b/Week_09/isomorphic-strings/isIsomorphic_test.go new file mode 100644 index 0000000..912a0bf --- /dev/null +++ b/Week_09/isomorphic-strings/isIsomorphic_test.go @@ -0,0 +1,27 @@ +package isomorphic_strings + +import "testing" + +func TestIsIsomorphic(t *testing.T) { + s := "egg" + ts := "add" + want := true + got := IsIsomorphic(s, ts) + if got != want { + t.Errorf("want:%t got:%t", want, got) + } + s = "foo" + ts = "bar" + want = false + got = IsIsomorphic(s, ts) + if got != want { + t.Errorf("want:%t got:%t", want, got) + } + s = "badc" + ts = "baba" + want = false + got = IsIsomorphic(s, ts) + if got != want { + t.Errorf("want:%t got:%t", want, got) + } +} diff --git a/Week_09/longest-increasing-subsequence/lengthOfLIS.go b/Week_09/longest-increasing-subsequence/lengthOfLIS.go new file mode 100644 index 0000000..0b9a888 --- /dev/null +++ b/Week_09/longest-increasing-subsequence/lengthOfLIS.go @@ -0,0 +1,33 @@ +package longest_increasing_subsequence + +func max(a, b int) int { + if a > b { + return a + } + return b +} + +func LengthOfLIS(nums []int) int { + if len(nums) < 2 { + return len(nums) + } + // dp数组初始化,dp[i]代表以nums[i]结尾的最长子序列长度 + dp := make([]int, len(nums)) + for i := 0; i < len(dp); i++ { + dp[i] = 1 + } + for i := 1; i < len(nums); i++ { + for j := 0; j < i; j++ { + // 前面的数字比当前数字小,把可以把当前数字拼到结尾形成一个子序列,最终值取较大值 + if nums[j] < nums[i] { + dp[i] = max(dp[i], dp[j]+1) + } + } + } + // 选取最大递增子序列长度 + ans := 0 + for i := range dp { + ans = max(ans, dp[i]) + } + return ans +} diff --git a/Week_09/longest-increasing-subsequence/lengthOfLIS_test.go b/Week_09/longest-increasing-subsequence/lengthOfLIS_test.go new file mode 100644 index 0000000..cd9003a --- /dev/null +++ b/Week_09/longest-increasing-subsequence/lengthOfLIS_test.go @@ -0,0 +1,24 @@ +package longest_increasing_subsequence + +import "testing" + +func TestLengthOfLIS(t *testing.T) { + nums := []int{10, 9, 2, 5, 3, 7, 101, 18} + want := 4 + got := LengthOfLIS(nums) + if got != want { + t.Errorf("want:%d got:%d", want, got) + } + nums = []int{0, 1, 0, 3, 2, 3} + want = 4 + got = LengthOfLIS(nums) + if got != want { + t.Errorf("want:%d got:%d", want, got) + } + nums = []int{7, 7, 7, 7, 7, 7, 7} + want = 1 + got = LengthOfLIS(nums) + if got != want { + t.Errorf("want:%d got:%d", want, got) + } +} diff --git a/Week_09/longest-palindromic-substring/longestPalindrome.go b/Week_09/longest-palindromic-substring/longestPalindrome.go new file mode 100644 index 0000000..01b6e30 --- /dev/null +++ b/Week_09/longest-palindromic-substring/longestPalindrome.go @@ -0,0 +1,33 @@ +package longest_palindromic_substring + +func LongestPalindrome(s string) string { + if len(s) <= 0 { + return s + } + low, high := 0, 0 + for i := 0; i < len(s); i++ { + //寻找长度为奇数的回文串 + l, r := palindrome(s, i, i) + // 区间长度更大,则更新 + if r - l > high - low { + low, high = l, r + } + //寻找长度为偶数的回文串 + l, r = palindrome(s, i, i+1) + // 区间长度更大,则更新 + if r - l > high - low { + low, high = l, r + } + } + // 最长回文 + return s[low:high+1] +} + +// 从l,r向两端扩散,返回最长回文区间 +func palindrome(s string, l, r int) (int, int) { + for l >= 0 && r < len(s) && s[l] == s[r] { + l-- + r++ + } + return l + 1, r - 1 +} diff --git a/Week_09/longest-palindromic-substring/longestPalindrome_test.go b/Week_09/longest-palindromic-substring/longestPalindrome_test.go new file mode 100644 index 0000000..7f925b9 --- /dev/null +++ b/Week_09/longest-palindromic-substring/longestPalindrome_test.go @@ -0,0 +1,18 @@ +package longest_palindromic_substring + +import "testing" + +func TestLongestPalindrome(t *testing.T) { + s := "babad" + want := "bab" + got := LongestPalindrome(s) + if got != want { + t.Errorf("want:%s got:%s", want, got) + } + s = "cbbd" + want = "bb" + got = LongestPalindrome(s) + if got != want { + t.Errorf("want:%s got:%s", want, got) + } +} diff --git a/Week_09/longest-valid-parentheses/longestValidParentheses.go b/Week_09/longest-valid-parentheses/longestValidParentheses.go new file mode 100644 index 0000000..f7a9211 --- /dev/null +++ b/Week_09/longest-valid-parentheses/longestValidParentheses.go @@ -0,0 +1,84 @@ +package longest_valid_parentheses + +// dp +func LongestValidParentheses(s string) int { + ans, n := 0, len(s) + dp := make([]int, n) + for i := 1; i < len(s); i++ { + // 以左括号结尾的子串,则肯定不是有效括号 + if s[i] == '(' { + dp[i] = 0 + }else{ + // 以右括号结尾,并且前一个字符是左括号, 则dp = dp[i-2]+2 + if s[i-1] == '(' { + // 当前和前一个字符构成有效括号 + dp[i] = 2 + // 还需再加上dp[i-2] + if i >= 2 { + dp[i] += dp[i-2] + } + } else { + // 前一个字符也是右括号, + // 并且前一个字符之前的字符是有效括号 + if dp[i-1] > 0 { + // 前一个有效括号组的前一个字符是'(' 与当前的')'匹配 + if i - dp[i-1] - 1 >= 0 && s[i - dp[i-1] - 1] == '(' { + dp[i] = dp[i-1] + 2 + // 前边可能还有有效括号 + if i - dp[i-1] - 2 >= 0 { + dp[i] += dp[i - dp[i-1] - 2] + } + } + } + } + } + if dp[i] > ans { + ans = dp[i] + } + } + return ans +} + +//栈 +func LongestValidParentheses2(s string) int { + ans := 0 + // 栈底为不匹配的右括号位置,初始化为-1,很重要,能统一逻辑,不用边界特殊判断 + stack := []int{-1} + for i := range s { + if s[i] == '(' { + stack = append(stack, i) + }else{ + stack = stack[0:len(stack)-1] + // 栈空了,压入当前右括号位置作为新的栈底 + if len(stack) == 0 { + stack = append(stack, i) + } + top := stack[len(stack)-1] + if ans < i - top { + ans = i - top + } + } + } + return ans +} + +//错误的方法 +func LongestValidParentheses3(s string) int { + ans := 0 + stack := []int{} + for i := range s { + if s[i] == '(' { + stack = append(stack, i) + }else{ + // 这样只能计入最近匹配的有效括号组, s = ")()())" 这种情况下只会得到:2 + if len(stack) > 0 { + top := stack[len(stack)-1] + stack = stack[0:len(stack)-1] + if ans < i - top + 1 { + ans = i - top + 1 + } + } + } + } + return ans +} diff --git a/Week_09/longest-valid-parentheses/longestValidParentheses_test.go b/Week_09/longest-valid-parentheses/longestValidParentheses_test.go new file mode 100644 index 0000000..31f998f --- /dev/null +++ b/Week_09/longest-valid-parentheses/longestValidParentheses_test.go @@ -0,0 +1,33 @@ +package longest_valid_parentheses + +import "testing" + +func TestLongestValidParentheses(t *testing.T) { + s := "(()" + want := 2 + got := LongestValidParentheses(s) + if got != want { + t.Errorf("want:%d got:%d", want, got) + } + s = ")()())" + want = 4 + got = LongestValidParentheses(s) + if got != want { + t.Errorf("want:%d got:%d", want, got) + } +} + +func TestLongestValidParentheses2(t *testing.T) { + s := "(()" + want := 2 + got := LongestValidParentheses2(s) + if got != want { + t.Errorf("want:%d got:%d", want, got) + } + s = ")()())" + want = 4 + got = LongestValidParentheses2(s) + if got != want { + t.Errorf("want:%d got:%d", want, got) + } +} diff --git a/Week_09/min-cost-climbing-stairs/minCostClimbingStairs.go b/Week_09/min-cost-climbing-stairs/minCostClimbingStairs.go new file mode 100644 index 0000000..d078345 --- /dev/null +++ b/Week_09/min-cost-climbing-stairs/minCostClimbingStairs.go @@ -0,0 +1,25 @@ +package min_cost_climbing_stairs + +func min(a, b int) int { + if a < b { + return a + } + return b +} + +func MinCostClimbingStairs(cost []int) int { + // 小于2阶,直接跳过 + if len(cost) < 2 { + return 0 + } + // dp初始化 + f0, f1 := cost[0], cost[1] + for i := 2; i < len(cost); i++ { + // dp[i] = min(dp[i-1],dp[i-2]) + cost[i] + f2 := min(f0, f1) + cost[i] + f0 = f1 + f1 = f2 + } + // min(dp[len(cost)-1], dp[len(cost)-2]) + return min(f0, f1) +} diff --git a/Week_09/min-cost-climbing-stairs/minCostClimbingStairs_test.go b/Week_09/min-cost-climbing-stairs/minCostClimbingStairs_test.go new file mode 100644 index 0000000..db780f7 --- /dev/null +++ b/Week_09/min-cost-climbing-stairs/minCostClimbingStairs_test.go @@ -0,0 +1,12 @@ +package min_cost_climbing_stairs + +import "testing" + +func TestMinCostClimbingStairs(t *testing.T) { + cost := []int{1, 100, 1, 1, 1, 100, 1, 1, 100, 1} + want := 6 + got := MinCostClimbingStairs(cost) + if got != want { + t.Errorf("want:%d got:%d", want, got) + } +} diff --git a/Week_09/race-car/racecar.go b/Week_09/race-car/racecar.go new file mode 100644 index 0000000..c9c7120 --- /dev/null +++ b/Week_09/race-car/racecar.go @@ -0,0 +1,35 @@ +package race_car + +func Racecar(target int) int { + dp := make([]int, target + 2) + dp[1] = 1 //A + dp[2] = 4 //AARA 或者 ARRA + k := 2 + // S记录连续k个A指令,达到的位置 + S := 3 + for i := 3; i <= target; i++ { + if i == S { + dp[i] = k + k++ + // 2^k - 1 + S = (1<= left; i-- { + ans += string(bs[i]) + } + // 加上一个空格,处理下一个单词 + ans += " " + left = right + } + // 最后一个是空格,则删除掉最后的空格,每个单词处理完后加了个空格,这里再处理下 + if ans[len(ans)-1] == ' ' { + ans = ans[:len(ans)-1] + } + return ans +} + +// 直接使用库函数 +func ReverseWords2(s string) string { + if len(s) <= 0 { + return "" + } + ans := "" + // 按" " split + ss := strings.Split(s, " ") + // 反向拼接单词,如果是"",不拼接 + for i := len(ss) -1; i >= 0 ; i-- { + if ss[i] == "" { + continue + } + // 每拼一个单词加个" " + ans += ss[i] + " " + } + // 去掉最后一个空格 + return ans[:len(ans)-1] +} diff --git a/Week_09/reverse-words-in-a-string/reverseWords_test.go b/Week_09/reverse-words-in-a-string/reverseWords_test.go new file mode 100644 index 0000000..ac7ff5f --- /dev/null +++ b/Week_09/reverse-words-in-a-string/reverseWords_test.go @@ -0,0 +1,33 @@ +package reverse_words_in_a_string + +import "testing" + +func TestReverseWords(t *testing.T) { + s := "the sky is blue" + want := "blue is sky the" + got := ReverseWords(s) + if got != want { + t.Errorf("want:%s got:%s", want, got) + } + s = " hello world! " + want = "world! hello" + got = ReverseWords(s) + if got != want { + t.Errorf("want:%s got:%s", want, got) + } +} + +func TestReverseWords2(t *testing.T) { + s := "the sky is blue" + want := "blue is sky the" + got := ReverseWords2(s) + if got != want { + t.Errorf("want:%s got:%s", want, got) + } + s = " hello world! " + want = "world! hello" + got = ReverseWords2(s) + if got != want { + t.Errorf("want:%s got:%s", want, got) + } +} diff --git a/Week_09/string-to-integer-atoi/myAtoi.go b/Week_09/string-to-integer-atoi/myAtoi.go new file mode 100644 index 0000000..05538ef --- /dev/null +++ b/Week_09/string-to-integer-atoi/myAtoi.go @@ -0,0 +1,82 @@ +package string_to_integer_atoi + +import "math" + +func MyAtoi(s string) int { + // 删除头部空格 + s = removePrefixSpace(s) + if len(s) <= 0 { + return 0 + } + sign, num := 1, int64(0) + // 判断第一位是否符号位,然后进行转化 + if s[0] == '+' { + sign = 1 + num = str2int(s[1:], sign) + }else if s[0] == '-'{ + sign = -1 + num = str2int(s[1:], sign) + }else{ + num = str2int(s, sign) + } + return int(num) +} + +// 删除开始的空格 +func removePrefixSpace(s string) string { + i := 0 + for i = 0; i < len(s); { + if s[i] == ' ' { + i++ + }else{ + break + } + } + return s[i:] +} + +// string转int,使用int64防止int32溢出 +func str2int(s string, sign int) int64 { + num := int64(0) + for i := 0; i < len(s); i++ { + if !isNumberChar(s[i]) { + return num + } + num = num * 10 + int64(sign) * int64(s[i]-'0') + // 正数溢出 + if sign == 1 && num > math.MaxInt32 { + return math.MaxInt32 + } + // 负数溢出 + if sign == -1 && num < math.MinInt32 { + return math.MinInt32 + } + } + return num +} + +// 字符是否是数字 +func isNumberChar(ch byte) bool { + return ch >= '0' && ch <= '9' +} + +// string转int,使用int32,溢出判断比直接使用int64麻烦 +func str2int2(s string, sign int) int64 { + num := int32(0) + for i := 0; i < len(s); i++ { + if !isNumberChar(s[i]) { + return int64(num) + } + // 正数溢出 + if sign == 1 && num > math.MaxInt32/10 || (num == math.MaxInt32/10 && (int32(s[i]-'0') > math.MaxInt32%10)) { + return math.MaxInt32 + } + // 负数溢出 + if sign == -1 && num < math.MinInt32 /10 || (num == math.MinInt32/10 && ((-1 * int32(s[i]-'0')) < math.MinInt32%10)){ + return math.MinInt32 + } + num = num * 10 + int32(sign) * int32(s[i]-'0') + } + return int64(num) +} + diff --git a/Week_09/string-to-integer-atoi/myAtoi_test.go b/Week_09/string-to-integer-atoi/myAtoi_test.go new file mode 100644 index 0000000..77f345d --- /dev/null +++ b/Week_09/string-to-integer-atoi/myAtoi_test.go @@ -0,0 +1,48 @@ +package string_to_integer_atoi + +import "testing" + +func TestMyAtoi(t *testing.T) { + s := "42" + want := 42 + got := MyAtoi(s) + if got != want { + t.Errorf("want:%d got:%d", want, got) + } + s = " -42" + want = -42 + got = MyAtoi(s) + if got != want { + t.Errorf("want:%d got:%d", want, got) + } + s = "4193 with words" + want = 4193 + got = MyAtoi(s) + if got != want { + t.Errorf("want:%d got:%d", want, got) + } + s = "words and 987" + want = 0 + got = MyAtoi(s) + if got != want { + t.Errorf("want:%d got:%d", want, got) + } + s = "-91283472332" + want = -2147483648 + got = MyAtoi(s) + if got != want { + t.Errorf("want:%d got:%d", want, got) + } + s = "91283472332" + want = 1 << 31 - 1 + got = MyAtoi(s) + if got != want { + t.Errorf("want:%d got:%d", want, got) + } + s = "-2147483649" + want = -1 << 31 + got = MyAtoi(s) + if got != want { + t.Errorf("want:%d got:%d", want, got) + } +} diff --git a/Week_09/unique-paths-ii/uniquePathsWithObstacles.go b/Week_09/unique-paths-ii/uniquePathsWithObstacles.go new file mode 100644 index 0000000..785edc7 --- /dev/null +++ b/Week_09/unique-paths-ii/uniquePathsWithObstacles.go @@ -0,0 +1,39 @@ +package unique_paths_ii + +func UniquePathsWithObstacles(obstacleGrid [][]int) int { + if len(obstacleGrid) <= 0 || len(obstacleGrid[0]) <= 0 { + return 0 + } + m, n := len(obstacleGrid), len(obstacleGrid[0]) + dp := make([]int, n) + // dp初始化 + for j := 0; j < n; j++ { + // 如果为障碍物,则dp值为0 + if obstacleGrid[0][j] == 1 { + dp[j] = 0 + }else{ + // 不是障碍物,第0列为1 + if j == 0 { + dp[j] = 1 + }else{ + // 不是第0列,则为前一列的值 + dp[j] = dp[j-1] + } + } + } + for i := 1; i < m; i++ { + for j := 0; j < n; j++ { + // 障碍物,则dp值设置为0 + if obstacleGrid[i][j] == 1 { + dp[j] = 0 + }else{ + // 不是障碍物,且不是第0列,则为上边和下边数值相加 + if j != 0 { + dp[j] += dp[j-1] + } + // 是第0列,则和上边的值相同 + } + } + } + return dp[n-1] +} diff --git a/Week_09/unique-paths-ii/uniquePathsWithObstacles_test.go b/Week_09/unique-paths-ii/uniquePathsWithObstacles_test.go new file mode 100644 index 0000000..cdffa15 --- /dev/null +++ b/Week_09/unique-paths-ii/uniquePathsWithObstacles_test.go @@ -0,0 +1,12 @@ +package unique_paths_ii + +import "testing" + +func TestUniquePathsWithObstacles(t *testing.T) { + obstacleGrid := [][]int{{0,0,0},{0,1,0},{0,0,0}} + want := 2 + got := UniquePathsWithObstacles(obstacleGrid) + if got != want { + t.Errorf("want:%d got:%d", want, got) + } +} diff --git a/Week_09/valid-palindrome-ii/validPalindrome.go b/Week_09/valid-palindrome-ii/validPalindrome.go new file mode 100644 index 0000000..09c8d75 --- /dev/null +++ b/Week_09/valid-palindrome-ii/validPalindrome.go @@ -0,0 +1,26 @@ +package valid_palindrome_ii + +func ValidPalindrome(s string) bool { + left, right := 0, len(s) -1 + for left < right { + // 如果不相等,去掉左边或右边的一个字符再判断剩余字符串是否回文 + if s[left] != s[right] { + return validPalindrome(s, left+1, right) || validPalindrome(s, left, right-1) + } + left++ + right-- + } + return true +} + +// 判断指定左右区间的字符是否回文 +func validPalindrome(s string, left, right int) bool { + for left < right { + if s[left] != s[right] { + return false + } + left++ + right-- + } + return true +} diff --git a/Week_09/valid-palindrome-ii/validPalindrome_test.go b/Week_09/valid-palindrome-ii/validPalindrome_test.go new file mode 100644 index 0000000..d0943e0 --- /dev/null +++ b/Week_09/valid-palindrome-ii/validPalindrome_test.go @@ -0,0 +1,18 @@ +package valid_palindrome_ii + +import "testing" + +func TestValidPalindrome(t *testing.T) { + s := "aba" + want := true + got := ValidPalindrome(s) + if got != want { + t.Errorf("want:%t got:%t", want, got) + } + s = "abca" + want = true + got = ValidPalindrome(s) + if got != want { + t.Errorf("want:%t got:%t", want, got) + } +} diff --git a/Week_09/wildcard-matching/isMatch.go b/Week_09/wildcard-matching/isMatch.go new file mode 100644 index 0000000..0094853 --- /dev/null +++ b/Week_09/wildcard-matching/isMatch.go @@ -0,0 +1,28 @@ +package wildcard_matching + +func IsMatch(s string, p string) bool { + m, n := len(s), len(p) + dp := make([][]bool, m + 1) + for i := 0; i <= m; i++ { + dp[i] = make([]bool, n + 1) + } + // 两个空串,匹配 + dp[0][0] = true + for j := 1; j <= n; j++ { + // s为空串,则p每个字符均是*,则匹配 + dp[0][j] = p[j-1] == '*' && dp[0][j-1] + } + for i := 1; i <= m; i++ { + for j := 1; j <= n; j++ { + // 当前字符相等或p当前字符是 '?' 可以匹配任何一个非'',则dp[i][j]可以从dp[i-1][j-1]转移过来 + if p[j-1] == '?' || s[i-1] == p[j-1] { + dp[i][j] = dp[i-1][j-1] + }else if p[j-1] == '*' { + // *匹配'' 或者 * 匹配多个字符 + dp[i][j] = dp[i-1][j] || dp[i][j-1] + } + + } + } + return dp[m][n] +} diff --git a/Week_09/wildcard-matching/isMatch_test.go b/Week_09/wildcard-matching/isMatch_test.go new file mode 100644 index 0000000..0e9917f --- /dev/null +++ b/Week_09/wildcard-matching/isMatch_test.go @@ -0,0 +1,13 @@ +package wildcard_matching + +import "testing" + +func TestIsMatch(t *testing.T) { + s := "adceb" + p := "*a*b" + want := true + got := IsMatch(s, p) + if got != want { + t.Errorf("want:%t got:%t", want, got) + } +} diff --git "a/Week_10 \346\257\225\344\270\232\346\200\273\347\273\223/\346\257\225\344\270\232\346\200\273\347\273\223.md" "b/Week_10 \346\257\225\344\270\232\346\200\273\347\273\223/\346\257\225\344\270\232\346\200\273\347\273\223.md" new file mode 100644 index 0000000..39de61f --- /dev/null +++ "b/Week_10 \346\257\225\344\270\232\346\200\273\347\273\223/\346\257\225\344\270\232\346\200\273\347\273\223.md" @@ -0,0 +1,15 @@ +## **毕业总结** + +**1、学习感受** + +(1)学习到不少常用数据结构和算法,包括数组、链表、树、动态规划、回溯、双指针、滑动窗口等。 + +(2)学习到常用算法框架和解题思路,遇到不会题时多看解题思路,到国际网站挑选好的解题方法,然后消化吸收。 + +(3)改变了以前刷题的误区,只做一遍,死磕,老师的五毒神掌很实用。 + +**2、毕业后的计划** + +(1)还要继续坚持刷题,按照老师的五毒神掌刷题,以免造成做过题目又忘记了。 + +(2)对于同一个题目,多学习国际站上的高票解答。在解答出题目的同时把代码尽量写得优雅。