8.31任务
1.听一套听力题,细听错误的片段
2.记单词100个
3.go语言基础学习(go语言菜鸟之旅)
4.刷leetcode算法题3道,做好总结
LeetCode面试经典150题 第5题 多数元素
题目描述
给定一个大小为 n 的数组 nums ,返回其中的多数元素。多数元素是指在数组中出现次数大于⌊ n/2 ⌋的元素。
你可以假设数组是非空的,并且给定的数组总是存在多数元素。
ini
示例 1:
输入:nums = [3,2,3]
输出:3
示例 2:
输入:nums = [2,2,1,1,1,2,2]
输出:2
进阶:尝试设计时间复杂度为 O(n)、空间复杂度为 O(1) 的算法解决此问题。
思路
读题,其实就是让我们找数组中占半数以上的自然数,并将其输出。第一个想法是类似于每当有俩个不一样的数,就抵消,最后留下的一定是最多的数
怎么实现?考虑引入多个计数器?感觉有点麻烦
然后转换思路,因为要占半数,所以直接将数组排序, 取最中间的那个数一定是多数的那个元素
第一次写的代码是这样的。。。
go
func majorityElement(nums []int) int {
lens := len(nums)
nums=sort.Ints(nums)
return nums[lens/2]
}
报错Line 3: Char 10: sort.Ints(nums) (no value) used as value (solution.go)
错误原因:
sort.Ints(nums) 并不返回值,因此不能直接将其赋值给 nums。
sort.Ints(nums) 会直接修改切片 nums,而不是返回一个新的排序后的切片。
修改代码:
go
func majorityElement(nums []int) int {
lens := len(nums)
sort.Ints(nums)
return nums[lens/2]
}
还有一个易错点
注意是sort.Ints 而不是.Strings 。
因为本题的数组是Int类型的
官方解答--摩尔投票法
前面提到的排序是最简单实现的,但是时间复杂度比较高。
接下来是官方解答,竟然和我一开始的想法一样(乐),学学他们是怎么实现的。
时间复杂度:O(n),仅遍历一次数组
空间复杂度:O(1),没有使用额外空间
摩尔投票法(Boyer--Moore majority vote algorithm),也被称作「多数投票法」,算法解决的问题是:如何在任意多的候选人中(选票无序),选出获得票数最多的那个。
算法可以分为两个阶段:
对抗阶段:分属两个候选人的票数进行两两对抗抵消
计数阶段:计算对抗结果中最后留下的候选人票数是否有效
思路
将当前票数最多的候选人与其获得的(抵消后)票数分别存储在 major 与 count 中。
当我们遍历下一个选票时,判断当前 count 是否为零:
若 count == 0,代表当前 major 空缺,直接将当前候选人赋值给 major,并令 count++
若 count != 0,代表当前 major 的票数未被完全抵消,因此令 count--,即使用当前候选人的票数抵消 major 的票数(妙哇!)
看着思路自己写一遍代码。
go
func majorityElement(nums []int) int {
leng := len(nums)
count :=1
major :=nums[0]
for i:=1 ; i<leng ; i++{
if nums[i]!=major{
count--
if count==0{
major=nums[i]
count++
}
}else{
count++
}
}
return major
}
过了,但是 时间击败 66.68%使用 Go 的用户 内存击败 54.40%使用 Go 的用户。呃呃呃,看看他们的解答代码。
go
func majorityElement(nums []int) int {
major := 0
count := 0
for _, num := range nums {
if count == 0 {
major = num
}
if major == num {
count += 1
} else {
count -= 1
}
}
return major
}
总结问题: 第一:循环写的不够简洁!
第二:流程应该是先判断是否为0,然后再根据是否相等来加减。我是先判断是否相等,然后若不相等减去,然后判断是否为0。
嘶,我好像写的不对哇。比如数组是1,2,1 。 刚开始major是1,然后判断第2个元素,是2 ,此时count--,为0.会直接将2赋给major???!
总结思考
在判断大多数元素的时候,可以采用对抗的方法,将不同的数俩俩抵消,最后剩下的数一定是大多数元素。。。此时不得不想一下,如果只是要求你求出多数元素,并没有一定到一半以上,该咋做。。。比如数组为1,1,1,2,2,3,3.用我们这个做法得到的是3,但实际却是1。
这就是摩尔投票法后面计数阶段应该做的事情。 代码如下:
go
count = 0
for _, num := range nums {
if num == candidate {
count++
}
}
if count > len(nums) / 2 {
return candidate
}
进行验证,不符合就重新再去找。
然后找了一个类似的题229. 多数元素 II - 力扣(LeetCode)
需要把大于n/3的数都找出来,做法大概就是设置俩个判断点,中间思路和此题一样,最后对两者进行验证,输出符合条件的。
其他大于n/x的,就设置x-1个判断点即可。
直接贴代码:
ini
func majorityElement(nums []int) []int {
candidate1, candidate2 := 0, 0
count1, count2 := 0, 0
for _, num := range nums {
if candidate1 == num {
count1++
} else if candidate2 == num {
count2++
} else if count1 == 0 {
candidate1 = num
count1 = 1
} else if count2 == 0 {
candidate2 = num
count2 = 1
} else {
count1--
count2--
}
}
// 验证两个候选元素的出现次数是否满足要求
count1, count2 = 0, 0
for _, num := range nums {
if num == candidate1 {
count1++
} else if num == candidate2 {
count2++
}
}
result := []int{}
if count1 > len(nums) / 3 {
result = append(result, candidate1)
}
if count2 > len(nums) / 3 {
result = append(result, candidate2)
}
return result
}
打表
上述后面的计数阶段,还是停留在告诉你占了多少分之1,如果啥都没说,就让你求最大值。就可以用打表的思想。
代码如下
go
func majorityElement(nums []int) int {
var statMap = make(map[int]int)
for _, v := range nums {
statMap[v]++
}
maxCount := 0
maxElement := 0
for k, v := range statMap {
if v > maxCount {
maxCount = v
maxElement = k
}
}
return maxElement
}
这种方法就是最易想到的方法,非常滴朴实无华。
面试经典150题 第六题 轮转数组
题目描述
给定一个整数数组 nums,将数组中的元素向右轮转 k 个位置,其中 k 是非负数。
ini
示例 1:
输入: nums = [1,2,3,4,5,6,7], k = 3
输出: [5,6,7,1,2,3,4]
解释:
向右轮转 1 步: [7,1,2,3,4,5,6]
向右轮转 2 步: [6,7,1,2,3,4,5]
向右轮转 3 步: [5,6,7,1,2,3,4]
示例 2:
输入:nums = [-1,-100,3,99], k = 2
输出:[3,99,-1,-100]
解释:
向右轮转 1 步: [99,-1,-100,3]
向右轮转 2 步: [3,99,-1,-100]
进阶:
尽可能想出更多的解决方案,至少有 三种 不同的方法可以解决这个问题。
你可以使用空间复杂度为 O(1) 的 原地 算法解决这个问题吗?
思路
题目很好懂,就是看k是多少,然后把数组依次向右移动相应k位。
最开始的想法: 看k是多少,然后把数组最后的k位放到一个新数组里面,然后老数组依次移动k位,最后在把新数组里面的数按顺序放到老数组里面。
代码如下:
css
func rotate(nums []int, k int) {
arry := make([]int,k)
leng := len(nums)
if leng <= 1 {
return
}
k %= leng
for i:=0;i< k;i++{
arry[i] = nums[leng-i-1]
}
//得逆序来放
for z:= leng-k-1 ; z>=0;z--{
nums[z+k] = nums[z]
}
for j:=0;j<k;j++{
nums[j]=arry[k-j-1]
}
}
代码解释:
kotlin
if leng <= 1 {
return
}
k %= leng
这一块是因为我后面的代码对数组长度为1的时候,没有做特定判断,会报错。 有一说一,上面代码写的可读性一坨。。。。
然后继续想有没有啥其他的解决方法:
还是看看官方解答吧,悲
官方解答---数组翻转
该方法基于如下的事实:当我们将数组的元素向右移动 k 次后,尾部 k mod n 个元素会移动至数组头部,其余元素向后移动 k mod n 个位置。
该方法为数组的翻转:我们可以先将所有元素翻转,这样尾部的 k mod n 个元素就被移至数组头部,然后我们再翻转 [0,k mod n−1]区间的元素和 [k mod n,n−1] 区间的元素即能得到最后的答案。(确实可以)
代码如下:
css
func reverse(a []int) {
for i, n := 0, len(a); i < n/2; i++ {
a[i], a[n-1-i] = a[n-1-i], a[i]
}
}
func rotate(nums []int, k int) {
k %= len(nums)
reverse(nums)
reverse(nums[:k])
reverse(nums[k:])
}
翻转函数实现,是根据中心点对称,将其交换
实现代码不难,其实主要是一个数学的思想,没考虑到三次翻转。
go语言面试题(第一天)
资料来自第一天 · Go语言中文文档-面试题 (topgoer.com)
下面这段代码输出什么
go
package main
import (
"fmt"
)
func main() {
defer_call()
}
func defer_call() {
defer func() { fmt.Println("打印前") }()
defer func() { fmt.Println("打印中") }()
defer func() { fmt.Println("打印后") }()
panic("触发异常")
}
主要考察的应该是 defer 的应用
如果让我做的话我应该会答: 打印前 打印中 触发异常 打印后
因为defer是延后输出 ,但不是都把他推到最后,而是在下一个函数或语句运行后,在输出。
答案
令人感慨,全都错了 。。
go
打印后
打印中
打印前
panic: 触发异常
参考解析:defer 的执行顺序是后进先出。当出现 panic 语句的时候,会先按照 defer 的后进先出的顺序执行,最后才会执行panic
错误原因: defer 没有理解对,而且不知道panic语句的作用。。。go基础不牢,悲()
defer就用堆栈的形式来理解吧,后进先出。
panic介绍
panic 是一个 Go 内置函数,它用来停止当前常规控制流并启动 panicking(运行时恐慌)过程。 当函数 F 调用 panic 函数时,函数 F 的执行停止,函数 F 中已进行了求值的 defer 函数都将得到正常执行,然后函数 F 将控制权返还给其调用者。
用例子来说吧
go
func main(){
fmt.Println("c")
defer func(){ // 必须要先声明defer,否则不能捕获到panic异常
fmt.Println("d")
if err:=recover();err!=nil{
fmt.Println(err) // 这里的err其实就是panic传入的内容
}
fmt.Println("e")
}()
f() //开始调用f
fmt.Println("f") //这里开始下面代码不会再执行
}
func f(){
fmt.Println("a")
panic("异常信息")
fmt.Println("b") //这里开始下面代码不会再执行
fmt.Println("f")
}
输出结果:
c
a
d
异常信息
e
1.在panic后面的代码就不会在执行了
2.顺序是
一、执行c
二、到defer,先放到堆栈中,继续下面代码运行
三、调用函数f,输出到a就停止(因为panic以后就不会在执行代码)
四、此时就会把堆栈中的函数运行完(不会在运行输出f了)
个人理解 代码的运行其实是从堆栈中调用的过程,不过一般都是先进先出,但是到defer是先进后出。然后到panic执行完以后,就不会在读取代码了。但是此时堆栈中的命令还是会继续运行完。
最后!!
正确使用 Panic
[禁止] 使用 panic 用于正常的错误处理。应该使用 error 和多个返回值。 例:(写fabric链码经常用到的)
go
f, err := os.Open("file.txt")
if err != nil {
// handle error here
}
// do stuff with f here
panic 只适合用于那些严重影响程序运行的错误。参考Effective Go - The Go Programming Language