├── .gitignore ├── 003-二维数组中的查找 ├── README.md ├── problem003.go └── problem003_test.go ├── 004-替换空格 ├── README.md ├── problem004.go └── problem004_test.go ├── 005-从尾到头打印链表(ing) ├── README.md └── problem005.go ├── 006-重建二叉树 ├── README.md └── problem006.go ├── 007-用两个栈实现队列 ├── README.md ├── problem007.go └── problem007_test.go ├── 008-旋转数组的最小数字 ├── README.md ├── problem008.go └── problem008_test.go ├── 009-斐波那契数列 ├── README.md ├── problem009.go └── problem009_test.go ├── 010-二进制中1的个数 ├── README.md ├── problem010.go └── problem010_test.go ├── 011-数值的整数次方 ├── README.md ├── problem011.go └── problem011_test.go ├── 012-打印1到最大的N位数 ├── README.md └── problem012.go ├── 014-调整数组顺序使奇数位于偶数前面 ├── README.md └── problem014.go ├── 015-链表中倒数第k个结点 ├── README.md └── problem015.go ├── 016-反转链表 ├── README.md └── problem016.go ├── 017-合并两个排序的链表 ├── README.md └── problem017.go ├── 018-树的子结构 ├── README.md └── problem018.go ├── 019-二叉树的镜像 ├── README.md └── problem019.go ├── 020-顺时针打印矩阵 └── README.md ├── 021-包含min函数的栈 └── README.md ├── 022-栈的压入弹出序列 ├── README.md └── problem022.go ├── 023-从上往下打印二叉树 ├── README.md └── problem023.go ├── 024-二叉搜索树的后序遍历序列 ├── README.md └── problem024.go ├── 025-二叉树中和为某一值的路径 └── README.md ├── 026-复杂链表的复制 ├── README.md └── problem026.go ├── 027-二叉搜索树与双向链表 ├── README.md └── problem027.go ├── 028-字符串的排列 ├── README.md └── problem028.go ├── 029-数组中出现次数超过一半的数字 ├── README.md ├── problem029.go └── problem029_test.go ├── 030-最小的K个数 ├── README.md └── problem030.go ├── 031-连续子数组的最大和 ├── README.md └── problem031.go ├── 032-从1到n整数中1出现的次数 ├── README.md └── problem032.go ├── 033-把数组排成最小的数 ├── README.md └── problem033.go ├── 034-丑数 ├── README.md └── problem034.go ├── 035-第一个只出现一次的字符位置 ├── README.md └── problem035.go ├── 036-数组中的逆序对 ├── README.md └── problem036.go ├── 037-两个链表的第一个公共结点 ├── README.md └── problem037.go ├── 038-数字在排序数组中出现的次数 ├── README.md └── problem038.go ├── 039-二叉树的深度 ├── README.md └── problem039.go ├── 039-平衡二叉树[附加] ├── README.md └── problem039.go ├── 040-数组中只出现一次的数字 ├── README.md └── problem040.go ├── 041-和为S的两个数字 ├── README.md └── problem041.go ├── 041-和为S的连续正数序列 ├── README.md └── problem041.go ├── 042-左旋转字符串 ├── README.md └── problem042.go ├── 042-翻转单词顺序列 ├── README.md └── problem042.go ├── 044-扑克牌顺子 ├── README.md └── problem044.go ├── 045-孩子们的游戏(圆圈中最后剩下的数) ├── README.md └── problem045.go ├── 046-求1+2+3+...+n └── README.md ├── 047-不用加减乘除做加法 ├── README.md └── problem047.go ├── 048-不能被继承的类 └── README.md ├── 049-把字符串转换成整数 ├── README.md └── problem049.go ├── 051-数组中重复的数字 ├── README.md └── problem051.go ├── 052-构建乘积数组 ├── README.md └── problem052.go ├── 053-正则表达式匹配 ├── README.md └── problem053.go ├── 054-表示数值的字符串 ├── README.md └── problem054.go ├── 055-字符流中第一个不重复的字符 ├── README.md └── problem055.go ├── 056-链表中环的入口结点 ├── README.md └── problem056.go ├── 057-删除链表中重复的结点 ├── README.md └── problem057.go ├── 058-二叉树的下一个结点 ├── README.md └── problem058.go ├── 059-对称的二叉树 ├── README.md └── problem059.go ├── 060-把二叉树打印成多行 ├── README.md └── problem060.go ├── 061-按之字形顺序打印二叉树 ├── README.md └── problem061.go ├── 062-序列化二叉树 ├── README.md └── problem062.go ├── 063-二叉搜索树的第K个结点 ├── README.md └── problem063.go ├── 064-数据流之中的中位数 ├── README.md └── problem064.go ├── 065-滑动窗口的最大值 ├── README.md └── problem065.go ├── LICENSE ├── README.md └── utils ├── maxHeap.go ├── maxHeap_test.go ├── minHeap.go ├── minHeap_test.go ├── stack.go └── stack_test.go /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .idea -------------------------------------------------------------------------------- /003-二维数组中的查找/README.md: -------------------------------------------------------------------------------- 1 | # 题意 2 | 3 | ## 题目描述 4 | 5 | 在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。 6 | 7 | ## 输入描述 8 | 9 | >array: 待查找的二维数组 target:查找的数字 10 | 11 | ## 输出描述 12 | 13 | >查找到返回true,查找不到返回false 14 | 15 | ## 解题思路 16 | 17 | 见程序注释 -------------------------------------------------------------------------------- /003-二维数组中的查找/problem003.go: -------------------------------------------------------------------------------- 1 | package problem003 2 | 3 | // 用了分治的思想,根据题意可得 4 | // 从数组中选取数字,和目标数字的关系有三种情况:=,<或>。 5 | // 如果是等于则查找成功; 6 | // 如果是数组中元素小于要查找的数字,说明要查找的数字应该在当前位置的右边或下边。 7 | // 如果是数组中元素大于要查找的数字,说明要查找的数字应该在当前位置的左边或上边。 8 | // 即 对于数组中的任何一个元素, 比它小的元素都在它的左方或者上方, 比它大的元素都在它的右边或者下方 9 | // 但是这两个区域还有可能有重叠,比如右边或下边会在右下角有重叠。 10 | 11 | // 为了不重复的处理重叠的数据, 我们可以找几个特殊的起点, 比如 12 | 13 | // 起点 性质 可否作为起点 14 | // 左上角 没有上方元素(小)和左方元素(小) 15 | // 只有下方元素(大)和右方元素(大) 否 16 | 17 | // 右上角 没有上方元素(小), 和右方元素(大) 18 | // 只有下方元素(大)和左方元素(小) 是 19 | 20 | // 左下角 没有下方元素(大), 和左方元素(小) 21 | // 只有上方元素(小)和右方元素(大) 是 22 | 23 | // 右下角 没有下方元素(大), 和右方元素(大) 24 | // 只有上方元素(小)和左方元素(小) 否 25 | 26 | // 因此重叠问题的解决方法: 27 | // 如果查找从右上角开始,如果要查找的数字不在右上角,则每次可以剔除一列或一行。 28 | // 也可以从左下角开始 29 | // 但是不能从左上角或者右下角开始。 30 | 31 | 32 | func Find(board [][]int, target int) bool { 33 | rlen := len(board) 34 | clen := len(board[0]) 35 | 36 | // 我们从右上角的元素找起来 37 | for r,c:=0,clen-1; r=0; { 38 | if board[r][c] == target { 39 | return true 40 | } 41 | if board[r][c] > target { 42 | c-- 43 | continue 44 | } else { 45 | r++ 46 | } 47 | } 48 | return false 49 | } 50 | -------------------------------------------------------------------------------- /003-二维数组中的查找/problem003_test.go: -------------------------------------------------------------------------------- 1 | package problem003 2 | 3 | import ( 4 | "testing" 5 | "github.com/stretchr/testify/assert" 6 | ) 7 | 8 | type para struct { 9 | board [][]int 10 | target int 11 | } 12 | 13 | type ans struct { 14 | find bool 15 | } 16 | 17 | type question struct { 18 | p para 19 | a ans 20 | } 21 | 22 | func Test_OK(t *testing.T) { 23 | ast := assert.New(t) 24 | 25 | qs := []question{ 26 | question{ 27 | p: para{ 28 | board: [][]int{ 29 | []int{ 1, 2, 8, 9, }, 30 | []int{ 2, 4, 9, 12, }, 31 | []int{ 4, 7, 10, 13, }, 32 | []int{ 6, 8, 11, 15, }, 33 | }, 34 | target: 7, 35 | }, 36 | a: ans{ 37 | find: true, 38 | }, 39 | }, 40 | question{ 41 | p: para{ 42 | board: [][]int{ 43 | []int{ 1, 2, 8, 9, }, 44 | []int{ 2, 4, 9, 12, }, 45 | []int{ 4, 6, 10, 13, }, 46 | []int{ 6, 8, 11, 15, }, 47 | }, 48 | target: 7, 49 | }, 50 | a: ans{ 51 | find: false, 52 | }, 53 | }, 54 | } 55 | 56 | for _, q := range qs { 57 | a, p := q.a, q.p 58 | ast.Equal(a.find, Find(p.board, p.target), "输入:%v", p) 59 | } 60 | } -------------------------------------------------------------------------------- /004-替换空格/README.md: -------------------------------------------------------------------------------- 1 | # 题意 2 | 3 | 请实现一个函数,将一个字符串中的空格替换成“%20”。 例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。 4 | 5 | We Are Happy 6 | 7 | We%20Are%20Happy 8 | 9 | 如果不考虑在原来的字符串上替换的话, 那么我们直接再开一个数组,从前往后依次赋值 10 | 11 | 遇见空格,就填上%20, 否则就填当前字符。 12 | 13 | 但是这个肯定不是面试官期待的 14 | 15 | 那么怎么在原字符串上进行高效的替换呢? 16 | 17 | ## 解题思路 18 | 19 | 见程序注释 -------------------------------------------------------------------------------- /004-替换空格/problem004.go: -------------------------------------------------------------------------------- 1 | package problem004 2 | func replaceSpace(str []byte, length int) { 3 | count := 0 4 | // 遍历一遍字符串, 统计字符出现的数目, 计算替换后的字符串长度 5 | for i:=0; i=0 && nl>=0; { 14 | if str[l]==' '{ 15 | str[nl] = '0' 16 | nl-- 17 | str[nl] = '2' 18 | nl-- 19 | str[nl] = '%' 20 | nl-- 21 | l-- 22 | } else { 23 | str[nl] = str[l] 24 | nl-- 25 | l-- 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /004-替换空格/problem004_test.go: -------------------------------------------------------------------------------- 1 | package problem004 2 | 3 | import ( 4 | "testing" 5 | "github.com/stretchr/testify/assert" 6 | ) 7 | 8 | type para struct { 9 | str []byte 10 | length int 11 | } 12 | 13 | type ans struct { 14 | res []byte 15 | } 16 | 17 | type question struct { 18 | p para 19 | a ans 20 | } 21 | 22 | func Test_OK(t *testing.T) { 23 | ast := assert.New(t) 24 | 25 | qs := []question{ 26 | question{ 27 | p: para{ 28 | str: []byte{'a',' ','b',' ','c','x','x','x','x'}, 29 | length: 5, 30 | }, 31 | a: ans{ 32 | res: []byte{'a','%','2','0','b','%','2','0','c'}, 33 | }, 34 | }, 35 | } 36 | 37 | for _, q := range qs { 38 | a, p := q.a, q.p 39 | replaceSpace(p.str, p.length) 40 | ast.Equal(string(a.res), string(p.str), "输入:%v", p) 41 | } 42 | } -------------------------------------------------------------------------------- /005-从尾到头打印链表(ing)/README.md: -------------------------------------------------------------------------------- 1 | # 题意 2 | 3 | 输入一个链表,从尾到头打印链表每个节点的值。 4 | 5 | 输入描述: 6 | 7 | 输入为链表的表头 8 | 9 | 输出描述: 10 | 11 | 输出为需要打印的“新链表”的表头 12 | 13 | ## 反转链表 14 | 15 | 首先我们想到的就是反转链表了,如果把链表反转了,然后再返回头,这样再次遍历的时候就相当于从尾到头打印了。 16 | 17 | 但是修改输入数据真的可行么? 18 | 19 | 剑指Offer中为我们在面试中提出了如下小提示 20 | 21 | > 在面试时候,如果我们打算修改输入的数据,最好先问问面试官是不是允许修改 22 | 23 | 通常打印只是一个只读操作,我们肯定不希望输入时候修改链表的内容 24 | 25 | **如果要求反转的话,直接头插法就可以了** 26 | 27 | ## 解题思路 28 | 29 | 见程序注释 -------------------------------------------------------------------------------- /005-从尾到头打印链表(ing)/problem005.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | type NodeList struct{ 8 | Val int 9 | Next *NodeList 10 | } 11 | 12 | // just print it form tail to head and do not modify the original NodeList 13 | 14 | func printListFromTailToHead(head *NodeList) { 15 | if head != nil { 16 | printListFromTailToHead(head.Next) 17 | fmt.Printf("%d -> ", head.Val) 18 | } 19 | } 20 | 21 | func main() { 22 | // example 23 | n3 := &NodeList{3, nil} 24 | n2 := &NodeList{2, n3} 25 | n1 := &NodeList{1, n2} 26 | 27 | fmt.Printf("\n NodeList 1 -> 2 -> 3 \n") 28 | 29 | // test 30 | fmt.Printf("\n Output: ") 31 | printListFromTailToHead(n1) 32 | fmt.Printf("\n \n") 33 | 34 | 35 | } 36 | -------------------------------------------------------------------------------- /006-重建二叉树/README.md: -------------------------------------------------------------------------------- 1 | # 题意 2 | 题目描述 3 | 4 | 输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。 5 | 6 | 假设输入的前序遍历和中序遍历的结果中都不含重复的数字。 7 | 8 | 输入 9 | 10 | > 前序遍历序列{1,2,4,7,3,5,6,8} 11 | > 中序遍历序列{4,7,2,1,5,3,8,6} 12 | 13 | 则重建二叉树并返回。 14 | 15 | # 分析 16 | 17 | 这道题还是比较简单的,我们知道 18 | 19 | 前序遍历的顺序为:根左右 20 | 中序遍历的顺序为:左根右 21 | 22 | 递归思想: 23 | 24 | 1. 我们先根据前序遍历序列的第一个确定根,然后在中序遍历的序列中找到根的位置,根左边的就是其左子树,右边就是其右子树 25 | 2. 构建根和左右子树 26 | 3. 递归的进行1和2 27 | 28 | 29 | ## 解题思路 30 | 31 | 见程序注释 32 | 33 | -------------------------------------------------------------------------------- /006-重建二叉树/problem006.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | type TreeNode struct{ 8 | Val int 9 | Left *TreeNode 10 | Right *TreeNode 11 | } 12 | 13 | func printPreOrder(root *TreeNode){ 14 | if root != nil { 15 | fmt.Printf("%d ", root.Val) 16 | printPreOrder(root.Left) 17 | printPreOrder(root.Right) 18 | } 19 | } 20 | 21 | func printInOrder(root *TreeNode){ 22 | if root != nil { 23 | printInOrder(root.Left) 24 | fmt.Printf("%d ", root.Val) 25 | printInOrder(root.Right) 26 | } 27 | } 28 | 29 | func reConstructBinaryTree(pre []int, in []int) *TreeNode { 30 | if len(pre) != len(in) || len(pre) == 0 { 31 | return nil 32 | } 33 | // find root and root Index in inOrder 34 | rootVal := pre[0] 35 | rootIndex := 0 36 | for i := 0; i < len(in); i++ { 37 | if in[i] == rootVal { 38 | rootIndex = i 39 | } 40 | } 41 | // pre and in for left and right 42 | inL, inR := in[:rootIndex], in[rootIndex+1:] 43 | preL, preR := pre[1:rootIndex+1], pre[rootIndex+1:] 44 | // revursive 45 | left := reConstructBinaryTree(preL, inL) 46 | right := reConstructBinaryTree(preR, inR) 47 | return &TreeNode{Val: rootVal, Left: left, Right: right} 48 | } 49 | 50 | 51 | func main() { 52 | // example 53 | pre := []int{1,2,4,7,3,5,6,8} 54 | in := []int{4,7,2,1,5,3,6,8} 55 | 56 | fmt.Println("preOder: ", pre) 57 | fmt.Println("inOrder: ", in) 58 | 59 | // Reconstruct 60 | fmt.Println("\nReconstruct Binary Tree... \n ",) 61 | root := reConstructBinaryTree(pre, in) 62 | 63 | // test 64 | fmt.Printf("preOder from Tree reconstructed: ") 65 | printPreOrder(root) 66 | fmt.Printf("\n") 67 | 68 | fmt.Printf("inOder from Tree reconstructed: ") 69 | printInOrder(root) 70 | fmt.Printf("\n") 71 | } 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | -------------------------------------------------------------------------------- /007-用两个栈实现队列/README.md: -------------------------------------------------------------------------------- 1 | # 题意 2 | 题目描述 3 | 4 | > 用两个栈来实现一个队列,完成队列的Push和Pop操作。 队列中的元素为int类型。 5 | 6 | ## 分析 7 | 始终维护s1作为存储空间,以s2作为临时缓冲区。 8 | 9 | 始终维护s1作为输入栈,以s2作为输出栈 10 | 11 | - 入队时,将元素压入s1。 12 | - 出队时,判断s2是否为空,如不为空,则直接弹出顶元素;如为空,则将s1的元素逐个“倒入”s2,把最后一个元素弹出并出队。 这个思路,避免了反复“倒”栈,仅在需要时才“倒”一次。但在实际面试中很少有人说出,可能是时间较少的缘故吧。 13 | 14 | 15 | ## 解题思路 16 | 17 | 见程序注释 18 | 19 | -------------------------------------------------------------------------------- /007-用两个栈实现队列/problem007.go: -------------------------------------------------------------------------------- 1 | package problem007 2 | 3 | import ( 4 | "fmt" 5 | "errors" 6 | "../utils" 7 | ) 8 | 9 | type Queue struct { 10 | in utils.Stack 11 | out utils.Stack 12 | } 13 | 14 | func (q *Queue) IsEmpty() bool { 15 | return q.in.IsEmpty() && q.out.IsEmpty() 16 | } 17 | 18 | func (q *Queue) Push(value interface{}) { 19 | q.in.Push(value) 20 | } 21 | 22 | func (q *Queue) Pop() (interface{}, error) { 23 | if q.IsEmpty() { 24 | return nil, errors.New("Queue is empty") 25 | } 26 | 27 | var value interface{} 28 | 29 | if !q.out.IsEmpty() { 30 | value, _ = q.out.Pop() 31 | return value, nil 32 | } 33 | 34 | for !q.in.IsEmpty() { 35 | value, _ = q.in.Pop() 36 | q.out.Push(value) 37 | } 38 | value, _ = q.out.Pop() 39 | return value, nil 40 | } 41 | 42 | 43 | func main() { 44 | var myQueue Queue 45 | 46 | myQueue.Push(1) 47 | myQueue.Push(2) 48 | fmt.Println(myQueue.Pop()) 49 | fmt.Println(myQueue.Pop()) 50 | myQueue.Push(3) 51 | myQueue.Push(4) 52 | fmt.Println(myQueue.Pop()) 53 | fmt.Println(myQueue.Pop()) 54 | fmt.Println(myQueue.Pop()) 55 | } 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /007-用两个栈实现队列/problem007_test.go: -------------------------------------------------------------------------------- 1 | package problem007 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestQueue_IsEmpty(t *testing.T) { 8 | var myQueue Queue 9 | myQueue.Push(1) 10 | myQueue.Push("test") 11 | if myQueue.IsEmpty() == false { 12 | t.Log("Pass Queue.IsEmpty") 13 | } else { 14 | t.Error("Failed Queue.IsEmpty") 15 | } 16 | } 17 | 18 | func TestQueue_Push(t *testing.T) { 19 | var mQueue Queue 20 | mQueue.Push(3) 21 | mQueue.Push(4) 22 | mQueue.Push(5) 23 | mQueue.Push(6) 24 | if mQueue.IsEmpty() == false { 25 | t.Log("Pass Queue.Push") 26 | } else { 27 | t.Error("Failed Queue.Push") 28 | } 29 | } 30 | 31 | func TestQueue_Pop(t *testing.T) { 32 | var mQueue Queue 33 | if _, err := mQueue.Pop(); err == nil { 34 | t.Error("Failed Queue.Pop") 35 | } 36 | 37 | mQueue.Push(3) 38 | mQueue.Push(4) 39 | 40 | 41 | if value, _ := mQueue.Pop(); value == 3 { 42 | t.Log("Pass Queue.Pop") 43 | } else { 44 | t.Errorf("Failed Queue.Pop, value should be %d", 3) 45 | } 46 | if value, _ := mQueue.Pop(); value == 4 { 47 | t.Log("Pass Queue.Pop") 48 | } else { 49 | t.Errorf("Failed Queue.Pop, value should be %d", 4) 50 | } 51 | 52 | if _, err := mQueue.Pop(); err == nil { 53 | t.Error("Failed Queue.Pop") 54 | } 55 | 56 | mQueue.Push(5) 57 | 58 | if value, _ := mQueue.Pop(); value == 5 { 59 | t.Log("Pass Queue.Pop") 60 | } else { 61 | t.Errorf("Failed Queue.Pop, value should be %d", 5) 62 | } 63 | 64 | if _, err := mQueue.Pop(); err == nil { 65 | t.Error("Failed Queue.Pop") 66 | } 67 | 68 | 69 | } -------------------------------------------------------------------------------- /008-旋转数组的最小数字/README.md: -------------------------------------------------------------------------------- 1 | # 题意 2 | 3 | 把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个非递减序列的一个旋转,输出旋转数组的最小元素。 4 | 5 | 例如 6 | 7 | >数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转, 8 | 9 | 该数组的最小值为1。 10 | 11 | ## 解题思路 12 | 13 | 和二分查找法一样,用两个指针分别指向数组的第一个元素和最后一个元素。 14 | 15 | 我们注意到旋转之后的数组实际上可以划分为两个排序的子数组,而且前面的子数组的元素都大于或者等于后面子数组的元素。我们还可以注意到最小的元素刚好是这两个子数组的分界线。 16 | 17 | 我们试着用二元查找法的思路在寻找这个最小的元素。 18 | 19 | 首先我们用两个指针,分别指向数组的第一个元素和最后一个元素。按照题目旋转的规则,第一个元素应该是大于或者等于最后一个元素的(这其实不完全对,还有特例。后面再讨论特例)。 20 | 21 | 接着我们得到处在数组中间的元素 22 | 23 | - 如果该中间元素位于前面的递增子数组,那么它应该大于或者等于第一个指针指向的元素。 24 | 25 | 此时数组中最小的元素应该位于该中间 元素的后面。我们可以把第一指针指向该中间元素,这样可以缩小寻找的范围。 26 | 27 | - 同样,如果中间元素位于后面的递增子数组,那么它应该小于或者等于第二个指针指向的元素。此时该数组中最小的元素应该位于该中间元素的前面。我们可以把第二个指针指向该中间元素,这样同样可以缩小寻找的范围。我们接着再用更新之后的 两个指针,去得到和比较新的中间元素,循环下去。 28 | 29 | ## 特殊 30 | 31 | 我们考虑下特殊情况,我们的循环判断是以rotateArray[low] >= rotateArray[high]为条件的,不满足这个的特殊情况有那些呢? 32 | 33 | 由于是把递增排序数组前面的若干个数据搬到后面去,因此第一个数字总是大于或者等于最后一个数字,但按照定义还有一个 34 | 35 | 特例:开始时就rotateArray[low] < rotateArray[high],那么循环不会执行 36 | 37 | 如果数组旋转后仍然有序,即rotateArray[low] < rotateArray[high] 38 | 如果把排序数组前面0个元素搬到后面,也就是说其实没有旋转, 39 | 40 | 那么第0个元素就是最小的元素 41 | 42 | 因此我们将mid初始化为0 43 | 44 | 现在可以了么,有没有特殊情况仍然未被处理的, 45 | 46 | 如果rotateArray[low] = rotateArray[high] 47 | 48 | > 测试用例: [1, 0, 1, 0, 1, 1] 49 | > 对应输出应该为: 50 | > 0 51 | > 你的输出为: 52 | > 1 53 | 54 | 此时 55 | 56 | > rotateArray[low] rotateArray[mid] rotateArray[high]三者相等 57 | > 无法确定中间元素是属于前面还是后面的递增子数组 58 | > 只能顺序查找 -------------------------------------------------------------------------------- /008-旋转数组的最小数字/problem008.go: -------------------------------------------------------------------------------- 1 | package problem008 2 | 3 | func minNumberInRotateArray(array []int) int { 4 | low, high := 0, len(array)-1 5 | if array[low] < array[high] { 6 | return array[low] 7 | } 8 | mid := (low + high) / 2 9 | for array[low] >= array[high] { 10 | // array[low] >= array[high] 所以此时high为最小值 11 | if high-low == 1 { 12 | mid = high 13 | break 14 | } 15 | mid = (low + high) / 2 16 | 17 | // array[low] array[mid] array[high]三者相等 18 | // 无法确定中间元素是属于前面还是后面的递增子数组 19 | // 只能顺序查找 20 | if array[low] == array[mid] && array[mid] == array[high] { 21 | return MinOrder(array, low, high) 22 | } 23 | 24 | 25 | if array[mid] >= array[low] { 26 | low = mid 27 | } else { 28 | high = mid 29 | } 30 | } 31 | return array[mid] 32 | } 33 | 34 | // 顺序查找 35 | func MinOrder(array []int, low, high int) int { 36 | min := array[low] 37 | for i := low; i <= high; i++ { 38 | if array[i] < min { 39 | min = array[i] 40 | } 41 | } 42 | return min 43 | } 44 | -------------------------------------------------------------------------------- /008-旋转数组的最小数字/problem008_test.go: -------------------------------------------------------------------------------- 1 | package problem008 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func Test_case1(t *testing.T) { 8 | if minNumberInRotateArray([]int{3, 4, 5, 1, 2}) == 1 { 9 | t.Log("Pass") 10 | } else { 11 | t.Error("Failed") 12 | } 13 | } 14 | 15 | func Test_case2(t *testing.T) { 16 | if minNumberInRotateArray([]int{1 , 0, 1, 1, 1}) == 0 { 17 | t.Log("Pass") 18 | } else { 19 | t.Error("Failed") 20 | } 21 | } 22 | 23 | func Test_case3(t *testing.T) { 24 | if minNumberInRotateArray([]int{2,2,3,4,1,1,2,2,2}) == 1 { 25 | t.Log("Pass") 26 | } else { 27 | t.Error("Failed") 28 | } 29 | } -------------------------------------------------------------------------------- /009-斐波那契数列/README.md: -------------------------------------------------------------------------------- 1 | # 题意 2 | 3 | 大家都知道斐波那契数列, 现在要求输入一个整数n, 请你输出斐波那契数列的第n项。 4 | 5 | # 递推公式 6 | 7 | 我们很容易的想到了递推公式 8 | 9 | f(n) = 0, 当n=0 10 | f(n) = 1, 当n=1 11 | f(n) = f(n - 1) + f(n - 2), 其他 12 | 13 | 因此我们马上想到了递归,但是要尽量减少重复计算 -------------------------------------------------------------------------------- /009-斐波那契数列/problem009.go: -------------------------------------------------------------------------------- 1 | package problem009 2 | 3 | func Fibonacci(n int) int { 4 | if n <= 1 { 5 | return n 6 | } 7 | f1, f2 := 0, 1 8 | res := 0 9 | for i := 2; i <= n; i++ { 10 | res = f1+f2 11 | f1, f2 = f2, res 12 | } 13 | return res 14 | } 15 | -------------------------------------------------------------------------------- /009-斐波那契数列/problem009_test.go: -------------------------------------------------------------------------------- 1 | package problem009 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func Test_case(t *testing.T) { 8 | if Fibonacci(0) == 0 { 9 | t.Log("Pass") 10 | } else { 11 | t.Error("Failed") 12 | } 13 | 14 | if Fibonacci(1) == 1 { 15 | t.Log("Pass") 16 | } else { 17 | t.Error("Failed") 18 | } 19 | 20 | if Fibonacci(7) == 13 { 21 | t.Log("Pass") 22 | } else { 23 | t.Error("Failed") 24 | } 25 | 26 | 27 | if Fibonacci(13) == 233 { 28 | t.Log("Pass") 29 | } else { 30 | t.Error("Failed") 31 | } 32 | 33 | } 34 | 35 | -------------------------------------------------------------------------------- /010-二进制中1的个数/README.md: -------------------------------------------------------------------------------- 1 | # 题意 2 | 3 | > 输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示。 4 | 5 | 样例输入 6 | 7 | 3 4 5 -1 8 | 9 | 样例输出 10 | 11 | 1 2 32 12 | 13 | ## 通过移位测试每一位 14 | 15 | 我们很容易想到的算法就是 16 | 通过移位的方法,挨个判断其每一位 17 | 18 | 但是这个算法有致命的缺陷,我们假设通过移位的方式可以获取到其每一位,但是并不总是对的 19 | 20 | ##逻辑右移与算术右移 21 | 22 | 比如一个有符号位的8位二进制数11001101,逻辑右移就不管符号位,如果移一位就变成01100110。算术右移要管符号位,右移一位变成10100110。 23 | 24 | 逻辑左移=算数左移,右边统一添0 25 | 逻辑右移,左边统一添0 26 | 算数右移,左边添加的数和符号有关 27 | 28 | e.g:1010101010,其中[]位是添加的数字 29 | 30 | > 逻辑左移一位:010101010[0] 31 | > 算数左移一位:010101010[0] 32 | > 逻辑右移一位:[0]101010101 33 | > 算数右移一位:[1]101010101 34 | 35 | 因此如果输入负数,那么我们的算法简单的判断是不是0来终结,岂不是要死循环 36 | 37 | ## 避免负数移位的死循环 38 | 39 | 为了负数时候避免死循环,我们可以不右移数字n,转而去移动测试位 40 | 41 | 那么思考我们的循环结束条件,flag一直左移(乘以2),当超出表示标识范围的时候,我们就可以终止了,但是这样子的话,最高位的符号位没有测试,因此要单独测试,同时由于会溢出,我们的flag需要用long来标识 42 | 43 | ## 整数中有几个1就循环几次--lowbit优化 44 | 45 | 我们分析n以n-1两个数的差别, 46 | 47 | - 如果n!=0,那么其二进制位中至少有一个1 48 | 49 | - 如果n的最低位是1(奇数),那么n-1正好把这个最低位的1变成0,其他位不变 50 | 51 | - 如果n的最低位是0(偶数),那么假设其右起第一个1位于m位,即m位后面全是0,那么n-1的第m位由1变成0,而第m位后面的所有0均变成1,m位之前的所有位保持不变。 52 | 53 | 因此通过分析发现: 54 | 55 | **把一个整数n减去1,再和原来的整数做与运算,会把该整数最右边一个1变成0,那么该整数有多少个1,就会进行多少次与运算** 56 | 57 | 即循环的次数,即二进制中1的个数 58 | 59 | >如果一个整数不为0,那么这个整数至少有一位是1。如果我们把这个整数减1,那么原来处在整数最右边的1就会变为0,原来在1后面的所有的0都会变成1(如果最右边的1后面还有0的话)。其余所有位将不会受到影响。 举个例子:一个二进制数1100,从右边数起第三位是处于最右边的一个1。减去1后,第三位变成0,它后面的两位0变成了1,而前面的1保持不变,因此得到的结果是1011.我们发现减1的结果是把最右边的一个1开始的所有位都取反了。这个时候如果我们再把原来的整数和减去1之后的结果做与运算,从原来整数最右边一个1那一位开始所有位都会变成0。如1100&1011=1000.也就是说,把一个整数减去1,再和原整数做与运算,会把该整数最右边一个1变成0.那么一个整数的二进制有多少个1,就可以进行多少次这样的操作。 60 | 61 | -------------------------------------------------------------------------------- /010-二进制中1的个数/problem010.go: -------------------------------------------------------------------------------- 1 | package problem010 2 | 3 | 4 | func Ones(n int) int { 5 | count := 0; 6 | for n!=0 { 7 | count++ 8 | n = n & (n-1) 9 | } 10 | return count 11 | } -------------------------------------------------------------------------------- /010-二进制中1的个数/problem010_test.go: -------------------------------------------------------------------------------- 1 | package problem010 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func Test_case(t *testing.T) { 8 | if Ones(0) == 0 { 9 | t.Log("Pass") 10 | } else { 11 | t.Error("Failed") 12 | } 13 | // int64 14 | if Ones(-1) == 64 { 15 | t.Log("Pass") 16 | } else { 17 | t.Error("Failed value: ",Ones(-1)) 18 | } 19 | 20 | if Ones(8) == 1 { 21 | t.Log("Pass") 22 | } else { 23 | t.Error("Failed") 24 | } 25 | 26 | 27 | if Ones(5) == 2 { 28 | t.Log("Pass") 29 | } else { 30 | t.Error("Failed") 31 | } 32 | 33 | } 34 | 35 | -------------------------------------------------------------------------------- /011-数值的整数次方/README.md: -------------------------------------------------------------------------------- 1 | # 题意 2 | 3 | > 给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方。 4 | 5 | 前面所说的指数为负数只是边界的一种情况,学习算法,必须全面了解所有的边界 6 | 7 | 指数幂的所有边界包括: 8 | 9 | - 指数为0的情况,不管底数是多少都应该是1 10 | - 指数为负数的情况,求出的应该是其倒数幂的倒数 11 | - 指数为负数的情况下,底数不能为0 12 | 13 | 正整数幂次可以用位运算来求 -------------------------------------------------------------------------------- /011-数值的整数次方/problem011.go: -------------------------------------------------------------------------------- 1 | package problem011 2 | 3 | import ( 4 | "errors" 5 | ) 6 | 7 | 8 | func Pow(base float64, exp int) (float64, error) { 9 | if exp == 0 { 10 | return 1, nil 11 | } 12 | if exp < 0 && base == 0 { 13 | return -1 , errors.New("base == 0 and exp < 0") 14 | } 15 | 16 | if exp > 0 { 17 | return PowNormal(base, exp), nil 18 | } else { 19 | res := PowNormal(base, -exp) 20 | res = 1/res 21 | return res, nil 22 | } 23 | 24 | } 25 | 26 | 27 | func PowNormal(base float64, exp int) float64 { 28 | res, temp := 1.0, base 29 | for exp != 0 { 30 | if exp&1 == 1 { 31 | res *= temp 32 | } 33 | temp *= temp 34 | exp >>= 1 35 | } 36 | return res 37 | } -------------------------------------------------------------------------------- /011-数值的整数次方/problem011_test.go: -------------------------------------------------------------------------------- 1 | package problem011 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func Test_case(t *testing.T) { 8 | res, _ := Pow(0, 0) 9 | if res == 1 { 10 | t.Log("Pass") 11 | } else { 12 | t.Error("Failed") 13 | } 14 | 15 | res, _ = Pow(2, -1) 16 | if res == 0.5 { 17 | t.Log("Pass") 18 | } else { 19 | t.Error("Failed value: ") 20 | } 21 | 22 | res, _ = Pow(2, 3) 23 | if res == 8 { 24 | t.Log("Pass") 25 | } else { 26 | t.Error("Failed") 27 | } 28 | 29 | res, _ = Pow(2, -2) 30 | if res == 0.25 { 31 | t.Log("Pass") 32 | } else { 33 | t.Error("Failed") 34 | } 35 | 36 | res, err := Pow(0, -2) 37 | if err != nil { 38 | t.Log("Pass") 39 | } else { 40 | t.Error("Failed") 41 | } 42 | 43 | } 44 | 45 | -------------------------------------------------------------------------------- /012-打印1到最大的N位数/README.md: -------------------------------------------------------------------------------- 1 | # 题意 2 | 3 | 给定一个数字N,打印从1到最大的N位数。 4 | 5 | 输入 6 | 7 | 每个输入文件仅包含一组测试样例。 8 | 9 | 对于每个测试案例,输入一个数字N(1<=N<=5)。 输出 对应每个测试案例,依次打印从1到最大的N位数。 10 | 11 | 样例输入 12 | 13 | 1 14 | 15 | 样例输出 16 | 17 | 1 2 3 4 5 6 7 8 9 18 | 19 | 这题其实没什么太大意义,控制好溢出就好了。 -------------------------------------------------------------------------------- /012-打印1到最大的N位数/problem012.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "errors" 6 | ) 7 | 8 | var MaxInt int = int(^uint(0) >> 1) 9 | 10 | // problem 011 11 | func Pow(base float64, exp int) (float64, error) { 12 | if exp == 0 { 13 | return 1, nil 14 | } 15 | if exp < 0 && base == 0 { 16 | return -1 , errors.New("base == 0 and exp < 0") 17 | } 18 | 19 | if exp > 0 { 20 | return PowNormal(base, exp), nil 21 | } else { 22 | res := PowNormal(base, -exp) 23 | res = 1/res 24 | return res, nil 25 | } 26 | 27 | } 28 | 29 | func PowNormal(base float64, exp int) float64 { 30 | res, temp := 1.0, base 31 | for exp != 0 { 32 | if exp&1 == 1 { 33 | res *= temp 34 | } 35 | temp *= temp 36 | exp >>= 1 37 | } 38 | return res 39 | } 40 | 41 | // 012 42 | func showN(n int) error { 43 | top, _ := Pow(10, n) 44 | 45 | if (int(top) < 0){ 46 | return errors.New("Overflow") 47 | } 48 | 49 | for i := 1; i < int(top); i++ { 50 | fmt.Println(i) 51 | } 52 | return nil 53 | } 54 | 55 | func main() { 56 | showN(1) 57 | showN(3) 58 | } 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /014-调整数组顺序使奇数位于偶数前面/README.md: -------------------------------------------------------------------------------- 1 | #题意 2 | 3 | 题目描述 4 | 5 | 输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。 6 | 7 | 样例输入 8 | 9 | 5 1 2 3 4 5 10 | 11 | 样例输出 12 | 13 | 1 3 5 2 4 14 | 15 | 下面我们考虑算法复杂度的同时还会考虑其稳定性,(排序的稳定型则是指相同元素在数组中的相对位置是否发生变化),这里的稳定性我们理解为,顺序交换后,各个奇数(或者偶数)在数组中的相对位置是否发生变化 16 | 17 | ## 不影响顺序 => 冒泡解法 18 | 最简单的思路就是从头到尾扫描一遍数组,每遇见一个偶数时,就拿出这个数字,并把位于这个数字之后的所有数字往前挪动一位,然后把当前这个偶数放到最后。 19 | 20 | 这样每次遇到一个偶数就要挪动$O(n)$个数字,因此总的时间复杂度是$O(n^2)$ 21 | 22 | 但是这种方法不仅暴力而且还需要复杂的挪动工作,因此我们对比一下冒泡排序,每次循环前偶后奇就交换 23 | 24 | 同时我们设立一个标识,来标识数组是否已经符合要求 25 | 26 | 当再次循环时,发现没有要交换的数据,说明数组已经符合要求 27 | 28 | ## 高效但是影响顺序 => 双指针 29 | 由于题目中只要求记奇数在偶数之前,那么我们在扫描这个数组的时候,如果发现一个偶数出现在奇数之前就交换他们的位置,这样一趟后就满足要求了。 30 | 31 | 因此我们 32 | 33 | 维护两个索引或者指针,一个指向数组的第一个元素,并向后移动,一个指向数组的最后一个元素,并向前移动。 34 | 35 | 如果第一个指针指向的元素是偶数,而第二个指针指向的元素是奇数,说明偶数在奇数前面,那么就交换这两个数。 36 | 37 | 直到两个指针相遇为止 -------------------------------------------------------------------------------- /014-调整数组顺序使奇数位于偶数前面/problem014.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | 8 | func oddFirst(s []int){ 9 | left, right := 0, len(s)-1 10 | for left < right { 11 | for s[right]%2==0 && left 1 && tail != nil { 18 | tail = tail.Next 19 | k-- 20 | } 21 | if tail == nil { 22 | return nil 23 | } 24 | for tail.Next != nil { 25 | tail = tail.Next 26 | head = head.Next 27 | } 28 | return head 29 | } 30 | 31 | func main() { 32 | //test 33 | l3 := &NodeList{3, nil} 34 | l2 := &NodeList{2, l3} 35 | l1 := &NodeList{1, l2} 36 | fmt.Println("1 -> 2 -> 3") 37 | 38 | fmt.Println("reverse 1th") 39 | fmt.Println(kthNode(l1, 1)) 40 | fmt.Println("reverse 2th") 41 | fmt.Println(kthNode(l1, 2)) 42 | fmt.Println("reverse 3th") 43 | fmt.Println(kthNode(l1, 3)) 44 | fmt.Println("reverse 4th") 45 | fmt.Println(kthNode(l1, 4)) 46 | 47 | } 48 | -------------------------------------------------------------------------------- /016-反转链表/README.md: -------------------------------------------------------------------------------- 1 | # 题意 2 | 3 | 输入一个链表,反转链表后,输出链表的所有元素。 4 | 5 | > 头插法或者三指针滑动 -------------------------------------------------------------------------------- /016-反转链表/problem016.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | type NodeList struct { 8 | Val int 9 | Next *NodeList 10 | } 11 | 12 | func reverse(head *NodeList) *NodeList { 13 | if head == nil || head.Next == nil { return head } 14 | vide := &NodeList{-1, nil} 15 | 16 | for head != nil { 17 | next := head.Next 18 | head.Next = vide.Next 19 | vide.Next = head 20 | head = next 21 | } 22 | 23 | return vide.Next 24 | 25 | } 26 | 27 | func print(head *NodeList){ 28 | for head!=nil{ 29 | fmt.Printf("%d -> ", head.Val) 30 | head=head.Next 31 | } 32 | } 33 | 34 | func main() { 35 | //test 36 | l3 := &NodeList{3, nil} 37 | l2 := &NodeList{2, l3} 38 | l1 := &NodeList{1, l2} 39 | fmt.Println("1 -> 2 -> 3") 40 | print(reverse(l1)) 41 | fmt.Println("\n") 42 | 43 | 44 | l2 = &NodeList{2, nil} 45 | l1 = &NodeList{1, l2} 46 | fmt.Println("1 -> 2 ->") 47 | print(reverse(l1)) 48 | fmt.Println("\n") 49 | 50 | 51 | l1 = &NodeList{1, nil} 52 | fmt.Println("1 -> ") 53 | print(reverse(l1)) 54 | fmt.Println("\n") 55 | 56 | } 57 | -------------------------------------------------------------------------------- /017-合并两个排序的链表/README.md: -------------------------------------------------------------------------------- 1 | # 题意 2 | 题目描述 3 | 4 | 输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。 5 | 6 | 样例输入 7 | 8 | 1 3 5 7 9 9 | 10 | 2 4 11 | 12 | 样例输出 13 | 14 | 1 2 3 4 5 7 9 -------------------------------------------------------------------------------- /017-合并两个排序的链表/problem017.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | type ListNode struct { 8 | Val int 9 | Next *ListNode 10 | } 11 | 12 | func print(head *ListNode) { 13 | for head!=nil{ 14 | fmt.Printf("%d -> ", head.Val) 15 | head=head.Next 16 | } 17 | } 18 | 19 | func mergeTwoLists(l1 *ListNode, l2 *ListNode) *ListNode { 20 | res := &ListNode{} 21 | cur := res 22 | for l1 != nil || l2 != nil{ 23 | if l1 == nil { 24 | cur.Next = l2 25 | break 26 | } else if l2 == nil { 27 | cur.Next = l1 28 | break 29 | } 30 | if l1.Val > l2.Val { 31 | cur.Next = l2 32 | cur = cur.Next 33 | l2 = l2.Next 34 | } else { 35 | cur.Next = l1 36 | cur = cur.Next 37 | l1 = l1.Next 38 | } 39 | } 40 | return res.Next 41 | } 42 | 43 | func main() { 44 | //test 45 | l3 := &ListNode{6, nil} 46 | l2 := &ListNode{4, l3} 47 | l1 := &ListNode{2, l2} 48 | 49 | print(l1) 50 | 51 | fmt.Println("\n") 52 | 53 | n3 := &ListNode{5, nil} 54 | n2 := &ListNode{3, n3} 55 | n1 := &ListNode{1, n2} 56 | 57 | print(n1) 58 | 59 | fmt.Println("\n") 60 | print(mergeTwoLists(l1, n1)) 61 | 62 | fmt.Println("\n") 63 | } 64 | -------------------------------------------------------------------------------- /018-树的子结构/README.md: -------------------------------------------------------------------------------- 1 | # 题意 2 | 3 | 4 | 输入两颗二叉树A,B,判断B是不是A的子结构。 5 | 6 | # 分析 7 | 8 | 要查找树A中是否存在和树B结构一样的子树,可以分成两步: 9 | 10 | 1.第一步在树A中找到和B的根节点的值一样的结点R; 这实际上就是树的遍历。可以用递归实现 11 | 12 | 递归调用HasSubTree遍历二叉树A。如果发现某一结点的值和树B的头结点的值相同,则转向第2步判断两个结点为根的数是否存在父子关系 13 | 14 | 2.第二步再判断树A中以R为根结点的子树是不是包含和树B一样的结构。 这个过程其实就是要要判断两棵树对应的节点数据是否相同。这个是一个递归的过程。 15 | 16 | 首先我们先实现第2步的操作,这个操作其实就是递归判断两个树对应节是否相同, 递归的终结是如果之前的节点均相同,最后子树为空时,而父树如果也是NULL,则说明两颗树完全一样,如果父树不是NULL,则子树是父树的一部分 17 | 18 | -------------------------------------------------------------------------------- /018-树的子结构/problem018.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | type TreeNode struct { 8 | Val int 9 | Left *TreeNode 10 | Right *TreeNode 11 | } 12 | 13 | 14 | 15 | func hasSubRootOrTree(p *TreeNode, c *TreeNode) bool { 16 | if c == nil { return true } 17 | if p == nil { return false } 18 | 19 | if p.Val == c.Val { 20 | if hasSub(p, c) { 21 | return true 22 | } 23 | } 24 | 25 | return hasSubRootOrTree(p.Left, c) || hasSubRootOrTree(p.Right, c) 26 | } 27 | 28 | func hasSub(p *TreeNode, c *TreeNode) bool { 29 | if c == nil { return true } 30 | if p == nil { return false } 31 | 32 | if p.Val != c.Val { 33 | return true 34 | } 35 | 36 | return hasSub(p.Left, c.Left) && hasSub(p.Right, c.Right) 37 | } 38 | 39 | func main() { 40 | //test 41 | root := &TreeNode{1, &TreeNode{2, &TreeNode{4, nil, nil}, &TreeNode{5, nil, nil}}, &TreeNode{3, nil, nil}} 42 | sub1 := &TreeNode{1, &TreeNode{2, nil, nil}, nil} 43 | sub2 := &TreeNode{2, &TreeNode{4, nil, nil}, &TreeNode{5, nil, nil}} 44 | sub3 := &TreeNode{2, nil, nil} 45 | 46 | fmt.Println(hasSubRootOrTree(root, sub1), hasSubRootOrTree(root, sub2), hasSubRootOrTree(root, sub3)) 47 | 48 | 49 | 50 | } 51 | -------------------------------------------------------------------------------- /019-二叉树的镜像/README.md: -------------------------------------------------------------------------------- 1 | # 题意 2 | 3 | 题目描述 4 | 5 | 操作给定的二叉树,将其变换为源二叉树的镜像。 6 | 7 | 输入描述 8 | 9 | 二叉树的镜像定义:源二叉树 10 | > 8 11 | > 6 10 12 | > 5 7 9 11 13 | 14 | 镜像二叉树 15 | > 8 16 | > 10 6 17 | > 11 9 7 5 -------------------------------------------------------------------------------- /019-二叉树的镜像/problem019.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | type TreeNode struct { 8 | Val int 9 | Left *TreeNode 10 | Right *TreeNode 11 | } 12 | 13 | 14 | 15 | func MirrorTree(p *TreeNode) { 16 | if p == nil { return } 17 | p.Left, p.Right = p.Right, p.Left 18 | MirrorTree(p.Left) 19 | MirrorTree(p.Right) 20 | } 21 | 22 | func Print(root *TreeNode){ 23 | if root == nil { return } 24 | fmt.Printf("%d ", root.Val) 25 | Print(root.Left) 26 | Print(root.Right) 27 | } 28 | 29 | func main() { 30 | //test 31 | root := &TreeNode{1, &TreeNode{2, &TreeNode{4, nil, nil}, &TreeNode{5, nil, nil}}, &TreeNode{3, nil, nil}} 32 | Print(root) 33 | fmt.Printf("\n") 34 | MirrorTree(root) 35 | Print(root) 36 | fmt.Printf("\n") 37 | 38 | root = &TreeNode{1, &TreeNode{2, nil, nil}, &TreeNode{3, nil, nil}} 39 | Print(root) 40 | fmt.Printf("\n") 41 | MirrorTree(root) 42 | Print(root) 43 | fmt.Printf("\n") 44 | 45 | } 46 | -------------------------------------------------------------------------------- /020-顺时针打印矩阵/README.md: -------------------------------------------------------------------------------- 1 | # 题意 2 | 3 | 输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字, 4 | 5 | 例如, 如果输入如下矩阵: 6 | 7 | 1 2 3 4 8 | 5 6 7 8 9 | 9 10 11 12 10 | 13 14 15 16 11 | 12 | 则依次打印出数字 13 | 14 | 1,2,3,4,8,12,16,15,14,13,9,5,6,7,11,10 15 | 16 | ## 参考Leetcode原题 17 | 18 | [LeetCode 54. Spiral Matrix](https://leetcode.com/problems/spiral-matrix/) 19 | 20 | ```golang 21 | // 根据题目要求写出规律,我们把螺旋遍历当作扒一层一层皮 22 | // 我们可以设置一个变量layer,用来记录当前在记录第几层。 23 | // 然后用四个变量来记录四个边界,按照同样的规律扒皮,最后扒完算是结束 24 | // 用元素个数保证正确性 25 | func spiralOrder(matrix [][]int) []int { 26 | var res []int 27 | n := len(matrix) 28 | if n==0 {return res} 29 | m := len(matrix[0]) 30 | // 计算元素个数 31 | nb := n * m 32 | // 初始为0层皮 33 | layer := 0 34 | // 初始边界 35 | startN, endN, startM, endM := 0, 0, 0, 0 36 | // 每放入一个元素,nb就减一,若nb==0,则说明扒皮结束 37 | for nb > 0 { 38 | // 按照layer数计算边界 39 | startN, endN = layer, n-layer-1 40 | startM, endM = layer, m-layer-1 41 | // 4个for来扒皮 42 | for i := startM; i <= endM && nb > 0; i++ { 43 | res = append(res, matrix[startN][i]) 44 | nb-- 45 | } 46 | for i := startN + 1; i <= endN && nb > 0; i++ { 47 | res = append(res, matrix[i][endM]) 48 | nb-- 49 | } 50 | for i := endM - 1; i >= startM && nb > 0; i-- { 51 | res = append(res, matrix[endN][i]) 52 | nb-- 53 | } 54 | for i := endN - 1; i >= startN+1 && nb > 0; i-- { 55 | res = append(res, matrix[i][startM]) 56 | nb-- 57 | } 58 | // 层数递增 59 | layer++ 60 | } 61 | return res 62 | } 63 | // -------------------------------------------------------------------------------- /021-包含min函数的栈/README.md: -------------------------------------------------------------------------------- 1 | # 题意 2 | 题目描述 3 | 4 | 定义栈的数据结构,请在该类型中实现一个能够得到栈最小元素的min函数。 5 | 6 | # 分析 7 | 思路很简单,我们维持两个栈, 8 | 9 | 数据栈data,存储栈的数据用于常规的栈操作 10 | 11 | 最小栈min,保存每次push和pop时候的最小值, 12 | 13 | 在push-data栈的时候,将当前最小数据压入, 14 | 15 | 在pop-data栈的时候,将min栈栈顶的最小数据弹出 16 | 17 | 这样保证min栈中存储着当前现场的最小值,并随着数据栈的更新而更新 18 | 19 | ## 参考Leetcode原题 20 | 21 | [LeetCode 155. Min Stack](https://leetcode.com/problems/min-stack/) 22 | 23 | ```golang 24 | // 易错点 25 | // 对于每一个的item的min,如果,前一个元素的min比自己的x还小,那么自己的min就储存之前的元素min,反之,元素min等于x 26 | 27 | // MinStack 是可以返回最小值的栈 28 | type MinStack struct { 29 | stack []item 30 | } 31 | type item struct { 32 | min, x int 33 | } 34 | 35 | // Constructor 构造 MinStack 36 | func Constructor() MinStack { 37 | return MinStack{} 38 | } 39 | 40 | // Push 存入数据 41 | func (s *MinStack) Push(x int) { 42 | min := x 43 | if len(s.stack) > 0 && s.GetMin() < x { 44 | min = s.GetMin() 45 | } 46 | s.stack = append(s.stack, item{min: min, x: x}) 47 | } 48 | 49 | // Pop 抛弃最后一个入栈的值 50 | func (s *MinStack) Pop() { 51 | s.stack = s.stack[:len(s.stack)-1] 52 | } 53 | 54 | // Top 返回最大值 55 | func (s *MinStack) Top() int { 56 | return s.stack[len(s.stack)-1].x 57 | } 58 | 59 | // GetMin 返回最小值 60 | func (s *MinStack) GetMin() int { 61 | return s.stack[len(s.stack)-1].min 62 | } 63 | 64 | ``` -------------------------------------------------------------------------------- /022-栈的压入弹出序列/README.md: -------------------------------------------------------------------------------- 1 | # 题意 2 | 题目描述 3 | 4 | 输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否为该栈的弹出顺序。 5 | 6 | 假设压入栈的所有数字均不相等。 7 | 8 | 例如序列1,2,3,4,5是某栈的压入顺序, 9 | 10 | 序列4,5,3,2,1是该压栈序列对应的一个弹出序列, 11 | 12 | 但4,3,5,1,2就不可能是该压栈序列的弹出序列。 13 | 14 | # 分析 15 | http://www.cnblogs.com/kaituorensheng/p/3618339.html http://blog.csdn.net/htyurencaotang/article/details/9266157 http://www.nowcoder.com/questionTerminal/d77d11405cc7470d82554cb392585106 http://www.cnblogs.com/tgkx1054/archive/2012/11/13/2769014.html 16 | 17 | # 辅助栈模拟入栈出栈过程 18 | 思路: 19 | 20 | 开辟一个辅助栈,模拟入栈出战过程(假设pa为入栈序列,pb为出战序列) 21 | 22 | pa中的元素依次压入辅助栈 23 | 24 | 新压入的元素与弹出序列的栈底相同,辅助栈弹出,同时pb向上移动 25 | 26 | 不相同了pa中的元素继续入辅助 27 | 28 | 如果下一个弹出的数字刚好是栈顶数字,则直接弹出。 29 | 30 | 若下一个弹出的数字不在栈顶,则把压栈序列中还没有入栈的数字压入辅助栈,直到把下一个需要弹出的数字压入栈顶为止。 31 | 32 | 若所有的数字都压入栈了仍没有找到下一个弹出的数字,则表明该序列不可能滴一个弹出序列。 -------------------------------------------------------------------------------- /022-栈的压入弹出序列/problem022.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "../utils" 6 | ) 7 | 8 | 9 | func stackOrder(in []int, out []int) bool { 10 | var s utils.Stack 11 | if len(out) != len(in) { return false } 12 | pi, po := 0, 0 13 | for pi=0; i--{ 13 | if post[i] < root { 14 | left = i 15 | break 16 | } 17 | } 18 | 19 | for _,v := range post[:left+1] { 20 | if v > root { 21 | return false 22 | } 23 | } 24 | 25 | return isPostOrder(post[0:left+1]) && isPostOrder(post[left+1:len(post)-1]) 26 | 27 | } 28 | 29 | func main() { 30 | //test 31 | post1 := []int{ 2, 9, 5, 16, 17, 15, 19, 18, 12 } 32 | post2 := []int{ 2, 13, 5, 16, 17, 15, 19, 18, 12 } 33 | post3 := []int{ 2, 9, 5, 16, 17, 15, 11, 18, 12 } 34 | 35 | fmt.Println(post1 ,"is a postOrder ? ", isPostOrder(post1)) 36 | fmt.Println(post2 ,"is a postOrder ? ", isPostOrder(post2)) 37 | fmt.Println(post3 ,"is a postOrder ? ", isPostOrder(post3)) 38 | fmt.Printf("\n") 39 | } -------------------------------------------------------------------------------- /025-二叉树中和为某一值的路径/README.md: -------------------------------------------------------------------------------- 1 | # 题意 2 | 3 | 题目描述 4 | 5 | 输入一颗二叉树和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径 6 | 7 | # 分析 8 | 9 | DFS 10 | 11 | ## 参考Leetcode原题 12 | 13 | [113. Path Sum II](https://leetcode.com/problems/path-sum-ii/) 14 | 15 | ```golang 16 | // 易错点: 17 | // 语法问题!!! 18 | // 公用solution的时候,记得递归前后恢复原样 19 | // 添加到res时注意深拷贝数组 20 | func pathSum(root *TreeNode, sum int) [][]int {//写个递归函数,从左到右把每条路线都试一下,有符合的就把路径加入paths 21 | var res [][]int 22 | solution := []int{} 23 | var dfs func(root *TreeNode, sum int) 24 | dfs = func(root *TreeNode, sum int){ 25 | if root == nil { return } 26 | rest := sum - root.Val 27 | if rest == 0 && root.Left == nil && root.Right == nil { 28 | solution = append(solution, root.Val) 29 | tmp := make([]int, len(solution)) 30 | copy(tmp, solution) 31 | res = append(res, tmp) 32 | solution = solution[:len(solution)-1] 33 | return 34 | } 35 | solution = append(solution, root.Val) 36 | dfs(root.Left, rest) 37 | dfs(root.Right, rest) 38 | solution = solution[:len(solution)-1] 39 | } 40 | dfs(root, sum) 41 | return res 42 | } 43 | ``` -------------------------------------------------------------------------------- /026-复杂链表的复制/README.md: -------------------------------------------------------------------------------- 1 | # 题意 2 | 3 | 题目描述 4 | 5 | 输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点)。 6 | 7 | 要求你编写函数复制这个复杂链表 8 | 9 | ## 分析 10 | 11 | 利用hashmap 12 | ```golang 13 | m := make(map[*RandNodeList]*RandNodeList) 14 | ``` 15 | 只需要遍历一次List,新旧两边的Node按照Next的顺序在hashmap中对应,遍历一次,map中有就链接,没有就创建后在链接。 -------------------------------------------------------------------------------- /026-复杂链表的复制/problem026.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | type RandNodeList struct { 8 | Val int 9 | Next *RandNodeList 10 | Rand *RandNodeList 11 | } 12 | 13 | func CopyRandRandNodeList(head *RandNodeList) *RandNodeList { 14 | if head == nil { return nil } 15 | m := make(map[*RandNodeList]*RandNodeList) 16 | cur := head 17 | for cur != nil { 18 | if _, ok := m[cur]; !ok { 19 | m[cur] = &RandNodeList{cur.Val, nil, nil} 20 | } 21 | if cur.Next != nil { 22 | if _, ok := m[cur.Next]; !ok { 23 | m[cur.Next] = &RandNodeList{cur.Next.Val, nil, nil} 24 | } 25 | m[cur].Next = m[cur.Next] 26 | } 27 | 28 | if cur.Rand != nil { 29 | if _, ok := m[cur.Rand]; !ok { 30 | m[cur.Rand] = &RandNodeList{cur.Rand.Val, nil, nil} 31 | } 32 | m[cur].Rand = m[cur.Rand] 33 | } 34 | cur = cur.Next 35 | } 36 | return m[head] 37 | } 38 | 39 | func print(head *RandNodeList){ 40 | for head!=nil{ 41 | fmt.Printf("%d Ram: %d -> ", head.Val, head.Rand.Val) 42 | head=head.Next 43 | } 44 | } 45 | 46 | func main() { 47 | //test 48 | l3 := &RandNodeList{3, nil, nil} 49 | l2 := &RandNodeList{2, l3, nil} 50 | l1 := &RandNodeList{1, l2, nil} 51 | l1.Rand = l3 52 | l2.Rand = l2 53 | l3.Rand = l1 54 | print(l1) 55 | fmt.Println("") 56 | fmt.Println("Copy: ") 57 | print(CopyRandRandNodeList(l1)) 58 | fmt.Println("") 59 | 60 | } 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /027-二叉搜索树与双向链表/README.md: -------------------------------------------------------------------------------- 1 | # 题意 2 | 3 | 题目描述 4 | 5 | 输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向。 6 | 7 | # 分析 8 | 二叉排序树的每个节点均由两个指针指向其两个孩子,双向链表中每个节点又都有两个指针指向其前驱和后继 9 | 10 | 二叉排序树的左节点的值 < 根结点的值 < 右子节点的值,其中序遍历就是一个排序好的信息串 11 | 12 | 因此我们可以通过如下两种方法来实现 13 | 14 | 中序遍历来实现二叉搜索树向双向链表的转换,访问过程需修改为链接操作 15 | 把左子树和右子树都转换成排序的双向链表之后再和根节点链接起来,整棵二叉搜索树就转换成了排序的双向链表 16 | 17 | #中序递归 18 | 19 | 采用中序遍历,而中序遍历中当前结点的前一个节点 20 | 21 | 要么是当前结点的左子树的的最右孩子 22 | 23 | 要么是当前结点其前一个节点的右孩子 24 | 25 | 对于第二种,我们好判断,但是对于第一种方式,无法快速的找到其左子树的最右孩子,因此我们链接的时候需要保存其前驱节点,我们称之为lastNode节点 -------------------------------------------------------------------------------- /027-二叉搜索树与双向链表/problem027.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | 8 | 9 | type TreeNode struct { 10 | Val int 11 | Left *TreeNode 12 | Right *TreeNode 13 | } 14 | 15 | func TreeToList(root *TreeNode) (*TreeNode, *TreeNode){ 16 | head, tail := root, root 17 | if root == nil { return head, tail } 18 | if root.Left == nil && root.Right == nil { 19 | head.Left, tail.Right = root, root 20 | return head, tail 21 | } 22 | if root.Left != nil { 23 | leftHead, leftTail := TreeToList(root.Left) 24 | head = leftHead 25 | root.Left = leftTail 26 | leftTail.Right = root 27 | } 28 | if root.Right != nil { 29 | rightHead, rightTail := TreeToList(root.Right) 30 | tail = rightTail 31 | root.Right = rightHead 32 | rightHead.Left = root 33 | } 34 | head.Left, tail.Right = head, tail 35 | return head, tail 36 | } 37 | 38 | 39 | 40 | func printRight(root *TreeNode){ 41 | for root.Right != root { 42 | fmt.Printf("%d ", root.Val) 43 | root = root.Right 44 | } 45 | fmt.Printf("%d ", root.Val) 46 | } 47 | 48 | func printLeft(root *TreeNode){ 49 | for root.Left != root { 50 | fmt.Printf("%d ", root.Val) 51 | root = root.Left 52 | } 53 | fmt.Printf("%d ", root.Val) 54 | } 55 | 56 | func main() { 57 | //test 58 | root := &TreeNode{5, nil, nil} 59 | l11 := &TreeNode{3, nil, nil} 60 | l12 := &TreeNode{8, nil, nil} 61 | l21 := &TreeNode{1, nil, nil} 62 | l22 := &TreeNode{4, nil, nil} 63 | l23 := &TreeNode{6, nil, nil} 64 | l24 := &TreeNode{9, nil, nil} 65 | root.Left, root.Right = l11, l12 66 | l11.Left, l11.Right = l21, l22 67 | l12.Left, l12.Right = l23, l24 68 | head, tail := TreeToList(root) 69 | printLeft(tail) 70 | fmt.Printf("\n") 71 | printRight(head) 72 | fmt.Printf("\n") 73 | 74 | fmt.Printf("\n") 75 | 76 | root = &TreeNode{3, &TreeNode{1, nil, nil}, &TreeNode{4, nil, nil}} 77 | head, tail = TreeToList(root) 78 | printLeft(tail) 79 | fmt.Printf("\n") 80 | printRight(head) 81 | fmt.Printf("\n") 82 | 83 | } 84 | -------------------------------------------------------------------------------- /028-字符串的排列/README.md: -------------------------------------------------------------------------------- 1 | #题意 2 | 3 | 题目描述 4 | 5 | 输入一个字符串,按字典序打印出该字符串中字符的所有排列 6 | 7 | 例如输入字符串abc, 8 | 9 | 则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。 10 | 11 | 结果请按字母顺序输出。 12 | 13 | 注意 输入一个字符串,长度不超过9(可能有字符重复),字符只包括大小写字母 14 | 15 | 样例输入 16 | 17 | >abc 18 | >BCA 19 | 20 | 样例输出 21 | 22 | >abc acb bac bca cab cba 23 | >ABC ACB BAC BCA CAB CBA 24 | 25 | # 分析 26 | 27 | 类似 [LeetCode 47. Permutations II](https://leetcode.com/problems/permutations-ii/) -------------------------------------------------------------------------------- /028-字符串的排列/problem028.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func stringPermutation(str string) []string { 8 | var res []string 9 | s := []byte(str) 10 | length := len(s) 11 | var dfs func(idx int) 12 | dfs = func(idx int){ 13 | if idx == length { 14 | str := string(s) 15 | res = append(res, str) 16 | return 17 | } 18 | m := make(map[byte]bool) 19 | for i:=idx; iKmax,则不更新数组。这样每次更新和不更新数组所用的时间为O(k)或O(0),整趟下来,总的时间复杂度平均下来为:nO(k) = O(nk);——方法二 17 | 18 | ## 方法三--最大堆 19 | 当然,更好的办法是维护k个元素的最大堆,原理与上述第2个方案一致,即用容量为K的最大堆存储最先遍历的K个数,并假设它们即是最小的K个数,建堆需要O(k)后,有k1)。继续遍历数列,每次遍历一个元素x,与堆顶元素比较,x),否则不更新堆。这样下来,总费时O(k+(n-k)logk) = O(nlogk)。此方法得益于在堆中,查找等各项操作时间复杂度均为logk(不然,就如上述思路2所述:直接用数组也可以找出前k个小的元素,用时O(nk)); 20 | 21 | ## 方法四--快速排序的分治划分(中位数作为枢轴) 22 | 按编程之美第141页上解法二的所述,类似快速排序的划分方法,N个数存储在数组S中,再从数组中随机选取一个数X(随机选取枢纽元,可做到线性期望时间O(N)的复杂度),把数组划分为Sa和Sb两部分,Sa<= X <=Sb,如果要查找的K个小的元素小于Sa中的元素个数,则返回Sa中较小的K个元素,否则返回Sa中K个小的元素 + Sb中小的K-|Sa|个元素。像上述过程一样,这个运用类似快速排序的partition的快速选择Select算法寻找最小的K个元素,在最坏的情况下亦能做到O(N)的复杂度。 23 | 24 | 不过值得一提的是,这个快速选择Select算法是选择数组中“中位数的中位数”作为枢纽元,而非随机选择枢纽元; 25 | 26 | ## 方法五--快速排序的分治划分(随机枢轴) 27 | Randomized-Select,每次都是随机选择数列中的一个元素作为主元,在O(n)的时间内找到第K小的元素,然后遍历输出前面的K个小的元素。如果能的话,那么总的时间复杂度为线性期望时间:O(n+k) = O(n)(当n比较小时); 28 | 29 | ## 方法六--线性排序 30 | 线性时间的排序,即计数排序,时间复杂度虽能达到O(n),但是,限制条件太多了,不常用; 31 | 32 | ## 方法七--最小堆与优先队列 33 | ”可以用最小堆初始化数组,然后取这个优先队列前k个值。复杂度为O(n)+kO(logn)“。意思是针对整个数组序列建立最小堆,建堆所用时间为O(n),然后取堆中的前k个数,即总的时间复杂度为:O(n+klogn)。 34 | 35 | ## 方法八--提取最小堆的元素 36 | 与上述思路7类似,不同的是在对元素数组原地建立最小堆O(n)后,然后提取K次,但是每次提取时,换到顶部的元素只需要下移顶多K次就足够了,下移次数逐次减少(而上述思路7每次提取都需要logn,所有提取K次,思路7需要K*logn,而本思路8只需要K^2); -------------------------------------------------------------------------------- /030-最小的K个数/problem030.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "errors" 6 | . "../utils" 7 | ) 8 | 9 | func minK(nums []int, k int) ([]int, error){ 10 | res := []int{} 11 | if k > len(nums) { 12 | return res, errors.New("k > length of nums") 13 | } 14 | 15 | maxHeap := NewMaxHeap() 16 | 17 | for _, v := range nums { 18 | if maxHeap.Length() < k { 19 | maxHeap.Insert(v) 20 | } else { 21 | max, _ := maxHeap.Max() 22 | if max > v { 23 | maxHeap.DeleteMax() 24 | maxHeap.Insert(v) 25 | } 26 | } 27 | } 28 | for maxHeap.Length()>0 { 29 | v, _ := maxHeap.DeleteMax() 30 | res = append(res, v) 31 | } 32 | return res, nil 33 | } 34 | 35 | func main() { 36 | test := []int{1,2,3,4,5,6,7,8,9} 37 | fmt.Println(test) 38 | fmt.Println(minK(test, 1)) 39 | fmt.Println(minK(test, 2)) 40 | fmt.Println(minK(test, 3)) 41 | fmt.Println(minK(test, 4)) 42 | fmt.Println(minK(test, 5)) 43 | } -------------------------------------------------------------------------------- /031-连续子数组的最大和/README.md: -------------------------------------------------------------------------------- 1 | # 题意 2 | 3 | 题目描述 4 | 5 | HZ偶尔会拿些专业问题来忽悠那些非计算机专业的同学。 6 | 7 | 今天测试组开完会后,他又发话了: 8 | 9 | 在古老的一维模式识别中,常常需要计算连续子向量的最大和,当向量全为正数的时候,问题很好解决。 10 | 11 | 但是,如果向量中包含负数,是否应该包含某个负数,并期望旁边的正数会弥补它呢? 12 | 13 | 例如:{6,-3,-2,7,-15,1,2,2}, 连续子向量的最大和为8(从第0个开始,到第3个为止)。 你会不会被他忽悠住? 14 | 15 | 16 | # 分析见题目注释 17 | 18 | Leecode指路[LeetCode 53. Maximum Subarray 19 | ](https://leetcode.com/problems/maximum-subarray/submissions/) 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /031-连续子数组的最大和/problem031.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | // dp[i] 代表以i结尾时最大连续子数组的最大和 8 | // dp[i] 初始值为nums[0] 9 | // 计算dp[i]时 10 | // 如果dp[i-1]>0: 那我们放心的加进来所以dp[i] = nums[i]+dp[i-1] 11 | // 否则:dp[i]取自己 dp[i] = nums[i] 12 | // 再利用全局变量,保存出出现过的最大值 13 | func MaxSubset(nums []int) int { 14 | if len(nums) == 0 { return 0 } 15 | dp := make([]int, len(nums)) 16 | dp[0] = nums[0] 17 | res := nums[0] 18 | for i:=1; i 0 { 20 | dp[i] = nums[i]+dp[i-1] 21 | } else { 22 | dp[i] = nums[i] 23 | } 24 | 25 | if dp[i] > res { 26 | res = dp[i] 27 | } 28 | } 29 | return res 30 | } 31 | 32 | // 精简一些 33 | // dp[i] 代表以i结尾时最大连续子数组的最大和(同时) 34 | // 复制nums到dp 35 | // 如果dp[i-1]>0那就把dp[i]=dp[i-1]+dp[i] 36 | // 再利用全局变量,保存出出现过的最大值 37 | 38 | func MaxSubset2(nums []int) int { 39 | if len(nums) == 0 { return 0 } 40 | dp := make([]int, len(nums)) 41 | copy(dp, nums) 42 | max := dp[0] 43 | for i := 1; i < len(dp); i++ { 44 | if dp[i-1]>0{ 45 | dp[i] = dp[i-1]+dp[i] 46 | } 47 | if dp[i]>max { 48 | max = dp[i] 49 | } 50 | } 51 | return max 52 | } 53 | 54 | func max(a,b int) int { 55 | if a > b { 56 | return a 57 | } 58 | return b 59 | } 60 | 61 | func main() { 62 | test := []int{1,-2,3,10,-4,7,2,-5} 63 | fmt.Println(test) 64 | fmt.Println(MaxSubset(test)) 65 | fmt.Println(MaxSubset2(test)) 66 | } -------------------------------------------------------------------------------- /032-从1到n整数中1出现的次数/README.md: -------------------------------------------------------------------------------- 1 | # 题意 2 | 3 | 题目描述 4 | 5 | 从1到n整数中1出现的次数 6 | 7 | # 分治递归 8 | 9 | 我们重新分析下这个问题, 10 | 11 | 对于任意一个个位数n,只要n>=1,它就包含一个"1"; 12 | 13 | n<1,即n=0时,则包含的"1"的个数为0。 14 | 15 | 于是我们考虑用分治的思想将任意一个n位数不断缩小规模分解成许多个个位数,这样求解就很方便。 16 | 17 | 但是,我们该如何降低规模? 18 | 19 | 仔细分析,我们会发现, 20 | 21 | **任意一个n位数中"1"的个位可以分解为两个n-1位数中"1"的个数的和,最后再加上一个与最高位数相关的常数C** 22 | 例如, 23 | 24 | 对于n=12,可以拆分为0109,1012,即 f(12) = f(10 - 1) + f(12 - 10) + 3,其中3是表示最高位为1的数字个数,这里就是10,11,12, 25 | 对于n=132,可以拆分为099,100132,即f(132)=f(100 -1) + f(132 - 100) + 33,33代表最高位为1的数字的个数,这里就是100-132百位数字的1出新了33次. 26 | 27 | 对于232,可以拆分为099,100232,即f(232) = 2\*f(100 - 1) + f(32) + 100,因为232大于199,所以它包括了所有最高位为1的数字即100-199,共100个。 28 | 29 | 综上,我们分析得出,最后加的常数C只跟最高位n1是否为1有关 30 | 31 | 当最高位为1时,常数C为原数字N去掉最高位后剩下的数字+1,如N=12时,$C = 2 + 1 = 3$,N=132时,$C = 32 + 1 = 33$ 32 | 33 | 当最高位大于1时,常数C为$10^(bit-1)$,其中bit为N的位数,如N=232时,bit=3,$C = 10^(bit-1) = 10^2 = 100$。 于是,我们可以列出递归方程如下: 34 | 35 | if(n1 == 1) 36 | f(n) = f(10bit-1) + f(n - 10bit) + n - 10bit+ 1; 37 | else 38 | f(n) = n1\*f(10bit-1) + f(n – n1\*10bit) + 10bit; 39 | 进一步可以归结为 40 | 41 | f(n) = n1\*f(10bit-1) + f(n – n1\*10bit) + LEFT; 42 | 其中 43 | if(n1 == 1) 44 | LEFT = n - 10bit+ 1; 45 | else 46 | LEFT = 10bit; 47 | 48 | 此算法的优点是不用遍历1-N就可以得到f(N)。经过我测试,此算法的运算速度比解法一快了许多许多,数字在1010内时,算法都可以在毫秒级内结 49 | 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /032-从1到n整数中1出现的次数/problem032.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | // f(n) = n1*f(10bit-1) + f(n – n1*10bit) + LEFT; 8 | // 其中 9 | // if(n1 == 1) 10 | // LEFT = n - 10bit+ 1; 11 | // else 12 | // LEFT = 10bit; 13 | 14 | func Ones(n int) int { 15 | if n == 0 { 16 | return 0 17 | } 18 | if n > 1 && n < 10 { 19 | return 1 20 | } 21 | count := 0 22 | highest := n 23 | bit := 0 24 | for highest >= 10 { 25 | highest /= 10 26 | bit++ 27 | } 28 | 29 | weight := pow(10, bit) 30 | if highest == 1 { 31 | count = Ones(weight - 1) + Ones(n - weight) + ( n - weight + 1) 32 | } else { 33 | count = Ones(weight - 1) + Ones(n - highest*weight) + weight 34 | } 35 | 36 | return count 37 | 38 | } 39 | 40 | func pow(a,b int) int { 41 | res := 1 42 | for i := b; i >0 ; i-- { 43 | res *= a 44 | } 45 | return res 46 | } 47 | 48 | func main() { 49 | 50 | fmt.Println("10 has ", Ones(10)) 51 | fmt.Println("15 has ", Ones(15)) 52 | fmt.Println("20 has ", Ones(20)) 53 | fmt.Println("100 has ", Ones(100)) 54 | fmt.Println("120 has ", Ones(120)) 55 | } 56 | 57 | 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /033-把数组排成最小的数/README.md: -------------------------------------------------------------------------------- 1 | # 题意 2 | 题目描述 3 | 4 | 输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。 输入 例如输入数组 5 | 6 | {3,32,321} 7 | 8 | 输出 9 | 10 | 则打印出这三个数字能排成的最小数字为 11 | 12 | 321323 13 | 14 | # 快排+判断大小的函数 -------------------------------------------------------------------------------- /033-把数组排成最小的数/problem033.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | ) 7 | 8 | func getMinStr(nums []int) string { 9 | quickSort(nums, 0, len(nums)-1) 10 | noZero := 0 11 | for nums[noZero] == 0 { 12 | noZero++ 13 | } 14 | res := "" 15 | fmt.Println(nums) 16 | for i := noZero; i <= len(nums)-1; i++ { 17 | res += strconv.Itoa(nums[i]) 18 | } 19 | return res 20 | 21 | } 22 | 23 | // 要点: 24 | // 注意sup的条件,确定一个数应该在前面还是后面 25 | func quickSort(nums []int, left int, right int) { 26 | if left < right { 27 | tmp := nums[left] 28 | l, r := left, right 29 | for { 30 | // 先从右向左!!! 31 | for l < r && sup(nums[r],tmp) { 32 | r-- 33 | } 34 | for l < r && inf(nums[l],tmp) { 35 | l++ 36 | } 37 | if l >= r { 38 | break 39 | } 40 | nums[l], nums[r] = nums[r], nums[l] 41 | } 42 | nums[left], nums[l] = nums[l], nums[left] 43 | quickSort(nums, left, l-1) 44 | quickSort(nums, l+1, right) 45 | } 46 | } 47 | 48 | func sup(a, b int) bool { 49 | aStr := strconv.Itoa(a) 50 | bStr := strconv.Itoa(b) 51 | 52 | if aStr+bStr >= bStr+aStr { 53 | return true 54 | } 55 | return false 56 | } 57 | 58 | func inf(a, b int) bool { 59 | aStr := strconv.Itoa(a) 60 | bStr := strconv.Itoa(b) 61 | if aStr+bStr <= bStr+aStr { 62 | return true 63 | } 64 | return false 65 | } 66 | 67 | type bytes [][]byte 68 | 69 | func (b bytes) Less(i, j int) bool { 70 | size := len(b[i]) + len(b[j]) 71 | 72 | bij := make([]byte, 0, size) 73 | bij = append(bij, b[i]...) 74 | bij = append(bij, b[j]...) 75 | 76 | bji := make([]byte, 0, size) 77 | bji = append(bji, b[j]...) 78 | bji = append(bji, b[i]...) 79 | 80 | for k := 0; k < size; k++ { 81 | if bij[k] > bji[k] { 82 | return true 83 | } else if bij[k] < bji[k] { 84 | return false 85 | } 86 | } 87 | 88 | return false 89 | } 90 | 91 | 92 | func main() { 93 | list := []int{10, 9, 9, 111, 111, 222,9, 7, 7, 6, 5, 5, 4, 4, 3, 2, 1, 0, 1, 2, 3, 4, 5} 94 | fmt.Println(getMinStr(list)) 95 | list = []int{3,30,34,5,9} 96 | fmt.Println(getMinStr(list)) 97 | } 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | -------------------------------------------------------------------------------- /034-丑数/README.md: -------------------------------------------------------------------------------- 1 | # 题意 2 | 题目描述 3 | 4 | 把只包含因子2、3和5的数称作丑数(Ugly Number)。 5 | 6 | 例如6、8都是丑数,但14不是,因为它包含因子7。 习惯上我们把1当做是第一个丑数。求按从小到大的顺序的第N个丑数。 7 | 8 | # 寻找所有的丑数 9 | 根据丑数的定义,丑数应该是另一个丑数乘以2、3或者5的结果(1除外)。 因此我们可以创建一个数组,里面的数字是排好序的丑数。里面的每一个丑数是前面的丑数乘以2、3或者5得到的。那关键就是确保数组里的丑数是有序的了。 我们假设数组中已经有若干个丑数,排好序后存在数组中。我们把现有的最大丑数记做M。 现在我们来生成下一个丑数,该丑数肯定是前面某一个丑数乘以2、3或者5的结果。 我们首先考虑把已有的每个丑数乘以2。在乘以2的时候,能得到若干个结果小于或等于M的。由于我们是按照顺序生成的,小于或者等于M肯定已经在数组中了,我们不需再次考虑; 我们还会得到若干个大于M的结果,但我们只需要第一个大于M的结果,因为我们希望丑数是按从小到大顺序生成的,其他更大的结果我们以后再说。 我们把得到的第一个乘以2后大于M的结果,记为M2。同样我们把已有的每一个丑数乘以3和5,能得到第一个大于M的结果M3和M5。那么下一个丑数应该是M2、M3和M5三个数的最小者。 -------------------------------------------------------------------------------- /034-丑数/problem034.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func getUgly(N int) []int { 8 | res := make([]int, N) 9 | if N < 1 { return res } 10 | res[0] = 1 11 | index2, index3, index5 := 0, 0, 0 12 | index := 1 13 | for index <= N-1 { 14 | minValue := min(min(res[index2]*2, res[index3]*3), res[index5]*5) 15 | if minValue == res[index2]*2{ 16 | index2++ 17 | } 18 | // 不是elseif 因为可能重复 19 | if minValue == res[index3]*3{ 20 | index3++ 21 | } 22 | if minValue == res[index5]*5{ 23 | index5++ 24 | } 25 | res[index] = minValue 26 | index++ 27 | } 28 | return res 29 | } 30 | 31 | func min(a,b int) int { 32 | if aa[j],i < j。那么在排序的过程中,会把a[i]和a[j]交换过来,这个交换的过程,每交换一次,就是一个逆序对的“正序”过程。 19 | 20 | ## 归并排序 -------------------------------------------------------------------------------- /036-数组中的逆序对/problem036.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func InversePairs(nums []int) int { 8 | // 用来存储排序好的数组 9 | tmp := make([]int, len(nums)) 10 | 11 | // 合并两个排序好的array 12 | var merge func (start int, mid int, end int) int 13 | merge = func (start int, mid int, end int) int { 14 | if start >= end { return 0 } 15 | p1, p2 := mid, end 16 | k, count := 0, 0 17 | for p1 >= start && p2 >= mid+1 { 18 | if nums[p1] <= nums[p2] { 19 | tmp[k] = nums[p2] 20 | p2-- 21 | k++ 22 | } else { 23 | // nums[p1] > nums[p2] 24 | // 因为两个数组是排好序的,所以说明对于num[p1]来说 25 | // 至少有p2-mid个逆序对 26 | tmp[k] = nums[p1] 27 | count += p2 - mid 28 | p1-- 29 | k++ 30 | } 31 | } 32 | for p1 >= start { 33 | tmp[k] = nums[p1] 34 | k++ 35 | p1-- 36 | } 37 | for p2 >= mid+1 { 38 | tmp[k] = nums[p2] 39 | k++ 40 | p2-- 41 | } 42 | for i := 0; i <= k-1; i++ { 43 | nums[end-i] = tmp[i] 44 | } 45 | 46 | return count 47 | } 48 | var sort func (start, end int) int 49 | sort = func (start, end int) int { 50 | count := 0 51 | if start < end { 52 | mid := (start+end)/2 53 | count += sort(start, mid) 54 | count += sort(mid+1, end) 55 | count += merge(start, mid, end) 56 | return count 57 | } 58 | return 0 59 | } 60 | 61 | return sort(0, len(nums)-1) 62 | } 63 | 64 | 65 | func main() { 66 | test := []int{7,5,6,4} 67 | fmt.Println(test) 68 | fmt.Println(" InversePairs: ", InversePairs(test)) 69 | 70 | test = []int{7,6,5,4} 71 | fmt.Println(test) 72 | fmt.Println(" InversePairs: ", InversePairs(test)) 73 | } 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /037-两个链表的第一个公共结点/README.md: -------------------------------------------------------------------------------- 1 | # 题意 2 | 题目描述 3 | 4 | 输入两个链表,找出它们的第一个公共结点。 5 | 6 | # 暴力方法 7 | 最简单直接的方法就是,对于第一个链表的每个节点,我们依次判断其是不是第二条链表的公共结点 8 | 9 | # 右对齐两个链表 10 | 11 | 如果两个链表有公共节点,则它们的形状必然是一个Y字形。 12 | 13 | ### 长链表先走,实现右对齐 14 | 15 | 先假设这两个链表的长度相等,则我们可以同步遍历这两个链表,找到公共节点。现在有两个链表,我们可以先分别求齐长度得其差n,然后遍历长的那个链表n个节点,然后同步遍历这两个链表即可。 16 | 17 | ### hashmap存储遍历第一条链的节点,遍历另一条,寻找已经出现的key  18 | 19 | -------------------------------------------------------------------------------- /037-两个链表的第一个公共结点/problem037.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | type LinkNode struct { 8 | Val int 9 | Next *LinkNode 10 | } 11 | 12 | // 长链表先走,实现右对齐 13 | func firstCommon(h1, h2 *LinkNode) *LinkNode { 14 | start, l1 := h1, 0 15 | for start != nil { 16 | start = start.Next 17 | l1++ 18 | } 19 | start, l2 := h2, 0 20 | for start != nil { 21 | start = start.Next 22 | l2++ 23 | } 24 | 25 | s1, s2 := h1, h2 26 | if l1 > l2 { 27 | diff := l1-l2 28 | for s1 != nil && diff >0 { 29 | s1 = s1.Next 30 | diff-- 31 | } 32 | } else if l1 < l2 { 33 | diff := l2-l1 34 | for s2 != nil && diff >0 { 35 | s2 = s2.Next 36 | diff-- 37 | } 38 | } 39 | for s1!=nil && s2!=nil && s1!=s2 { 40 | s1 = s1.Next 41 | s2 = s2.Next 42 | } 43 | return s1 44 | } 45 | 46 | // Hashmap 47 | func firstCommonMap(h1, h2 *LinkNode) *LinkNode { 48 | m := make(map[*LinkNode]bool) 49 | for h1 != nil { 50 | m[h1] = true 51 | h1 = h1.Next 52 | } 53 | 54 | for h2 != nil { 55 | if _, ok := m[h2]; ok { 56 | return h2 57 | } 58 | h2 = h2.Next 59 | } 60 | return h2 61 | } 62 | 63 | func main() { 64 | comm := &LinkNode{666, &LinkNode{1, &LinkNode{2, nil}}} 65 | h1 := &LinkNode{-2, &LinkNode{-1, comm}} 66 | h2 := &LinkNode{-4, &LinkNode{-3, &LinkNode{-2, comm}}} 67 | h3 := &LinkNode{-6, comm} 68 | 69 | fmt.Println("Comm: ", (firstCommonMap(h1,h2))) 70 | fmt.Println("Comm: ", (firstCommonMap(h3,h2))) 71 | fmt.Println("Comm: ", (firstCommonMap(h3,h1))) 72 | 73 | fmt.Println("Comm: ", (firstCommon(h1,h2))) 74 | fmt.Println("Comm: ", (firstCommon(h3,h2))) 75 | fmt.Println("Comm: ", (firstCommon(h3,h1))) 76 | } 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | -------------------------------------------------------------------------------- /038-数字在排序数组中出现的次数/README.md: -------------------------------------------------------------------------------- 1 | # 题意 2 | 题目描述 3 | 4 | 统计一个数字在排序数组中出现的次数。 5 | 6 | # 暴力方法 7 | 由于数组是有序的,因此我么通过一次遍历,对要查找的元素直接计数就可以了 8 | 9 | # 二分查找(递归和非递归) 10 | 我们通过二分查找到指定的元素K后,然后再分别向前和向后查找总的个数 -------------------------------------------------------------------------------- /038-数字在排序数组中出现的次数/problem038.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | // Binary search 8 | func count(nums []int, target int) int { 9 | 10 | left, right := 0, len(nums)-1 11 | var mid int 12 | for left < right { 13 | mid = (left + right) / 2 14 | if nums[mid] == target { 15 | for nums[mid] != nums[right] { 16 | right-- 17 | } 18 | for nums[mid] != nums[left] { 19 | left++ 20 | } 21 | break 22 | } 23 | if nums[mid] > target { 24 | left = mid + 1 25 | } else { 26 | right = mid - 1 27 | } 28 | } 29 | if left < right { 30 | return right-left+1 31 | } 32 | return -1 33 | } 34 | 35 | func main() { 36 | test := []int{1,2,3,4,4,4,4,5,6,7,8} 37 | fmt.Println("Count 4 in ", test, ": ", count(test, 4)) 38 | test = []int{4,4,4,4} 39 | fmt.Println("Count 4 in ", test, ": ", count(test, 4)) 40 | test = []int{1,2,3,4,4,4,4,6,7,8} 41 | fmt.Println("Count 5 in ", test, ": ", count(test, 5)) 42 | } 43 | -------------------------------------------------------------------------------- /039-二叉树的深度/README.md: -------------------------------------------------------------------------------- 1 | # 题意 2 | 3 | 题目描述 4 | 5 | 输入一棵二叉树,求该树的深度。 6 | 7 | 从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径的长度为树的深度。 8 | 9 | # 分析 10 | 11 | 对二叉树进行层次遍历,维护一个层数计数器,每次进入一层就增加1,从而得到二叉树的层数。 当然如果使用递归的话,思路就更简单了,返回左右子树中深度最大的那个 -------------------------------------------------------------------------------- /039-二叉树的深度/problem039.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | type TreeNode { 8 | Val int 9 | Left *TreeNode 10 | Right *TreeNode 11 | } 12 | 13 | func maxDepth(root *TreeNode) int { 14 | max := 0 15 | var dfs func(root *TreeNode, level int) 16 | dfs = func(root *TreeNode, level int){ 17 | if root == nil { 18 | return 19 | } 20 | if level > max { 21 | max = level 22 | } 23 | dfs(root.Left, level+1) 24 | dfs(root.Right, level+1) 25 | } 26 | dfs(root, 1) 27 | return max 28 | } 29 | 30 | // 测试地址 31 | // https://leetcode.com/problems/maximum-depth-of-binary-tree/ 32 | 33 | -------------------------------------------------------------------------------- /039-平衡二叉树[附加]/README.md: -------------------------------------------------------------------------------- 1 | # 题意 2 | 3 | 输入一棵二叉树,判断该二叉树是否是平衡二叉树。 4 | 5 | # 递归法 6 | 7 | 根据平衡二叉树的定义 8 | 9 | 平衡二叉树要求对于每一个节点来说,它的左右子树的高度之差不能超过1 10 | 11 | 因此我们递归的判断每一个根节点,判断左右子树的高度差 -------------------------------------------------------------------------------- /039-平衡二叉树[附加]/problem039.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | // 递归的思想 8 | // 易错点 9 | // 多指返回 定义 var recur func(root *TreeNode) (int, bool) 10 | // 返回值第一个为深度,第二个为是否平衡 11 | // 12 | // 只要遇到以下情况就返回false 13 | // 1.左右子树只要有一个不平衡 14 | // 2.左右子树深度相差大于一 15 | // 16 | // 注意返回当前深度时为 max(ldepth, rdepth)+1 17 | func isBalanced(root *TreeNode) bool { 18 | var recur func(node *TreeNode) (int, bool) 19 | recur = func(node *TreeNode) (int, bool) { 20 | if node == nil { return 0, true } 21 | ldepth, lbalance := recur(node.Left) 22 | rdepth, rbalance := recur(node.Right) 23 | if !lbalance || !rbalance || abs(ldepth-rdepth)>1 { 24 | return max(ldepth, rdepth)+1, false 25 | } 26 | return max(ldepth, rdepth)+1, true 27 | } 28 | _, balanced := recur(root) 29 | return balanced 30 | } 31 | 32 | // 精简版 33 | func isBalanced(root *TreeNode) bool { 34 | if root == nil { return true } 35 | var recur func(root *TreeNode) (int, bool) 36 | recur = func(root *TreeNode) (int, bool) { 37 | if root == nil { return 0, true } 38 | rightD, rightB := recur(root.Right) 39 | leftD, leftB := recur(root.Left) 40 | return max(rightD, leftD)+1, abs(rightD-leftD)<=1 && rightB && leftB 41 | } 42 | _, res := recur(root) 43 | return res 44 | } 45 | 46 | func abs(a int) int { 47 | if a < 0 { 48 | return -a 49 | } 50 | return a 51 | } 52 | 53 | func max(a, b int) int { 54 | if a > b { 55 | return a 56 | } 57 | return b 58 | } 59 | 60 | 61 | // 测试地址 62 | // https://leetcode.com/problems/balanced-binary-tree/ 63 | -------------------------------------------------------------------------------- /040-数组中只出现一次的数字/README.md: -------------------------------------------------------------------------------- 1 | # 题意 2 | 题目描述 3 | 4 | 一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字 5 | 6 | 样例输入 7 | 8 | 2 4 3 6 3 2 5 5 9 | 10 | 样例输出 11 | 12 | 4 6 13 | 14 | ## 分析 15 | 16 | 我们在答案里直接解答多个变种 17 | 18 | - 一个整型数组里除了一个数字之外,其他的数字都出现了两次 19 | - 一个整型数组里除了一个数字之外,其他的数字都出现了三次 20 | - 一个整型数组里除了两个数字之外,其他的数字都出现了两次 21 | -------------------------------------------------------------------------------- /040-数组中只出现一次的数字/problem040.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | // 题目:一个整型数组里除了一个数字之外,其他的数字都出现了两次 8 | // 证明:我们把每个比特分开来看,有题目可知,每个元素出现两次,只有一个元素出现一次,那么,我们只需要保留每一位比特的出现单数次的符号就可以了 9 | // 比如: [4, 1, 2, 1, 2] 用二进制表示 10 | // 0 1 0 0 11 | // 0 0 0 1 12 | // 0 0 1 0 13 | // 0 0 0 1 14 | // 0 0 1 0 15 | // 所以 第一位0出现单数次,我们取0 16 | // 第二位, 1 出现单数次, 我们取1 17 | // 第三位,0 出现单数次,我们取0 18 | // 第四位,0 出现单数次,我们取0 19 | // 所以 结果位 0100, 以上的操作用xor按顺序运行过来就可以。 20 | // 测试地址 21 | // https://leetcode.com/problems/single-number/ 22 | func singleNumber(nums []int) int { 23 | res := 0 24 | for _, n := range nums { 25 | // n^n == 0 26 | // a^b^a^b^a == a 27 | res ^= n 28 | } 29 | return res 30 | } 31 | 32 | 33 | // 一个整型数组里除了一个数字之外,其他的数字都出现了三次 34 | // 如果是出现两次的话,用一个 bit 就可以 35 | // 比如 ones,初始为0 36 | // 当 5 第 1 次出现, ones = 5 37 | // 当 5 第 2 次出现, ones 清空为 0,表示 ones 可以去处理其他数字了 38 | // 所以,最后 如果 ones != 0的话, ones 记录的就是只出现了一次的那个数字 39 | 40 | // 公式是 ones = ones xor i 41 | 42 | // 那么,如果是三次的话,香农定理,需要用 2 bits 进行记录 43 | 44 | // 当 5 第 1 次出现的时候,ones = 5, twos = 0, ones 记录这个数字 45 | // 当 5 第 2 次出现的时候,ones = 0, twos = 5, twos 记录了这个数字 46 | // 当 5 第 3 次出现的时候,ones = 0, twos = 0, 都清空了,可以去处理其他数字了 47 | // 所以,如果有某个数字出现了 1 次,就存在 ones 中,出现了 2 次,就存在 twos 中 48 | 49 | // 公式方面, 上面 2 次的时候,ones 清空的公式是 ones = ones xor i 50 | // 而第 3 次时, ones 要等于零, 而这时 twos 是 True , 所以再 & 一个 twos 的非就可以, ones = (ones xor i) & ~twos 51 | // 所以,总的公式是 52 | // ones = (ones xor i) & ~twos 53 | // twos = (twos xor i) & ~ones 54 | // 测试地址 55 | // https://leetcode.com/problems/single-number-ii/ 56 | func singleNumber(nums []int) int { 57 | ones, twos := 0, 0 58 | for i := 0; i < len(nums); i++ { 59 | ones = (ones ^ nums[i]) & ^twos 60 | twos = (twos ^ nums[i]) & ^ones 61 | } 62 | return ones 63 | } 64 | 65 | // 一个整型数组里除了两个数字之外,其他的数字都出现了两次 66 | // 此题考察的是异或运算的特点:即两个相同的数异或结果为0。 67 | // 此题用了两次异或运算特点: 68 | // 第一次使用异或运算,得到了两个只出现一次的数相异或的结果。 69 | // 因为两个只出现一次的数肯定不同,即他们的异或结果一定不为0,一定有一个位上有1。 70 | // 另外一个此位上没有1,我们可以根据此位上是否有1,将整个数组重新划分成两部分, 71 | // 一部分此位上一定有1,另一部分此位上一定没有1, 72 | // 然后分别对每部分求异或,因为划分后的两部分有这样的特点: 73 | // 其他数都出现两次,只有一个数只出现一次。 74 | // 因此,我们又可以运用异或运算,分别得到两部分只出现一次的数。 75 | 76 | func singleNumber(nums []int) []int { 77 | xor := 0 78 | for _,v := range nums { 79 | xor ^= v 80 | } 81 | 82 | // 取xor最低位为1的数 83 | lowest := xor & -xor 84 | 85 | a,b := 0,0 86 | for _, v := range nums { 87 | if lowest&v == 0 { 88 | a ^= v 89 | } else { 90 | b ^= v 91 | } 92 | } 93 | return []int{a,b} 94 | } 95 | // 测试地址 96 | // https://leetcode.com/problems/single-number-iii/ 97 | -------------------------------------------------------------------------------- /041-和为S的两个数字/README.md: -------------------------------------------------------------------------------- 1 | # 题目描述 2 | 3 | 输入一个递增排序的数组和一个数字s,在数组中查找两个数,得它们的和正好是s。如果有多对数字的和等于s,输出乘积最小的即可。 4 | 5 | 举例说明 6 | 7 | 例如输入数组{1 、2 、4、7 、11 、15 }和数字15. 8 | 9 | 由于4+ 11 = 15 ,因此输出4 和11 。 10 | -------------------------------------------------------------------------------- /041-和为S的两个数字/problem041.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | // 把target-v当作key存入下次遇到target-v就说明找到了 8 | func twoSum(nums []int, target int) []int { 9 | m := make(map[int]int) 10 | for i, v := range nums { 11 | if val, ok := m[v]; ok { 12 | return []int{val, i} 13 | } 14 | m[target-v] = i 15 | } 16 | return []int{} 17 | } 18 | // 测试地址 19 | // https://leetcode.com/problems/two-sum/ 20 | 21 | 22 | -------------------------------------------------------------------------------- /041-和为S的连续正数序列/README.md: -------------------------------------------------------------------------------- 1 | # 和为S的连续正数序列 2 | 3 | 解题思路 4 | 5 | 考虑用两个数start和end分别表示序列的最小值和最大值。首先把start初始化为1, end初始化为2。如果从start到end的序列的和大于s,我们可以从序列中去掉较小的值,也就是增大start的值。如果从start到end的序列的和小于s,我们可以增大big,让这个序列包含更多的数字。因为这个序列至少要有两个数字,我们一直增加start到(1+s)/2 为止。 6 | 7 | 以求和为9 的所有连续序列为例,我们先把start初始化为1, end初始化为2。此时介于start和end之间的序列是{1,2},序列的和为3,小于9,所以我们下一步要让序列包含更多的数字。我们把end增加1 变成3,此时序列为{ I, 2,坷。由于序列的和是6,仍然小于9,我们接下来再增加end变成4,介于start和end之间的序列也随之变成{ l, 2, 3, 4}。由于列的和10 大于9,我们要删去去序列中的一些数字, 于是我们增加start变成2,此时得到的序列是{2, 3, 4}, 序列的和E好是9。我们找到了第一个和为9 的连续序列,把它打印出来。接下来我们再增加big,重复前面的过程,可以找到第二个和为9 的连续序列{4,5}。 8 | 9 | -------------------------------------------------------------------------------- /041-和为S的连续正数序列/problem041.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func arraySum(target int) []int{ 8 | if target < 3 { return []int{} } 9 | start, end := 1, 2 10 | sum := 3 11 | for end <= target/2+1 { 12 | if sum == target { 13 | break 14 | } else if sum < target { 15 | end++ 16 | sum += end 17 | } else { 18 | sum -= start 19 | start++ 20 | } 21 | } 22 | res := []int{} 23 | if sum == target { 24 | for i:=start; i<=end; i++{ 25 | res = append(res, i) 26 | } 27 | } 28 | return res 29 | } 30 | 31 | func main() { 32 | fmt.Println("5 : ", arraySum(5)) 33 | fmt.Println("100 : ", arraySum(100)) 34 | } 35 | 36 | -------------------------------------------------------------------------------- /042-左旋转字符串/README.md: -------------------------------------------------------------------------------- 1 | # 题意 2 | 题目描述 3 | 4 | 汇编语言中有一种移位指令叫做循环左移(ROL) 5 | 6 | 现在有个简单的任务,就是用字符串模拟这个指令的运算结果。 对于一个给定的字符序列S,请你把其循环左移K位后的序列输出。 7 | 8 | 例如,字符序列S=”abcXYZdef”, 要求输出循环左移3位后的结果,即“XYZdefabc”。 9 | 10 | 是不是很简单?OK,搞定它! 11 | 12 | ## 直接找到旋转后的对应关系 13 | 14 | 我们可以很明显的发现,将一个字符串循环左移,那么新串和原来串位的对应关系就如下 15 | 16 | 新串i位置的元素,其实是原来串i+n位置的元素, 17 | 当然i+n如果超过了字符串的长度,就会被循环移位到左侧,即(i+n) % str.size() 18 | 另外,如果移位n超过了字符串长度,那么只需要移动n%str.size()位即可 19 | 20 | ## 通过翻转直线循环移位 21 | 22 | 通过reverse操作 一个序列abcdefg,如果向左循环移动n位, 例如3位,则会编程(defg)(abc),我们把原序列分成两部分,A=(abc),B=(defg),原序列为AB,我们要的结果为BA,则可以这么做:(ArBr)r = ((B)r)r((A)r)r=BA 假设原序列有n位,循环左移i位的过程如下: 23 | 24 | reverse(0,i-1); 25 | 26 | reverse(i,n-1); 27 | 28 | reverse(1,n-1); 29 | 30 | 例如原序列:abcdefg,循环左移3位: 31 | 32 | abc defg -=> cba defg 33 | 34 | cba defg -=> cba gfed 35 | 36 | cba gfed -=> defg abc 37 | 代码就非常简单了,而且reverse操作非常简单,效率高也不容易出错,要记住一点就是STL中的迭代器是左闭右开区间,所以reverse操作的第二个参数需要往后移动一位 -------------------------------------------------------------------------------- /042-左旋转字符串/problem042.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func rotateString(str string, shift int) string { 8 | shift = shift%len(str) 9 | list := []byte(str) 10 | reverseString(list[:shift]) 11 | reverseString(list[shift:]) 12 | reverseString(list) 13 | return string(list) 14 | } 15 | 16 | func reverseString(s []byte) { 17 | start, end := 0, len(s)-1 18 | for start <= len(s)/2 -1 { 19 | s[start], s[end] = s[end], s[start] 20 | start++ 21 | end-- 22 | } 23 | } 24 | 25 | func main() { 26 | 27 | fmt.Println("1234567 " + " Rotate 3 : " + rotateString("1234567", 3)) 28 | fmt.Println("1234567 " + " Rotate 4 : " + rotateString("1234567", 4)) 29 | fmt.Println("1234567 " + " Rotate 5 : " + rotateString("1234567", 5)) 30 | fmt.Println("1234567 " + " Rotate 6 : " + rotateString("1234567", 6)) 31 | fmt.Println("1234567 " + " Rotate 7 : " + rotateString("1234567", 7)) 32 | fmt.Println("1234567 " + " Rotate 8 : " + rotateString("1234567", 8)) 33 | } 34 | 35 | -------------------------------------------------------------------------------- /042-翻转单词顺序列/README.md: -------------------------------------------------------------------------------- 1 | # 题意 2 | 3 | 牛客最近来了一个新员工Fish,每天早晨总是会拿着一本英文杂志,写些句子在本子上。同事Cat对Fish写的内容颇感兴趣,有一天他向Fish借来翻看,但却读不懂它的意思。例如,“student. a am I”。后来才意识到,这家伙原来把句子单词的顺序翻转了,正确的句子应该是“I am a student.”。Cat对一一的翻转这些单词顺序可不在行,你能帮助他么? 4 | 5 | 样例输入 6 | 7 | student. a am I 8 | 9 | I'm a Freshman and I like JOBDU! 10 | 11 | 样例输出 12 | 13 | I am a student. 14 | 15 | JOBDU! like I and Freshman a I'm 16 | 17 | # 先翻转所有字符,再逐个单词翻转 18 | 19 | 这道题主要思想就是先翻转所有字符,再逐个单词翻转 20 | 21 | 然后是如何查找到每个单词,用指针left和right维护单词的起始位置和结束位置 22 | 23 | 如果当前遇到一个空格,说明发现了一个单词 24 | 25 | 如果找到了单词的最后,而末尾字符不是空格,说明字符串的末尾也是一个单词 -------------------------------------------------------------------------------- /042-翻转单词顺序列/problem042.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func reverseWord(str string) string { 8 | list := []byte(str) 9 | reverseString(list) 10 | left, right := 0, 0 11 | for right<=len(list)-1 { 12 | if list[right] != ' ' { 13 | right++ 14 | continue 15 | } 16 | reverseString(list[left:right]) 17 | left, right = right+1, right+1 18 | } 19 | reverseString(list[left:right]) 20 | return string(list) 21 | } 22 | 23 | func reverseString(s []byte) { 24 | start, end := 0, len(s)-1 25 | for start <= len(s)/2 -1 { 26 | s[start], s[end] = s[end], s[start] 27 | start++ 28 | end-- 29 | } 30 | } 31 | 32 | func main() { 33 | 34 | fmt.Println("student. a am I" + " ===》 " + reverseWord("student. a am I")) 35 | fmt.Println("I'm a Freshman and I like JOBDU!" + " ===》 " + reverseWord("I'm a Freshman and I like JOBDU!")) 36 | 37 | } 38 | 39 | -------------------------------------------------------------------------------- /044-扑克牌顺子/README.md: -------------------------------------------------------------------------------- 1 | # 题意 2 | 题目描述 3 | 4 | LL今天心情特别好,因为他去买了一副扑克牌,发现里面居然有2个大王,2个小王(一副牌原本是54张) 5 | 6 | 他随机从中抽出了5张牌,想测测自己的手气,看看能不能抽到顺子,如果抽到的话,他决定去买体育彩票 ,嘿嘿!!“红心A,黑桃3,小王,大王,方片5”, “Oh My God!”不是顺子.....LL不高兴了,他想了想,决定大\小王可以看成任何数字,并且A看作1,J为11,Q为12,K为13。 上面的5张牌就可以变成“1,2,3,4,5”(大小王分别看作2和4),“So Lucky!”。 7 | 8 | LL决定去买体育彩票啦。 现在,要求你使用这幅牌模拟上面的过程,然后告诉我们LL的运气如何。为了方便起见,你可以认为大小王是0。 9 | 10 | 样例输入 11 | 12 | 3 5 1 0 4 13 | 14 | 3 5 4 7 6 15 | 16 | 3 5 7 4 8 17 | 18 | 样例输出 19 | 20 | So Lucky! 21 | 22 | So Lucky! 23 | 24 | Oh My God! 25 | 26 | # 排序后看0能不能填补空缺 27 | 28 | 可以把5张牌看成由5个数字组成的数组。大、小王是特殊的数字,我们不妨把它们定义为0,这样就能和其他扑克牌区分开来了。 29 | 30 | 接下来我们分析怎样判断5个数字是不是连续的,最直观的方法是把数组排序。值得注意的是,由于0可以当成任意数字,我们可以用0去补满数组中的空缺。如果排序之后的数组不是连续的,即相邻的两个数字相隔若干个数字,但只要我们有足够的0可以补满这两个数字的空缺,这个数组实际上还是连续的。举个例子,数组排序之后为{0,1,3,4,5},在1和3之间空缺了一个2,刚好我们有一个0,也就是我们可以把它当成2去填补这个空缺。 31 | 32 | 于是我们需要做3件事: 33 | 34 | 首先把数组排序 35 | 36 | 再统计数组中的0的个数 37 | 38 | 最后统计排序之后的数组中相邻数字之间的空缺总数。 39 | 40 | 如果空缺的总数小于或者等于0的个数,那么这个数组就是连续的;反之则不连续。 41 | 42 | 最后,我们还需要注意一点: 43 | 44 | 如果数组中的非0数字重复出现,则该数组不是连续的。 45 | 46 | ### 但是我们考虑一下子题目中特殊条件对顺子有什么要求 47 | 48 | 条件: 5张牌,顺子,除0之外不能重复 49 | 50 | 结论: 非0元素的极差(最大值最小值的差)不超过4, 非0元素不重复 51 | 52 | 非0元素的极差不查过4 53 | 首先5张牌的顺子,即使包含了0,那么最大值和最小值的差值肯定不超过4,否则的话一定不是顺子 54 | 55 | 比如{ 1,2,3,4,6 },最大最小值的差(极差)为6-1=5>4 56 | 57 | 正好满足的情况,{1, 2, 3, 4, 5},极差刚好是4 58 | 59 | 可以填补的情况,0填补的位置分四种情况,前面 {0, 2, 3, 4, 5},,中间{1, 2, 0, 4, 5}和结尾{1, 2, 3, 4, 0,},以及混合情况,这些情况下极差都不可能超过4 60 | 61 | 这个比较好判断,我们维护一个非0的最大最小值,那么判断极差即可 62 | 63 | 非0元素不重复 64 | 接着是怎么判断非0元素是否重复呢? 65 | 66 | 我们可以通过位运算,设置一个标识flag 67 | 68 | -------------------------------------------------------------------------------- /044-扑克牌顺子/problem044.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sort" 6 | ) 7 | 8 | func poker(nums []int) string { 9 | sort.Ints(nums) 10 | start := 0 11 | for nums[start] == 0 { 12 | start++ 13 | } 14 | for i := start+1; i < len(nums); i++{ 15 | if nums[i] - nums[i-1] -1 > start || nums[i] == nums[i-1] { 16 | return "Oh my god" 17 | } 18 | start -= (nums[i] - nums[i-1] -1) 19 | fmt.Println(i, start) 20 | } 21 | if start >= 0 { 22 | return "So lucky" 23 | } 24 | return "Oh my god" 25 | } 26 | 27 | func main() { 28 | arr1 := []int{ 1, 3, 2, 6, 4 }; 29 | fmt.Println(arr1, " => ", poker(arr1)) 30 | 31 | arr2 := []int{ 3, 5, 1, 0, 4, }; 32 | fmt.Println(arr2, " => ", poker(arr2)) 33 | 34 | arr3 := []int{ 1, 0, 0, 1, 0 }; 35 | fmt.Println(arr3, " => ", poker(arr3)) 36 | } 37 | 38 | -------------------------------------------------------------------------------- /045-孩子们的游戏(圆圈中最后剩下的数)/README.md: -------------------------------------------------------------------------------- 1 | # 题意 2 | 题目描述 3 | 4 | 每年六一儿童节,NowCoder都会准备一些小礼物去看望孤儿院的小朋友,今年亦是如此。 5 | 6 | HF作为NowCoder的资深元老,自然也准备了一些小游戏。 7 | 8 | 其中,有个游戏是这样的: 9 | 10 | 首先,让小朋友们围成一个大圈。然后,他随机指定一个数m,让编号为0的小朋友开始报数。 11 | 12 | 每次喊到m的那个小朋友要出列唱首歌,然后可以在礼品箱中任意的挑选礼物,并且不再回到圈中,从他的下一个小朋友开始,继续0...m-1报数....这样下去....直到剩下最后一个小朋友,可以不用表演, 13 | 14 | 并且拿到NowCoder名贵的“名侦探柯南”典藏版(名额有限哦!!^_^)。 15 | 16 | 请你试着想下,哪个小朋友会得到这份礼品呢? 17 | 18 | # 递推公式-获取最后的胜利者的序号 19 | 无论是用链表实现还是用数组实现都有一个共同点:要模拟整个游戏过程,不仅程序写起来比较烦,而且时间复杂度高达$O(nm)$,当n,m非常大(例如上百万,上千万)的时候,几乎是没有办法在短时间内出结果的。我们注意到原问题仅仅是要求出最后的胜利者的序号,而不是要读者模拟整个过程。因此如果要追求效率,就要打破常规,实施一点数学策略。 20 | 21 | 为了讨论方便,先把问题稍微改变一下,并不影响原意: 我们知道第一个人(编号一定是(m-1)) 出列之后,剩下的n-1个人组成了一个新的约瑟夫环(以编号为k=m mod n的人开始): 22 | 23 | k k+1 k+2 ... n-2,n-1,0,1,2,... k-2 24 | 25 | 并且从k开始报0。 我们把他们的编号做一下转换: 26 | 27 | k --> 0 28 | k+1 --> 1 29 | k+2 --> 2 30 | ... 31 | ... 32 | k-2 --> n-2 33 | 34 | 变换后就完完全全成为了(n-1)个人报数的子问题,假如我们知道这个子问题的解:例如x是最终的胜利者,那么根据上面这个表把这个x变回去不刚好就是n个人情况的解吗?!!变回去的公式很简单,相信大家都可以推出来:x'=(x+k) mod n 如何知道(n-1)个人报数的问题的解?对,只要知道(n-2)个人的解就行了。(n-2)个人的解呢?当然是先求(n-3)的情况 ---- 这显然就是一个倒推问题!好了,思路出来了,下面写递推公式: 35 | 令f表示i个人玩游戏报m退出最后胜利者的编号,最后的结果自然是f[n] 36 | 37 | 递推公式 38 | 39 | 让f[i]为i个人玩游戏报m退出最后的胜利者的编号,最后的结果自然是f[n] 40 | 41 | f[1] = 0; f[i] = (f[i - 1] + m) mod i; 42 | 43 | 有了这个公式,我们要做的就是从1-n顺序算出f的数值,最后结果是f[n]。 44 | 45 | 注意我们的编号是从0开始,如果从1开始可以返回f[n] + 1 46 | 47 | -------------------------------------------------------------------------------- /045-孩子们的游戏(圆圈中最后剩下的数)/problem045.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func cycle(n,m int) int { 8 | if n < 1 || m < 1 { 9 | return -1 10 | } else if n == 1{ 11 | return 0 12 | } else { 13 | // F[n] = (F[n - 1] + m) % n 14 | return (cycle(n-1,m) + m) % n 15 | } 16 | } 17 | 18 | func main() { 19 | 20 | fmt.Println(3,2, " => ", cycle(3,2)) 21 | 22 | fmt.Println(4,2, " => ", cycle(4,2)) 23 | 24 | fmt.Println(5,2, " => ", cycle(5,2)) 25 | } 26 | -------------------------------------------------------------------------------- /046-求1+2+3+...+n/README.md: -------------------------------------------------------------------------------- 1 | # 题意 2 | 题目描述 3 | 4 | 求1+2+3+...+n, 5 | 6 | 要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)。 7 | 8 | 样例输入 9 | 10 | 3 11 | 12 | 5 13 | 14 | 样例输出 15 | 16 | 6 17 | 18 | 15 19 | 20 | # 递归+短路判断终止 21 | 计算1+2+3+...+n, 可以认为是一个递归的过程, 这点很容易理解 22 | 23 | 但是怎么不用分支判断来保证递归的终止呢 24 | 25 | 通过短路运算0&&cout使条件为假值,从而不执行输出语句,那么我们也可以通过短路来实现循环终止,从n开始递减进程递归的相加运算当递归至0时使递归短路即可。 26 | 27 | 代码如下 28 | ```java 29 | class Solution 30 | { 31 | public: 32 | int SumRecursion(int n) 33 | { 34 | int ans = n; 35 | //debug < 101 7 -=> 111 31 | 32 | 相加各位的值,不算进位,得到010,二进制每位相加就相当于各位做异或操作,101^111=010 33 | 34 | 计算进位值,得到1010,相当于各位做与操作得到101,再向左移一位得到1010,(101&111)<<1。 35 | 36 | 重复上述两步, 各位相加 010^1010=1000,进位值为100=(010&1010)<<1。 37 | 38 | 继续重复上述两步:1000^100 = 1100,进位值为0,跳出循环,1100为最终结果。 39 | 40 | -------------------------------------------------------------------------------- /047-不用加减乘除做加法/problem047.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func Add(n, m int) int { 8 | tmp := 0 9 | for m != 0 { 10 | tmp = n ^ m 11 | m = (n & m) << 1 12 | n = tmp 13 | } 14 | return n 15 | } 16 | 17 | func main() { 18 | 19 | fmt.Println("5+7: ", Add(5, 7)) 20 | fmt.Println("50+70: ", Add(50, 70)) 21 | fmt.Println("15+7: ", Add(15, 7)) 22 | } 23 | -------------------------------------------------------------------------------- /048-不能被继承的类/README.md: -------------------------------------------------------------------------------- 1 | # 题意 2 | 3 | 题目描述 4 | 5 | 设计一个不能被继承的类 6 | 7 | ## golang: 臣妾做不到 -------------------------------------------------------------------------------- /049-把字符串转换成整数/README.md: -------------------------------------------------------------------------------- 1 | #题意 2 | 3 | 将一个字符串转换成一个整数,要求不能使用字符串转换整数的库函数。 4 | 5 | 6 | [LeetCode 8. String to Integer (atoi)](https://leetcode.com/problems/string-to-integer-atoi/) 7 | 8 | ``` 9 | Example 1: 10 | 11 | Input: "42" 12 | Output: 42 13 | ``` 14 | ``` 15 | Example 2: 16 | 17 | Input: " -42" 18 | Output: -42 19 | Explanation: The first non-whitespace character is '-', which is the minus sign. Then take as many numerical digits as possible, which gets 42. 20 | ``` 21 | ``` 22 | Example 3: 23 | 24 | Input: "4193 with words" 25 | Output: 4193 26 | Explanation: Conversion stops at digit '3' as the next character is not a numerical digit. 27 | ``` 28 | ``` 29 | Example 4: 30 | 31 | Input: "words and 987" 32 | Output: 0 33 | Explanation: The first non-whitespace character is 'w', which is not a numerical digit or a +/- sign. Therefore no valid conversion could be performed. 34 | ``` 35 | -------------------------------------------------------------------------------- /049-把字符串转换成整数/problem049.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math" 6 | ) 7 | 8 | func myAtoi(str string) int { 9 | res := 0 10 | flag := 1 11 | start := 0 12 | // space 13 | for start <= len(str)-1 && str[start] == ' ' { 14 | start++ 15 | } 16 | // +/- 17 | if start <= len(str)-1 { 18 | if str[start] == '-' { 19 | flag = -1 20 | start++ 21 | } else if str[start] == '+' { 22 | start++ 23 | } 24 | } 25 | // is digital ? 26 | for start <= len(str)-1 && isDigital(str[start]) { 27 | if res == 0 { 28 | res += int(str[start] - '0') 29 | start++ 30 | } else { 31 | res = res*10 + int((str[start] - '0')) 32 | start++ 33 | } 34 | // overflow int32 35 | if res*flag > math.MaxInt32 { 36 | return math.MaxInt32 37 | } else if res*flag < math.MinInt32 { 38 | return math.MinInt32 39 | } 40 | } 41 | 42 | res *= flag 43 | return res 44 | } 45 | 46 | func isDigital(b byte) bool { 47 | if b <= '9' && b >= '0' { 48 | return true 49 | } 50 | return false 51 | } 52 | 53 | func main() { 54 | 55 | fmt.Println(" 42", " => ", myAtoi(" 42")) 56 | fmt.Println(" 42asdfsdf", " => ", myAtoi(" 42asdfsdf")) 57 | fmt.Println(" -42asdfsdf", " => ", myAtoi(" -42asdfsdf")) 58 | fmt.Println("9223372036854775808", " => ", myAtoi("9223372036854775808")) 59 | fmt.Println("-9223372036854775808", " => ", myAtoi("-9223372036854775808")) 60 | 61 | } 62 | -------------------------------------------------------------------------------- /051-数组中重复的数字/README.md: -------------------------------------------------------------------------------- 1 | # 题意 2 | 3 | 在一个长度为n的数组里的所有数字都在0到n-1的范围内。 4 | 5 | 数组中某些数字是重复的,但不知道有几个数字是重复的。 6 | 7 | 也不知道每个数字重复几次。请找出数组中任意一个重复的数字。 8 | 9 | 例如,如果输入长度为7的数组{2,3,1,0,2,5,3},那么对应的输出是重复的数字2或者3。 10 | 11 | 样例输入 12 | 13 | 2, 3, 1, 0, 2, 5, 3 14 | 15 | 2, 1, 3, 1, 4 16 | 17 | 样例输出 18 | 19 | 2 20 | 21 | 1 22 | 23 | 24 | # Hashtable -------------------------------------------------------------------------------- /051-数组中重复的数字/problem051.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func again(nums []int) int { 8 | appear := make(map[int]bool) 9 | for _, v := range nums { 10 | if _, ok := appear[v]; ok { 11 | return v 12 | } 13 | appear[v] = true 14 | } 15 | // Not Found Error 16 | return len(nums) + 1 17 | 18 | } 19 | 20 | func main() { 21 | 22 | list := []int{2, 3, 1, 0, 2, 5, 3} 23 | fmt.Println(list, " => ", again(list)) 24 | list = []int{2, 1, 3, 1, 4} 25 | fmt.Println(list, " => ", again(list)) 26 | 27 | } 28 | -------------------------------------------------------------------------------- /052-构建乘积数组/README.md: -------------------------------------------------------------------------------- 1 | #题意 2 | 3 | 给定一个数组A[0,1,...,n-1],请构建一个数组B[0,1,...,n-1],其中B中的元素B[i]=A[0]A[1]...*A[i-1]A[i+1]...*A[n-1]。 4 | 5 | **不能使用除法。** 6 | 7 | 样例输入 8 | 9 | [1, 2, 3, 4, 5] 10 | 11 | 样例输出 12 | 13 | [120, 60, 40, 30, 24] 14 | 15 | # 分析 16 | 那么对于新数组ans[i],我们从前往后遍历,可求得了A[1]A[2]...\*A[i-1], 17 | 18 | 然后我们再从尾到头扫描一遍,对于当前第i位,我们任然按照前面的思想,累乘A[len-1]\*...A[i+1] 19 | 20 | 例如:A[]={1,2,3}求B[] 21 | 22 | B[0]=A[1]×A[2]=2×3=6 23 | 24 | B[1]=A[0]×A[2]=1×3=3 25 | 26 | B[2]=A[0]×A[1]=1×2=2 27 | 28 | B[0]初始化为1,从下标i=1开始,先求出C[i]的值并放入B[i],即B[i]=C[i]=C[i-1]×A[i-1],所以B[1]=B[1-1]×A[1-1]=B[0]×A[0]=1×1=1,i++ 29 | 30 | B[2]=B[2-1]×A[2-1]=B[1]×A[1]=1×2=2,i++超出长度停止循环 31 | 32 | C[i]计算完毕求D[i],设置一个临时变量temp初始化为1 33 | 34 | 从后往前变量数组,LengthA=3初始化i=LengthA-2=1,结束条件为i>=0 35 | 36 | 第一次循环,temp=temp×A[i+1]=1×A[2]=3,计算出A中最后一个元素的值放入temp,temp相当于D[i]的值 37 | 38 | 因为之前的B[i]=C[i],所以让B[i]×D[i]就是要保存的结果,即B[i]=B[1]=B[1]×temp=1×3=3,i–=0 39 | 40 | 计算B[i]=B[0],temp上一步中的值是A[2],在这次循环中temp=temp×A[0+1]=A[2]×A[1]=3×2=6 41 | 42 | B[i]=B[0]=B0]×temp=1×6=6,i–<0循环结束 43 | 44 | 所以B数组为{6,3,2} 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /052-构建乘积数组/problem052.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func multiArray(nums []int) []int { 8 | res := make([]int, len(nums)) 9 | tmp := 1 10 | for i := range res { 11 | res[i] = tmp 12 | tmp *= nums[i] 13 | } 14 | 15 | tmp = 1 16 | for i := range res { 17 | res[len(res)-1-i] *= tmp 18 | tmp *= nums[len(res)-1-i] 19 | } 20 | 21 | return res 22 | 23 | } 24 | 25 | func main() { 26 | 27 | list := []int{1,2,3,4,5} 28 | fmt.Println(list, " => ", multiArray(list)) 29 | 30 | 31 | } 32 | -------------------------------------------------------------------------------- /053-正则表达式匹配/README.md: -------------------------------------------------------------------------------- 1 | # 题意 2 | 题目描述 3 | 4 | 请实现一个函数用来匹配包括'.'和''的正则表达式。模式中的字符'.'表示任意一个字符,而''表示它前面的字符可以出现任意次(包含0次)。 在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串"aaa"与模式"a.a"和"abaca"匹配,但是与"aa.a"和"ab\*a"均不匹配 5 | 6 | 样例输入 7 | 8 | "a","ab\*a" 9 | 10 | 样例输出 11 | 12 | false 13 | 14 | [LeetCode 10. Regular Expression Matching](https://leetcode.com/problems/regular-expression-matching/) -------------------------------------------------------------------------------- /053-正则表达式匹配/problem053.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | // DP 8 | func isMatch(s, p string) bool { 9 | sSize := len(s) 10 | pSize := len(p) 11 | 12 | dp := make([][]bool, sSize+1) 13 | for i := range dp { 14 | dp[i] = make([]bool, pSize+1) 15 | } 16 | 17 | /* dp[i][j] 代表了 s[:i] 能否与 p[:j] 匹配 */ 18 | 19 | dp[0][0] = true 20 | /** 21 | * 根据题目的设定, "" 可以与 "a*b*c*" 相匹配 22 | * 所以,需要把相应的 dp 设置成 true 23 | */ 24 | for j := 1; j < pSize && dp[0][j-1]; j += 2 { 25 | if p[j] == '*' { 26 | dp[0][j+1] = true 27 | } 28 | } 29 | 30 | for i := 0; i < sSize; i++ { 31 | for j := 0; j < pSize; j++ { 32 | if p[j] == '.' || p[j] == s[i] { 33 | /* p[j] 与 s[i] 可以匹配上,所以,只要前面匹配,这里就能匹配上 */ 34 | dp[i+1][j+1] = dp[i][j] 35 | } else if p[j] == '*' { 36 | /* 此时,p[j] 的匹配情况与 p[j-1] 的内容相关。 */ 37 | if p[j-1] != s[i] && p[j-1] != '.' { 38 | /** 39 | * p[j] 无法与 s[i] 匹配上 40 | * p[j-1:j+1] 只能被当做 "" 41 | */ 42 | dp[i+1][j+1] = dp[i+1][j-1] 43 | } else { 44 | /** 45 | * p[j] 与 s[i] 匹配上 46 | * p[j-1;j+1] 作为 "x*", 可以有三种解释 47 | */ 48 | dp[i+1][j+1] = dp[i+1][j-1] || /* "x*" 解释为 "" */ 49 | dp[i+1][j] || /* "x*" 解释为 "x" */ 50 | dp[i][j+1] /* "x*" 解释为 "xx..." */ 51 | } 52 | } 53 | } 54 | } 55 | 56 | return dp[sSize][pSize] 57 | } 58 | 59 | // https://leetcode.com/problems/regular-expression-matching 60 | -------------------------------------------------------------------------------- /054-表示数值的字符串/README.md: -------------------------------------------------------------------------------- 1 | # 题目描述 2 | 请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。 3 | 4 | 例如, 字符串"+100","5e2","-123","3.1416"和"-1E-16"都表示数值。 但是"12e","1a3.14","1.2.3","+-5"和"12e+4.3"都不是。 5 | 6 | # 分析 7 | 我们首先分析一下子可能是数值的字符串的格式 在数值之前可能有一个表示正负的’-‘或者’+’。 接下来是若干个0到9的数位表示数值的整数部分(在某些小数里可能没有数值的整数部分)。 如果数值是一个小数,那么在小数点后面可能会有若干个0到9的数位表示数值的小数部分。如果数值用科学计数法表示,接下来是一个’e’或者‘E’,以及紧跟着的一个整数(可以有正负号)表示指数。    判断一个字符串是否符合上述模式时, 8 | 9 | 首先看第一个字符是不是正负号。 10 | 如果是,在字符串上移动一个字符,继续扫描剩余的字符串中0到9的数位。 11 | 如果是一个小数,则将遇到小数点。 12 | 另外,如果是用科学计数法表示的数值,在整数或者小数的后面还有可能遇到’e’或者’E’。 -------------------------------------------------------------------------------- /054-表示数值的字符串/problem054.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func isNumber(str string) bool { 8 | if len(str) == 0 { 9 | return false 10 | } 11 | start := 0 12 | if str[start] == '+' || str[start] == '-' { 13 | start++ 14 | } 15 | if start >= len(str) { 16 | return false 17 | } 18 | 19 | numberic := true 20 | 21 | start = scanDigits(str, start) 22 | 23 | 24 | if start < len(str) { 25 | if str[start] == '.' { 26 | start++ 27 | start = scanDigits(str, start) 28 | if start == len(str) { 29 | return true 30 | } 31 | if str[start] == 'e' || str[start] == 'E' { 32 | numberic, start = isE(str, start) 33 | 34 | } 35 | } else if str[start] == 'e' || str[start] == 'E' { 36 | 37 | numberic, start = isE(str, start) 38 | 39 | } else { 40 | numberic = false 41 | } 42 | } 43 | 44 | return numberic && start == len(str) 45 | 46 | } 47 | 48 | func scanDigits(str string, start int) int { 49 | i := start 50 | for i < len(str) && str[i] <= '9' && str[i] >= '0' { 51 | i++ 52 | } 53 | return i 54 | } 55 | 56 | func isE(str string, start int) (bool, int) { 57 | if str[start] != 'e' && str[start] != 'E' { 58 | return false, start 59 | } 60 | start++ 61 | if start >= len(str) { 62 | return false, start 63 | } 64 | 65 | if str[start] == '+' || str[start] == '-' { 66 | start++ 67 | } 68 | 69 | if start >= len(str) { 70 | return false, start 71 | } 72 | 73 | start = scanDigits(str, start) 74 | 75 | if start == len(str) { 76 | return true, start 77 | } 78 | return false, start 79 | } 80 | 81 | func main() { 82 | fmt.Println("Should be true") 83 | 84 | test := "+100" 85 | fmt.Println(test, " is Number ", isNumber(test)) 86 | test = "5e2" 87 | fmt.Println(test, " is Number ", isNumber(test)) 88 | test = "-123" 89 | fmt.Println(test, " is Number ", isNumber(test)) 90 | 91 | test = "-3.1416" 92 | fmt.Println(test, " is Number ", isNumber(test)) 93 | 94 | test = "-3E-16" 95 | fmt.Println(test, " is Number ", isNumber(test)) 96 | 97 | fmt.Println("") 98 | fmt.Println("Should be false") 99 | 100 | test = "12e" 101 | fmt.Println(test, " is Number ", isNumber(test)) 102 | 103 | test = "1a3.14" 104 | fmt.Println(test, " is Number ", isNumber(test)) 105 | 106 | test = "1.2.3" 107 | fmt.Println(test, " is Number ", isNumber(test)) 108 | 109 | test = "+-5" 110 | fmt.Println(test, " is Number ", isNumber(test)) 111 | 112 | test = "12e+4.3" 113 | fmt.Println(test, " is Number ", isNumber(test)) 114 | 115 | } 116 | -------------------------------------------------------------------------------- /055-字符流中第一个不重复的字符/README.md: -------------------------------------------------------------------------------- 1 | # 题目描述 2 | 3 | 请实现一个函数用来找出字符流中第一个只出现一次的字符。例如,当从字符流中只读出前两个字符"go"时,第一个只出现一次的字符是"g"。当从该字符流中读出前六个字符“google"时,第一个只出现一次的字符是"l"。 4 | 5 | 如果当前字符流没有存在出现一次的字符,返回#字符。 6 | 7 | # 分析 8 | 9 | 类似的题目 10 | 11 | [剑指Offer--035-第一个只出现一次的字符位置](https://github.com/DinghaoLI/Coding-Interviews-Golang/tree/master/035-%E7%AC%AC%E4%B8%80%E4%B8%AA%E5%8F%AA%E5%87%BA%E7%8E%B0%E4%B8%80%E6%AC%A1%E7%9A%84%E5%AD%97%E7%AC%A6%E4%BD%8D%E7%BD%AE) 12 | 13 | 但是第35题只要求我们找到一个字符串中第一个只出现一次字符即可, 但是这道题相当于一个在线算法, 要求我们能对一个源源不断的输入流进行处理 14 | 15 | 字符只能一个接着一个从字符流中读取出来, 因此我们可以定义一个数据容器来保存字符在字符流中的位置, 当一个字符第一次从字符流中拂去出来的时候, 把它保存在字符流中的位置保存到容器中。 16 | 17 | 当这个字符再次从字符流中被读取出来的时候, 那么它就不是只出现一次的字符, 也就是被忽略了。这时候就把在数据容器里保存的值更新成一个特殊的值即可。 18 | 19 | 为了更加高效的解决这个问题, 需要在O(1)的时间内往数据容器里插入一个字符, 以及更新一个字符对应的值。 -------------------------------------------------------------------------------- /055-字符流中第一个不重复的字符/problem055.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | type Solution struct { 8 | str string 9 | count []int 10 | } 11 | 12 | func (s *Solution) Insert(char byte) { 13 | s.str += string(char) 14 | s.count[char]++ 15 | } 16 | 17 | func (s *Solution) GetOnceAppear() byte { 18 | for i, v := range s.count { 19 | if v == 1 { 20 | return byte(i) 21 | } 22 | } 23 | return '#' 24 | } 25 | 26 | func main() { 27 | sol := &Solution{"", make([]int, 256)} 28 | 29 | sol.Insert('g') 30 | fmt.Println(sol.str, " GetOnceAppear: ", string(sol.GetOnceAppear())) 31 | sol.Insert('o') 32 | fmt.Println(sol.str, " GetOnceAppear: ", string(sol.GetOnceAppear())) 33 | sol.Insert('o') 34 | fmt.Println(sol.str, " GetOnceAppear: ", string(sol.GetOnceAppear())) 35 | sol.Insert('g') 36 | fmt.Println(sol.str, " GetOnceAppear: ", string(sol.GetOnceAppear())) 37 | sol.Insert('l') 38 | fmt.Println(sol.str, " GetOnceAppear: ", string(sol.GetOnceAppear())) 39 | sol.Insert('e') 40 | fmt.Println(sol.str, " GetOnceAppear: ", string(sol.GetOnceAppear())) 41 | 42 | } 43 | -------------------------------------------------------------------------------- /056-链表中环的入口结点/README.md: -------------------------------------------------------------------------------- 1 | # 题目描述 2 | 3 | 一个链表中包含环,请找出该链表的环的入口结点。 4 | 5 | [LeetCode 142. Linked List Cycle II](https://leetcode.com/problems/linked-list-cycle-ii) 6 | 7 | # 双指针法 8 | 9 | 受到第15题的启发剑指Offer--015-链表中倒数第k个结点, 我们考虑这样一个事实 10 | 11 | 假设链表长度为N, 那么第N链接到了第k个节点形成了环,即我们需要查找到倒数第N-K+1个节点, 那么环中就有N-K+1个节点,这时候我们定义两个指针$P_1$和$P_2$指向链表的头部, 指针$P_1$先在链表中向前移动n-k+1步,到达第n-k+2个节点, 然后两个指针同步向前移动, 当$P_2$走了K-1步到达环的入口的时候, 指针$P_1$正好走了N+1步, 到达了环的入口, 即两个指针会相遇在环的入口处 12 | 13 | 那么我们剩下的问题就是如何得到环中节点的数目? 14 | 15 | 我们可以使用一快一慢两个指针(比如慢指针一次走一步, 慢指针一次走两步),如果走的过程中发现快指针追上了慢指针, 说明遇见了环,而且相遇的位置一定在环内, 考虑一下环内, 从任何一个节点出现再回到这个节点的距离就是环的长度, 于是我们可以进一步移动慢指针,快指针原地不动, 当慢指针再次回到相遇位置时, 正好在环内走了一圈, 从而我们通过计数就可以获取到环的长度 16 | 17 | 第一步,找环中相汇点。分别用p1,p2指向链表头部,p1每次走一步,p2每次走二步,直到p1==p2找到在环中的相汇点。 18 | 19 | 第二步,找环的长度。从环中的相汇点开始, p2不动, p1前移, 当再次相遇时,p1刚好绕环一周, 其移动即为环的长度K 20 | 21 | 第三步, 求换的起点, 转换为求环的倒数第N-K个节点,则两指针left和right均指向起始, right先走K步, 然后两个指针开始同步移动, 当两个指针再次相遇时, right刚好绕环一周回到起点, left则刚好走到了起点位置 22 | 23 | ```golang 24 | /** 25 | * Definition for singly-linked list. 26 | * type ListNode struct { 27 | * Val int 28 | * Next *ListNode 29 | * } 30 | */ 31 | // https://juejin.im/post/59e5544851882551dd311710 32 | // n为head到entrance的节点数 33 | // m为环的长度 34 | // k为slow过了entrance后走的节点数 35 | // m-k为从fast、slow的相遇点到entrance的节点数(向前走) 36 | // slow = n + k 37 | // fast = qm + n + k (q为正整数,为圈数) 38 | // fast = 2 * slow 39 | // 可得 n = qm -k 40 | // 亦是 n = (q-1)m + (m-k) 41 | // 证毕 42 | func detectCycle(head *ListNode) *ListNode { 43 | slow, fast := head, head 44 | if head == nil || head.Next == nil { 45 | return nil 46 | } 47 | 48 | for fast.Next != nil && fast.Next.Next != nil { 49 | fast = fast.Next.Next 50 | slow = slow.Next 51 | if slow == fast { 52 | tmp := head 53 | // n = (q-1)m + (m-k) 54 | // 所以从slow和head同时开始走,当slow==head那说明,这就是entrance 55 | for tmp != slow { 56 | slow = slow.Next 57 | tmp = tmp.Next 58 | } 59 | return tmp 60 | } 61 | } 62 | 63 | return nil 64 | } 65 | ``` -------------------------------------------------------------------------------- /056-链表中环的入口结点/problem056.go: -------------------------------------------------------------------------------- 1 | /** 2 | * Definition for singly-linked list. 3 | * type ListNode struct { 4 | * Val int 5 | * Next *ListNode 6 | * } 7 | */ 8 | // https://juejin.im/post/59e5544851882551dd311710 9 | // n为head到entrance的节点数 10 | // m为环的长度 11 | // k为slow过了entrance后走的节点数 12 | // m-k为从fast、slow的相遇点到entrance的节点数(向前走) 13 | // slow = n + k 14 | // fast = qm + n + k (q为正整数,为圈数) 15 | // fast = 2 * slow 16 | // 可得 n = qm -k 17 | // 亦是 n = (q-1)m + (m-k) 18 | // 证毕 19 | func detectCycle(head *ListNode) *ListNode { 20 | if head == nil || head.Next == nil { 21 | return nil 22 | } 23 | slow := head.Next 24 | fast := head.Next.Next 25 | 26 | for fast != nil && fast.Next != nil { 27 | if slow == fast { 28 | break 29 | } 30 | slow, fast = slow.Next, fast.Next.Next 31 | } 32 | 33 | tmp := head 34 | // n = (q-1)m + (m-k) 35 | // 所以从slow和head同时开始走,当slow==head那说明,这就是entrance 36 | for tmp!=nil && slow!=nil { 37 | if slow == tmp { 38 | return slow 39 | } 40 | slow, tmp = slow.Next, tmp.Next 41 | } 42 | return nil 43 | } 44 | 45 | // 测试地址 46 | // https://leetcode.com/problems/linked-list-cycle-ii/ 47 | 48 | 49 | -------------------------------------------------------------------------------- /057-删除链表中重复的结点/README.md: -------------------------------------------------------------------------------- 1 | # 题意 2 | 3 | 在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。 4 | 5 | 例如,链表1->2->3->3->4->4->5 6 | 7 | 处理后为 1->2->5 8 | 9 | # 递归大法好 10 | 11 | 我们的思路是: 12 | 13 | 每次返回没有重复的head,如果有重复删除头节点,删完了,就用相同逻辑处理Next节点 14 | 15 | [LeetCode 82. Remove Duplicates from Sorted List II](https://leetcode.com/problems/remove-duplicates-from-sorted-list-ii/) 16 | 17 | -------------------------------------------------------------------------------- /057-删除链表中重复的结点/problem057.go: -------------------------------------------------------------------------------- 1 | // 对于每一部分处理方式相同,所以我我们考虑用递归 2 | // 递归 3 | /** 4 | * Definition for singly-linked list. 5 | * type ListNode struct { 6 | * Val int 7 | * Next *ListNode 8 | * } 9 | */ 10 | func deleteDuplicates(head *ListNode) *ListNode { 11 | // 长度 <=1 的 list ,可以直接返回 12 | if head == nil || head.Next == nil { 13 | return head 14 | } 15 | 16 | // 要么 head 重复了,那就删除 head 17 | if head.Val == head.Next.Val { 18 | for head.Next != nil && head.Val == head.Next.Val { 19 | head = head.Next 20 | } 21 | // 有重复所以直接不带head 22 | return deleteDuplicates(head.Next) 23 | } 24 | 25 | // 要么 head 不重复,递归处理 head 后面的节点 26 | head.Next = deleteDuplicates(head.Next) 27 | return head 28 | } 29 | 30 | // 测试地址 31 | // https://leetcode.com/problems/remove-duplicates-from-sorted-list-ii/ -------------------------------------------------------------------------------- /058-二叉树的下一个结点/README.md: -------------------------------------------------------------------------------- 1 | # 题意 2 | 3 | 给定一个二叉树和其中的一个结点,请找出中序遍历顺序的下一个结点并且返回。 4 | 5 | 注意,树中的结点不仅包含左右子结点,同时包含指向父结点的指针。 6 | 7 | # 分析 8 | 9 | - 如果当前结点有右子树,那么其中序遍历的下一个结点就是其右子树的最左结点比如结点2中序遍历的下一个结点是3 10 | 11 | - 如果当前结点没有右子树,而它是其父结点的左子结点那么其中序遍历的下一个结点就是他的父亲结点比如结点1中序遍历的下一个结点是2 12 | 13 | - 如果当前结点没有右子树,而它还是其父结点的右子结点,这种情况下其下一个结点应该是当前结点所在的左子树的根,因此我们可以顺着其父节点一直向上遍历,直到找到一个是它父结点的左子结点的结点。 -------------------------------------------------------------------------------- /058-二叉树的下一个结点/problem058.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | type TreeNode struct { 8 | Val int 9 | Left *TreeNode 10 | Right *TreeNode 11 | Parent *TreeNode 12 | } 13 | 14 | func getNext(node *TreeNode) *TreeNode { 15 | if node == nil { 16 | return node 17 | } 18 | //如果节点有右子树,那么它的下一个节点就是它的右子树中最左边的节点 19 | if node.Right != nil { 20 | node = node.Right 21 | for node.Left != nil { 22 | node = node.Left 23 | } 24 | return node 25 | } 26 | // 先取目标的父节点 27 | p := node.Parent 28 | n := node 29 | for p != nil { 30 | // 如果p节点是p的父节点的右节点 =》 继续向上找 31 | if n == p.Right { 32 | n = p 33 | p = p.Parent 34 | continue 35 | } 36 | // p是p父节点的左节点 =》 返回父节点 37 | return p 38 | } 39 | // 目标节点没有下一个节点 40 | return nil 41 | } 42 | 43 | func inOrder(root *TreeNode) { 44 | if root == nil { 45 | return 46 | } 47 | inOrder(root.Left) 48 | fmt.Printf("%d -> ", root.Val) 49 | inOrder(root.Right) 50 | } 51 | 52 | func main() { 53 | n1 := &TreeNode{1, nil, nil, nil} 54 | n2 := &TreeNode{2, nil, nil, nil} 55 | n3 := &TreeNode{3, nil, nil, nil} 56 | n4 := &TreeNode{4, nil, nil, nil} 57 | n5 := &TreeNode{5, nil, nil, nil} 58 | n6 := &TreeNode{6, nil, nil, nil} 59 | n7 := &TreeNode{7, nil, nil, nil} 60 | n8 := &TreeNode{8, nil, nil, nil} 61 | n9 := &TreeNode{9, nil, nil, nil} 62 | 63 | n4.Left, n4.Right, n4.Parent = n2, n6, n8 64 | n2.Left, n2.Right, n2.Parent = n1, n3, n4 65 | n1.Parent = n2 66 | n3.Parent = n2 67 | n6.Left, n6.Right, n6.Parent = n5, n7, n4 68 | n5.Parent = n6 69 | n7.Parent = n6 70 | n8.Left, n8.Right = n4, n9 71 | n9.Parent = n8 72 | 73 | // Example: 74 | // 8 75 | // 4 9 76 | // 2 6 77 | // 1 3 5 7 78 | 79 | fmt.Println("Inorder: ") 80 | inOrder(n8) 81 | fmt.Println("") 82 | fmt.Println(n1.Val, " getNext : ", getNext(n1).Val) 83 | fmt.Println(n2.Val, " getNext : ", getNext(n2).Val) 84 | fmt.Println(n3.Val, " getNext : ", getNext(n3).Val) 85 | fmt.Println(n4.Val, " getNext : ", getNext(n4).Val) 86 | fmt.Println(n5.Val, " getNext : ", getNext(n5).Val) 87 | fmt.Println(n6.Val, " getNext : ", getNext(n6).Val) 88 | fmt.Println(n7.Val, " getNext : ", getNext(n7).Val) 89 | fmt.Println(n8.Val, " getNext : ", getNext(n8).Val) 90 | fmt.Println(n9.Val, " getNext : ", getNext(n9)) 91 | 92 | 93 | } 94 | 95 | 96 | 97 | 98 | -------------------------------------------------------------------------------- /059-对称的二叉树/README.md: -------------------------------------------------------------------------------- 1 | # 题意 2 | 3 | 请实现一个函数,用来判断一颗二叉树是不是对称的。 4 | 5 | 注意,如果一个二叉树同此二叉树的镜像是同样的,定义其为对称的 6 | 7 | # 递归 8 | 9 | 左右子树同时遍历,若出现不一致,则说明不对称 10 | 11 | 二叉树是否对称的本质,其实是判定两棵树是否镜像 12 | 13 | 即响应对的位置的节点是否对应相等t -------------------------------------------------------------------------------- /059-对称的二叉树/problem059.go: -------------------------------------------------------------------------------- 1 | 2 | func isSymmetric(root *TreeNode) bool { 3 | if root == nil { 4 | return true 5 | } 6 | var isMirror func(*TreeNode,*TreeNode) bool 7 | isMirror = func(t1, t2 *TreeNode) bool { 8 | // 判断两个树是否都存在 9 | if t1 == nil && t2 == nil { 10 | return true 11 | } 12 | if t1 == nil || t2 == nil { 13 | return false 14 | } 15 | // t1和t2得相等,同时t1.Left和t2.Right, t1.Right和t2.Left 都需要对称 16 | return t1.Val == t2.Val && isMirror(t1.Left, t2.Right) && isMirror(t1.Right, t2.Left) 17 | } 18 | return isMirror(root.Left, root.Right) 19 | } 20 | 21 | // 测试地址 22 | // https://leetcode.com/problems/symmetric-tree/ -------------------------------------------------------------------------------- /060-把二叉树打印成多行/README.md: -------------------------------------------------------------------------------- 1 | # 题目描述 2 | 从上到下按层打印二叉树,同一层结点从左至右输出。每一层输出一行。 3 | 4 | # 思路 5 | 按层次输出二叉树 6 | 7 | 访问根节点,并将根节点入队。 8 | 9 | 当队列不空的时候,重复以下操作。 10 | 11 | 弹出一个元素。作为当前的根节点。 12 | 如果根节点有左孩子,访问左孩子,并将左孩子入队。 13 | 如果根节点有右孩子,访问右孩子,并将右孩子入队。 -------------------------------------------------------------------------------- /060-把二叉树打印成多行/problem060.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | type TreeNode struct { 8 | Val int 9 | Left *TreeNode 10 | Right *TreeNode 11 | } 12 | 13 | func print(root *TreeNode) { 14 | var q1 []*TreeNode 15 | q1 = append(q1, root) 16 | for len(q1) != 0 { 17 | for len(q1) != 0 { 18 | node := q1[0] 19 | q1 = q1[1:len(q1)] 20 | fmt.Printf("%d ", node.Val) 21 | if node.Left != nil { 22 | q1 = append(q1, node.Left) 23 | } 24 | if node.Right != nil { 25 | q1 = append(q1, node.Right) 26 | } 27 | } 28 | } 29 | } 30 | 31 | func main() { 32 | // q1 1 33 | // q2 2 3 34 | // q1 4 5 6 7 35 | root := &TreeNode{1, &TreeNode{2, &TreeNode{4, nil, nil}, &TreeNode{5, nil, nil}}, &TreeNode{3, &TreeNode{6, nil, nil}, &TreeNode{7, nil, nil}}} 36 | print(root) 37 | fmt.Printf("\n") 38 | 39 | root.Right.Left.Left = &TreeNode{8, nil, nil} 40 | root.Right.Left.Right = &TreeNode{9, nil, nil} 41 | root.Right.Right.Left = &TreeNode{10, nil, nil} 42 | root.Right.Right.Right = &TreeNode{11, nil, nil} 43 | print(root) 44 | fmt.Printf("\n") 45 | } 46 | -------------------------------------------------------------------------------- /061-按之字形顺序打印二叉树/README.md: -------------------------------------------------------------------------------- 1 | # 题意 2 | 3 | 请实现一个函数按照之字形打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右至左的顺序打印,第三行按照从左到右的顺序打印,其他行以此类推。 4 | 5 | # 层次遍历后结果之按字输出 6 | 7 | 其实我们可以借鉴层次遍历时候的思路, 参见剑指Offer--060-把二叉树打印成多行, 我们把层次遍历的结果保存在vector< vector >中每一层保存在一个vector中, 那么我们输出的时候就可以进行调整, 按照之字形输出即可 8 | 9 | -------------------------------------------------------------------------------- /061-按之字形顺序打印二叉树/problem061.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | type TreeNode struct { 8 | Val int 9 | Left *TreeNode 10 | Right *TreeNode 11 | } 12 | 13 | func Zprint(root *TreeNode){ 14 | var q1 []*TreeNode 15 | var q2 []*TreeNode 16 | q1 = append(q1, root) 17 | for len(q1) != 0 || len(q2) !=0 { 18 | if len(q2) == 0 { 19 | for len(q1) != 0 { 20 | node := q1[len(q1)-1] 21 | q1 = q1[:len(q1)-1] 22 | fmt.Printf("%d ", node.Val) 23 | if node.Left != nil { 24 | q2 = append(q2, node.Left) 25 | } 26 | if node.Right != nil { 27 | q2 = append(q2, node.Right) 28 | } 29 | } 30 | } else { 31 | for len(q2) != 0 { 32 | node := q2[len(q2)-1] 33 | q2 = q2[:len(q2)-1] 34 | fmt.Printf("%d ", node.Val) 35 | if node.Right != nil { 36 | q1 = append(q1, node.Right) 37 | } 38 | if node.Left != nil { 39 | q1 = append(q1, node.Left) 40 | } 41 | } 42 | } 43 | } 44 | } 45 | 46 | func main() { 47 | // q1 1 48 | // q2 2 3 49 | // q1 4 5 6 7 50 | root := &TreeNode{1, &TreeNode{2, &TreeNode{4,nil,nil}, &TreeNode{5,nil,nil}}, &TreeNode{3, &TreeNode{6,nil,nil}, &TreeNode{7,nil,nil}}} 51 | Zprint(root) 52 | fmt.Printf("\n") 53 | 54 | root.Right.Left.Left = &TreeNode{8, nil, nil} 55 | root.Right.Left.Right = &TreeNode{9, nil, nil} 56 | root.Right.Right.Left = &TreeNode{10, nil, nil} 57 | root.Right.Right.Right = &TreeNode{11, nil, nil} 58 | Zprint(root) 59 | fmt.Printf("\n") 60 | } 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /062-序列化二叉树/README.md: -------------------------------------------------------------------------------- 1 | # 题目描述 2 | 3 | 请实现两个函数,分别用来序列化和反序列化二叉树。这里没有规定序列化的方式 4 | 5 | # 分析 6 | 7 | ## 遍历二叉树 8 | 9 | 其实这道题约定的序列化没有固定的格式, 只要你序列化后的结果, 再反序列化后与原树相同即可, 10 | 11 | 因此我们可以随意指定自己的格式, 12 | 13 | 比如空节点用$表示,或则#表示, 14 | 15 | 然后遍历采用先序, 中序, 后序或者层次都可以, 16 | 17 | 我们的示例程序中采用空结点用#表示, 结点与结点用逗号,分隔 18 | 19 | 选择了合适的遍历算法, 那么剩下的问题就是字符串序列和整数权值的相互转换问题 20 | 序列化的关键, 其实就是将树的权值(整数)转换为字符串序列, 可以采用ostringstream, sprintf和itoa 21 | 22 | 反序列化的关键, 则正好相反, 将字符串转换为整数, 可以使用istringstream, sscanf和atoi -------------------------------------------------------------------------------- /062-序列化二叉树/problem062.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | ) 7 | 8 | type TreeNode struct { 9 | Val int 10 | Left *TreeNode 11 | Right *TreeNode 12 | } 13 | 14 | func Serialize(root *TreeNode) string { 15 | if root == nil { 16 | return "#," 17 | } 18 | str := "" 19 | str = SerializeTree(root, str) 20 | return str 21 | } 22 | 23 | func SerializeTree(root *TreeNode, str string) string { 24 | if root == nil { 25 | str += "#," 26 | return str 27 | } 28 | 29 | str += strconv.Itoa(root.Val) + "," 30 | str += SerializeTree(root.Left, "") 31 | str += SerializeTree(root.Right, "") 32 | return str 33 | } 34 | 35 | func Deserialize(str string) *TreeNode { 36 | if str == "" { 37 | return nil 38 | } 39 | return DeserializeTree(str) 40 | } 41 | 42 | func DeserializeTree(str string) *TreeNode { 43 | index := 0 44 | var recur func(str string) *TreeNode 45 | recur = func(str string) *TreeNode { 46 | if str[index] == '#' { 47 | index += 2 48 | return nil 49 | } 50 | num := 0 51 | for str[index] != ',' && index < len(str) { 52 | num = num*10 + int(str[index] - '0') 53 | index++ 54 | } 55 | index++ 56 | 57 | root := &TreeNode{num, nil, nil} 58 | root.Left = recur(str) 59 | root.Right = recur(str) 60 | return root 61 | } 62 | root := recur(str) 63 | return root 64 | 65 | } 66 | 67 | func print(root *TreeNode) { 68 | if root == nil { 69 | return 70 | } 71 | fmt.Printf("%d -> ", root.Val) 72 | print(root.Left) 73 | print(root.Right) 74 | } 75 | 76 | func main() { 77 | // q1 1 78 | // q2 2 3 79 | // q1 4 5 6 7 80 | root := &TreeNode{1, &TreeNode{2, &TreeNode{4, nil, nil}, &TreeNode{5, nil, nil}}, &TreeNode{3, &TreeNode{6, nil, nil}, &TreeNode{7, nil, nil}}} 81 | 82 | fmt.Println("Output: ", Serialize(root)) 83 | fmt.Println("") 84 | fmt.Println("before") 85 | print(root) 86 | fmt.Println("") 87 | root = Deserialize(Serialize(root)) 88 | fmt.Println("after") 89 | print(root) 90 | fmt.Println("") 91 | 92 | } 93 | -------------------------------------------------------------------------------- /063-二叉搜索树的第K个结点/README.md: -------------------------------------------------------------------------------- 1 | # 题意 2 | 3 | 4 | ``` 5 | 5 6 | / \ 7 | 3 7 8 | / \ /\ 9 | 2 4 6 8 10 | ``` 11 | 12 | 给定一棵二叉搜索树,请找出其中的第k小的结点。例如, (5,3,7,2,4,6,8) 中,按结点数值大小顺序第三小结点的值为4。 13 | 14 | 二叉搜索树的中序遍历正好是一个递增的序列,因此中序遍历的第K个结点就是二叉搜索树的第K个节点 -------------------------------------------------------------------------------- /063-二叉搜索树的第K个结点/problem063.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | type TreeNode struct { 8 | Val int 9 | Left *TreeNode 10 | Right *TreeNode 11 | } 12 | 13 | func getKth(root *TreeNode, k int) *TreeNode { 14 | if root == nil || k<1 { 15 | return nil 16 | } 17 | var res *TreeNode 18 | found := false 19 | var inOrder func(*TreeNode) 20 | inOrder = func(node *TreeNode) { 21 | if found == true || node == nil { 22 | return 23 | } 24 | inOrder(node.Left) 25 | if k == 1 { 26 | res = node 27 | found = true 28 | } 29 | k-- 30 | inOrder(node.Right) 31 | } 32 | inOrder(root) 33 | return res 34 | } 35 | 36 | 37 | func print(root *TreeNode) { 38 | if root == nil { 39 | return 40 | } 41 | print(root.Left) 42 | fmt.Printf("%d -> ", root.Val) 43 | print(root.Right) 44 | } 45 | 46 | func main() { 47 | // q1 5 48 | // q2 3 7 49 | // q1 1 4 6 8 50 | root := &TreeNode{5, &TreeNode{3, &TreeNode{1, nil, nil}, &TreeNode{4, nil, nil}}, &TreeNode{7, &TreeNode{6, nil, nil}, &TreeNode{8, nil, nil}}} 51 | print(root) 52 | fmt.Println("") 53 | fmt.Println("Get 1th: ", getKth(root, 1).Val) 54 | fmt.Println("Get 2th: ", getKth(root, 2).Val) 55 | fmt.Println("Get 3th: ", getKth(root, 3).Val) 56 | fmt.Println("Get 4th: ", getKth(root, 4).Val) 57 | fmt.Println("Get 5th: ", getKth(root, 5).Val) 58 | fmt.Println("Get 6th: ", getKth(root, 6).Val) 59 | fmt.Println("Get 7th: ", getKth(root, 7).Val) 60 | fmt.Println("Get 8th: ", getKth(root, 8)) 61 | 62 | 63 | } 64 | -------------------------------------------------------------------------------- /064-数据流之中的中位数/README.md: -------------------------------------------------------------------------------- 1 | # 题意 2 | 3 | 题目描述 4 | 5 | 如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。 6 | 7 | # 堆排序策略 8 | 9 | 对于数据流,对应的就是在线算法了,一道很经典的题目就是在1亿个数中找到最大的前100个数,这是一道堆应用题,找最大的前100个数,那么我们就创建一个大小为100的最小化堆,每来一个元素就与堆顶元素比较,因为堆顶元素是目前前100大数中的最小数,前来的元素如果比该元素大,那么就把原来的堆顶替换掉。 10 | 11 | 那么对于这一道题呢?如果单纯的把所有元素放到一个数组里,每次查找中位数最快也要O(n),综合下来是O(n^2)的复杂度。我们可以利用上面例子中的想法,用一个最大堆来维护当前前n/2小的元素,那么每次找中位数只到取出堆顶就可以了。但是,有一个问题,数据要动态增长,有可能之前被替换掉的元素随着元素的增加又跑回来了,所以我们不能单纯得向上题一样把元素丢掉,我们可以再用一个最小化堆来存前n/2大的元素。 -------------------------------------------------------------------------------- /064-数据流之中的中位数/problem064.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "../utils" 5 | "fmt" 6 | ) 7 | 8 | type Solution struct { 9 | max *utils.MaxHeap 10 | min *utils.MinHeap 11 | } 12 | 13 | func (s *Solution) Insert(num int) { 14 | if ((s.max.Length() + s.min.Length()) & 1) == 0 { 15 | // 偶数数据的情况下 16 | // 直接将新的数据插入到数组的后半段 17 | // 即在最小堆中插入元素 18 | // 此时最小堆中多出一个元素, 19 | // 即最小元素, 将其弹出后, 压入前半段(即最大堆中) 20 | if s.max.Length() > 0 && num < s.max.GetMax() { 21 | s.max.Insert(num) 22 | val, _ := s.max.DeleteMax() 23 | s.min.Insert(val) 24 | } else { 25 | s.min.Insert(num) 26 | } 27 | } else { 28 | // 奇数情况 29 | if s.max.Length() > 0 && num < s.max.GetMax() { 30 | s.max.Insert(num) 31 | } else { 32 | s.min.Insert(num) 33 | val, _ := s.min.DeleteMin() 34 | s.max.Insert(val) 35 | } 36 | } 37 | } 38 | 39 | func (s *Solution) GetMedian() float64 { 40 | size := s.max.Length() + s.min.Length() 41 | if size == 0 { 42 | return -1 43 | } 44 | var median float64 45 | if size&1 != 0 { 46 | median = float64(s.min.GetMin()) 47 | } else { 48 | median = float64(s.min.GetMin() + s.max.GetMax()) / 2 49 | } 50 | return median 51 | } 52 | 53 | 54 | func main() { 55 | 56 | sol := &Solution{utils.NewMaxHeap(), utils.NewMinHeap()} 57 | sol.Insert(1) 58 | fmt.Println("Insert 1") 59 | fmt.Println(sol.GetMedian()) 60 | 61 | sol.Insert(2) 62 | fmt.Println("Insert 2") 63 | fmt.Println(sol.GetMedian()) 64 | 65 | sol.Insert(3) 66 | fmt.Println("Insert 3") 67 | fmt.Println(sol.GetMedian()) 68 | 69 | sol.Insert(4) 70 | fmt.Println("Insert 4") 71 | fmt.Println(sol.GetMedian()) 72 | 73 | sol.Insert(5) 74 | fmt.Println("Insert 5") 75 | fmt.Println(sol.GetMedian()) 76 | 77 | } 78 | -------------------------------------------------------------------------------- /065-滑动窗口的最大值/README.md: -------------------------------------------------------------------------------- 1 | # 题意 2 | 3 | 题目描述 4 | 5 | 给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值 6 | 7 | 例如,如果输入数组{2,3,4,2,6,2,5,1}及滑动窗口的大小3 8 | 9 | 那么一共存在6个滑动窗口, 他们的最大值分别为{4,4,6,6,6,5}; 10 | 11 | 针对数组{2,3,4,2,6,2,5,1}的滑动窗口有以下6个 {[2,3,4],2,6,2,5,1},最大值4 {2,[3,4,2],6,2,5,1},最大值4 {2,3,[4,2,6],2,5,1},最大值6 {2,3,4,[2,6,2],5,1},最大值6 {2,3,4,2,[6,2,5],1},最大值6 {2,3,4,2,6,[2,5,1]},最大值5 12 | 13 | [LeetCode 239. Sliding Window Maximum](https://leetcode.com/problems/sliding-window-maximum/) -------------------------------------------------------------------------------- /065-滑动窗口的最大值/problem065.go: -------------------------------------------------------------------------------- 1 | // deque 双向队列 2 | func maxSlidingWindow(nums []int, k int) []int { 3 | var res []int 4 | var window []int 5 | for index := 0; index < len(nums); index++ { 6 | // 最大值已经超出窗口范围 7 | if index >= k && window[0] <= index - k { 8 | window = window[1:] 9 | } 10 | // 当前值比队列末尾大则替换末尾 11 | for len(window) > 0 && nums[window[len(window) - 1]] < nums[index] { 12 | window = window[:len(window) - 1] 13 | } 14 | // 添加元素进队列 15 | window = append(window, index) 16 | // 记录当前最大值 17 | if index >= k - 1 { 18 | res = append(res, nums[window[0]]) 19 | } 20 | } 21 | return res 22 | } 23 | 24 | // 测试地址 25 | // https://leetcode.com/problems/sliding-window-maximum/ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 DinghaoLI 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 剑指Offer - Golang实现 2 | 3 | ## 简介 4 | 5 | 一直没找到完整版的剑指Offer的Golang实现,所以索性自己来写。 6 | 7 | 每个文件夹对应每个题目,文件夹内的README.md内有题目和必要的分析,Problem\*.go 是和算法相对应的代码。 8 | 9 | 如果大家使用的是Chrome,给推荐大家一个小插件:**[octotree](https://chrome.google.com/webstore/detail/octotree/bkhaagjahfmjljalopjnoealnfndnagc?hl=zh-CN)**,这是一个极其方便的github Repo索引小插件,真的很方便。 10 | 11 | ## 运行代码 12 | 13 | 若题目对应包内有 \*\_test.go 文件,可直接执行go test进行单元测试 14 | ```shell 15 | go test 16 | ``` 17 | 18 | 若没有test文件,请直接执行 19 | ```shell 20 | go run problemXXX.go 21 | ``` 22 | 23 | **如果文档里有说明该题目是LeetCode上原题,请点击内附的LeetCode题目链接,并复制代码到LeetCode上执行。** 24 | 25 | ## 如有错误或者更好的算法版本,欢迎各种PR~ 26 | 27 | 28 | | 题目 | 29 | | ---------- | 30 | | [003-二维数组中的查找](https://github.com/DinghaoLI/Coding-Interviews-Golang/tree/master/003-%E4%BA%8C%E7%BB%B4%E6%95%B0%E7%BB%84%E4%B8%AD%E7%9A%84%E6%9F%A5%E6%89%BE) | 31 | [004-替换空格](https://github.com/DinghaoLI/Coding-Interviews-Golang/tree/master/004-%e6%9b%bf%e6%8d%a2%e7%a9%ba%e6%a0%bc) | 32 | | [005-从尾到头打印链表](https://github.com/DinghaoLI/Coding-Interviews-Golang/tree/master/005-%e4%bb%8e%e5%b0%be%e5%88%b0%e5%a4%b4%e6%89%93%e5%8d%b0%e9%93%be%e8%a1%a8) | 33 | | [006-重建二叉树](https://github.com/DinghaoLI/Coding-Interviews-Golang/tree/master/006-%e9%87%8d%e5%bb%ba%e4%ba%8c%e5%8f%89%e6%a0%91) | 34 | | [007-用两个栈实现队列](https://github.com/DinghaoLI/Coding-Interviews-Golang/tree/master/007-%e7%94%a8%e4%b8%a4%e4%b8%aa%e6%a0%88%e5%ae%9e%e7%8e%b0%e9%98%9f%e5%88%97) | 35 | | [008-旋转数组的最小数字](https://github.com/DinghaoLI/Coding-Interviews-Golang/tree/master/008-%e6%97%8b%e8%bd%ac%e6%95%b0%e7%bb%84%e7%9a%84%e6%9c%80%e5%b0%8f%e6%95%b0%e5%ad%97) | 36 | | [009-斐波那契数列](https://github.com/DinghaoLI/Coding-Interviews-Golang/tree/master/009-%e6%96%90%e6%b3%a2%e9%82%a3%e5%a5%91%e6%95%b0%e5%88%97) | 37 | | [010-二进制中1的个数](https://github.com/DinghaoLI/Coding-Interviews-Golang/tree/master/010-%e4%ba%8c%e8%bf%9b%e5%88%b6%e4%b8%ad1%e7%9a%84%e4%b8%aa%e6%95%b0) | 38 | | [011-数值的整数次方](https://github.com/DinghaoLI/Coding-Interviews-Golang/tree/master/011-%e6%95%b0%e5%80%bc%e7%9a%84%e6%95%b4%e6%95%b0%e6%ac%a1%e6%96%b9) | 39 | | [012-打印1到最大的N位数](https://github.com/DinghaoLI/Coding-Interviews-Golang/tree/master/012-%e6%89%93%e5%8d%b01%e5%88%b0%e6%9c%80%e5%a4%a7%e7%9a%84N%e4%bd%8d%e6%95%b0) | 40 | | [014-调整数组顺序使奇数位于偶数前面](https://github.com/DinghaoLI/Coding-Interviews-Golang/tree/master/014-%e8%b0%83%e6%95%b4%e6%95%b0%e7%bb%84%e9%a1%ba%e5%ba%8f%e4%bd%bf%e5%a5%87%e6%95%b0%e4%bd%8d%e4%ba%8e%e5%81%b6%e6%95%b0%e5%89%8d%e9%9d%a2) | 41 | | [015-链表中倒数第k个结点](https://github.com/DinghaoLI/Coding-Interviews-Golang/tree/master/015-%e9%93%be%e8%a1%a8%e4%b8%ad%e5%80%92%e6%95%b0%e7%ac%ack%e4%b8%aa%e7%bb%93%e7%82%b9) | 42 | | [016-反转链表](https://github.com/DinghaoLI/Coding-Interviews-Golang/tree/master/016-%e5%8f%8d%e8%bd%ac%e9%93%be%e8%a1%a8) | 43 | | [017-合并两个排序的链表](https://github.com/DinghaoLI/Coding-Interviews-Golang/tree/master/017-%e5%90%88%e5%b9%b6%e4%b8%a4%e4%b8%aa%e6%8e%92%e5%ba%8f%e7%9a%84%e9%93%be%e8%a1%a8) | 44 | | [018-树的子结构](https://github.com/DinghaoLI/Coding-Interviews-Golang/tree/master/018-%e6%a0%91%e7%9a%84%e5%ad%90%e7%bb%93%e6%9e%84) | 45 | | [019-二叉树的镜像](https://github.com/DinghaoLI/Coding-Interviews-Golang/tree/master/019-%e4%ba%8c%e5%8f%89%e6%a0%91%e7%9a%84%e9%95%9c%e5%83%8f) | 46 | | [020-顺时针打印矩阵](https://github.com/DinghaoLI/Coding-Interviews-Golang/tree/master/020-%e9%a1%ba%e6%97%b6%e9%92%88%e6%89%93%e5%8d%b0%e7%9f%a9%e9%98%b5) | 47 | | [021-包含min函数的栈](https://github.com/DinghaoLI/Coding-Interviews-Golang/tree/master/021-%e5%8c%85%e5%90%abmin%e5%87%bd%e6%95%b0%e7%9a%84%e6%a0%88) | 48 | | [022-栈的压入弹出序列](https://github.com/DinghaoLI/Coding-Interviews-Golang/tree/master/022-%e6%a0%88%e7%9a%84%e5%8e%8b%e5%85%a5%e5%bc%b9%e5%87%ba%e5%ba%8f%e5%88%97) | 49 | | [023-从上往下打印二叉树](https://github.com/DinghaoLI/Coding-Interviews-Golang/tree/master/023-%e4%bb%8e%e4%b8%8a%e5%be%80%e4%b8%8b%e6%89%93%e5%8d%b0%e4%ba%8c%e5%8f%89%e6%a0%91) | 50 | | [024-二叉搜索树的后序遍历序列](https://github.com/DinghaoLI/Coding-Interviews-Golang/tree/master/024-%e4%ba%8c%e5%8f%89%e6%90%9c%e7%b4%a2%e6%a0%91%e7%9a%84%e5%90%8e%e5%ba%8f%e9%81%8d%e5%8e%86%e5%ba%8f%e5%88%97) | 51 | | [025-二叉树中和为某一值的路径](https://github.com/DinghaoLI/Coding-Interviews-Golang/tree/master/025-%e4%ba%8c%e5%8f%89%e6%a0%91%e4%b8%ad%e5%92%8c%e4%b8%ba%e6%9f%90%e4%b8%80%e5%80%bc%e7%9a%84%e8%b7%af%e5%be%84) | 52 | | [026-复杂链表的复制](https://github.com/DinghaoLI/Coding-Interviews-Golang/tree/master/026-%e5%a4%8d%e6%9d%82%e9%93%be%e8%a1%a8%e7%9a%84%e5%a4%8d%e5%88%b6) | 53 | | [027-二叉搜索树与双向链表](https://github.com/DinghaoLI/Coding-Interviews-Golang/tree/master/027-%e4%ba%8c%e5%8f%89%e6%90%9c%e7%b4%a2%e6%a0%91%e4%b8%8e%e5%8f%8c%e5%90%91%e9%93%be%e8%a1%a8) | 54 | | [028-字符串的排列](https://github.com/DinghaoLI/Coding-Interviews-Golang/tree/master/028-%e5%ad%97%e7%ac%a6%e4%b8%b2%e7%9a%84%e6%8e%92%e5%88%97) | 55 | | [029-数组中出现次数超过一半的数字](https://github.com/DinghaoLI/Coding-Interviews-Golang/tree/master/029-%e6%95%b0%e7%bb%84%e4%b8%ad%e5%87%ba%e7%8e%b0%e6%ac%a1%e6%95%b0%e8%b6%85%e8%bf%87%e4%b8%80%e5%8d%8a%e7%9a%84%e6%95%b0%e5%ad%97) | 56 | | [030-最小的K个数](https://github.com/DinghaoLI/Coding-Interviews-Golang/tree/master/030-%e6%9c%80%e5%b0%8f%e7%9a%84K%e4%b8%aa%e6%95%b0) | 57 | | [031-连续子数组的最大和](https://github.com/DinghaoLI/Coding-Interviews-Golang/tree/master/031-%e8%bf%9e%e7%bb%ad%e5%ad%90%e6%95%b0%e7%bb%84%e7%9a%84%e6%9c%80%e5%a4%a7%e5%92%8c) | 58 | | [032-从1到n整数中1出现的次数](https://github.com/DinghaoLI/Coding-Interviews-Golang/tree/master/032-%e4%bb%8e1%e5%88%b0n%e6%95%b4%e6%95%b0%e4%b8%ad1%e5%87%ba%e7%8e%b0%e7%9a%84%e6%ac%a1%e6%95%b0) | 59 | | [033-把数组排成最小的数](https://github.com/DinghaoLI/Coding-Interviews-Golang/tree/master/033-%e6%8a%8a%e6%95%b0%e7%bb%84%e6%8e%92%e6%88%90%e6%9c%80%e5%b0%8f%e7%9a%84%e6%95%b0) | 60 | | [034-丑数](https://github.com/DinghaoLI/Coding-Interviews-Golang/tree/master/034-%e4%b8%91%e6%95%b0) | 61 | | [035-第一个只出现一次的字符位置](https://github.com/DinghaoLI/Coding-Interviews-Golang/tree/master/035-%e7%ac%ac%e4%b8%80%e4%b8%aa%e5%8f%aa%e5%87%ba%e7%8e%b0%e4%b8%80%e6%ac%a1%e7%9a%84%e5%ad%97%e7%ac%a6%e4%bd%8d%e7%bd%ae) | 62 | | [036-数组中的逆序对](https://github.com/DinghaoLI/Coding-Interviews-Golang/tree/master/036-%e6%95%b0%e7%bb%84%e4%b8%ad%e7%9a%84%e9%80%86%e5%ba%8f%e5%af%b9) | 63 | | [037-两个链表的第一个公共结点](https://github.com/DinghaoLI/Coding-Interviews-Golang/tree/master/037-%e4%b8%a4%e4%b8%aa%e9%93%be%e8%a1%a8%e7%9a%84%e7%ac%ac%e4%b8%80%e4%b8%aa%e5%85%ac%e5%85%b1%e7%bb%93%e7%82%b9) | 64 | | [038-数字在排序数组中出现的次数](https://github.com/DinghaoLI/Coding-Interviews-Golang/tree/master/038-%e6%95%b0%e5%ad%97%e5%9c%a8%e6%8e%92%e5%ba%8f%e6%95%b0%e7%bb%84%e4%b8%ad%e5%87%ba%e7%8e%b0%e7%9a%84%e6%ac%a1%e6%95%b0) | 65 | | [039-二叉树的深度](https://github.com/DinghaoLI/Coding-Interviews-Golang/tree/master/039-%e4%ba%8c%e5%8f%89%e6%a0%91%e7%9a%84%e6%b7%b1%e5%ba%a6) | 66 | | [039-平衡二叉树[附加]](https://github.com/DinghaoLI/Coding-Interviews-Golang/tree/master/039-%e5%b9%b3%e8%a1%a1%e4%ba%8c%e5%8f%89%e6%a0%91%5b%e9%99%84%e5%8a%a0%5d) | 67 | | [040-数组中只出现一次的数字](https://github.com/DinghaoLI/Coding-Interviews-Golang/tree/master/040-%e6%95%b0%e7%bb%84%e4%b8%ad%e5%8f%aa%e5%87%ba%e7%8e%b0%e4%b8%80%e6%ac%a1%e7%9a%84%e6%95%b0%e5%ad%97) | 68 | | [041-和为S的两个数字](https://github.com/DinghaoLI/Coding-Interviews-Golang/tree/master/041-%e5%92%8c%e4%b8%baS%e7%9a%84%e4%b8%a4%e4%b8%aa%e6%95%b0%e5%ad%97) | 69 | | [041-和为S的连续正数序列](https://github.com/DinghaoLI/Coding-Interviews-Golang/tree/master/041-%e5%92%8c%e4%b8%baS%e7%9a%84%e8%bf%9e%e7%bb%ad%e6%ad%a3%e6%95%b0%e5%ba%8f%e5%88%97) | 70 | | [042-左旋转字符串](https://github.com/DinghaoLI/Coding-Interviews-Golang/tree/master/042-%e5%b7%a6%e6%97%8b%e8%bd%ac%e5%ad%97%e7%ac%a6%e4%b8%b2) | 71 | | [042-翻转单词顺序列](https://github.com/DinghaoLI/Coding-Interviews-Golang/tree/master/042-%e7%bf%bb%e8%bd%ac%e5%8d%95%e8%af%8d%e9%a1%ba%e5%ba%8f%e5%88%97) | 72 | | [044-扑克牌顺子](https://github.com/DinghaoLI/Coding-Interviews-Golang/tree/master/044-%e6%89%91%e5%85%8b%e7%89%8c%e9%a1%ba%e5%ad%90) | 73 | | [045-孩子们的游戏(圆圈中最后剩下的数)](https://github.com/DinghaoLI/Coding-Interviews-Golang/tree/master/045-%e5%ad%a9%e5%ad%90%e4%bb%ac%e7%9a%84%e6%b8%b8%e6%88%8f(%e5%9c%86%e5%9c%88%e4%b8%ad%e6%9c%80%e5%90%8e%e5%89%a9%e4%b8%8b%e7%9a%84%e6%95%b0)) | 74 | | [046-求1+2+3+...+n](https://github.com/DinghaoLI/Coding-Interviews-Golang/tree/master/046-%e6%b1%821%2b2%2b3%2b...%2bn) | 75 | | [047-不用加减乘除做加法](https://github.com/DinghaoLI/Coding-Interviews-Golang/tree/master/047-%e4%b8%8d%e7%94%a8%e5%8a%a0%e5%87%8f%e4%b9%98%e9%99%a4%e5%81%9a%e5%8a%a0%e6%b3%95) | 76 | | [048-不能被继承的类](https://github.com/DinghaoLI/Coding-Interviews-Golang/tree/master/048-%e4%b8%8d%e8%83%bd%e8%a2%ab%e7%bb%a7%e6%89%bf%e7%9a%84%e7%b1%bb) | 77 | | [049-把字符串转换成整数](https://github.com/DinghaoLI/Coding-Interviews-Golang/tree/master/049-%e6%8a%8a%e5%ad%97%e7%ac%a6%e4%b8%b2%e8%bd%ac%e6%8d%a2%e6%88%90%e6%95%b4%e6%95%b0) | 78 | | [051-数组中重复的数字](https://github.com/DinghaoLI/Coding-Interviews-Golang/tree/master/051-%e6%95%b0%e7%bb%84%e4%b8%ad%e9%87%8d%e5%a4%8d%e7%9a%84%e6%95%b0%e5%ad%97) | 79 | | [052-构建乘积数组](https://github.com/DinghaoLI/Coding-Interviews-Golang/tree/master/052-%e6%9e%84%e5%bb%ba%e4%b9%98%e7%a7%af%e6%95%b0%e7%bb%84) | 80 | | [053-正则表达式匹配](https://github.com/DinghaoLI/Coding-Interviews-Golang/tree/master/053-%e6%ad%a3%e5%88%99%e8%a1%a8%e8%be%be%e5%bc%8f%e5%8c%b9%e9%85%8d) | 81 | | [054-表示数值的字符串](https://github.com/DinghaoLI/Coding-Interviews-Golang/tree/master/054-%e8%a1%a8%e7%a4%ba%e6%95%b0%e5%80%bc%e7%9a%84%e5%ad%97%e7%ac%a6%e4%b8%b2) | 82 | | [055-字符流中第一个不重复的字符](https://github.com/DinghaoLI/Coding-Interviews-Golang/tree/master/055-%e5%ad%97%e7%ac%a6%e6%b5%81%e4%b8%ad%e7%ac%ac%e4%b8%80%e4%b8%aa%e4%b8%8d%e9%87%8d%e5%a4%8d%e7%9a%84%e5%ad%97%e7%ac%a6) | 83 | | [056-链表中环的入口结点](https://github.com/DinghaoLI/Coding-Interviews-Golang/tree/master/056-%e9%93%be%e8%a1%a8%e4%b8%ad%e7%8e%af%e7%9a%84%e5%85%a5%e5%8f%a3%e7%bb%93%e7%82%b9) | 84 | | [057-删除链表中重复的结点](https://github.com/DinghaoLI/Coding-Interviews-Golang/tree/master/057-%e5%88%a0%e9%99%a4%e9%93%be%e8%a1%a8%e4%b8%ad%e9%87%8d%e5%a4%8d%e7%9a%84%e7%bb%93%e7%82%b9) | 85 | | [058-二叉树的下一个结点](https://github.com/DinghaoLI/Coding-Interviews-Golang/tree/master/058-%e4%ba%8c%e5%8f%89%e6%a0%91%e7%9a%84%e4%b8%8b%e4%b8%80%e4%b8%aa%e7%bb%93%e7%82%b9) | 86 | | [059-对称的二叉树](https://github.com/DinghaoLI/Coding-Interviews-Golang/tree/master/059-%e5%af%b9%e7%a7%b0%e7%9a%84%e4%ba%8c%e5%8f%89%e6%a0%91) | 87 | | [060-把二叉树打印成多行](https://github.com/DinghaoLI/Coding-Interviews-Golang/tree/master/060-%e6%8a%8a%e4%ba%8c%e5%8f%89%e6%a0%91%e6%89%93%e5%8d%b0%e6%88%90%e5%a4%9a%e8%a1%8c) | 88 | | [061-按之字形顺序打印二叉树](https://github.com/DinghaoLI/Coding-Interviews-Golang/tree/master/061-%e6%8c%89%e4%b9%8b%e5%ad%97%e5%bd%a2%e9%a1%ba%e5%ba%8f%e6%89%93%e5%8d%b0%e4%ba%8c%e5%8f%89%e6%a0%91) | 89 | | [062-序列化二叉树](https://github.com/DinghaoLI/Coding-Interviews-Golang/tree/master/062-%e5%ba%8f%e5%88%97%e5%8c%96%e4%ba%8c%e5%8f%89%e6%a0%91) | 90 | | [063-二叉搜索树的第K个结点](https://github.com/DinghaoLI/Coding-Interviews-Golang/tree/master/063-%e4%ba%8c%e5%8f%89%e6%90%9c%e7%b4%a2%e6%a0%91%e7%9a%84%e7%ac%acK%e4%b8%aa%e7%bb%93%e7%82%b9) | 91 | | [064-数据流之中的中位数](https://github.com/DinghaoLI/Coding-Interviews-Golang/tree/master/064-%e6%95%b0%e6%8d%ae%e6%b5%81%e4%b9%8b%e4%b8%ad%e7%9a%84%e4%b8%ad%e4%bd%8d%e6%95%b0) | 92 | | [065-滑动窗口的最大值](https://github.com/DinghaoLI/Coding-Interviews-Golang/tree/master/065-%e6%bb%91%e5%8a%a8%e7%aa%97%e5%8f%a3%e7%9a%84%e6%9c%80%e5%a4%a7%e5%80%bc) | 93 | 94 | -------------------------------------------------------------------------------- /utils/maxHeap.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "fmt" 5 | "math" 6 | ) 7 | 8 | type MaxHeap struct { 9 | Element []int 10 | } 11 | 12 | 13 | 14 | // MaxHeap constructor 15 | func NewMaxHeap() *MaxHeap { 16 | first := math.MaxInt64 17 | h := &MaxHeap{Element: []int{first}} 18 | return h 19 | } 20 | 21 | // Length of Maxheap 22 | func (H *MaxHeap) Length() int { 23 | return len(H.Element) - 1 24 | } 25 | 26 | // Get the Maximum of the Maxheap 27 | func (H *MaxHeap) Max() (int, error) { 28 | if len(H.Element) > 1 { 29 | return H.Element[1], nil 30 | } 31 | return -1, fmt.Errorf("heap is empty") 32 | } 33 | 34 | // Get the Maximum of the Maxheap 35 | func (H *MaxHeap) GetMax() int { 36 | if H.Length() > 0 { 37 | return H.Element[1] 38 | } 39 | return math.MaxInt64 40 | } 41 | 42 | // Inserting items requires ensuring the nature of the Maxheap 43 | func (H *MaxHeap) Insert(v int) { 44 | H.Element = append(H.Element, v) 45 | i := len(H.Element) - 1 46 | for ; (H.Element[i/2]) < v; i /= 2 { 47 | H.Element[i] = H.Element[i/2] 48 | } 49 | 50 | H.Element[i] = v 51 | } 52 | 53 | // Delete and return the Maximum 54 | func (H *MaxHeap) DeleteMax() (int, error) { 55 | if len(H.Element) <= 1 { 56 | return -1, fmt.Errorf("MaxHeap is empty") 57 | } 58 | MaxElement := H.Element[1] 59 | lastElement := H.Element[len(H.Element)-1] 60 | var i, child int 61 | for i = 1; i*2 < len(H.Element); i = child { 62 | child = i * 2 63 | if child < len(H.Element)-1 && H.Element[child+1] > H.Element[child] { 64 | child++ 65 | } 66 | if lastElement < H.Element[child] { 67 | H.Element[i] = H.Element[child] 68 | } else { 69 | break 70 | } 71 | } 72 | H.Element[i] = lastElement 73 | H.Element = H.Element[:len(H.Element)-1] 74 | return MaxElement, nil 75 | } 76 | -------------------------------------------------------------------------------- /utils/maxHeap_test.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "testing" 5 | "fmt" 6 | ) 7 | 8 | func Test_MaxHeap(t *testing.T) { 9 | heap := NewMaxHeap() 10 | if heap.Length() == 0 { 11 | t.Log("Pass") 12 | } else { 13 | t.Error("Failed") 14 | } 15 | 16 | heap.Insert(1) 17 | heap.Insert(2) 18 | heap.Insert(3) 19 | heap.Insert(4) 20 | heap.Insert(5) 21 | 22 | 23 | if heap.Length() == 5 { 24 | t.Log("Pass") 25 | } else { 26 | t.Error("Failed") 27 | } 28 | 29 | v, _ := heap.Max() 30 | if v == 5 { 31 | t.Log("Pass") 32 | } else { 33 | t.Error("Failed") 34 | } 35 | 36 | v, _ = heap.DeleteMax() 37 | if v == 5 { 38 | t.Log("Pass") 39 | } else { 40 | t.Error("Failed") 41 | } 42 | 43 | v, _ = heap.DeleteMax() 44 | if v == 4 { 45 | t.Log("Pass") 46 | } else { 47 | fmt.Println(v) 48 | t.Error("Failed") 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /utils/minHeap.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "fmt" 5 | "math" 6 | ) 7 | 8 | type MinHeap struct { 9 | Element []int 10 | } 11 | 12 | 13 | 14 | // MinHeap constructor 15 | func NewMinHeap() *MinHeap { 16 | first := math.MinInt64 17 | h := &MinHeap{Element: []int{first}} 18 | return h 19 | } 20 | 21 | // Length of Minheap 22 | func (H *MinHeap) Length() int { 23 | return len(H.Element) - 1 24 | } 25 | 26 | // Get the Minimum of the Minheap 27 | func (H *MinHeap) Min() (int, error) { 28 | if len(H.Element) > 1 { 29 | return H.Element[1], nil 30 | } 31 | return -1, fmt.Errorf("heap is empty") 32 | } 33 | 34 | // Get the Minimum of the Minheap 35 | func (H *MinHeap) GetMin() int { 36 | if H.Length() > 0 { 37 | return H.Element[1] 38 | } 39 | return math.MinInt64 40 | } 41 | 42 | // Inserting items requires ensuring the nature of the Minheap 43 | func (H *MinHeap) Insert(v int) { 44 | H.Element = append(H.Element, v) 45 | i := len(H.Element) - 1 46 | for ; (H.Element[i/2]) > v; i /= 2 { 47 | H.Element[i] = H.Element[i/2] 48 | } 49 | 50 | H.Element[i] = v 51 | } 52 | 53 | // Delete and return the Minimum 54 | func (H *MinHeap) DeleteMin() (int, error) { 55 | if len(H.Element) <= 1 { 56 | return -1, fmt.Errorf("MinHeap is empty") 57 | } 58 | MinElement := H.Element[1] 59 | lastElement := H.Element[len(H.Element)-1] 60 | var i, child int 61 | for i = 1; i*2 < len(H.Element); i = child { 62 | child = i * 2 63 | if child < len(H.Element)-1 && H.Element[child+1] < H.Element[child] { 64 | child++ 65 | } 66 | if lastElement > H.Element[child] { 67 | H.Element[i] = H.Element[child] 68 | } else { 69 | break 70 | } 71 | } 72 | H.Element[i] = lastElement 73 | H.Element = H.Element[:len(H.Element)-1] 74 | return MinElement, nil 75 | } 76 | -------------------------------------------------------------------------------- /utils/minHeap_test.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "testing" 5 | "fmt" 6 | ) 7 | 8 | func Test_MinHeap(t *testing.T) { 9 | heap := NewMinHeap() 10 | if heap.Length() == 0 { 11 | t.Log("Pass") 12 | } else { 13 | t.Error("Failed") 14 | } 15 | 16 | heap.Insert(1) 17 | heap.Insert(2) 18 | heap.Insert(3) 19 | heap.Insert(4) 20 | heap.Insert(5) 21 | 22 | 23 | if heap.Length() == 5 { 24 | t.Log("Pass") 25 | } else { 26 | t.Error("Failed") 27 | } 28 | 29 | v, _ := heap.Min() 30 | if v == 1 { 31 | t.Log("Pass") 32 | } else { 33 | t.Error("Failed") 34 | } 35 | 36 | v, _ = heap.DeleteMin() 37 | if v == 1 { 38 | t.Log("Pass") 39 | } else { 40 | t.Error("Failed") 41 | } 42 | 43 | v, _ = heap.DeleteMin() 44 | if v == 2 { 45 | t.Log("Pass") 46 | } else { 47 | fmt.Println(v) 48 | t.Error("Failed") 49 | } 50 | v, _ = heap.DeleteMin() 51 | if v == 3 { 52 | t.Log("Pass") 53 | } else { 54 | fmt.Println(v) 55 | t.Error("Failed") 56 | } 57 | v, _ = heap.DeleteMin() 58 | if v == 4 { 59 | t.Log("Pass") 60 | } else { 61 | fmt.Println(v) 62 | t.Error("Failed") 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /utils/stack.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import "errors" 4 | 5 | type Stack []interface {} 6 | 7 | func (stack Stack) Len() int { 8 | return len(stack) 9 | } 10 | 11 | func (stack Stack) IsEmpty() bool { 12 | return len(stack) == 0 13 | } 14 | 15 | func (stack Stack) Cap() int { 16 | return cap(stack) 17 | } 18 | 19 | func (stack *Stack) Push(value interface{}) { 20 | *stack = append(*stack, value) 21 | } 22 | 23 | func (stack Stack) Top() (interface{}, error) { 24 | if len(stack) == 0 { 25 | return nil, errors.New("Out of index, len is 0") 26 | } 27 | return stack[len(stack) - 1], nil 28 | } 29 | 30 | func (stack *Stack) Pop() (interface{}, error) { 31 | theStack := *stack 32 | if len(theStack) == 0 { 33 | return nil, errors.New("Out of index, len is 0") 34 | } 35 | value := theStack[len(theStack) - 1] 36 | *stack = theStack[:len(theStack) - 1] 37 | return value, nil 38 | } -------------------------------------------------------------------------------- /utils/stack_test.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestStack_Len(t *testing.T) { 8 | var myStack Stack 9 | myStack.Push(1) 10 | myStack.Push("test") 11 | if myStack.Len() == 2 { 12 | t.Log("Pass Stack.Len") 13 | } else { 14 | t.Error("Failed Stack.Len") 15 | } 16 | } 17 | 18 | func TestStack_IsEmpty(t *testing.T) { 19 | var mStack Stack 20 | if mStack.IsEmpty() { 21 | t.Log("Pass Stack.IsEmpty") 22 | } else { 23 | t.Error("Failed Stack.IsEmpty") 24 | } 25 | } 26 | 27 | func TestStack_Cap(t *testing.T) { 28 | myStack := make(Stack, 3) 29 | if myStack.Cap() == 3 { 30 | t.Log("Pass Stack.Cap") 31 | } else { 32 | t.Error("Failed Stack.Cap") 33 | } 34 | } 35 | 36 | func TestStack_Push(t *testing.T) { 37 | var mStack Stack 38 | mStack.Push(3) 39 | if mStack.Len() == 1 { 40 | t.Log("Pass Stack.Push") 41 | } else { 42 | t.Error("Failed Stack.Push") 43 | } 44 | } 45 | 46 | func TestStack_Top(t *testing.T) { 47 | var mStack Stack 48 | if _, err := mStack.Top(); err == nil { 49 | t.Error("Failed Stack.Top") 50 | } 51 | mStack.Push(3) 52 | if value, _ := mStack.Top(); value == 3 { 53 | t.Log("Pass Stack.Top") 54 | } else { 55 | t.Errorf("Failed Stack.Top, value is %d", value) 56 | } 57 | } 58 | 59 | func TestStack_Pop(t *testing.T) { 60 | var mStack Stack 61 | if _, err := mStack.Pop(); err == nil { 62 | t.Error("Failed Stack.Top") 63 | } 64 | mStack.Push("test") 65 | mStack.Push(3) 66 | if value, _ := mStack.Pop(); value == 3 && mStack.Len() == 1 { 67 | t.Log("Pass Stack.Pop") 68 | } else { 69 | t.Errorf("Failed Stack.Pop, value is %d, len is %d", value, mStack.Len()) 70 | } 71 | } --------------------------------------------------------------------------------