代码每日一练--total38

前言

从现在开始,争取每天写一道题目,再将题目的解答思路分析出来,将每一类型的题归纳到一起去,这一篇主要写动态规划,将不断进行更新,欢迎一起探讨思路。
total
38

排序

问题1–把数组排成最小的数

https://leetcode-cn.com/problems/ba-shu-zu-pai-cheng-zui-xiao-de-shu-lcof/
输入一个非负整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。

在这里插入代码片

问题2–德州扑克

https://leetcode-cn.com/problems/bu-ke-pai-zhong-de-shun-zi-lcof/
从扑克牌中随机抽5张牌,判断是不是一个顺子,即这5张牌是不是连续的。2~10为数字本身,A为1,J为11,Q为12,K为13,而大、小王为 0 ,可以看成任意数字。A 不能视为 14。

func isStraight(nums []int) bool {
	sort.Ints(nums)
	var zero int
	for i:=0;i<=len(nums)-2;i++{
		if nums[i]==0{
			zero++
		}else{
			cap:=nums[i+1]-nums[i] - 1
			if cap > 0{
				zero = zero - cap
				if zero<0{
					return false
				}
			}else if cap<0{
				return false
			}
		}
	}
	return true
}

搜索与回溯

问题1–机器人运动范围

https://leetcode-cn.com/problems/ji-qi-ren-de-yun-dong-fan-wei-lcof/
地上有一个m行n列的方格,从坐标 [0,0] 到坐标 [m-1,n-1] 。一个机器人从坐标 [0, 0] 的格子开始移动,它每次可以向左、右、上、下移动一格(不能移动到方格外),也不能进入行坐标和列坐标的数位之和大于k的格子。例如,当k为18时,机器人能够进入方格 [35, 37] ,因为3+5+3+7=18。但它不能进入方格 [35, 38],因为3+5+3+8=19。请问该机器人能够到达多少个格子?

func getSUmForK(i,j int)int{
	var sum int
	for i>0{
		sum = sum + i%10
		i = i/10
	}
	for j>0{
		sum = sum + j%10
		j = j/10
	}
	return sum
}
//考虑矩阵的特性,右下角的值是矩阵的最大
//由于采用的是位数相加,所以无法找出数学规律,需要进行搜索
func innerMove(res *int,i,j,m,n,k int,path[][]bool){
	//上可达,满足小于k,没有走到过
	if i-1>=0&&getSUmForK(i-1,j)<=k&&path[i-1][j]==false{
		*res = *res + 1
		path[i-1][j] = true
		innerMove(res,i-1,j,m,n,k,path)
	}
	//下
	if i+1<m&&getSUmForK(i+1,j)<=k&&path[i+1][j]==false{
		*res = *res + 1
		path[i+1][j] = true
		innerMove(res,i+1,j,m,n,k,path)
	}
	//左
	if j-1>=0&&getSUmForK(i,j-1)<=k&&path[i][j-1]==false{
		*res = *res + 1
		path[i][j-1] = true
		innerMove(res,i,j-1,m,n,k,path)
	}
	//右
	if j+1<n&&getSUmForK(i,j+1)<=k&&path[i][j+1]==false{
		*res = *res + 1
		path[i][j+1] = true
		innerMove(res,i,j+1,m,n,k,path)
	}
}

func movingCount(m int, n int, k int) int {
	path:=make([][]bool,m)
	for i:=0;i<m;i++{
		path[i] = make([]bool,n)
	}
	var res int
	path[0][0] = true
	res = 1
	innerMove(&res,0,0,m,n,k,path)
	return res
}

问题2–矩阵中路径

//采用暴力解决方法,遍历数组查看是否有符合的内容
func exist(board [][]byte, word string) bool {
	if len(word) < 1 || len(board)==0{
		return false
	}
	m:=len(board)
	n:=len(board[0])
	for i:=0;i<len(board);i++{
		for j:=0;j<len(board[i]);j++{
			if word[0] == board[i][j]{
				if len(word)==1{
					return true
				}
				path:=make([][]bool,m)
				for i:=0;i<m;i++{
					path[i] = make([]bool,n)
				}
				path[i][j] = true
				if innerExist(board,word[1:],i,j,m,n,path){
					return true
				}
			}
		}
	}
	return false
}

func innerExist(board [][]byte,word string,i,j,m,n int,path [][]bool)bool{
	//上边可以走,上面没有走过,且相等
	if i-1>=0 && path[i-1][j]==false && word[0]==board[i-1][j]{
		//满足条件
		if len(word)==1{
			return true
		}else{
			path[i-1][j] = true
			if innerExist(board,word[1:],i-1,j,m,n,path){
				return true
			}
			path[i-1][j] = false
		}
	}
	//下边可以走
	if i+1<m && path[i+1][j]==false && word[0]==board[i+1][j]{
		//满足条件
		if len(word)==1{
			return true
		}else{
			path[i+1][j] = true
			if innerExist(board,word[1:],i+1,j,m,n,path){
				return true
			}
			path[i+1][j] = false
		}
	}
	//左边
	if j-1>=0 && path[i][j-1]==false && word[0]==board[i][j-1]{
		//满足条件
		if len(word)==1{
			return true
		}else{
			path[i][j-1] = true
			if innerExist(board,word[1:],i,j-1,m,n,path){
				return true
			}
			path[i][j-1] = false
		}
	}
	//右边
	if j+1<n && path[i][j+1]==false && word[0]==board[i][j+1]{
		//满足条件
		if len(word)==1{
			return true
		}else{
			path[i][j+1] = true
			if innerExist(board,word[1:],i,j+1,m,n,path){
				return true
			}
			path[i][j+1] = false
		}
	}
	return false
}

