Implementation of sword finger offer go version Chapter 5: optimizing time and space efficiency

Posted by klycette on Sun, 09 Jan 2022 04:49:31 +0100

Main purpose of this chapter

Although the main purpose of this chapter is to optimize time and space, we all know that this is our most troublesome optimization, because various principles and implementation methods are difficult to understand and innovate. Even the schemes proposed by predecessors are still difficult to implement in code or even understand. The last article said that this chapter is very difficult, which has been proved to be true. This chapter is extremely difficult. I mainly understand the optimization scheme of time and space. Most questions can be done through secondary circulation, but leetcode will prompt timeout, which is also the focus of investigation. To prevent the use of double circulation, there is little restriction on space. As long as you want, it is basically the way of space for time.

Officially start Chapter 4

Interview question 29: numbers that appear more than half in the array

leetcode-cn.com/problems/shu-zu-zh...
A number in the array appears more than half the length of the array. Please find out this number.
You can assume that the array is non empty and that there are always many elements in a given array.
It's very memory consuming. It's best to add the verification that it doesn't exist, because leetcode already assumes that it all exists, but there is no assumption in the textbook

func majorityElement(nums []int) int {
    cacheTimes := make(map[int]int)
    for i := 0; i < len(nums); i++ {
        times := cacheTimes[nums[i]]
        times++
        cacheTimes[nums[i]] = times
        fmt.Println(times)
        if times > len(nums)/2 {
            return nums[i]
        }
    }
    return 0
}

This is a question of finding confidence.

Interview question 30: minimum number of k

leetcode-cn.com/problems/zui-xiao-...
Enter the integer array arr to find the minimum number of k. For example, if you enter 8 numbers: 4, 5, 1, 6, 2, 7, 3 and 8, the minimum 4 numbers are 1, 2, 3 and 4.
A container stores the k numbers

func getLeastNumbers(arr []int, k int) []int {
    //Quick row
    //sort.Ints(arr)
    //return arr[:k]
    //Array container
    cacheSlice := make([]int, 0) //Ordered container
    for i := 0; i < len(arr); i++ {
        if len(cacheSlice) >= k {
            //isChange := false
            for j := k-1; j >0; j-- {
                fmt.Println("cacheSlice[j]",cacheSlice[j],arr[i])
                if cacheSlice[j] > arr[i] {
                    //isChange = true
                    cacheSlice[j] = arr[i]
                    sort.Ints(cacheSlice)//It needs to be reordered once. It must timeout
                    break
                }
            }
        } else {
            //fmt.Println(arr[i])
            cacheSlice = append(cacheSlice,arr[i])
            sort.Ints(cacheSlice)
        }
    }
    return cacheSlice
}

Heap implementation

func (h IHeap) Len() int           { return len(h) }
func (h IHeap) Less(i, j int) bool { return h[i] > h[j] }
func (h IHeap) Swap(i, j int)      { h[i], h[j] = h[j], h[i] }

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

func (h *IHeap) Pop() interface{} {
    old := *h
    n := len(old)
    x := old[n-1]
    *h = old[0 : n-1]
    return x
}

// Time and memory consumption are very large. The focus is on the implementation method. Don't care about these details
func getLeastNumbers(arr []int, k int) []int {
    cacheHeap := &IHeap{}
    heap.Init(cacheHeap)
    for i := 0; i < len(arr); i++ {
        heap.Push(cacheHeap, arr[i])
        if cacheHeap.Len() > k {
            heap.Pop(cacheHeap)
        }
    }
    cacheSlice := make([]int, k, k)
    for i := 0; i < k; i++ {
        cacheSlice[i] = heap.Pop(cacheHeap).(int)
    }
    return cacheSlice
}

Interview question 31: maximum sum of continuous subarrays

leetcode-cn.com/problems/lian-xu-z...
dynamic programming

