程序员面试金典

面试题01.01.判定字符是否唯一(5)

  • 题目
实现一个算法,确定一个字符串 s 的所有字符是否全都不同。
示例 1:输入: s = "leetcode" 输出: false 
示例 2:输入: s = "abc" 输出: true
限制:
    0 <= len(s) <= 100
    如果你不使用额外的数据结构,会很加分。
  • 解题思路
No. 思路 时间复杂度 空间复杂度
01 哈希辅助 O(n) O(1)
02 位运算 O(n) O(1)
03 遍历 O(n^2) O(1)
04 排序遍历 O(nlog(n)) O(n)
05 数组辅助 O(n) O(1)
func isUnique(astr string) bool {
    m := make(map[byte]bool)
    for i := 0; i < len(astr); i++ {
        if m[astr[i]] == true {
            return false
        }
        m[astr[i]] = true
    }
    return true
}

# 2
func isUnique(astr string) bool {
    value := uint32(0)
    for i := 0; i < len(astr); i++ {
        index := astr[i] - 'a'
        if value&(1<<index) == (1 << index) {
            return false
        }
        value = value ^ (1 << index)
    }
    return true
}

# 3
func isUnique(astr string) bool {
    for i := 0; i < len(astr); i++ {
        for j := i + 1; j < len(astr); j++ {
            if astr[i] == astr[j] {
                return false
            }
        }
    }
    return true
}

# 4
func isUnique(astr string) bool {
    arr := []byte(astr)
    sort.Slice(arr, func(i, j int) bool {
        return arr[i] < arr[j]
    })
    for i := 1; i < len(arr); i++ {
        if arr[i] == arr[i-1] {
            return false
        }
    }
    return true
}

# 5
func isUnique(astr string) bool {
    arr := make([]int, 256)
    for i := 0; i < len(astr); i++ {
        if arr[astr[i]] > 0 {
            return false
        }
        arr[astr[i]] = 1
    }
    return true
}

面试题01.02.判定是否互为字符重排(2)

  • 题目
给定两个字符串 s1 和 s2,请编写一个程序,确定其中一个字符串的字符重新排列后,能否变成另一个字符串。
示例 1:输入: s1 = "abc", s2 = "bca" 输出: true 
示例 2:输入: s1 = "abc", s2 = "bad" 输出: false
说明:
    0 <= len(s1) <= 100
    0 <= len(s2) <= 100
  • 解题思路
No. 思路 时间复杂度 空间复杂度
01 内置函数 O(nlog(n)) O(n)
02 哈希辅助 O(n) O(1)
03 数组辅助 O(n) O(1)
func CheckPermutation(s1 string, s2 string) bool {
    arr1 := strings.Split(s1, "")
    arr2 := strings.Split(s2, "")
    sort.Strings(arr1)
    sort.Strings(arr2)
    return strings.Join(arr1,"") == strings.Join(arr2,"")
    // return reflect.DeepEqual(arr1, arr2)
}

#
func CheckPermutation(s1 string, s2 string) bool {
    if len(s1) != len(s2) {
        return false
    }
    m := make(map[byte]int)
    for i := 0; i < len(s1); i++ {
        m[s1[i]]++
        m[s2[i]]--
    }
    for _, v := range m {
        if v != 0 {
            return false
        }
    }
    return true
}

#
func CheckPermutation(s1 string, s2 string) bool {
    if len(s1) != len(s2) {
        return false
    }
    arr := [256]int{}
    for i := 0; i < len(s1); i++ {
        arr[s1[i]]++
        arr[s2[i]]--
    }
    for _, v := range arr {
        if v != 0 {
            return false
        }
    }
    return true
}

面试题01.03.URL化(2)

  • 题目
URL化。编写一种方法,将字符串中的空格全部替换为%20。假定该字符串尾部有足够的空间存放新增字符,
并且知道字符串的“真实”长度。(注:用Java实现的话,请使用字符数组实现,以便直接在数组上操作。)
示例1:输入:"Mr John Smith    ", 13 输出:"Mr%20John%20Smith"
示例2:输入:"               ", 5 输出:"%20%20%20%20%20"
提示:
    字符串长度在[0, 500000]范围内。
  • 解题思路
No. 思路 时间复杂度 空间复杂度
01 内置函数 O(n) O(n)
02 遍历 O(n) O(n)
func replaceSpaces(S string, length int) string {
    return strings.ReplaceAll(S[:length], " ","%20")
}

#
func replaceSpaces(S string, length int) string {
    res := make([]byte,0)
    for i := 0; i < length; i++ {
        if S[i] == ' ' {
            res = append(res,'%')
            res = append(res,'2')
            res = append(res,'0')
        } else {
            res = append(res,S[i])
        }
    }
    return string(res)
}

面试题01.04.回文排列(2)

  • 题目
给定一个字符串,编写一个函数判定其是否为某个回文串的排列之一。
回文串是指正反两个方向都一样的单词或短语。排列是指字母的重新排列。
回文串不一定是字典当中的单词。
示例1:输入:"tactcoa" 输出:true(排列有"tacocat"、"atcocta",等等)
  • 解题思路
No. 思路 时间复杂度 空间复杂度
01 哈希辅助 O(n) O(1)
02 数组辅助 O(n) O(1)
func canPermutePalindrome(s string) bool {
    m := make(map[byte]int)
    for i := 0; i < len(s); i++ {
        m[s[i]]++
        if m[s[i]] == 2 {
            delete(m, s[i])
        }
    }
    return len(m) <= 1
}

#
func canPermutePalindrome(s string) bool {
    arr := [256]int{}
    for i := 0; i < len(s); i++ {
        arr[s[i]]++
    }
    count := 0
    for i := 0; i < len(arr); i++{
        if arr[i] % 2== 1{
            count++
        }
    }
    return count <= 1
}

面试题01.05.一次编辑(2)

  • 题目
字符串有三种编辑操作:插入一个字符、删除一个字符或者替换一个字符。
给定两个字符串,编写一个函数判定它们是否只需要一次(或者零次)编辑。
示例 1:输入: first = "pale"second = "ple" 输出: True
示例 2:输入: first = "pales"second = "pal" 输出: False
  • 解题思路
No. 思路 时间复杂度 空间复杂度
01 遍历 O(n) O(1)
02 遍历 O(n) O(1)
func oneEditAway(first string, second string) bool {
    if len(first)-len(second) > 1 || len(second)-len(first) > 1 {
        return false
    }
    if first == second {
        return true
    }
    i := 0
    for ; i < len(first) && i < len(second); i++ {
        if first[i] != second[i] {
            if len(first) == len(second) {
                if first[i+1:] == second[i+1:] {
                    return true
                }
            } else if len(first) < len(second) {
                if first[i:] == second[i+1:] {
                    return true
                }
            } else {
                if first[i+1:] == second[i:] {
                    return true
                }
            }
            break
        }
    }
    if i == len(first) || i == len(second) {
        return true
    }
    return false
}

#
func oneEditAway(first string, second string) bool {
    if len(first)-len(second) > 1 || len(second)-len(first) > 1 {
        return false
    }
    if first == second {
        return true
    }
    if len(first) > len(second) {
        first, second = second, first
    }
    for i := 0; i < len(first); i++ {
        if first[i] == second[i] {
            continue
        }
        return first[i:] == second[i+1:] || first[i+1:] == second[i+1:]
    }
    return true
}

面试题01.06.字符串压缩(2)

  • 题目
字符串压缩。利用字符重复出现的次数,编写一种方法,实现基本的字符串压缩功能。
比如,字符串aabcccccaaa会变为a2b1c5a3。若“压缩”后的字符串没有变短,则返回原先的字符串。
你可以假设字符串中只包含大小写英文字母(a至z)。
示例1:输入:"aabcccccaaa" 输出:"a2b1c5a3"
示例2:输入:"abbccd" 输出:"abbccd" 
解释:"abbccd"压缩后为"a1b2c2d1",比原字符串长度更长。
提示:字符串长度在[0, 50000]范围内。
  • 解题思路
No. 思路 时间复杂度 空间复杂度
01 遍历 O(n) O(n)
02 双指针 O(n) O(n)
func compressString(S string) string {
    if len(S) <= 1 {
        return S
    }
    prev := S[0]
    count := 1
    res := ""
    for i := 1; i < len(S); i++ {
        if prev == S[i] {
            count++
        } else {
            res = res + string(prev) + strconv.Itoa(count)
            prev = S[i]
            count = 1
        }
    }
    res = res + string(prev) + strconv.Itoa(count)
    if len(res) >= len(S) {
        return S
    }
    return res
}

#
func compressString(S string) string {
    if len(S) <= 1 {
        return S
    }
    i := 0
    j := 0
    res := ""
    for j = 1; j < len(S); j++ {
        if S[i] != S[j] {
            res = res + string(S[i]) + strconv.Itoa(j-i)
            i = j
        }
    }
    res = res + string(S[i]) + strconv.Itoa(j-i)
    if len(res) >= len(S) {
        return S
    }
    return res
}

面试题01.07.旋转矩阵(3)

  • 题目
给你一幅由 N × N 矩阵表示的图像,其中每个像素的大小为 4 字节。请你设计一种算法,将图像旋转 90 度。
不占用额外内存空间能否做到?
示例 1:给定 matrix = 
[
  [1,2,3],
  [4,5,6],
  [7,8,9]
],
原地旋转输入矩阵,使其变为:
[
  [7,4,1],
  [8,5,2],
  [9,6,3]
]
示例 2:给定 matrix =
[
  [ 5, 1, 9,11],
  [ 2, 4, 8,10],
  [13, 3, 6, 7],
  [15,14,12,16]
], 
原地旋转输入矩阵,使其变为:
[
  [15,13, 2, 5],
  [14, 3, 4, 1],
  [12, 6, 8, 9],
  [16, 7,10,11]
]
  • 解题思路
No. 思路 时间复杂度 空间复杂度
01 遍历 O(n^2) O(1)
02 遍历 O(n^2) O(1)
03 数组辅助 O(n^2) O(n^2)
func rotate(matrix [][]int) {
    n := len(matrix)
    // 同行逆置
    // [[1 2 3] [4 5 6] [7 8 9]]
    // [[3 2 1] [6 5 4] [9 8 7]]
    for i := 0; i < n; i++ {
        for j := 0; j < n/2; j++ {
            matrix[i][j], matrix[i][n-1-j] = matrix[i][n-1-j], matrix[i][j]
        }
    }
    // 左下右上对角线对互换
    // [[3 2 1] [6 5 4] [9 8 7]]
    // [[7 4 1] [8 5 2] [9 6 3]]
    for i := 0; i < n-1; i++ {
        for j := 0; j < n-1-i; j++ {
            matrix[i][j], matrix[n-1-j][n-1-i] = matrix[n-1-j][n-1-i], matrix[i][j]
        }
    }
}

# 2
func rotate(matrix [][]int) {
    n := len(matrix)
    for start, end := 0, n-1; start < end; {
        for s, e := start, end; s < end; {
            matrix[start][s], matrix[e][start], matrix[end][e], matrix[s][end] =
                matrix[e][start], matrix[end][e], matrix[s][end], matrix[start][s]
            s++
            e--
        }
        start++
        end--
    }
}

# 3
func rotate(matrix [][]int) {
    n := len(matrix)
    arr := make([][]int, n)
    for i := 0; i < n; i++ {
        arr[i] = make([]int, n)
    }
    for i := 0; i < n; i++ {
        for j := 0; j < n; j++ {
            arr[j][n-1-i] = matrix[i][j]
        }
    }
    copy(matrix, arr)
}

面试题01.08.零矩阵(4)

  • 题目
编写一种算法,若M × N矩阵中某个元素为0,则将其所在的行与列清零。
示例 1:输入:
[
  [1,1,1],
  [1,0,1],
  [1,1,1]
]
输出:
[
  [1,0,1],
  [0,0,0],
  [1,0,1]
]
示例 2:输入:
[
  [0,1,2,0],
  [3,4,5,2],
  [1,3,1,5]
]
输出:
[
  [0,0,0,0],
  [0,4,5,0],
  [0,3,1,0]
]
  • 解题思路
No. 思路 时间复杂度 空间复杂度
01 哈希辅助 O(n^2) O(n)
02 暴力法 O(n^4) O(1)
03 遍历 O(n^2) O(1)
04 遍历 O(n^2) O(1)
func setZeroes(matrix [][]int) {
    x := make(map[int]int)
    y := make(map[int]int)
    for i := 0; i < len(matrix); i++ {
        for j := 0; j < len(matrix[i]); j++ {
            if matrix[i][j] == 0 {
                x[i] = 1
                y[j] = 1
            }
        }
    }
    for i := 0; i < len(matrix); i++ {
        for j := 0; j < len(matrix[i]); j++ {
            if x[i] == 1 || y[j] == 1 {
                matrix[i][j] = 0
            }
        }
    }
}

# 2
func setZeroes(matrix [][]int) {
    m := make(map[[2]int]bool)
    for i := 0; i < len(matrix); i++ {
        for j := 0; j < len(matrix[i]); j++ {
            if matrix[i][j] == math.MinInt32 {
                m[[2]int{i, j}] = true
            }
        }
    }
    for i := 0; i < len(matrix); i++ {
        for j := 0; j < len(matrix[i]); j++ {
            if matrix[i][j] == 0 {
                for k := 0; k < len(matrix); k++ {
                    for l := 0; l < len(matrix[k]); l++ {
                        if (k == i || l == j) && matrix[k][l] != 0 {
                            delete(m, [2]int{k, l})
                            matrix[k][l] = math.MinInt32
                        }
                    }
                }
            }
        }
    }
    for i := 0; i < len(matrix); i++ {
        for j := 0; j < len(matrix[i]); j++ {
            if matrix[i][j] == math.MinInt32 && m[[2]int{i, j}] == false {
                matrix[i][j] = 0
            }
        }
    }
}

# 3
func setZeroes(matrix [][]int) {
    flag := false
    for i := 0; i < len(matrix); i++ {
        if matrix[i][0] == 0 {
            flag = true
        }
        for j := 1; j < len(matrix[i]); j++ {
            if matrix[i][j] == 0 {
                matrix[i][0] = 0
                matrix[0][j] = 0
            }
        }
    }
    for i := 1; i < len(matrix); i++ {
        for j := 1; j < len(matrix[i]); j++ {
            if matrix[i][0] == 0 || matrix[0][j] == 0 {
                matrix[i][j] = 0
            }
        }
    }
    // 第一行处理
    if matrix[0][0] == 0 {
        for j := 0; j < len(matrix[0]); j++ {
            matrix[0][j] = 0
        }
    }
    // 第一列处理
    if flag == true {
        for i := 0; i < len(matrix); i++ {
            matrix[i][0] = 0
        }
    }
}

# 4
func setZeroes(matrix [][]int) {
    flag := false
    for i := 0; i < len(matrix); i++ {
        if matrix[i][0] == 0 {
            flag = true
        }
        for j := 1; j < len(matrix[i]); j++ {
            if matrix[i][j] == 0 {
                matrix[i][0] = 0
                matrix[0][j] = 0
            }
        }
    }
    for i := len(matrix) - 1; i >= 0; i-- {
        for j := len(matrix[i]) - 1; j >= 1; j-- {
            if matrix[i][0] == 0 || matrix[0][j] == 0 {
                matrix[i][j] = 0
            }
        }
    }
    // 第一列处理
    if flag == true {
        for i := 0; i < len(matrix); i++ {
            matrix[i][0] = 0
        }
    }
}

面试题01.09.字符串轮转(2)

  • 题目
字符串轮转。给定两个字符串s1和s2,请编写代码检查s2是否为s1旋转而成(
比如,waterbottle是erbottlewat旋转后的字符串)。
示例1: 输入:s1 = "waterbottle", s2 = "erbottlewat" 输出:True
示例2:输入:s1 = "aa", s2 = "aba" 输出:False
提示:字符串长度在[0, 100000]范围内。
说明: 你能只调用一次检查子串的方法吗?
  • 解题思路
No. 思路 时间复杂度 空间复杂度
01 内置函数 O(n) O(1)
02 遍历 O(n) O(1)
func isFlipedString(s1 string, s2 string) bool {
    if len(s1) != len(s2){
        return false
    }
    return strings.Contains(s1+s1, s2)
}

#
func isFlipedString(s1 string, s2 string) bool {
    if s1 == s2 {
        return true
    }
    if len(s1) != len(s2) {
        return false
    }
    for i := 0; i < len(s1); i++ {
        s1 = s1[1:] + string(s1[0])
        if s1 == s2 {
            return true
        }
    }
    return false
}

面试题02.01.移除重复节点(3)

  • 题目
编写代码,移除未排序链表中的重复节点。保留最开始出现的节点。
示例1:输入:[1, 2, 3, 3, 2, 1]输出:[1, 2, 3]
示例2:输入:[1, 1, 1, 1, 2]输出:[1, 2]
提示:
    链表长度在[0, 20000]范围内。
    链表元素在[0, 20000]范围内。
进阶:如果不得使用临时缓冲区,该怎么解决?
  • 解题思路
No. 思路 时间复杂度 空间复杂度
01 哈希辅助 O(n) O(n)
02 遍历 O(n^2) O(1)
03 递归 O(n) O(n)
func removeDuplicateNodes(head *ListNode) *ListNode {
    if head == nil {
        return head
    }
    m := make(map[int]bool)
    m[head.Val] = true
    temp := head
    for temp.Next != nil {
        if m[temp.Next.Val] == true {
            temp.Next = temp.Next.Next
        } else {
            m[temp.Next.Val] = true
            temp = temp.Next
        }
    }
    return head
}

# 2
func removeDuplicateNodes(head *ListNode) *ListNode {
    if head == nil {
        return head
    }
    temp := head
    for temp != nil {
        second := temp
        for second.Next != nil {
            if second.Next.Val == temp.Val {
                second.Next = second.Next.Next
            } else {
                second = second.Next
            }
        }
        temp = temp.Next
    }
    return head
}

# 3
var m map[int]bool

func removeDuplicateNodes(head *ListNode) *ListNode {
    m = make(map[int]bool)
    return remove(head)

}

func remove(head *ListNode) *ListNode {
    if head == nil {
        return head
    }
    if m[head.Val] == true {
        return remove(head.Next)
    }
    m[head.Val] = true
    head.Next = remove(head.Next)
    return head
}

面试题02.02.返回倒数第k个节点(4)

  • 题目
实现一种算法,找出单向链表中倒数第 k 个节点。返回该节点的值。
注意:本题相对原题稍作改动
示例:输入: 1->2->3->4->5 和 k = 2 输出: 4
说明:给定的 k 保证是有效的。
  • 解题思路
No. 思路 时间复杂度 空间复杂度
01 数组辅助 O(n) O(n)
02 快慢指针 O(n) O(1)
03 统计+遍历 O(n) O(1)
04 递归 O(n) O(n)
func kthToLast(head *ListNode, k int) int {
    arr := make([]*ListNode, 0)
    for head != nil {
        arr = append(arr, head)
        head = head.Next
    }
    if len(arr) >= k {
        return arr[len(arr)-k].Val
    }
    return -1
}

# 2
func kthToLast(head *ListNode, k int) int {
    fast := head
    for k > 0 && head != nil {
        fast = fast.Next
        k--
    }
    if k > 0 {
        return -1
    }
    slow := head
    for fast != nil {
        fast = fast.Next
        slow = slow.Next
    }
    return slow.Val
}

# 3
func kthToLast(head *ListNode, k int) int {
    temp := head
    count := 0
    for temp != nil {
        count++
        temp = temp.Next
    }
    if count < k {
        return -1
    }
    for i := 0; i < count-k; i++ {
        head = head.Next
    }
    return head.Val
}

# 4
func kthToLast(head *ListNode, k int) int {
    res, count := dfs(head, k)
    if count > 0 {
        return -1
    }
    return res.Val
}

func dfs(node *ListNode, k int) (*ListNode, int) {
    if node == nil {
        return node, k
    }
    next, nextValue := dfs(node.Next, k)
    if nextValue <= 0 {
        return next, nextValue
    }
    nextValue = nextValue - 1
    return node, nextValue
}

面试题02.03.删除中间节点(1)

  • 题目
实现一种算法,删除单向链表中间的某个节点(即不是第一个或最后一个节点),假定你只能访问该节点。
示例:输入:单向链表a->b->c->d->e->f中的节点c
结果:不返回任何数据,但该链表变为a->b->d->e->f
  • 解题思路
No. 思路 时间复杂度 空间复杂度
01 把当前节点替换成下一个节点 O(1) O(1)
func deleteNode(node *ListNode) {
    // *node = *node.Next
    node.Val = node.Next.Val
    node.Next = node.Next.Next
}

面试题02.04.分割链表(2)

  • 题目
编写程序以 x 为基准分割链表,使得所有小于 x 的节点排在大于或等于 x 的节点之前。
如果链表中包含 x,x 只需出现在小于 x 的元素之后(如下所示)。
分割元素 x 只需处于“右半部分”即可,其不需要被置于左右两部分之间。
示例:输入: head = 3->5->8->5->10->2->1, x = 5
输出: 3->1->2->10->5->5->8
  • 解题思路
No. 思路 时间复杂度 空间复杂度
01 双指针 O(n) O(1)
02 数组辅助 O(n) O(n)
func partition(head *ListNode, x int) *ListNode {
    first := &ListNode{}
    second := &ListNode{}
    a := first
    b := second
    for head != nil {
        if head.Val < x {
            a.Next = head
            a = head
        } else {
            b.Next = head
            b = head
        }
        head = head.Next
    }
    b.Next = nil
    a.Next = second.Next
    return first.Next
}

# 2
func partition(head *ListNode, x int) *ListNode {
    a := make([]*ListNode, 0)
    b := make([]*ListNode, 0)

    for head != nil {
        if head.Val < x {
            a = append(a, head)
        } else {
            b = append(b, head)
        }
        head = head.Next
    }
    temp := &ListNode{}
    node := temp
    for i := 0; i < len(a); i++ {
        node.Next = a[i]
        node = node.Next
    }
    for i := 0; i < len(b); i++ {
        node.Next = b[i]
        node = node.Next
    }
    node.Next = nil
    return temp.Next
}

面试题02.05.链表求和(2)

  • 题目
给定两个用链表表示的整数,每个节点包含一个数位。
这些数位是反向存放的,也就是个位排在链表首部。
编写函数对这两个整数求和,并用链表形式返回结果。
示例:输入:(7 -> 1 -> 6) + (5 -> 9 -> 2),即617 + 295 输出:2 -> 1 -> 9,即912
进阶:假设这些数位是正向存放的,请再做一遍。
示例:输入:(6 -> 1 -> 7) + (2 -> 9 -> 5),即617 + 295 输出:9 -> 1 -> 2,即912
  • 解题思路
No. 思路 时间复杂度 空间复杂度
01 遍历 O(n) O(n)
02 递归 O(n) O(n)
func addTwoNumbers(l1 *ListNode, l2 *ListNode) *ListNode {
    res := &ListNode{}
    cur := res
    carry := 0
    for l1 != nil || l2 != nil || carry > 0 {
        sum := carry
        if l1 != nil {
            sum += l1.Val
            l1 = l1.Next
        }
        if l2 != nil {
            sum += l2.Val
            l2 = l2.Next
        }
        carry = sum / 10 // 进位
        cur.Next = &ListNode{Val: sum % 10}
        cur = cur.Next
    }
    return res.Next
}

# 2
func addTwoNumbers(l1 *ListNode, l2 *ListNode) *ListNode {
    if l1 == nil && l2 == nil {
        return nil
    }
    if l1 == nil {
        return l2
    }
    if l2 == nil {
        return l1
    }
    sum := l1.Val + l2.Val
    res := &ListNode{Val: sum % 10}
    if sum >= 10 {
        l1.Next = addTwoNumbers(l1.Next, &ListNode{Val: 1})
    }
    res.Next = addTwoNumbers(l1.Next, l2.Next)
    return res
}

面试题02.06.回文链表(4)

  • 题目
编写一个函数,检查输入的链表是否是回文的。
示例 1:输入: 1->2 输出: false 
示例 2:输入: 1->2->2->1 输出: true 
进阶:你能否用 O(n) 时间复杂度和 O(1) 空间复杂度解决此题?
  • 解题思路
No. 思路 时间复杂度 空间复杂度
01 数组辅助 O(n) O(n)
02 快慢指针反转链表 O(n) O(1)
03 栈辅助 O(n) O(n)
04 递归 O(n) O(n)
func isPalindrome(head *ListNode) bool {
    m := make([]int, 0)
    for head != nil {
        m = append(m, head.Val)
        head = head.Next
    }
    i, j := 0, len(m)-1
    for i < j {
        if m[i] != m[j] {
            return false
        }
        i++
        j--
    }
    return true
}

# 2
func isPalindrome(head *ListNode) bool {
    fast, slow := head, head
    for fast != nil && fast.Next != nil {
        fast = fast.Next.Next
        slow = slow.Next
    }
    var pre *ListNode
    cur := slow
    for cur != nil{
        next := cur.Next
        cur.Next = pre
        pre = cur
        cur = next
    }
    for pre != nil{
        if head.Val != pre.Val{
            return false
        }
        pre = pre.Next
        head = head.Next
    }
    return true
}

# 3
func isPalindrome(head *ListNode) bool {
    m := make([]int, 0)
    temp := head
    for temp != nil {
        m = append(m, temp.Val)
        temp = temp.Next
    }
    for head != nil {
        val := m[len(m)-1]
        m = m[:len(m)-1]
        if head.Val != val {
            return false
        }
        head = head.Next
    }
    return true
}

# 4
var p *ListNode
func isPalindrome(head *ListNode) bool {
    if head == nil{
        return true
    }
    if p == nil{
        p = head
    }
    if isPalindrome(head.Next) && (p.Val == head.Val){
        p = p.Next
        return true
    }
    p = nil
    return false
}

面试题02.07.链表相交(4)

  • 题目
给定两个(单向)链表,判定它们是否相交并返回交点。请注意相交的定义基于节点的引用,而不是基于节点的值。
换句话说,如果一个链表的第k个节点与另一个链表的第j个节点是同一节点(引用完全相同),则这两个链表相交。
示例 1:
输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,0,1,8,4,5], skipA = 2, skipB = 3
输出:Reference of the node with value = 8
输入解释:相交节点的值为 8 (注意,如果两个列表相交则不能为 0)。从各自的表头开始算起,
链表 A 为 [4,1,8,4,5],链表 B 为 [5,0,1,8,4,5]。
在 A 中,相交节点前有 2 个节点;在 B 中,相交节点前有 3 个节点。
示例 2:输入:intersectVal = 2, listA = [0,9,1,2,4], listB = [3,2,4], skipA = 3, skipB = 1
输出:Reference of the node with value = 2
输入解释:相交节点的值为 2 (注意,如果两个列表相交则不能为 0)。
从各自的表头开始算起,链表 A 为 [0,9,1,2,4],链表 B 为 [3,2,4]。
在 A 中,相交节点前有 3 个节点;在 B 中,相交节点前有 1 个节点。
示例 3:输入:intersectVal = 0, listA = [2,6,4], listB = [1,5], skipA = 3, skipB = 2
输出:null
输入解释:从各自的表头开始算起,链表 A 为 [2,6,4],链表 B 为 [1,5]。
由于这两个链表不相交,所以 intersectVal 必须为 0,而 skipA 和 skipB 可以是任意值。
解释:这两个链表不相交,因此返回 null。
注意:
    如果两个链表没有交点,返回 null 。
    在返回结果后,两个链表仍须保持原有的结构。
    可假定整个链表结构中没有循环。
    程序尽量满足 O(n) 时间复杂度,且仅用 O(1) 内存。
  • 解题思路
No. 思路 时间复杂度 空间复杂度
01 对齐比较 O(n) O(1)
02 交换比较 O(n) O(1)
03 暴力法 O(n^2) O(1)
04 哈希辅助 O(n) O(n)
func getIntersectionNode(headA, headB *ListNode) *ListNode {
    ALength := 0
    A := headA
    for A != nil {
        ALength++
        A = A.Next
    }
    BLength := 0
    B := headB
    for B != nil {
        BLength++
        B = B.Next
    }

    pA := headA
    pB := headB
    if ALength > BLength {
        n := ALength - BLength
        for n > 0 {
            pA = pA.Next
            n--
        }
    } else {
        n := BLength - ALength
        for n > 0 {
            pB = pB.Next
            n--
        }
    }

    for pA != pB {
        pA = pA.Next
        pB = pB.Next
    }
    return pA
}