问题3–二叉树和为某一值

https://leetcode-cn.com/problems/er-cha-shu-zhong-he-wei-mou-yi-zhi-de-lu-jing-lcof/

func innerPathSum(root *TreeNode, now,target int,arr []int,res *[][]int){
	if root==nil{
		return
	}
	if root.Val + now ==target&& root.Left==nil&&root.Right==nil{
		arr = append(arr,root.Val)
		result:=make([]int,0,len(arr))
		for i:=0;i<len(arr);i++{
			result = append(result,arr[i])
		}
		*res = append(*res,result)
		return
	}
	if root.Left!=nil||root.Right!=nil {
		arr = append(arr,root.Val)
		innerPathSum(root.Left,now+root.Val,target,arr,res)
		innerPathSum(root.Right,now+root.Val,target,arr,res)
	}
}

func pathSum(root *TreeNode, target int) [][]int {
	if root==nil{
		return nil
	}
	nowtarget:=root.Val
	if nowtarget==target&& root.Left==nil&&root.Right==nil{
		return [][]int{{target}}
	}
	var res [][]int
	innerPathSum(root.Left,nowtarget,target,[]int{nowtarget},&res)
	innerPathSum(root.Right,nowtarget,target,[]int{nowtarget},&res)
	return res
}

动态规划

问题1–动态规划

描述

https://leetcode-cn.com/problems/wildcard-matching/submissions/
给定一个字符串 (s) 和一个字符模式 § ,实现一个支持 ‘?’ 和 ‘’ 的通配符匹配。
‘?’ 可以匹配任何单个字符。
'
’ 可以匹配任意字符串(包括空字符串)。
两个字符串完全匹配才算匹配成功。
说明:
s 可能为空,且只包含从 a-z 的小写字母。
p 可能为空,且只包含从 a-z 的小写字母,以及字符 ? 和 *。

思路

一开始想的是递归
1.针对p的p1位?符号一定可以匹配一个s的坐标s1,isMatch(s[s1:],p[p1:]) = isMatch(s[s1+1:],p[p1+1:])
2.针对p的p1位符号可以匹配多个任意值或者是不匹配,所以isMatch(s[s1:],p[p1:]) = isMatch(s[s1+1:],p[p1:]) ||isMatch(s[s1:],p[p1+1:])
但是这样做有一个问题是时间复杂度的问题,递归的时间复杂度等于递归的层数
递归的操作次数
如果遇到过个的字符串,很容易超时
转换思路,使用动态规划
涉及dp[i][j]为长度i的s与长度j的p的匹配结果
如果s[i]p[j]或者p[j]‘?’,那么dp[i][j] = dp[i-1][j-1]
如果p[j]==’

那么p[j]不起作用dp[i][j] = dp[i][j-1]
那么p[j]起作用,dp[i][j] = dp[i-1][j]
匹配结果dp[i][j] = dp[i][j-1] || dp[i-1][j]
再根据原则初始化我们的原本dp数组,最终可以得到代码如下
通配符匹配,针对’?'的场景,

func isMatch(s string, p string) bool {
    slength,plength:=len(s),len(p)
    dp:=make([][]bool,slength+1,slength+1)
    for i:=0;i<=slength;i++{
    	dp[i] = make([]bool,plength+1,plength+1)
	}
	//如果s为空,p为空,那么可以匹配
	dp[0][0] = true
	//如果s为空,p只有全是*才可以匹配,注意p的坐标从0开始,而我们的dp是从1开始,所以需要i+1,下面为了防止越界也需要i-1
	for i:=0;i<plength;i++{
		if p[i]=='*'{
			dp[0][i+1] = true
		}else{
			break
		}
	}
	for i:=1;i<=slength;i++{
		for j:=1;j<=plength;j++{
			if s[i-1]==p[j-1]||p[j-1]=='?'{
				//i,j相等,则结果与i-1,j-1保持一致
				dp[i][j] = dp[i-1][j-1]
			}else if p[j-1]=='*'{
				//p[j]==*,有两个可能性,1用上了dp[i-1][j],2没用上dp[i][j-1]
				dp[i][j] = dp[i-1][j]||dp[i][j-1]
			}
		}
	}
	return dp[slength][plength]
}

问题2–动态规划 不同路径

https://leetcode-cn.com/problems/unique-paths/

描述

一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。
机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )。
问总共有多少条不同的路径?

