参考:剑指offer书
https://github.com/DinghaoLI/Coding-Interviews-Golang
面试题11. 旋转数组的最小数字
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素。例如,数组 [3,4,5,1,2] 为 [1,2,3,4,5] 的一个旋转,该数组的最小值为1。
示例 1:
输入:[3,4,5,1,2]
输出:1
示例 2:
输入:[2,2,2,0,1]
输出:0
func minArray(numbers []int) int {
left := 0
right := len(numbers) - 1
middle := 0
for numbers[left] >= numbers[right] {
if right-left == 1 {
middle = right
break
}
middle = (left + right) / 2
if numbers[left] == numbers[middle] && numbers[right] == numbers[middle] {
return inOrder(numbers, left, right)
}
if numbers[middle] >= numbers[left] {
left = middle
} else if numbers[middle] <= numbers[right] {
right = middle
}
}
return numbers[middle]
}
func inOrder(numbers []int, left, right int) int {
result := numbers[left]
for i := left + 1; i < right; i++ {
if numbers[i] < result {
result = numbers[i]
}
}
return result
}
面试题10- II. 青蛙跳台阶问题
一只青蛙一次可以跳上1级台阶,也可以跳上2级台阶。求该青蛙跳上一个 n 级的台阶总共有多少种跳法。
答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。
示例 1:
输入:n = 2
输出:2
示例 2:
输入:n = 7
输出:21
func numWays(n int) int {
if n <= 0 {
return 1
}
if n == 1 || n == 2 {
return n
}
dp := make([]int, n+1)
dp[1] = 1
dp[2] = 2
for i := 3; i <= n; i++ {
dp[i] = (dp[i-1] + dp[i-2])%1000000007
}
return dp[n]
}
请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一格开始,每一步可以在矩阵中向左、右、上、下移动一格。如果一条路径经过了矩阵的某一格,那么该路径不能再次进入该格子。例如,在下面的3×4的矩阵中包含一条字符串“bfce”的路径(路径中的字母用加粗标出)。
[[“a”,“b”,“c”,“e”],
[“s”,“f”,“c”,“s”],
[“a”,“d”,“e”,“e”]]
但矩阵中不包含字符串“abfb”的路径,因为字符串的第一个字符b占据了矩阵中的第一行第二个格子之后,路径不能再次进入这个格子。
示例 1:
输入:board = [[“A”,“B”,“C”,“E”],[“S”,“F”,“C”,“S”],[“A”,“D”,“E”,“E”]], word = “ABCCED”
输出:true
func exist(board [][]byte, word string) bool {
wordByte := []byte(word)
for i := 0; i < len(board); i++ {
for j := 0; j < len(board[0]); j++ {
if dfs1(board, wordByte, i, j, 0) {
return true
}
}
}
return false
}
func dfs1(board [][]byte, word []byte, i, j, k int) bool {
if i < 0 || i > len(board)-1 || j < 0 || j > len(board[0])-1 || board[i][j] != word[k] {
return false
}
if k == len(word)-1 {
return true
}
tmp := board[i][j]
board[i][j] = '/'
res := dfs1(board, word, i+1, j, k+1) || dfs1(board, word, i-1, j, k+1) || dfs1(board, word, i, j-1, k+1) || dfs1(board, word, i, j+1, k+1)
board[i][j] = tmp
return res
}
地上有一个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。请问该机器人能够到达多少个格子?
示例 1:
输入:m = 2, n = 3, k = 1
输出:3
参考:https://leetcode-cn.com/problems/ji-qi-ren-de-yun-dong-fan-wei-lcof/solution/mian-shi-ti-13-ji-qi-ren-de-yun-dong-fan-wei-dfs-b/
分析:深度优先遍历 DFS
深度优先搜索: 可以理解为暴力法模拟机器人在矩阵中的所有路径。DFS 通过递归,先朝一个方向搜到底,再回溯至上个节点,沿另一个方向搜索,以此类推。
剪枝: 在搜索中,遇到数位和超出目标值、此元素已访问,则应立即返回,称之为 可行性剪枝 。
算法解析:
递归参数: 当前元素在矩阵中的行列索引 i 和 j ,两者的数位和 si, sj 。
终止条件: 当 ① 行列索引越界 或 ② 数位和超出目标值 k 或 ③ 当前元素已访问过 时,返回 00 ,代表不计入可达解。
递推工作:
标记当前单元格 :将索引 (i, j) 存入 Set visited 中,代表此单元格已被访问过。
搜索下一单元格: 计算当前元素的 下、右 两个方向元素的数位和,并开启下层递归 。
回溯返回值: 返回 1 + 右方搜索的可达解总数 + 下方搜索的可达解总数,代表从本单元格递归搜索的可达解总数。
func movingCount(m int, n int, k int) int {
visited := make([][]bool, m+1)
for i := range visited {
visited[i] = make([]bool, n+1)
}
return dfs2(0, 0, 0, 0, m, n, k, visited)
}
func dfs2(i, j, si, sj, m, n, k int, visited [][]bool) int {
if i >= m || j >= n || si+sj > k || visited[i][j] {
return 0
}
visited[i][j] = true
return 1 + dfs2(i+1, j, getNext(i, si), sj, m, n, k, visited) + dfs2(i, j+1, si, getNext(j, sj), m, n, k, visited)
}
func getNext(i, si int) int {
if (i+1)%10 == 0 {
si = si - 8
} else {
si = si + 1
}
return si
}
给你一个由 ‘1’(陆地)和 ‘0’(水)组成的的二维网格,请你计算网格中岛屿的数量。
岛屿总是被水包围,并且每座岛屿只能由水平方向或竖直方向上相邻的陆地连接形成。
此外,你可以假设该网格的四条边均被水包围。
示例 1:
输入:
11110
11010
11000
00000
输出: 1
//深度优先遍历
func numIslands(grid [][]byte) int {
count := 0
for i := 0; i < len(grid); i++ {
for j := 0; j < len(grid[0]); j++ {
if grid[i][j] == '1' {
dfs(grid, i, j)
count++
}
}
}
return count
}
func dfs(grid [][]byte, x, y int) {
//终止条件越过边界和超过岛屿时return,跟二叉树root==nil return一个道理
if x < 0 || y < 0 || x >=len(grid) || y >=len(grid[0]) || grid[x][y] == '0' {
return
}
grid[x][y] = '0' //把遍历到的岛屿变为海洋,防止重复遍历到
//上下左右
dfs(grid, x+1, y)
dfs(grid, x-1, y)
dfs(grid, x, y-1)
dfs(grid, x, y+1)
}
给你一根长度为 n 的绳子,请把绳子剪成整数长度的 m 段(m、n都是整数,n>1并且m>1),每段绳子的长度记为 k[0],k[1]…k[m-1] 。请问 k[0]k[1]…*k[m-1] 可能的最大乘积是多少?例如,当绳子的长度是8时,我们把它剪成长度分别为2、3、3的三段,此时得到的最大乘积是18。
示例 1:
输入: 2
输出: 1
解释: 2 = 1 + 1, 1 × 1 = 1
分析:
https://leetcode-cn.com/problems/jian-sheng-zi-lcof/solution/mian-shi-ti-14-i-jian-sheng-zi-tan-xin-si-xiang-by/
// 尽可能将绳子以长度 33 等分为多段时,乘积最大。
func cuttingRope(n int) int {
if n <= 3 {
return n - 1
}
a := n / 3 //n可以拆分成几个3
b := n % 3
if b == 0 { //n能被3整除
return int(math.Pow(3, float64(a)))
}
if b == 1 { //n除3余1,因为2*2=1*3,所以把最后一个3倍和余数1拆成2+2
return int(math.Pow(3, float64(a-1))) * 4
}
return int(math.Pow(3, float64(a))) * 2 //n除3余2
}
请实现一个函数,输入一个整数,输出该数二进制表示中 1 的个数。例如,把 9 表示成二进制是 1001,有 2 位是 1。因此,如果输入 9,则该函数输出 2。
示例 1:
输入:00000000000000000000000000001011
输出:3
解释:输入的二进制串 00000000000000000000000000001011 中,共有三位为 ‘1’。
分析:示例n&(n-1)每次都会消除最右边的1
func hammingWeight(num uint32) int {
n := 0
for num != 0 {
n++
num = num & (num - 1)
}
return n
}
实现函数double Power(double base, int exponent),求base的exponent次方。不得使用库函数,同时不需要考虑大数问题。
示例 1:
输入: 2.00000, 10
输出: 1024.00000
func myPow(x float64, n int) float64 {
res := 1.0
if n == 0 {
return res
}
if n < 0 {
x = 1 / x
n = -n
}
for n > 0 {
if n&1 == 1 {
res *= x
}
x *= x
n = n >> 1
}
return res
}
输入数字 n,按顺序打印出从 1 到最大的 n 位十进制数。比如输入 3,则打印出 1、2、3 一直到最大的 3 位数 999。
示例 1:
输入: n = 1
输出: [1,2,3,4,5,6,7,8,9]
请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。例如,字符串"+100"、“5e2”、"-123"、“3.1416”、“0123"都表示数值,但"12e”、“1a3.14”、“1.2.3”、“±5”、"-1E-16"及"12e+5.4"都不是。
func isNumber(s string) bool {
if s == "" {
return false
}
str := strings.TrimSpace(s)
num := false
dot := false
e := false
for i, v := range str {
if v >= '0' && v <= '9' {
num = true
} else if v == '.' { //点之前不能出现点和e
if dot == true || e == true {
return false
}
dot = true
} else if v == 'e' || v == 'E' { // e之前不能出现e或者必须出现数
if e == true || !num {
return false
}
e = true
num = false //重置numSeen,排除123e或者123e+的情况,确保e之后也出现数
} else if v == '-' || v == '+' { //在0位置或者在e的后面出现
if i != 0 && str[i-1] != 'e' && str[i-1] != 'E' {
return false
}
} else {
return false
}
}
return num
}
输入一个整型数组,数组里有正数也有负数。数组中的一个或连续多个整数组成一个子数组。求所有子数组的和的最大值。
要求时间复杂度为O(n).
示例1:
输入: nums = [-2,1,-3,4,-1,2,1,-5,4]
输出: 6
解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。
动态规划:
func maxSubArray1(nums []int) int {
if nums == nil || len(nums) == 0 {
return 0
}
max := nums[0]
dp := make([]int, len(nums))
dp[0] = nums[0]
for i := 1; i < len(nums); i++ {
dp[i] = getMax(nums[i], dp[i-1]+nums[i])
if max < dp[i] {
max = dp[i]
}
}
return max
}
func getMax(a, b int) int {
max := a
if b > a {
max = b
}
return max
}
我们把只包含因子 2、3 和 5 的数称作丑数(Ugly Number)。求按从小到大的顺序的第 n 个丑数。
示例:
输入: n = 10
输出: 12
解释: 1, 2, 3, 4, 5, 6, 8, 9, 10, 12 是前 10 个丑数
方法一: 该算法非常直观,代码也非常简洁,但最大的问题每个整数都需要计算。即使一个数字不是丑数,我们还是需要对它做求余数和 除法操作。因此该算法的时间效率不是很高
func nthUglyNumber(n int) int {
number := 0
found := 0
for found < n {
number++
if uglyNumber(number) {
found++
}
}
return number
}
func uglyNumber(num int) bool {
for num%2 == 0 {
num /= 2
}
for num%3 == 0 {
num /= 3
}
for num%5 == 0 {
num /= 5
}
return num == 1
}
方法二:已有的丑数是按顺序存放在数组中的。对乘以 2而言,肯定存在某一个丑数T2,排在它之前的每一个丑数乘以2得到的
结果都会小于已有最大的丑数,在它之后的每一个丑数乘以2得到的结果都会太大。我们只需记下这个丑数的位置,同时每次生成新的丑数的 时候,去更新这个T2。对乘以3和5而言,也存在着同样的T3和T5
以空间换时间
func nthUglyNumber1(n int) int {
u2, u3, u5, nextUglyIndex := 0, 0, 0, 1
uglyArray := make([]int, n)
uglyArray[0] = 1
for nextUglyIndex < n {
uglyArray[nextUglyIndex] = min3(uglyArray[u2]*2, uglyArray[u3]*3, uglyArray[u5]*5)
for uglyArray[u2]*2 <= uglyArray[nextUglyIndex] {
u2++
}
for uglyArray[u3]*3 <= uglyArray[nextUglyIndex] {
u3++
}
for uglyArray[u5]*5 <= uglyArray[nextUglyIndex] {
u5++
}
nextUglyIndex++
}
return uglyArray[n-1]
}
func min3(a, b, c int) int {
min := a
if b < min {
min = b
}
if c < min {
min = c
}
return min
}
在字符串 s 中找出第一个只出现一次的字符。如果没有,返回一个单空格。
示例:
s = “abaccdeff”
返回 “b”
s = “”
返回 " "
func firstUniqChar(s string) byte {
var list [26]int
for i := 0; i < len(s); i++ {
list[s[i]-'a']++
}
for j := 0; j < len(s); j++ {
if list[s[j]-'a'] == 1 {
return s[j]
}
}
return ' '
}
在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数。
示例 1:
输入: [7,5,6,4]
输出: 5
分析:
图中省略了最后一步,即复制第二个子数组最后剩余的4到辅助数组中。(a)P1指 向的数字大于P2指向的数字,表明数组中存在逆序对。P2指向的数字是第二个子数组的第二个 数字,因此第二个子数组中有两个数字比7小。把逆序对数目加2,并把7复制到辅助数组,向前 移动P1和P3。(b)P1指向的数字小于P2指向的数字,没有逆序对。把P2指向的数字复制到辅 助数组,并向前移动P2和P3。(c)P1指向的数字大于P2指向的数字,因此存在逆序对。由于 P2指向的数字是第二个子数组的第一个数字,子数组中只有一个数字比5小。把逆序对数目加 1,并把5复制到辅助数组,向前移动P1和P3。 接下来我们统计两个长度为2的子数组之间的逆序对。我们在图5.2 中细分图5.1(d)的合并子数组及统计逆序对的过程。
我们先用两个指针分别指向两个子数组的末尾,并每次比较两个指 针指向的数字。如果第一个子数组中的数字大于第二个子数组中的数 字,则构成逆序对,并且逆序对的数目等于第二个子数组中剩余数字的 个数(如图5.2(a)和图5.2(c)所示)。如果第一个数组中的数字小 于或等于第二个数组中的数字,则不构成逆序对(如图5.2(b)所 示)。每一次比较的时候,我们都把较大的数字从后往前复制到一个辅 助数组中去,确保辅助数组中的数字是递增排序的。在把较大的数字复 制到辅助数组之后,把对应的指针向前移动一位,接下来进行下一轮比 较。
参考:很像归并排序https://emacsist.github.io/2016/11/22/golang-%E5%BD%92%E5%B9%B6%E6%8E%92%E5%BA%8Fmergesort/
实现:
func reversePairs(nums []int) int {
if len(nums) <= 0 || nums == nil {
return 0
}
copy := make([]int, len(nums))
for k, v := range nums {
copy[k] = v
}
return reversePairCore(nums, copy, 0, len(nums)-1)
}
func reversePairCore(data, copy []int, start, end int) int {
if start == end {
copy[start] = data[start]
return 0
}
halfLen := (end - start) / 2
left := reversePairCore(copy, data, start, start+halfLen) //分成两半,前半部分
right := reversePairCore(copy, data, start+halfLen+1, end) //后半部分
i := start + halfLen //模拟前半部分的最后一个
j := end //模拟后半部分的最后一个
copyIndex := end //写入辅助数组时大数写在后,确保辅助数组递增
count := 0
for i >= start && j >= start+halfLen+1 {
if data[i] > data[j] { //如果第一个子数组中的数字大于第二个子数组中的数字,则构成逆序对,并且逆序对的数目等于第二个子数组中剩余数字的个数
copy[copyIndex] = data[i]
i--
copyIndex--
count += j - start - halfLen //j-(start+halfLen+1)+1 j是后半部分的尾,start+halfLen+1是后半部分的头,尾-头+1
} else { //就没有逆序数了
copy[copyIndex] = data[j]
j--
copyIndex--
}
}
for ; i >= start; i-- {
copy[copyIndex] = data[i]
copyIndex--
}
for ; j >= start+halfLen+1; j-- {
copy[copyIndex] = data[j]
copyIndex--
}
return left + right + count
}
输入两个链表,找出它们的第一个公共节点。
如下面的两个链表:
在节点 c1 开始相交。
分析:我们可以先遍历一次得到它们的长度 分别为5和4,也就是较长的链表与较短的链表相比多一个结点。第二次 先在长的链表上走1步,到达结点2。接下来分别从结点2和结点4出发同 时遍历两个结点,直到找到它们第一个相同的结点6,这就是我们想要 的结果。
func getIntersectionNode(headA, headB *ListNode18) *ListNode18 {
lenA := getLength(headA)
lenB := getLength(headB)
lenDiff := lenA - lenB
listLong := headA
listShort := headB
if lenA < lenB {
listLong = headB
listShort = headA
lenDiff = lenB - lenA
}
for i := 0; i < lenDiff; i++ {
listLong = listLong.next
}
//长度一样后同时前进,O(m+n)
for listLong != nil && listShort != nil && listLong != listShort {
listLong = listLong.next
listShort = listShort.next
}
return listLong
}
func getLength(head *ListNode18) int {
length := 0
for head != nil {
length++
head = head.next
}
return length
}
统计一个数字在排序数组中出现的次数。
示例 1:
输入: nums = [5,7,7,8,8,10], target = 8
输出: 2
分析:我们思考如何更好地利用二分查找算法。假设我们是统计数 字k在排序数组中出现的次数.用二分查找算法直接找到第一个k及最后一个k,我们先分析如何用二分查找算法在数组中找到第一个k
。二分查找 算法总是先拿数组中间的数字和k作比较。如果中间的数字比k大,那么 k只有可能出现在数组的前半段,下一轮我们只在数组的前半段查找就 可以了。如果中间的数字比k小,那么k只有可能出现在数组的后半段, 下一轮我们只在数组的后半段查找就可以了。如果中间的数字和k相等 呢?我们先判断这个数字是不是第一个k。如果位于中间数字的前面一 个数字不是k,此时中间的数字刚好就是第一个k。如果中间数字的前面 一个数字也是k,也就是说第一个k肯定在数组的前半段,下一轮我们仍 然需要在数组的前半段查找。
我们可以用同样的思路在排序数组中找到最后一个k
。如果中间数 字比k大,那么k只能出现在数组的前半段。如果中间数字比k小,k就只 能出现在数组的后半段。如果中间数字等于k呢?我们需要判断这个k是 不是最后一个k,也就是中间数字的下一个数字是不是也等于k。如果下 一个数字不是k,则中间数字就是最后一个k了;否则下一轮我们还是要 在数组的后半段中去查找。
length := len(nums)
number := 0
if length == 0 {
return 0
}
first := getFirstIndex(nums, length, target, 0, length-1)
last := getLastIndex(nums, length, target, 0, length-1)
if first > -1 && last > -1 {
number = last - first + 1
}
return number
}
//找target的第一个元素
func getFirstIndex(nums []int, length, target, start, end int) int {
if start > end {
return -1
}
mid := (start + end) / 2
if nums[mid] == target {
if (mid > 0 && nums[mid-1] != target) || mid == 0 { //如果前一个值不是target,说明mid就是第一个target
return mid
} else { //如果前一个值也是target,也就是说第一个k肯定在数组的前半段,下一轮仍然需要在数组的前半段查找。
end = mid - 1
}
} else if nums[mid] < target { //中间值比target小了,target在后半部分
start = mid + 1
} else {
end = mid - 1
}
return getFirstIndex(nums, length, target, start, end)
}
//找target的最后一个元素
func getLastIndex(nums []int, length, target, start, end int) int {
if start > end {
return -1
}
mid := (start + end) / 2
if nums[mid] == target {
if (mid < length-1 && nums[mid+1] != target) || mid == length-1 { //如果后一个值不是target,说明mid就是最后一个target
return mid
} else { //如果后一个值也是target,也就是说最后一个k肯定在数组的后半段,下一轮仍然需要在数组的后半段查找。
start = mid + 1
}
} else if nums[mid] < target { //中间值比target小了,target在后半部分
start = mid + 1
} else {
end = mid - 1
}
return getLastIndex(nums, length, target, start, end)
}
一个长度为n-1的递增排序数组中的所有数字都是唯一的,并且每个数字都在范围0~n-1之内。在范围0~n-1内的n个数字中有且只有一个数字不在该数组中,请找出这个数字。
示例 1:
输入: [0,1,3]
输出: 2
示例 2:
输入: [0,1,2,3,4,5,6,7,9]
输出: 8
分析:有序数组也可以用二分法
func missingNumber(nums []int) int {
if len(nums) == 0 {
return 0
}
result, v := 0, nums[0]
for result, v = range nums {
if result != v {
return result
}
}
return result + 1
}
一个整型数组 nums 里除两个数字之外,其他数字都出现了两次。请写程序找出这两个只出现一次的数字。要求时间复杂度是O(n),空间复杂度是O(1)。
示例 1:
输入:nums = [4,1,4,6]
输出:[1,6] 或 [6,1]
示例 2:
输入:nums = [1,2,10,4,1,4,3,3]
输出:[2,10] 或 [10,2]
分析:
一个整型数组里除了两个数字之外,其他的数字都出现了两次
此题考察的是异或运算的特点:即两个相同的数异或结果为0。
此题用了两次异或运算特点:
第一次使用异或运算,得到了两个只出现一次的数相异或的结果。
因为两个只出现一次的数肯定不同,即他们的异或结果一定不为0,一定有一个位上有1。
另外一个此位上没有1,我们可以根据此位上是否有1,将整个数组重新划分成两部分,
一部分此位上一定有1,另一部分此位上一定没有1,
然后分别对每部分求异或,因为划分后的两部分有这样的特点:
其他数都出现两次,只有一个数只出现一次。
因此,我们又可以运用异或运算,分别得到两部分只出现一次的数。
func singleNumbers(nums []int) []int {
xor := 0
for _,v := range nums {
xor ^= v
}
// 取xor最低位为1的数
lowest := xor & -xor
a,b := 0,0
for _, v := range nums {
if lowest&v == 0 {
a ^= v
} else {
b ^= v
}
}
return []int{a,b}
}
在一个数组 nums 中除一个数字只出现一次之外,其他数字都出现了三次。请找出那个只出现一次的数字。
示例 1:
输入:nums = [3,4,3,3]
输出:4
示例 2:
输入:nums = [9,1,7,9,7,9,7]
输出:1
分析:一个整型数组里除了一个数字之外,其他的数字都出现了三次如果是出现两次的话,用一个bit就可以比如ones,初始为0
当 5 第 1 次出现, ones = 5
当 5 第 2 次出现, ones 清空为 0,表示 ones 可以去处理其他数字了
所以,最后 如果 ones != 0的话, ones 记录的就是只出现了一次的那个数字
公式是 ones = ones xor i
那么,如果是三次的话,香农定理,需要用 2 bits 进行记录
当5第1次出现的时候,ones = 5, twos = 0, ones 记录这个数字
当5第2次出现的时候,ones = 0, twos = 5, twos 记录了这个数字
当 5 第 3 次出现的时候,ones = 0, twos = 0, 都清空了,可以去处理其他数字了
所以,如果有某个数字出现了 1 次,就存在 ones 中,出现了 2 次,就存在 twos 中
公式方面, 上面 2 次的时候,ones 清空的公式是 ones = ones xor i
而第 3 次时, ones 要等于零, 而这时 twos 是 True , 所以再 & 一个 twos 的非就可以, ones = (ones xor i) & ~twos
所以,总的公式是
ones = (ones xor i) & ~twos
twos = (twos xor i) & ~ones
func singleNumber(nums []int) int {
ones, twos := 0, 0
for i := 0; i < len(nums); i++ {
ones = (ones ^ nums[i]) & ^twos
twos = (twos ^ nums[i]) & ^ones
}
return ones
}
输入一个递增排序的数组和一个数字s,在数组中查找两个数,使得它们的和正好是s。如果有多对数字的和等于s,则输出任意一对即可。
示例 1:
输入:nums = [2,7,11,15], target = 9
输出:[2,7] 或者 [7,2]
示例 2:
输入:nums = [10,26,30,31,47,60], target = 40
输出:[10,30] 或者 [30,10]
func twoSum(nums []int, target int) []int {
if len(nums) == 0 {
return []int{}
}
left, right := 0, len(nums)-1
for left < right {
if nums[left]+nums[right] > target {
right--
} else if nums[left]+nums[right] < target {
left++
} else {
return []int{nums[left], nums[right]}
}
}
return []int{}
}
输入一个正整数 target ,输出所有和为 target 的连续正整数序列(至少含有两个数)。
序列内的数字由小到大排列,不同序列按照首个数字从小到大排列。
示例 1:
输入:target = 9
输出:[[2,3,4],[4,5]]
示例 2:
输入:target = 15
输出:[[1,2,3,4,5],[4,5,6],[7,8]]
分析滑动窗口
https://leetcode-cn.com/problems/he-wei-sde-lian-xu-zheng-shu-xu-lie-lcof/solution/shi-yao-shi-hua-dong-chuang-kou-yi-ji-ru-he-yong-h/
func findContinuousSequence(target int) [][]int {
low := 1
high := 2
result := [][]int{}
for low < high {
temp := []int{}
currSum := (low + high) * (high - low + 1) / 2 //当前和等于(low+high) 1+100,2+99
if currSum == target {
for i := low; i <= high; i++ {
temp = append(temp, i)
}
result = append(result, temp)
low++ //这里要前进
} else if currSum < target {
high++
} else {
low++
}
}
return result
}
难度
简单
45
给定一个数组 nums 和滑动窗口的大小 k,请找出所有滑动窗口里的最大值。
示例:
输入: nums = [1,3,-1,-3,5,3,6,7], 和 k = 3
输出: [3,3,5,5,6,7]
解释:
滑动窗口的位置 最大值
[1 3 -1] -3 5 3 6 7 3
1 [3 -1 -3] 5 3 6 7 3
1 3 [-1 -3 5] 3 6 7 5
1 3 -1 [-3 5 3] 6 7 5
1 3 -1 -3 [5 3 6] 7 6
1 3 -1 -3 5 [3 6 7] 7