diff --git "a/LeetCode/\345\211\221\346\214\207offer/day11_\345\217\214\346\214\207\351\222\210(\347\256\200\345\215\225)/\345\210\240\351\231\244\351\223\276\350\241\250\347\232\204\350\212\202\347\202\271.go" "b/LeetCode/\345\211\221\346\214\207offer/day11_\345\217\214\346\214\207\351\222\210(\347\256\200\345\215\225)/\345\210\240\351\231\244\351\223\276\350\241\250\347\232\204\350\212\202\347\202\271.go" new file mode 100644 index 0000000..93e0b30 --- /dev/null +++ "b/LeetCode/\345\211\221\346\214\207offer/day11_\345\217\214\346\214\207\351\222\210(\347\256\200\345\215\225)/\345\210\240\351\231\244\351\223\276\350\241\250\347\232\204\350\212\202\347\202\271.go" @@ -0,0 +1,59 @@ +// 题目链接:https://leetcode.cn/problems/shan-chu-lian-biao-de-jie-dian-lcof/?envType=study-plan&id=lcof +// day11/31 +// 第 11 天主题为:双指针(简单) +// 包含两道题目: +// 剑指offer18.删除链表的节点 +// 剑指offer22.链表中倒数第k个节点 + +package main + +//思路很简单的题目,就是要注意代码的鲁棒性 +//链表相关的题目非常考察代码的鲁棒性,谨防访问空节点.Next 这种情况的发生。 + +//要删除某个节点,我们需要记录要删除节点的前一个节点,将前一个节点的 Next域 指向要删除节点的下一个节点, +//所以我们要保存要删除节点的前一个节点,才能做到删除节点。 +//cur=head,一次遍历链表,遍历条件为 cur.Next != nil,如果 cur.Next 是我们要删除的节点, +//cur.Next = cur.Next.Next,然后结束遍历,否则 cur=cur.Next,向后移动节点。最后返回 head。 + +//有一种情况需要额外判断,就是 head 节点就是要删除的节点,因为我们无法保存该节点的前一个节点, +//针对这种情况,直接返回 head.Next 即可 +//在遍历之前,我们就要对这种情况进行判断。 + +//Definition for singly-linked list. +type ListNode struct { + Val int + Next *ListNode +} + +func deleteNode(head *ListNode, val int) *ListNode { + if head.Val == val{ + return head.Next + } + cur := head + for cur.Next != nil{ + if cur.Next.Val == val{ + cur.Next = cur.Next.Next + // 跳出是为了应对要删除的节点为链表最后一个,会造成访问空指针的情况出现 + break + } + cur = cur.Next + } + return head +} + +//稍微来扩展一下,对应《剑指offer》 +// +//如果题目给的是一个链表的头指针 和 一个要被删除的节点,而非节点的值。那我们应该如何处理呢? +//按照如上时间复杂度O(n)的思路当然是可以的,那是不是一定需要找到被删除节点的前一个节点呢? +//答案是否定的,我们可以很方便地找到要删除的节点的下一个节点。如果我们把下一个节点的内容复制到被删除节点上来覆盖该节点原有的内容,再把下一个节点删除,那是不是就相当于把需要删除的节点删除了? + +//(这个思路很新奇,在用户的需求中,删除节点的意思就是删除节点值,而非内存空间,要抓住这样的需求和抽象层析的信息不对称来寻求突破) + +//上述思路还有一个问题:如果被删除的节点位于链表的尾部,Next域为空,就无法使用了,在这种情况下,我们只能遍历链表,得到该节点的前一个节点,将该节点删除。 +//值得注意的是:上述代码仍然不是一段完美的代码,因为它基于这样一种假设:要删除的节点确定在链表中。我们需要O(n)的时间才能判断链表是否包含某一节点。 +//在面试的过程中,我们可以和面试官讨论这个假设,这样面试官就会觉得我们考虑问题非常全面。 +// +//考察点: +//- 应聘者对链表的编程能力 +//- 创新思维能力,这需要应聘者打破常规思维模式。当我们想要删除某一节点时,不一定要删除节点本身,可以先把下一个节点的内容复制出来覆盖要被删除节点的内容,再将下一个节点删除,这种思路不容易想到 +//- 代码的鲁棒性,也即应聘者思维的全面性,全面考虑到该节点是链表尾结点或头结点的情况。 \ No newline at end of file diff --git "a/LeetCode/\345\211\221\346\214\207offer/day11_\345\217\214\346\214\207\351\222\210(\347\256\200\345\215\225)/\351\223\276\350\241\250\344\270\255\345\200\222\346\225\260\347\254\254K\344\270\252\350\212\202\347\202\271.go" "b/LeetCode/\345\211\221\346\214\207offer/day11_\345\217\214\346\214\207\351\222\210(\347\256\200\345\215\225)/\351\223\276\350\241\250\344\270\255\345\200\222\346\225\260\347\254\254K\344\270\252\350\212\202\347\202\271.go" new file mode 100644 index 0000000..bdb2d64 --- /dev/null +++ "b/LeetCode/\345\211\221\346\214\207offer/day11_\345\217\214\346\214\207\351\222\210(\347\256\200\345\215\225)/\351\223\276\350\241\250\344\270\255\345\200\222\346\225\260\347\254\254K\344\270\252\350\212\202\347\202\271.go" @@ -0,0 +1,38 @@ +//题目链接:https://leetcode.cn/problems/lian-biao-zhong-dao-shu-di-kge-jie-dian-lcof/ + +package main + +//这道题比较通俗的解法是两次遍历 +//先一次遍历链表,得到链表的长度 n,倒数第 k 个节点,即正数第 n-k+1 个节点,第二次遍历输出该节点即可。 +func getKthFromEnd(head *ListNode, k int) *ListNode { + n := 0 + cur := head + // 第一次遍历得到链表长度n + for cur != nil{ + n ++ + cur = cur.Next + } + cur = head + x := n-k+1 + //第二次遍历找到整数第n-k+1个节点并返回 + for i:=0;i0,那 x 和 y 走 m+n-x 步后到达该节点,若不存在公共节点,x 和 y 始终不相等,知道 x 和 y 共同走向合并链表的尽头, +//也就是空节点,此时,返回 A or B 都是空节点。 +// +//针对细节,在做一些描述(也是自己曾经的疑惑): +//1. 对一个链表(长度为n),从头结点走到尾结点需要 n-1 步,走 n 步会到达尾结点的Next域(即空节点),在本题的双指针解法中,真是要走到该空节点位置,然后再走到另一条链表的道路,和另一个指针一起判断是否存在公共节点。 +//2. 公共节点,该节点不仅Val域相同,Next域也是相同的,所以以该节点作为头结点的链表长度也是相同的。 +//3. 若两链表长度相同呢,感觉都走不到对方的链表上面?确实是这样,因为这种情况下我们也不需要走到对方的链表上,具体分两种情况 +// 1. 存在公共节点,两指针 x 和 y 在第一条路上就能找到公共节点 +// 2. 不存在公共节点,x 和 y 走到各自链表的尾结点的 Next 域时,已经相同,返回空节点即可 + +// 此解法时间复杂度O(m+n),空间复杂度O(1),满足题目要求(题目描述是O(n),这里我认为已经满足) +func getIntersectionNode_2(headA, headB *ListNode) *ListNode { + x,y := headA,headB + for x != y{ + if x != nil{ + x = x.Next + } else { + x = headB + } + if y != nil{ + y = y.Next + } else { + y = headA + } + } + return x +} \ No newline at end of file diff --git "a/LeetCode/\345\211\221\346\214\207offer/day12_\345\217\214\346\214\207\351\222\210(\347\256\200\345\215\225)2/\345\220\210\345\271\266\344\270\244\344\270\252\346\216\222\345\272\217\347\232\204\351\223\276\350\241\250.go" "b/LeetCode/\345\211\221\346\214\207offer/day12_\345\217\214\346\214\207\351\222\210(\347\256\200\345\215\225)2/\345\220\210\345\271\266\344\270\244\344\270\252\346\216\222\345\272\217\347\232\204\351\223\276\350\241\250.go" new file mode 100644 index 0000000..475d552 --- /dev/null +++ "b/LeetCode/\345\211\221\346\214\207offer/day12_\345\217\214\346\214\207\351\222\210(\347\256\200\345\215\225)2/\345\220\210\345\271\266\344\270\244\344\270\252\346\216\222\345\272\217\347\232\204\351\223\276\350\241\250.go" @@ -0,0 +1,60 @@ +//题目链接:https://leetcode.cn/problems/he-bing-liang-ge-pai-xu-de-lian-biao-lcof/?envType=study-plan&id=lcof + +package main + +//Definition for singly-linked list. +type ListNode struct { + Val int + Next *ListNode +} + +//很经典的链表题目了,计算机考研算法必做题 +//我的思路是,新建一个最终返回链表的头结点 head,Val域为0(随便设),Next域为空,之后的链表合并操作在其 Next域进行,最终返回 head.Next 即可。 +//再声明一个变量 cur = head,用于合并链表,具体分为两步: +//1. 循环合并:当 l1 与 l2 链表头结点均非空时,判断两者头结点的Val域,谁更小,cur.Next 指向该节点, +// 然后该链表头节点指向其下一个节点,cur也指向其Next域,如此循环,直至 l1 或 l2 为空; +//2. 合并剩余尾部:此时若 l1 链表非空,则 head 指向的链表所有节点值均小于等于 l1 链表剩余节点值,cur.Next 指向 l1 即可; +// 若是 l2 链表非空,同理。 + +//最终返回 head.Next 即可。 +func mergeTwoLists(l1 *ListNode, l2 *ListNode) *ListNode { + head := &ListNode{0,nil} + cur := head + for l1 != nil && l2 != nil{ + if l1.Val <= l2.Val{ + cur.Next = l1 + cur = cur.Next + l1 = l1.Next + } else { + cur.Next = l2 + cur = cur.Next + l2 = l2.Next + } + } + if l1 != nil{ + cur.Next = l1 + } + if l2 != nil{ + cur.Next = l2 + } + return head.Next +} + +//这道题还可以递归解决: +//当 l1 或 l2 链表为空时,无需合并,直接返回另一个非空链表即可。 +//否则,判断 l1 和 l2 链表的头结点谁更小,改变其 Next 域,递归地指向 l1 和 l2.Next 合并链表的结果,最后返回该链表头结点。递归结束条件为某一链表为空。 +func mergeTwoLists_2(l1 *ListNode, l2 *ListNode) *ListNode { + if l1 == nil{ + return l2 + } + if l2 == nil{ + return l1 + } + if l1.Val <= l2.Val{ + l1.Next = mergeTwoLists(l1.Next,l2) + return l1 + } else { + l2.Next = mergeTwoLists(l1,l2.Next) + return l2 + } +} \ No newline at end of file diff --git "a/LeetCode/\345\211\221\346\214\207offer/day13_\345\217\214\346\214\207\351\222\210(\347\256\200\345\215\225)3/\345\222\214\344\270\272s\347\232\204\344\270\244\344\270\252\346\225\260\345\255\227.go" "b/LeetCode/\345\211\221\346\214\207offer/day13_\345\217\214\346\214\207\351\222\210(\347\256\200\345\215\225)3/\345\222\214\344\270\272s\347\232\204\344\270\244\344\270\252\346\225\260\345\255\227.go" new file mode 100644 index 0000000..2a796c9 --- /dev/null +++ "b/LeetCode/\345\211\221\346\214\207offer/day13_\345\217\214\346\214\207\351\222\210(\347\256\200\345\215\225)3/\345\222\214\344\270\272s\347\232\204\344\270\244\344\270\252\346\225\260\345\255\227.go" @@ -0,0 +1,66 @@ +// 题目链接:https://leetcode.cn/problems/he-wei-sde-liang-ge-shu-zi-lcof/?envType=study-plan&id=lcof +// day13/31 +// 第 13 天主题为:双指针(简单) +// 包含三道题目: +// 剑指offer21.调整数组顺序使奇数位于偶数前面 +// 剑指offer57.和为s的两个数字 +// 剑指offer58-I.翻转单词顺序 + +package main + +//梦回LeetCode第一题:两数之和,有点像是吧,这道题和两数之和的区别在于:数组有序。 +//方法 1 :先来暴力解法,双层遍历 +//第一层遍历从头开始,第二层遍历从第一层遍历元素的下一个元素开始,若两数之和等于 target,返回即可。 +func twoSum(nums []int, target int) []int { + n := len(nums) + for i:=0;i target{ + right -- + } else if nums[left]+nums[right] < target{ + left ++ + } else { + return []int{nums[left],nums[right]} + } + } + return []int{0,0} +} +//时间复杂度O(n),空间复杂度O(1),充分利用题目所给条件,为最优解法! \ No newline at end of file diff --git "a/LeetCode/\345\211\221\346\214\207offer/day13_\345\217\214\346\214\207\351\222\210(\347\256\200\345\215\225)3/\347\277\273\350\275\254\345\215\225\350\257\215\351\241\272\345\272\217.go" "b/LeetCode/\345\211\221\346\214\207offer/day13_\345\217\214\346\214\207\351\222\210(\347\256\200\345\215\225)3/\347\277\273\350\275\254\345\215\225\350\257\215\351\241\272\345\272\217.go" new file mode 100644 index 0000000..ee0057f --- /dev/null +++ "b/LeetCode/\345\211\221\346\214\207offer/day13_\345\217\214\346\214\207\351\222\210(\347\256\200\345\215\225)3/\347\277\273\350\275\254\345\215\225\350\257\215\351\241\272\345\272\217.go" @@ -0,0 +1,78 @@ +//题目链接:https://leetcode.cn/problems/fan-zhuan-dan-ci-shun-xu-lcof/?envType=study-plan&id=lcof + +package main + +import ( + "strings" +) + +//我的想法是使用栈,每遇到一个完整的单词,添加到栈中,最后出栈。 +// +//具体过程:一次遍历,用 string.Builder 构建字符串,若当前字符不是空格,则写入将当前字符写入 sb, +//若是,则将 sb 的内容写入栈 并 清空内容,准备下一个单词的填充。 +func reverseWords(s string) string { + var sb strings.Builder + n := len(s) + if n == 0{ + return "" + } + stack := []string{} + for i:=0;i0 && s[i-1]!=32{ + stack = append(stack,sb.String()) + sb.Reset() + } else if s[i]!=32{ + sb.WriteByte(s[i]) + } + } + if s[n-1] != 32{ + stack = append(stack,sb.String()) + } + sb.Reset() + for i:=len(stack)-1;i>=0;i--{ + sb.WriteString(stack[i]) + sb.WriteByte(' ') + } + res := sb.String() + // 去掉末尾空格 + // 同时注意特殊情况:s全为空格,res长度为0,对其进行去除末尾空格操作会索引越界 + if len(res)==0{ + return "" + } + res = res[:len(res)-1] + return res +} + + +//还可以用双指针: +// +//声明变量 res 为空字符串,left 和 right 两指针初始化为 0,左右指针分别用于寻找每个单词的左右边界。 +//一次遍历字符串,左指针向右移动寻找第一个非空格字符,为要寻找字符串的左边界,然后开始寻找右边界,先将 left 的值赋给 right,然后 right 向右移动, +//直到到达字符串末尾或者寻找到空格字符,此时 left 和 right 分别指向一个单词的左右边界,将其加入 res,然后将 right 赋给 left,开始寻找下一个单词。 +// +//具体实现时,注意每加入一个单词的同时,还要添加一个空格,最后返回 res 前,处理掉最后添加的空格。 +// +//注意特殊情况的处理,当输入字符串全为空格时,res 长度为空,此时如果对其进行去除空格的操作,会索引越界。 +// 方法2: +func reverseWords_2(s string) string { + n := len(s) + left,right := 0,0 + res := "" + for left < n{ + if s[left] == 32{ + left ++ + } else { + right = left + for right=m || j >= n || bitsum(i)+bitsum(j)>k{ + return 0 + } + if used[i][j]{ + return 0 + } + used[i][j] = true + return 1 + dfs(i,j+1) + dfs(i+1,j) + } + return dfs(0,0) +} + +func bitsum(x int) int { + res := 0 + for x > 0{ + res += x % 10 + x /= 10 + } + return res +} + + +//方法2:BFS +//本题中,虽然我们也使用 used 数组记录每个位置是否被访问过,但是我们不需要回溯操作,所以是可以使用 BFS 的。 +//BFS通常需要借助队列的先进先出特性来实现 + +func movingCount_2(m int, n int, k int) int { + used := make([][]bool,m) + for i:=0;i 4{ + return false + } + return true +} + + +//另外,我们也可以不用排序,而是使用一次遍历的方式求得最大最小值(0除外) +//思路和排序是一致的,代码如下 +//这种解法注意 minNum 和 maxNum 的初始化,minNum 和 maxNum 分别初始化为最大和最小的数字 +func isStraight_2(nums []int) bool { + minNum,maxNum := 14,0 + record := map[int]struct{}{} + for _,num := range nums{ + if num == 0{ + continue + } + if _,ok:=record[num];ok{ + return false + } + record[num] = struct{}{} + minNum = min(minNum,num) + maxNum = max(maxNum,num) + } + if maxNum - minNum > 4{ + return false + } + return true +} + +func max(x,y int) int{ + if x > y{ + return x + } + return y +} + +func min(x,y int) int{ + if x < y{ + return x + } + return y +} \ No newline at end of file diff --git "a/LeetCode/\345\211\221\346\214\207offer/day16_\346\216\222\345\272\217(\347\256\200\345\215\225)/\346\212\212\346\225\260\347\273\204\346\216\222\346\210\220\346\234\200\345\260\217\347\232\204\346\225\260.go" "b/LeetCode/\345\211\221\346\214\207offer/day16_\346\216\222\345\272\217(\347\256\200\345\215\225)/\346\212\212\346\225\260\347\273\204\346\216\222\346\210\220\346\234\200\345\260\217\347\232\204\346\225\260.go" new file mode 100644 index 0000000..a949a27 --- /dev/null +++ "b/LeetCode/\345\211\221\346\214\207offer/day16_\346\216\222\345\272\217(\347\256\200\345\215\225)/\346\212\212\346\225\260\347\273\204\346\216\222\346\210\220\346\234\200\345\260\217\347\232\204\346\225\260.go" @@ -0,0 +1,55 @@ +// 题目链接:https://leetcode.cn/problems/ba-shu-zu-pai-cheng-zui-xiao-de-shu-lcof/ +package main + +import ( + "sort" + "strconv" +) + +//这道题我刚开始以为数组的元素都是个位数,想着用哈希表统计每个数字出现的频率,然后从 0 开始遍历,将制 +//定数目的数字添加至返回的在字符串中, 看了下示例,才知道数组的元素可以不是个位数。 +// +//但其实原理也是类似的,小数在前,大数在后,本质上是一个排序问题,设 nums 中两整数分别为 x 和 y, +//先将其转化为字符串,然后拼接位 x+y 和 y+x 作比较 +//- 若拼接字符串 x+y > y+x,则 x > y +//- 否则(包括相等的情况),x < y +//根据此规则,套用任何排序算法对 nums 执行即可。 +func minNumber(nums []int) string { + n := len(nums) + strNums := make([]string,len(nums)) + for i:=0;i12 的情况下,使用的是快速排序,经过递归,长度降至 12以下时,使用插入排序。 +//下面咱来不用标准库,自己复习下冒泡,来解这道题 +func minNumber_2(nums []int) string { + n := len(nums) + strNums := make([]string,len(nums)) + for i:=0;i strNums[j+1]+strNums[j]{ + strNums[j],strNums[j+1] = strNums[j+1],strNums[j] + } + } + } + res := "" + for i:=0;i k{ + return quickSort(nums,left,partitionIndex-1,k) + } else { + // 当相等时,达到我们的要求,返回“排好序”的数组 + return nums + } + } + return nums +} + +//移动左右指针,按照基准(这里使用nums[left])划分区域。最后返回基准所在的下标 +func partition(nums []int,left,right int) int { + // 基准 选择 left 指向的元素 + pivot := nums[left] + for left < right{ + for left=pivot{ + right -- + } + nums[left] = nums[right] + for left y{ + return x + } + return y +} + + + +func maxDepth_2(root *TreeNode) int { + if root == nil { + return 0 + } + res := 0 + q := []*TreeNode{root} + for len(q) != 0{ + res ++ + n := len(q) + for i:=0;i= 0 +} + +func depth(node *TreeNode) int { + if node == nil{ + return 0 + } + leftDepth := depth(node.Left) + rightDepth := depth(node.Right) + if leftDepth == -1 || rightDepth == -1 { + return -1 + } + if abs(leftDepth-rightDepth)>1{ + return -1 + } + return 1+max(leftDepth,rightDepth) +} \ No newline at end of file diff --git "a/LeetCode/\345\211\221\346\214\207offer/day19_\346\220\234\347\264\242\344\270\216\345\233\236\346\272\257\347\256\227\346\263\225(\344\270\255\347\255\211)2/BST\347\232\204\346\234\200\350\277\221\345\205\254\345\205\261\347\245\226\345\205\210.go" "b/LeetCode/\345\211\221\346\214\207offer/day19_\346\220\234\347\264\242\344\270\216\345\233\236\346\272\257\347\256\227\346\263\225(\344\270\255\347\255\211)2/BST\347\232\204\346\234\200\350\277\221\345\205\254\345\205\261\347\245\226\345\205\210.go" new file mode 100644 index 0000000..3c9dd00 --- /dev/null +++ "b/LeetCode/\345\211\221\346\214\207offer/day19_\346\220\234\347\264\242\344\270\216\345\233\236\346\272\257\347\256\227\346\263\225(\344\270\255\347\255\211)2/BST\347\232\204\346\234\200\350\277\221\345\205\254\345\205\261\347\245\226\345\205\210.go" @@ -0,0 +1,69 @@ +//题目链接:https://leetcode.cn/problems/er-cha-sou-suo-shu-de-zui-jin-gong-gong-zu-xian-lcof/ +// day19/31 +// 第 19 天主题为:搜索与回溯算法(中等) +// 包含三道题目: +// 剑指offer64.求1+2+...+n +// 剑指offer68-I.二叉搜索树的最近公共祖先 +// 剑指offer68-II.二叉树的最近公共祖先 +package main + + +//Definition for a binary tree node. +type TreeNode struct { + Val int + Left *TreeNode + Right *TreeNode +} + +//BST 的中序遍历是有序数组,root 节点的左子树中所有节点 val 都比 root.val 小,右子树中所有节点的 val 比root.val 大。 +//根据这个特点,我们对 root、p、q 的节点值进行分情况讨论: +// +//1. p.val > root.val 且 q.val < root.val,说明 p 和 q 分别存在于 root 节点的右左子树中,root 节点为最近公共祖先(p.val < q.val 同理); +// +//2. p.Val < root.Val 且 q.Val > root.Val,与第一种情况同理,p、q 分别在root 的左右子树中,root 节点为最近公共祖先; +// +//3. root.Val == p.Val 或者 root.Val == q.Val,说明另一个节点在 root 的左子树或者右子树中, root 已经是两节点的最近公共祖先, +// 因为我们是从整棵树的根节点开始往下遍历的 +// +//4. p.Val < root.Val 且 q.Val < root.Val,p 和 q 均在 root 的左子树中,root 的左孩子节点会是 p 和 q 的公共祖先, +// 对 root.Left 进行递归操作寻找 p 和 q 的最近公共祖先 +// +//5. p.Val > root.Val 且 q.Val > root.Val,与情况 4 同理,对 root.Right 进行递归操作寻找 p 和 q 的最近公共祖先。 +// +//因为 1 和 2同理,4 和 5 同理,精简一些,只有三种情况,令 p.Val 小于 q.Val +// +//1. p.Val <= root.Val 或者 root.val < q.Val,此时 root 为 最近公共祖先 +//2. p.Val < root.Val < q.Val,p 和 q 分别在 root 的左右子树中,root 为 最近公共祖先 +//3. p.Val < root.Val 且 q.Val < root.Val,对 root.Left 递归操作;若均大于,对 root.Right 递归操作 +// +//虽然我分析过程中写的是递归,但实际还是有递归和迭代两种写法的 + +//递归 + +func lowestCommonAncestor(root, p, q *TreeNode) *TreeNode { + if root.Valp.Val && root.Val>q.Val{ + return lowestCommonAncestor(root.Left,p,q) + } + return root +} + +//迭代 +func lowestCommonAncestor_2(root, p, q *TreeNode) *TreeNode { + for root != nil { + if p.Valroot.Val && q.Val>root.Val{ + root = root.Right + continue + } + return root + } + // 此种情况不会发生,因为题目说明p和q均存在于给定BST中 + // 只是为了保证代码的逻辑正确 + return root +} \ No newline at end of file diff --git "a/LeetCode/\345\211\221\346\214\207offer/day19_\346\220\234\347\264\242\344\270\216\345\233\236\346\272\257\347\256\227\346\263\225(\344\270\255\347\255\211)2/\344\272\214\345\217\211\346\240\221\347\232\204\346\234\200\350\277\221\345\205\254\345\205\261\347\245\226\345\205\210.go" "b/LeetCode/\345\211\221\346\214\207offer/day19_\346\220\234\347\264\242\344\270\216\345\233\236\346\272\257\347\256\227\346\263\225(\344\270\255\347\255\211)2/\344\272\214\345\217\211\346\240\221\347\232\204\346\234\200\350\277\221\345\205\254\345\205\261\347\245\226\345\205\210.go" new file mode 100644 index 0000000..b9a2e13 --- /dev/null +++ "b/LeetCode/\345\211\221\346\214\207offer/day19_\346\220\234\347\264\242\344\270\216\345\233\236\346\272\257\347\256\227\346\263\225(\344\270\255\347\255\211)2/\344\272\214\345\217\211\346\240\221\347\232\204\346\234\200\350\277\221\345\205\254\345\205\261\347\245\226\345\205\210.go" @@ -0,0 +1,67 @@ +//题目链接:https://leetcode.cn/problems/er-cha-shu-de-zui-jin-gong-gong-zu-xian-lcof/?envType=study-plan&id=lcof +package main + +//取消了第一版题目BST的条件,退化为普通的二叉树,我们就只能相对暴力地去解决这个问题。 +// +//解法参考自题解:https://leetcode.cn/problems/er-cha-shu-de-zui-jin-gong-gong-zu-xian-lcof/solution/mian-shi-ti-68-ii-er-cha-shu-de-zui-jin-gong-gon-7/ +// +//考虑通过递归对二叉树进行先序遍历,遇到 p 或 q 时返回,从底至顶回溯,当节点 p、q 分别在 root 的两侧 或 p、q 两者之一就是 root 节点时, +//root 为 p 和 q 的LCA,返回 root。 +// +//然后具体分析如何实现: +// +//1. 终止条件: +// 1. 当越过叶节点时,直接返回 nil; +// 2. root 等于 p 或 q 时,返回 root +//2. 递推工作: +// 1. 递归 root.Left,记返回值为 left; +// 2. 递归 root.Right,记返回值为 right; +//3. 返回值:根据 left 和 right,分为以下四种情况: +// 1. left 和 right 同时为空,说明 root 的左右子树均不包含 p 和 q,以 root 为根节点的子树不可能包含 p 和 q,返回 nil +// 2. left 为 空,right 不为空,说明 p 和 q 都包含在 root.Right 中,返回 right(分两种情况, +// 第一种:p 和 q 其中一个节点在 root 的右子树中,此时 right 为所包含的节点; +// 第二种:p 和 q 均在 root 的右子树中,此时 right 指向 p 和 q 的 LCA) +// 3. right 为 空,left 不为空,与情况2同理 +// 4. left 和 right 均不为空,说明 p、q 分别在 root 的左右子树中,root 为 LCA,返回 root 即可。 +func lowestCommonAncestor_3(root, p, q *TreeNode) *TreeNode { + // 递归结束条件 + if root == nil{ + return nil + } + if root.Val==p.Val || root.Val==q.Val{ + return root + } + // 开始递归 + left := lowestCommonAncestor(root.Left,p,q) + right := lowestCommonAncestor(root.Right,p,q) + // 情况1 + if left==nil && right==nil{ + return nil + } + // 情况2 + if left == nil{ + return right + } + // 情况3 + if right == nil{ + return left + } + // 情况4 + return root +} + +//这里想看过的一段话: +// 学习一个算法,最要的是弄清楚这个算法要解决什么样的问题,它的已知量(input)是什么? +// 待求的未知量(output)是什么 +// 如果这三个问题没找到答案就去学习算法,就会浪费大量时间在逻辑猜测与记忆上 + +//针对本题,对 lowestCommonAncestor 函数,本人分析如下 +//函数 lowestCommonAncestor 输入为 root、p 和 q +// +//输出分几种情况: +//1. 若 root 为空节点,直接返回 root 即可 +//2. 以 root 为根节点的树包含 p 和 q,返回 p 和 q 的 LCA; +//3. 以 root 为根节点的树只包含 p 或 q 其中一个,则返回 包含的节点; +//4. 以 root 为根节点的树不包含 p 且 不包含 q,返回 nil + +//解决问题:在以 root 为根节点的子树中,寻找 p 和 q 的 LCA diff --git "a/LeetCode/\345\211\221\346\214\207offer/day19_\346\220\234\347\264\242\344\270\216\345\233\236\346\272\257\347\256\227\346\263\225(\344\270\255\347\255\211)2/\346\261\2021+2+...+n.go" "b/LeetCode/\345\211\221\346\214\207offer/day19_\346\220\234\347\264\242\344\270\216\345\233\236\346\272\257\347\256\227\346\263\225(\344\270\255\347\255\211)2/\346\261\2021+2+...+n.go" new file mode 100644 index 0000000..9357abd --- /dev/null +++ "b/LeetCode/\345\211\221\346\214\207offer/day19_\346\220\234\347\264\242\344\270\216\345\233\236\346\272\257\347\256\227\346\263\225(\344\270\255\347\255\211)2/\346\261\2021+2+...+n.go" @@ -0,0 +1,26 @@ +//题目链接:https://leetcode.cn/problems/qiu-12n-lcof/?envType=study-plan&id=lcof +package main + +//先不考虑限制条件,有三种解法, +//1.高斯求和公式(使用乘除法) +//2.迭代(使用for或while) +//3.递归(递归终止条件需要使用if)。 +// +//考虑限制条件的话,难点在于如何实现这个 1~n 的循环。 +//我们可以利用运算符的短路效应实现该循环 +// +//先看下逻辑与 与 逻辑或 的短路效应 +//- if a && b; 若 a 为 false,对 b 的判断不会执行(即短路),直接返回 false +//- if a || b; 若 a 为 true,对 b的判断不会执行,直接返回 true +// +//我们可以利用这种短路机制,通过递归实现 1~n 的相加 +func sumNums(n int) int { + res := 0 + var f func(*int,int) bool + f = func(res *int,n int) bool{ + *res += n + return n > 0 && f(res,n-1) + } + f(&res,n) + return res +} \ No newline at end of file diff --git "a/LeetCode/\345\211\221\346\214\207offer/day1_\346\240\210\344\270\216\351\230\237\345\210\227(\347\256\200\345\215\225)/\345\214\205\345\220\253min\345\207\275\346\225\260\347\232\204\346\240\210.go" "b/LeetCode/\345\211\221\346\214\207offer/day1_\346\240\210\344\270\216\351\230\237\345\210\227(\347\256\200\345\215\225)/\345\214\205\345\220\253min\345\207\275\346\225\260\347\232\204\346\240\210.go" new file mode 100644 index 0000000..fb7dd34 --- /dev/null +++ "b/LeetCode/\345\211\221\346\214\207offer/day1_\346\240\210\344\270\216\351\230\237\345\210\227(\347\256\200\345\215\225)/\345\214\205\345\220\253min\345\207\275\346\225\260\347\232\204\346\240\210.go" @@ -0,0 +1,60 @@ +// 题目链接:https://leetcode.cn/problems/bao-han-minhan-shu-de-zhan-lcof/ +// 思路:建立辅助栈,与存储数据的栈大小相同,在向栈中存数据时,辅助栈同时存入一个数字 +// 该辅助栈入栈元素的序列是一个非严格递增序列 +// 如果该数据小于辅助栈栈顶元素,则辅助栈存入该数据,否则辅助栈还存入一个辅助栈的栈顶元素。 +//(如果是存入第一个元素,辅助栈直接入栈即可,无比较操作) + +package main + +type MinStack struct { + nums []int //储存栈 + min []int //辅助储存栈,存储最小值 +} + +/** initialize your data structure here. */ +// 为解决命名冲突,这里函数名后+“2”,在LeetCode需删除 +func Constructor2() MinStack { + return MinStack{ + []int{}, + []int{}, + } +} + +// 入栈时,存储栈直接入栈 +// 对辅助栈,若栈长度为0,直接入栈 +// 否则,与栈顶元素进行比较,若大于栈顶元素,入栈,否则,辅助栈再次存入栈顶元素 +func (this *MinStack) Push(x int) { + this.nums=append(this.nums,x) + if len(this.min)==0{ + this.min=append(this.min,x) + }else if this.min[len(this.min)-1] 0{ + x := this.stack1[len(this.stack1)-1] + this.stack2 = append(this.stack2,x) + this.stack1 = this.stack1[:len(this.stack1)-1] + } + } + // stack2出栈的元素即为队首元素 + if len(this.stack2) > 0{ + res := this.stack2[len(this.stack2)-1] + this.stack2 = this.stack2[:len(this.stack2)-1] + return res + } + // 若stack2长度仍为0,说明队列为空,返回-1 + return -1 +} + + +/** + * Your CQueue object will be instantiated and called as such: + * obj := Constructor(); + * obj.AppendTail(value); + * param_2 := obj.DeleteHead(); + */ \ No newline at end of file diff --git "a/LeetCode/\345\211\221\346\214\207offer/day1_\346\240\210\344\270\216\351\230\237\345\210\227(\347\256\200\345\215\225)/\347\224\250\344\270\244\344\270\252\346\240\210\345\256\236\347\216\260\351\230\237\345\210\227_\351\223\276\350\241\250\345\256\236\347\216\260.go" "b/LeetCode/\345\211\221\346\214\207offer/day1_\346\240\210\344\270\216\351\230\237\345\210\227(\347\256\200\345\215\225)/\347\224\250\344\270\244\344\270\252\346\240\210\345\256\236\347\216\260\351\230\237\345\210\227_\351\223\276\350\241\250\345\256\236\347\216\260.go" new file mode 100644 index 0000000..7ecbf5a --- /dev/null +++ "b/LeetCode/\345\211\221\346\214\207offer/day1_\346\240\210\344\270\216\351\230\237\345\210\227(\347\256\200\345\215\225)/\347\224\250\344\270\244\344\270\252\346\240\210\345\256\236\347\216\260\351\230\237\345\210\227_\351\223\276\350\241\250\345\256\236\347\216\260.go" @@ -0,0 +1,35 @@ +// 题目解析在 用两个栈实现队列.go +// 本文件为 用链表实现的栈 来 实现队列 +// 为解决命名冲突,本文件结构体与函数名后+1 +package main + +import "container/list" + +type CQueue1 struct { + stack1, stack2 *list.List +} + +func Constructor1() CQueue1 { + return CQueue1{ + stack1: list.New(), + stack2: list.New(), + } +} + +func (this *CQueue1) AppendTail1(value int) { + this.stack1.PushBack(value) +} + +func (this *CQueue1) DeleteHead1() int { + if this.stack2.Len() == 0 { + for this.stack1.Len() > 0 { + this.stack2.PushBack(this.stack1.Remove(this.stack1.Back())) + } + } + if this.stack2.Len() != 0 { + e := this.stack2.Back() + this.stack2.Remove(e) + return e.Value.(int) + } + return -1 +} \ No newline at end of file diff --git "a/LeetCode/\345\211\221\346\214\207offer/day20_\345\210\206\346\262\273\347\256\227\346\263\225(\344\270\255\347\255\211)/\344\272\214\345\217\211\346\220\234\347\264\242\346\240\221\347\232\204\345\220\216\345\272\217\351\201\215\345\216\206\345\272\217\345\210\227.go" "b/LeetCode/\345\211\221\346\214\207offer/day20_\345\210\206\346\262\273\347\256\227\346\263\225(\344\270\255\347\255\211)/\344\272\214\345\217\211\346\220\234\347\264\242\346\240\221\347\232\204\345\220\216\345\272\217\351\201\215\345\216\206\345\272\217\345\210\227.go" new file mode 100644 index 0000000..300206c --- /dev/null +++ "b/LeetCode/\345\211\221\346\214\207offer/day20_\345\210\206\346\262\273\347\256\227\346\263\225(\344\270\255\347\255\211)/\344\272\214\345\217\211\346\220\234\347\264\242\346\240\221\347\232\204\345\220\216\345\272\217\351\201\215\345\216\206\345\272\217\345\210\227.go" @@ -0,0 +1,57 @@ +//题目链接:https://leetcode.cn/problems/er-cha-sou-suo-shu-de-hou-xu-bian-li-xu-lie-lcof/ +// day20/31 +// 第 20 天主题为:分治算法(中等) +// 包含三道题目: +// 剑指offer07.重建二叉树 +// 剑指offer16.数值的整数次方 +// 剑指offer33.二叉搜索树的后序遍历序列 + +package main + +//一句话描述:后序遍历的根节点在最后一位,前面可以分为两部分,一部分为左子树的后序遍历,另一部分为右子树的后序遍历, +//右子树所有节点值均大于根节点,找到右子树节点的起始节点,判断该部分是否所有节点值都大于根节点的值, +//若否,说明该序列无法构成BST的后序遍历序列,之后再递归判断左子树部分和右子树部分的序列是否为BST的后序遍历序列。 +// +//下面是详细描述: +//二叉树后序遍历的结果是:[ [左子树的后序遍历结果], [右子树的后序遍历结果],根节点] +//我们可以把后序遍历数组分为三部分:左子树的后序遍历、右子树的后序遍历和该二叉树的根节点。二叉树的根节点一定是后序遍历数组的最后一个元素。 +//BST左子树所有元素节点值均小于根节点,右子树所有元素值均大于根节点。所以,我们可以从后序遍历数组的第一个元素遍历到倒数第二个元素, +//进行判断,是否大于等于根节点,若大于等于,说明该元素之前所有元素为左子树的后序遍历结果,从该元素至倒数第二个元素为右子树的后序遍历结果。 +//从该元素起,遍历到倒数第二个元素,若存在元素值小于根节点的值,说明该数组不是BST的后序遍历序列。 然后递归地判断左子树序列与右子树序列是否为某BST的后序遍历序列。 +// +//有一点需要注意的是,right的初始化应该是序列长度-1,我刚开始初始化为0,结果就出错了。 +//如果将 k 初始化为 0,且右子树区间大小为 0,最后 k 值为 0,递归验证右子树的区间会是 postorder[0:n-1], +//而实际上该区间大小为 0,所以我们初始化 k 时,需要其值 大于等于 n-1。 +//这是容易出错的一个点,需要牢记 + +func verifyPostorder(postorder []int) bool { + // 若数组大小小于等于2,则必定是某BST的后序遍历序列 + if len(postorder) <= 2{ + return true + } + n := len(postorder) + // 后序遍历的根节点在数组最后一位 + root := postorder[n-1] + // k 为右子树的后序遍历序列起始下标 + // 初始化为 n-1 + k := n-1 + // 寻找 k 值 + for i:=0;i < n-1;i++{ + if postorder[i] > root{ + k = i + break + } + } + // 验证根节点的右子树所有节点值均大于根节点值 + // 若存在小于根节点的节点,返回 false + for i:=k+1;i>= 1 + } + return res +} \ No newline at end of file diff --git "a/LeetCode/\345\211\221\346\214\207offer/day20_\345\210\206\346\262\273\347\256\227\346\263\225(\344\270\255\347\255\211)/\351\207\215\345\273\272\344\272\214\345\217\211\346\240\221.go" "b/LeetCode/\345\211\221\346\214\207offer/day20_\345\210\206\346\262\273\347\256\227\346\263\225(\344\270\255\347\255\211)/\351\207\215\345\273\272\344\272\214\345\217\211\346\240\221.go" new file mode 100644 index 0000000..d067d52 --- /dev/null +++ "b/LeetCode/\345\211\221\346\214\207offer/day20_\345\210\206\346\262\273\347\256\227\346\263\225(\344\270\255\347\255\211)/\351\207\215\345\273\272\344\272\214\345\217\211\346\240\221.go" @@ -0,0 +1,45 @@ +//题目链接:https://leetcode.cn/problems/zhong-jian-er-cha-shu-lcof/?envType=study-plan&id=lcof +package main + +//解题思路:参考自题解(https://leetcode.cn/problems/zhong-jian-er-cha-shu-lcof/solution/mian-shi-ti-07-zhong-jian-er-cha-shu-by-leetcode-s/ +// +//对于任意一颗树而言,前序遍历的形式总是:[根节点,[左子树的前序遍历结果], [右子树的前序遍历结果]] +//即根节点总是前序遍历中的第一个节点。 +//而中序遍历的形式总是:[[左子树的中序遍历结果],根节点,[右子树的中序遍历结果]] +// +//根据二叉树的前序遍历,我们可以构造出根节点,之后要考虑的是如何构造根节点的左孩子节点和右孩子节点 +//只要我们在中序遍历中定位到根节点,我们就可以知道该二叉树左子树和右子树的节点数量。 +//知道数量后,我们就可以在前序遍历和中序遍历中通过切片得到左子树和右子树的前序遍历和中序遍历。 +// +//之后递归构造根节点的左孩子节点和右孩子节点即可 +// +//在中序遍历中对根节点进行定位时,我们这里使用遍历的方式 + +//Definition for a binary tree node. +type TreeNode struct { + Val int + Left *TreeNode + Right *TreeNode +} + +func buildTree(preorder []int, inorder []int) *TreeNode { + // 递归退出条件,遍历序列长度为0,节点为空 + if len(preorder) == 0{ + return nil + } + rootVal := preorder[0] + // 构造根节点 + root := &TreeNode{rootVal,nil,nil} + // k初始化为0,寻找中序遍历中根节点的下标 + k := 0 + for ;k>= 1 + } + return res +} + +//方法2:位运算优化,消除二进制末尾的 1 +//非常巧妙的做法,当 n 非零时,n=n&(n-1)可消除n的二进制中最后一个出现的 1. +//因此,执行 n=n&(n-1)使得n变成 0 的操作次数,就是 n 的二进制中 1 的个数。 +func hammingWeight_2(num uint32) int { + res := 0 + for num != 0{ + res ++ + num &= num-1 + } + return res +} + +//第一种解法的时间复杂度为 O(logn),第二种解法为 O(m),m 为二进制串中 1 的个数; +//两种解法的空间复杂度均为 O(1)。 +// +//个人感觉啊,位运算优化只是一种技巧,在效率方面对比逐位检查提升并不大。 +//因为逐位检查已经是对数级别了,对数级别的时间复杂度已经是相当高效。 \ No newline at end of file diff --git "a/LeetCode/\345\211\221\346\214\207offer/day22_\344\275\215\350\277\220\347\256\227(\344\270\255\347\255\211)/\346\225\260\347\273\204\344\270\255\346\225\260\345\255\227\345\207\272\347\216\260\347\232\204\346\254\241\346\225\260.go" "b/LeetCode/\345\211\221\346\214\207offer/day22_\344\275\215\350\277\220\347\256\227(\344\270\255\347\255\211)/\346\225\260\347\273\204\344\270\255\346\225\260\345\255\227\345\207\272\347\216\260\347\232\204\346\254\241\346\225\260.go" new file mode 100644 index 0000000..734a65f --- /dev/null +++ "b/LeetCode/\345\211\221\346\214\207offer/day22_\344\275\215\350\277\220\347\256\227(\344\270\255\347\255\211)/\346\225\260\347\273\204\344\270\255\346\225\260\345\255\227\345\207\272\347\216\260\347\232\204\346\254\241\346\225\260.go" @@ -0,0 +1,47 @@ +//题目链接:https://leetcode.cn/problems/shu-zu-zhong-shu-zi-chu-xian-de-ci-shu-lcof/ +// day22/31 +// 第 22 天主题为:位运算(中等) +// 包含两道题目: +// 剑指offer56.数组中数字出现的次数 +// 剑指offer56-II.数组中数字出现的次数 II +package main + +//本题最通俗的解法是哈希表记录每个数字出现的次数,不符合题目要求空间复杂度,位运算解法可将空间复杂度从 O(n) 降低至 O(1) +//解题思路:分组异或 +//先考虑一种简单的情况,一个整形数组 nums 中除一个数字出现一次之外,其余数字都出现了两次,找出这个数字。 +//很容易想到用 异或 来解决。两个相同的数字进行异或操作结果为 0。对 nums 进行一次遍历,xor 变量初始化为 0 +//与 nums 中每个数字进行异或操作,相同的数字会两两抵消,最后 xor 就与仅出现一次的数字值相同。 +// +//回到本题,有两个出现一次的数字,设为 a 和 b,如果我们使用相同的方法对 nums 进行异或,最终得到的结果 xor 会是 a 与 b 异或的结果。 +//xor 必定不等于 0,因为若 xor 等于 0,说明 a==b,与条件相悖。 + +//我们可以根据 xor 的二进制中任意不等于 0 的位进行分组,将 a 和 b 分到不同的组中,然后对两组数字分别进行异或,取出 a 和 b。 +//假设 xor 的第二位为 1,那我们就取 x = 10,对 nums 进行遍历,记当前数字为 num,根据 num&x==0和 num&x!=0 分组, +//a 和 b 在各自组中异或得到。 + +// 简单一句话描述: +//先对所有数字进行异或操作,得到两个只出现一次的数字进行异或的结果,在该结果中找到任意为 1 的位, +//根据该位对所有数字进行分组,在每个组内进行异或操作,即可得到两个数字。 +func singleNumbers(nums []int) []int { + xor := 0 + for _,num := range nums{ + xor ^= num + } + x := 1 + for { + if xor & x == 0{ + x <<= 1 + } else { + break + } + } + a,b := 0,0 + for _,num := range nums{ + if num & x == 0{ + a ^= num + } else { + b ^= num + } + } + return []int{a,b} +} \ No newline at end of file diff --git "a/LeetCode/\345\211\221\346\214\207offer/day22_\344\275\215\350\277\220\347\256\227(\344\270\255\347\255\211)/\346\225\260\347\273\204\344\270\255\346\225\260\345\255\227\345\207\272\347\216\260\347\232\204\346\254\241\346\225\260II.go" "b/LeetCode/\345\211\221\346\214\207offer/day22_\344\275\215\350\277\220\347\256\227(\344\270\255\347\255\211)/\346\225\260\347\273\204\344\270\255\346\225\260\345\255\227\345\207\272\347\216\260\347\232\204\346\254\241\346\225\260II.go" new file mode 100644 index 0000000..ecccc49 --- /dev/null +++ "b/LeetCode/\345\211\221\346\214\207offer/day22_\344\275\215\350\277\220\347\256\227(\344\270\255\347\255\211)/\346\225\260\347\273\204\344\270\255\346\225\260\345\255\227\345\207\272\347\216\260\347\232\204\346\254\241\346\225\260II.go" @@ -0,0 +1,26 @@ +//题目链接:https://leetcode.cn/problems/shu-zu-zhong-shu-zi-chu-xian-de-ci-shu-ii-lcof/?envType=study-plan&id=lcof +package main + +//本题最通俗的解法是哈希表记录每个数字出现的次数,我们还是用位运算进行优化,将空间复杂度从 O(n) 降低至 O(1) +// +//解题思路:依次确定二进制位 +//记数组中出现一次的数字为 res,题目给定数组中整数范围为 1~2^31-1,我们可以从最低位开始,依次确定 res 当前位 是 0 还是 1 。 +//在每一位中,我们遍历数组所有元素,统计当前位为 1 的个数 num,若 num 对 3 取余 为 0,说明 res 当前位为 0,否则为 1 。 + +func singleNumber(nums []int) int { + n := len(nums) + res := 0 + for i:=0;i<32;i++{ + num := 0 + bit := 1<=0;i--{ + suffix[i] = suffix[i+1] * a[i+1] + } + for i:=0;i=0;i--{ + temp *= a[i+1] + res[i] *= temp + } + return res +} diff --git "a/LeetCode/\345\211\221\346\214\207offer/day24_\346\225\260\345\255\246(\344\270\255\347\255\211)/\345\211\252\347\273\263\345\255\220.go" "b/LeetCode/\345\211\221\346\214\207offer/day24_\346\225\260\345\255\246(\344\270\255\347\255\211)/\345\211\252\347\273\263\345\255\220.go" new file mode 100644 index 0000000..d847e1d --- /dev/null +++ "b/LeetCode/\345\211\221\346\214\207offer/day24_\346\225\260\345\255\246(\344\270\255\347\255\211)/\345\211\252\347\273\263\345\255\220.go" @@ -0,0 +1,58 @@ +//题目链接:https://leetcode.cn/problems/jian-sheng-zi-lcof/?envType=study-plan&id=lcof +// day24/31 +// 第 24 天主题为:数学(中等) +// 包含三道题目: +// 剑指offer14-I.剪绳子 +// 剑指offer57-II.和为s的连续正数序列 +// 剑指offer62.圆圈中最后剩下的数字 + +package main + +import "math" + +//先上传统的动态规划解法 +// +//1. 确定dp数组大小及下标含义:dp[i] 代表长度为 i 的绳子切割后的最大乘积,长度为 n+1 +//2. dp 数组初始化:dp[1]=1,dp[2]=2,dp[3]=3,需要注意的是,该初始值只针对 n>=4 的情况,n<4的情况,我们会单独处理 +//3. 状态转移方程:dp[i] = max(dp[i] * dp[i-j]),j 从 1 遍历到 i-1,这里我们可以使用一个小技巧,j 从 1 遍历至 n/2 即可,因为 1*6 = 6 * 1 +// +//再说下 n<4 的情况,题目给定 n>=2,当 n<4 时,返回 n-1 即可。 + +func cuttingRope(n int) int { + if n < 4{ + return n-1 + } + dp := make([]int,n+1) + dp[1],dp[2],dp[3] = 1,2,3 + for i:=4;i<=n;i++{ + for j:=1;j<=i/2;j++{ + dp[i] = max(dp[i],dp[j]*dp[i-j]) + } + } + return dp[n] +} + +func max(x,y int) int{ + if x > y{ + return x + } + return y +} + +//这道题还有一种数学的解法,时间和空间复杂度都超过dp +// +//公式我在纸上推导,拍照放在了同一文件夹中,剪绳子.jpg + +func cuttingRope_2(n int) int { + if n <= 3{ + return n-1 + } + a,b := n/3,n%3 + if b == 0{ + return int(math.Pow(3,float64(a))) + } else if b == 1{ + return int(math.Pow(3,float64(a-1)) * 4) + } else { + return int(math.Pow(3,float64(a)) * 2) + } +} \ No newline at end of file diff --git "a/LeetCode/\345\211\221\346\214\207offer/day24_\346\225\260\345\255\246(\344\270\255\347\255\211)/\345\211\252\347\273\263\345\255\220.jpg" "b/LeetCode/\345\211\221\346\214\207offer/day24_\346\225\260\345\255\246(\344\270\255\347\255\211)/\345\211\252\347\273\263\345\255\220.jpg" new file mode 100644 index 0000000..cc42284 Binary files /dev/null and "b/LeetCode/\345\211\221\346\214\207offer/day24_\346\225\260\345\255\246(\344\270\255\347\255\211)/\345\211\252\347\273\263\345\255\220.jpg" differ diff --git "a/LeetCode/\345\211\221\346\214\207offer/day24_\346\225\260\345\255\246(\344\270\255\347\255\211)/\345\222\214\344\270\272s\347\232\204\350\277\236\347\273\255\346\255\243\346\225\260\345\272\217\345\210\227.go" "b/LeetCode/\345\211\221\346\214\207offer/day24_\346\225\260\345\255\246(\344\270\255\347\255\211)/\345\222\214\344\270\272s\347\232\204\350\277\236\347\273\255\346\255\243\346\225\260\345\272\217\345\210\227.go" new file mode 100644 index 0000000..4b9b221 --- /dev/null +++ "b/LeetCode/\345\211\221\346\214\207offer/day24_\346\225\260\345\255\246(\344\270\255\347\255\211)/\345\222\214\344\270\272s\347\232\204\350\277\236\347\273\255\346\255\243\346\225\260\345\272\217\345\210\227.go" @@ -0,0 +1,42 @@ +//题目链接:https://leetcode.cn/problems/he-wei-sde-lian-xu-zheng-shu-xu-lie-lcof/?envType=study-plan&id=lcof + +package main + +//解题思路:滑动窗口 +// +//设连续正整数序列的左边界与右边界分别为 left 和 right,构建滑动窗口向右滑动。 +//循环中,每轮判断滑动窗口内元素和与 target 的大小关系 +//- 若相等则记录结果,然后移动左指针 +//- 若 大于 target 则移动左指针 +//- 若小于 target 则移动右指针 +// +//每次移动指针的同时,更新滑动窗口内元素和 sum +//因为序列至少由两个数字组成,所以左指针边界为 [1,target/2] +func findContinuousSequence(target int) [][]int { + // left,right 为滑动窗口左右指针 + // sum 动态记录窗口元素和 + // 窗口至少含有两个数 + left, right, sum := 1, 2,3 + res := make([][]int, 0) + // 序列至少右两个元素组成,所以左边界只需遍历到 target/2 + for left <= target>>1 { + if sum < target { + // move right cursor and increase sum + right++ + sum += right + } else if sum > target { + // move left cursor and reduce sum + sum -= left + left++ + } else { + nums := make([]int, 0) + for i := left; i <= right; i++ { + nums = append(nums, i) + } + res = append(res,nums) + sum -= left + left++ + } + } + return res +} \ No newline at end of file diff --git "a/LeetCode/\345\211\221\346\214\207offer/day24_\346\225\260\345\255\246(\344\270\255\347\255\211)/\345\234\206\345\234\210\344\270\255\346\234\200\345\220\216\345\211\251\344\270\213\347\232\204\346\225\260\345\255\227.go" "b/LeetCode/\345\211\221\346\214\207offer/day24_\346\225\260\345\255\246(\344\270\255\347\255\211)/\345\234\206\345\234\210\344\270\255\346\234\200\345\220\216\345\211\251\344\270\213\347\232\204\346\225\260\345\255\227.go" new file mode 100644 index 0000000..826a2d1 --- /dev/null +++ "b/LeetCode/\345\211\221\346\214\207offer/day24_\346\225\260\345\255\246(\344\270\255\347\255\211)/\345\234\206\345\234\210\344\270\255\346\234\200\345\220\216\345\211\251\344\270\213\347\232\204\346\225\260\345\255\227.go" @@ -0,0 +1,54 @@ +//题目链接:https://leetcode.cn/problems/yuan-quan-zhong-zui-hou-sheng-xia-de-shu-zi-lcof/?envType=study-plan&id=lcof + +package main + +//约瑟夫环问题,模拟整个删除过程最直观,这里我用数组进行模拟,首先构建长度为 n 的模拟数组 nums, +//元素值分别为 0 ~ n-1,start 为每次循环中数组第一个元素下标,初始化为 0,之后开始模拟 +// +//先获取当前数组的长度 clen,删除第 m 个数字,m 可能大于等于 n,我们将 m-1 对 n 取余, 直接得到要删除的元素下标, +//考虑到初始元素下标不一定为 0,最终待删除的元素下表 loc_del = (m-1+start) % clen, +//将删除 该元素后的数组重新赋值给 nums,start 更新为 loc_del,开启下一次循环,直至数组长度为 1, +//可得到圆圈剩下的最后一个数组,返回该元素即可。 + +func lastRemaining(n int, m int) int { + // 每次循环中数组的第一个元素下标 + start := 0 + // 构建 nums 数组 + nums := []int{} + for i:= 0;i 1{ + clen := len(nums) + loc_del := (m-1+start) % clen + nums = append(nums[:loc_del],nums[loc_del+1:]...) + start = loc_del + } + return nums[0] +} + +//提交后会超时,个人感觉时间是浪费在数组的拼接上。 +// +//下述解法参考自官方题解 +//现在我们建模 n 个数字删除 第 m 个元素的问题为 f(n,m),f(n,m) 的值为最后剩余元素值(同元素下标) +//那 f(1,m) 的答案是一定的,数组只存在一个元素,f(1,m) = 0,我们可以试想下,可不可以通过 f(1,n) 推出 f(n,m) 的值。 +//进一步来说,如果能通过 f(n-1,m) 推出 f(n,m),那就一定能从 f(1,n) 推出 f(n,m) +//因为 f(n,m) 要删除第 m%n 个元素后,长度就变成了 n-1,那自然而然就与 f(n-1,m) 扯上了关联,它们的区别在哪里呢? +//对应元素下标不同! 只要我们把它们的下标相对应起来,那我们就可以求解改题目。 +//下标不同在哪里呢?起始位置,f(n-1,m) 起始元素下标为 0,而 f(n,m) 删除一个元素后的起始元素下标为 m%n, +//我们把它们对应起来,就可以通过 f(n-1,m) 求得 f(n,m) +//同理,由于 f(1,m) 值是固定的,我们可以从 f(1,m) 递归到 f(n,m) + +func lastRemaining_2(n int, m int) int { + // n=1时,剩余元素下标为0 + res := 0 + // 从前向后推,求n=2,3,...,n时,剩余元素下标 + // i 为数组长度 + for i:= 2;i <= n;i++{ + // 将i-1数组元素下标与长度为i的数组下标对应起来 + // 求得长度为 i 的数组剩余元素 + res = (res + m) % i + } + return res +} \ No newline at end of file diff --git "a/LeetCode/\345\211\221\346\214\207offer/day25_\346\250\241\346\213\237(\344\270\255\347\255\211)/\346\240\210\347\232\204\345\216\213\345\205\245\343\200\201\345\274\271\345\207\272\345\272\217\345\210\227.go" "b/LeetCode/\345\211\221\346\214\207offer/day25_\346\250\241\346\213\237(\344\270\255\347\255\211)/\346\240\210\347\232\204\345\216\213\345\205\245\343\200\201\345\274\271\345\207\272\345\272\217\345\210\227.go" new file mode 100644 index 0000000..c38056f --- /dev/null +++ "b/LeetCode/\345\211\221\346\214\207offer/day25_\346\250\241\346\213\237(\344\270\255\347\255\211)/\346\240\210\347\232\204\345\216\213\345\205\245\343\200\201\345\274\271\345\207\272\345\272\217\345\210\227.go" @@ -0,0 +1,25 @@ +//题目链接:https://leetcode.cn/problems/zhan-de-ya-ru-dan-chu-xu-lie-lcof/?envType=study-plan&id=lcof +// day25/31 +// 第 25 天主题为:模拟(中等) +// 包含两道题目: +// 剑指offer29.顺时针打印矩阵 +// 剑指offer31.栈的压入、弹出序列 +package main + +//简单题,模拟该过程,声明一个栈 stack 和 变量 i(i 符合条件的出栈元素数量),遍历压入序列 pushed,依次入栈,每次入栈结束后 +//判断栈顶元素是否和出栈序列 popped[i] 相等 +//若相等,出栈,i++,再次判断栈顶元素和 popped[i]是否相等...直至栈长度为0或者栈顶元素和popped[i]不相等。 +// +//遍历完成后,若 i = len(popped),说明所有元素出栈成功,返回 true。判断条件改为 len(stack)=0 也可以,道理相同 +func validateStackSequences(pushed []int, popped []int) bool { + stack := []int{} + i := 0 + for _,num := range(pushed){ + stack = append(stack,num) + for len(stack) > 0 && stack[len(stack)-1] == popped[i]{ + stack = stack[:len(stack)-1] + i++ + } + } + return len(stack) == 0 +} \ No newline at end of file diff --git "a/LeetCode/\345\211\221\346\214\207offer/day25_\346\250\241\346\213\237(\344\270\255\347\255\211)/\351\241\272\346\227\266\351\222\210\346\211\223\345\215\260\347\237\251\351\230\265.go" "b/LeetCode/\345\211\221\346\214\207offer/day25_\346\250\241\346\213\237(\344\270\255\347\255\211)/\351\241\272\346\227\266\351\222\210\346\211\223\345\215\260\347\237\251\351\230\265.go" new file mode 100644 index 0000000..6a639d8 --- /dev/null +++ "b/LeetCode/\345\211\221\346\214\207offer/day25_\346\250\241\346\213\237(\344\270\255\347\255\211)/\351\241\272\346\227\266\351\222\210\346\211\223\345\215\260\347\237\251\351\230\265.go" @@ -0,0 +1,84 @@ +//题目链接:https://leetcode.cn/problems/shun-shi-zhen-da-yin-ju-zhen-lcof/?envType=study-plan&id=lcof +package main + +//简单模拟,这种题目,个人感觉比较看经验,做过类似题目的话,很快可以模拟出来,没做过或者没思路的话,就有点无从下手或者不知道如何用代码实现自己的想法。 +//需要考虑三个问题:移动方向、边界 和 结束条件。 +// +//1. 移动方向:很明显为 右、下、左、上 这样的循环,我们可以用一个二维数组代表四个方向,每移动到边界,更改方向; +//2. 边界:边界问题是本题的重点,因为边界是随着遍历在变化的,打印矩阵的过程中,边界逐渐变小。规则是:如果当前行(列)遍历结束后,将次行(列)的边界向内移动一格; +//3. 结束条件:当矩阵所有位置都被打印过,即遍历数组的长度等于矩阵元素个数时,结束遍历。 +// +//代码中,x和y代表当前遍历到的元素所在位置索引,dirs代表移动的四个方向(上、右、下、左是一个循环), +//dir[cur_d]代表下一次移动的方向,结束条件是 res 数组元素个数等于矩阵元素个数。 +func spiralOrder(matrix [][]int) []int { + if len(matrix) == 0 || len(matrix[0]) == 0{ + return []int{} + } + m,n := len(matrix),len(matrix[0]) + directions := [][]int{{0,1},{1,0},{0,-1},{-1,0}} + cur_dir := 0 + top,right,bottom,left := 0,n-1,m-1,0 + res := make([]int,0) + x,y := 0,0 + for len(res) < m*n{ + res = append(res,matrix[x][y]) + if cur_dir == 0 && y == right{ + cur_dir ++ + top ++ + } else if cur_dir == 1 && x == bottom{ + cur_dir ++ + right -- + } else if cur_dir == 2 && y == left{ + cur_dir ++ + bottom -- + } else if cur_dir == 3 && x == top{ + cur_dir ++ + left ++ + } + cur_dir %= 4 + x += directions[cur_dir][0] + y += directions[cur_dir][1] + } + return res +} + +//也可以按层模拟,将矩阵分为若干层,先打印最外层元素,再打印次外层的元素,直到输出完最内层的元素。 +// +//对于每层,从左上方开始以顺时针的顺序遍历当前层所有元素。还是要着重考虑边界的问题,假设当前层左上角元素索引为(top,left),右下角元素索引为(bottom,right). +// +//- 从左到右遍历上侧元素,依次为 (top,left) 到 (top,right)。 +//- 从上到下遍历右侧元素,依次为 (top+1,right) 到 (bottom,right)。 +//- 如果 left=left;i--{ + res = append(res,matrix[bottom][i]) + } + for i := bottom-1;i>top;i--{ + res = append(res,matrix[i][left]) + } + } + top ++ + right -- + bottom -- + left ++ + } + return res +} \ No newline at end of file diff --git "a/LeetCode/\345\211\221\346\214\207offer/day26_\345\255\227\347\254\246\344\270\262(\344\270\255\347\255\211)/\346\212\212\345\255\227\347\254\246\344\270\262\350\275\254\346\215\242\346\210\220\346\225\264\346\225\260.go" "b/LeetCode/\345\211\221\346\214\207offer/day26_\345\255\227\347\254\246\344\270\262(\344\270\255\347\255\211)/\346\212\212\345\255\227\347\254\246\344\270\262\350\275\254\346\215\242\346\210\220\346\225\264\346\225\260.go" new file mode 100644 index 0000000..9d37282 --- /dev/null +++ "b/LeetCode/\345\211\221\346\214\207offer/day26_\345\255\227\347\254\246\344\270\262(\344\270\255\347\255\211)/\346\212\212\345\255\227\347\254\246\344\270\262\350\275\254\346\215\242\346\210\220\346\225\264\346\225\260.go" @@ -0,0 +1,71 @@ +//题目链接:https://leetcode.cn/problems/ba-zi-fu-chuan-zhuan-huan-cheng-zheng-shu-lcof/?envType=study-plan&id=lcof +// day26/31 +// 第 26 天主题为:字符串(中等) +// 包含两道题目: +// 剑指offer20.表示数值的字符串 +// 剑指offer67.把字符串转换成整数 + +package main + +// 模拟即可 +// 官方题解的解法是:确定有限状态自动机,这块我不太懂,有兴趣的同学自行了解哈 +func isNumber(s string) bool { + n := len(s) + // 若字符串长度为0,无法表示数值 + if n == 0{ + return false + } + // 从下标0开始遍历 + index := 0 + // 读取字符开头处的若干空格 + for index < n && s[index] == ' '{ + index ++ + } + // 读取整数部分,,numeric代表读取到index是否为数值 + // index 持续向前推进 + numeric := ScanInteger(s,&index) + // 若下一个字符为'.',说明为小数,读取小数部分 + if index < n && s[index] == '.'{ + index ++ + // 这里用逻辑或的原因: + // 1. '.'前若无数字,则'.'后至少需要一位数字 + // 对应左表达式为 true,右表达式为 false + // 2.'.'前有数字,则后方可以有也可以没有数字 + // 对应左true右true 和 左false右true + numeric = ScanUnsignedInteger(s,&index) || numeric + } + // 若出现e,后面需要跟一个整数,原理同上 + if index < n && (s[index] == 'e' || s[index] == 'E'){ + index ++ + numeric = numeric && ScanInteger(s,&index) + } + // 读取字符串结尾处的若干空格 + for index < n && s[index] == ' '{ + index ++ + } + // 若 numeric 为 true,且读取到字符串末尾,说明为数值 + return numeric && index == len(s) +} + +// 读取字符串s从下标index开始的有符号整数 +func ScanInteger(s string,index *int) bool{ + // 先读取正负号,若无符号,默认正数 + if *index < len(s) && (s[*index] == '+' || s[*index] == '-'){ + *index ++ + } + // 读取符号后,读取无符号整数 + return ScanUnsignedInteger(s,index) +} + +// 读取字符串s从下标index开始的无符号整数 +// 若未读取到无符号整数,返回false,否则,返回true +func ScanUnsignedInteger(s string,index *int) bool { + start_of_integer := *index + // 根据ASCII码判断是否为数字字符 + // 若读取到非数字字符,直接跳出即可 + for *index < len(s) && s[*index] >= '0' && s[*index] <= '9'{ + *index ++ + } + // 若*index大于读取前的值,说明有整数被读取到 + return *index > start_of_integer +} \ No newline at end of file diff --git "a/LeetCode/\345\211\221\346\214\207offer/day26_\345\255\227\347\254\246\344\270\262(\344\270\255\347\255\211)/\350\241\250\347\244\272\346\225\260\345\200\274\347\232\204\345\255\227\347\254\246\344\270\262.go" "b/LeetCode/\345\211\221\346\214\207offer/day26_\345\255\227\347\254\246\344\270\262(\344\270\255\347\255\211)/\350\241\250\347\244\272\346\225\260\345\200\274\347\232\204\345\255\227\347\254\246\344\270\262.go" new file mode 100644 index 0000000..c0eef3c --- /dev/null +++ "b/LeetCode/\345\211\221\346\214\207offer/day26_\345\255\227\347\254\246\344\270\262(\344\270\255\347\255\211)/\350\241\250\347\244\272\346\225\260\345\200\274\347\232\204\345\255\227\347\254\246\344\270\262.go" @@ -0,0 +1,85 @@ +//题目链接:https://leetcode.cn/problems/biao-shi-shu-zhi-de-zi-fu-chuan-lcof/?envType=study-plan&id=lcof +package main + +//这里我想先解释一下为什么负数范围比正数范围大 1 . +//因为对有符号数,在计算机中表达时,最高位约定为符号位,符号位为 0 时代表正数,符号位为 1 时代表负数。 +//对 int32,真正表达数值的部分就是剩下的 31 位,正数范围很容易理解为 1~2^31-1(2147483647) +//有一个特殊的值,就是 0 值,对符号位 1 和 0 的时候,都是 0,用两个形式表达同一个数无疑是浪费的, +//所以,我们就规定,符号位为 1 时的全 0,表达 -2147483648,这也是负数表示的范围比正数多1的原因。 +//综上,对于任意位的,无论是8位,16位,32位甚至64位的整数类型表示范围的计算公式为: +//如总位数为n位,那么有符号数的范围为:-2^(n-1) ~ 2^(n-1)-1,无符号数的表示范围为:0~2^n-1 + + +//此题解参考自链接:https://leetcode.cn/problems/ba-zi-fu-chuan-zhuan-huan-cheng-zheng-shu-lcof/submissions/ + +//首部空格: 删除之即可; +//符号位: 三种情况,即 ''+'' , ''-'' , ''无符号" ;新建一个变量保存符号位,返回前判断正负即可。 +//非数字字符: 遇到首个非数字的字符时,应立即返回。 +//数字字符: +//字符转数字: “此数字的 ASCII 码” 与 “ 00 的 ASCII 码” 相减即可; +//数字拼接: 若从左向右遍历数字,设当前位字符为 c ,当前位数字为 x ,数字结果为 res,则数字拼接公式为:res = 10 * res + x +//x = ascii(c) - ascii('0') +// +//接下来是比较关键的数字越界处理: +//题目要求返回的数值范围应在 [-2^{31}, 2^{31} - 1],因此需要考虑数字越界问题。 +//而由于题目指出 环境只能存储 32 位大小的有符号整数 ,因此判断数字越界时,要始终保持 res 在 int 类型的取值范围内。 +// +//在每轮数字拼接前,判断 res 在此轮拼接后是否超过 2147483647,若超过则加上符号位直接返回。 +//设数字拼接边界 boundary = 2147483647 // 10 = 214748364,则以下两种情况越界: +// +//1. res > boundry, 拼接后越界 +//2. res = boundry && x > 7,拼接后越界 +// +//这种处理方式非常巧妙,> ‘7’ 这个操作看似只考虑到了最大值,其实也考虑了最小值,只要大于,直接返回即可。 +const ( + MAX_INT = 1 << 31 - 1 + MIN_INT = -1 * (1 << 31) +) + +func strToInt(str string) int { + // index 为 str 遍历到的位置 + index := 0 + // 为防止读取空格出现越界panic,需要提前处理str为空字符或全空格的情况 + if str == ""{ + return 0 + } + for i:=0;i '9'{ + break + } + // 预计出现越界情况,根据符号位返回最大或最小值 + if res > boundary || res == boundary && s > '7'{ + if sign == 1{ + return MAX_INT + } else { + return MIN_INT + } + } + res = res * 10 + int(s - '0') + + } + return sign * res +} \ No newline at end of file diff --git "a/LeetCode/\345\211\221\346\214\207offer/day27_\346\240\210\344\270\216\351\230\237\345\210\227(\345\233\260\351\232\276)/\346\273\221\345\212\250\347\252\227\345\217\243\347\232\204\346\234\200\345\244\247\345\200\274.go" "b/LeetCode/\345\211\221\346\214\207offer/day27_\346\240\210\344\270\216\351\230\237\345\210\227(\345\233\260\351\232\276)/\346\273\221\345\212\250\347\252\227\345\217\243\347\232\204\346\234\200\345\244\247\345\200\274.go" new file mode 100644 index 0000000..594636a --- /dev/null +++ "b/LeetCode/\345\211\221\346\214\207offer/day27_\346\240\210\344\270\216\351\230\237\345\210\227(\345\233\260\351\232\276)/\346\273\221\345\212\250\347\252\227\345\217\243\347\232\204\346\234\200\345\244\247\345\200\274.go" @@ -0,0 +1,72 @@ +// 题目链接:https://leetcode.cn/problems/hua-dong-chuang-kou-de-zui-da-zhi-lcof/?envType=study-plan&id=lcof +// day27/31 +// 第 27 天主题为:栈与队列(困难) +// 包含两道题目: +// 剑指offer59-I.滑动窗口的最大值 +// 剑指offer59-II.队列的最大值 +package main + +//可以先去暴力解决,对每个滑动窗口,遍历所有元素得到最大值。 +//对长度为 n 的数组 nums,窗口数量为 n-k+1,暴力解法时间复杂度为 O((n-k+1)*k) = O(nk) +func maxSlidingWindow(nums []int, k int) []int { + n := len(nums) + if n == 0 || k == 0{ + return []int{} + } + res := []int{} + for i := 0;i < n-k+1;i++{ + res = append(res,max(nums[i:i+k])) + } + return res +} + + +func max(sli []int) (res int){ + res = sli[0] + for _,i := range sli{ + if i > res{ + res = i + } + } + return +} + +//法二:单调队列 +//然后想一想,暴力没有用到题目中哪些条件,可以针对这些条件去做改进 +//相邻的两个窗口,有重叠元素 k-1 个,这 k-1 个元素中的最大值对两个窗口来说是相同的,这就是我们改进的方向 +// +//设想一下,一个窗口中两个元素下标分别为 i 和 j,其中 i 在 j 的左侧(i= nums[j],nums[i] 出队后,nums[j] 是有可能成为队列最大值的,nums[j] 需要保留 +// +//因此,我们可以使用一个队列来存储还没有被删除的元素。在队列中,这些元素的值是单调递减的 +//当窗口向右滑动时,我们需要把一个新的元素放入队列中,为了保持队列的性质,我们需要不断将新元素与队列队尾元素进行比较, +//若新元素较大,则队尾元素出队,不断进行此操作,直至队列为空 或 新的元素小于等于队尾的元素 +// +//由于队列的元素是严格单调递减的,且队列中元素属于该窗口,所以队首元素就是该窗口的最大值 +//此时,我们需要考虑最后一个问题,若当前窗口的最大值为窗口最左侧元素,那进入下一个窗口前队列中该元素应该出队,因为该元素并不属于下一个窗口 +// +//该队列元素单调递减,满足这种单调性的队列一般称作 单调队列。 +func maxSlidingWindow_2(nums []int, k int) []int { + n := len(nums) + monoQ := []int{} + res := make([]int,0,n-k+1) + for i:=0;i 0 && nums[i] > monoQ[len(monoQ)-1]{ + monoQ = monoQ[:len(monoQ)-1] + } + monoQ = append(monoQ,nums[i]) + // 若单调队列最大值非窗口元素 + // 将该元素出队 + if i >= k && monoQ[0] == nums[i-k]{ + monoQ = monoQ[1:] + } + // i=k-1时,窗口元素数量达到 k + // 开始向 res 数组添加窗口最大值 + if i >= k-1{ + res = append(res,monoQ[0]) + } + } + return res +} \ No newline at end of file diff --git "a/LeetCode/\345\211\221\346\214\207offer/day27_\346\240\210\344\270\216\351\230\237\345\210\227(\345\233\260\351\232\276)/\351\230\237\345\210\227\347\232\204\346\234\200\345\244\247\345\200\274.go" "b/LeetCode/\345\211\221\346\214\207offer/day27_\346\240\210\344\270\216\351\230\237\345\210\227(\345\233\260\351\232\276)/\351\230\237\345\210\227\347\232\204\346\234\200\345\244\247\345\200\274.go" new file mode 100644 index 0000000..9a934a3 --- /dev/null +++ "b/LeetCode/\345\211\221\346\214\207offer/day27_\346\240\210\344\270\216\351\230\237\345\210\227(\345\233\260\351\232\276)/\351\230\237\345\210\227\347\232\204\346\234\200\345\244\247\345\200\274.go" @@ -0,0 +1,61 @@ +//题目链接:https://leetcode.cn/problems/dui-lie-de-zui-da-zhi-lcof/?envType=study-plan&id=lcof +package main + +//有了上一题的基础后,这一题就简简单单了 +//和第一题的解析类似,这里我们也可以使用单调队列。把队列看作是第一题的窗口,只不过滑动规则有所差异, +//pop 出队时,窗口的左指针右移,并对我们实现的单调队列进行相应操作,求队列最大值时,只需返回单调队列队首元素即可。 +type MaxQueue struct { + // 存储队列 + q []int + // 单调队列 + monoQ []int +} + +// 构造函数 +func Constructor() MaxQueue { + return MaxQueue{[]int{},[]int{}} +} + +// 取最大值,若队列长度为 0,返回-1,否则返回单调队列队首元素 +func (this *MaxQueue) Max_value() int { + if len(this.q) == 0{ + return -1 + } + return this.monoQ[0] +} + +// 入队,存储队列直接入队 +// 单调队列需要判断队尾元素与新元素大小关系 +// 若新元素大于队尾元素,出队 +// 直至单调队列长度为 0或者 新元素值 小于等于 队尾元素 +func (this *MaxQueue) Push_back(value int) { + this.q = append(this.q,value) + for len(this.monoQ)>0 && value>this.monoQ[len(this.monoQ)-1]{ + this.monoQ = this.monoQ[:len(this.monoQ)-1] + } + this.monoQ = append(this.monoQ,value) +} + +// 出队,先判断存储队列长度,若为 0,返回-1 +// 获取存储队列队首元素后,存储队列队首元素出队 +// 若该元素等于单调队列队首元素,则单调队列队首元素出队 +func (this *MaxQueue) Pop_front() int { + if len(this.q) == 0{ + return -1 + } + res := this.q[0] + this.q = this.q[1:] + if res == this.monoQ[0]{ + this.monoQ = this.monoQ[1:] + } + return res +} + + +/** + * Your MaxQueue object will be instantiated and called as such: + * obj := Constructor(); + * param_1 := obj.Max_value(); + * obj.Push_back(value); + * param_3 := obj.Pop_front(); + */ \ No newline at end of file diff --git "a/LeetCode/\345\211\221\346\214\207offer/day28_\346\220\234\347\264\242\344\270\216\345\233\236\346\272\257\347\256\227\346\263\225(\345\233\260\351\232\276)/\345\255\227\347\254\246\344\270\262\347\232\204\346\216\222\345\210\227.go" "b/LeetCode/\345\211\221\346\214\207offer/day28_\346\220\234\347\264\242\344\270\216\345\233\236\346\272\257\347\256\227\346\263\225(\345\233\260\351\232\276)/\345\255\227\347\254\246\344\270\262\347\232\204\346\216\222\345\210\227.go" new file mode 100644 index 0000000..f24e5e3 --- /dev/null +++ "b/LeetCode/\345\211\221\346\214\207offer/day28_\346\220\234\347\264\242\344\270\216\345\233\236\346\272\257\347\256\227\346\263\225(\345\233\260\351\232\276)/\345\255\227\347\254\246\344\270\262\347\232\204\346\216\222\345\210\227.go" @@ -0,0 +1,84 @@ +// 题目链接:https://leetcode.cn/problems/zi-fu-chuan-de-pai-lie-lcof/?envType=study-plan&id=lcof +// day28/31 +// 第 28 天主题为:搜索与回溯算法(困难) +// 包含两道题目: +// 剑指offer37.序列化二叉树 +// 剑指offer38.字符串的排列 +package main + +import ( + "fmt" + "sort" +) + +//全排列问题,如果题目所给字符不包含重复字符,用下面简单回溯即可: +func permutation(s string) []string { + res := []string{} + n := len(s) + used := make([]bool,n) + var backtrace func(path string) + backtrace = func(path string){ + if len(path) == n{ + res = append(res,path) + return + } + for i:=0;i0 && runeS[i-1]==runeS[i] && !used[i-1]{ + continue + } + used[i] = true + path = append(path,runeS[i]) + backtrace(path) + path = path[:len(path)-1] + used[i] = false + } + } + backtrace([]byte{}) + fmt.Println(runeS) + return res +} + +//本题解题思路与 LeetCode 48:全排列 II 完全一致,有兴趣可以再去做做练习一下 \ No newline at end of file diff --git "a/LeetCode/\345\211\221\346\214\207offer/day28_\346\220\234\347\264\242\344\270\216\345\233\236\346\272\257\347\256\227\346\263\225(\345\233\260\351\232\276)/\345\272\217\345\210\227\345\214\226\344\272\214\345\217\211\346\240\221.go" "b/LeetCode/\345\211\221\346\214\207offer/day28_\346\220\234\347\264\242\344\270\216\345\233\236\346\272\257\347\256\227\346\263\225(\345\233\260\351\232\276)/\345\272\217\345\210\227\345\214\226\344\272\214\345\217\211\346\240\221.go" new file mode 100644 index 0000000..d60c500 --- /dev/null +++ "b/LeetCode/\345\211\221\346\214\207offer/day28_\346\220\234\347\264\242\344\270\216\345\233\236\346\272\257\347\256\227\346\263\225(\345\233\260\351\232\276)/\345\272\217\345\210\227\345\214\226\344\272\214\345\217\211\346\240\221.go" @@ -0,0 +1,84 @@ +//题目链接:https://leetcode.cn/problems/xu-lie-hua-er-cha-shu-lcof/?envType=study-plan&id=lcof +package main + +import ( + "strconv" + "strings" +) + +//本题接参考自官方题解:https://leetcode.cn/problems/serialize-and-deserialize-binary-tree/solution/er-cha-shu-de-xu-lie-hua-yu-fan-xu-lie-hua-by-le-2/ +// +//二叉树的序列化本质上是对其值进行编码,更重要的是对其结构进行编码。可以遍历树来完成上述任务。 +//众所周知,我们一般有两个策略:广度优先搜索和深度优先搜索。 +//广度优先搜索可以按照层次的顺序从上到下遍历所有的节点 +// +//深度优先搜索可以从一个根开始,一直延伸到某个叶,然后回到根,到达另一个分支。 +//根据根节点、左节点和右节点之间的相对顺序,可以进一步将深度优先搜索策略区分为:先序遍历、中序遍历 以及 后序遍历。 +// +//对题目所给例子进行举例,最终序列化字符串是 1,2,3,None,None,4,None,None,5,None,None,。 +//其中,None,None 用来标记缺少左右子节点,这是我们在序列化期间保存树结构的方式。 +// +//即我们可以先序遍历这颗二叉树,遇到空子树的时候序列化成 None,否则继续递归序列化。那么我们如何反序列化呢? +//首先我们需要根据 , 把原先的序列分割开来得到先序遍历的元素列表,然后从左向右遍历这个序列: +//- 如果当前的元素为 None,则当前为空树 +//- 否则先解析这棵树的左子树,再解析它的右子树 + +//Definition for a binary tree node. +type TreeNode struct { + Val int + Left *TreeNode + Right *TreeNode +} + + +type Codec struct { + +} + +func Constructor() Codec { + return Codec{} +} + +// Serializes a tree to a single string. +func (this *Codec) serialize(root *TreeNode) string { + var sb strings.Builder + var preorder func(root *TreeNode) + preorder = func(root *TreeNode) { + if root == nil{ + sb.WriteString("nil,") + return + } + sb.WriteString(strconv.Itoa(root.Val)) + sb.WriteString(",") + preorder(root.Left) + preorder(root.Right) + } + preorder(root) + return sb.String() +} + +// Deserializes your encoded data to tree. +func (this *Codec) deserialize(data string) *TreeNode { + s := strings.Split(data,",") + var build func() *TreeNode + build = func() *TreeNode { + if s[0] == "nil"{ + s = s[1:] + return nil + } + val,_ := strconv.Atoi(s[0]) + s = s[1:] + node := &TreeNode{val,build(),build()} + return node + } + return build() +} + + +/** + * Your Codec object will be instantiated and called as such: + * ser := Constructor(); + * deser := Constructor(); + * data := ser.serialize(root); + * ans := deser.deserialize(data); + */ \ No newline at end of file diff --git "a/LeetCode/\345\211\221\346\214\207offer/day8_\345\212\250\346\200\201\350\247\204\345\210\222(\347\256\200\345\215\225)/\346\226\220\346\263\242\351\202\243\345\245\221\346\225\260\345\210\227.go" "b/LeetCode/\345\211\221\346\214\207offer/day8_\345\212\250\346\200\201\350\247\204\345\210\222(\347\256\200\345\215\225)/\346\226\220\346\263\242\351\202\243\345\245\221\346\225\260\345\210\227.go" index 813a9c3..49fd990 100644 --- "a/LeetCode/\345\211\221\346\214\207offer/day8_\345\212\250\346\200\201\350\247\204\345\210\222(\347\256\200\345\215\225)/\346\226\220\346\263\242\351\202\243\345\245\221\346\225\260\345\210\227.go" +++ "b/LeetCode/\345\211\221\346\214\207offer/day8_\345\212\250\346\200\201\350\247\204\345\210\222(\347\256\200\345\215\225)/\346\226\220\346\263\242\351\202\243\345\245\221\346\225\260\345\210\227.go" @@ -1,6 +1,6 @@ // 题目链接:https://leetcode.cn/problems/fei-bo-na-qi-shu-lie-lcof/?envType=study-plan&id=lcof -// day7/31 -// 第 7 天主题为:搜索与回溯算法(简单),与前一天主题相同 +// day8/31 +// 第 8 天主题为:动态规划(简单) // 包含三道题目: // 剑指offer10-I.斐波那契数列 // 剑指offer10-II.青蛙跳台阶问题