diff --git a/Week_04/id_117/LeetCode_169_117.go b/Week_04/id_117/LeetCode_169_117.go new file mode 100644 index 00000000..c3410ae8 --- /dev/null +++ b/Week_04/id_117/LeetCode_169_117.go @@ -0,0 +1,54 @@ +package solution + +/* +169. Majority Element + +Given an array of size n, find the majority element. The majority element is the element that appears more than ⌊ n/2 ⌋ times. + +You may assume that the array is non-empty and the majority element always exist in the array. + +Example 1: + +Input: [3,2,3] +Output: 3 +Example 2: + +Input: [2,2,1,1,1,2,2] +Output: 2 +*/ + +// 方法一, 直接定义map[int]int来存放数据, 找出出现次数最多的数即可, map的长度为 n/2 + 1, 空间复杂度O(n), 时间复杂度O(n) +func majorityElement(nums []int) int { + mps := make(map[int]int, 0) + numsLen := len(nums) + res := 0 + for _, item := range nums { + mps[item]++ + if mps[item] > numsLen/2 { + res = item + } + } + return res +} + +// 方法二, 分治法思想 +// 摩尔投票法,先假设第一个数过半,并设置count=1,之后遍历剩余数据,如果相同则+1,如果不同则-1; 前提条件是存在元素的个数大于n/2 +func majorityElement2(nums []int) int { + numsLen := len(nums) + if numsLen == 0 { + return 0 + } + majar := nums[0] + count := 1 + for i := 1; i < numsLen; i++ { + if count == 0 { + majar = nums[i] + } + if majar == nums[i] { + count++ + } else { + count-- + } + } + return majar +} diff --git a/Week_04/id_117/LeetCode_169_117_test.go b/Week_04/id_117/LeetCode_169_117_test.go new file mode 100644 index 00000000..7bd4b747 --- /dev/null +++ b/Week_04/id_117/LeetCode_169_117_test.go @@ -0,0 +1,32 @@ +package solution + +import ( + "testing" +) + +func Test_eventualSafeNodes(t *testing.T) { + testData := []map[string]interface{}{ + map[string]interface{}{ + "name": "test1", + "ins": []int{3, 2, 3}, + "outs": 3, + }, + map[string]interface{}{ + "name": "test1", + "ins": []int{2, 2, 1, 1, 1, 2, 2}, + "outs": 2, + }, + } + for _, tt := range testData { + name := tt["name"].(string) + ins := tt["ins"].([]int) + outs := tt["outs"].(int) + t.Run(name, func(t *testing.T) { + got := majorityElement2(ins) + eq := got == outs + if !eq { + t.Errorf("eventualSafeNodes() = %v, want %v", got, outs) + } + }) + } +} diff --git a/Week_04/id_117/LeetCode_211_117.go b/Week_04/id_117/LeetCode_211_117.go new file mode 100644 index 00000000..61c0c0f1 --- /dev/null +++ b/Week_04/id_117/LeetCode_211_117.go @@ -0,0 +1,131 @@ +package solution + +/* +211. Add and Search Word - Data structure design + +Design a data structure that supports the following two operations: + +void addWord(word) +bool search(word) +search(word) can search a literal word or a regular expression string containing only letters a-z or .. A . means it can represent any one letter. + +Example: + +addWord("bad") +addWord("dad") +addWord("mad") +search("pad") -> false +search("bad") -> true +search(".ad") -> true +search("b..") -> true +Note: +You may assume that all words are consist of lowercase letters a-z. +*/ + +/** + * 测试案例 + * Your WordDictionary object will be instantiated and called as such: + * obj := Constructor(); + * obj.AddWord(word); + * param_2 := obj.Search(word); + */ + +// 方法一 +type WordDictionary struct { + Words map[int][]string +} + +/** Initialize your data structure here. */ +func Constructor() WordDictionary { + return WordDictionary{Words: make(map[int][]string)} +} + +/** Adds a word into the data structure. */ +func (this *WordDictionary) AddWord(word string) { + m := len(word) + if len(this.Words[m]) > 0 { + this.Words[m] = append(this.Words[m], word) + } else { + this.Words[m] = []string{word} + } +} + +/** Returns if the word is in the data structure. A word could contain the dot character '.' to represent any one letter. */ +func (this *WordDictionary) Search(word string) bool { + m := len(word) + if this.Words[m] == nil { + return false + } + // 当前列表中元素 + w := len(this.Words[m]) + for i := 0; i < w; i++ { + tmp := true + for j := 0; j < m; j++ { + if word[j] != '.' && word[j] != this.Words[m][i][j] { + tmp = false + continue + } + } + if tmp { + return true + } + } + + return false +} + +// 方法二 +type WordDictionary2 struct { + children map[rune]*WordDictionary2 + isWord bool +} + +/** Initialize your data structure here. */ +func Constructor2() WordDictionary2 { + return WordDictionary2{children: make(map[rune]*WordDictionary2)} +} + +/** Adds a word into the data structure. */ +func (this *WordDictionary2) AddWord2(word string) { + parent := this + for _, v := range word { + // 判断是否已存储在树中 + if child, ok := parent.children[v]; !ok { + newDic := &WordDictionary2{ + children: make(map[rune]*WordDictionary2), + isWord: false, + } + parent.children[v] = newDic + parent = newDic + } else { + parent = child + } + } + parent.isWord = true +} + +/** Returns if the word is in the data structure. A word could contain the dot character '.' to represent any one letter. */ +func (this *WordDictionary2) Search2(word string) bool { + parent := this + wLen := len(word) + for i := 0; i < wLen; i++ { + if word[i] == '.' { + // 递归查找 + mapStatus := false + for _, v := range parent.children { + if v.Search2(word[i+1:]) { + mapStatus = true + } + } + return mapStatus + } else { + // 非.字符直接判读 + if _, ok := parent.children[rune(word[i])]; !ok { + return false + } + } + // 移动指针 + parent = parent.children[rune(word[i])] + } + return len(parent.children) == 0 || parent.isWord +} diff --git a/Week_04/id_117/LeetCode_211_117_test.go b/Week_04/id_117/LeetCode_211_117_test.go new file mode 100644 index 00000000..f2c6f673 --- /dev/null +++ b/Week_04/id_117/LeetCode_211_117_test.go @@ -0,0 +1,36 @@ +package solution + +import ( + "testing" +) + +func Test_Search(t *testing.T) { + testData := []map[string]interface{}{ + map[string]interface{}{ + "name": "test1", + "ins": []string{"bad", "dad", "mad"}, + "finds": []string{"pad", "bad", ".ad", "b.."}, + "outs": []bool{false, true, true, true}, + }, + } + + for _, tt := range testData { + name := tt["name"].(string) + finds := tt["finds"].([]string) + ins := tt["ins"].([]string) + outs := tt["outs"].([]bool) + t.Run(name, func(t *testing.T) { + obj := Constructor2() + for _, word := range ins { + obj.AddWord2(word) + } + for k, word := range finds { + + got := obj.Search2(word) + if got != outs[k] { + t.Errorf("Search got= %v, want %v, k=%d", got, outs[k], k) + } + } + }) + } +} diff --git a/Week_04/id_117/LeetCode_241_117.go b/Week_04/id_117/LeetCode_241_117.go new file mode 100644 index 00000000..05ac6d8a --- /dev/null +++ b/Week_04/id_117/LeetCode_241_117.go @@ -0,0 +1,103 @@ +package solution + +import ( + "fmt" + "strconv" +) + +/* +241. Different Ways to Add Parentheses + +Given a string of numbers and operators, return all possible results from computing all the different possible ways to group numbers and operators. The valid operators are +, - and *. + +Example 1: + +Input: "2-1-1" +Output: [0, 2] +Explanation: +((2-1)-1) = 0 +(2-(1-1)) = 2 +Example 2: + +Input: "2*3-4*5" +Output: [-34, -14, -10, -10, 10] +Explanation: +(2*(3-(4*5))) = -34 +((2*3)-(4*5)) = -14 +((2*(3-4))*5) = -10 +(2*((3-4)*5)) = -10 +(((2*3)-4)*5) = 10 +*/ + +/* +方法1,直接for遍历,然后根据运算符来执行判断, 为了减少计算次数,最好使用map来保存已经计算的值 +*/ + +func diffWaysToCompute(input string) []int { + ans := make([]int, 0) + for i := 0; i < len(input); i++ { + if input[i] == '-' || input[i] == '+' || input[i] == '*' { + parts1 := input[0:i] + parts2 := input[i+1:] + parts1Res := diffWaysToCompute(parts1) // [2, 33]结果 + parts2Res := diffWaysToCompute(parts2) // [2, 1]结果 + for _, m := range parts1Res { + for _, n := range parts2Res { + c := 0 + if input[i] == '*' { + c = m * n + } + if input[i] == '+' { + c = m + n + } + if input[i] == '-' { + c = m - n + } + ans = append(ans, c) + } + } + } + } + + if len(ans) == 0 { + x, _ := strconv.Atoi(input) + ans = append(ans, x) + } + + return ans +} + +/* +分析: +1.有多少个运算符就有多少个括号 +2.从第一个数开始递归查找,每一个运算符都有两种方式 +3.表达式如何存储? +*/ +func diffWaysToCompute2(input string) []int { + exps := make([]string, 0) // 表达式 + ans := make([]int, 0) // 表达式计算值 + curStr := input + compute(input, &exps, curStr) + return ans +} + +func compute(input string, exps *[]string, curStr string) { + // 主逻辑, 每次去掉一个数字和运算符 + symbolNum := len(input) / 2 + if symbolNum == 1 { + return + } + fmt.Println("curStr ====>", curStr) + // 左边第一个数加i+1个括号 + for i := 0; i < symbolNum; i++ { + curStr = fmt.Sprintf("(%s)", curStr) + fmt.Println("curStr ===>", curStr) + compute(input[2:], exps, curStr) + } +} + +/* +设想: +1.是否可以将操作符和数字分别取出来,然后计算 +2.是否可以使用动态规划来实现,在某一个数字的组合种类为前面组合种类递推 +*/ diff --git a/Week_04/id_117/LeetCode_241_117_test.go b/Week_04/id_117/LeetCode_241_117_test.go new file mode 100644 index 00000000..f650b9c7 --- /dev/null +++ b/Week_04/id_117/LeetCode_241_117_test.go @@ -0,0 +1,40 @@ +package solution + +import ( + "reflect" + "sort" + "testing" +) + +func Test_diffWaysToCompute(t *testing.T) { + testData := []map[string]interface{}{ + map[string]interface{}{ + "name": "test1", + "ins": "2-1-1", + "outs": []int{0, 2}, + }, + map[string]interface{}{ + "name": "test1", + "ins": "2*3-4*5", + "outs": []int{-34, -14, -10, -10, 10}, + }, + map[string]interface{}{ + "name": "test1", + "ins": "2-1", + "outs": []int{1}, + }, + } + for _, tt := range testData { + name := tt["name"].(string) + ins := tt["ins"].(string) + outs := tt["outs"].([]int) + t.Run(name, func(t *testing.T) { + got := diffWaysToCompute(ins) + sort.Ints(got) + eq := reflect.DeepEqual(got, outs) + if !eq { + t.Errorf("eventualSafeNodes() = %v, want %v", got, outs) + } + }) + } +} diff --git a/Week_04/id_117/LeetCode_309_117.go b/Week_04/id_117/LeetCode_309_117.go new file mode 100644 index 00000000..cbb6227d --- /dev/null +++ b/Week_04/id_117/LeetCode_309_117.go @@ -0,0 +1,51 @@ +package solution + +/* +309. Best Time to Buy and Sell Stock with Cooldown + +Say you have an array for which the ith element is the price of a given stock on day i. + +Design an algorithm to find the maximum profit. You may complete as many transactions as you like (ie, buy one and sell one share of the stock multiple times) with the following restrictions: + +You may not engage in multiple transactions at the same time (ie, you must sell the stock before you buy again). +After you sell your stock, you cannot buy stock on next day. (ie, cooldown 1 day) +Example: + +Input: [1,2,3,0,2] +Output: 3 +Explanation: transactions = [buy, sell, cooldown, buy, sell] +*/ +/* +解题思路: +prices := [1,2,3,0,2] +1.第i天买股票能够剩下的最大利润 +(1)第i-1天买股票能够剩下的最大利润, 即第i天冷冻 +(2)第i-2天卖股票剩余的最大利润 - 第i天买股票能够剩下的最大利润 +公式: buy[i] = max(buy[i-1], sell[i-2] - prices[i-1]) +2.第i天卖股票能够剩下的最大利润 +(1)第i-1天卖股票能够剩下的最大利润, 即第i天冷冻 +(2)第i天卖股票的钱 + 第i-1天买股票剩余的最大利润 +公式: sell[i] = max(sell[i-1], buy[i-1] + prices[i-1]) +3.初始化设置buy[1]= prices[0], 默认第1天就开始买, 如果第2天是较低价格, 则会重新设置 +*/ + +func getMax(a, b int) int { + if a > b { + return a + } + return b +} +func maxProfit(prices []int) int { + pLen := len(prices) + if pLen <= 0 { + return 0 + } + sell := make([]int, pLen+1) + buy := make([]int, pLen+1) + buy[1] = -prices[0] + for i := 2; i <= pLen; i++ { + buy[i] = getMax(buy[i-1], sell[i-2]-prices[i-1]) + sell[i] = getMax(sell[i-1], buy[i-1]+prices[i-1]) + } + return sell[pLen] +} diff --git a/Week_04/id_117/LeetCode_309_117_test.go b/Week_04/id_117/LeetCode_309_117_test.go new file mode 100644 index 00000000..e6381f61 --- /dev/null +++ b/Week_04/id_117/LeetCode_309_117_test.go @@ -0,0 +1,32 @@ +package solution + +import ( + "testing" +) + +func Test_maxProfit(t *testing.T) { + testData := []map[string]interface{}{ + map[string]interface{}{ + "name": "test1", + "ins": []int{1, 2, 3, 0, 2}, + "outs": 3, + }, + map[string]interface{}{ + "name": "test2", + "ins": []int{100, 99, 88, 77, 66, 55, 44}, + "outs": 0, + }, + } + for _, tt := range testData { + name := tt["name"].(string) + ins := tt["ins"].([]int) + outs := tt["outs"].(int) + t.Run(name, func(t *testing.T) { + got := maxProfit(ins) + eq := (got == outs) + if !eq { + t.Errorf("maxProfit() = %v, want %v", got, outs) + } + }) + } +} diff --git a/Week_04/id_117/LeetCode_746_117.go b/Week_04/id_117/LeetCode_746_117.go new file mode 100644 index 00000000..ff06ac56 --- /dev/null +++ b/Week_04/id_117/LeetCode_746_117.go @@ -0,0 +1,47 @@ +package solution + +/* +746. Min Cost Climbing Stairs + +On a staircase, the i-th step has some non-negative cost cost[i] assigned (0 indexed). + +Once you pay the cost, you can either climb one or two steps. You need to find minimum cost to reach the top of the floor, and you can either start from the step with index 0, or the step with index 1. + +Example 1: +Input: cost = [10, 15, 20] +Output: 15 +Explanation: Cheapest is start on cost[1], pay that cost and go to the top. +Example 2: +Input: cost = [1, 100, 1, 1, 1, 100, 1, 1, 100, 1] +Output: 6 +Explanation: Cheapest is start on cost[0], and only step on 1s, skipping cost[3]. +Note: +cost will have a length in the range [2, 1000]. +Every cost[i] will be an integer in the range [0, 999]. + +*/ + +func getSm(a, b int) int { + if a > b { + return b + } + return a +} + +func minCostClimbingStairs(cost []int) int { + costLen := len(cost) + if costLen == 0 { + return -1 + } + if costLen == 1 { + return cost[0] + } + + dp := make([]int, costLen+1) + dp[0] = cost[0] + dp[1] = cost[1] + for i := 2; i < costLen; i++ { + dp[i] = getSm(dp[i-1], dp[i-2]) + cost[i] + } + return getSm(dp[costLen-1], dp[costLen-2]) +} diff --git a/Week_04/id_117/LeetCode_746_117_test.go b/Week_04/id_117/LeetCode_746_117_test.go new file mode 100644 index 00000000..1325f643 --- /dev/null +++ b/Week_04/id_117/LeetCode_746_117_test.go @@ -0,0 +1,43 @@ +package solution + +import ( + "reflect" + "testing" +) + +/* +Example 1: +Input: cost = [10, 15, 20] +Output: 15 +Explanation: Cheapest is start on cost[1], pay that cost and go to the top. +Example 2: +Input: cost = [1, 100, 1, 1, 1, 100, 1, 1, 100, 1] +Output: 6 +Explanation: Cheapest is start on cost[0], and only step on 1s, skipping cost[3]. +*/ +func Test_letterCasePermutation(t *testing.T) { + testData := []map[string]interface{}{ + map[string]interface{}{ + "name": "test1", + "ins": []int{10, 15, 20}, + "outs": 15, + }, + map[string]interface{}{ + "name": "test2", + "ins": []int{1, 100, 1, 1, 1, 100, 1, 1, 100, 1}, + "outs": 6, + }, + } + for _, tt := range testData { + name := tt["name"].(string) + ins := tt["ins"].([]int) + outs := tt["outs"].(int) + t.Run(name, func(t *testing.T) { + got := minCostClimbingStairs(ins) + eq := reflect.DeepEqual(got, outs) + if !eq { + t.Errorf("minCostClimbingStairs() = %v, want %v", got, outs) + } + }) + } +} diff --git a/Week_04/id_117/LeetCode_784_117.go b/Week_04/id_117/LeetCode_784_117.go new file mode 100644 index 00000000..087c3d3b --- /dev/null +++ b/Week_04/id_117/LeetCode_784_117.go @@ -0,0 +1,73 @@ +package solution + +import ( + "strings" +) + +/* +784. Letter Case Permutation + +Given a string S, we can transform every letter individually to be lowercase or uppercase to create another string. Return a list of all possible strings we could create. + +Examples: +Input: S = "a1b2" +Output: ["a1b2", "a1B2", "A1b2", "A1B2"] + +Input: S = "3z4" +Output: ["3z4", "3Z4"] + +Input: S = "12345" +Output: ["12345"] +Note: + +S will be a string with length between 1 and 12. +S will consist only of letters or digits. +*/ + +// 方法一,必须要先转换大小写 +func letterCasePermutation(S string) []string { + // 先转化为小写 + S = strings.ToLower(S) + mps := make(map[string]string) + permute(S, mps) + ans := make([]string, 0) + for _, v := range mps { + ans = append(ans, v) + } + return ans +} + +func permute(s string, m map[string]string) { + if _, ok := m[s]; !ok { + m[s] = s + for i := 0; i < len(s); i++ { + temp := s + temp = string(temp[0:i]) + strings.ToUpper(string(temp[i])) + string(temp[i+1:]) + temp = strings.TrimSpace(temp) + permute(temp, m) + } + } +} + +// 方法二,节省了转化大小写的时间,直接分别递归 +func letterCasePermutation2(s string) []string { + ans := make([]string, 0) + formatString(s, 0, "", &ans) + return ans +} + +func formatString(s string, i int, prevString string, ans *[]string) { + if i >= len(s) { + *ans = append(*ans, prevString) + return + } + if s[i] >= 48 && s[i] <= 57 { + formatString(s, i+1, prevString+string(s[i]), ans) + } else if s[i] >= 65 && s[i] <= 90 { + formatString(s, i+1, prevString+string(s[i]+byte(32)), ans) + formatString(s, i+1, prevString+string(s[i]), ans) + } else { + formatString(s, i+1, prevString+string(s[i]-byte(32)), ans) + formatString(s, i+1, prevString+string(s[i]), ans) + } +} diff --git a/Week_04/id_117/LeetCode_784_117_test.go b/Week_04/id_117/LeetCode_784_117_test.go new file mode 100644 index 00000000..c0586298 --- /dev/null +++ b/Week_04/id_117/LeetCode_784_117_test.go @@ -0,0 +1,46 @@ +package solution + +import ( + "reflect" + "sort" + "testing" +) + +func Test_letterCasePermutation(t *testing.T) { + testData := []map[string]interface{}{ + map[string]interface{}{ + "name": "test1", + "ins": "a1b2", + "outs": []string{"a1b2", "a1B2", "A1b2", "A1B2"}, + }, + map[string]interface{}{ + "name": "test2", + "ins": "3z4", + "outs": []string{"3z4", "3Z4"}, + }, + map[string]interface{}{ + "name": "test3", + "ins": "12345", + "outs": []string{"12345"}, + }, + map[string]interface{}{ + "name": "test1", + "ins": "a1b2c", + "outs": []string{"a1b2c", "a1B2c", "A1b2c", "a1b2C", "A1B2c", "a1B2C", "A1b2C", "A1B2C"}, + }, + } + for _, tt := range testData { + name := tt["name"].(string) + ins := tt["ins"].(string) + outs := tt["outs"].([]string) + t.Run(name, func(t *testing.T) { + got := letterCasePermutation(ins) + sort.Strings(got) + sort.Strings(outs) + eq := reflect.DeepEqual(got, outs) + if !eq { + t.Errorf("eventualSafeNodes() = %v, want %v", got, outs) + } + }) + } +} diff --git a/Week_04/id_117/NOTE.md b/Week_04/id_117/NOTE.md deleted file mode 100644 index c684e62f..00000000 --- a/Week_04/id_117/NOTE.md +++ /dev/null @@ -1 +0,0 @@ -# 学习笔记 \ No newline at end of file diff --git a/Week_04/id_117/README.md b/Week_04/id_117/README.md new file mode 100644 index 00000000..3b41466d --- /dev/null +++ b/Week_04/id_117/README.md @@ -0,0 +1,78 @@ +# 学习笔记 + +### 169 +方法一, 直接定义map[int]int来存放数据, 找出出现次数最多的数即可, map的长度为 n/2 + 1, 空间复杂度O(n), 时间复杂度O(n) + +方法二, 分治法思想, 摩尔投票法,先假设第一个数过半,并设置count=1,之后遍历剩余数据,如果相同则+1,如果不同则-1; 前提条件是存在元素的个数大于n/2 + +### 241 +方法一,直接for遍历,然后根据运算符来执行判断, 这里的收获是在每一层定义ans来保存当前的原始值或者计算值 + +方法二,使用动态规划,定义dp为三维数组dp[][][]int + +方法三,希望能够将每一个表达式计算出来,但是不知如何保存 + +### 784 +方法一, 使用`map[string]string`存储已经变换的字符串 + +### 211 +方法一,使用暴力破解法,使用`map[int][]string`将长度相同的单词存放在一起,比较简单 +方法二,使用`Trie`树(前缀树) + +`Trie`树特点: +1.根节点不包含字符,除根节点外的每一个子节点都包含一个字符。 +2.从根节点到某一个节点,路径上经过的字符连接起来,为该节点对应的字符串。 +3.每个节点的所有子节点包含的字符互不相同。 + +关键点在于构建和查找 +```go +type WordDictionary struct { + // 使用某一层数据 + children map[rune]*WordDictionary + isWord bool +} + + +/** Initialize your data structure here. */ +func Constructor() WordDictionary { + return WordDictionary{children:make(map[rune]*WordDictionary)} +} + + +/** Adds a word into the data structure. */ +func (this *WordDictionary) AddWord(word string) { + parent := this + for _,ch := range word { + if child,ok:= parent.children[ch];ok { + parent = child + } else { + newChild := &WordDictionary{children:make(map[rune]*WordDictionary)} + parent.children[ch] = newChild + // 下一层 + parent = newChild + } + } + parent.isWord = true +} + + +/** Returns if the word is in the data structure. A word could contain the dot character '.' to represent any one letter. */ +func (this *WordDictionary) Search(word string) bool { + parent := this + for i,ch := range word { + if rune(ch) == '.' { + isMatched := false + for _,v := range parent.children { + if v.Search(word[i+1:]) { + isMatched = true + } + } + return isMatched + } else if _,ok := parent.children[rune(ch)];!ok { + return false + } + parent = parent.children[rune(ch)] + } + return len(parent.children) ==0 || parent.isWord +} +```