# 2
func getIntersectionNode(headA, headB *ListNode) *ListNode {
    A, B := headA, headB
    for A != B {
        if A != nil {
            A = A.Next
        } else {
            A = headB
        }
        if B != nil {
            B = B.Next
        } else {
            B = headA
        }
    }
    return A
}

# 3
func getIntersectionNode(headA, headB *ListNode) *ListNode {
    A, B := headA, headB
    for A != nil {
        for B != nil {
            if A == B {
                return A
            }
            B = B.Next
        }
        A = A.Next
        B = headB
    }
    return nil
}

# 4
func getIntersectionNode(headA, headB *ListNode) *ListNode {
    m := make(map[*ListNode]bool)
    for headA != nil {
        m[headA] = true
        headA = headA.Next
    }

    for headB != nil {
        if _, ok := m[headB]; ok {
            return headB
        }
        headB = headB.Next
    }
    return nil
}

面试题02.08.环路检测(3)

  • 题目
给定一个链表,如果它是有环链表,实现一个算法返回环路的开头节点。
有环链表的定义:在链表中某个节点的next元素指向在它前面出现过的节点,则表明该链表存在环路。
示例 1:输入:head = [3,2,0,-4], pos = 1 输出:tail connects to node index 1
解释:链表中有一个环,其尾部连接到第二个节点。
示例 2:输入:head = [1,2], pos = 0 输出:tail connects to node index 0
解释:链表中有一个环,其尾部连接到第一个节点。
示例 3:输入:head = [1], pos = -1 输出:no cycle
解释:链表中没有环。
进阶:你是否可以不用额外空间解决此题?
  • 解题思路
No. 思路 时间复杂度 空间复杂度
01 哈希辅助 O(n) O(n)
02 快慢指针 O(n) O(1)
03 遍历标记 O(n) O(1)
func detectCycle(head *ListNode) *ListNode {
    m := make(map[*ListNode]bool)
    for head != nil {
        if m[head] {
            return head
        }
        m[head] = true
        head = head.Next
    }
    return nil
}

# 2
func detectCycle(head *ListNode) *ListNode {
    if head == nil {
        return nil
    }
    fast, slow := head, head
    for fast != nil && fast.Next != nil {
        fast = fast.Next.Next
        slow = slow.Next
        if fast == slow {
            break
        }
    }
    if fast == nil || fast.Next == nil {
        return nil
    }
    slow = head
    for fast != slow {
        fast = fast.Next
        slow = slow.Next
    }
    return slow
}

# 3
func detectCycle(head *ListNode) *ListNode {
    for head != nil {
        if head.Val == math.MaxInt32 {
            return head
        }
        head.Val = math.MaxInt32
        head = head.Next
    }
    return head
}

面试题03.01.三合一(1)

  • 题目
三合一。描述如何只用一个数组来实现三个栈。
你应该实现push(stackNum, value)、pop(stackNum)、isEmpty(stackNum)、peek(stackNum)方法。stackNum表示栈下标,value表示压入的值。
构造函数会传入一个stackSize参数,代表每个栈的大小。
示例1:输入:["TripleInOne", "push", "push", "pop", "pop", "pop", "isEmpty"]
[[1], [0, 1], [0, 2], [0], [0], [0], [0]]
 输出:[null, null, null, 1, -1, -1, true]
说明:当栈为空时`pop, peek`返回-1,当栈满时`push`不压入元素。
示例2:输入: ["TripleInOne", "push", "push", "push", "pop", "pop", "pop", "peek"]
[[2], [0, 1], [0, 2], [0, 3], [0], [0], [0], [0]]
 输出:[null, null, null, null, 2, 1, -1, -1]
  • 解题思路
No. 思路 时间复杂度 空间复杂度
01 数组 O(1) O(n)
type TripleInOne struct {
    arr    []int
    length int
    index  [3]int
}

func Constructor(stackSize int) TripleInOne {
    return TripleInOne{
        arr:    make([]int, stackSize*3),
        length: stackSize,
        index:  [3]int{0, 0, 0},
    }
}

func (this *TripleInOne) Push(stackNum int, value int) {
    if this.index[stackNum] < this.length {
        this.arr[3*this.index[stackNum]+stackNum] = value
        this.index[stackNum]++
    }
}

func (this *TripleInOne) Pop(stackNum int) int {
    res := -1
    if this.index[stackNum] != 0 {
        this.index[stackNum]--
        res = this.arr[3*this.index[stackNum]+stackNum]
    }
    return res
}

func (this *TripleInOne) Peek(stackNum int) int {
    res := -1
    if this.index[stackNum] != 0 {
        res = this.arr[3*(this.index[stackNum]-1)+stackNum]
    }
    return res
}

func (this *TripleInOne) IsEmpty(stackNum int) bool {
    if this.index[stackNum] == 0 {
        return true
    }
    return false
}

面试题03.02.栈的最小值(2)

  • 题目
请设计一个栈,除了常规栈支持的pop与push函数以外,还支持min函数,该函数返回栈元素中的最小值。
执行push、pop和min操作的时间复杂度必须为O(1)。
示例:MinStack minStack = new MinStack();
minStack.push(-2);
minStack.push(0);
minStack.push(-3);
minStack.getMin();   --> 返回 -3.
minStack.pop();
minStack.top();      --> 返回 0.
minStack.getMin();   --> 返回 -2.
  • 解题思路
No. 思路 时间复杂度 空间复杂度
01 结构体 O(n) O(n)
02 双栈 O(n) O(n)
type item struct {
    min, x int
}

type MinStack struct {
    stack []item
}

func Constructor() MinStack {
    return MinStack{}
}

func (this *MinStack) Push(x int) {
    min := x
    if len(this.stack) > 0 && this.GetMin() < x {
        min = this.GetMin()
    }
    this.stack = append(this.stack, item{
        min: min,
        x:   x,
    })
}

func (this *MinStack) Pop() {
    this.stack = this.stack[:len(this.stack)-1]
}

func (this *MinStack) Top() int {
    if len(this.stack) == 0 {
        return 0
    }
    return this.stack[len(this.stack)-1].x
}

func (this *MinStack) GetMin() int {
    if len(this.stack) == 0 {
        return 0
    }
    return this.stack[len(this.stack)-1].min
}

# 2
type MinStack struct {
    data []int
    min  []int
}

func Constructor() MinStack {
    return MinStack{[]int{}, []int{}}
}

func (this *MinStack) Push(x int) {
    if len(this.data) == 0 || x <= this.GetMin() {
        this.min = append(this.min, x)
    }
    this.data = append(this.data, x)
}

func (this *MinStack) Pop() {
    x := this.data[len(this.data)-1]
    this.data = this.data[:len(this.data)-1]
    if x == this.GetMin() {
        this.min = this.min[:len(this.min)-1]
    }
}

func (this *MinStack) Top() int {
    if len(this.data) == 0 {
        return 0
    }
    return this.data[len(this.data)-1]
}

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

面试题03.03.堆盘子(1)

  • 题目
堆盘子。设想有一堆盘子,堆太高可能会倒下来。因此,在现实生活中,盘子堆到一定高度时,我们就会另外堆一堆盘子。
请实现数据结构SetOfStacks,模拟这种行为。SetOfStacks应该由多个栈组成,并且在前一个栈填满时新建一个栈。
此外,SetOfStacks.push()和SetOfStacks.pop()应该与普通栈的操作方法相同
(也就是说,pop()返回的值,应该跟只有一个栈时的情况一样)。
进阶:实现一个popAt(int index)方法,根据指定的子栈,执行pop操作。
当某个栈为空时,应当删除该栈。当栈中没有元素或不存在该栈时,pop,popAt 应返回 -1.
示例1: 输入:["StackOfPlates", "push", "push", "popAt", "pop", "pop"]
[[1], [1], [2], [1], [], []]
 输出:[null, null, null, 2, 1, -1]
示例2:输入: ["StackOfPlates", "push", "push", "push", "popAt", "popAt", "popAt"]
[[2], [1], [2], [3], [0], [0], [0]]
 输出:[null, null, null, null, 2, 1, 3]
  • 解题思路
No. 思路 时间复杂度 空间复杂度
01 栈-二维 O(1) O(n^2)
type StackOfPlates struct {
    cap   int
    stack [][]int
}

func Constructor(cap int) StackOfPlates {
    return StackOfPlates{
        cap:   cap,
        stack: make([][]int, 0),
    }
}

func (this *StackOfPlates) Push(val int) {
    if this.cap == 0 {
        return
    }
    if len(this.stack) == 0 {
        newStack := make([]int, 0)
        newStack = append(newStack, val)
        this.stack = append(this.stack, newStack)
        return
    }
    last := this.stack[len(this.stack)-1]
    if len(last) == this.cap {
        newStack := make([]int, 0)
        newStack = append(newStack, val)
        this.stack = append(this.stack, newStack)
        return
    }
    last = append(last, val)
    this.stack[len(this.stack)-1] = last
}

func (this *StackOfPlates) Pop() int {
    if len(this.stack) == 0 {
        return -1
    }
    last := this.stack[len(this.stack)-1]
    res := last[len(last)-1]
    last = last[:len(last)-1]
    this.stack[len(this.stack)-1] = last
    if len(last) == 0 {
        this.stack = this.stack[:len(this.stack)-1]
    }
    return res
}

func (this *StackOfPlates) PopAt(index int) int {
    if index >= len(this.stack) {
        return -1
    }
    arr := this.stack[index]
    res := arr[len(arr)-1]
    arr = arr[:len(arr)-1]
    this.stack[index] = arr
    if len(arr) == 0 {
        this.stack = append(this.stack[:index], this.stack[index+1:]...)
    }
    return res
}

面试题03.04.化栈为队(3)

  • 题目
实现一个MyQueue类,该类用两个栈来实现一个队列。
示例:MyQueue queue = new MyQueue();
queue.push(1);
queue.push(2);
queue.peek();  // 返回 1
queue.pop();   // 返回 1
queue.empty(); // 返回 false
说明: 你只能使用标准的栈操作 -- 也就是只有 push to top, peek/pop from top, size 
和 is empty 操作是合法的。
    你所使用的语言也许不支持栈。
    你可以使用 list 或者 deque(双端队列)来模拟一个栈,只要是标准的栈操作即可。
    假设所有操作都是有效的 (例如,一个空的队列不会调用 pop 或者 peek 操作)。
  • 解题思路
No. 思路 时间复杂度 空间复杂度
01 使用切片 O(1) O(n)
02 使用2个栈实现 O(n) O(n)
03 使用2个切片实现 O(n) O(n)
type MyQueue struct {
    a []int
}

func Constructor() MyQueue {
    return MyQueue{}
}

func (m *MyQueue) Push(x int) {
    m.a = append(m.a, x)
}

func (m *MyQueue) Pop() int {
    if len(m.a) == 0 {
        return 0
    }
    first := m.a[0]
    m.a = m.a[1:]
    return first
}

func (m *MyQueue) Peek() int {
    if len(m.a) == 0 {
        return 0
    }
    return m.a[0]
}

func (m *MyQueue) Empty() bool {
    if len(m.a) == 0 {
        return true
    }
    return false
}

# 2
/*
入队: 直接入栈a
出队: 栈b为空,则把栈a中全部数据出栈进入栈b,然后出栈b,不为空直接出栈b
*/
type MyQueue struct {
    a, b *Stack
}

func Constructor() MyQueue {
    return MyQueue{
        a: NewStack(),
        b: NewStack(),
    }
}

func (m *MyQueue) Push(x int) {
    m.a.Push(x)
}

func (m *MyQueue) Pop() int {
    if m.b.Len() == 0 {
        for m.a.Len() > 0 {
            m.b.Push(m.a.Pop())
        }
    }
    return m.b.Pop()
}

func (m *MyQueue) Peek() int {
    res := m.Pop()
    m.b.Push(res)
    return res
}

func (m *MyQueue) Empty() bool {
    return m.a.Len() == 0 && m.b.Len() == 0
}

type Stack struct {
    nums []int
}

func NewStack() *Stack {
    return &Stack{
        nums: []int{},
    }
}

func (s *Stack) Push(n int) {
    s.nums = append(s.nums, n)
}

func (s *Stack) Pop() int {
    res := s.nums[len(s.nums)-1]
    s.nums = s.nums[:len(s.nums)-1]
    return res
}

func (s *Stack) Len() int {
    return len(s.nums)
}

func (s *Stack) IsEmpty() bool {
    return s.Len() == 0
}

# 3
type MyQueue struct {
    a []int
    b []int
}

func Constructor() MyQueue {
    return MyQueue{}
}

func (m *MyQueue) Push(x int) {
    m.a = append(m.a, x)
}

func (m *MyQueue) Pop() int {
    m.Peek()
    temp := m.b[len(m.b)-1]
    m.b = m.b[:len(m.b)-1]
    return temp
}

func (m *MyQueue) Peek() int {
    if len(m.b) == 0 {
        for len(m.a) > 0 {
            m.b = append(m.b, m.a[len(m.a)-1])
            m.a = m.a[:len(m.a)-1]
        }
    }
    if len(m.b) == 0 {
        return -1
    }
    return m.b[len(m.b)-1]
}

func (m *MyQueue) Empty() bool {
    return len(m.a) == 0 && len(m.b) == 0
}

面试题03.05.栈排序(1)

  • 题目
栈排序。 编写程序,对栈进行排序使最小元素位于栈顶。
最多只能使用一个其他的临时栈存放数据,但不得将元素复制到别的数据结构(如数组)中。
该栈支持如下操作:push、pop、peek 和 isEmpty。当栈为空时,peek 返回 -1。
示例1:输入:["SortedStack", "push", "push", "peek", "pop", "peek"]
[[], [1], [2], [], [], []]
 输出:[null,null,null,1,null,2]
示例2:输入: ["SortedStack", "pop", "pop", "push", "pop", "isEmpty"]
[[], [], [], [1], [], []]
 输出:[null,null,null,null,null,true]
说明:栈中的元素数目在[0, 5000]范围内。
  • 解题思路
No. 思路 时间复杂度 空间复杂度
01 双栈 O(n) O(n)
type SortedStack struct {
    stack []int
    temp  []int
}

func Constructor() SortedStack {
    return SortedStack{}
}

func (this *SortedStack) Push(val int) {
    for len(this.stack) > 0 && val >= this.stack[len(this.stack)-1] {
        this.temp = append(this.temp, this.stack[len(this.stack)-1])
        this.stack = this.stack[:len(this.stack)-1]
    }
    this.stack = append(this.stack, val)
    for len(this.temp) > 0 {
        this.stack = append(this.stack, this.temp[len(this.temp)-1])
        this.temp = this.temp[:len(this.temp)-1]
    }
}

func (this *SortedStack) Pop() {
    if len(this.stack) == 0 {
        return
    }
    this.stack = this.stack[:len(this.stack)-1]
}

func (this *SortedStack) Peek() int {
    if len(this.stack) == 0 {
        return -1
    }
    return this.stack[len(this.stack)-1]
}

func (this *SortedStack) IsEmpty() bool {
    return len(this.stack) == 0
}

面试题03.06.动物收容所(2)

  • 题目
动物收容所。有家动物收容所只收容狗与猫,且严格遵守“先进先出”的原则。
在收养该收容所的动物时,收养人只能收养所有动物中“最老”(由其进入收容所的时间长短而定)的动物,
或者可以挑选猫或狗(同时必须收养此类动物中“最老”的)。
换言之,收养人不能自由挑选想收养的对象。
请创建适用于这个系统的数据结构,实现各种操作方法,
比如enqueue、dequeueAny、dequeueDog和dequeueCat。允许使用Java内置的LinkedList数据结构。
enqueue方法有一个animal参数,animal[0]代表动物编号,animal[1]代表动物种类,其中 0 代表猫,1 代表狗。
dequeue*方法返回一个列表[动物编号, 动物种类],若没有可以收养的动物,则返回[-1,-1]。
示例1:输入:
["AnimalShelf", "enqueue", "enqueue", "dequeueCat", "dequeueDog", "dequeueAny"]
[[], [[0, 0]], [[1, 0]], [], [], []]
 输出:[null,null,null,[0,0],[-1,-1],[1,0]]
示例2:输入:
["AnimalShelf", "enqueue", "enqueue", "enqueue", "dequeueDog", "dequeueCat", "dequeueAny"]
[[], [[0, 0]], [[1, 0]], [[2, 1]], [], [], []]
输出:[null,null,null,null,[2,1],[0,0],[1,0]]
说明:收纳所的最大容量为20000
  • 解题思路
No. 思路 时间复杂度 空间复杂度
01 双数组 O(1) O(n)
02 内置list O(1) O(n)
type AnimalShelf struct {
    cat [][]int
    dog [][]int
}

func Constructor() AnimalShelf {
    return AnimalShelf{
        cat: make([][]int, 0),
        dog: make([][]int, 0),
    }
}

func (this *AnimalShelf) Enqueue(animal []int) {
    if animal[1] == 0 {
        this.cat = append(this.cat, animal)
    } else {
        this.dog = append(this.dog, animal)
    }
}

func (this *AnimalShelf) DequeueAny() []int {
    if len(this.dog) == 0 && len(this.cat) == 0 {
        return []int{-1, -1}
    }
    if len(this.dog) == 0 || len(this.cat) == 0 {
        if len(this.dog) == 0 {
            res := this.cat[0]
            this.cat = this.cat[1:]
            return res
        }
        res := this.dog[0]
        this.dog = this.dog[1:]
        return res
    }
    if this.dog[0][0] > this.cat[0][0] {
        res := this.cat[0]
        this.cat = this.cat[1:]
        return res

    }
    res := this.dog[0]
    this.dog = this.dog[1:]
    return res
}

func (this *AnimalShelf) DequeueDog() []int {
    if len(this.dog) == 0 {
        return []int{-1, -1}
    }
    res := this.dog[0]
    this.dog = this.dog[1:]
    return res
}

func (this *AnimalShelf) DequeueCat() []int {
    if len(this.cat) == 0 {
        return []int{-1, -1}
    }
    res := this.cat[0]
    this.cat = this.cat[1:]
    return res
}

# 2
type AnimalShelf struct {
    arr [2]*list.List
}

func Constructor() AnimalShelf {
    return AnimalShelf{
        arr: [2]*list.List{list.New(), list.New()},
    }
}

func (this *AnimalShelf) Enqueue(animal []int) {
    this.arr[animal[1]].PushBack(animal[0])
}

func (this *AnimalShelf) DequeueAny() []int {
    if this.arr[0].Len() == 0 && this.arr[1].Len() == 0 {
        return []int{-1, -1}
    }
    if this.arr[1].Len() > 0 &&
        (this.arr[0].Len() == 0 || this.arr[1].Front().Value.(int) < this.arr[0].Front().Value.(int)) {
        return []int{this.arr[1].Remove(this.arr[1].Front()).(int), 1}
    }
    return []int{this.arr[0].Remove(this.arr[0].Front()).(int), 0}
}

func (this *AnimalShelf) DequeueDog() []int {
    if this.arr[1].Len() > 0 {
        return []int{this.arr[1].Remove(this.arr[1].Front()).(int), 1}
    }
    return []int{-1, -1}
}

func (this *AnimalShelf) DequeueCat() []int {
    if this.arr[0].Len() > 0 {
        return []int{this.arr[0].Remove(this.arr[0].Front()).(int), 0}
    }
    return []int{-1, -1}
}

面试题04.01.节点间通路(2)

  • 题目
节点间通路。给定有向图,设计一个算法,找出两个节点之间是否存在一条路径。
示例1:输入:n = 3, graph = [[0, 1], [0, 2], [1, 2], [1, 2]], start = 0, target = 2 
输出:true
示例2:输入:n = 5, graph = [[0, 1], [0, 2], [0, 4], [0, 4], [0, 1], [1, 3], 
[1, 4], [1, 3], [2, 3], [3, 4]], start = 0, target = 4
输出 true
提示:节点数量n在[0, 1e5]范围内。
    节点编号大于等于 0 小于 n。
    图中可能存在自环和平行边。
  • 解题思路
No. 思路 时间复杂度 空间复杂度
01 广度优先搜索 O(n) O(n)
02 深度优先搜索 O(n) O(n)
func findWhetherExistsPath(n int, graph [][]int, start int, target int) bool {
    edges := make([][]int, n)
    // 邻接表
    for i := 0; i < len(graph); i++ {
        a := graph[i][0]
        b := graph[i][1]
        edges[a] = append(edges[a], b)
    }
    queue := make([]int, 0)
    queue = append(queue, start)
    visited := make([]bool, n)
    for len(queue) > 0 {
        node := queue[0]
        queue = queue[1:]
        visited[node] = true
        if node == target {
            return true
        }
        for i := 0; i < len(edges[node]); i++ {
            if visited[edges[node][i]] == false {
                if edges[node][i] == target {
                    return true
                }
                queue = append(queue, edges[node][i])
            }
        }
    }
    return false
}

# 2
func findWhetherExistsPath(n int, graph [][]int, start int, target int) bool {
    edges := make([][]int, n)
    // 邻接表
    for i := 0; i < len(graph); i++ {
        a := graph[i][0]
        b := graph[i][1]
        edges[a] = append(edges[a], b)
    }

    visited := make([]bool, n)
    return dfs(edges, visited, start, target)
}

func dfs(edges [][]int, visited []bool, start, target int) bool {
    if start == target {
        return true
    }
    visited[start] = true
    for i := 0; i < len(edges[start]); i++ {
        if visited[edges[start][i]] == false {
            if edges[start][i] == target {
                return true
            }
            if dfs(edges, visited, edges[start][i], target) {
                return true
            }
        }
    }
    return false
}

面试题04.02.最小高度树(2)

  • 题目
给定一个有序整数数组,元素各不相同且按升序排列,编写一个算法,创建一棵高度最小的二叉搜索树。
示例:给定有序数组: [-10,-3,0,5,9],
一个可能的答案是:[0,-3,9,-10,null,5],它可以表示下面这个高度平衡二叉搜索树:
          0 
         / \ 
       -3   9 
       /   / 
     -10  5
  • 解题思路
No. 思路 时间复杂度 空间复杂度
01 递归 O(n) O(log(n))
02 迭代 O(n) O(n)
func sortedArrayToBST(nums []int) *TreeNode {
    if len(nums) == 0 {
        return nil
    }
    mid := len(nums) / 2
    return &TreeNode{
        Val:   nums[mid],
        Left:  sortedArrayToBST(nums[:mid]),
        Right: sortedArrayToBST(nums[mid+1:]),
    }
}

# 2
type MyTreeNode struct {
    root  *TreeNode
    start int
    end   int
}

func sortedArrayToBST(nums []int) *TreeNode {
    if len(nums) == 0 {
        return nil
    }
    queue := make([]MyTreeNode, 0)
    root := &TreeNode{Val: 0}
    queue = append(queue, MyTreeNode{root, 0, len(nums)})
    for len(queue) > 0 {
        myRoot := queue[0]
        queue = queue[1:]
        start := myRoot.start
        end := myRoot.end
        mid := (start + end) / 2
        curRoot := myRoot.root
        curRoot.Val = nums[mid]
        if start < mid {
            curRoot.Left = &TreeNode{Val: 0}
            queue = append(queue, MyTreeNode{curRoot.Left, start, mid})
        }
        if mid+1 < end {
            curRoot.Right = &TreeNode{Val: 0}
            queue = append(queue, MyTreeNode{curRoot.Right, mid + 1, end})
        }
    }
    return root
}

面试题04.03.特定深度节点链表(2)

  • 题目
给定一棵二叉树,设计一个算法,创建含有某一深度上所有节点的链表
(比如,若一棵树的深度为 D,则会创建出 D 个链表)。返回一个包含所有深度的链表的数组。
示例:输入:[1,2,3,4,5,null,7,8]
        1
       /  \ 
      2    3
     / \    \ 
    4   5    7
   /
  8
输出:[[1],[2,3],[4,5,7],[8]]
  • 解题思路
No. 思路 时间复杂度 空间复杂度
01 层序遍历 O(n) O(n)
02 深度优先搜索 O(n) O(n)
func listOfDepth(tree *TreeNode) []*ListNode {
    res := make([]*ListNode, 0)
    if tree == nil {
        return res
    }
    queue := make([]*TreeNode, 0)
    queue = append(queue, tree)
    for len(queue) > 0 {
        length := len(queue)
        node := &ListNode{}
        tempNode := node
        for i := 0; i < length; i++ {
            node := queue[i]
            tempNode.Next = &ListNode{
                Val: node.Val,
            }
            tempNode = tempNode.Next
            if node.Left != nil {
                queue = append(queue, node.Left)
            }
            if node.Right != nil {
                queue = append(queue, node.Right)
            }
        }
        res = append(res, node.Next)
        queue = queue[length:]
    }
    return res
}

# 2
var res []*ListNode

func listOfDepth(tree *TreeNode) []*ListNode {
    level := 0
    res = make([]*ListNode, 0)
    dfs(tree, level)
    return res
}

func dfs(root *TreeNode, level int) {
    if root == nil {
        return
    }
    if level >= len(res) {
        res = append(res, &ListNode{root.Val, nil})
    } else {
        head := res[level]
        for head.Next != nil {
            head = head.Next
        }
        head.Next = &ListNode{root.Val, nil}
    }
    dfs(root.Left, level+1)
    dfs(root.Right, level+1)
}

面试题04.04.检查平衡性(3)

  • 题目
实现一个函数,检查二叉树是否平衡。在这个问题中,平衡树的定义如下:
任意一个节点,其两棵子树的高度差不超过 1。
示例 1:给定二叉树 [3,9,20,null,null,15,7]
    3
   / \
  9  20
    /  \
   15   7
返回 true 。
示例 2:给定二叉树 [1,2,2,3,3,null,null,4,4]
      1
     / \
    2   2
   / \
  3   3
 / \
4   4
返回 false 。
  • 解题思路
No. 思路 时间复杂度 空间复杂度
01 递归 O(n) O(log(n))
02 递归 O(n) O(log(n))
03 递归 O(n) O(log(n))
func isBalanced(root *TreeNode) bool {
    _, isBalanced := dfs(root)
    return isBalanced
}

func dfs(root *TreeNode) (int, bool) {
    if root == nil {
        return 0, true
    }

    leftDepth, leftIsBalanced := dfs(root.Left)
    if leftIsBalanced == false {
        return 0, false
    }
    rightDepth, rightIsBalanced := dfs(root.Right)
    if rightIsBalanced == false {
        return 0, false
    }

    if -1 <= leftDepth-rightDepth &&
        leftDepth-rightDepth <= 1 {
        return max(leftDepth, rightDepth) + 1, true
    }
    return 0, false
}

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

# 2
func isBalanced(root *TreeNode) bool {
    return dfs(root) != -1
}

func dfs(root *TreeNode) int {
    if root == nil {
        return 0
    }
    left := dfs(root.Left)
    right := dfs(root.Right)
    if left != -1 && right != -1 &&
        abs(left, right) <= 1 {
        return max(left, right) + 1
    }
    return -1
}

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

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

# 3
func isBalanced(root *TreeNode) bool {
    if root == nil {
        return true
    }
    if math.Abs(dfs(root.Left)-dfs(root.Right)) <= 1 {
        return isBalanced(root.Left) && isBalanced(root.Right)
    }
    return false
}

func dfs(root *TreeNode) float64 {
    if root == nil {
        return 0
    }
    return math.Max(dfs(root.Left), dfs(root.Right)) + 1
}

面试题04.05.合法二叉搜索树(5)

  • 题目
实现一个函数,检查一棵二叉树是否为二叉搜索树。
示例 1:输入:
    2
   / \
  1   3
输出: true
示例 2:输入:
    5
   / \
  1   4
     / \
    3   6
输出: false
解释: 输入为: [5,1,4,null,null,3,6]。根节点的值为 5 ,但是其右子节点值为 4 。
  • 解题思路