//思路:机器人每次只能向右,或者向下走
//得出公式 dp[i][j] = dp[i-1][j] + dp[i][j-1]
//i,j表达用户走到i行,j列的地方有多少方式
//最终能走的步数就是dp[m][n]
func uniquePaths(m int, n int) int {
	dp:=make([][]int,m+1)
	for i:=0;i<m+1;i++{
		dp[i] = make([]int,n+1)
	}
	//初始化机器人走路方式,机器人往右边直走,和往下面直走,都只有一种方式
	for i:=1;i<=m; i++{
		dp[i][1] = 1
	}
	for j:=1;j<=n;j++{
		dp[1][j] = 1
	}
	for i:=2;i<=m;i++{
		for j:=2;j<=n;j++{
			dp[i][j] = dp[i-1][j] + dp[i][j-1]
		}
	}
	return dp[m][n]
}
//时间复杂度 O(mxn)
//空间复杂度 O(mxn)

问题3–动态规划 不同路径 v2

https://leetcode-cn.com/problems/unique-paths-ii/

描述

一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。
机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。
现在考虑网格中有障碍物。那么从左上角到右下角将会有多少条不同的路径?

//思路:机器人每次只能向右,或者向下走
//得出公式 dp[i][j] = dp[i-1][j] + dp[i][j-1]
//i,j表达用户走到i行,j列的地方有多少方式
//最终能走的步数就是dp[m][n]
//由于数组中有障碍物,障碍物的坐标不可到达 dp[i][j] = 0
func uniquePathsWithObstacles(obstacleGrid [][]int) int {
	if len(obstacleGrid) == 0 {
		return 0
	}
	m := len(obstacleGrid)
	n := len(obstacleGrid[0])
	dp := make([][]int, m)
	for i := 0; i < m; i++ {
		dp[i] = make([]int, n)
	}
	//初始化路径
	for i:=0;i<m;i++{
		if obstacleGrid[i][0]==0{
			dp[i][0] = 1
		}else{
			break
		}
	}
	for j:=0;j<n;j++{
		if obstacleGrid[0][j]==0{
			dp[0][j] = 1
		}else{
			break
		}
	}


	for i := 1; i < m; i++ {
		for j := 1; j < n; j++ {
			if obstacleGrid[i][j] == 1 {
				dp[i][j] = 0
			} else {
				dp[i][j] = dp[i-1][j] + dp[i][j-1]
			}
		}
	}
	return dp[m-1][n-1]
}

问题4–动态规划

给你一个只包含 ‘(’ 和 ‘)’ 的字符串,找出最长有效(格式正确且连续)括号子串的长度。

//动态规划
//dp[i]表示i坐标下最长连续有效括号的数目
//当s[i]==')'有如下情况
//1.s[i-1]=='(',那么dp[i] = dp[i-2] +2
//2.s[i-1]==')',那么要回溯进行查找
//上一个连续有效括号最大数 可能与上上一个连续有效括号数连起来  加上本次数目
//dp[i] = dp[i-1]    +    dp[i-dp[i-1]-2]   +   2
func max(a, b int) int {
	if a > b {
		return a
	}
	return b
}
func longestValidParentheses(s string) int {
	maxAns := 0
	dp := make([]int, len(s))
	for i := 1; i < len(s); i++ {
		if s[i] == ')' {
			if s[i-1] == '(' {
				if i >= 2 {
					dp[i] = dp[i - 2] + 2
				} else {
					dp[i] = 2
				}
			} else if i - dp[i - 1] > 0 && s[i - dp[i - 1] - 1] == '(' {
				if i - dp[i - 1] >= 2 {
					dp[i] = dp[i - 1] + dp[i - dp[i - 1] - 2] + 2
				} else {
					dp[i] = dp[i - 1] + 2
				}
			}
			maxAns = max(maxAns, dp[i])
		}
	}
	return maxAns
}

问题4–动态规划 斐波那契取余

https://leetcode-cn.com/problems/fei-bo-na-qi-shu-lie-lcof/

func fib(n int) int {
	if n<=1{
		return n
	}
	dp:=[]int64{0,1}
	for i:=2;i<=n;i++{
		dp = append(dp,(dp[i-1] + dp[i-2])%1000000007)
	}
	return int(dp[n])
}

问题5–动态规划 青蛙跳台阶

https://leetcode-cn.com/problems/qing-wa-tiao-tai-jie-wen-ti-lcof/submissions/

// 青蛙跳 1 1
//  2 [1 1] [2]
// 3 [1][1 1] [1][1] [2][1]
//青蛙跳到n级台阶的次数等于 跳到n-1级再加n-2级
func numWays(n int) int {
    dp:=[]int {1,1,2}
    for i:=3;i<=n;i++{
        dp = append(dp,(dp[i-1] + dp[i-2])%1000000007)
    }
    return dp[n]
}

问题6–动态规划 买卖股票

时间复杂度O(N)空间复杂度O(1)

//买卖股票一次能获得的最大利润
func maxProfit(prices []int) int {
	if len(prices) < 1 {
		return 0
	}
	in := prices[0] //买入价格
	var maxpro int
	for i := 1; i < len(prices); i++ {
		if prices[i] > in && prices[i]-in > maxpro {
			maxpro = prices[i] - in
		} else if prices[i] < in{
			in = prices[i]
		}
	}
	return maxpro
}

问题7–动态规划 连续子数组最大和

时间复杂度O(N)空间复杂度O(1)

