===问:
给定两个数组,编写一个函数来计算它们的交集。
示例 1:
输入: nums1 = [1,2,2,1], nums2 = [2,2]
输出: [2,2]
示例 2:
输入: nums1 = [4,9,5], nums2 = [9,4,9,8,4]
输出: [4,9]
说明:
- 输出结果中每个元素出现的次数,应与元素在两个数组中出现的次数一致。
- 我们可以不考虑输出结果的顺序。
进阶:
- 如果给定的数组已经排好序呢?你将如何优化你的算法?
- 如果 nums1 的大小比 nums2 小很多,哪种方法更优?
- 如果 nums2 的元素存储在磁盘上,磁盘内存是有限的,并且你不能一次加载所有的元素到内存中,你该怎么办?
===答:
方法一:执行11.56%|90.44% ,内存94.59%
比较鸡贼,因为知道测试数据没有负数,因此用了负数替换,理论我想应该比append()
快点吧~
func intersect1(nums1 []int, nums2 []int) []int {
numsA := nums1
numsB := nums2
if len(nums1) < len(nums2) {
numsA = nums2
numsB = nums1
}
var r []int
for _, v := range numsA {
for i, y := range numsB {
if v == y {
r = append(r, v)
numsB[i] = -1
//退出一层
break
}
}
}
return r
}
方法二:执行100.00%|90.44% ,内存100.00%|94.59%
看了一下内存排名第一的用了递归。。。
func intersect2(nums1 []int, nums2 []int) []int {
numsA := nums1
numsB := nums2
if len(nums1) < len(nums2) {
numsA = nums2
numsB = nums1
}
var r []int
for _, v := range numsA {
// 本局意义不大,系统发现numsB为空值亦不会循环
/*if len(numsB) == 0 {
return r
}*/
for i, y := range numsB {
if v == y {
r = append(r, v)
//去除一个元素
numsB = append(numsB[:i], numsB[i+1:]...)
//退出一层
break
}
}
}
return r
}
===知识点
本题别的没什么好总结的,主要是一个知识点要强化一下,那就是break
、continue
、goto
的用法
参考:golang跳转语句goto,break,continue的使用及区别
goto
goto语句可以无条件地转移到过程中指定的行。 通常与条件语句配合使用。可用来实现条件转移, 构成循环,跳出循环体等功能。
在结构化程序设计中一般不主张使用goto语句, 以免造成程序流程的混乱
goto对应(标签)既可以定义在for循环前面,也可以定义在for循环后面,当跳转到标签地方时,继续执行标签下面的代码。
func goto1() {
// 放在for前面,此例会一直循环下去
Loop:
fmt.Println("test")
for a:=0;a<5;a++{
fmt.Println(a)
if a>3{
goto Loop
}
}
}
func goto2() {
for a:=0;a<5;a++{
fmt.Println(a)
if a>3{
goto Loop
}
}
// 放在for后边,跳出结束
Loop:
fmt.Println("test")
}
break
在没有使用loop标签的时候break只是跳出了第一层for循环
使用标签后跳出到指定的标签,break只能跳出到之前,
如果将Loop标签放在后边则会报错
break标签只能用于for循环,跳出后不再执行标签对应的for循环
func breakTest() {
//正常执行
Loop:
for j := 0; j < 3; j++ {
// 出错
// Loop:
fmt.Println(j)
for a := 0; a < 5; a++ {
// 出错
// Loop:
fmt.Println(a)
if a > 3 {
// 出错
// Loop:
break Loop
}
}
}
// 出错
// Loop:
fmt.Println("end")
}
/*
输出:
0
0
1
2
3
4
end
*/
continue
continue就是直接进入下一次循环。break是跳出整个循环,标签的使用类似于break
// 只能放在这个位置,其余位置和break一样,皆出错
loop:
for j := 0; j < 3; j++ {
fmt.Println(j)
for a := 0; a < 5; a++ {
fmt.Println(a)
if a > 3 {
continue loop
}
}
}
fmt.Println("end")
/*
输出:
0
0
1
2
3
4
1
0
1
2
3
4
2
0
1
2
3
4
end
*/
goto语句本身就是做跳转用的,而break和continue是配合for使用的。所以goto的使用不限于for,通常与条件语句配合使用
在for循环中break和continue可以配合标签使用。