No. 思路 时间复杂度 空间复杂度
01 递归 O(n) O(log(n))
02 递归 O(n) O(n)
03 迭代 O(n) O(n)
04 迭代 O(n) O(n)
05 递归 O(n) O(log(n))
func isValidBST(root *TreeNode) bool {
    return dfs(root, math.MinInt64, math.MaxInt64)
}

func dfs(root *TreeNode, left, right int) bool {
    if root == nil {
        return true
    }
    if left >= root.Val || right <= root.Val {
        return false
    }
    return dfs(root.Left, left, root.Val) && dfs(root.Right, root.Val, right)
}

# 2
var res []int

func isValidBST(root *TreeNode) bool {
    res = make([]int, 0)
    dfs(root)
    for i := 0; i < len(res)-1; i++ {
        if res[i] >= res[i+1] {
            return false
        }
    }
    return true
}

func dfs(root *TreeNode) {
    if root != nil {
        dfs(root.Left)
        res = append(res, root.Val)
        dfs(root.Right)
    }
}

# 3
func isValidBST(root *TreeNode) bool {
    if root == nil {
        return true
    }
    stack := make([]*TreeNode, 0)
    res := make([]int, 0)
    for len(stack) > 0 || root != nil {
        for root != nil {
            stack = append(stack, root)
            root = root.Left
        }
        last := len(stack) - 1
        res = append(res, stack[last].Val)
        root = stack[last].Right
        stack = stack[:last]
    }
    for i := 0; i < len(res)-1; i++ {
        if res[i] >= res[i+1] {
            return false
        }
    }
    return true
}

# 4
func isValidBST(root *TreeNode) bool {
    if root == nil {
        return true
    }
    stack := make([]*TreeNode, 0)
    pre := math.MinInt64
    for len(stack) > 0 || root != nil {
        for root != nil {
            stack = append(stack, root)
            root = root.Left
        }
        last := len(stack) - 1
        if stack[last].Val <= pre {
            return false
        }
        pre = stack[last].Val
        root = stack[last].Right
        stack = stack[:last]
    }
    return true
}

# 5
var pre int

func isValidBST(root *TreeNode) bool {
    pre = math.MinInt64
    return dfs(root)
}

func dfs(root *TreeNode) bool {
    if root == nil {
        return true
    }
    if dfs(root.Left) == false {
        return false
    }
    if root.Val <= pre {
        return false
    }
    pre = root.Val
    return dfs(root.Right)
}

面试题04.06.后继者(3)

  • 题目
设计一个算法,找出二叉搜索树中指定节点的“下一个”节点(也即中序后继)。
如果指定节点没有对应的“下一个”节点,则返回null。
示例 1:输入: root = [2,1,3], p = 1
  2
 / \
1   3
输出: 2
示例 2:输入: root = [5,3,6,2,4,null,null,1], p = 6
      5
     / \
    3   6
   / \
  2   4
 /   
1
输出: null
  • 解题思路
No. 思路 时间复杂度 空间复杂度
01 递归 O(n) O(n)
02 递归 O(log(n)) O(log(n))
03 迭代 O(log(n)) O(1)
var res []*TreeNode

func inorderSuccessor(root *TreeNode, p *TreeNode) *TreeNode {
    res = make([]*TreeNode, 0)
    dfs(root)
    for i := 0; i < len(res)-1; i++ {
        if res[i] == p {
            return res[i+1]
        }
    }
    return nil
}

func dfs(root *TreeNode) {
    if root == nil {
        return
    }
    dfs(root.Left)
    res = append(res, root)
    dfs(root.Right)
}

# 2
func inorderSuccessor(root *TreeNode, p *TreeNode) *TreeNode {
    if root == nil {
        return nil
    }
    if p.Val >= root.Val {
        return inorderSuccessor(root.Right, p)
    }
    res := inorderSuccessor(root.Left, p)
    if res == nil {
        return root
    }
    return res
}

# 3
func inorderSuccessor(root *TreeNode, p *TreeNode) *TreeNode {
    var res *TreeNode
    cur := root
    for cur != nil {
        if p.Val >= cur.Val {
            cur = cur.Right
        } else {
            res = cur
            cur = cur.Left
        }
    }
    return res
}

面试题04.08.首个共同祖先(2)

  • 题目
设计并实现一个算法,找出二叉树中某两个节点的第一个共同祖先。不得将其他的节点存储在另外的数据结构中。
注意:这不一定是二叉搜索树。
例如,给定如下二叉树: root = [3,5,1,6,2,0,8,null,null,7,4]
    3
   / \
  5   1
 / \ / \
6  2 0  8
  / \
 7   4
示例 1:输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1输出: 3
解释: 节点 5 和节点 1 的最近公共祖先是节点 3。
示例 2:输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 4 输出: 5
解释: 节点 5 和节点 4 的最近公共祖先是节点 5。因为根据定义最近公共祖先节点可以为节点本身。
说明:所有节点的值都是唯一的。p、q 为不同节点且均存在于给定的二叉树中。
  • 解题思路
No. 思路 时间复杂度 空间复杂度
01 递归 O(n) O(log(n))
02 递归 O(n) O(n)
func lowestCommonAncestor(root *TreeNode, p *TreeNode, q *TreeNode) *TreeNode {
    if root == nil {
        return nil
    }
    if root.Val == p.Val || root.Val == q.Val {
        return root
    }
    left := lowestCommonAncestor(root.Left, p, q)
    right := lowestCommonAncestor(root.Right, p, q)
    if left != nil && right != nil {
        return root
    }
    if left == nil {
        return right
    }
    return left
}

# 2
func lowestCommonAncestor(root *TreeNode, p *TreeNode, q *TreeNode) *TreeNode {
    if root == nil {
        return nil
    }
    m = make(map[int]*TreeNode)
    dfs(root)
    visited := make(map[int]bool)
    for p != nil {
        visited[p.Val] = true
        p = m[p.Val]
    }
    for q != nil {
        if visited[q.Val] == true {
            return q
        }
        q = m[q.Val]
    }
    return nil
}

func dfs(root *TreeNode) {
    if root == nil {
        return
    }
    if root.Left != nil {
        m[root.Left.Val] = root
        dfs(root.Left)
    }
    if root.Right != nil {
        m[root.Right.Val] = root
        dfs(root.Right)
    }
}

面试题04.09.二叉搜索树序列(1)

  • 题目
从左向右遍历一个数组,通过不断将其中的元素插入树中可以逐步地生成一棵二叉搜索树。
给定一个由不同节点组成的二叉搜索树,输出所有可能生成此树的数组。
示例:给定如下二叉树
        2
       / \
      1   3
返回:[
   [2,1,3],
   [2,3,1]
]
  • 解题思路
No. 思路 时间复杂度 空间复杂度
01 递归 O(2^log(n)) O(2^log(n))
var res [][]int

func BSTSequences(root *TreeNode) [][]int {
    res = make([][]int, 0)
    if root == nil {
        res = append(res, []int{})
        return res
    }
    dfs(append([]*TreeNode{}, root), make([]int, 0))
    return res
}

func dfs(arr []*TreeNode, path []int) {
    if len(arr) == 0 {
        res = append(res, path)
    }
    for i, node := range arr {
        temp := make([]int, len(path))
        copy(temp, path)
        temp = append(temp, node.Val)
        tempNode := make([]*TreeNode, len(arr))
        copy(tempNode, arr)
        tempNode = append(tempNode[:i], tempNode[i+1:]...) // 去除当前用过的
        if node.Left != nil {
            tempNode = append(tempNode, node.Left)
        }
        if node.Right != nil {
            tempNode = append(tempNode, node.Right)
        }
        dfs(tempNode, temp)
    }
}

面试题04.10.检查子树(2)

  • 题目
检查子树。你有两棵非常大的二叉树:T1,有几万个节点;T2,有几万个节点。
设计一个算法,判断 T2 是否为 T1 的子树。
如果 T1 有这么一个节点 n,其子树与 T2 一模一样,则 T2 为 T1 的子树,
也就是说,从节点 n 处把树砍断,得到的树与 T2 完全相同。
示例1:输入:t1 = [1, 2, 3], t2 = [2] 输出:true
示例2:输入:t1 = [1, null, 2, 4], t2 = [3, 2] 输出:false
提示:树的节点数目范围为[0, 20000]。
  • 解题思路
No. 思路 时间复杂度 空间复杂度
01 递归 O(n^2) O(log(n))
02 递归+字符串辅助 O(n) O(log(n))
03 栈辅助(超时) O(n) O(n)
func checkSubTree(t1 *TreeNode, t2 *TreeNode) bool {
    if t1 == nil {
        return false
    }
    return isSame(t1, t2) || checkSubTree(t1.Left, t2) || checkSubTree(t1.Right, t2)
}

func isSame(s *TreeNode, t *TreeNode) bool {
    if s == nil || t == nil {
        return t == s
    }
    return isSame(s.Left, t.Left) && isSame(s.Right, t.Right) && s.Val == t.Val
}

# 2
func checkSubTree(t1 *TreeNode, t2 *TreeNode) bool {
    sStr := dfs(t1, "")
    tStr := dfs(t2, "")
    return strings.Contains(sStr, tStr)
}

func dfs(s *TreeNode, pre string) string {
    if s == nil {
        return pre
    }
    return fmt.Sprintf("#%d%s%s", s.Val, dfs(s.Left, "l"), dfs(s.Right, "r"))
}

# 3
func checkSubTree(t1 *TreeNode, t2 *TreeNode) bool {
    sStr := preOrder(t1)
    tStr := preOrder(t2)
    return strings.Contains(sStr, tStr)
}

func preOrder(root *TreeNode) string {
    if root == nil {
        return ""
    }
    res := "!"
    stack := make([]*TreeNode, 0)
    temp := root
    for {
        for temp != nil {
            res += strconv.Itoa(temp.Val)
            res += "!"
            stack = append(stack, temp)
            temp = temp.Left
        }
        res += "#!"
        if len(stack) > 0 {
            node := stack[len(stack)-1]
            stack = stack[:len(stack)-1]
            temp = node.Right
        } else {
            break
        }
    }
    return res
}

面试题04.12.求和路径(4)

  • 题目
给定一棵二叉树,其中每个节点都含有一个整数数值(该值或正或负)。设计一个算法,
打印节点数值总和等于某个给定值的所有路径的数量。
注意,路径不一定非得从二叉树的根节点或叶节点开始或结束,但是其方向必须向下(只能从父节点指向子节点方向)。
示例:给定如下二叉树,以及目标和 sum = 22,
              5
             / \
            4   8
           /   / \
          11  13  4
         /  \    / \
        7    2  5   1
返回:3
解释:和为 22 的路径有:[5,4,11,2], [5,8,4,5], [4,11,7]
提示:节点总数 <= 10000
  • 解题思路
No. 思路 时间复杂度 空间复杂度
01 递归 O(n^2) O(n)
02 递归 O(n^2) O(n)
03 迭代+递归 O(n^2) O(n)
04 递归+路径 O(n^2) O(n)
func pathSum(root *TreeNode, sum int) int {
    if root == nil {
        return 0
    }
    res := 0
    var dfs func(*TreeNode, int)
    dfs = func(node *TreeNode, sum int) {
        if node == nil {
            return
        }
        sum = sum - node.Val
        // 路径不需要从根节点开始,也不需要在叶子节点结束
        if sum == 0 {
            res++
        }
        dfs(node.Left, sum)
        dfs(node.Right, sum)
    }
    dfs(root, sum)
    return res + pathSum(root.Left, sum) + pathSum(root.Right, sum)
}

# 2
func dfs(node *TreeNode, sum int) int {
    if node == nil {
        return 0
    }
    sum = sum - node.Val
    res := 0
    if sum == 0 {
        res = 1
    }
    return res + dfs(node.Left, sum) + dfs(node.Right, sum)
}

func pathSum(root *TreeNode, sum int) int {
    if root == nil {
        return 0
    }
    return dfs(root, sum) + pathSum(root.Left, sum) + pathSum(root.Right, sum)
}

# 3
func pathSum(root *TreeNode, sum int) int {
    if root == nil {
        return 0
    }
    queue := make([]*TreeNode, 0)
    queue = append(queue, root)
    res := 0
    for len(queue) > 0 {
        node := queue[0]
        queue = queue[1:]
        tempSum := 0
        res += dfs(node, sum, tempSum)
        if node.Left != nil {
            queue = append(queue, node.Left)
        }
        if node.Right != nil {
            queue = append(queue, node.Right)
        }
    }
    return res
}

func dfs(node *TreeNode, sum int, curSum int) int {
    res := 0
    curSum = curSum + node.Val
    if curSum == sum {
        res++
    }
    if node.Left != nil {
        res += dfs(node.Left, sum, curSum)
    }
    if node.Right != nil {
        res += dfs(node.Right, sum, curSum)
    }
    return res
}

# 4
func pathSum(root *TreeNode, sum int) int {
    return dfs(root, sum, make([]int, 1001), 0)
}

func dfs(node *TreeNode, sum int, path []int, level int) int {
    if node == nil {
        return 0
    }
    res := 0
    if sum == node.Val {
        res = 1
    }
    temp := node.Val
    for i := level - 1; i >= 0; i-- {
        temp = temp + path[i]
        if temp == sum {
            res++
        }
    }
    path[level] = node.Val
    return res + dfs(node.Left, sum, path, level+1) +
        dfs(node.Right, sum, path, level+1)
}

面试题05.01.插入(4)

  • 题目
插入。给定两个32位的整数N与M,以及表示比特位置的i与j。
编写一种方法,将M插入N,使得M从N的第j位开始,到第i位结束。假定从j位到i位足以容纳M,也即若M = 10 011,
那么j和i之间至少可容纳5个位。例如,不可能出现j = 3和i = 2的情况,因为第3位和第2位之间放不下M。
示例1:输入:N = 1024(10000000000), M = 19(10011), i = 2, j = 6 输出:N = 1100(10001001100)
示例2:输入: N = 0, M = 31(11111), i = 0, j = 4 输出:N = 31(11111)
  • 解题思路
No. 思路 时间复杂度 空间复杂度
01 位运算 O(1) O(1)
02 位运算 O(1) O(1)
03 数组辅助 O(1) O(1)
04 位运算 O(1) O(1)
func insertBits(N int, M int, i int, j int) int {
    a := (N >> (j + 1)) << (j + 1)
    b := (N>>i)<<i ^ N
    c := M << i
    return a | b | c
}

# 2 
func insertBits(N int, M int, i int, j int) int {
    for k := i; k <= j; k++ {
        if N&(1<<k) != 0 {
            N = N - 1<<k
        }
    }
    N = N + (M << i)
    return N
}

# 3
func insertBits(N int, M int, i int, j int) int {
    arr := make([]byte, 32)
    for i := 0; i < 32; i++ {
        arr[i] = '0'
    }
    a := fmt.Sprintf("%b", N)
    b := fmt.Sprintf("%b", M)
    for k := len(a) - 1; k >= 0; k-- {
        arr[31-(len(a)-1-k)] = a[k]
    }
    count := 0
    for k := 31 - i; k >= 31-j; k-- {
        if count < len(b) {
            arr[k] = b[len(b)-1-count]
            count++
        } else {
            arr[k] = '0'
        }
    }
    value, _ := strconv.ParseInt(string(arr), 2, 64)
    return int(value)
}

# 4
func insertBits(N int, M int, i int, j int) int {
    res := N
    setZero := 0
    for k := i; k <= j; k++ {
        setZero = setZero | (1 << k)
    }
    res = res&setZero ^ N
    res = res | (M << i)
    return res
}

面试题05.02.二进制数转字符串(2)

  • 题目
二进制数转字符串。给定一个介于0和1之间的实数(如0.72),类型为double,打印它的二进制表达式。
如果该数字不在0和1之间,或者无法精确地用32位以内的二进制表示,则打印“ERROR”。
示例1:输入:0.625 输出:"0.101"
示例2:输入:0.1 输出:"ERROR"
提示:0.1无法被二进制准确表示
提示:32位包括输出中的"0."这两位。
  • 解题思路
No. 思路 时间复杂度 空间复杂度
01 遍历 O(1) O(1)
02 遍历 O(1) O(1)
func printBin(num float64) string {
    res := "0."
    for num != float64(0) {
        num = num * 2
        if num >= 1 {
            res = res + "1"
            num = num - 1.0
        } else {
            res = res + "0"
        }
        if len(res) > 32 {
            return "ERROR"
        }
    }
    return res
}

# 2
func printBin(num float64) string {
    res := "0."
    value := float64(1)
    for i := 1; i <= 32; i++ {
        value = value / 2
        if num < value {
            res = res + "0"
            continue
        }
        res = res + "1"
        num = num - value
        if num == 0 {
            return res
        }
    }
    return "ERROR"
}

面试题05.03.翻转数位(2)

  • 题目
给定一个32位整数 num,你可以将一个数位从0变为1。请编写一个程序,找出你能够获得的最长的一串1的长度。
示例 1:输入: num = 1775(110111011112)输出: 8
示例 2:输入: num = 7(01112)输出: 4
  • 解题思路
No. 思路 时间复杂度 空间复杂度
01 遍历 O(1) O(1)
02 数组辅助 O(1) O(1)
func reverseBits(num int) int {
    res := 0
    a, b := 0, 0
    for num != 0 {
        if num%2 == 1 {
            a++
        } else {
            b = a
            a = 0
        }
        res = max(res, a+b)
        num = num / 2
    }
    return res + 1
}

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

# 2
func reverseBits(num int) int {
    res := 0
    arr := make([]int, 0)
    count := 0
    for num != 0 {
        if num%2 == 1 {
            count++
        } else {
            arr = append(arr, count)
            count = 0
        }
        num = num / 2
    }
    arr = append(arr, count)
    if len(arr) == 1 {
        return arr[0] + 1
    }
    for i := 1; i < len(arr); i++ {
        res = max(res, arr[i]+arr[i-1])
    }
    return res + 1
}

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

面试题05.04.下一个数

题目

下一个数。给定一个正整数,找出与其二进制表达式中1的个数相同且大小最接近的那两个数(一个略大,一个略小)。
示例1:输入:num = 2(或者0b10) 输出:[4, 1] 或者([0b100, 0b1])
示例2:输入:num = 1输出:[2, -1]
提示:num的范围在[1, 2147483647]之间;
    如果找不到前一个或者后一个满足条件的正数,那么输出 -1。

解题思路

No. 思路 时间复杂度 空间复杂度
01 内置函数 O(1) O(1)


面试题05.06.整数转换(4)

  • 题目
整数转换。编写一个函数,确定需要改变几个位才能将整数A转成整数B。
示例1:输入:A = 29 (或者0b11101), B = 15(或者0b01111)输出:2
示例2:输入:A = 1,B = 2 输出:2
提示: A,B范围在[-2147483648, 2147483647]之间
  • 解题思路
No. 思路 时间复杂度 空间复杂度
01 内置函数 O(1) O(1)
02 位运算 O(1) O(1)
03 位运算 O(1) O(1)
04 位运算 O(1) O(1)
func convertInteger(A int, B int) int {
    C := uint32(A) ^ uint32(B)
    return bits.OnesCount(uint(C))
}

# 2
func convertInteger(A int, B int) int {
    C := uint32(A) ^ uint32(B)
    res := 0
    for C != 0 {
        if C&1 == 1 {
            res++
        }
        C = C >> 1
    }
    return res
}

# 3
func convertInteger(A int, B int) int {
    C := uint32(A) ^ uint32(B)
    res := 0
    for C != 0 {
        res++
        C = C & (C - 1)
    }
    return res
}

# 4
func convertInteger(A int, B int) int {
    C := A ^ B
    res := 0
    for i := 0; i < 32; i++{
        if C & 1 ==1{
            res++
        }
        C = C >> 1
    }
    return res
}

面试题05.07.配对交换(2)

  • 题目
配对交换。编写程序,交换某个整数的奇数位和偶数位,尽量使用较少的指令
(也就是说,位0与位1交换,位2与位3交换,以此类推)。
示例1:输入:num = 2(或者0b10)输出 1 (或者 0b01)
示例2:输入:num = 3 输出:3
提示:num的范围在[0, 2^30 - 1]之间,不会发生整数溢出。
  • 解题思路
No. 思路 时间复杂度 空间复杂度
01 位运算 O(1) O(1)
02 数组辅助 O(1) O(1)
func exchangeBits(num int) int {
    // 0x55555555 = 01010101010101010101010101010101 提取偶数位=>左移
    // 0xaaaaaaaa = 10101010101010101010101010101010 提取奇数位=>右移
    a := (num & 0x55555555) << 1
    b := (num & 0xaaaaaaaa) >> 1
    return a | b
}

# 2
func exchangeBits(num int) int {
    a := fmt.Sprintf("%b", num)
    arr := make([]byte, 32)
    for i := 0; i < 32; i++ {
        arr[i] = '0'
    }
    count := 31
    for i := len(a) - 1; i >= 0; i-- {
        arr[count] = a[i]
        count--
    }
    for i := len(arr) - 2; i >= 0; i = i - 2 {
        arr[i], arr[i+1] = arr[i+1], arr[i]
    }
    value, _ := strconv.ParseInt(string(arr), 2, 64)
    return int(value)
}

面试题05.08.绘制直线(2)

  • 题目
绘制直线。
有个单色屏幕存储在一个一维数组中,使得32个连续像素可以存放在一个 int 里。
屏幕宽度为w,且w可被32整除(即一个 int 不会分布在两行上),屏幕高度可由数组长度及屏幕宽度推算得出。
请实现一个函数,绘制从点(x1, y)到点(x2, y)的水平线。
给出数组的长度 length,宽度 w(以比特为单位)、直线开始位置 x1(比特为单位)、直线结束位置 x2(比特为单位)、直线所在行数y。
返回绘制过后的数组。
示例1:输入:length = 1, w = 32, x1 = 30, x2 = 31, y = 0 输出:[3]
说明:在第0行的第30位到第31为画一条直线,屏幕表示为[0b000000000000000000000000000000011]
示例2:输入:length = 3, w = 96, x1 = 0, x2 = 95, y = 0 输出:[-1, -1, -1]
  • 解题思路
No. 思路 时间复杂度 空间复杂度
01 遍历 O(n) O(n)
02 遍历 O(n) O(n)
func drawLine(length int, w int, x1 int, x2 int, y int) []int {
    res := make([]int, length)
    for i := 0; i < length; i++ {
        start := i * 32
        var value int32
        for j := 0; j < 32; j++ {
            if x1+y*w <= start+j && start+j <= x2+y*w {
                value = value ^ (1 << (31 - j)) // 画线:把第31-j位变为1
            }
        }
        res[i] = int(value)
    }
    return res
}

# 2
func drawLine(length int, w int, x1 int, x2 int, y int) []int {
    arr := make([]int32, length)
    width := w / 32
    for i := x1; i <= x2; i++ {
        index := width*y + (i / 32)
        arr[index] = arr[index] ^ (1 << (31 - (i % 32)))
    }
    res := make([]int, length)
    for i := 0; i < length; i++ {
        res[i] = int(arr[i])
    }
    return res
}

面试题08.01.三步问题(2)

  • 题目
三步问题。有个小孩正在上楼梯,楼梯有n阶台阶,小孩一次可以上1阶、2阶或3阶。
实现一种方法,计算小孩有多少种上楼梯的方式。结果可能很大,你需要对结果模1000000007。
示例1:输入:n = 3 输出:4
说明: 有四种走法
示例2:输入:n = 5输出:13
提示:n范围在[1, 1000000]之间
  • 解题思路
No. 思路 时间复杂度 空间复杂度
01 动态规划 O(n) O(1)
02 动态规划 O(n) O(n)
func waysToStep(n int) int {
    if n == 1 {
        return 1
    }
    if n == 2 {
        return 2
    }
    if n == 3 {
        return 4
    }
    a, b, c := 1, 2, 4
    for i := 4; i <= n; i++ {
        a, b, c = b, c, (a+b+c)%1000000007
    }
    return c
}

# 2
func waysToStep(n int) int {
    dp := make([]int, n+3)
    dp[0] = 1
    dp[1] = 2
    dp[2] = 4
    for i := 3; i < n; i++ {
        dp[i] = (dp[i-1] + dp[i-2] + dp[i-3]) % 1000000007
    }
    return dp[n-1]
}

面试题08.02.迷路的机器人(2)

  • 题目
设想有个机器人坐在一个网格的左上角,网格 r 行 c 列。
机器人只能向下或向右移动,但不能走到一些被禁止的网格(有障碍物)。
设计一种算法,寻找机器人从左上角移动到右下角的路径。
网格中的障碍物和空位置分别用 1 和 0 来表示。
返回一条可行的路径,路径由经过的网格的行号和列号组成。左上角为 0 行 0 列。
如果没有可行的路径,返回空数组。
示例 1:输入:
[
  [0,0,0],
  [0,1,0],
  [0,0,0]
]
输出: [[0,0],[0,1],[0,2],[1,2],[2,2]]
解释: 输入中标粗的位置即为输出表示的路径,即
0行0列(左上角) -> 0行1列 -> 0行2列 -> 1行2列 -> 2行2列(右下角)
说明:r 和 c 的值均不超过 100。
  • 解题思路
No. 思路 时间复杂度 空间复杂度
01 深度优先搜索 O(n^2) O(n)
02 动态规划 O(n^2) O(1)
var res [][]int

func pathWithObstacles(obstacleGrid [][]int) [][]int {
    res = make([][]int, 0)
    path := make([][]int, 0)
    path = append(path, []int{0, 0})
    dfs(obstacleGrid, path)
    return res
}

func dfs(arr [][]int, path [][]int) {
    if len(res) == 0 {
        x, y := path[len(path)-1][0], path[len(path)-1][1]
        if arr[x][y] == 0 {
            arr[x][y] = 1
            if x < len(arr)-1 {
                dfs(arr, append(path, []int{x + 1, y}))
            }
            if y < len(arr[0])-1 {
                dfs(arr, append(path, []int{x, y + 1}))
            }
            if x == len(arr)-1 && y == len(arr[0])-1 {
                res = make([][]int, len(path))
                copy(res, path)
            }
        }
    }
}

# 2
func pathWithObstacles(obstacleGrid [][]int) [][]int {
    res := make([][]int, 0)
    n := len(obstacleGrid)
    m := len(obstacleGrid[0])
    if obstacleGrid[0][0] == 1 || obstacleGrid[n-1][m-1] == 1 {
        return res
    }
    for i := 0; i < n; i++ {
        for j := 0; j < m; j++ {
            if obstacleGrid[i][j] == 1 {
                obstacleGrid[i][j] = 0
                continue
            }
            if i == 0 && j == 0 {
                obstacleGrid[i][j] = 1
            } else if i == 0 {
                obstacleGrid[i][j] = obstacleGrid[i][j-1] + 1
            } else if j == 0 {
                obstacleGrid[i][j] = obstacleGrid[i-1][j] + 1
            } else {
                obstacleGrid[i][j] = max(obstacleGrid[i][j-1], obstacleGrid[i-1][j]) + 1
            }
        }
    }
    total := n + m - 1
    if obstacleGrid[n-1][m-1] != total {
        return res
    }
    i, j := n-1, m-1
    for i >= 0 && j >= 0 {
        if obstacleGrid[i][j] == total {
            newArr := make([][]int, 0)
            newArr = append(newArr, []int{i, j})
            res = append(newArr, res...)
            total = total - 1
        }
        if i == 0 && j == 0 {
            break
        }
        if i == 0 && obstacleGrid[i][j-1] == total {
            j--
        } else if j == 0 && obstacleGrid[i-1][j] == total {
            i--
        } else if obstacleGrid[i-1][j] == total {
            i--
        } else if obstacleGrid[i][j-1] == total {
            j--
        }
    }
    return res
}

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

面试题08.03.魔术索引(2)

  • 题目
魔术索引。 在数组A[0...n-1]中,有所谓的魔术索引,满足条件A[i] = i。
给定一个有序整数数组,编写一种方法找出魔术索引,若有的话,在数组A中找出一个魔术索引,如果没有,则返回-1。
若有多个魔术索引,返回索引值最小的一个。
示例1:输入:nums = [0, 2, 3, 4, 5] 输出:0 说明: 0下标的元素为0
示例2:输入:nums = [1, 1, 1] 输出:1
说明:
    nums长度在[1, 1000000]之间
    此题为原书中的 Follow-up,即数组中可能包含重复元素的版本
  • 解题思路