func maxSubArray(nums []int) int {
	if len(nums)<1{
        return 0
    }
    max:=nums[0]
	var now int
	for i:=0;i<len(nums);i++{
		now = now + nums[i]
		if now>max{
			max = now
		}
        if now<0{
            now = 0
        }
	}
	return max
}

问题8–动态规划 礼物最大值

https://leetcode-cn.com/problems/li-wu-de-zui-da-jie-zhi-lcof/


func max(a, b int) int {
	if a > b {
		return a
	}
	return b
}

//连续最大子数组和
//dp[i][j]  到达i,j位置的礼物最大值
//dp[i][j] = max(dp[i-1][j] + grid[i][j],dp[i][j-1]+grid[i][j])
func maxValue(grid [][]int) int {
	if len(grid)<1{
		return 0
	}
	m:=len(grid)
	n:=len(grid[0])
	dp:=make([][]int,m)
	for i:=0;i<m;i++{
		dp[i] = make([]int,n)
	}
	dp[0][0] = grid[0][0]
	//初始化第一行
	for i:=1;i<n;i++{
		dp[0][i] = grid[0][i] + dp[0][i-1]
	}
	//初始化第一列
	for j:=1;j<m;j++{
		dp[j][0] = grid[j][0] + dp[j-1][0]
	}
	
	
	for i:=1;i<m;i++{
		for j:=1;j<n;j++{
			dp[i][j] = max(dp[i-1][j],dp[i][j-1]) + grid[i][j]
		}
	}
	return dp[m-1][n-1]
}

问题9 把数字翻译成字符串

https://leetcode-cn.com/problems/ba-shu-zi-fan-yi-cheng-zi-fu-chuan-lcof/

//翻译数字到英文
//dp[i] 表示到达数字i位最多有几种翻译
//如果 num的i-1到i 可以组成单词
//dp[i] = dp[i-1] + dp[i-2]
//否则
//dp[i] = dp[i-1]
func translateNum(num int) int {
	dp:=make([]int,0,0)
	dp = append(dp,1)
	str:=strconv.Itoa(num)
	if len(str)<=1{
		return dp[0]
	}
	sed,_:=strconv.Atoi(str[:2])
	if sed<26{
		dp = append(dp,2)
	}else{
		dp = append(dp,1)
	}
	for i:=2;i<len(str);i++{
		sed,_:=strconv.Atoi(str[i-1:i+1])
		if sed<26&&str[i-1]!='0'{
			dp = append(dp,dp[i-1]+dp[i-2])
		}else{
			dp = append(dp,dp[i-1])
		}
	}
	return dp[len(str)-1]
}

问题10 最长不重复子字符串

https://leetcode-cn.com/problems/zui-chang-bu-han-zhong-fu-zi-fu-de-zi-zi-fu-chuan-lcof/


//最长不重复子字符串
//dp[i]表示到i位置为止最大不重复
//lastindex为一个map[uint8]int,表示值在上一个的index+1(避免0值)
//需要构造等式
//若是原先没有遇见这个值lastmap[s[i]]==0 dp[i] = dp[i-1] + 1    lastmap[s[i]] = i+1
//否则说明这个值出现过 lastmap[s[i]]!=0
//从lastindex获取上一个出现的坐标index=lastmap[s[i]]-1,从dp[i-1]获取上一个值的最长子序列长度length
//如果i - length >index 说明中间有断开的,那么这个新出现的值就是新值 那么dp[i] = dp[i-1] + 1
//否则i-length<=index-1 说明中间没有断开的或者中间的中场子序列中已经包含了s[i],此时对i点最长 dp[i] = i-index
func lengthOfLongestSubstring(s string) int {
	if len(s)<1{
		return 0
	}
	var max int
	dp:=make([]int,len(s))
	lastindex:=make(map[uint8]int)
	dp[0] = 1
	lastindex[s[0]] = 1
	max = 1
	for i:=1;i<len(s);i++ {
		if lastindex[s[i]] == 0 {
			dp[i] = dp[i-1] + 1
			lastindex[s[i]] = i + 1
		}else{
			length:=dp[i-1]
			index:=lastindex[s[i]] - 1
			if i-length>index{
				dp[i] = dp[i-1] + 1
			}else{
				dp[i] = i - index
			}
			lastindex[s[i]] = i + 1
		}
		if max <dp[i]{
			max = dp[i]
		}
	}
	return max
}

递归

问题1–递归多岔树(回溯)

描述

https://leetcode-cn.com/problems/permutations/
给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。
示例 1:

输入:nums = [1,2,3]
输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]

思路

//给定的数组是不重复的要求返回所有可能的全排列
//此时脑子里第一反应是多岔树,以1,2,3,4为例子
//            1
//    2       3       4
// 4    3    4  2   2   3
// 3    4    2  4  3      2
//以此类推使用递归
//时间复杂度是O(N)*O(N!)
func innerpermute(surnums []int,nums []int,res *[][]int){
	if len(nums)==1{
		surnums = append(surnums,nums[0])
		*res = append(*res,surnums)
		return
	}else{
		for i:=0;i<len(nums);i++{
			tmp:=surnums
			arr:=make([]int,len(nums))
			copy(arr,nums)//这样做的目的是append操作再做合并的时候会修改元数据造成数据不一致
			tmp = append(tmp,arr[i])
			tmpnums := append(arr[0:i],arr[i+1:]...)
			innerpermute(tmp,tmpnums,res)
		}
	}
}

