Leetcode模块训练2

2022-11-16 17:08:06 浏览数 (1)

1. 两数之和(1)#

代码语言:javascript复制
给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target  的那 两个 整数,并返回它们的数组下标。
你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。
你可以按任意顺序返回答案。

示例 1:
输入:nums = [2,7,11,15], target = 9
输出:[0,1]
解释:因为 nums[0]   nums[1] == 9 ,返回 [0, 1] 。

示例 2:
输入:nums = [3,2,4], target = 6
输出:[1,2]

示例 3:
输入:nums = [3,3], target = 6
输出:[0,1]

提示:
2 <= nums.length <= 104
只会存在一个有效答案
代码语言:javascript复制
//查找表法
//用map记录已经遍历过的数值和下标
//空间换时间
//时间复杂度:O(N)
//空间复杂度:O(N)
func twoSum(nums []int, target int) []int {
	mapTemp := make(map[int]int, len(nums))
	for i, v := range nums {
		if j, ok := mapTemp[target-v]; ok {
			return []int{j, i}
		}
		mapTemp[v] = i
	}
	return nil
}

func main() {
	nums := []int{2, 11, 7, 15}
	target := 9

	result1 := twoSum(nums, target)
	fmt.Println(result1)
}

2. 模拟行走机器人(874)#

代码语言:javascript复制
机器人在一个无限大小的 XY 网格平面上行走,从点(0, 0) 处开始出发,面向北方。
该机器人可以接收以下三种类型的命令 commands :
	-2 :向左转90 度
	-1 :向右转 90 度
	1 <= x <= 9 :向前移动x个单位长度
在网格上有一些格子被视为障碍物obstacles 。第 i 个障碍物位于网格点 obstacles[i] = (xi, yi)
机器人无法走到障碍物上,它将会停留在障碍物的前一个网格方块上,但仍然可以继续尝试进行该路线的其余部分
返回从原点到机器人所有经过的路径点(坐标为整数)的最大欧式距离的平方。(即,如果距离为 5 ,则返回 25 )

注意:
	北表示  Y 方向。
	东表示  X 方向。
	南表示 -Y 方向。
	西表示 -X 方向。

示例 1:
输入:commands = [4,-1,3], obstacles = []
输出:25

解释:
机器人开始位于 (0, 0):
1. 向北移动 4 个单位,到达 (0, 4)
2. 右转
3. 向东移动 3 个单位,到达 (3, 4)
距离原点最远的是 (3, 4) ,距离为 32   42 = 25

示例2:
输入:commands = [4,-1,4,-2,4], obstacles = [[2,4]]
输出:65

解释:
机器人开始位于 (0, 0):
1. 向北移动 4 个单位,到达 (0, 4)
2. 右转
3. 向东移动 1 个单位,然后被位于 (2, 4) 的障碍物阻挡,机器人停在 (1, 4)
4. 左转
5. 向北走 4 个单位,到达 (1, 8)
距离原点最远的是 (1, 8) ,距离为 12   82 = 65

提示:
commands[i] 只会存在于 [-2,-1,1,2,3,4,5,6,7,8,9] 中
代码语言:javascript复制
func max(i, j int) int {
	if i < j {
		return j
	}
	return i
}

func robotSim(commands []int, obstacles [][]int) int {
	// 判断障碍物在该位置是否存在
	obstaclesMap := make(map[[2]int]bool)
	for _, v := range obstacles {
		if len(v) == 2 {
			obstaclesMap[[2]int{v[0], v[1]}] = true
		}
	}
	// []int{北,东,南,西}
	dirX := [4]int{0, 1, 0, -1}
	dirY := [4]int{1, 0, -1, 0}
	x, y, dir, distance := 0, 0, 0, 0 // 当前位置,当前方向,欧式距离
	for _, v := range commands {
		switch v {
		case -2: // 左转
			dir = (dir   3) % 4
		case -1: // 右转
			dir = (dir   1) % 4
		default: // 前进
			for i := 0; i < v; i   { // 向dir方向走v步
				nextX := x   dirX[dir]
				nextY := y   dirY[dir]
				// 判断是否存在障碍物
				if _, ok := obstaclesMap[[2]int{nextX, nextY}]; ok {
					break
				}
				x = nextX
				y = nextY
				// 计算最大欧氏距离
				distance = max(distance, x*x y*y)
			}
		}

	}
	return distance
}