No. 思路 时间复杂度 空间复杂度
01 遍历 O(n) O(1)
02 递归 O(n) O(n)
func findMagicIndex(nums []int) int {
    for i := 0; i < len(nums); i++{
        if nums[i] == i{
            return i
        }
    }
    return -1
}

# 2
func findMagicIndex(nums []int) int {
    return search(nums, 0, len(nums)-1)
}

func search(nums []int, left, right int) int {
    if left > right {
        return -1
    }
    mid := left + (right-left)/2
    res := search(nums, left, mid-1)
    if res != -1 {
        return res
    } else if nums[mid] == mid {
        return mid
    }
    return search(nums, mid+1, right)
}

面试题08.04.幂集(3)

  • 题目
幂集。编写一种方法,返回某集合的所有子集。集合中不包含重复的元素。
说明:解集不能包含重复的子集。
示例:输入: nums = [1,2,3]输出:
[
  [3],
  [1],
  [2],
  [1,2,3],
  [1,3],
  [2,3],
  [1,2],
  []
]
  • 解题思路
No. 思路 时间复杂度 空间复杂度
01 回溯 O(n*2^n) O(n*2^n)
02 迭代 O(n*2^n) O(n*2^n)
03 位运算 O(n*2^n) O(n*2^n)
var res [][]int

func subsets(nums []int) [][]int {
    res = make([][]int, 0)
    dfs(nums, make([]int, 0), 0)
    return res
}

func dfs(nums []int, arr []int, level int) {
    temp := make([]int, len(arr))
    copy(temp, arr)
    res = append(res, temp)
    for i := level; i < len(nums); i++ {
        // dfs(nums, append(arr, nums[i]), i+1)
        arr = append(arr, nums[i])
        dfs(nums, arr, i+1)
        arr = arr[:len(arr)-1]
    }
}

# 2
func subsets(nums []int) [][]int {
    res := make([][]int, 0)
    res = append(res, []int{})
    for i := 0; i < len(nums); i++ {
        temp := make([][]int, len(res))
        for key, value := range res {
            value = append(value, nums[i])
            temp[key] = append(temp[key], value...)
        }
        for _, v := range temp {
            res = append(res, v)
        }
    }
    return res
}

# 3
func subsets(nums []int) [][]int {
    res := make([][]int, 0)
    n := len(nums)
    left := 1 << n
    right := 1 << (n + 1)
    for i := left; i < right; i++ {
        temp := make([]int, 0)
        for j := 0; j < n; j++ {
            if i&(1<<j) != 0 {
                temp = append(temp, nums[j])
            }
        }
        res = append(res, temp)
    }
    return res
}

面试题08.05.递归乘法(3)

  • 题目
递归乘法。 写一个递归函数,不使用 * 运算符, 实现两个正整数的相乘。可以使用加号、减号、位移,但要吝啬一些。
示例1:输入:A = 1, B = 10 输出:10
示例2:输入:A = 3, B = 4 输出:12
提示:保证乘法范围不会溢出
  • 解题思路
No. 思路 时间复杂度 空间复杂度
01 递归 O(n) O(n)
02 递归 O(log(n)) O(log(n))
03 迭代 O(log(n)) O(1)
func multiply(A int, B int) int {
    if B == 0 {
        return 0
    }
    return multiply(A, B-1) + A
}

# 2
func multiply(A int, B int) int {
    if B == 0 {
        return 0
    }
    if B == 1 {
        return A
    }
    if B%2 == 1 {
        return multiply(A<<1, B>>1) + A
    }
    return multiply(A<<1, B>>1)
}

# 3
func multiply(A int, B int) int {
    res := 0
    for B != 0{
        if B % 2==1{
            res = res + A
        }
        A = A+A
        B = B >> 1
    }
    return res
}

面试题08.06.汉诺塔问题(1)

  • 题目
在经典汉诺塔问题中,有 3 根柱子及 N 个不同大小的穿孔圆盘,盘子可以滑入任意一根柱子。
一开始,所有盘子自上而下按升序依次套在第一根柱子上(即每一个盘子只能放在更大的盘子上面)。
移动圆盘时受到以下限制:
(1) 每次只能移动一个盘子;
(2) 盘子只能从柱子顶端滑出移到下一根柱子;
(3) 盘子只能叠在比它大的盘子上。
请编写程序,用栈将所有盘子从第一根柱子移到最后一根柱子。
你需要原地修改栈。
示例1:输入:A = [2, 1, 0], B = [], C = [] 输出:C = [2, 1, 0]
示例2:输入:A = [1, 0], B = [], C = [] 输出:C = [1, 0]
提示:A中盘子的数目不大于14个。
  • 解题思路
No. 思路 时间复杂度 空间复杂度
01 递归 O(2^n) O(n)
func hanota(A []int, B []int, C []int) []int {
    if A == nil {
        return nil
    }
    move(len(A), &A, &B, &C)
    return C
}

func move(num int, A, B, C *[]int) {
    if num < 0 {
        return
    }
    if num == 1 {
        *C = append(*C, (*A)[len(*A)-1])
        *A = (*A)[:len(*A)-1]
        return
    }
    move(num-1, A, C, B)
    move(1, A, B, C)
    move(num-1, B, A, C)
}

面试题08.07.无重复字符串的排列组合(3)

  • 题目
无重复字符串的排列组合。编写一种方法,计算某字符串的所有排列组合,字符串每个字符均不相同。
示例1:输入:S = "qwe"输出:["qwe", "qew", "wqe", "weq", "ewq", "eqw"]
示例2:输入:S = "ab"输出:["ab", "ba"]
提示:字符都是英文字母。
    字符串长度在[1, 9]之间。
  • 解题思路
No. 思路 时间复杂度 空间复杂度
01 回溯 O(n^n) O(n*n!)
02 递归 O(n!) O(n*n!)
03 回溯 O(n!) O(n*n!)
var res []string

func permutation(S string) []string {
    res = make([]string, 0)
    nums := []byte(S)
    visited := make(map[int]bool)
    dfs(nums, 0, "", visited)
    return res
}

func dfs(nums []byte, index int, str string, visited map[int]bool) {
    if index == len(nums) {
        res = append(res, str)
        return
    }
    for i := 0; i < len(nums); i++ {
        if visited[i] == false {
            str = str + string(nums[i])
            visited[i] = true
            dfs(nums, index+1, str, visited)
            str = str[:len(str)-1]
            visited[i] = false
        }
    }
}

# 2
func permutation(S string) []string {
    if len(S) == 1 {
        return []string{S}
    }
    res := make([]string, 0)
    for i := 0; i < len(S); i++ {
        str := S[:i] + S[i+1:]
        arr := permutation(str)
        for _, v := range arr {
            res = append(res, v+string(S[i]))
        }
    }
    return res
}

# 3
var res []string

func permutation(S string) []string {
    res = make([]string, 0)
    nums := []byte(S)
    dfs(nums, 0, "")
    return res
}

func dfs(nums []byte, index int, str string) {
    if index == len(nums) {
        res = append(res, str)
        return
    }
    for i := index; i < len(nums); i++ {
        str = str + string(nums[i])
        nums[i], nums[index] = nums[index], nums[i]
        dfs(nums, index+1, str)
        nums[i], nums[index] = nums[index], nums[i]
        str = str[:len(str)-1]
    }
}

面试题08.08.有重复字符串的排列组合(3)

  • 题目
有重复字符串的排列组合。编写一种方法,计算某字符串的所有排列组合。
示例1:输入:S = "qqe" 输出:["eqq","qeq","qqe"]
示例2:输入:S = "ab"输出:["ab", "ba"]
提示:
    字符都是英文字母。
    字符串长度在[1, 9]之间。
  • 解题思路
No. 思路 时间复杂度 空间复杂度
01 回溯 O(n!) O(n*n!)
02 回溯 O(n!) O(n*n!)
03 回溯 O(n!) O(n*n!)
var res []string

func permutation(S string) []string {
    res = make([]string, 0)
    nums := []byte(S)
    sort.Slice(nums, func(i, j int) bool {
        return nums[i] < nums[j]
    })
    dfs(nums, 0, make([]int, len(nums)), "")
    return res
}

func dfs(nums []byte, index int, visited []int, str string) {
    if len(nums) == index {
        res = append(res, str)
        return
    }
    for i := 0; i < len(nums); i++ {
        if visited[i] == 1 {
            continue
        }
        if i > 0 && nums[i] == nums[i-1] && visited[i-1] == 0 {
            continue
        }
        str = str + string(nums[i])
        visited[i] = 1
        dfs(nums, index+1, visited, str)
        visited[i] = 0
        str = str[:len(str)-1]
    }
}

# 2
var res []string

func permutation(S string) []string {
    res = make([]string, 0)
    nums := []byte(S)
    sort.Slice(nums, func(i, j int) bool {
        return nums[i] < nums[j]
    })
    dfs(nums, 0)
    return res
}

func dfs(nums []byte, index int) {
    if index == len(nums) {
        res = append(res, string(nums))
        return
    }
    m := make(map[byte]int)
    for i := index; i < len(nums); i++ {
        if _, ok := m[nums[i]]; ok {
            continue
        }
        m[nums[i]] = 1
        nums[i], nums[index] = nums[index], nums[i]
        dfs(nums, index+1)
        nums[i], nums[index] = nums[index], nums[i]
    }
}

# 3
var res []string

func permutation(S string) []string {
    res = make([]string, 0)
    nums := []byte(S)
    sort.Slice(nums, func(i, j int) bool {
        return nums[i] < nums[j]
    })
    dfs(nums, "")
    return res
}

func dfs(nums []byte, str string) {
    if len(nums) == 0 {
        res = append(res, str)
        return
    }
    for i := 0; i < len(nums); i++ {
        if i > 0 && nums[i] == nums[i-1] {
            continue
        }
        str = str + string(nums[i])
        arr := append([]byte{}, nums[:i]...)
        arr = append(arr, nums[i+1:]...)
        dfs(arr, str)
        str = str[:len(str)-1]
    }
}

面试题08.09.括号(3)

  • 题目
括号。设计一种算法,打印n对括号的所有合法的(例如,开闭一一对应)组合。
说明:解集不能包含重复的子集。
例如,给出 n = 3,生成结果为:
[
  "((()))",
  "(()())",
  "(())()",
  "()(())",
  "()()()"
]
  • 解题思路
No. 思路 时间复杂度 空间复杂度
01 全排列-递归 O(4^n/n^(1/2)) O(4^n/n^(1/2))
02 动态规划 O(4^n/n^(1/2)) O(4^n/n^(1/2))
03 广度优先搜索 O(4^n/n^(1/2)) O(4^n/n^(1/2))
var res []string

func generateParenthesis(n int) []string {
    res = make([]string, 0)
    dfs(0, 0, n, "")
    return res
}

func dfs(left, right, max int, str string) {
    if left == right && left == max {
        res = append(res, str)
        return
    }
    if left < max {
        dfs(left+1, right, max, str+"(")
    }
    if right < left {
        dfs(left, right+1, max, str+")")
    }
}

# 2
/*
dp[i]表示n=i时括号的组合
dp[i]="(" + dp[j] + ")"+dp[i-j-1] (j<i)
dp[0] = ""
*/
func generateParenthesis(n int) []string {
    dp := make([][]string, n+1)
    dp[0] = make([]string, 0)
    if n == 0 {
        return dp[0]
    }
    dp[0] = append(dp[0], "")
    for i := 1; i <= n; i++ {
        dp[i] = make([]string, 0)
        for j := 0; j < i; j++ {
            for _, a := range dp[j] {
                for _, b := range dp[i-j-1] {
                    str := "(" + a + ")" + b
                    dp[i] = append(dp[i], str)
                }
            }
        }
    }
    return dp[n]
}

# 3
type Node struct {
    str   string
    left  int
    right int
}

func generateParenthesis(n int) []string {
    res := make([]string, 0)
    if n == 0 {
        return res
    }
    queue := make([]*Node, 0)
    queue = append(queue, &Node{
        str:   "",
        left:  n,
        right: n,
    })
    for len(queue) > 0 {
        node := queue[0]
        queue = queue[1:]
        if node.left == 0 && node.right == 0 {
            res = append(res, node.str)
        }
        if node.left > 0 {
            queue = append(queue, &Node{
                str:   node.str + "(",
                left:  node.left - 1,
                right: node.right,
            })
        }
        if node.right > 0 && node.left < node.right {
            queue = append(queue, &Node{
                str:   node.str + ")",
                left:  node.left,
                right: node.right - 1,
            })
        }
    }
    return res
}

面试题08.10.颜色填充(2)

  • 题目
编写函数,实现许多图片编辑软件都支持的「颜色填充」功能。
待填充的图像用二维数组 image 表示,元素为初始颜色值。初始坐标点的横坐标为 sr 纵坐标为 sc。
需要填充的新颜色为 newColor 。
「周围区域」是指颜色相同且在上、下、左、右四个方向上存在相连情况的若干元素。
请用新颜色填充初始坐标点的周围区域,并返回填充后的图像。
示例:输入: image = [[1,1,1],[1,1,0],[1,0,1]]  sr = 1, sc = 1, newColor = 2
输出:[[2,2,2],[2,2,0],[2,0,1]]
解释: 初始坐标点位于图像的正中间,坐标 (sr,sc)=(1,1) 。
初始坐标点周围区域上所有符合条件的像素点的颜色都被更改成 2 。
注意,右下角的像素没有更改为 2 ,因为它不属于初始坐标点的周围区域。
提示:
    image 和 image[0] 的长度均在范围 [1, 50] 内。
    初始坐标点 (sr,sc) 满足 0 <= sr < image.length 和 0 <= sc < image[0].length 。
    image[i][j] 和 newColor 表示的颜色值在范围 [0, 65535] 内。
  • 解题思路
No. 思路 时间复杂度 空间复杂度
01 广度优先搜索 O(n^2) O(n^2)
02 深度优先搜索 O(n^2) O(n^2)
var dx = []int{-1, 1, 0, 0}
var dy = []int{0, 0, -1, 1}

func floodFill(image [][]int, sr int, sc int, newColor int) [][]int {
    oldColor := image[sr][sc]
    if oldColor == newColor {
        return image
    }
    m, n := len(image), len(image[0])
    list := make([][]int, 1)
    list[0] = []int{sr, sc}

    for len(list) > 0 {
        node := list[0]
        list = list[1:]
        image[node[0]][node[1]] = newColor
        for i := 0; i < 4; i++ {
            x := node[0] + dx[i]
            y := node[1] + dy[i]
            if 0 <= x && x < m && 0 <= y && y < n &&
                image[x][y] == oldColor {
                list = append(list, []int{x, y})
            }
        }
    }
    return image
}

# 2
var dx = []int{-1, 1, 0, 0}
var dy = []int{0, 0, -1, 1}

func floodFill(image [][]int, sr int, sc int, newColor int) [][]int {
    if sr < 0 || sc < 0 || sr >= len(image) ||
        sc >= len(image[sr]) || image[sr][sc] == newColor {
        return image
    }
    oldColor := image[sr][sc]
    image[sr][sc] = newColor
    for i := 0; i < 4; i++ {
        x := sr + dx[i]
        y := sc + dy[i]
        if 0 <= x && x < len(image) && 0 <= y && y < len(image[x]) &&
            image[x][y] == oldColor {
            floodFill(image, x, y, newColor)
        }
    }
    return image
}

面试题08.11.硬币(2)

  • 题目
硬币。给定数量不限的硬币,币值为25分、10分、5分和1分,编写代码计算n分有几种表示法。
(结果可能会很大,你需要将结果模上1000000007)
示例1:输入: n = 5 输出:2 解释: 有两种方式可以凑成总金额:
5=5
5=1+1+1+1+1
示例2:输入: n = 10 输出:4 解释: 有四种方式可以凑成总金额:
10=10
10=5+5
10=5+1+1+1+1+1
10=1+1+1+1+1+1+1+1+1+1
说明:注意: 你可以假设: 0 <= n (总金额) <= 1000000
  • 解题思路
No. 思路 时间复杂度 空间复杂度
01 动态规划 O(n) O(n)
02 动态规划 O(n) O(n)
func waysToChange(n int) int {
    coins := []int{1, 5, 10, 25}
    dp := make([][]int, 5)
    for i := 0; i <= 4; i++ {
        dp[i] = make([]int, n+1)
        dp[i][0] = 1 // 金额为0的情况,只有都不选,组合情况为1
    }
    for i := 1; i <= 4; i++ {
        for j := 1; j <= n; j++ {
            if j-coins[i-1] >= 0 {
                dp[i][j] = dp[i-1][j] + dp[i][j-coins[i-1]]
            } else {
                dp[i][j] = dp[i-1][j]
            }
        }
    }
    return dp[4][n] % 1000000007
}

# 2
func waysToChange(n int) int {
    coins := []int{1, 5, 10, 25}
    dp := make([]int, n+1)
    dp[0] = 1
    for i := 1; i <= 4; i++ {
        for j := 1; j <= n; j++ {
            if j-coins[i-1] >= 0 {
                dp[j] = dp[j] + dp[j-coins[i-1]]
            }
        }
    }
    return dp[n] % 1000000007
}

面试题08.12.八皇后(3)

  • 题目
设计一种算法,打印 N 皇后在 N × N 棋盘上的各种摆法,其中每个皇后都不同行、不同列,也不在对角线上。
这里的“对角线”指的是所有的对角线,不只是平分整个棋盘的那两条对角线。
注意:本题相对原题做了扩展
示例:输入:4 输出:[[".Q..","...Q","Q...","..Q."],["..Q.","Q...","...Q",".Q.."]]
解释: 4皇后问题存在如下两个不同的解法。
[
 [".Q..",  // 解法 1
  "...Q",
  "Q...",
  "..Q."],
 ["..Q.",  // 解法 2
  "Q...",
  "...Q",
  ".Q.."]
]
  • 解题思路
No. 思路 时间复杂度 空间复杂度
01 回溯 O(n^n) O(n^2)
02 回溯 O(n^n) O(n^2)
03 回溯 O(n^n) O(n^2)
var res [][]string

func solveNQueens(n int) [][]string {
    res = make([][]string, 0)
    // 初始化棋盘
    arr := make([][]string, n)
    for i := 0; i < n; i++ {
        arr[i] = make([]string, n)
        for j := 0; j < n; j++ {
            arr[i][j] = "."
        }
    }
    // 从第1行开始,上层是满足条件
    dfs(arr, 0)
    return res
}

func dfs(arr [][]string, row int) {
    if len(arr) == row {
        temp := make([]string, 0)
        for i := 0; i < len(arr); i++ {
            str := ""
            for j := 0; j < len(arr[i]); j++ {
                str = str + arr[i][j]
            }
            temp = append(temp, str)
        }
        res = append(res, temp)
        return
    }
    // 每列尝试
    for col := 0; col < len(arr[0]); col++ {
        if valid(arr, row, col) == false {
            continue
        }
        arr[row][col] = "Q"
        dfs(arr, row+1)
        arr[row][col] = "."
    }
}

func valid(arr [][]string, row, col int) bool {
    n := len(arr)
    // 当前列判断(竖着)
    for row := 0; row < n; row++ {
        if arr[row][col] == "Q" {
            return false
        }
    }
    // 左上角
    for row, col := row-1, col-1; row >= 0 && col >= 0; row, col = row-1, col-1 {
        if arr[row][col] == "Q" {
            return false
        }
    }
    // 右上角
    for row, col := row-1, col+1; row >= 0 && col < n; row, col = row-1, col+1 {
        if arr[row][col] == "Q" {
            return false
        }
    }
    return true
}

# 2
var res [][]string
var rows, left, right []bool

func solveNQueens(n int) [][]string {
    res = make([][]string, 0)
    rows, left, right = make([]bool, n), make([]bool, 2*n-1), make([]bool, 2*n-1)
    // 初始化棋盘
    arr := make([][]string, n)
    for i := 0; i < n; i++ {
        arr[i] = make([]string, n)
        for j := 0; j < n; j++ {
            arr[i][j] = "."
        }
    }
    // 从第1行开始,上层是满足条件
    dfs(arr, 0)
    return res
}

func dfs(arr [][]string, row int) {
    n := len(arr)
    if len(arr) == row {
        temp := make([]string, 0)
        for i := 0; i < n; i++ {
            str := ""
            for j := 0; j < n; j++ {
                str = str + arr[i][j]
            }
            temp = append(temp, str)
        }
        res = append(res, temp)
        return
    }
    // 每列尝试
    for col := 0; col < n; col++ {
        if rows[col] == true || left[row-col+n-1] == true || right[row+col] == true {
            continue
        }
        rows[col], left[row-col+n-1], right[row+col] = true, true, true
        arr[row][col] = "Q"
        dfs(arr, row+1)
        arr[row][col] = "."
        rows[col], left[row-col+n-1], right[row+col] = false, false, false
    }
}

# 3
var res [][]string

func solveNQueens(n int) [][]string {
    res = make([][]string, 0)
    // 初始化棋盘
    arr := make([][]string, n)
    for i := 0; i < n; i++ {
        arr[i] = make([]string, n)
        for j := 0; j < n; j++ {
            arr[i][j] = "."
        }
    }
    // 从第1行开始,上层是满足条件
    dfs(arr, 0, 0, 0, 0)
    return res
}

func dfs(arr [][]string, row int, rows, left, right int) {
    n := len(arr)
    if len(arr) == row {
        temp := make([]string, 0)
        for i := 0; i < n; i++ {
            str := ""
            for j := 0; j < n; j++ {
                str = str + arr[i][j]
            }
            temp = append(temp, str)
        }
        res = append(res, temp)
        return
    }
    // 每列尝试
    for col := 0; col < n; col++ {
        a := uint(col)
        b := uint(row - col + n - 1)
        c := uint(row + col)
        if ((rows>>a)&1) != 0 || ((left>>b)&1) != 0 || ((right>>c)&1) != 0 {
            continue
        }
        arr[row][col] = "Q"
        dfs(arr, row+1, rows^(1<<a), left^(1<<b), right^(1<<c))
        arr[row][col] = "."
    }
}

面试题08.13.堆箱子(1)

  • 题目
堆箱子。给你一堆n个箱子,箱子宽 wi、深 di、高 hi。
箱子不能翻转,将箱子堆起来时,下面箱子的宽度、高度和深度必须大于上面的箱子。
实现一种方法,搭出最高的一堆箱子。箱堆的高度为每个箱子高度的总和。
输入使用数组[wi, di, hi]表示每个箱子。
示例1:输入:box = [[1, 1, 1], [2, 2, 2], [3, 3, 3]]输出:6
示例2:输入:box = [[1, 1, 1], [2, 3, 4], [2, 6, 7], [3, 4, 5]] 输出:10
提示:箱子的数目不大于3000个。
  • 解题思路
No. 思路 时间复杂度 空间复杂度
01 排序+动态规划 O(n^2) O(n)
func pileBox(box [][]int) int {
    sort.Slice(box, func(i, j int) bool {
        if box[i][0] == box[j][0] {
            if box[i][1] == box[j][1] {
                return box[i][2] < box[j][2]
            }
            return box[i][1] < box[j][1]
        }
        return box[i][0] < box[j][0]
    })
    n, res := len(box), 0
    dp := make([]int, n)
    for i := 0; i < n; i++ {
        dp[i] = box[i][2]
    }
    for i := 0; i < n; i++ {
        for j := 0; j < i; j++ {
            if box[j][0] < box[i][0] && box[j][1] < box[i][1] && box[j][2] < box[i][2] {
                dp[i] = max(dp[i], dp[j]+box[i][2])
            }
        }
        res = max(res, dp[i])
    }
    return res
}

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

面试题08.14.布尔运算(3)

  • 题目
给定一个布尔表达式和一个期望的布尔结果 result,布尔表达式由 0 (false)、1 (true)、& (AND)、
| (OR) 和 ^ (XOR) 符号组成。实现一个函数,算出有几种可使该表达式得出 result 值的括号方法。
示例 1:输入: s = "1^0|0|1", result = 0  输出: 2
解释:两种可能的括号方法是
1^(0|(0|1))
1^((0|0)|1)
示例 2:输入: s = "0&0&0&1^1|0", result = 1 输出: 10
提示:运算符的数量不超过 19 个
  • 解题思路
No. 思路 时间复杂度 空间复杂度
01 动态规划 O(n^3) O(n^2)
02 动态规划 O(n^3) O(n^2)
03 动态规划 O(n^3) O(n^2)
func countEval(s string, result int) int {
    n := len(s)
    // dp[i][j][0/1] => s[i:j+1]结果为0/1的方法数
    dp := make([][][2]int, n)
    for i := 0; i < n; i++ {
        dp[i] = make([][2]int, n)
    }
    for i := n - 1; i >= 0; i = i - 2 {
        for j := i; j < n; j = j + 2 {
            if i == j {
                if s[i] == '0' {
                    dp[i][j][0]++
                } else {
                    dp[i][j][1]++
                }
                continue
            }
            for k := i + 1; k < j; k = k + 2 { // 枚举操作符
                for a := 0; a <= 1; a++ {
                    for b := 0; b <= 1; b++ {
                        if getValue(a, b, s[k]) == 0 {
                            dp[i][j][0] = dp[i][j][0] +
                                dp[i][k-1][a]*dp[k+1][j][b]
                        } else {
                            dp[i][j][1] = dp[i][j][01] +
                                dp[i][k-1][a]*dp[k+1][j][b]
                        }
                    }
                }
            }
        }
    }
    return dp[0][n-1][result]
}

func getValue(a, b int, op byte) int {
    if op == '&' {
        return a & b
    } else if op == '|' {
        return a | b
    }
    return a ^ b
}

# 2
func countEval(s string, result int) int {
    n := len(s)
    // dp[i][j][0/1] => s[i:j+1]结果为0/1的方法数
    dp := make([][][2]int, n)
    for i := 0; i < n; i++ {
        dp[i] = make([][2]int, n)
    }
    for i := n - 1; i >= 0; i = i - 2 {
        for j := i; j < n; j = j + 2 {
            if i == j {
                dp[i][j][s[i]-'0']++
                continue
            }
            for k := i + 1; k < j; k = k + 2 { // 枚举操作符
                for a := 0; a <= 1; a++ {
                    for b := 0; b <= 1; b++ {
                        temp := getValue(a, b, s[k])
                        dp[i][j][temp] = dp[i][j][temp] +
                            dp[i][k-1][a]*dp[k+1][j][b]
                    }
                }
            }
        }
    }
    return dp[0][n-1][result]
}

func getValue(a, b int, op byte) int {
    if op == '&' {
        return a & b
    } else if op == '|' {
        return a | b
    }
    return a ^ b
}

# 3
func countEval(s string, result int) int {
    n := len(s)
    // dp[i][j][0/1] => s[i:j+1]结果为0/1的方法数
    dp := make([][][2]int, n)
    for i := 0; i < n; i++ {
        dp[i] = make([][2]int, n)
        if i%2 == 0 {
            dp[i][i][int(s[i]-'0')]++
        }
    }
    for length := 2; length <= n; length = length + 2 { // 枚举长度
        for i := 0; i <= n-length; i = i + 2 { // 枚举起点
            j := i + length                    // 确定终点
            for k := i + 1; k < j; k = k + 2 { // 枚举操作符
                for a := 0; a <= 1; a++ {
                    for b := 0; b <= 1; b++ {
                        temp := getValue(a, b, s[k])
                        dp[i][j][temp] = dp[i][j][temp] +
                            dp[i][k-1][a]*dp[k+1][j][b]
                    }
                }
            }
        }
    }
    return dp[0][n-1][result]
}

func getValue(a, b int, op byte) int {
    if op == '&' {
        return a & b
    } else if op == '|' {
        return a | b
    }
    return a ^ b
}

面试题10.01.合并排序的数组(3)

  • 题目
给定两个排序后的数组 A 和 B,其中 A 的末端有足够的缓冲空间容纳 B。 编写一个方法,将 B 合并入 A 并排序。
初始化 A 和 B 的元素数量分别为 m 和 n。
示例:输入:
A = [1,2,3,0,0,0], m = 3
B = [2,5,6],       n = 3
输出: [1,2,2,3,5,6]
说明:A.length == n + m
  • 解题思路