func permute(nums []int) [][]int {
	res:=make([][]int,0,0)
	innerpermute(nil,nums,&res)
	return res
}

问题2–递归

数字 n代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且
有效的 括号组合。

func innergenerate(str string, left, right int, resmap map[string]bool) {
	if left == 0 {
		for i := 0; i < right; i++ {
			str = str + ")"
		}
		resmap[str] = true
		return
	}
	if left > 0 {
		tmpleft := left - 1
		tmpstr := str + "("
		innergenerate(tmpstr, tmpleft, right, resmap)
	}
	if right > 0 && left < right {
		tmpright := right - 1
		tmpstr := str + ")"
		innergenerate(tmpstr, left, tmpright, resmap)
	}
}

//括号生成
func generateParenthesis(n int) []string {
	resmap := make(map[string]bool)
	var res []string
	innergenerate("", n, n, resmap)
	for k, _ := range resmap {
		res = append(res, k)
	}
	return res
}

双指针

问题1–最多k个数(双指针)

给定一个正整数数组 A,如果 A 的某个子数组中不同整数的个数恰好为 K,则称 A 的这个连续、不一定不同的子数组为好子数组。
(例如,[1,2,3,1,2] 中有 3 个不同的整数:1,2,以及 3。)
返回 A 中好子数组的数目。

https://leetcode-cn.com/problems/subarrays-with-k-different-integers/solution/cong-zui-jian-dan-de-wen-ti-yi-bu-bu-tuo-7f4v/

//如果问 A 中由最多 K 个不同整数组成的子数组的个数
func distinctMaxNum(nums []int, k int) int {
	n := len(nums)
	left := 0
	right := 0
	counter := make(map[int]int)
	distinct := 0
	res := 0
	for right < n {
		if counter[nums[right]] == 0 {
			distinct++
		}
		counter[nums[right]] = counter[nums[right]] + 1
		for distinct > k {
			counter[nums[left]] = counter[nums[left]] - 1
			if counter[nums[left]] == 0 {
				distinct = distinct - 1
			}
			left = left + 1
		}
		res += right - left + 1
		right = right + 1
	}
	return res
}

//同样的思想,使用双指针,原本双指针只求最大区间,现在的双指针要求满足条件的所有区间
//已经知道最多k个不同整数组成子数组个数的详细解释
//恰好由 K 个不同整数的子数组的个数 = 最多由 K 个不同整数的子数组的个数 - 最多由 K - 1 个不同整数的子数组的个数
func subarraysWithKDistinct(nums []int, k int) int {
	return distinctMaxNum(nums,k)-distinctMaxNum(nums,k-1)
}

问题2–寻找重复数(双指针)

https://leetcode-cn.com/problems/find-the-duplicate-number/submissions/

func findDuplicate(nums []int) int {
	n := len(nums)
	ans := 0
	bit_max := 31
	for ((n-1) >> bit_max) == 0 {
		bit_max--
	}
	for bit := 0; bit <= bit_max; bit++ {
		x, y := 0, 0
		for i := 0; i < n; i++ {
			if (nums[i] & (1 << bit)) > 0 {
				x++
			}
			if i >= 1 && (i & (1 << bit)) > 0 {
				y++
			}
		}
		if x > y {
			ans |= 1 << bit
		}
	}
	return ans
}

问题3–给定一个含有数字和运算符的字符串,为表达式添加括号,改变其运算优先级以求出不同的结果。你需要给出所有可能的组合的结果。有效的运算符号包含+, - 以及 * 。

func diffWaysToCompute(input string) []int {
	// 如果是数字,直接返回
	if isDigit(input) {
		tmp, _ := strconv.Atoi(input)
		return []int{tmp}
	}

	// 空切片
	var res []int
	// 遍历字符串
	for index, c := range input {
		tmpC := string(c)
		if tmpC == "+" || tmpC == "-" || tmpC == "*" {
			// 如果是运算符,则计算左右两边的算式
			left := diffWaysToCompute(input[:index])
			right := diffWaysToCompute(input[index+1:])

			for _, leftNum := range left {
				for _, rightNum := range right {
					var addNum int
					if tmpC == "+" {
						addNum = leftNum + rightNum
					} else if tmpC == "-" {
						addNum = leftNum - rightNum
					} else {
						addNum = leftNum * rightNum
					}
					res = append(res, addNum)
				}
			}
		}
	}

	return res
}

// 判断是否为全数字
func isDigit(input string) bool {
	_, err := strconv.Atoi(input)
	if err != nil {
		return false
	}
	return true
}

问题4–删除倒数第k个

/**
 * Definition for singly-linked list.
 * type ListNode struct {
 *     Val int
 *     Next *ListNode
 * }
 */
func getKthFromEnd(head *ListNode, k int) *ListNode {
    tmp:=head
    var res *ListNode
    z:=1
    for tmp!=nil{
        if z==k{
            res = head
            z++
        }else if z<k{
            z++
        } else{
            res = res.Next
        }
        tmp = tmp.Next
    }
    return res
}