func main() {
	commands := []int{4, -1, 4, -2, 4}
	var obstacles [][]int
	obstacles = append(obstacles, []int{2, 4})
	sim := robotSim(commands, obstacles)
	fmt.Println(sim)
}

3. 字母异位词分组(49)#

代码语言:javascript复制
给你一个字符串数组,请你将 字母异位词 组合在一起。可以按任意顺序返回结果列表。
字母异位词 是由重新排列源单词的字母得到的一个新单词,所有源单词中的字母通常恰好只用一次。

示例 1:
输入: strs = ["eat", "tea", "tan", "ate", "nat", "bat"]
输出: [["bat"],["nat","tan"],["ate","eat","tea"]]

示例 2:
输入: strs = [""]
输出: [[""]]

示例 3:
输入: strs = ["a"]
输出: [["a"]]

提示:strs[i] 仅包含小写字母
代码语言:javascript复制
/*
排序法
*/
func groupAnagrams(strs []string) [][]string {
	strMap := make(map[string][]string, 0)
	for _, v := range strs {
		strByte := []byte(v)
		sort.Slice(strByte, func(i, j int) bool {
			return strByte[i] < strByte[j]
		})
		strMap[string(strByte)] = append(strMap[string(strByte)], v)
	}
	result := make([][]string, 0, len(strMap))
	for _, v := range strMap {
		result = append(result, v)
	}
	return result
}

func main() {
	strs := []string{"eat", "tea", "tan", "ate", "nat", "bat"}
	anagrams := groupAnagrams(strs)
	fmt.Println(anagrams)
}

4. 串联所有单词的子串(30)#

代码语言:javascript复制
给定一个字符串s和一个字符串数组words。words中所有字符串 长度相同。
s中的 串联子串 是指一个包含words中所有字符串以任意顺序排列连接起来的子串。
例如,如果words = ["ab","cd","ef"], 那么"abcdef","abefcd","cdabef","cdefab",
"efabcd",和"efcdab" 都是串联子串。"acdbef" 不是串联子串,因为他不是任何words排列的连接。
返回所有串联字串在s中的开始索引。你可以以 任意顺序 返回答案。

示例 1:
输入:s = "barfoothefoobarman", words = ["foo","bar"]
输出:[0,9]
解释:因为 words.length == 2 同时 words[i].length == 3,连接的子字符串的长度必须为 6。
子串 "barfoo" 开始位置是 0。它是 words 中以 ["bar","foo"] 顺序排列的连接。
子串 "foobar" 开始位置是 9。它是 words 中以 ["foo","bar"] 顺序排列的连接。
输出顺序无关紧要。返回 [9,0] 也是可以的。

示例 2:
输入:s = "wordgoodgoodgoodbestword", words = ["word","good","best","word"]
输出:[]
解释:因为 words.length == 4 并且 words[i].length == 4,所以串联子串的长度必须为 16。
s 中没有子串长度为 16 并且等于 words 的任何顺序排列的连接。
所以我们返回一个空数组。

示例 3:
输入:s = "barfoofoobarthefoobarman", words = ["bar","foo","the"]
输出:[6,9,12]
解释:因为 words.length == 3 并且 words[i].length == 3,所以串联子串的长度必须为 9。
子串 "foobarthe" 开始位置是 6。它是 words 中以 ["foo","bar","the"] 顺序排列的连接。
子串 "barthefoo" 开始位置是 9。它是 words 中以 ["bar","the","foo"] 顺序排列的连接。
子串 "thefoobar" 开始位置是 12。它是 words 中以 ["the","foo","bar"] 顺序排列的连接。