No. 思路 时间复杂度 空间复杂度
01 合并后排序 O(nlog(n)) O(1)
02 双指针法 O(n) O(1)
03 数组辅助 O(n) O(n)
func merge(A []int, m int, B []int, n int) {
    A = A[:m]
    A = append(A, B[:n]...)
    sort.Ints(A)
}

# 2
func merge(A []int, m int, B []int, n int) {
    for m > 0 && n > 0 {
        if A[m-1] < B[n-1] {
            A[m+n-1] = B[n-1]
            n--
        } else {
            A[m+n-1] = A[m-1]
            m--
        }
    }
    if m == 0 && n > 0 {
        for n > 0 {
            A[n-1] = B[n-1]
            n--
        }
    }
}

# 3
func merge(A []int, m int, B []int, n int) {
    temp := make([]int, m)
    copy(temp, A)

    if n == 0 {
        return
    }
    first, second := 0, 0
    for i := 0; i < len(A); i++ {
        if second >= n {
            A[i] = temp[first]
            first++
            continue
        }
        if first >= m {
            A[i] = B[second]
            second++
            continue
        }
        if temp[first] < B[second] {
            A[i] = temp[first]
            first++
        } else {
            A[i] = B[second]
            second++
        }
    }
}

面试题10.02.变位词组(2)

  • 题目
编写一种方法,对字符串数组进行排序,将所有变位词组合在一起。变位词是指字母相同,但排列不同的字符串。
注意:本题相对原题稍作修改
示例:输入: ["eat", "tea", "tan", "ate", "nat", "bat"], 输出:
[
  ["ate","eat","tea"],
  ["nat","tan"],
  ["bat"]
]
说明:所有输入均为小写字母。
    不考虑答案输出的顺序。
  • 解题思路
No. 思路 时间复杂度 空间复杂度
01 哈希辅助 O(n^2log(n)) O(n^2)
02 哈希辅助 O(n^2) O(n^2)
func groupAnagrams(strs []string) [][]string {
    m := make(map[string]int)
    res := make([][]string, 0)
    for i := 0; i < len(strs); i++ {
        arr := []byte(strs[i])
        sort.Slice(arr, func(i, j int) bool {
            return arr[i] < arr[j]
        })
        newStr := string(arr)
        if _, ok := m[newStr]; ok {
            res[m[newStr]] = append(res[m[newStr]], strs[i])
        } else {
            m[newStr] = len(res)
            res = append(res, []string{strs[i]})
        }
    }
    return res
}

# 2
func groupAnagrams(strs []string) [][]string {
    m := make(map[[26]int]int)
    res := make([][]string, 0)
    for i := 0; i < len(strs); i++ {
        arr := [26]int{}
        for j := 0; j < len(strs[i]); j++{
            arr[strs[i][j]-'a']++
        }
        if _, ok := m[arr]; ok {
            res[m[arr]] = append(res[m[arr]], strs[i])
        } else {
            m[arr] = len(res)
            res = append(res, []string{strs[i]})
        }
    }
    return res
}

面试题10.03.搜索旋转数组(2)

  • 题目
搜索旋转数组。给定一个排序后的数组,包含n个整数,但这个数组已被旋转过很多次了,次数不详。
请编写代码找出数组中的某个元素,假设数组元素原先是按升序排列的。若有多个相同元素,返回索引值最小的一个。
示例1: 输入: arr = [15, 16, 19, 20, 25, 1, 3, 4, 5, 7, 10, 14], target = 5
输出: 8(元素5在该数组中的索引)
示例2:输入:arr = [15, 16, 19, 20, 25, 1, 3, 4, 5, 7, 10, 14], target = 11
输出:-1 (没有找到)
提示:arr 长度范围在[1, 1000000]之间
  • 解题思路
No. 思路 时间复杂度 空间复杂度
01 二分查找 O(log(n)) O(1)
02 遍历 O(n) O(1)
func search(nums []int, target int) int {
    left, right := 0, len(nums)-1
    for left < right {
        mid := left + (right-left)/2
        if nums[left] < nums[mid] { // 左边升序的情况
            if nums[left] <= target && target <= nums[mid] {
                right = mid
            } else {
                left = mid + 1
            }
        } else if nums[left] > nums[mid] { // 右边升序
            if nums[mid] < target && target <= nums[right] && nums[left] > nums[right] {
                left = mid + 1
            } else {
                right = mid
            }
        } else if nums[left] == nums[mid] {
            if nums[left] != target {
                left++
            } else {
                return left
            }
        }
    }
    if nums[left] == target {
        return left
    }
    return -1
}

#
func search(nums []int, target int) int {
    for i := 0; i < len(nums); i++ {
        if target == nums[i] {
            return i
        }
    }
    return -1
}

面试题10.05.稀疏数组搜索(2)

  • 题目
稀疏数组搜索。有个排好序的字符串数组,其中散布着一些空字符串,编写一种方法,找出给定字符串的位置。
示例1:
输入: words = ["at", "", "", "", "ball", "", "", "car", "", "","dad", "", ""], s = "ta"
输出:-1
说明: 不存在返回-1。
示例2:
输入:words = ["at", "", "", "", "ball", "", "", "car", "", "","dad", "", ""], s = "ball"
输出:4
提示: words的长度在[1, 1000000]之间
  • 解题思路
No. 思路 时间复杂度 空间复杂度
01 二分查找 O(log(n)) O(1)
02 暴力法 O(n) O(1)
func findString(words []string, s string) int {
    left := 0
    right := len(words) - 1
    for left <= right {
        mid := left + (right-left)/2
        index := mid
        word := words[mid]
        if word == "" {
            for index = mid; index <= right; index++ {
                if words[index] != "" {
                    word = words[index]
                    break
                }
            }
        }
        if word == s {
            return index
        } else if word < s {
            left = index + 1
        } else {
            right = mid - 1
        }
    }
    return -1
}

# 2
func findString(words []string, s string) int {
    for i := 0; i < len(words); i++ {
        if s == words[i] {
            return i
        }
    }
    return -1
}

面试题10.09.排序矩阵查找(6)

  • 题目
给定M×N矩阵,每一行、每一列都按升序排列,请编写代码找出某元素。
示例:现有矩阵 matrix 如下:
[
  [1,   4,  7, 11, 15],
  [2,   5,  8, 12, 19],
  [3,   6,  9, 16, 22],
  [10, 13, 14, 17, 24],
  [18, 21, 23, 26, 30]
]
给定 target = 5,返回 true。
给定 target = 20,返回 false。
  • 解题思路
No. 思路 时间复杂度 空间复杂度
01 暴力法 O(n^2) O(1)
02 暴力法-优化 O(n^2) O(1)
03 二分查找 O(nlog(n)) O(1)
04 左下角查找 O(n) O(1)
05 右上角查找 O(n) O(1)
06 内置函数 O(n^2) O(1)
func searchMatrix(matrix [][]int, target int) bool {
    if len(matrix) == 0 {
        return false
    }
    if len(matrix[0]) == 0 {
        return false
    }
    for i := 0; i < len(matrix); i++ {
        for j := 0; j < len(matrix[i]); j++ {
            if matrix[i][j] == target {
                return true
            }
        }
    }
    return false
}

# 2
func searchMatrix(matrix [][]int, target int) bool {
    if len(matrix) == 0 {
        return false
    }
    if len(matrix[0]) == 0 {
        return false
    }
    for i := 0; i < len(matrix); i++ {
        if matrix[i][0] <= target && matrix[i][len(matrix[i])-1] >= target {
            for j := 0; j < len(matrix[i]); j++ {
                if matrix[i][j] == target {
                    return true
                }
            }
        }
    }
    return false
}

# 3
func searchMatrix(matrix [][]int, target int) bool {
    if len(matrix) == 0 {
        return false
    }
    if len(matrix[0]) == 0 {
        return false
    }
    for i := 0; i < len(matrix); i++ {
        if matrix[i][0] <= target && matrix[i][len(matrix[i])-1] >= target {
            res := binarySearch(matrix[i], target)
            if res == true {
                return true
            }
        }
    }
    return false
}

func binarySearch(arr []int, target int) bool {
    left := 0
    right := len(arr) - 1
    for left <= right {
        mid := left + (right-left)/2
        if arr[mid] == target {
            return true
        } else if arr[mid] > target {
            right = mid - 1
        } else {
            left = mid + 1
        }
    }
    return false
}

# 4
func searchMatrix(matrix [][]int, target int) bool {
    if len(matrix) == 0 {
        return false
    }
    if len(matrix[0]) == 0 {
        return false
    }
    i := len(matrix) - 1
    j := 0
    for i >= 0 && j < len(matrix[0]) {
        if matrix[i][j] == target {
            return true
        } else if matrix[i][j] > target {
            i--
        } else {
            j++
        }
    }
    return false
}

# 5
func searchMatrix(matrix [][]int, target int) bool {
    if len(matrix) == 0 {
        return false
    }
    if len(matrix[0]) == 0 {
        return false
    }
    i := 0
    j := len(matrix[0]) - 1
    for j >= 0 && i < len(matrix) {
        if matrix[i][j] == target {
            return true
        } else if matrix[i][j] > target {
            j--
        } else {
            i++
        }
    }
    return false
}

# 6
func searchMatrix(matrix [][]int, target int) bool {
    if len(matrix) == 0 {
        return false
    }
    if len(matrix[0]) == 0 {
        return false
    }
    for i := 0; i < len(matrix); i++ {
        index := sort.SearchInts(matrix[i], target)
        if index < len(matrix[i]) && target == matrix[i][index] {
            return true
        }
    }
    return false
}

面试题10.10.数字流的秩(3)

  • 题目
假设你正在读取一串整数。每隔一段时间,你希望能找出数字 x 的秩(小于或等于 x 的值的个数)。
请实现数据结构和算法来支持这些操作,也就是说:
实现 track(int x)方法,每读入一个数字都会调用该方法;
实现 getRankOfNumber(int x) 方法,返回小于或等于 x 的值的个数。
注意:本题相对原题稍作改动
示例:输入: ["StreamRank", "getRankOfNumber", "track", "getRankOfNumber"] [[], [1], [0], [0]]
输出: [null,0,null,1]
提示:x <= 50000
track和getRankOfNumber 方法的调用次数均不超过 2000 次
  • 解题思路
No. 思路 时间复杂度 空间复杂度
01 树状数组 O(nlog(n)) O(n)
02 暴力法 O(n^2) O(n)
03 内置函数 O(nlog(n)) O(n)
type StreamRank struct {
    length int
    c      []int
}

func Constructor() StreamRank {
    return StreamRank{
        length: 50002,
        c:      make([]int, 50003),
    }
}

func (this *StreamRank) Track(x int) {
    this.upData(x+1, 1)
}

func (this *StreamRank) GetRankOfNumber(x int) int {
    return this.getSum(x + 1)
}

func (this *StreamRank) lowBit(x int) int {
    return x & (-x)
}

// 单点修改
func (this *StreamRank) upData(i, k int) { // 在i位置加上k
    for i <= this.length {
        this.c[i] = this.c[i] + k
        i = i + this.lowBit(i) // i = i + 2^k
    }
}

// 区间查询
func (this *StreamRank) getSum(i int) int {
    res := 0
    for i > 0 {
        res = res + this.c[i]
        i = i - this.lowBit(i)
    }
    return res
}

# 2
type StreamRank struct {
    m map[int]int
}

func Constructor() StreamRank {
    return StreamRank{m: make(map[int]int)}
}

func (this *StreamRank) Track(x int) {
    this.m[x]++
}

func (this *StreamRank) GetRankOfNumber(x int) int {
    res := 0
    for k, v := range this.m {
        if k <= x {
            res = res + v
        }
    }
    return res
}

# 3
type StreamRank struct {
    arr []int
}

func Constructor() StreamRank {
    return StreamRank{arr: make([]int, 0)}
}

func (this *StreamRank) Track(x int) {
    index := sort.Search(len(this.arr), func(i int) bool {
        return x <= this.arr[i]
    })
    temp := append([]int{}, this.arr[:index]...)
    temp = append(temp, x)
    temp = append(temp, this.arr[index:]...)
    this.arr = temp
}

func (this *StreamRank) GetRankOfNumber(x int) int {
    return sort.Search(len(this.arr), func(i int) bool {
        return x < this.arr[i]
    })
}

面试题10.11.峰与谷(2)

  • 题目
在一个整数数组中,“峰”是大于或等于相邻整数的元素,相应地,“谷”是小于或等于相邻整数的元素。
例如,在数组{5, 8, 4, 2, 3, 4, 6}中,{8, 6}是峰, {5, 2}是谷。
现在给定一个整数数组,将该数组按峰与谷的交替顺序排序。
示例:输入: [5, 3, 1, 2, 3] 输出:[5, 1, 3, 2, 3]
提示:nums.length <= 10000
  • 解题思路
No. 思路 时间复杂度 空间复杂度
01 遍历 O(n) O(1)
02 排序 O(nlog(n)) O(1)
func wiggleSort(nums []int) {
    for i := 0; i < len(nums)-1; i++ {
        if (i%2 == 1 && nums[i] > nums[i+1]) ||
            (i%2 == 0 && nums[i] < nums[i+1]) {
            nums[i], nums[i+1] = nums[i+1], nums[i]
        }
    }
}

# 2
func wiggleSort(nums []int) {
    sort.Ints(nums)
    for i := 0; i < len(nums)-1; i = i + 2 {
        nums[i], nums[i+1] = nums[i+1], nums[i]
    }
}

面试题16.01.交换数字(3)

  • 题目
编写一个函数,不用临时变量,直接交换numbers = [a, b]中a与b的值。
示例:输入: numbers = [1,2] 输出: [2,1]
提示: numbers.length == 2
  • 解题思路
No. 思路 时间复杂度 空间复杂度
01 直接返回 O(1) O(1)
02 位运算 O(1) O(1)
03 加减 O(1) O(1)
func swapNumbers(numbers []int) []int {
    return []int{numbers[1], numbers[0]}
}

# 2
func swapNumbers(numbers []int) []int {
    numbers[0] = numbers[0] ^ numbers[1]
    numbers[1] = numbers[1] ^ numbers[0]
    numbers[0] = numbers[0] ^ numbers[1]
    return numbers
}

# 3
func swapNumbers(numbers []int) []int {
    numbers[0] = numbers[0] + numbers[1]
    numbers[1] = numbers[0] - numbers[1]
    numbers[0] = numbers[0] - numbers[1]
    return numbers
}

面试题16.02.单词频率(2)

  • 题目
设计一个方法,找出任意指定单词在一本书中的出现频率。
你的实现应该支持如下操作:
    WordsFrequency(book)构造函数,参数为字符串数组构成的一本书
    get(word)查询指定单词在书中出现的频率
示例:WordsFrequency wordsFrequency = 
new WordsFrequency({"i", "have", "an", "apple", "he", "have", "a", "pen"});
wordsFrequency.get("you"); //返回0,"you"没有出现过
wordsFrequency.get("have"); //返回2,"have"出现2次
wordsFrequency.get("an"); //返回1
wordsFrequency.get("apple"); //返回1
wordsFrequency.get("pen"); //返回1
提示:book[i]中只包含小写字母
    1 <= book.length <= 100000
    1 <= book[i].length <= 10
    get函数的调用次数不会超过100000
  • 解题思路
No. 思路 时间复杂度 空间复杂度
02 map O(1) O(n)
02 trie树 O(1) O(n)
type WordsFrequency struct {
    m map[string]int
}

func Constructor(book []string) WordsFrequency {
    res := WordsFrequency{m: make(map[string]int)}
    for k := range book {
        res.m[book[k]]++
    }
    return res
}

func (this *WordsFrequency) Get(word string) int {
    return this.m[word]
}

# 2
type WordsFrequency struct {
    ending int
    next   [26]*WordsFrequency
}

func Constructor(book []string) WordsFrequency {
    res := WordsFrequency{}
    for _, v := range book {
        res.Insert(v)
    }
    return res
}

func (this *WordsFrequency) Get(word string) int {
    temp := this
    for _, v := range word {
        nextWord := v - 'a'
        if temp.next[nextWord] == nil {
            return 0
        }
        temp = temp.next[nextWord]
    }
    return temp.ending
}

func (this *WordsFrequency) Insert(word string) {
    temp := this
    for _, v := range word {
        nextWord := v - 'a'
        if temp.next[nextWord] == nil {
            temp.next[nextWord] = &WordsFrequency{}
        }
        temp = temp.next[nextWord]
    }
    temp.ending = temp.ending + 1
}

面试题16.04.井字游戏(1)

  • 题目
设计一个算法,判断玩家是否赢了井字游戏。输入是一个 N x N 的数组棋盘,由字符" ","X"和"O"组成,其中字符" "代表一个空位。
以下是井字游戏的规则:
玩家轮流将字符放入空位(" ")中。
第一个玩家总是放字符"O",且第二个玩家总是放字符"X"。
"X"和"O"只允许放置在空位中,不允许对已放有字符的位置进行填充。
当有N个相同(且非空)的字符填充任何行、列或对角线时,游戏结束,对应该字符的玩家获胜。
当所有位置非空时,也算为游戏结束。
如果游戏结束,玩家不允许再放置字符。
如果游戏存在获胜者,就返回该游戏的获胜者使用的字符("X"或"O");
如果游戏以平局结束,则返回 "Draw";
如果仍会有行动(游戏未结束),则返回 "Pending"。
示例 1:输入: board = ["O X"," XO","X O"] 输出: "X"
示例 2:输入: board = ["OOX","XXO","OXO"] 输出: "Draw"
解释: 没有玩家获胜且不存在空位
示例 3:输入: board = ["OOX","XXO","OX "] 输出: "Pending"
解释: 没有玩家获胜且仍存在空位
提示:1 <= board.length == board[i].length <= 100
输入一定遵循井字棋规则
  • 解题思路
No. 思路 时间复杂度 空间复杂度
01 遍历 O(n^2) O(n)
func tictactoe(board []string) string {
    n := len(board)
    flag := false                     // 有没有空格
    rows := make([][2]int, n)         // 行
    cols := make([][2]int, n)         // 列
    left, right := [2]int{}, [2]int{} // 对角线
    for i := 0; i < n; i++ {
        for j := 0; j < n; j++ {
            if board[i][j] == ' ' {
                flag = true
            } else if board[i][j] == 'X' {
                rows[i][0]++
                cols[j][0]++
                if i == j {
                    left[0]++
                }
                if i == n-1-j {
                    right[0]++
                }
            } else if board[i][j] == 'O' {
                rows[i][1]++
                cols[j][1]++
                if i == j {
                    left[1]++
                }
                if i == n-1-j {
                    right[1]++
                }
            }
        }
    }
    for i := 0; i < n; i++ { // 行列判断
        if rows[i][0] == n || cols[i][0] == n {
            return "X"
        }
        if rows[i][1] == n || cols[i][1] == n {
            return "O"
        }
    }
    if left[0] == n || right[0] == n { // 对角线判断
        return "X"
    }
    if left[1] == n || right[1] == n {
        return "O"
    }
    if flag == true {
        return "Pending"
    }
    return "Draw"
}

面试题16.05.阶乘尾数(1)

  • 题目
设计一个算法,算出 n 阶乘有多少个尾随零。
示例 1:输入: 3 输出: 0 解释: 3! = 6, 尾数中没有零。
示例 2:输入: 5输出: 1 解释: 5! = 120, 尾数中有 1 个零.
说明: 你算法的时间复杂度应为 O(log n) 。
  • 解题思路
No. 思路 时间复杂度 空间复杂度
01 数学,找规律 O(log(n)) O(1)
// N!有多少个后缀0,即N!有多少个质因数5。
// N!有多少个质因数5,即N可以划分成多少组5个数字一组,
// 加上划分成多少组25个数字一组,加上划分多少组成125个数字一组,等等
// Ans = N/5 + N/(5^2) + N/(5^3) + ...
func trailingZeroes(n int) int {
    result := 0
    for n >= 5 {
        n = n / 5
        result = result + n
    }
    return result
}

面试题16.06.最小差(2)

  • 题目
给定两个整数数组a和b,计算具有最小差绝对值的一对数值(每个数组中取一个值),并返回该对数值的差
示例:输入:{1, 3, 15, 11, 2}, {23, 127, 235, 19, 8} 输出: 3,即数值对(11, 8)
提示:
    1 <= a.length, b.length <= 100000
    -2147483648 <= a[i], b[i] <= 2147483647
    正确结果在区间[-2147483648, 2147483647]内
  • 解题思路
No. 思路 时间复杂度 空间复杂度
01 排序双指针 O(nlog(n)) O(1)
02 排序+二分查找 O(nlog(n)) O(1)
func smallestDifference(a []int, b []int) int {
    sort.Ints(a)
    sort.Ints(b)
    i, j := 0, 0
    res := math.MaxInt32
    for i < len(a) && j < len(b) {
        res = min(res, abs(a[i], b[j]))
        if a[i] > b[j] {
            j++
        } else {
            i++
        }
    }
    return res
}

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

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

# 2
func smallestDifference(a []int, b []int) int {
    sort.Ints(b)
    res := math.MaxInt32
    for i := 0; i < len(a); i++ {
        left, right := 0, len(b)-1
        for left <= right {
            mid := left + (right-left)/2
            if b[mid] == a[i] {
                return 0
            } else if b[mid] > a[i] {
                right = mid - 1
            } else {
                left = mid + 1
            }
        }
        if left < len(b) {
            res = min(res, abs(a[i], b[left]))
        }
        if left > 0 {
            res = min(res, abs(a[i], b[left-1]))
        }
    }
    return res
}

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

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

面试题16.07.最大数值(3)

  • 题目
编写一个方法,找出两个数字a和b中最大的那一个。不得使用if-else或其他比较运算符。
示例:输入: a = 1, b = 2 输出: 2
  • 解题思路
No. 思路 时间复杂度 空间复杂度
01 数学 O(1) O(1)
02 内置函数 O(1) O(1)
03 位运算 O(1) O(1)
func maximum(a int, b int) int {
    // max(a,b) = (abs(a-b)+a+b)/2
    return (int(math.Abs(float64(a-b))) + a + b) / 2
}

# 2
func maximum(a int, b int) int {
    return int(math.Max(float64(a), float64(b)))
}

# 3
func maximum(a int, b int) int {
    value := int(uint64(a-b) >> 63) // 取符号位,a-b>0 => 符号位为0 a-b<0 =>符号位为1
    return value*b + int(1^value)*a // value=0=> 0^1=1 1^1=0
}

面试题16.08.整数的英语表示(2)

  • 题目
给定一个整数,打印该整数的英文描述。
示例 1:输入: 123 输出: "One Hundred Twenty Three"
示例 2:输入: 12345 输出: "Twelve Thousand Three Hundred Forty Five"
示例 3:输入: 1234567 
输出: "One Million Two Hundred Thirty Four Thousand Five Hundred Sixty Seven"
示例 4:输入: 1234567891
输出: "One Billion Two Hundred Thirty Four Million Five Hundred Sixty Seven
Thousand Eight Hundred Ninety One"
  • 解题思路
No. 思路 时间复杂度 空间复杂度
01 遍历 O(1) O(1)
02 递归 O(1) O(1)
func numberToWords(num int) string {
    if num == 0 {
        return "Zero"
    }
    res := ""
    billion := num / 1000000000
    million := (num - billion*1000000000) / 1000000
    thousand := (num - billion*1000000000 - million*1000000) / 1000
    left := num - billion*1000000000 - million*1000000 - thousand*1000
    if billion != 0 {
        res += three(billion) + " Billion"
    }
    if million != 0 {
        if res != "" {
            res += " "
        }
        res += three(million) + " Million"
    }
    if thousand != 0 {
        if res != "" {
            res += " "
        }
        res += three(thousand) + " Thousand"
    }
    if left != 0 {
        if res != "" {
            res += " "
        }
        res += three(left)
    }
    return res
}

func three(num int) string {
    hundred := num / 100
    left := num - hundred*100
    if hundred == 0 {
        return two(num)
    }
    res := transfer[hundred] + " Hundred"
    if left != 0 {
        res += " " + two(left)
    }
    return res
}

func two(num int) string {
    if num == 0 {
        return ""
    } else if num < 10 {
        return transfer[num]
    } else if num < 20 {
        return transfer[num]
    }
    ten := num / 10
    left := num - ten*10
    ten = ten * 10
    res := transfer[ten]
    if left != 0 {
        res += " " + transfer[left]
    }
    return res
}

var transfer = map[int]string{
    0:  "Zero",
    1:  "One",
    2:  "Two",
    3:  "Three",
    4:  "Four",
    5:  "Five",
    6:  "Six",
    7:  "Seven",
    8:  "Eight",
    9:  "Nine",
    10: "Ten",
    11: "Eleven",
    12: "Twelve",
    13: "Thirteen",
    14: "Fourteen",
    15: "Fifteen",
    16: "Sixteen",
    17: "Seventeen",
    18: "Eighteen",
    19: "Nineteen",
    20: "Twenty",
    30: "Thirty",
    40: "Forty",
    50: "Fifty",
    60: "Sixty",
    70: "Seventy",
    80: "Eighty",
    90: "Ninety",
}

# 2
func numberToWords(num int) string {
    if num == 0 {
        return "Zero"
    }
    return strings.Trim(dfs(num), " ")
}

func dfs(n int) string {
    if n < 20 {
        return transfer[n]
    }
    if n < 100 {
        return transfer[n/10*10] + dfs(n%10)
    }
    if n < 1000 {
        return transfer[n/100] + "Hundred " + dfs(n%100)
    }
    if n < 1000000 {
        return dfs(n/1000) + "Thousand " + dfs(n%1000)
    }
    if n < 1000000000 {
        return dfs(n/1000000) + "Million " + dfs(n%1000000)
    }
    return dfs(n/1000000000) + "Billion " + dfs(n%1000000000)
}

var transfer = map[int]string{
    1:  "One ",
    2:  "Two ",
    3:  "Three ",
    4:  "Four ",
    5:  "Five ",
    6:  "Six ",
    7:  "Seven ",
    8:  "Eight ",
    9:  "Nine ",
    10: "Ten ",
    11: "Eleven ",
    12: "Twelve ",
    13: "Thirteen ",
    14: "Fourteen ",
    15: "Fifteen ",
    16: "Sixteen ",
    17: "Seventeen ",
    18: "Eighteen ",
    19: "Nineteen ",
    20: "Twenty ",
    30: "Thirty ",
    40: "Forty ",
    50: "Fifty ",
    60: "Sixty ",
    70: "Seventy ",
    80: "Eighty ",
    90: "Ninety ",
}

面试题16.10.生存人数(2)

  • 题目
给定N个人的出生年份和死亡年份,第i个人的出生年份为birth[i],死亡年份为death[i],
实现一个方法以计算生存人数最多的年份。
你可以假设所有人都出生于1900年至2000年(含1900和2000)之间。
如果一个人在某一年的任意时期都处于生存状态,那么他们应该被纳入那一年的统计中。
例如,生于1908年、死于1909年的人应当被列入1908年和1909年的计数。
如果有多个年份生存人数相同且均为最大值,输出其中最小的年份。
示例:输入:birth = {1900, 1901, 1950} death = {1948, 1951, 2000} 输出: 1901
提示:0 < birth.length == death.length <= 10000
    birth[i] <= death[i]
  • 解题思路
No. 思路 时间复杂度 空间复杂度
01 排序双指针 O(nlog(n)) O(1)
02 计数 O(n) O(n)
func maxAliveYear(birth []int, death []int) int {
    sort.Ints(birth)
    sort.Ints(death)
    res := birth[0]
    max := 0
    j := 0
    count := 0
    for i := 0; i < len(birth); i++ {
        count++
        for birth[i] > death[j] {
            count--
            j++
        }
        if count > max {
            max = count
            res = birth[i]
        }
    }
    return res
}

# 2
func maxAliveYear(birth []int, death []int) int {
    arr := make([]int, 102)
    for i := 0; i < len(birth); i++ {
        arr[birth[i]-1900]++
        arr[death[i]-1900+1]--
    }
    max := 0
    sum := 0
    res := 0
    for i := 0; i < len(arr); i++ {
        sum = sum + arr[i]
        if sum > max {
            max = sum
            res = i + 1900
        }
    }
    return res
}