问题5–调整数组奇数偶数

https://leetcode-cn.com/problems/diao-zheng-shu-zu-shun-xu-shi-qi-shu-wei-yu-ou-shu-qian-mian-lcof/


func exchange(nums []int) []int {
	ouindex:=-1
	for i:=0;i<len(nums);i++{
		if nums[i]%2==0&&ouindex==-1{
			//偶数,要获取第一个偶数出现的坐标
			ouindex = i
		}else if nums[i]%2!=0{
			if ouindex>-1{
				//第一个奇数出来以后偶数已经在前面了
				//将奇数与偶数呼唤
				tmp:=nums[ouindex]
				nums[ouindex] = nums[i]
				nums[i] = tmp
				ouindex = ouindex+1
			}
		}
	}
	return nums
}

数据结构

数据结构1 两个栈实现队列

https://leetcode-cn.com/problems/yong-liang-ge-zhan-shi-xian-dui-lie-lcof/submissions/

type CQueue struct {
	stack1, stack2 *list.List
}

func Constructor() CQueue {
	return CQueue{
		stack1: list.New(),
		stack2: list.New(),
	}
}

func (this *CQueue) AppendTail(value int)  {
	this.stack1.PushBack(value)
}

func (this *CQueue) DeleteHead() int {
	// 如果第二个栈为空
	if this.stack2.Len() == 0 {
		for this.stack1.Len() > 0 {
			this.stack2.PushBack(this.stack1.Remove(this.stack1.Back()))
		}
	}
	if this.stack2.Len() != 0 {
		e := this.stack2.Back()
		this.stack2.Remove(e)
		return e.Value.(int)
	}
	return -1
}


数据结构2 包含min函数的栈


type MinStack struct {
	arr []int
	minarr []int
}


/** initialize your data structure here. */
func Constructor() MinStack {
	return  MinStack{
	}

}


func (this *MinStack) Push(x int)  {
	this.arr = append(this.arr,x)
	if len(this.minarr) == 0{
		this.minarr = append(this.minarr,x)
	}else if x<=this.minarr[len(this.minarr)-1]{
		this.minarr = append(this.minarr,x)
	}
}


func (this *MinStack) Pop()  {
	pnum:=this.arr[len(this.arr)-1]
	this.arr = this.arr[:len(this.arr)-1]
	if this.minarr[len(this.minarr)-1]==pnum{
		this.minarr = this.minarr[:len(this.minarr)-1]
	}
}


func (this *MinStack) Top() int {
	return this.arr[len(this.arr)-1]
}


func (this *MinStack) Min() int {
	return this.minarr[len(this.minarr)-1]
}

数据结构3 链表

从头到尾打印
https://leetcode-cn.com/problems/cong-wei-dao-tou-da-yin-lian-biao-lcof/
反转链表
https://leetcode-cn.com/problems/fan-zhuan-lian-biao-lcof/
复杂链表复制
https://leetcode-cn.com/problems/fu-za-lian-biao-de-fu-zhi-lcof/

引申思考

1.回溯算法
2.go语言的append原理
3.分治算法

美团笔试

美团的技术博客我经常有看,所以对他们技术映像很好,今天刚好有一道比较有意思的题

小美是美团仓库的管理员,她会根据单据的要求按顺序取出仓库中的货物,每取出一件货物后会把剩余货物重新堆放,使得自己方便查找。已知货物入库的时候是按顺序堆放在一起的。如果小美取出其中一件货物,则会把货物所在的一堆物品以取出的货物为界分成两堆,这样可以保证货物局部的顺序不变。
已知货物最初是按 1~n 的顺序堆放的,每件货物的重量为 w[i] ,小美会根据单据依次不放回的取出货物。请问根据上述操作,小美每取出一件货物之后,重量和最大的一堆货物重量是多少?
https://leetcode-cn.com/problems/TJZLyC/

思路:使用逆转思维,把拿东西的次序倒过来看成是放东西,查看放入的左右是否有合并集,如果有,就合并得出值,记得顺序要倒叙,且最后一次拿出的值是0

func Warehouse(length int,arr []int,put []int){
	sumarr:=make([]int,length)
	var resarr []int
	resarr = append(resarr,0)
	for i:=length-1;i>=1;i--{
		index:=put[i]-1
		var res int
		if index>0&&sumarr[index-1]!=0{
			res = res + sumarr[index-1]
		}
		if index<length-1&&sumarr[index+1]!=0{
			res = res + sumarr[index+1]
		}
		res = res + arr[index]
		tmp:=index-1
		tmp2:=index+1
		for tmp>=0&&sumarr[tmp]!=0{
			sumarr[tmp] = res
			tmp--
		}
		for tmp2<length&&sumarr[tmp2]!=0{
			sumarr[tmp2] = res
			tmp2++
		}
		sumarr[index] = res
		resarr = append([]int{res},resarr...)
	}
	fmt.Println(resarr)
}

二分查找

二分查找1 在排序数组中查找target出现次数