提示:
1 <= s.length <= 104
1 <= words.length <= 5000
1 <= words[i].length <= 30
words[i] 和 s 由小写英文字母组成
代码语言:javascript复制
/*
map,自己写的,时间复杂度偏高
时间复杂度:O(ls*m*n), ls 是输入 s 的长度,m 是 word 的单词, n 是 words 中每个单词的长度
空间复杂度:O(m*n), m 是 word 的单词数,n 是 words 中每个单词的长度
*/
func findSubstring(s string, words []string) []int {
	wordFirstMap := make(map[string]bool, 0)
	for _, v := range words {
		firstStr := string(v[0])
		wordFirstMap[firstStr] = true
	}
	wordLength := len(words[0])
	amount := len(words)
	length := amount * wordLength
	result := make([]int, 0)
	for i, v := range s {
		if i length > len(s) {
			break
		}
		vStr := string(v)
		if wordFirstMap[vStr] && i length <= len(s) {
			tempStr := s[i : i length]
			tempWords := make([]string, 0, amount)
			tempWordMap := make(map[string]int, 0)
			for _, u := range words {
				tempWordMap[u]  = 1
			}
			for j := 0; j < length; {
				value := tempStr[j : j wordLength]
				if tempWordMap[value] > 0 {
					tempWords = append(tempWords, value)
					tempWordMap[value]--
				} else {
					break
				}
				j  = wordLength
			}
			if len(tempWords) == amount {
				result = append(result, i)
			}
		}
	}
	return result
}

/*
滑动窗口
时间复杂度:O(ls×n), ls 是输入 s 的长度,n 是 words 中每个单词的长度
空间复杂度:O(m×n), m 是 word 的单词数,n 是 words 中每个单词的长度
*/
func findSubstring2(s string, words []string) (ans []int) {
	ls, m, n := len(s), len(words), len(words[0])
	for i := 0; i < n && i m*n <= ls; i   {
		differ := map[string]int{}
		for j := 0; j < m; j   {
			differ[s[i j*n:i (j 1)*n]]  
		}
		for _, word := range words {
			differ[word]--
			if differ[word] == 0 {
				delete(differ, word)
			}
		}
		for start := i; start < ls-m*n 1; start  = n {
			if start != i {
				word := s[start (m-1)*n : start m*n]
				differ[word]  
				if differ[word] == 0 {
					delete(differ, word)
				}
				word = s[start-n : start]
				differ[word]--
				if differ[word] == 0 {
					delete(differ, word)
				}
			}
			if len(differ) == 0 {
				ans = append(ans, start)
			}
		}
	}
	return
}

func main() {
	s := "wordgoodgoodgoodbestword"
	words := []string{"word", "good", "best", "good"}
	fmt.Println(findSubstring(s, words))
	fmt.Println(findSubstring2(s, words))
}

5. LRU缓存(146)#

代码语言:javascript复制
请你设计并实现一个满足  LRU (最近最少使用) 缓存 约束的数据结构。
实现 LRUCache 类:
LRUCache(int capacity) 以 正整数 作为容量 capacity 初始化 LRU 缓存
int get(int key) 如果关键字 key 存在于缓存中,则返回关键字的值,否则返回 -1 。
void put(int key, int value) 如果关键字 key 已经存在,则变更其数据值 value ;
如果不存在,则向缓存中插入该组 key-value 。如果插入操作导致关键字数量超过 capacity ,则应该 逐出 最久未使用的关键字。
函数 get 和 put 必须以 O(1) 的平均时间复杂度运行。

示例:
输入
["LRUCache", "put", "put", "get", "put", "get", "put", "get", "get", "get"]
[[2], [1, 1], [2, 2], [1], [3, 3], [2], [4, 4], [1], [3], [4]]
输出
[null, null, null, 1, null, -1, null, -1, 3, 4]
解释
LRUCache lRUCache = new LRUCache(2);
lRUCache.put(1, 1); // 缓存是 {1=1}
lRUCache.put(2, 2); // 缓存是 {1=1, 2=2}
lRUCache.get(1);    // 返回 1
lRUCache.put(3, 3); // 该操作会使得关键字 2 作废,缓存是 {1=1, 3=3}
lRUCache.get(2);    // 返回 -1 (未找到)
lRUCache.put(4, 4); // 该操作会使得关键字 1 作废,缓存是 {4=4, 3=3}
lRUCache.get(1);    // 返回 -1 (未找到)
lRUCache.get(3);    // 返回 3
lRUCache.get(4);    // 返回 4
代码语言:javascript复制
/*
哈希表 双向链表
双向链表带有头结点和尾结点,双向链表用于按时间顺序保存节点
时间复杂度:O(1)
空间复杂度:O(capacity)
*/

type LRUCache struct {
	size       int
	capacity   int
	cache      map[int]*DLinkedNode
	head, tail *DLinkedNode
}