面试题16.11.跳水板(2)

  • 题目
你正在使用一堆木板建造跳水板。有两种类型的木板,其中长度较短的木板长度为shorter,
长度较长的木板长度为longer。你必须正好使用k块木板。编写一个方法,生成跳水板所有可能的长度。
返回的长度需要从小到大排列。
示例 1 输入:shorter = 1 longer = 2 k = 3 输出: [3,4,5,6]
解释:可以使用 3 次 shorter,得到结果 3;使用 2 次 shorter 和 1 次 longer,得到结果 4 。
以此类推,得到最终结果。
提示:
    0 < shorter <= longer
    0 <= k <= 100000
  • 解题思路
No. 思路 时间复杂度 空间复杂度
01 遍历 O(n) O(n)
02 遍历 O(n) O(n)
func divingBoard(shorter int, longer int, k int) []int {
    res := make([]int, 0)
    if k == 0 {
        return res
    }
    if shorter == longer {
        return []int{shorter * k}
    }
    for i := 0; i <= k; i++ {
        res = append(res, shorter*(k-i)+longer*i)
    }
    return res
}

#
func divingBoard(shorter int, longer int, k int) []int {
    res := make([]int, 0)
    if k == 0 {
        return res
    }
    if shorter == longer {
        return []int{shorter * k}
    }
    start := shorter * k
    diff := longer - shorter
    for i := 0; i <= k; i++ {
        res = append(res, start+i*diff)
    }
    return res
}

面试题16.13.平分正方形(1)

  • 题目
给定两个正方形及一个二维平面。请找出将这两个正方形分割成两半的一条直线。假设正方形顶边和底边与 x 轴平行。
每个正方形的数据square包含3个数值,正方形的左下顶点坐标[X,Y] = [square[0],square[1]],以及正方形的边长square[2]。
所求直线穿过两个正方形会形成4个交点,请返回4个交点形成线段的两端点坐标(两个端点即为4个交点中距离最远的2个点,
这2个点所连成的线段一定会穿过另外2个交点)。2个端点坐标[X1,Y1]和[X2,Y2]的返回格式为{X1,Y1,X2,Y2},
要求若X1 != X2,需保证X1 < X2,否则需保证Y1 <= Y2。
若同时有多条直线满足要求,则选择斜率最大的一条计算并返回(与Y轴平行的直线视为斜率无穷大)。
示例:输入:square1 = {-1, -1, 2} square2 = {0, -1, 2} 输出: {-1,0,2,0}
解释: 直线 y = 0 能将两个正方形同时分为等面积的两部分,返回的两线段端点为[-1,0]和[2,0]
提示:square.length == 3
square[2] > 0
  • 解题思路
No. 思路 时间复杂度 空间复杂度
01 几何 O(1) O(1)
func cutSquares(square1 []int, square2 []int) []float64 {
    // 2个正方形的中点坐标
    x1, y1, z1 := float64(square1[0])+float64(square1[2])/2, float64(square1[1])+float64(square1[2])/2, float64(square1[2])
    x2, y2, z2 := float64(square2[0])+float64(square2[2])/2, float64(square2[1])+float64(square2[2])/2, float64(square2[2])
    var a, b, c, d float64
    if x1 == x2 { // 1、垂直
        a, b, c, d = x1, min(float64(square1[1]), float64(square2[1])), x1, max(float64(square1[1])+z1, float64(square2[1])+z2)
        return []float64{a, b, c, d}
    }
    // 2、有斜率: y = kx + b1
    k := (y1 - y2) / (x1 - x2)
    b1 := y1 - k*x1
    if abs(k) > 1 { // 斜率大于1,交点通过正方形的上边+下边(根据纵坐标求横坐标)
        b = min(float64(square1[1]), float64(square2[1]))
        d = max(float64(square1[1])+z1, float64(square2[1])+z2)
        a = (b - b1) / k
        c = (d - b1) / k
    } else { // 斜率小于等于1,交点通过正方形的左边+右边(根据横坐标求纵坐标)
        a = min(float64(square1[0]), float64(square2[0]))
        c = max(float64(square1[0])+z1, float64(square2[0])+z2)
        b = a*k + b1
        d = c*k + b1
    }
    if a > c {
        a, c = c, a
        b, d = d, b
    }
    return []float64{a, b, c, d}
}

func abs(a float64) float64 {
    if a < 0 {
        return -a
    }
    return a
}

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

func min(a, b float64) float64 {
    if a > b {
        return b
    }
    return a
}

面试题16.14.最佳直线(2)

  • 题目
给定一个二维平面及平面上的 N 个点列表Points,其中第i个点的坐标为Points[i]=[Xi,Yi]。请找出一条直线,其通过的点的数目最多。
设穿过最多点的直线所穿过的全部点编号从小到大排序的列表为S,你仅需返回[S[0],S[1]]作为答案,
若有多条直线穿过了相同数量的点,则选择S[0]值较小的直线返回,S[0]相同则选择S[1]值较小的直线返回。
示例:输入: [[0,0],[1,1],[1,0],[2,0]] 输出: [0,2]
解释: 所求直线穿过的3个点的编号为[0,2,3]
提示:2 <= len(Points) <= 300
len(Points[i]) = 2
  • 解题思路
No. 思路 时间复杂度 空间复杂度
01 暴力法 O(n^3) O(1)
02 哈希 O(n^2) O(n^2)
func bestLine(points [][]int) []int {
    res := []int{0, 1}
    maxCount := 0
    n := len(points)
    for i := 0; i < n; i++ {
        for j := i + 1; j < n; j++ {
            count := 2
            x1 := points[i][0] - points[j][0]
            y1 := points[i][1] - points[j][1]
            for k := j + 1; k < n; k++ {
                x2 := points[i][0] - points[k][0]
                y2 := points[i][1] - points[k][1]
                if x1*y2 == x2*y1 { // 斜率相同+1
                    count++
                }
            }
            if count > maxCount {
                maxCount = count
                res[0] = i
                res[1] = j
            }
        }
    }
    return res
}

# 2
func bestLine(points [][]int) []int {
    res := []int{0, 1}
    maxCount := 0
    n := len(points)
    m := make(map[[3]int]int)
    mToKey := make(map[[3]int][]int)
    for i := 0; i < n; i++ {
        for j := i + 1; j < n; j++ {
            // AX+BY+C=0
            A := points[j][1] - points[i][1]
            B := points[i][0] - points[j][0]
            C := points[i][1]*points[j][0] - points[i][0]*points[j][1]
            com := gcd(gcd(A, B), C)
            A, B, C = A/com, B/com, C/com
            node := [3]int{A, B, C}
            if m[node] == 0 {
                mToKey[node] = []int{i, j}
            }
            m[node]++
            if m[node] > maxCount {
                maxCount = m[node]
                res = mToKey[node]
            } else if m[node] == maxCount {
                if mToKey[node][0] < res[0] ||
                    (mToKey[node][0] == res[0] && mToKey[node][1] < res[1]) {
                    res = mToKey[node]
                }
            }
        }
    }
    return res
}

func gcd(a, b int) int {
    for a != 0 {
        a, b = b%a, a
    }
    return b
}

面试题16.15.珠玑妙算(2)

  • 题目
珠玑妙算游戏(the game of master mind)的玩法如下。
计算机有4个槽,每个槽放一个球,颜色可能是红色(R)、黄色(Y)、绿色(G)或蓝色(B)。
例如,计算机可能有RGGB 4种(槽1为红色,槽2、3为绿色,槽4为蓝色)。
作为用户,你试图猜出颜色组合。打个比方,你可能会猜YRGB。
要是猜对某个槽的颜色,则算一次“猜中”;要是只猜对颜色但槽位猜错了,则算一次“伪猜中”。
注意,“猜中”不能算入“伪猜中”。
给定一种颜色组合solution和一个猜测guess,
编写一个方法,返回猜中和伪猜中的次数answer,其中answer[0]为猜中的次数,answer[1]为伪猜中的次数。
示例:输入: solution="RGBY",guess="GGRR" 输出: [1,1]
解释: 猜中1次,伪猜中1次。
提示:len(solution) = len(guess) = 4
    solution和guess仅包含"R","G","B","Y"这4种字符
  • 解题思路
No. 思路 时间复杂度 空间复杂度
01 哈希辅助 O(1) O(1)
02 数组辅助 O(1) O(1)
func masterMind(solution string, guess string) []int {
    m := make(map[byte]int)
    a, b := 0, 0
    for i := 0; i < len(solution); i++ {
        if solution[i] == guess[i] {
            a++
        } else {
            m[solution[i]]++
        }
    }
    for i := 0; i < len(guess); i++ {
        if solution[i] != guess[i] {
            if m[guess[i]] > 0 {
                b++
                m[guess[i]]--
            }
        }
    }
    return []int{a, b}
}

# 2
func masterMind(solution string, guess string) []int {
    arr := [256]int{}
    a, b := 0, 0
    for i := 0; i < len(solution); i++ {
        if solution[i] == guess[i] {
            a++
        } else {
            arr[solution[i]]++
        }
    }
    for i := 0; i < len(guess); i++ {
        if solution[i] != guess[i] {
            if arr[guess[i]] > 0 {
                b++
                arr[guess[i]]--
            }
        }
    }
    return []int{a, b}
}

面试题16.16.部分排序(2)

  • 题目
给定一个整数数组,编写一个函数,找出索引m和n,只要将索引区间[m,n]的元素排好序,整个数组就是有序的。
注意:n-m尽量最小,也就是说,找出符合条件的最短序列。
函数返回值为[m,n],若不存在这样的m和n(例如整个数组是有序的),请返回[-1,-1]。
示例:输入: [1,2,4,7,10,11,7,12,6,7,16,18,19] 输出: [3,9]
提示:0 <= len(array) <= 1000000
  • 解题思路
No. 思路 时间复杂度 空间复杂度
01 排序遍历 O(nlog(n)) O(n)
02 遍历 O(n) O(1)
func subSort(array []int) []int {
    temp := make([]int, len(array))
    copy(temp, array)
    sort.Ints(temp)
    left, right := -1, -1
    for i := 0; i < len(array); i++ {
        if temp[i] != array[i] {
            left = i
            break
        }
    }
    for i := len(array) - 1; i >= 0; i-- {
        if temp[i] != array[i] {
            right = i
            break
        }
    }
    return []int{left, right}
}

# 2
func subSort(array []int) []int {
    left, right := -1, -1
    maxValue := math.MinInt32
    minValue := math.MaxInt32
    for i := 0; i < len(array); i++ {
        if array[i] >= maxValue {
            maxValue = array[i]
        } else {
            right = i
        }
    }
    for i := len(array) - 1; i >= 0; i-- {
        if minValue >= array[i] {
            minValue = array[i]
        } else {
            left = i
        }
    }
    return []int{left, right}
}

面试题16.17.连续数列(5)

  • 题目
给定一个整数数组,找出总和最大的连续数列,并返回总和。
示例:输入: [-2,1,-3,4,-1,2,1,-5,4]输出: 6
解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。
进阶:如果你已经实现复杂度为 O(n) 的解法,尝试使用更为精妙的分治法求解。
  • 解题思路
No. 思路 时间复杂度 空间复杂度
01 贪心法 O(n) O(1)
02 暴力法 O(n^2) O(1)
03 动态规划 O(n) O(n)
04 动态规划 O(n) O(1)
05 分治 O(nlog(n)) O(log(n))
func maxSubArray(nums []int) int {
    result := nums[0]
    sum := 0
    for i := 0; i < len(nums); i++ {
        if sum > 0 {
            sum += nums[i]
        } else {
            sum = nums[i]
        }
        if sum > result {
            result = sum
        }
    }
    return result
}

# 2
func maxSubArray(nums []int) int {
    result := math.MinInt32
    for i := 0; i < len(nums); i++ {
        sum := 0
        for j := i; j < len(nums); j++ {
            sum += nums[j]
            if sum > result {
                result = sum
            }
        }
    }
    return result
}

# 3
// dp[i] = max(dp[i-1]+nums[i], nums[i])
// res = max(dp[i], res)
func maxSubArray(nums []int) int {
    dp := make([]int, len(nums))
    dp[0] = nums[0]
    result := nums[0]
    for i := 1; i < len(nums); i++ {
        if dp[i-1]+nums[i] > nums[i] {
            dp[i] = dp[i-1] + nums[i]
        } else {
            dp[i] = nums[i]
        }
        if dp[i] > result {
            result = dp[i]
        }
    }
    return result
}

# 4
func maxSubArray(nums []int) int {
    dp := nums[0]
    result := dp
    for i := 1; i < len(nums); i++ {
        if dp+nums[i] > nums[i] {
            dp = dp + nums[i]
        } else {
            dp = nums[i]
        }

        if dp > result {
            result = dp
        }
    }
    return result
}

# 5
func maxSubArray(nums []int) int {
    result := maxSubArr(nums, 0, len(nums)-1)
    return result
}

func maxSubArr(nums []int, left, right int) int {
    if left == right {
        return nums[left]
    }

    mid := (left + right) / 2
    leftSum := maxSubArr(nums, left, mid)        // 最大子序在左边
    rightSum := maxSubArr(nums, mid+1, right)    // 最大子序在右边
    midSum := findMaxArr(nums, left, mid, right) // 跨中心
    result := max(leftSum, rightSum)
    result = max(result, midSum)
    return result
}

func findMaxArr(nums []int, left, mid, right int) int {
    leftSum := math.MinInt32
    sum := 0
    // 从右到左
    for i := mid; i >= left; i-- {
        sum += nums[i]
        leftSum = max(leftSum, sum)
    }
    rightSum := math.MinInt32
    sum = 0
    // 从左到右
    for i := mid + 1; i <= right; i++ {
        sum += nums[i]
        rightSum = max(rightSum, sum)
    }
    return leftSum + rightSum
}

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

面试题16.18.模式匹配(1)

  • 题目
你有两个字符串,即pattern和value。 pattern字符串由字母"a"和"b"组成,用于描述字符串中的模式。
例如,字符串"catcatgocatgo"匹配模式"aabab"(其中"cat"是"a","go"是"b"),
该字符串也匹配像"a"、"ab"和"b"这样的模式。
但需注意"a"和"b"不能同时表示相同的字符串。编写一个方法判断value字符串是否匹配pattern字符串。
示例 1:输入: pattern = "abba", value = "dogcatcatdog" 输出: true
示例 2:输入: pattern = "abba", value = "dogcatcatfish" 输出: false
示例 3:输入: pattern = "aaaa", value = "dogcatcatdog" 输出: false
示例 4:输入: pattern = "abba", value = "dogdogdogdog" 输出: true
解释: "a"="dogdog",b="",反之也符合规则
提示:1 <= len(pattern) <= 1000
0 <= len(value) <= 1000
你可以假设pattern只包含字母"a"和"b",value仅包含小写字母。
  • 解题思路
No. 思路 时间复杂度 空间复杂度
01 枚举 O(n^2) O(n)
func patternMatching(pattern string, value string) bool {
    countA := 0
    for i := 0; i < len(pattern); i++ {
        if pattern[i] == 'a' {
            countA++
        }
    }
    countB := len(pattern) - countA
    if value == "" {
        return countA == 0 || countB == 0
    }
    if countA < countB { // 令a>b
        countA, countB = countB, countA
        str := ""
        for i := 0; i < len(pattern); i++ {
            if pattern[i] == 'a' {
                str = str + "b"
            } else {
                str = str + "a"
            }
        }
        pattern = str
    }
    for a := 0; a <= len(value)/countA; a++ { // 枚举
        if judge(pattern, value, a, countA, countB) {
            return true
        }
    }
    return false
}

func judge(pattern string, value string, a, countA, countB int) bool {
    left := len(value) - a*countA
    if (countB == 0 && left == 0) || (countB > 0 && left%countB == 0) {
        var b int
        if countB > 0 {
            b = left / countB
        }
        var strA, strB string
        index := 0
        flag := true
        for i := 0; i < len(pattern); i++ {
            if pattern[i] == 'a' {
                str := value[index : index+a]
                if strA == "" {
                    strA = str
                } else if str != strA {
                    flag = false
                    break
                }
                index = index + a
            } else {
                str := value[index : index+b]
                if strB == "" {
                    strB = str
                } else if str != strB {
                    flag = false
                    break
                }
                index = index + b
            }
        }
        if flag == true && strA != strB {
            return true
        }
    }
    return false
}

面试题16.19.水域大小(2)

  • 题目
你有一个用于表示一片土地的整数矩阵land,该矩阵中每个点的值代表对应地点的海拔高度。
若值为0则表示水域。由垂直、水平或对角连接的水域为池塘。池塘的大小是指相连接的水域的个数。
编写一个方法来计算矩阵中所有池塘的大小,返回值需要从小到大排序。
示例:输入:
[
  [0,2,1,0],
  [0,1,0,1],
  [1,1,0,1],
  [0,1,0,1]
]
输出: [1,2,4]
提示:
    0 < len(land) <= 1000
    0 < len(land[i]) <= 1000
  • 解题思路
No. 思路 时间复杂度 空间复杂度
01 深度优先搜索 O(n^2) O(n)
02 深度优先搜索 O(n^2) O(n)
func pondSizes(land [][]int) []int {
    res := make([]int, 0)
    for i := range land {
        for j := range land[i] {
            if land[i][j] == 0 {
                res = append(res, getArea(land, i, j))
            }
        }
    }
    sort.Ints(res)
    return res
}

func getArea(grid [][]int, i, j int) int {
    if grid[i][j] != 0 {
        return 0
    }
    grid[i][j] = 1
    area := 1
    for a := i - 1; a <= i+1; a++ {
        for b := j - 1; b <= j+1; b++ {
            if (i == a && j == b) || a < 0 || a >= len(grid) ||
                b < 0 || b >= len(grid[0]) {
                continue
            }
            area = area + getArea(grid, a, b)
        }
    }
    return area
}

# 2
func pondSizes(land [][]int) []int {
    res := make([]int, 0)
    for i := range land {
        for j := range land[i] {
            if land[i][j] == 0 {
                res = append(res, getArea(land, i, j))
            }
        }
    }
    sort.Ints(res)
    return res
}

func getArea(grid [][]int, i, j int) int {
    if i < 0 || i >= len(grid) ||
        j < 0 || j >= len(grid[0]) || grid[i][j] != 0 {
        return 0
    }

    grid[i][j] = 1
    res := 1
    res = res + getArea(grid, i+1, j)
    res = res + getArea(grid, i+1, j+1)
    res = res + getArea(grid, i+1, j-1)
    res = res + getArea(grid, i-1, j)
    res = res + getArea(grid, i-1, j+1)
    res = res + getArea(grid, i-1, j-1)
    res = res + getArea(grid, i, j+1)
    res = res + getArea(grid, i, j-1)
    return res
}

面试题16.20.T9键盘(1)

  • 题目
在老式手机上,用户通过数字键盘输入,手机将提供与这些数字相匹配的单词列表。每个数字映射到0至4个字母。
给定一个数字序列,实现一个算法来返回匹配单词的列表。你会得到一张含有有效单词的列表。映射如下图所示:
示例 1:输入: num = "8733", words = ["tree", "used"] 输出: ["tree", "used"]
示例 2:输入: num = "2", words = ["a", "b", "c", "d"] 输出: ["a", "b", "c"]
提示:num.length <= 1000
    words.length <= 500
    words[i].length == num.length
    num中不会出现 0, 1 这两个数字
  • 解题思路
No. 思路 时间复杂度 空间复杂度
01 遍历 O(n^2) O(n)
var m [26]byte = [26]byte{
    '2', '2', '2',
    '3', '3', '3',
    '4', '4', '4',
    '5', '5', '5',
    '6', '6', '6',
    '7', '7', '7', '7',
    '8', '8', '8',
    '9', '9', '9', '9',
}

func getValidT9Words(num string, words []string) []string {
    res := make([]string, 0)
    for _, str := range words {
        if len(str) != len(num) {
            continue
        }
        flag := true
        for i := 0; i < len(str); i++ {
            if num[i] != m[str[i]-'a'] {
                flag = false
                break
            }
        }
        if flag {
            res = append(res, str)
        }
    }
    return res
}

面试题16.21.交换和(1)

  • 题目
给定两个整数数组,请交换一对数值(每个数组中取一个数值),使得两个数组所有元素的和相等。
返回一个数组,第一个元素是第一个数组中要交换的元素,第二个元素是第二个数组中要交换的元素。
若有多个答案,返回任意一个均可。若无满足条件的数值,返回空数组。
示例:输入: array1 = [4, 1, 2, 1, 1, 2], array2 = [3, 6, 3, 3] 输出: [1, 3]
示例:输入: array1 = [1, 2, 3], array2 = [4, 5, 6] 输出: []
提示:1 <= array1.length, array2.length <= 100000
  • 解题思路
No. 思路 时间复杂度 空间复杂度
01 哈希辅助 O(n) O(n)
func findSwapValues(array1 []int, array2 []int) []int {
    m := make(map[int]bool)
    sumA, sumB := 0, 0
    for i := 0; i < len(array1); i++ {
        sumA = sumA + array1[i]
        m[array1[i]] = true
    }
    for i := 0; i < len(array2); i++ {
        sumB = sumB + array2[i]
    }
    if (sumA+sumB)%2 == 1 {
        return nil
    }
    half := (sumA - sumB) / 2
    a, b := 0, 0
    // sumA-A[i]+B[j] == sumB-B[j]+A[i]
    // sumA-sumB=2(A[i]-B[j])
    // (sumA-sumB)/2 = A[i]-B[j]
    for _, b = range array2 {
        a = b + half
        if m[a] == true {
            return []int{a, b}
        }
    }
    return nil
}

面试题16.22.兰顿蚂蚁(1)

  • 题目
一只蚂蚁坐在由白色和黑色方格构成的无限网格上。开始时,网格全白,蚂蚁面向右侧。
每行走一步,蚂蚁执行以下操作。
(1) 如果在白色方格上,则翻转方格的颜色,向右(顺时针)转 90 度,并向前移动一个单位。
(2) 如果在黑色方格上,则翻转方格的颜色,向左(逆时针方向)转 90 度,并向前移动一个单位。
编写程序来模拟蚂蚁执行的前 K 个动作,并返回最终的网格。
网格由数组表示,每个元素是一个字符串,代表网格中的一行,黑色方格由'X'表示,白色方格由'_'表示,
蚂蚁所在的位置由'L', 'U', 'R', 'D'表示,分别表示蚂蚁左、上、右、下 的朝向。
只需要返回能够包含蚂蚁走过的所有方格的最小矩形。
示例 1:输入: 0 输出: ["R"]
示例 2:输入: 2 输出:
[
 "_X",
 "LX"
]
示例 3:输入: 5 输出:
[
 "_U",
 "X_",
 "XX"
]
说明:K <= 100000
  • 解题思路
No. 思路 时间复杂度 空间复杂度
01 遍历模拟 O(n) O(n)
func printKMoves(K int) []string {
    var dirArr = []byte{'R', 'D', 'L', 'U'}
    var dx = []int{1, 0, -1, 0}
    var dy = []int{0, -1, 0, 1}
    dir := 0 // 向右
    x, y := 0, 0
    left, right := 0, 0
    up, down := 0, 0
    m := make(map[[2]int]int) // 1黑色,0白色
    for i := 0; i < K; i++ {
        if m[[2]int{x, y}] == 1 { // 变方向
            dir = (dir + 3) % 4 // 逆时针
        } else {
            dir = (dir + 1) % 4 // 顺时针
        }
        m[[2]int{x, y}] = 1 - m[[2]int{x, y}]
        x = x + dx[dir]
        y = y + dy[dir]
        left = min(left, x)
        right = max(right, x)
        down = min(down, y)
        up = max(up, y)
    }
    w := right - left + 1
    h := up - down + 1
    res := make([]string, 0)
    for i := 0; i < h; i++ {
        arr := make([]byte, w)
        for j := 0; j < w; j++ {
            newX := j + left
            newY := up - i
            arr[j] = '_'
            if v, ok := m[[2]int{newX, newY}]; ok && v == 1 {
                arr[j] = 'X'
            }
            if newX == x && newY == y {
                arr[j] = dirArr[dir]
            }
        }
        res = append(res, string(arr))
    }
    return res
}

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

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

面试题16.24.数对和(2)

  • 题目
设计一个算法,找出数组中两数之和为指定值的所有整数对。一个数只能属于一个数对。
示例 1:输入: nums = [5,6,5], target = 11 输出: [[5,6]]
示例 2:输入: nums = [5,6,5,6], target = 11 输出: [[5,6],[5,6]]
提示:nums.length <= 100000
  • 解题思路
No. 思路 时间复杂度 空间复杂度
01 排序双指针 O(nlog(n)) O(n)
02 哈希辅助 O(n) O(n)
func pairSums(nums []int, target int) [][]int {
    res := make([][]int, 0)
    sort.Ints(nums)
    left, right := 0, len(nums)-1
    for left < right {
        sum := nums[left] + nums[right]
        if target == sum {
            res = append(res, []int{nums[left], nums[right]})
            left++
            right--
        } else if target > sum {
            left++
        } else {
            right--
        }
    }
    return res
}

# 2
func pairSums(nums []int, target int) [][]int {
    res := make([][]int, 0)
    m := make(map[int]int)
    for i := 0; i < len(nums); i++ {
        x := target - nums[i]
        if m[x] > 0 {
            res = append(res, []int{nums[i], x})
            m[x]--
            continue
        }
        m[nums[i]]++
    }
    return res
}

面试题16.25.LRU缓存(1)

  • 题目
设计和构建一个“最近最少使用”缓存,该缓存会删除最近最少使用的项目。
缓存应该从键映射到值(允许你插入和检索特定键对应的值),并在初始化时指定最大容量。
当缓存被填满时,它应该删除最近最少使用的项目。
它应该支持以下操作: 获取数据 get 和 写入数据 put 。
获取数据 get(key) - 如果密钥 (key) 存在于缓存中,则获取密钥的值(总是正数),否则返回 -1。
写入数据 put(key, value) - 如果密钥不存在,则写入其数据值。
当缓存容量达到上限时,它应该在写入新数据之前删除最近最少使用的数据值,从而为新的数据值留出空间。
示例:LRUCache cache = new LRUCache( 2 /* 缓存容量 */ );
cache.put(1, 1);
cache.put(2, 2);
cache.get(1);       // 返回  1
cache.put(3, 3);    // 该操作会使得密钥 2 作废
cache.get(2);       // 返回 -1 (未找到)
cache.put(4, 4);    // 该操作会使得密钥 1 作废
cache.get(1);       // 返回 -1 (未找到)
cache.get(3);       // 返回  3
cache.get(4);       // 返回  4
  • 解题思路
No. 思路 时间复杂度 空间复杂度
01 双向链表 O(1) O(n)
type Node struct {
    key   int
    value int
    prev  *Node
    next  *Node
}

type LRUCache struct {
    cap    int
    header *Node
    tail   *Node
    m      map[int]*Node
}

func Constructor(capacity int) LRUCache {
    cache := LRUCache{
        cap:    capacity,
        header: &Node{},
        tail:   &Node{},
        m:      make(map[int]*Node, capacity),
    }
    cache.header.next = cache.tail
    cache.tail.prev = cache.header
    return cache
}

func (this *LRUCache) Get(key int) int {
    if node, ok := this.m[key]; ok {
        this.remove(node)
        this.putHead(node)
        return node.value
    }
    return -1
}

func (this *LRUCache) Put(key int, value int) {
    if node, ok := this.m[key]; ok {
        node.value = value
        this.remove(node)
        this.putHead(node)
        return
    }
    if this.cap <= len(this.m) {
        // 删除尾部
        deleteKey := this.tail.prev.key
        this.remove(this.tail.prev)
        delete(this.m, deleteKey)
    }
    // 插入到头部
    newNode := &Node{key: key, value: value}
    this.putHead(newNode)
    this.m[key] = newNode
}

// 删除尾部节点
func (this *LRUCache) remove(node *Node) {
    node.prev.next = node.next
    node.next.prev = node.prev
}