func maxSubArray(nums []int) int {
    length := len(nums)
    if length == 0 {
        return 0
    }
    dp := make([]int, length, length)
    dp[0] = nums[0]
    maxSum := dp[0]
    for i := 1; i < len(nums); i++ {
        if dp[i-1] < 0 {
            dp[i] = nums[i]
        } else {
            dp[i] = dp[i-1] + nums[i]
        }
        if maxSum < dp[i] {
            maxSum = dp[i]
        }
    }
    return maxSum
}

Number of occurrences of 1 in interview question 32:1 ~ n integer

leetcode-cn.com/problems/1nzheng-s...
Enter an integer n and find the number of occurrences of 1 in the decimal representation of N integers 1 ~ n.
Difficult level questions

// Timeout, at least can be achieved in the form of string
func countDigitOne(n int) int {
    count := 0
    for i := 0; i <= n; i++ {
        intStr := fmt.Sprintf("%s",i)
        for _,v := range intStr{
            if v == '1' {
                count++
            }
        }
    }
    return count
}

After reading the official answer, I said I didn't understand it. The level was too poor. I shared it because I wanted to make up 😄

func countDigitOne(n int) (ans int) {
    // mulk means 10^k
    // In the following code, you can find that K is not used directly (all use 10^k)
    // But in order to make the code look more intuitive, k is retained here
    for k, mulk := 0, 1; n >= mulk; k++ {
        ans += (n/(mulk*10))*mulk + min(max(n%(mulk*10)-mulk+1, 0), mulk)
        mulk *= 10
    }
    return
}

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

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

Interview question 33: arrange the array into the smallest number

leetcode-cn.com/problems/ba-shu-zu...
The output can be very large, so you need to return a string instead of an integer
The spliced numbers may have leading zeros, and the leading zeros do not need to be removed in the final result
It is not simply to compare the size of two numbers, but to compare the size of two numbers after splicing
Idea: compare arrays converted into strings and splice them directly in the order from small to large. As for why, I also said that I didn't know until I read the answer.

func minNumber(nums []int) string {
    sort.Slice(nums, func(i, j int) bool {
        x := fmt.Sprintf("%d%d", nums[i], nums[j])
        y := fmt.Sprintf("%d%d", nums[j], nums[i])
        return x < y
    })
    //fmt.Println(nums)
    res := ""
    for i := 0; i < len(nums); i++ {
        res += fmt.Sprintf("%d", nums[i])
        //fmt.Println(res)
    }
    return res
}

Interview question 34: ugly number

leetcode-cn.com/problems/chou-shu-...
We call numbers containing only qualitative factors 2, 3 and 5 ugly numbers. Find the nth Ugly Number in the order from small to large. Traditionally, we regard 1 as the first Ugly Number
n not more than 1690

// Timeout version
func nthUglyNumber(n int) int {
    count := 0
    res := 1
    for i := 1; count < n; i++ {
        if isUglyNumber(i) {
            count++
            //fmt.Println("count-> ",count,"i->",i)
            res = i
        }
    }
    return res
}
func isUglyNumber(n int) bool {
    for n%2 == 0 {
        n /= 2
    }
    for n%3 == 0 {
        n /= 3
    }
    for n%5 == 0 {
        n /= 5
    }
    return n == 1
}

//Dynamic planning space for time version

func nthUglyNumber(n int) int {
    uglyNumbers := make([]int, n)
    uglyNumbers[0] = 1
    mult2, mult3, mult5 := 0,0,0
    for i := 1; i < n; i++ {
        temp2, temp3, temp5 := uglyNumbers[mult2]*2, uglyNumbers[mult3]*3, uglyNumbers[mult5]*5
        uglyNumbers[i] = min(min(temp2, temp3), temp5)
        if uglyNumbers[i] == temp2 {
            mult2++
        }
        if uglyNumbers[i] == temp3 {
            mult3++
        }
        if uglyNumbers[i] == temp5 {
            mult5++
        }
    }
    return uglyNumbers[n-1]
}
func min(a, b int) int {
    if a < b {
        return a
    }
    return b
}