https://leetcode-cn.com/problems/zai-pai-xu-shu-zu-zhong-cha-zhao-shu-zi-lcof/

//1 2 3 3 4 5 6
func searchLeft(nums []int,target int)int{
	start:=0
	end:=len(nums)-1
	for start<=end{
		mid:=(start+end)/2
		if nums[mid]<target{
			//左边界在右边
			start = mid + 1
		}else if nums[mid]>=target{
			//左边界在左边
			end = mid - 1
		}
	}
	return end
}

func searchRight(nums []int,target int)int{
	start:=0
	end:=len(nums)-1
	for start<=end{
		mid:=(start+end)/2
		if nums[mid]>target{
			//右边界在左边
			end = mid -1
		}else if nums[mid]<=target{
			//右边界在右边
			start = mid+1
		}
	}
	return start
}

//统计目标在排序数组中出现的次数
//已知排序数组查询,第一个想到二分,需要找到左边界和右边界
func search(nums []int, target int) int {
	return  searchRight(nums,target) - searchLeft(nums,target) -1
}

二分查找2 一个长度为n-1的递增排序数组中的所有数字都是唯一的,并且每个数字都在范围0~n-1之内。在范围0~n-1内的n个数字中有且只有一个数字不在该数组中,请找出这个数字。

//排序数组中的搜索问题,首先想到 二分法
//mid:=(start+end)/2
//当nums[i]==i说明 i以前全部都符合没有空缺 start = mid+1
//当nums[i]!=i说明左边有空缺,end = mid -1
func missingNumber(nums []int) int {
	start := 0
	end:=len(nums)-1
	for start<=end{
		mid:=(start+end)/2
		if nums[mid]==mid{
			start = mid+1
		}else{
			end = mid - 1
		}
	}
	return start
}

二分查找3 把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素。例如,数组 [3,4,5,1,2] 为 [1,2,3,4,5] 的一个旋转,该数组的最小值为1。

https://leetcode-cn.com/problems/xuan-zhuan-shu-zu-de-zui-xiao-shu-zi-lcof/

//首先数组是有序的,第一个想到二分查找,需要找到旋转的点的特征
//左边一定大于右边,直到左边小于右边
func minArray(numbers []int) int {
	start:=0
	end:=len(numbers)-1
	for start < end{
		mid := (start+end)/2
		if numbers[mid]>numbers[end]{
			// 4 0 1 1 1
			//说明临界点在右边
			start = mid + 1
		}else if numbers[mid]<numbers[end]{
			// 2 3 4 0 1
			//临界点在左边
			end = mid
		}else {
			end = end - 1
		}
	}
	return numbers[end]
}

数学规律

https://leetcode-cn.com/problems/er-wei-shu-zu-zhong-de-cha-zhao-lcof/
在一个 n * m 的二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个高效的函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。

func findNumberIn2DArray(matrix [][]int, target int) bool {
	var n,m int
	n = len(matrix)
	if n>=1{
		m = len(matrix[0])
	}
	var i,j int
	i = 0
	j = m-1
	for i<n&&j>=0{
		if matrix[i][j]<target{
			i++
		}else if matrix[i][j]>target{
			j--
		}else{
			return true
		}
	}
	return false
}

二叉树

从上到下打印二叉树

从上到下打印出二叉树的每个节点,同一层的节点按照从左到右的顺序打印。
https://leetcode-cn.com/problems/cong-shang-dao-xia-da-yin-er-cha-shu-lcof/

func levelOrder(root *TreeNode) []int {
	if root==nil{
		return nil
	}
	var arr []int
	treeNodes:=make([]*TreeNode,0,0)
	treeNodes = append(treeNodes,root)
	for len(treeNodes)>0{
		var tmpTreeNodes []*TreeNode
		for i:=0;i<len(treeNodes);i++{
			arr = append(arr,treeNodes[i].Val)
			if treeNodes[i].Left!=nil{
				tmpTreeNodes = append(tmpTreeNodes,treeNodes[i].Left)
			}
			if treeNodes[i].Right!=nil{
				tmpTreeNodes = append(tmpTreeNodes,treeNodes[i].Right)
			}
		}
		treeNodes = tmpTreeNodes
	}
	return arr
}

从上到下打印二叉树 2

从上到下按层打印二叉树,同一层的节点按从左到右的顺序打印,每一层打印到一行。
https://leetcode-cn.com/problems/cong-shang-dao-xia-da-yin-er-cha-shu-ii-lcof/


func levelOrderV2(root *TreeNode) [][]int {
	if root==nil{
		return nil
	}
	var arr [][]int
	treeNodes:=make([]*TreeNode,0,0)
	treeNodes = append(treeNodes,root)
	for len(treeNodes)>0{
		var tmpTreeNodes []*TreeNode
		var tmparr []int
		for i:=0;i<len(treeNodes);i++{
			tmparr = append(tmparr,treeNodes[i].Val)
			if treeNodes[i].Left!=nil{
				tmpTreeNodes = append(tmpTreeNodes,treeNodes[i].Left)
			}
			if treeNodes[i].Right!=nil{
				tmpTreeNodes = append(tmpTreeNodes,treeNodes[i].Right)
			}
			arr = append(arr,tmparr)
		}
		treeNodes = tmpTreeNodes
	}
	return arr
}