// 插入头部
func (this *LRUCache) putHead(node *Node) {
    next := this.header.next
    this.header.next = node
    node.next = next
    next.prev = node
    node.prev = this.header
}

面试题16.26.计算器(2)

  • 题目
给定一个包含正整数、加(+)、减(-)、乘(*)、除(/)的算数表达式(括号除外),计算其结果。
表达式仅包含非负整数,+, - ,*,/ 四种运算符和空格  。 整数除法仅保留整数部分。
示例 1:输入: "3+2*2" 输出: 7
示例 2:输入: " 3/2 " 输出: 1
示例 3:输入: " 3+5 / 2 " 输出: 5
说明:你可以假设所给定的表达式都是有效的。
    请不要使用内置的库函数 eval。
  • 解题思路
No. 思路 时间复杂度 空间复杂度
01 栈辅助 O(n) O(n)
02 栈辅助 O(n) O(n)
func calculate(s string) int {
    stack := make([]int, 0)
    op := make([]int, 0)
    num := 0
    for i := 0; i < len(s); i++ {
        if '0' <= s[i] && s[i] <= '9' {
            num = 0
            for i < len(s) && '0' <= s[i] && s[i] <= '9' {
                num = num*10 + int(s[i]-'0')
                i++
            }
            // 处理乘除计算
            if len(op) > 0 && op[len(op)-1] > 1 {
                if op[len(op)-1] == 2 {
                    stack[len(stack)-1] = stack[len(stack)-1] * num
                } else {
                    stack[len(stack)-1] = stack[len(stack)-1] / num
                }
                op = op[:len(op)-1]
            } else {
                stack = append(stack, num)
            }
            i--
        } else if s[i] == '+' {
            op = append(op, 1)
        } else if s[i] == '-' {
            op = append(op, -1)
        } else if s[i] == '*' {
            op = append(op, 2)
        } else if s[i] == '/' {
            op = append(op, 3)
        }
    }
    // 处理加减
    for len(op) > 0 {
        stack[1] = stack[0] + stack[1]*op[0]
        stack = stack[1:]
        op = op[1:]
    }
    return stack[0]
}

# 2
func calculate(s string) int {
    s = strings.Trim(s, " ") // 避免"3/2 "的情况
    stack := make([]int, 0)
    num := 0
    sign := byte('+')
    for i := 0; i < len(s); i++ {
        if s[i] == ' ' {
            continue
        }
        if '0' <= s[i] && s[i] <= '9' {
            num = num*10 + int(s[i]-'0')
        }
        if s[i] == '+' || s[i] == '-' || s[i] == '*' || s[i] == '/' || i == len(s)-1 {
            // 处理前一个符号
            switch sign {
            case '+':
                stack = append(stack, num)
            case '-':
                stack = append(stack, -num)
            case '*':
                prev := stack[len(stack)-1]
                stack = stack[:len(stack)-1]
                stack = append(stack, num*prev)
            case '/':
                prev := stack[len(stack)-1]
                stack = stack[:len(stack)-1]
                stack = append(stack, prev/num)
            }
            num = 0
            sign = s[i]
        }
    }
    res := 0
    for i := 0; i < len(stack); i++ {
        res = res + stack[i]
    }
    return res
}

面试题17.01.不用加号的加法(2)

  • 题目
设计一个函数把两个数字相加。不得使用 + 或者其他算术运算符。
示例:输入: a = 1, b = 1 输出: 2
提示: a, b 均可能是负数或 0
    结果不会溢出 32 位整数
  • 解题思路
No. 思路 时间复杂度 空间复杂度
01 迭代 O(1) O(1)
02 递归 O(1) O(1)
/*
0 + 0 = 0
0 + 1 = 1
1 + 0 = 1
1 + 1 = 0(进位 1)
异或的一个重要特性是无进位加法
(a 和 b 的无进位结果) + (a 和 b 的进位结果)
*/
func add(a int, b int) int {
    for b != 0 {
        a, b = a^b, (a&b)<<1
    }
    return a
}

# 
func add(a int, b int) int {
    if b == 0 {
        return a
    }
    return add(a^b, (a&b)<<1)
}

面试题17.04.消失的数字(5)

  • 题目
数组nums包含从0到n的所有整数,但其中缺了一个。请编写代码找出那个缺失的整数。你有办法在O(n)时间内完成吗?
注意:本题相对书上原题稍作改动
示例 1:输入:[3,0,1]输出:2
示例 2:输入:[9,6,4,2,3,5,7,0,1] 输出:8
  • 解题思路
No. 思路 时间复杂度 空间复杂度
01 数学计算 O(n) O(1)
02 排序遍历 O(nlog(n)) O(1)
03 异或-位运算 O(n) O(1)
04 交换排序(就地排序) O(n) O(1)
05 哈希辅助 O(n) O(n)
func missingNumber(nums []int) int {
    n := len(nums)
    sum := n * (n + 1) / 2
    for i := 0; i < n; i++ {
        sum = sum - nums[i]
    }
    return sum
}

# 2
func missingNumber(nums []int) int {
    sort.Ints(nums)
    for i := 0; i < len(nums); i++ {
        if nums[i] != i {
            return i
        }
    }
    return len(nums)
}

# 3
func missingNumber(nums []int) int {
    res := 0
    for i := 0; i < len(nums); i++ {
        res = res ^ (i+1) ^ nums[i]
    }
    return res
}

# 4
func missingNumber(nums []int) int {
    n := len(nums)
    index := n
    for i := 0; i < n; {
        if nums[i] == n{
            index = i
            i++
            continue
        }
        if i == nums[i]{
            i++
            continue
        }
        nums[i], nums[nums[i]] = nums[nums[i]], nums[i]
    }
    return index
}

# 5
func missingNumber(nums []int) int {
    m := make(map[int]bool)
    for i := range nums {
        m[nums[i]] = true
    }
    for i := 0; i <= len(nums); i++ {
        if m[i] == false {
            return i
        }
    }
    return 0
}

面试题17.05.字母与数字(1)

  • 题目
给定一个放有字符和数字的数组,找到最长的子数组,且包含的字符和数字的个数相同。
返回该子数组,若存在多个最长子数组,返回左端点最小的。若不存在这样的数组,返回一个空数组。
示例 1:
输入: ["A","1","B","C","D","2","3","4","E","5","F","G","6","7","H","I","J","K","L","M"]
输出: ["A","1","B","C","D","2","3","4","E","5","F","G","6","7"]
示例 2:输入: ["A","A"]输出: []
提示:array.length <= 100000
  • 解题思路
No. 思路 时间复杂度 空间复杂度
01 前缀和 O(n) O(n)
func findLongestSubarray(array []string) []string {
    m := make(map[int]int)
    m[0] = 0
    res := 0
    begin := 0
    total := 0
    for i := 0; i < len(array); i++ {
        if '0' <= array[i][0] && array[i][0] <= '9' {
            total++
        } else {
            total--
        }
        if total == 0 {
            begin = 0
            res = i + 1
        } else if index, ok := m[total]; ok {
            if i-index > res {
                res = i - index
                begin = index + 1
            }
        } else {
            m[total] = i
        }
    }
    return array[begin : begin+res]
}

面试题17.06.2出现的次数(3)

  • 题目
编写一个方法,计算从 0 到 n (含 n) 中数字 2 出现的次数。
示例:输入: 25 输出: 9
解释: (2, 12, 20, 21, 22, 23, 24, 25)(注意 22 应该算作两次)
提示:n <= 10^9
  • 解题思路
No. 思路 时间复杂度 空间复杂度
01 找规律 O(log(n)) O(1)
02 找规律 O(log(n)) O(1)
03 找规律 O(log(n)) O(1)
func numberOf2sInRange(n int) int {
    if n <= 0 {
        return 0
    }
    res := 0
    for i := 1; i <= n; i = i * 10 {
        left := n / i
        right := n % i
        res = res + (left+7)/10*i
        if left%10 == 2 {
            res = res + right + 1
        }
    }
    return res
}

# 2
func numberOf2sInRange(n int) int {
    res := 0
    digit := 1
    high := n / 10
    cur := n % 10
    low := 0
    for high != 0 || cur != 0 {
        if cur > 2 {
            res = res + (high+1)*digit
        } else if cur == 2 {
            res = res + high*digit + low + 1
        } else {
            res = res + high*digit
        }
        low = low + cur*digit
        cur = high % 10
        high = high / 10
        digit = digit * 10
    }
    return res
}

# 3
func numberOf2sInRange(n int) int {
    if n <= 0 {
        return 0
    }
    str := strconv.Itoa(n)
    return dfs(str)
}

func dfs(str string) int {
    if str == "" {
        return 0
    }
    first := int(str[0] - '0')
    if len(str) == 1 && first == 0 {
        return 0
    }
    if len(str) == 1 && first >= 2 {
        return 1
    }
    count := 0
    if first > 2 {
        count = int(math.Pow(float64(10), float64(len(str)-1)))
    } else if first == 2 {
        count, _ = strconv.Atoi(str[1:])
        count = count + 1
    }
    other := first * (len(str) - 1) * int(math.Pow(float64(10), float64(len(str)-2)))
    numLeft := dfs(str[1:])
    return count + numLeft + other
}

面试题17.07.婴儿名字(1)

  • 题目
每年,政府都会公布一万个最常见的婴儿名字和它们出现的频率,也就是同名婴儿的数量。
有些名字有多种拼法,例如,John 和 Jon 本质上是相同的名字,但被当成了两个名字公布出来。
给定两个列表,一个是名字及对应的频率,另一个是本质相同的名字对。设计一个算法打印出每个真实名字的实际频率。
注意,如果 John 和 Jon 是相同的,并且 Jon 和 Johnny 相同,
则 John 与 Johnny 也相同,即它们有传递和对称性。
在结果列表中,选择 字典序最小 的名字作为真实名字。
示例:输入:names = ["John(15)","Jon(12)","Chris(13)","Kris(4)","Christopher(19)"], 
synonyms = ["(Jon,John)","(John,Johnny)","(Chris,Kris)","(Chris,Christopher)"]
输出:["John(27)","Chris(36)"]
提示:names.length <= 100000
  • 解题思路
No. 思路 时间复杂度 空间复杂度
01 并查集 O(n) O(n)
func trulyMostPopular(names []string, synonyms []string) []string {
    res := make([]string, 0)
    node = Node{}
    nameArr := make([]string, 0)
    countArr := make([]int, 0)
    m := make(map[string]int)
    for i := 0; i < len(names); i++ {
        arr := strings.Split(names[i], "(")
        nameArr = append(nameArr, arr[0])
        tempArr := strings.Split(arr[1], ")")
        count, _ := strconv.Atoi(tempArr[0])
        countArr = append(countArr, count)
        m[arr[0]] = i
    }
    Init(nameArr, countArr)

    for i := 0; i < len(synonyms); i++ {
        str := strings.TrimLeft(synonyms[i], "(")
        str = strings.TrimRight(str, ")")
        arr := strings.Split(str, ",")
        a := m[arr[0]]
        b := m[arr[1]]
        union(a, b)
    }
    for i := 0; i < len(node.fa); i++ {
        if node.fa[i] < 0 {
            temp := node.names[i] + "(" + strconv.Itoa(node.count[i]) + ")"
            res = append(res, temp)
        }
    }
    return res
}

var node Node

type Node struct {
    fa    []int
    names []string
    count []int
}

// 初始化
func Init(names []string, count []int) {
    node.fa = make([]int, len(names))
    for i := 0; i < len(names); i++ {
        node.fa[i] = -1
    }
    node.names = names
    node.count = count
}

// 查询
func find(x int) int {
    if node.fa[x] < 0 {
        return x
    }
    res := find(node.fa[x])
    node.fa[x] = res
    return res
}

// 合并
func union(i, j int) {
    x, y := find(i), find(j)
    if x == y {
        return
    }
    if node.names[x] <= node.names[y] {
        node.fa[y] = x
        node.count[x] = node.count[x] + node.count[y]
    } else {
        node.fa[x] = y
        node.count[y] = node.count[y] + node.count[x]
    }
}

面试题17.08.马戏团人塔(2)

  • 题目
有个马戏团正在设计叠罗汉的表演节目,一个人要站在另一人的肩膀上。
出于实际和美观的考虑,在上面的人要比下面的人矮一点且轻一点。
已知马戏团每个人的身高和体重,请编写代码计算叠罗汉最多能叠几个人。
示例:输入:height = [65,70,56,75,60,68] weight = [100,150,90,190,95,110] 输出:6
解释:从上往下数,叠罗汉最多能叠 6 层:
(56,90), (60,95),(65,100), (68,110), (70,150), (75,190)
提示: height.length == weight.length <= 10000
  • 解题思路
No. 思路 时间复杂度 空间复杂度
01 二分查找 O(nlog(n)) O(n)
02 内置函数 O(nlog(n)) O(n)
func bestSeqAtIndex(height []int, weight []int) int {
    arr := make([][2]int, 0)
    for i := 0; i < len(height); i++ {
        arr = append(arr, [2]int{height[i], weight[i]})
    }
    sort.Slice(arr, func(i, j int) bool {
        if arr[i][0] == arr[j][0] {
            return arr[i][1] < arr[j][1]
        }
        return arr[i][0] > arr[j][0]
    })
    res := make([]int, 0)
    for i := 0; i < len(arr); i++ {
        if len(res) == 0 || arr[res[len(res)-1]][0] > arr[i][0] &&
            arr[res[len(res)-1]][1] > arr[i][1] {
            res = append(res, i)
        } else {
            left := 0
            right := len(res) - 1
            for left <= right {
                mid := left + (right-left)/2
                if arr[res[mid]][0] > arr[i][0] && arr[res[mid]][1] > arr[i][1] {
                    left = mid + 1
                } else {
                    right = mid - 1
                }
            }
            res[left] = i
        }
    }
    return len(res)
}

# 2
func bestSeqAtIndex(height []int, weight []int) int {
    arr := make([][2]int, 0)
    for i := 0; i < len(height); i++ {
        arr = append(arr, [2]int{height[i], weight[i]})
    }
    sort.Slice(arr, func(i, j int) bool {
        if arr[i][0] == arr[j][0] {
            return arr[i][1] < arr[j][1]
        }
        return arr[i][0] > arr[j][0]
    })
    res := make([]int, 0)
    for i := 0; i < len(arr); i++ {
        index := sort.Search(len(res), func(j int) bool {
            return arr[res[j]][0] <= arr[i][0] || arr[res[j]][1] <= arr[i][1]
        })
        if index == len(res) {
            res = append(res, i)
        } else {
            res[index] = i
        }
    }
    return len(res)
}

面试题17.09.第k个数(1)

  • 题目
有些数的素因子只有 3,5,7,请设计一个算法找出第 k 个数。
注意,不是必须有这些素因子,而是必须不包含其他的素因子。
例如,前几个数按顺序应该是 1,3,5,7,9,15,21。
示例 1:输入: k = 5输出: 9
  • 解题思路
No. 思路 时间复杂度 空间复杂度
01 动态规划 O(n) O(n)
func getKthMagicNumber(k int) int {
    dp := make([]int, k)
    dp[0] = 1
    // *3或5或7之后得到
    idx3, idx5, idx7 := 0, 0, 0
    for i := 1; i < k; i++ {
        dp[i] = min(dp[idx3]*3, min(dp[idx5]*5, dp[idx7]*7))
        if dp[i] == dp[idx3]*3 {
            idx3++
        }
        if dp[i] == dp[idx5]*5 {
            idx5++
        }
        if dp[i] == dp[idx7]*7 {
            idx7++
        }
    }
    return dp[k-1]
}

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

面试题17.10.主要元素(5)

  • 题目
数组中占比超过一半的元素称之为主要元素。给定一个整数数组,找到它的主要元素。若没有,返回-1。
示例 1:输入:[1,2,5,9,5,9,5,5,5]输出:5
示例 2:输入:[3,2]输出:-1
示例 3:输入:[2,2,1,1,1,2,2]输出:2
说明:你有办法在时间复杂度为 O(N),空间复杂度为 O(1) 内完成吗?
  • 解题思路
No. 思路 时间复杂度 空间复杂度
01 哈希辅助 O(n) O(n)
02 Boyer-Moore投票算法 O(n) O(1)
03 排序 O(nlog(n)) O(1)
04 位运算 O(n) O(1)
05 分治法 O(nlog(n)) O(log(n))
func majorityElement(nums []int) int {
    m := make(map[int]int)
    result := -1
    for _, v := range nums{
        if _,ok := m[v];ok{
            m[v]++
        }else {
            m[v]=1
        }
        if m[v] > (len(nums)/2){
            result = v
        }
    }
    return result
}

# 2
func majorityElement(nums []int) int {
    result, count := 0, 0
    for i := 0; i < len(nums); i++ {
        if count == 0 {
            result = nums[i]
            count++
        } else if result == nums[i] {
            count++
        } else {
            count--
        }
    }
    total := 0
    for i := 0; i < len(nums); i++ {
        if nums[i] == result {
            total++
        }
    }
    if total <= len(nums)/2 {
        return -1
    }
    return result
}

# 3
func majorityElement(nums []int) int {
    sort.Ints(nums)
    for i := 0; i <= len(nums)/2; i++ {
        if nums[i] == nums[i+len(nums)/2] {
            return nums[i]
        }
    }
    return -1
}

# 4
func majorityElement(nums []int) int {
    if len(nums) == 1 {
        return nums[0]
    }
    result := int32(0)
    mask := int32(1)
    for i := 0; i < 32; i++ {
        count := 0
        for j := 0; j < len(nums); j++ {
            if mask&int32(nums[j]) == mask {
                count++
            }
        }
        if count > len(nums)/2 {
            result = result | mask
        }
        mask = mask << 1
    }
    total := 0
    for i := 0; i < len(nums); i++ {
        if nums[i] == int(result) {
            total++
        }
    }
    if total <= len(nums)/2 {
        return -1
    }
    return int(result)
}

# 5
func majorityElement(nums []int) int {
    res := majority(nums, 0, len(nums)-1)
    total := 0
    for i := 0; i < len(nums); i++ {
        if nums[i] == res {
            total++
        }
    }
    if total <= len(nums)/2 {
        return -1
    }
    return res
}

func count(nums []int, target int, start int, end int) int {
    countNum := 0
    for i := start; i <= end; i++ {
        if nums[i] == target {
            countNum++
        }
    }
    return countNum
}

func majority(nums []int, start, end int) int {
    if start == end {
        return nums[start]
    }
    mid := (start + end) / 2
    left := majority(nums, start, mid)
    right := majority(nums, mid+1, end)
    if left == right {
        return left
    }
    leftCount := count(nums, left, start, end)
    rightCount := count(nums, right, start, end)
    if leftCount > rightCount {
        return left
    }
    return right
}

面试题17.11.单词距离(2)

  • 题目
有个内含单词的超大文本文件,给定任意两个单词,找出在这个文件中这两个单词的最短距离(相隔单词数)。
如果寻找过程在这个文件中会重复多次,而每次寻找的单词不同,你能对此优化吗?
示例:输入:words = ["I","am","a","student","from","a","university","in","a","city"], 
word1 = "a", word2 = "student"
输出:1
提示:words.length <= 100000
  • 解题思路
No. 思路 时间复杂度 空间复杂度
01 遍历 O(n) O(1)
02 数组辅助 O(n) O(n)
func findClosest(words []string, word1 string, word2 string) int {
    res := len(words) - 1
    a, b := -1, -1
    for i := 0; i < len(words); i++ {
        if words[i] == word1 {
            a = i
        }
        if words[i] == word2 {
            b = i
        }
        if a != -1 && b != -1 && abs(a, b) < res {
            res = abs(a, b)
        }
    }
    return res
}

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

# 2
func findClosest(words []string, word1 string, word2 string) int {
    res := len(words) - 1
    arrA, arrB := make([]int, 0), make([]int, 0)
    for i := 0; i < len(words); i++ {
        if words[i] == word1 {
            arrA = append(arrA, i)
        }
        if words[i] == word2 {
            arrB = append(arrB, i)
        }
    }
    i, j := 0, 0
    for i < len(arrA) && j < len(arrB) {
        if abs(arrA[i], arrB[j]) < res {
            res = abs(arrA[i], arrB[j])
        }
        if arrA[i] < arrB[j] {
            i++
        } else {
            j++
        }
    }
    return res
}

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

面试题17.12.BiNode(2)

  • 题目
二叉树数据结构TreeNode可用来表示单向链表(其中left置空,right为下一个链表节点)。
实现一个方法,把二叉搜索树转换为单向链表,要求依然符合二叉搜索树的性质,转换操作应是原址的,
也就是在原始的二叉搜索树上直接修改。
返回转换后的单向链表的头节点。
注意:本题相对原题稍作改动
示例:输入: [4,2,5,1,3,null,6,0]
输出: [0,null,1,null,2,null,3,null,4,null,5,null,6]
提示:节点数量不会超过 100000。
  • 解题思路
No. 思路 时间复杂度 空间复杂度
01 递归 O(n) O(log(n))
02 迭代 O(n) O(n)
func convertBiNode(root *TreeNode) *TreeNode {
    head := &TreeNode{}
    cur := head
    dfs(root, cur)
    return head.Right
}

func dfs(root, cur *TreeNode) *TreeNode {
    if root != nil {
        cur = dfs(root.Left, cur)
        root.Left = nil
        cur.Right = root
        cur = root
        cur = dfs(root.Right, cur)
    }
    return cur
}

# 2
func convertBiNode(root *TreeNode) *TreeNode {
    head := &TreeNode{}
    cur := head
    stack := make([]*TreeNode, 0)
    node := root
    for node != nil || len(stack) > 0 {
        if node != nil {
            stack = append(stack, node)
            node = node.Left
        } else {
            node = stack[len(stack)-1]
            stack = stack[:len(stack)-1]
            node.Left = nil
            cur.Right = node
            cur = node
            node = node.Right
        }
    }
    return head.Right
}

面试题17.13.恢复空格(2)

  • 题目
哦,不!你不小心把一个长篇文章中的空格、标点都删掉了,并且大写也弄成了小写。
像句子"I reset the computer. It still didn’t boot!"已经变成了"iresetthecomputeritstilldidntboot"。
在处理标点符号和大小写之前,你得先把它断成词语。
当然了,你有一本厚厚的词典dictionary,不过,有些词没在词典里。
假设文章用sentence表示,设计一个算法,把文章断开,要求未识别的字符最少,返回未识别的字符数。
注意:本题相对原题稍作改动,只需返回未识别的字符数
示例:输入: dictionary = ["looked","just","like","her","brother"]
sentence = "jesslookedjustliketimherbrother"
输出: 7
解释: 断句后为"jess looked just like tim her brother",共7个未识别字符。
提示:0 <= len(sentence) <= 1000
dictionary中总字符数不超过 150000。
你可以认为dictionary和sentence中只包含小写字母。
  • 解题思路
No. 思路 时间复杂度 空间复杂度
01 动态规划+字典树 O(n^2) O(n)
02 动态规划+哈希 O(n^2) O(n)
func respace(dictionary []string, sentence string) int {
    n := len(sentence)
    root := &Trie{
        next: [26]*Trie{},
    }
    for i := 0; i < len(dictionary); i++ {
        root.Insert(reverse(dictionary[i])) // 反序插入
    }
    dp := make([]int, n+1)
    for i := 1; i <= n; i++ {
        dp[i] = dp[i-1] + 1 // 上一个长度+1
        cur := root
        for j := i; j >= 1; j-- {
            value := int(sentence[j-1] - 'a')
            if cur.next[value] == nil {
                break
            } else if cur.next[value].ending > 0 { // 找到,更新
                dp[i] = min(dp[i], dp[j-1])
            }
            if dp[i] == 0 {
                break
            }
            cur = cur.next[value]
        }
    }
    return dp[n]
}

func reverse(s string) string {
    arr := []byte(s)
    for i := 0; i < len(s)/2; i++ {
        arr[i], arr[len(s)-1-i] = arr[len(s)-1-i], arr[i]
    }
    return string(arr)
}

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

type Trie struct {
    next   [26]*Trie // 下一级指针,如不限于小写字母,[26]=>[256]
    ending int       // 次数(可以改为bool)
}

// 插入word
func (this *Trie) Insert(word string) {
    temp := this
    for _, v := range word {
        value := v - 'a'
        if temp.next[value] == nil {
            temp.next[value] = &Trie{
                next:   [26]*Trie{},
                ending: 0,
            }
        }
        temp = temp.next[value]
    }
    temp.ending++
}

# 2
func respace(dictionary []string, sentence string) int {
    n := len(sentence)
    m := make(map[string]bool)
    for i := 0; i < len(dictionary); i++ {
        m[dictionary[i]] = true
    }
    dp := make([]int, n+1)
    for i := 1; i <= n; i++ {
        dp[i] = dp[i-1] + 1 // 上一个长度+1
        for j := i; j >= 1; j-- {
            str := sentence[j-1 : i]
            if m[str] == true {
                dp[i] = min(dp[i], dp[j-1])
            }
            if dp[i] == 0 {
                break
            }
        }
    }
    return dp[n]
}

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

面试题17.14.最小K个数(3)

  • 题目
设计一个算法,找出数组中最小的k个数。以任意顺序返回这k个数均可。
示例:输入: arr = [1,3,5,7,2,4,6,8], k = 4 输出: [1,2,3,4]
提示:0 <= len(arr) <= 100000
    0 <= k <= min(100000, len(arr))
  • 解题思路
No. 思路 时间复杂度 空间复杂度
01 堆排序 O(nlog(n)) O(n)
02 快排 O(nlog(n)) O(log(n))
03 内置函数 O(nlog(n)) O(1)
func smallestK(arr []int, k int) []int {
    intHeap := make(IntHeap, 0)
    heap.Init(&intHeap)
    for i := 0; i < len(arr); i++ {
        heap.Push(&intHeap, arr[i])
    }
    res := make([]int, 0)
    for i := 0; i < k; i++ {
        value := heap.Pop(&intHeap).(int)
        res = append(res, value)
    }
    return res
}

type IntHeap []int

func (h IntHeap) Len() int {
    return len(h)
}

// 小根堆<,大根堆变换方向>
func (h IntHeap) Less(i, j int) bool {
    return h[i] < h[j]
}

func (h IntHeap) Swap(i, j int) {
    h[i], h[j] = h[j], h[i]
}

func (h *IntHeap) Push(x interface{}) {
    *h = append(*h, x.(int))
}

func (h *IntHeap) Pop() interface{} {
    value := (*h)[len(*h)-1]
    *h = (*h)[:len(*h)-1]
    return value
}

# 2
func smallestK(arr []int, k int) []int {
    return quickSort(arr, 0, len(arr)-1, k)
}

func quickSort(arr []int, left, right, k int) []int {
    if left > right {
        return nil
    }
    index := partition(arr, left, right)
    if index == k {
        return arr[:k]
    } else if index < k {
        return quickSort(arr, index+1, right, k)
    }
    return quickSort(arr, left, index-1, k)
}

func partition(arr []int, left, right int) int {
    baseValue := arr[left] // 基准值
    for left < right {
        for baseValue <= arr[right] && left < right {
            right-- // 依次查找大于基准值的位置
        }
        arr[left] = arr[right]
        for arr[left] <= baseValue && left < right {
            left++ // 依次查找小于基准值的位置
        }
        arr[right] = arr[left]
    }
    arr[right] = baseValue
    return right
}

# 3
func smallestK(arr []int, k int) []int {
    sort.Ints(arr)
    return arr[:k]
}

面试题17.15.最长单词(2)

  • 题目
给定一组单词words,编写一个程序,找出其中的最长单词,且该单词由这组单词中的其他单词组合而成。
若有多个长度相同的结果,返回其中字典序最小的一项,若没有符合要求的单词则返回空字符串。
示例:输入: ["cat","banana","dog","nana","walk","walker","dogwalker"] 输出: "dogwalker"
解释: "dogwalker"可由"dog"和"walker"组成。
提示:0 <= len(words) <= 200
1 <= len(words[i]) <= 100
  • 解题思路
No. 思路 时间复杂度 空间复杂度
01 排序+递归 O(n^2) O(n)
02 排序+动态规划 O(n^3) O(n)
var m map[string]bool