Interview question 35: the first character that appears only once

leetcode-cn.com/problems/di-yi-ge-...
Find the first character in the string s that appears only once. If not, a single space is returned. S contains only lowercase letters.

func firstUniqChar(s string) byte {
    cacheByte := make(map[byte]int)
    for i := 0; i < len(s); i++ {
        cacheByte[s[i]] = cacheByte[s[i]] + 1
    }
    for i := 0; i < len(s); i++ {
        if cacheByte[s[i]] == 1 {
            return s[i]
        }
    }
    return ' '
}

Interview question 36: reverse pairs in the array

leetcode-cn.com/problems/shu-zu-zh...
Two numbers in the array. If the previous number is greater than the following number, the two numbers form an inverse pair. Enter an array and find the total number of reverse pairs in this array.
There is no doubt that the double loop will timeout. The code is not posted, which is meaningless

// Plagiarize the official website, merge and sort, I won't, and I should strengthen my practice
func reversePairs(nums []int) int {
    return mergeSort(nums, 0, len(nums)-1)
}

func mergeSort(nums []int, start, end int) int {
    if start >= end {
        return 0
    }
    mid := start + (end-start)/2
    cnt := mergeSort(nums, start, mid) + mergeSort(nums, mid+1, end)
    tmp := []int{}
    i, j := start, mid+1
    for i <= mid && j <= end {
        if nums[i] <= nums[j] {
            tmp = append(tmp, nums[i])
            cnt += j - (mid + 1)
            i++
        } else {
            tmp = append(tmp, nums[j])
            j++
        }
    }
    for ; i <= mid; i++ {
        tmp = append(tmp, nums[i])
        cnt += end - (mid + 1) + 1
    }
    for ; j <= end; j++ {
        tmp = append(tmp, nums[j])
    }
    for i := start; i <= end; i++ {
        nums[i] = tmp[i-start]
    }
    return cnt
}

Interview question 37: the first common node of two linked lists

leetcode-cn.com/problems/liang-ge-...
Enter two linked lists and find their first common node.

//Definition for singly-linked list.
type ListNode struct {
    Val  int
    Next *ListNode
}

func getIntersectionNode(headA, headB *ListNode) *ListNode {
    lenA := getNodeLength(headA)
    lenB := getNodeLength(headB)
    longNode := headA
    shotNode := headB
    kipSteps := lenA - lenB
    if lenB > lenA {
        kipSteps = lenB - lenA
        longNode = headB
        shotNode = headA
    }
    //The long linked list takes kipSteps first, and then takes N steps with the short list to reach the same node together
    for i := 0; i < kipSteps; i++ {
        longNode = longNode.Next
    }

    for longNode != nil {
        if longNode == shotNode {
            return longNode
        }
        longNode = longNode.Next
        shotNode = shotNode.Next
    }
    return nil
}
// Get linked list length
func getNodeLength(root *ListNode) (length int) {
    tail := root
    for tail != nil {
        length++
        tail = tail.Next
    }
    return length
}

Summary

There are two difficulty levels involved here. It is conceivable that I also know a few questions by looking at the official answers. I have posted the code symbolically. I have to practice more myself. I have time to review these questions. They are classic and difficult questions. Even if they are not for the interview, they are useful in practical business. The reason why the algorithm is difficult is that we can't think of an implementation scheme. The business is simple because someone told us in detail how to operate the business step by step. It's all brain use. Practice more and you won't be wrong. The next two chapters are questions. I estimate it will take at least two weeks. It's good to have a thorough understanding before the Spring Festival. Fortunately, at the end of the year, the work will not be too busy and there will be more free time. Some people say they can't watch it. They are not in the mood or motivated. It can only be said that as an eight part essay for job hopping in an interview, you must be in no mood or energy. As a hobby, you have the right to be a university teacher and assign you more questions. In short, find a reason to stick to it. After all, your head will rust if you don't use it.

Topics: Go Algorithm