feat(Go-StudyExample):2020/12/25:新增leetcode部分习题(数据结构、二进制相关)

This commit is contained in:
Huangzj
2020-12-25 11:49:56 +08:00
parent 6c3a44b2e5
commit 0a2e5e71a6
12 changed files with 648 additions and 0 deletions
+11
View File
@@ -0,0 +1,11 @@
/*
* @Author : huangzj
* @Time : 2020/12/21 22:26
* @Description
*/
package simple
type Doc interface {
Description(string) string
}
@@ -0,0 +1,78 @@
/*
* @Author : huangzj
* @Time : 2020/12/22 14:23
* @Description
*/
package simple
import (
"fmt"
"testing"
)
type FindDisappearedNumbers struct{}
func (FindDisappearedNumbers) Description(string) string {
return `
给定一个范围在  1 ≤ a[i] ≤ n ( n = 数组大小 ) 的 整型数组,数组中的元素一些出现了两次,另一些只出现一次。
找到所有在 [1, n] 范围之间没有出现在数组中的数字。
您能在不使用额外空间且时间复杂度为O(n)的情况下完成这个任务吗? 你可以假定返回的数组不算在额外空间内。
示例:
输入:
[4,3,2,7,8,2,3,1]
输出:
[5,6]
`
}
//思路分析:
// *
// * 找出 1 - n 中没有出现的数字。不能使用额外的空间,两次循环时间复杂度为 2O(n),即为 O(n)。
// *
// * 解题思路:使用数组的下标来标记数字的出现于否,通过一遍遍历即可标记出全部已经出现的数组
// *
// * [4,3,2,7,8,2,3,1] 初始数据
// *
// * [4,3,2,-7,8,2,3,1] 第一个数据 4 出现,将数组的第四个也就是下标 3 的数据修改为负数。-7 计算时,通过绝对值处理一下即可不影响数据的计算
// * [4,3,-2,-7,8,2,3,1]
// * [4,-3,-2,-7,8,2,3,1]
// * [4,-3,-2,-7,8,2,-3,1]
// * [4,-3,-2,-7,8,2,-3,-1]
// * [4,-3,-2,-7,8,2,-3,-1]
// * [4,-3,-2,-7,8,2,-3,-1]
// * [-4,-3,-2,-7,8,2,-3,-1]
// *
// * 计算结束,数组的第五个,第六个依然为整数,证明 5,6 没有出现
// *
func findDisappearedNumbers(nums []int) []int {
list := make([]int, 0)
for _, num := range nums {
nums[toPositive(num)-1] = -toPositive(nums[toPositive(num)-1])
}
for index, num := range nums {
if num >= 1 {
list = append(list, index+1)
}
}
return list
}
func toPositive(num int) int {
if num < 0 {
return -num
}
return num
}
func TestFindDisappearedNumbers(t *testing.T) {
fmt.Println(findDisappearedNumbers([]int{4, 3, 2, 7, 8, 2, 3, 1}))
}
+66
View File
@@ -0,0 +1,66 @@
/*
* @Author : huangzj
* @Time : 2020/12/21 22:26
* @Description
*/
package simple
import (
"fmt"
"testing"
)
type GroupAnagrams struct{}
func (GroupAnagrams) Description(string) string {
return `
给定一个字符串数组,将字母异位词组合在一起。字母异位词指字母相同,但排列不同的字符串。
示例:
输入: ["eat", "tea", "tan", "ate", "nat", "bat"]
输出:
[
["ate","eat","tea"],
["nat","tan"],
["bat"]
]
说明:
所有输入均为小写字母。
不考虑答案输出的顺序。
`
}
//思路分析:定义26个质数作为字母的哈希,因为题目要求都是小写字母,并且质数相乘不会出现相同(三个不同的质数相乘的结果一定不一样)
//如果单词中包含的是是完全不同的字母。还可以使用二进制32位的26位来表示,性能更好
var prime = []int{2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103}
func groupAnagrams(strs []string) [][]string {
m := make(map[int][]string, 0)
for _, str := range strs {
num := 1
for _, b := range []byte(str) {
num = num * prime[int(b-97)]
}
if _, ok := m[num]; ok {
m[num] = append(m[num], str)
continue
}
m[num] = []string{str}
}
list := make([][]string, 0)
for _, value := range m {
list = append(list, value)
}
return list
}
func TestGroupAnagrams(t *testing.T) {
for _, row := range groupAnagrams([]string{"eat", "tea", "tan", "ate", "nat", "bat"}) {
fmt.Println(row)
}
}
+63
View File
@@ -0,0 +1,63 @@
/*
* @Author : huangzj
* @Time : 2020/12/22 11:53
* @Description
*/
package simple
import (
"fmt"
"testing"
)
type IsAnagram struct{}
func (IsAnagram) Description(string) string {
return `
给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。
示例 1:
输入: s = "anagram", t = "nagaram"
输出: true
示例 2:
输入: s = "rat", t = "car"
输出: false
说明:
你可以假设字符串只包含小写字母。
进阶:
如果输入字符串包含 unicode 字符怎么办?你能否调整你的解法来应对这种情况?
`
}
//思路分析:因为这边只提到了26个字母.所以声明长度为26的数组,在字符串t中出现的字符位 + 1 ,在字符串s中出现的字符位 -1,达到抵消的效果,最后如果数组都为0,就是异位数,否则不是
//这个题目还可以使用排序、前缀树 map 等方法处理
func isAnagram(s string, t string) bool {
if len(s) != len(t) {
return false
}
num := [26]int{}
sb := []byte(s)
tb := []byte(t)
for i := 0; i < len(s); i++ {
num[sb[i]-'a']++
num[tb[i]-'a']--
}
for _, r := range num {
if r != 0 {
return false
}
}
return true
}
func TestIsAnagram(t *testing.T) {
fmt.Println(isAnagram("axmsdawd", "dwsadmxa"))
fmt.Println(isAnagram("axmsdawd", "dwsadmxb"))
}
@@ -0,0 +1,74 @@
/*
* @Author : huangzj
* @Time : 2020/12/21 23:01
* @Description
*/
package simple
import (
"fmt"
"testing"
)
type LongestCommonPrefix struct{}
func (LongestCommonPrefix) Description(string) string {
return `
编写一个函数来查找字符串数组中的最长公共前缀。
如果不存在公共前缀,返回空字符串 ""。
示例 1:
输入: ["flower","flow","flight"]
输出: "fl"
示例 2:
输入: ["dog","racecar","car"]
输出: ""
解释: 输入不存在公共前缀。
`
}
//思路分析:最长前缀其实就是求最长字符串长度.所以第一个字符串作为公共字符串,然后循环所有的字符串,进行公共字符串的缩减
func longestCommonPrefix(strs []string) string {
if len(strs) == 0 {
return ""
}
var match []byte
for index, str := range strs {
if index == 0 {
match = []byte(str)
continue
}
position := getPosition(match, str)
match = match[0:position]
}
return string(match)
}
func getPosition(match []byte, str string) int {
var position int
for i, m := range []byte(str) {
if len(match)-1 < i {
break
}
if m == match[i] {
position++
continue
}
break
}
return position
}
func TestLongestCommonPrefix(t *testing.T) {
fmt.Println(longestCommonPrefix([]string{"flower", "flow", "flight"}))
fmt.Println(longestCommonPrefix([]string{"flow", "flower", "flight"}))
fmt.Println(longestCommonPrefix([]string{"dog", "racecar", "car"}))
fmt.Println(longestCommonPrefix([]string{"cir", "car"}))
}
@@ -0,0 +1,54 @@
/*
* @Author : huangzj
* @Time : 2020/12/24 15:18
* @Description
*/
package simple
import (
"fmt"
"testing"
)
type MajorityElement1 struct{}
func (MajorityElement1) Description(string) string {
return `
给定一个大小为 n 的数组,找到其中的多数元素。多数元素是指在数组中出现次数大于 ⌊ n/2 ⌋ 的元素。
你可以假设数组是非空的,并且给定的数组总是存在多数元素。
示例 1:
输入: [3,2,3]
输出: 3
示例 2:
输入: [2,2,1,1,1,2,2]
输出: 2
`
}
//思路说明:
func majorityElement1(nums []int) int {
var result, count int
for _, num := range nums {
if count == 0 {
result = num
count++
continue
}
if num == result {
count++
continue
}
count--
}
return result
}
func TestMajorityElement1(t *testing.T) {
fmt.Println(majorityElement1([]int{3, 2, 3}))
fmt.Println(majorityElement1([]int{2, 2, 1, 1, 1, 2, 2}))
fmt.Println(majorityElement1([]int{3, 3, 4}))
}
@@ -0,0 +1,109 @@
/*
* @Author : huangzj
* @Time : 2020/12/23 16:37
* @Description
*/
package simple
import (
"fmt"
"testing"
)
type MajorityElement2 struct{}
func (MajorityElement2) Description(string) string {
return `
给定一个大小为 n 的整数数组,找出其中所有出现超过 ⌊ n/3 ⌋ 次的元素。
进阶:尝试设计时间复杂度为 O(n)、空间复杂度为 O(1)的算法解决此问题。
示例 1
输入:[3,2,3]
输出:[3]
示例 2
输入:nums = [1]
输出:[1]
示例 3
输入:[1,1,1,3,3,2,2,2]
输出:[1,2]
`
}
type NumCount struct {
num int
count int
}
//思路说明:如果在一个数组中,有数字超过数组长度的1/3,那么对于整个数组来说,最多有两个数字能够超过1/3
//多数投票升级版:
//
//超过n/3的数最多只能有两个。先选出两个候选人A,B。 遍历数组,分三种情况:
// 1.如果投A(当前元素等于A),则A的票数++;
// 2.如果投B(当前元素等于B),B的票数++;
// 3.如果A,B都不投(即当前与A,B都不相等),那么检查此时A或B的票数是否减为0:
// 3.1 如果为0,则当前元素成为新的候选人;
// 3.2 如果A,B两个人的票数都不为0,那么A,B两个候选人的票数均减一;
//遍历结束后选出了两个候选人,但是这两个候选人是否满足>n/3,还需要再遍历一遍数组,找出两个候选人的具体票数。
func majorityElement2(nums []int) []int {
result := make([]int, 0)
var numCount1, numCount2 NumCount
for _, num := range nums {
switch num {
case numCount1.num:
numCount1.count++
continue
case numCount2.num:
numCount2.count++
default:
if numCount1.count == 0 {
numCount1.num = num
numCount1.count++
continue
}
if numCount2.count == 0 {
numCount2.num = num
numCount2.count++
continue
}
numCount1.count--
numCount2.count--
}
}
//遍历结束后选出了两个候选人,但是这两个候选人是否满足>n/3,还需要再遍历一遍数组,找出两个候选人的具体票数。
var count1, count2 int
for _, num := range nums {
if numCount1.num == num {
count1++
}
if numCount2.num == num && numCount1.num != numCount2.num {
count2++
}
}
if count1 > len(nums)/3 {
result = append(result, numCount1.num)
}
if count2 > len(nums)/3 {
result = append(result, numCount2.num)
}
return result
}
func TestMajorityElement2(t *testing.T) {
fmt.Println(majorityElement2([]int{3, 2, 3}))
fmt.Println(majorityElement2([]int{1, 1, 1, 3, 3, 2, 2, 2}))
fmt.Println(majorityElement2([]int{1}))
}
@@ -0,0 +1,64 @@
/*
* @Author : huangzj
* @Time : 2020/12/24 9:30
* @Description
*/
package SingleNum
import (
"fmt"
"testing"
)
type SingleNumber2 struct{}
func (SingleNumber2) Description(string) string {
return `
给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现了三次。找出那个只出现了一次的元素。
说明:
你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?
示例 1:
输入: [2,2,3,2]
输出: 3
示例2:
输入: [0,1,0,1,0,1,99]
输出: 99
`
}
//思路分析:最常规的方法是使用map统计或者是set去重,这边记录另外两种解法
//如果所有数字都出现了 3 次,那么每一列的 1 的个数就一定是 3 的倍数。之所以有的列不是 3 的倍数,就是因为只出现了 1 次的数贡献出了 1。所以所有不是 3 的倍数的列写 1,其他列写 0 ,就找到了这个出现 1 次的数。
//go这边要取64位,因为我的机器是64位的,如果我取32位就没办法得到正确的答案
func singleNumber2(nums []int) int {
var result int
//遍历每一位。这边只考虑64位的情况
for i := 0; i < 64; i++ {
var res int
//遍历每一个数字
for _, num := range nums {
//对每个数字进行移位,然后 & 上1就可以得到移位后的最后一位是否为1的结果。进行该位置的1的个数的统计
res += (num >> i) & 1
}
//该位置的1的个数%3,余数就是该位置是否为1的判断,然后再移动原来的位置,通过 | 来得到最终的结果
result = result | ((res % 3) << i)
}
return result
}
//思路分析:
//func singleNumber3(nums []int) int {
//
//}
func TestSingleNumber2(t *testing.T) {
fmt.Println(singleNumber2([]int{2, 2, 3, 2}))
fmt.Println(singleNumber2([]int{0, 1, 0, 1, 0, 1, 99}))
fmt.Println(singleNumber2([]int{-2, -2, 1, 1, 4, 1, 4, 4, -4, -2}))
}
@@ -0,0 +1,37 @@
/*
* @Author : huangzj
* @Time : 2020/12/24 14:56
* @Description
*/
package SingleNum
import "testing"
type SimpleNumber3 struct{}
func (SimpleNumber3) Description(string) string {
return `
给定一个整数数组 nums,其中恰好有两个元素只出现一次,其余所有元素均出现两次。 找出只出现一次的那两个元素。
示例 :
输入: [1,2,1,3,2,5]
输出: [3,5]
注意:
结果输出的顺序并不重要,对于上面的例子, [5, 3] 也是正确答案。
你的算法应该具有线性时间复杂度。你能否仅使用常数空间复杂度来实现?
`
}
func singleNumber3(nums []int) int {
//todo 待解答:https://leetcode-cn.com/problems/single-number-iii/solution/zhi-chu-xian-yi-ci-de-shu-zi-iii-by-leetcode/
//todo https://leetcode-cn.com/problems/single-number-iii/solution/java-yi-huo-100-yao-shi-kan-bu-dong-wo-jiu-qu-chu-/
return 1
}
func TestSimpleNum3(t *testing.T) {
}
@@ -0,0 +1,46 @@
/*
* @Author : huangzj
* @Time : 2020/12/24 9:24
* @Description
*/
package SingleNum
import (
"fmt"
"testing"
)
type SingleNumber struct{}
func (SingleNumber) Description(string) string {
return `
给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。
说明:
你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?
示例 1:
输入: [2,2,1]
输出: 1
示例 2:
输入: [4,1,2,1,2]
输出: 4
`
}
func singleNumber(nums []int) int {
var result int
for _, num := range nums {
result = result ^ num
}
return result
}
func TestSingleNumber(t *testing.T) {
fmt.Println(singleNumber([]int{2, 2, 1}))
fmt.Println(singleNumber([]int{4, 1, 2, 1, 2}))
}
+44
View File
@@ -0,0 +1,44 @@
/*
* @Author : huangzj
* @Time : 2020/12/21 11:18
* @Description
*/
package simple
import "testing"
type TwoNum struct{}
func (TwoNum) Description(string) string {
return `
给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。
你可以假设每种输入只会对应一个答案。但是,数组中同一个元素不能使用两遍。
示例:
给定 nums = [2, 7, 11, 15], target = 9
因为 nums[0] + nums[1] = 2 + 7 = 9
所以返回 [0, 1]
`
}
//思路分析:map用来缓存比target小的数据, 比如说A + B =target.先遍历到A,则存储B -> A的下标,当找到B的时候,就可以把A的下标一起返回
func twoSum(nums []int, target int) []int {
deal := make(map[int]int, 0)
for i, row := range nums {
if index, ok := deal[row]; ok {
return []int{index, i}
}
deal[target-row] = i
}
return []int{}
}
func TestTwoSum(t *testing.T) {
}