func longestWord(words []string) string {
    m = make(map[string]bool)
    n := len(words)
    for i := 0; i < n; i++ {
        m[words[i]] = true
    }
    sort.Slice(words, func(i, j int) bool {
        if len(words[i]) == len(words[j]) {
            return words[i] < words[j]
        }
        return len(words[i]) > len(words[j])
    })
    for i := 0; i < n; i++ { // 从最长最小字典序的开始找
        m[words[i]] = false
        if dfs(words[i]) == true {
            return words[i]
        }
    }
    return ""
}

func dfs(str string) bool {
    if len(str) == 0 || m[str] == true {
        return true
    }
    for i := 1; i <= len(str); i++ {
        subStr := str[:i]
        if m[subStr] == true {
            if dfs(str[i:]) == true {
                return true
            }
        }
    }
    return false
}

# 2
var m map[string]bool

func longestWord(words []string) string {
    m = make(map[string]bool)
    n := len(words)
    for i := 0; i < n; i++ {
        m[words[i]] = true
    }
    sort.Slice(words, func(i, j int) bool {
        if len(words[i]) == len(words[j]) {
            return words[i] < words[j]
        }
        return len(words[i]) > len(words[j])
    })
    // 从最长最小字典序的开始找
    for i := 0; i < n; i++ {
        m[words[i]] = false
        if judge(words[i]) == true {
            return words[i]
        }
    }
    return ""
}

// leetcode 139.单词拆分
func judge(s string) bool {
    dp := make([]bool, len(s)+1)
    dp[0] = true
    n := len(s)
    for i := 1; i <= n; i++ {
        for j := 0; j < i; j++ {
            if dp[j] == true && m[s[j:i]] == true {
                dp[i] = true
                break
            }
        }
    }
    return dp[n]
}

面试题17.16.按摩师(4)

  • 题目
一个有名的按摩师会收到源源不断的预约请求,每个预约都可以选择接或不接。
在每次预约服务之间要有休息时间,因此她不能接受相邻的预约。
给定一个预约请求序列,替按摩师找到最优的预约集合(总预约时间最长),返回总的分钟数。
注意:本题相对原题稍作改动
示例 1:输入: [1,2,3,1] 输出: 4
解释: 选择 1 号预约和 3 号预约,总时长 = 1 + 3 = 4。
示例 2:输入: [2,7,9,3,1] 输出: 12
解释: 选择 1 号预约、 3 号预约和 5 号预约,总时长 = 2 + 9 + 1 = 12。
示例 3:输入: [2,1,4,5,3,1,1,3] 输出: 12
解释: 选择 1 号预约、 3 号预约、 5 号预约和 8 号预约,总时长 = 2 + 4 + 3 + 3 = 12。
  • 解题思路
No. 思路 时间复杂度 空间复杂度
01 动态规划 O(n) O(1)
02 动态规划+一维数组 O(n) O(n)
03 动态规划+二维数组 O(n) O(n)
04 奇偶法 O(n) O(1)
func massage(nums []int) int {
    if len(nums) == 0 {
        return 0
    }
    if len(nums) == 1 {
        return nums[0]
    }
    a := nums[0]
    b := max(a, nums[1])

    for i := 2; i < len(nums); i++ {
        a, b = b, max(a+nums[i], b)
    }
    return b
}

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

# 2
func massage(nums []int) int {
    n := len(nums)
    if n == 0 {
        return 0
    }
    if n == 1 {
        return nums[0]
    }
    dp := make([]int, n)
    dp[0] = nums[0]
    if nums[0] > nums[1] {
        dp[1] = nums[0]
    } else {
        dp[1] = nums[1]
    }
    for i := 2; i < n; i++ {
        dp[i] = max(dp[i-1], dp[i-2]+nums[i])
    }
    return dp[n-1]
}

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

# 3
func massage(nums []int) int {
    if len(nums) == 0 {
        return 0
    }
    if len(nums) == 1 {
        return nums[0]
    }
    n := len(nums)
    dp := make([][]int, n)
    for n := range dp {
        dp[n] = make([]int, 2)
    }
    dp[0][0], dp[0][1] = 0, nums[0]
    for i := 1; i < n; i++ {
        dp[i][0] = max(dp[i-1][0], dp[i-1][1])
        dp[i][1] = dp[i-1][0] + nums[i]
    }
    return max(dp[n-1][0], dp[n-1][1])
}

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

# 4
func massage(nums []int) int {
    var a, b int
    for i, v := range nums {
        if i%2 == 0 {
            a = max(a+v, b)
        } else {
            b = max(a, b+v)
        }
    }
    return max(a, b)
}

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

面试题17.17.多次搜索(3)

  • 题目
给定一个较长字符串big和一个包含较短字符串的数组smalls,设计一个方法,根据smalls中的每一个较短字符串,对big进行搜索。
输出smalls中的字符串在big里出现的所有位置positions,其中positions[i]为smalls[i]出现的所有位置。
示例:输入:big = "mississippi" smalls = ["is","ppi","hi","sis","i","ssippi"]
输出: [[1,4],[8],[],[3],[1,4,7,10],[5]]
提示:0 <= len(big) <= 1000
0 <= len(smalls[i]) <= 1000
smalls的总字符数不会超过 100000。
你可以认为smalls中没有重复字符串。
所有出现的字符均为英文小写字母。
  • 解题思路
No. 思路 时间复杂度 空间复杂度
01 内置函数 O(n^2log(n)) O(n^2)
02 暴力法 O(n^3) O(n^2)
03 字典树 O(n^2) O(n^2)
func multiSearch(big string, smalls []string) [][]int {
    n := len(smalls)
    res := make([][]int, n)
    arr := suffixarray.New([]byte(big)) // 创建后缀树
    for i := 0; i < n; i++ {
        target := []byte(smalls[i])
        temp := arr.Lookup(target, -1) // 返回arr中所有target出现的位置,从后往前
        sort.Ints(temp)
        res[i] = temp
    }
    return res
}

# 2
func multiSearch(big string, smalls []string) [][]int {
    n := len(smalls)
    res := make([][]int, n)
    for i := 0; i < n; i++ {
        arr := make([]int, 0)
        if smalls[i] == "" {
            res[i] = arr
            continue
        }
        for j := 0; j+len(smalls[i]) <= len(big); j++ {
            if big[j:j+len(smalls[i])] == smalls[i] {
                arr = append(arr, j)
            }
        }
        res[i] = arr
    }
    return res
}

# 3
func multiSearch(big string, smalls []string) [][]int {
    n := len(smalls)
    res := make([][]int, n)
    root := &Trie{
        next: [26]*Trie{},
    }
    for i := 0; i < n; i++ {
        root.Insert(smalls[i], i+1)
    }
    for i := 0; i < len(big); i++ {
        temp := root.Search(big[i:])
        for j := 0; j < len(temp); j++ {
            res[temp[j]] = append(res[temp[j]], i)
        }
    }
    return res
}

type Trie struct {
    next   [26]*Trie // 下一级指针,如不限于小写字母,[26]=>[256]
    ending int       // 下标,从1开始
}

// 插入word
func (this *Trie) Insert(word string, index int) {
    temp := this
    for _, v := range word {
        value := v - 'a'
        if temp.next[value] == nil {
            temp.next[value] = &Trie{
                next:   [26]*Trie{},
                ending: 0,
            }
        }
        temp = temp.next[value]
    }
    temp.ending = index
}

// 查找
func (this *Trie) Search(word string) []int {
    arr := make([]int, 0) // 存放匹配到的下标列表
    temp := this
    for _, v := range word {
        value := v - 'a'
        if temp = temp.next[value]; temp == nil {
            return arr
        }
        if temp.ending > 0 {
            arr = append(arr, temp.ending-1)
        }
    }
    return arr
}

面试题17.18.最短超串(1)

  • 题目
假设你有两个数组,一个长一个短,短的元素均不相同。
找到长数组中包含短数组所有的元素的最短子数组,其出现顺序无关紧要。
返回最短子数组的左端点和右端点,如有多个满足条件的子数组,返回左端点最小的一个。若不存在,返回空数组。
示例 1:输入:big = [7,5,9,0,2,1,3,5,7,9,1,1,5,8,8,9,7] small = [1,5,9] 输出: [7,10]
示例 2:输入: big = [1,2,3] small = [4] 输出: []
提示: big.length <= 100000
    1 <= small.length <= 100000
  • 解题思路
No. 思路 时间复杂度 空间复杂度
01 滑动窗口 O(n) O(n)
func shortestSeq(big []int, small []int) []int {
    res := make([]int, 0)
    m := make(map[int]int)
    for i := 0; i < len(small); i++ {
        m[small[i]]++
    }
    total := len(m)
    j := 0
    for i := 0; i < len(big); i++ {
        m[big[i]]--
        if m[big[i]] == 0 {
            total--
        }
        for total == 0 {
            m[big[j]]++
            if m[big[j]] > 0 {
                total++
                if len(res) == 0 || res[1]-res[0] > i-j {
                    res = []int{j, i}
                }
            }
            j++
        }
    }
    return res

面试题17.19.消失的两个数字(4)

  • 题目
给定一个数组,包含从 1 到 N 所有的整数,但其中缺了两个数字。
你能在 O(N) 时间内只用 O(1) 的空间找到它们吗?
以任意顺序返回这两个数字均可。
示例 1:输入: [1] 输出: [2,3]
示例 2:输入: [2,3] 输出: [1,4]
提示:nums.length <= 30000
  • 解题思路
No. 思路 时间复杂度 空间复杂度
01 哈希辅助 O(n) O(n)
02 数学 O(n) O(1)
03 交换 O(n) O(1)
04 异或 O(n) O(1)
func missingTwo(nums []int) []int {
    res := make([]int, 0)
    m := make(map[int]bool)
    for i := 0; i < len(nums); i++ {
        m[nums[i]] = true
    }
    for i := 1; i <= len(nums)+2; i++ {
        if m[i] == false {
            res = append(res, i)
        }
    }
    return res
}

# 2
func missingTwo(nums []int) []int {
    n := len(nums) + 2
    sum := (1 + n) * n / 2
    total := 0
    for i := 0; i < len(nums); i++ {
        total = total + nums[i]
    }
    diff := sum - total // a+b
    mid := diff / 2     // (a+b)/2
    tempSum := (1 + mid) * mid / 2
    temp := 0
    for i := 0; i < len(nums); i++ {
        if nums[i] <= mid {
            temp = temp + nums[i]
        }
    }
    a := tempSum - temp
    b := diff - a
    return []int{a, b}
}

# 3
func missingTwo(nums []int) []int {
    res := make([]int, 0)
    nums = append(nums, -1, -1, 0)
    for i := 0; i < len(nums); i++ {
        for nums[i] != -1 && nums[i] != i {
            nums[nums[i]], nums[i] = nums[i], nums[nums[i]]
        }
    }
    for i := 1; i < len(nums); i++ {
        if nums[i] == -1 {
            res = append(res, i)
        }
    }
    return res
}

# 4
func missingTwo(nums []int) []int {
    temp := 0
    for i := 0; i < len(nums); i++ {
        temp = temp ^ nums[i]
    }
    for i := 1; i <= len(nums)+2; i++ {
        temp = temp ^ i
    }
    a := 0
    diff := temp & (-temp)
    for i := 1; i <= len(nums)+2; i++ {
        if diff&i != 0 {
            a = a ^ i
        }
    }
    for i := 0; i < len(nums); i++ {
        if diff&nums[i] != 0 {
            a = a ^ nums[i]
        }
    }
    return []int{a, a ^ temp}
}

面试题17.20.连续中值(1)

  • 题目
随机产生数字并传递给一个方法。你能否完成这个方法,在每次产生新值时,寻找当前所有值的中间值(中位数)并保存。
中位数是有序列表中间的数。如果列表长度是偶数,中位数则是中间两个数的平均值。
例如,[2,3,4]的中位数是 3
[2,3] 的中位数是 (2 + 3) / 2 = 2.5
设计一个支持以下两种操作的数据结构:
void addNum(int num) - 从数据流中添加一个整数到数据结构中。
double findMedian() - 返回目前所有元素的中位数。
示例:addNum(1)
addNum(2)
findMedian() -> 1.5
addNum(3) 
findMedian() -> 2
  • 解题思路
No. 思路 时间复杂度 空间复杂度
01 大小根堆-内置heap接口 O(log(n)) O(n)
type MinHeap []int

func (i MinHeap) Len() int {
    return len(i)
}

func (i MinHeap) Less(x, y int) bool {
    return i[x] < i[y]
}

func (i MinHeap) Swap(x, y int) {
    i[x], i[y] = i[y], i[x]
}
func (i *MinHeap) Push(v interface{}) {
    *i = append(*i, v.(int))
}

func (i *MinHeap) Pop() interface{} {
    value := (*i)[len(*i)-1]
    *i = (*i)[:len(*i)-1]
    return value
}

type MaxHeap []int

func (i MaxHeap) Len() int {
    return len(i)
}

func (i MaxHeap) Less(x, y int) bool {
    return i[x] > i[y]
}

func (i MaxHeap) Swap(x, y int) {
    i[x], i[y] = i[y], i[x]
}
func (i *MaxHeap) Push(v interface{}) {
    *i = append(*i, v.(int))
}

func (i *MaxHeap) Pop() interface{} {
    value := (*i)[len(*i)-1]
    *i = (*i)[:len(*i)-1]
    return value
}

type MedianFinder struct {
    minArr *MinHeap
    maxArr *MaxHeap
}

func Constructor() MedianFinder {
    res := new(MedianFinder)
    res.minArr = new(MinHeap)
    res.maxArr = new(MaxHeap)
    heap.Init(res.minArr)
    heap.Init(res.maxArr)
    return *res
}

func (this *MedianFinder) AddNum(num int) {
    if this.maxArr.Len() == this.minArr.Len() {
        heap.Push(this.minArr, num)
        heap.Push(this.maxArr, heap.Pop(this.minArr))
    } else {
        heap.Push(this.maxArr, num)
        heap.Push(this.minArr, heap.Pop(this.maxArr))
    }
}

func (this *MedianFinder) FindMedian() float64 {
    if this.minArr.Len() == this.maxArr.Len() {
        return (float64((*this.maxArr)[0]) + float64((*this.minArr)[0])) / 2
    } else {
        return float64((*this.maxArr)[0])
    }
}

面试题17.21.直方图的水量(4)

  • 题目
给定一个直方图(也称柱状图),假设有人从上面源源不断地倒水,最后直方图能存多少水量?直方图的宽度为 1。
上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的直方图,
在这种情况下,可以接 6 个单位的水(蓝色部分表示水)。 感谢 Marcos 贡献此图。
示例:输入: [0,1,0,2,1,0,1,3,2,1,2,1] 输出: 6
  • 解题思路
No. 思路 时间复杂度 空间复杂度
01 暴力法 O(n^2) O(1)
02 数组辅助 O(n) O(n)
03 栈辅助 O(n) O(n)
04 双指针 O(n) O(1)
func trap(height []int) int {
    res := 0
    for i := 0; i < len(height); i++ {
        left, right := 0, 0
        for j := i; j >= 0; j-- {
            left = max(left, height[j])
        }
        for j := i; j < len(height); j++ {
            right = max(right, height[j])
        }
        // 当前坐标形成的面积=(min(左边最高,右边最高)-当前高度) * 宽度(1,可省略)
        area := min(left, right) - height[i]
        res = res + area
    }
    return res
}

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

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

# 2
func trap(height []int) int {
    res := 0
    if len(height) == 0 {
        return 0
    }
    left := make([]int, len(height))
    right := make([]int, len(height))
    left[0] = height[0]
    right[len(right)-1] = height[len(height)-1]
    for i := 1; i < len(height); i++ {
        left[i] = max(height[i], left[i-1])
    }
    for i := len(height) - 2; i >= 0; i-- {
        right[i] = max(height[i], right[i+1])
    }
    for i := 0; i < len(height); i++ {
        // 当前坐标形成的面积=(min(左边最高,右边最高)-当前高度) * 宽度(1,可省略)
        area := min(left[i], right[i]) - height[i]
        res = res + area
    }
    return res
}

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

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

# 3
func trap(height []int) int {
    res := 0
    stack := make([]int, 0)
    for i := 0; i < len(height); i++ {
        for len(stack) > 0 && height[i] > height[stack[len(stack)-1]] {
            bottom := height[stack[len(stack)-1]]
            stack = stack[:len(stack)-1]
            if len(stack) > 0 {
                prev := stack[len(stack)-1]
                // 横着的面积=长(min(height[i], height[prev])-bottom)*宽(i-prev-1)
                h := min(height[i], height[prev]) - bottom
                w := i - prev - 1
                area := h * w
                res = res + area
            }
        }
        stack = append(stack, i)
    }
    return res
}

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

# 4
func trap(height []int) int {
    res := 0
    if len(height) == 0 {
        return 0
    }
    left := 0
    right := len(height) - 1
    leftMax := 0  // 左边的最大值
    rightMax := 0 // 右边的最大值
    for left < right {
        // 当前坐标形成的面积=(min(左边最高,右边最高)-当前高度) * 宽度(1,可省略)
        // 选择高度低的一边处理并求最大值, 说明当前侧最大值小于另一侧
        if height[left] < height[right] {
            // 也可以写成这样
            // leftMax = max(leftMax, height[left])
            // res = res + leftMax - height[left]
            if height[left] >= leftMax { // 递增无法蓄水
                leftMax = height[left]
            } else {
                res = res + leftMax - height[left]
            }
            left++
        } else {
            // 也可以写成这样
            // rightMax = max(rightMax, height[right])
            // res = res + rightMax - height[right]
            if height[right] >= rightMax { // 递减无法蓄水
                rightMax = height[right]
            } else {
                res = res + rightMax - height[right]
            }
            right--
        }
    }
    return res
}

面试题17.22.单词转换(2)

  • 题目
给定字典中的两个词,长度相等。写一个方法,把一个词转换成另一个词, 但是一次只能改变一个字符。
每一步得到的新词都必须能在字典中找到。
编写一个程序,返回一个可能的转换序列。如有多个可能的转换序列,你可以返回任何一个。
示例 1:输入:beginWord = "hit", endWord = "cog",
wordList = ["hot","dot","dog","lot","log","cog"]
输出:["hit","hot","dot","lot","log","cog"]
示例 2:输入:beginWord = "hit" endWord = "cog"
wordList = ["hot","dot","dog","lot","log"]
输出: []
解释: endWord "cog" 不在字典中,所以不存在符合要求的转换序列。
  • 解题思路
No. 思路 时间复杂度 空间复杂度
01 广度优先搜索 O(n^2) O(n^2)
02 广度优先搜索 O(n^2) O(n^2)
func findLadders(beginWord string, endWord string, wordList []string) []string {
    m, preMap := make(map[string]int), make(map[string][]string)
    for i := 0; i < len(wordList); i++ {
        m[wordList[i]] = 1
    }
    if m[endWord] == 0 {
        return nil
    }
    for i := 0; i < len(wordList); i++ {
        for j := 0; j < len(wordList[i]); j++ {
            newStr := wordList[i][:j] + "*" + wordList[i][j+1:]
            preMap[newStr] = append(preMap[newStr], wordList[i])
        }
    }
    visited := make(map[string]bool)
    queue, path := make([]string, 0), make([][]string, 0)
    queue, path = append(queue, beginWord), append(path, []string{beginWord})
    for len(queue) > 0 {
        length := len(queue)
        for i := 0; i < length; i++ {
            for j := 0; j < len(beginWord); j++ {
                newStr := queue[i][:j] + "*" + queue[i][j+1:]
                temp := make([]string, len(path[i]))
                copy(temp, path[i])
                for _, word := range preMap[newStr] {
                    if word == endWord {
                        return append(temp, endWord)
                    } else if visited[word] == false {
                        visited[word] = true
                        queue, path = append(queue, word), append(path, append(temp, word))
                    }
                }
            }
        }
        queue, path = queue[length:], path[length:]
    }
    return nil
}

# 2
func findLadders(beginWord string, endWord string, wordList []string) []string {
    m := make(map[string]int)
    for i := 0; i < len(wordList); i++ {
        m[wordList[i]] = 1
    }
    if m[endWord] == 0 {
        return nil
    }
    preMap := make(map[string][]string)
    for i := 0; i < len(wordList); i++ {
        for j := 0; j < len(wordList[i]); j++ {
            newStr := wordList[i][:j] + "*" + wordList[i][j+1:]
            if _, ok := preMap[newStr]; !ok {
                preMap[newStr] = make([]string, 0)
            }
            preMap[newStr] = append(preMap[newStr], wordList[i])
        }
    }
    visited := make(map[string]bool)
    count := 0
    queue := make([]string, 0)
    queue = append(queue, beginWord)
    path := make([][]string, 0)
    path = append(path, []string{beginWord})
    for len(queue) > 0 {
        count++
        node := queue[0]
        queue = queue[1:]
        arr := path[0]
        path = path[1:]
        for j := 0; j < len(beginWord); j++ {
            newStr := node[:j] + "*" + node[j+1:]
            temp := make([]string, len(arr))
            copy(temp, arr)
            for _, word := range preMap[newStr] {
                if word == endWord {
                    return append(temp, endWord)
                }
                if visited[word] == false {
                    visited[word] = true
                    queue = append(queue, word)
                    path = append(path, append(temp, word))
                }
            }
        }
    }
    return nil
}

面试题17.23.最大黑方阵

题目

给定一个方阵,其中每个单元(像素)非黑即白。设计一个算法,找出 4 条边皆为黑色像素的最大子方阵。
返回一个数组 [r, c, size] ,其中r,c分别代表子方阵左上角的行号和列号,size 是子方阵的边长。
若有多个满足条件的子方阵,返回 r 最小的,若 r 相同,返回 c 最小的子方阵。
若无满足条件的子方阵,返回空数组。
示例 1:输入:
[
  [1,0,1],
  [0,0,1],
  [0,0,1]
]
输出: [1,0,2]
解释: 输入中 0 代表黑色,1 代表白色,标粗的元素即为满足条件的最大子方阵
示例 2:输入:
[
  [0,1,1],
  [1,0,1],
  [1,1,0]
]
输出: [0,0,1]
提示:matrix.length == matrix[0].length <= 200

解题思路

No. 思路 时间复杂度 空间复杂度
01 暴力法 O(n^2) O(1)


面试题17.24.最大子矩阵(3)

  • 题目
给定一个正整数、负整数和 0 组成的 N × M矩阵,编写代码找出元素总和最大的子矩阵。
返回一个数组 [r1, c1, r2, c2],其中 r1, c1 分别代表子矩阵左上角的行号和列号,r2, c2 分别代表右下角的行号和列号。
若有多个满足条件的子矩阵,返回任意一个均可。
注意:本题相对书上原题稍作改动
示例:输入:
[
  [-1,0],
  [0,-1]
]
输出:[0,1,0,1]
解释:输入中标粗的元素即为输出所表示的矩阵
说明:1 <= matrix.length, matrix[0].length <= 200
  • 解题思路
No. 思路 时间复杂度 空间复杂度
01 前缀和+最大子序和 O(n^3) O(n^2)
02 前缀和+最大子序和 O(n^3) O(n^2)
03 最大子序和 O(n^3) O(n)
func getMaxMatrix(matrix [][]int) []int {
    n, m := len(matrix), len(matrix[0])
    arr := make([][]int, n+1)
    for i := 0; i <= n; i++ {
        arr[i] = make([]int, m+1)
    }
    for i := 0; i < n; i++ {
        for j := 0; j < m; j++ {
            arr[i+1][j+1] = matrix[i][j] + arr[i+1][j] + arr[i][j+1] - arr[i][j]
        }
    }
    maxValue := math.MinInt32
    res := make([]int, 0)
    for a := 1; a <= n; a++ { // 上边界
        for b := a; b <= n; b++ { // 下边界
            left := 1
            value := 0
            for right := 1; right <= m; right++ {
                value = arr[b][right] - arr[b][left-1] - arr[a-1][right] + arr[a-1][left-1]
                if value > maxValue {
                    maxValue = value
                    res = []int{a - 1, left - 1, b - 1, right - 1}
                }
                if value < 0 {
                    value = 0
                    left = right + 1
                }
            }
        }
    }
    return res
}

# 2
func getMaxMatrix(matrix [][]int) []int {
    n, m := len(matrix), len(matrix[0])
    arr := make([][]int, n+1)
    for i := 0; i <= n; i++ {
        arr[i] = make([]int, m+1)
    }
    for i := 0; i < n; i++ {
        for j := 0; j < m; j++ {
            arr[i+1][j+1] = matrix[i][j] + arr[i+1][j] + arr[i][j+1] - arr[i][j]
        }
    }
    maxValue := math.MinInt32
    res := make([]int, 0)
    for a := 0; a < n; a++ { // 上边界
        for b := a; b < n; b++ { // 下边界
            left := 0
            value := 0
            for right := 0; right < m; right++ {
                value = arr[b+1][right+1] - arr[b+1][left] - arr[a][right+1] + arr[a][left]
                if value > maxValue {
                    maxValue = value
                    res = []int{a, left, b, right}
                }
                if value < 0 {
                    value = 0
                    left = right + 1
                }
            }
        }
    }
    return res
}

# 3
func getMaxMatrix(matrix [][]int) []int {
    n, m := len(matrix), len(matrix[0])
    maxValue := math.MinInt32
    res := make([]int, 0)
    for a := 0; a < n; a++ { // 上边界
        arr := make([]int, m)
        for b := a; b < n; b++ { // 下边界
            left := 0
            value := 0
            for right := 0; right < m; right++ {
                arr[right] = arr[right] + matrix[b][right]
                value = value + arr[right]
                if value > maxValue {
                    maxValue = value
                    res = []int{a, left, b, right}
                }
                if value < 0 {
                    value = 0
                    left = right + 1
                }
            }
        }
    }
    return res
}

面试题17.26.稀疏相似度(1)

  • 题目
两个(具有不同单词的)文档的交集(intersection)中元素的个数除以并集(union)中元素的个数,
就是这两个文档的相似度。
例如,{1, 5, 3} 和 {1, 7, 2, 3} 的相似度是 0.4,其中,交集的元素有 2 个,并集的元素有 5 个。
给定一系列的长篇文档,每个文档元素各不相同,并与一个 ID 相关联。
它们的相似度非常“稀疏”,也就是说任选 2 个文档,相似度都很接近 0。
请设计一个算法返回每对文档的 ID 及其相似度。
只需输出相似度大于 0 的组合。请忽略空文档。
为简单起见,可以假定每个文档由一个含有不同整数的数组表示。
输入为一个二维数组 docs,docs[i]表示id 为 i 的文档。
返回一个数组,其中每个元素是一个字符串,代表每对相似度大于 0 的文档,
其格式为 {id1},{id2}: {similarity},其中 id1 为两个文档中较小的 id,similarity 为相似度,
精确到小数点后 4 位。以任意顺序返回数组均可。
示例:输入: 
[
 [14, 15, 100, 9, 3],
 [32, 1, 9, 3, 5],
 [15, 29, 2, 6, 8, 7],
 [7, 10]
]
输出:
[
 "0,1: 0.2500",
 "0,2: 0.1000",
 "2,3: 0.1429"
]
提示:docs.length <= 500
docs[i].length <= 500
  • 解题思路
No. 思路 时间复杂度 空间复杂度
01 哈希辅助 O(n^3) O(n^2)
func computeSimilarities(docs [][]int) []string {
    res := make([]string, 0)
    n := len(docs)
    m := make(map[[2]int]int)
    m1 := make(map[int][]int) // 字符出现的位置
    for i := 0; i < n; i++ {
        for j := 0; j < len(docs[i]); j++ {
            char := docs[i][j]
            for _, v := range m1[char] {
                m[[2]int{v, i}]++
            }
            m1[char] = append(m1[char], i)
        }
    }
    for k, v := range m {
        x := v
        y := len(docs[k[0]]) + len(docs[k[1]]) - v
        res = append(res, fmt.Sprintf("%d,%d: %.4f",
            k[0], k[1], float64(x)/float64(y)+1e-9))
    }
    return res
}
Copyright © Zhi2014 2023 all right reserved,powered by Gitbook该文件修订时间: 2023-10-23 15:20:51

results matching ""

    No results matching ""