func Constructor(capacity int) LRUCache {
	l := LRUCache{
		capacity: capacity,
		cache:    map[int]*DLinkedNode{},
		head:     initDLinkedNode(0, 0),
		tail:     initDLinkedNode(0, 0),
	}
	l.head.next = l.tail
	l.tail.prev = l.head
	return l
}

func (this *LRUCache) Get(key int) int {
	if v, ok := this.cache[key]; ok {
		this.moveToHead(v)
		return v.value
	}
	return -1
}

func (this *LRUCache) Put(key int, value int) {
	if _, ok := this.cache[key]; !ok {
		node := initDLinkedNode(key, value)
		this.cache[key] = node
		this.addToHead(node)
		this.size  
		if this.size > this.capacity {
			tail := this.removeTail()
			delete(this.cache, tail.key)
			this.size--
		}
	} else {
		node := this.cache[key]
		node.value = value
		this.moveToHead(node)
	}
}

type DLinkedNode struct {
	key, value int
	prev, next *DLinkedNode
}

func initDLinkedNode(key, value int) *DLinkedNode {
	return &DLinkedNode{
		key:   key,
		value: value,
	}
}

func (this *LRUCache) addToHead(node *DLinkedNode) {
	node.prev = this.head
	node.next = this.head.next
	this.head.next.prev = node
	this.head.next = node
}

func (this *LRUCache) removeNode(node *DLinkedNode) {
	node.prev.next = node.next
	node.next.prev = node.prev
}

func (this *LRUCache) moveToHead(node *DLinkedNode) {
	this.removeNode(node)
	this.addToHead(node)
}

func (this *LRUCache) removeTail() *DLinkedNode {
	node := this.tail.prev
	this.removeNode(node)
	return node
}

/**
 * Your LRUCache object will be instantiated and called as such:
 * obj := Constructor(capacity);
 * param_1 := obj.Get(key);
 * obj.Put(key,value);
 */
func main() {
	capacity := 2
	obj := Constructor(capacity)
	obj.Put(1, 1)
	obj.Put(2, 2)
	obj.Get(1)
}

6. 子域名访问计数(811)#

代码语言:javascript复制
网站域名 "discuss.leetcode.com" 由多个子域名组成。顶级域名为 "com" ,二级域名为 "leetcode.com" ,
最低一级为 "discuss.leetcode.com" 。当访问域名 "discuss.leetcode.com" 时,
同时也会隐式访问其父域名 "leetcode.com" 以及 "com" 。
计数配对域名 是遵循 "rep d1.d2.d3" 或 "rep d1.d2" 格式的一个域名表示,其中 rep 表示访问域名的次数,d1.d2.d3 为域名本身。
例如:
"9001 discuss.leetcode.com" 就是一个 计数配对域名 ,表示 discuss.leetcode.com 被访问了 9001 次。
给你一个 计数配对域名 组成的数组 cpdomains ,解析得到输入中每个子域名对应的 计数配对域名 ,并以数组形式返回。可以按 任意顺序 返回答案。

示例 1:
输入:cpdomains = ["9001 discuss.leetcode.com"]
输出:["9001 leetcode.com","9001 discuss.leetcode.com","9001 com"]
解释:
例子中仅包含一个网站域名:"discuss.leetcode.com"。
按照前文描述,子域名 "leetcode.com" 和 "com" 都会被访问,所以它们都被访问了 9001 次。

示例 2:
输入:cpdomains = ["900 google.mail.com", "50 yahoo.com", "1 intel.mail.com", "5 wiki.org"]
输出:["901 mail.com","50 yahoo.com","900 google.mail.com","5 wiki.org","5 org","1 intel.mail.com","951 com"]
解释:
按照前文描述,会访问 "google.mail.com" 900 次,"yahoo.com" 50 次,"intel.mail.com" 1 次,"wiki.org" 5 次。
而对于父域名,会访问 "mail.com" 900   1 = 901 次,"com" 900   50   1 = 951 次,和 "org" 5 次。