之字形打印二叉树 3

请实现一个函数按照之字形顺序打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右到左的顺序打印,第三行再按照从左到右的顺序打印,其他行以此类推。
https://leetcode-cn.com/problems/cong-shang-dao-xia-da-yin-er-cha-shu-iii-lcof/

func levelOrder(root *TreeNode) [][]int {
	if root==nil{
		return nil
	}
	var arr [][]int
	treeNodes:=make([]*TreeNode,0,0)
	treeNodes = append(treeNodes,root)
	level:=1
	for len(treeNodes)>0{
		var tmpTreeNodes []*TreeNode
		var tmparr []int
		for i:=0;i<len(treeNodes);i++{
			if level%2!=0{
				tmparr = append(tmparr,treeNodes[i].Val)
			}else{
				tmparr = append([]int{treeNodes[i].Val},tmparr...)
			}
			if treeNodes[i].Left!=nil{
				tmpTreeNodes = append(tmpTreeNodes,treeNodes[i].Left)
			}
			if treeNodes[i].Right!=nil{
				tmpTreeNodes = append(tmpTreeNodes,treeNodes[i].Right)
			}
		}
        level++
		arr = append(arr,tmparr)
		treeNodes = tmpTreeNodes
	}
	return arr
}

二叉树判断子数

输入两棵二叉树A和B,判断B是不是A的子结构。(约定空树不是任意一个树的子结构)
B是A的子结构, 即 A中有出现和B相同的结构和节点值。
https://leetcode-cn.com/problems/shu-de-zi-jie-gou-lcof/


func isInnerEqual(node *TreeNode,subnode *TreeNode)bool{
	if subnode==nil{
		return true
	}
	if node==nil{
		return false
	}
	if subnode.Val == node.Val{
		return isInnerEqual(node.Left,subnode.Left)&&isInnerEqual(node.Right,subnode.Right)
	}
	return false
}


func isSubStructure(A *TreeNode, B *TreeNode) bool {
	if A==nil||B==nil{
		return false
	}
	var issub bool
	treeNodes:=make([]*TreeNode,0,0)
	treeNodes = append(treeNodes,A)
	for len(treeNodes)>0&&issub==false{
		var tmpTreeNodes []*TreeNode
		for _,v:=range treeNodes{
			if v.Val==B.Val{
				issub = isInnerEqual(v.Left,B.Left)&&isInnerEqual(v.Right,B.Right)
				if issub{
					return issub
				}
			}
            if v.Left!=nil{
            tmpTreeNodes = append(tmpTreeNodes,v.Left)    
            }
			if v.Right!=nil{
t.        mpTreeNodes = append(tmpTreeNodes,v.Right)
            }
		}
		treeNodes = tmpTreeNodes
	}
	return issub
}

二叉树镜像

func exchangeNode(node *TreeNode){
	left:=node.Left
	right:=node.Right
	node.Right = left
	node.Left = right
}


func mirrorTree(root *TreeNode) *TreeNode {
	if root==nil{
		return nil
	}
	treeNodes:=make([]*TreeNode,0,0)
	treeNodes = append(treeNodes,root)
	for len(treeNodes)!=0{
		tmpTreeNodes:=make([]*TreeNode,0,0)
		for i:=0;i<len(treeNodes);i++{
			if treeNodes[i]==nil{
				continue
			}
			tmpTreeNodes = append(tmpTreeNodes,treeNodes[i].Left)
			tmpTreeNodes = append(tmpTreeNodes,treeNodes[i].Right)
			exchangeNode(treeNodes[i])
		}
		treeNodes = tmpTreeNodes
	}
	return root
}

对称二叉树

func isDuichengArr(arr []string)bool{
	if len(arr)%2!=0{
		return false
	}
	start:=0
	end:=len(arr)-1
	for start<end{
		if arr[start]!=arr[end]{
			return false
		}
		start++
		end--
	}
	return true
}

func levelOrderv3(root *TreeNode) [][]string {
	if root==nil{
		return nil
	}
	var arr [][]string
	treeNodes:=make([]*TreeNode,0,0)
	treeNodes = append(treeNodes,root)
	for len(treeNodes)>0{
		var tmpTreeNodes []*TreeNode
		var tmparr []string
		for i:=0;i<len(treeNodes);i++{
			if treeNodes[i]==nil{
				tmparr  = append(tmparr,"")
				continue
			}
			sval:=strconv.Itoa(treeNodes[i].Val)
			tmparr  = append(tmparr,sval)
			tmpTreeNodes = append(tmpTreeNodes,treeNodes[i].Left)
			tmpTreeNodes = append(tmpTreeNodes,treeNodes[i].Right)
		}
		arr = append(arr,tmparr)
		treeNodes = tmpTreeNodes
	}
	return arr
}

//遍历left
//遍历right
//两个数组相等则相等
func isSymmetric(root *TreeNode) bool {
	arr:=levelOrderv3(root)
	for i:=1;i<len(arr);i++{
		if !isDuichengArr(arr[i]){
			return false
		}
	}
	return true
}

你可能感兴趣的:(面试,动态规划)