提示:
1 <= cpdomain.length <= 100
1 <= cpdomain[i].length <= 100
cpdomain[i] 会遵循 "repi d1i.d2i.d3i" 或 "repi d1i.d2i" 格式
repi 是范围 [1, 104] 内的一个整数
d1i、d2i 和 d3i 由小写英文字母组成
代码语言:javascript复制
/*
map
时间复杂度:O(n),n为所有子域名的长度
空间复杂度:O(n),n为所有子域名的长度
*/
func subdomainVisits(cpdomains []string) []string {
	domainMap := make(map[string]int, 0)
	for _, v := range cpdomains {
		splits := strings.Split(v, " ")
		rep, _ := strconv.Atoi(splits[0])
		domain := splits[1]
		splitDom := strings.Split(domain, ".")
		tempDom := ""
		for i := len(splitDom) - 1; i >= 0; i-- {
			if i == len(splitDom)-1 {
				tempDom = splitDom[i]
			} else {
				tempDom = splitDom[i]   "."   tempDom
			}
			domainMap[tempDom]  = rep
		}
	}
	res := make([]string, 0, len(domainMap))
	for k, v := range domainMap {
		rep := strconv.Itoa(v)
		res = append(res, rep " " k)
	}
	return res
}

func main() {
	cpdomains := []string{"900 google.mail.com", "50 yahoo.com", "1 intel.mail.com", "5 wiki.org"}
	fmt.Println(subdomainVisits(cpdomains))
}

7. 数组的度(697)#

代码语言:javascript复制
给定一个非空且只包含非负数的整数数组 nums,数组的 度 的定义是指数组里任一元素出现频数的最大值。
你的任务是在 nums 中找到与 nums 拥有相同大小的度的最短连续子数组,返回其长度。

示例 1:
输入:nums = [1,2,2,3,1]
输出:2
解释:
输入数组的度是 2 ,因为元素 1 和 2 的出现频数最大,均为 2 。
连续子数组里面拥有相同度的有如下所示:
[1, 2, 2, 3, 1], [1, 2, 2, 3], [2, 2, 3, 1], [1, 2, 2], [2, 2, 3], [2, 2]
最短连续子数组 [2, 2] 的长度为 2 ,所以返回 2 。

示例 2:
输入:nums = [1,2,2,3,1,4,2]
输出:6
解释:
数组的度是 3 ,因为元素 2 重复出现 3 次。
所以 [2,2,3,1,4,2] 是最短子数组,因此返回 6 。

提示:
nums.length 在 1 到 50,000 范围内。
nums[i] 是一个在 0 到 49,999 范围内的整数。
代码语言:javascript复制
/*
我的解法map:
时间复杂度较高
空间复杂度:O(n)
*/
func findShortestSubArray(nums []int) int {
	numMap, maxNum, maxCount := make(map[int]int, 0), 0, 0
	for _, v := range nums {
		numMap[v]  
		if numMap[v] > maxCount {
			maxNum = v
			maxCount = numMap[v]
		}
	}

	maxSlice := make([]int, 0)
	maxSlice = append(maxSlice, maxNum)
	for k, v := range numMap {
		if v == maxCount && k != maxNum {
			maxSlice = append(maxSlice, k)
		}
	}
	minLen := 50000
	for _, v := range maxSlice {
		i, j := 0, len(nums)-1
		for i < len(nums) {
			if nums[i] == v {
				break
			}
			i  
		}
		for j >= 0 && j >= i {
			if nums[j] == v {
				break
			}
			j--
		}
		if j-i 1 < minLen {
			minLen = j - i   1
		}
	}
	return minLen
}

/*
官方思路map
时间复杂度:O(n)
空间复杂度:O(n)
*/
type assist struct {
	count int
	l, r  int
}

func min(i, j int) int {
	if i > j {
		return j
	}
	return i
}

func findShortestSubArray2(nums []int) int {
	numMap := make(map[int]*assist, 0)
	for i, v := range nums {
		if value, ok := numMap[v]; ok {
			value.count  
			value.r = i
			numMap[v] = value
		} else {
			numMap[v] = &assist{
				count: 1,
				l:     i,
				r:     i,
			}
		}
	}

	maxCount, res := 0, 0
	for _, v := range numMap {
		if v.count > maxCount {
			maxCount = v.count
			res = v.r - v.l   1
		} else if v.count == maxCount {
			res = min(res, v.r-v.l 1)
		}
	}
	return res
}

func main() {
	nums := []int{1, 2, 2, 3, 1, 4, 2}
	//nums := []int{1, 2, 2, 3, 1}
	fmt.Println(findShortestSubArray(nums))
	fmt.Println(findShortestSubArray2(nums))
}

0 人点赞