Implementation of sword finger offer go version Chapter 3: high quality code

Posted by DeathStar on Wed, 29 Dec 2021 13:01:57 +0100

Main purpose of this chapter

It mainly focuses on high-quality code, completeness, standardization and robustness.
The writing of test cases is usually done hastily in order to complete the unit test coverage. After studying this chapter, we can find that our previous test cases are very poor in practicability. There are only normal cases, and there is only one in many cases. After that, I also follow the use cases from three aspects: 1. Functional test, 2. Negative test, and 3. Boundary test. Negative test can be understood as error test and abnormal test.

Officially start Chapter 3

Interview question 11: integer power of value

leetcode-cn.com/problems/shu-zhi-d...
According to the meaning of the topic, it is easy to write the basic logic code.

func myPow(x float64, n int) float64 {
   var result = 1.0

   if n < 0 {
       x = 1 / x
       n = -n
   }
   for i := 0; i < n; i++ {
       result *= x
   }
   return result
}

Unfortunately, the timeout will be prompted, which is very embarrassing. So I read the logic of the original text and implemented it again. The logic is as follows:
Fast power algorithm
Algorithm flow:
When x = 0: directly return 0 (avoid error reporting in subsequent x = 1 / x operations).
Initialize res = 1
When n < 0: convert the problem to the range of n ≥ 0, that is, execute x = 1/x, n = - n;
Loop calculation: jump out when n = 0;
When n & 1 = = 1: multiply the current x into res (i.e. res *= x);
Execute x = x^2 (i.e. x *= x)
Perform n shift right one bit (i.e. n > > = 1).
Return res

func myPow(x float64, n int) float64 {
    var result = 1.0

    if n < 0 {
        x = 1 / x
        n = -n
    }
    for ; n > 0; {
        if n&1 == 1 { //I also learned a bit operation, which is equivalent to n% 2 = = 1
            result *= x
        }
        x *= x
        n >>= 1 // Halve the index
    }
    return result
}

Interview question 12: print 1 to the maximum n digits

leetcode-cn.com/problems/da-yin-co...
Although it can pass leetcode, it is not in line with the purpose of making questions in the book, because we should consider the case of large numbers. The idea given in the book is to convert it into a string for output

func printNumbers(n int) []int {
   res := make([]int,0)
   var count = 1
   for i := 1; i < n; i++ {
       count *= 10
   }

   for i := 0; i < count; i++ {
       res = append(res,i)
   }

   return res
}

This does not include large numbers, which is not the original intention of the book. It is revised as follows:
The integrity of the code is investigated. The return should be [] string instead of [] int. depending on the specific code, there is no way to verify it in leetcode. You can only print the log yourself.

func printNumbers(n int) []string {
    if n <= 0 {
        return nil
    }
    res := make([]int, 0)
    result := make([]string, 0)
    number := make([]int, n)
    for !increment(number) {
        for i := 0; i < len(number); i++ {
            res = append(res, number[i])
        }
    }
    //The array is divided into n bits and is an array
    temp := make([]int, 0)
    for i := 0; i < len(res); i++ {
        temp = append(temp, res[i])
        if (i+1)%n == 0 { // The remainder of 3 is 0. The description is an integral multiple of n
            result = append(result, getString(temp))
            temp = make([]int, 0)
        }
    }
    return result
}

func getString(number []int) string {
    var res string
    isBegin := false
    for i := 0; i < len(number); i++ {
        if number[i] != 0 {
            isBegin = true
        }
        if isBegin {
            res += fmt.Sprintf("%d", number[i])
        }
    }
    return res
}

func increment(number []int) bool {
    var isOverFlow = false //Overflow end
    var nTakeOver = 0      // carry
    var nLength = len(number)
    for i := nLength - 1; i >= 0; i-- {
        nSum := number[i] + nTakeOver
        if nLength-1 == i {
            nSum++
        }
        if nSum >= 10 {
            if i == 0 {
                isOverFlow = true
            } else {
                nSum -= 10
                nTakeOver = 1
                number[i] = nSum
            }
        } else {
            number[i] = nSum
            break
        }
    }
    return isOverFlow
}

Interview question 13: delete linked list nodes at O(1) time

leetcode-cn.com/problems/shan-chu-...
leetcode requires less time complexity than books

// Regardless of O (1), traversal is the most direct
func deleteNode(head *ListNode, val int){
   if head == nil {
       return
   }
   for head.Next != nil {
       if head.Next.Val == val {
           head.Next = head.Next.Next
       }
       head = head.Next
   }
}

The linked list needs false head, new linked list, double pointer and other aids. At present, it is the best example of using false head. It is different from that in the book. Ha, the deletion in the textbook is over and there is no need to return. The force buckle is to return to the head node after deletion

func deleteNode(head *ListNode, val int) *ListNode {
   //for head.Next != nil {
   //    if val == head.Next.Val {
   //        head = head.Next.Next / / delete the node. This is the end.
   //    }
   //}
   dummy := &ListNode{}// Generate a new linked list
   tail := dummy
   for head != nil {
       if head.Val != val {
           //Add to new linked list
           tail.Next = head
           tail = head
       }
       head = head.Next
   }
   tail.Next = nil //Set tail to empty
   return dummy.Next
}

In the above example, the space complexity is O(n). If it wants to be 1, it can only be passed according to the parameters in the textbook. Otherwise, it cannot be realized

func deleteNode(head *ListNode, deleteNode *ListNode) {
   if head == nil || deleteNode == nil {
       return
   }
   if deleteNode.Next != nil {//Not a tail node
       //Delete the next value of the node, replace the node to be deleted, and then delete the next node, which is equivalent to deleting the current node
       deleteNode.Val = deleteNode.Next.Val
       deleteNode.Next = deleteNode.Next.Next
   }else if head == deleteNode {//The head node is deleted, and there is only one node
       head = nil
   }else{ // For multiple nodes, the tail node is deleted. Don't the first if condition include it?
       for head.Next != deleteNode {
           head = head.Next
       }
       head.Next = nil
   }
}

Interview question 14: adjust the array order so that odd numbers precede even numbers

leetcode-cn.com/problems/diao-zhen...
Idea: two pointers, one pointing to the head and the other pointing to the tail. Both sides move to the middle at the same time. If the value of the head pointer is even and the tail pointer is odd, it will be exchanged
Generality: change the judgment condition to function to judge. Separate num [left] & 1 = = 1 and num [right] & 1 = = 0 and replace them with functions. It's very simple to add them by yourself.

func exchange(nums []int) []int {
    if len(nums) == 0 {
        return nil
    }
    var left = 0
    var right = len(nums) - 1

    for left < right {
        for left < right && nums[left]&1 == 1 { //Odd, shift right, left
            left++
        }
        for left < right && nums[right]&1 == 0 { //Even, shift left right
            right--
        }
        if left < right { //exchange
            nums[left], nums[right] = nums[right], nums[left]
        }
    }
    return nums
}

Interview question 15: the penultimate node in the lin k ed list

leetcode-cn.com/problems/lian-biao...
Idea: two pointers, one pointing to the head and the other taking k steps first, referred to as fast and slow pointer for short

func getKthFromEnd(head *ListNode, k int) *ListNode {
   if head != nil {
       return nil
   }
   var first = head
   var second = head
   for i := 0; i < k; i++ {
       second = second.Next
   }else{//k is longer than the length of the entire linked list. Return to nil directly. If you don't want this step, you can still pass, and the robustness will become worse
        return nil
   }

   for second != nil {
       first = first.Next
       second = second.Next
   }
   return first
}

Interview question 16: reverse linked list

leetcode-cn.com/problems/UHnkqh/
For this problem, if there is no limit to return the header node and only output, the implementation methods are N kinds, and the array is also possible. However, the requirement here is to return the inverted head node, so auxiliary means are required.

//You need to save the node to prevent it from being found after disconnection
func reverseList(head *ListNode) *ListNode {
   var prevHead *ListNode //Save the previous node traversed
   var currentNode = head //Save the current node traversed
   for currentNode != nil {
       nextNode := currentNode.Next
       currentNode.Next = prevHead
       prevHead = currentNode
       currentNode = nextNode
   }
   return prevHead
}

According to the practice in the textbook, I copied the code. However, I feel that there is a little problem with this code, but I don't know where the problem is. I hope you can point out what you know and correct it. Thank you very much.

func reverseList(head *ListNode) *ListNode {
    var reversedHead *ListNode //Inverted head node
    var prev *ListNode
    var node = head
    for node != nil {
        next := node.Next
        if next == nil {
            reversedHead = node
        }
        node.Next = prev
        prev = node
        node = next
    }
    return reversedHead
}

Interview question 17: merge two sorted linked lists

leetcode-cn.com/problems/he-bing-l...
It also uses the false head of the linked list and the new linked list as an auxiliary

func mergeTwoLists(l1 *ListNode, l2 *ListNode) *ListNode {
    dummy := &ListNode{}
    tail := dummy
    for l1 != nil && l2 != nil {
        if l1.Val < l2.Val {
            tail.Next = l1
            l1 = l1.Next
        } else {
            tail.Next = l2
            l2 = l2.Next
        }
        tail = tail.Next
    }
    if l1 != nil {// If l1 there is, splice directly to the back
        tail.Next = l1
        tail = tail.Next
        l1 = l1.Next
    }

    if l2 != nil {//If l2 there are any, directly splice the back
        tail.Next = l2
        tail = tail.Next
        l2 = l2.Next
    }
    return dummy.Next
}

Interview question 18: substructure of tree

leetcode-cn.com/problems/shu-de-zi...
Input two binary trees a and B to judge whether B is the substructure of A. (the contract empty tree is not a substructure of any tree)
Mainly related to trees, there is no simple level. Of course, there is no simple level related to recursion. Not familiar with recursion and tree, this problem needs some time to understand.

func isSubStructure(A *TreeNode, B *TreeNode) bool {
    var result = false
    if A != nil && B != nil {
        //First compare the root node, and then compare the left and right subtree nodes
        if A.Val == B.Val {
            //If the root node is satisfied, it is judged that it is not a substructure
            result = isStructure(A,B) //Without recursion, it can also be written as the judgment of left and right subtrees
        }
        //The left subtree of the comparison is the root node and starts to traverse the comparison
        if !result {
            result = isSubStructure(A.Left,B)
        }
        //Compare the right subtree as the root node and start traversing the comparison
        if !result {
            result = isSubStructure(A.Right,B)
        }
    }
    return result
}

func isStructure(a *TreeNode, b *TreeNode) bool {
    if b == nil {//Description tree B has been matched (beyond the leaf node), so it returns true
        return true
    }
    if a == nil {//It indicates that the leaf node of tree A has been crossed, that is, the matching fails, and false is returned
        return false
    }
    if a.Val != b.Val {//If the values are different, matching fails and false is returned
        return false
    }
    return isStructure(a.Left,b.Left) && isStructure(a.Right,b.Right)
}
``````go
func isSubStructure(A *TreeNode, B *TreeNode) bool {
    var result = false
    if A != nil && B != nil {
        //First compare the root node, and then compare the left and right subtree nodes
        if A.Val == B.Val {
            //If the root node is satisfied, it is judged that it is not a substructure
            result = isStructure(A,B) //Without recursion, it can also be written as the judgment of left and right subtrees
        }
        //The left subtree of the comparison is the root node and starts to traverse the comparison
        if !result {
            result = isSubStructure(A.Left,B)
        }
        //Compare the right subtree as the root node and start traversing the comparison
        if !result {
            result = isSubStructure(A.Right,B)
        }
    }
    return result
}

func isStructure(a *TreeNode, b *TreeNode) bool {
    if b == nil {//Description tree B has been matched (beyond the leaf node), so it returns true
        return true
    }
    if a == nil {//It indicates that the leaf node of tree A has been crossed, that is, the matching fails, and false is returned
        return false
    }
    if a.Val != b.Val {//If the values are different, matching fails and false is returned
        return false
    }
    return isStructure(a.Left,b.Left) && isStructure(a.Right,b.Right)
}
``````go
func isSubStructure(A *TreeNode, B *TreeNode) bool {
    var result = false
    if A != nil && B != nil {
        //First compare the root node, and then compare the left and right subtree nodes
        if A.Val == B.Val {
            //If the root node is satisfied, it is judged that it is not a substructure
            result = isStructure(A,B) //Without recursion, it can also be written as the judgment of left and right subtrees
        }
        //The left subtree of the comparison is the root node and starts to traverse the comparison
        if !result {
            result = isSubStructure(A.Left,B)
        }
        //Compare the right subtree as the root node and start traversing the comparison
        if !result {
            result = isSubStructure(A.Right,B)
        }
    }
    return result
}

func isStructure(a *TreeNode, b *TreeNode) bool {
    if b == nil {//Description tree B has been matched (beyond the leaf node), so it returns true
        return true
    }
    if a == nil {//It indicates that the leaf node of tree A has been crossed, that is, the matching fails, and false is returned
        return false
    }
    if a.Val != b.Val {//If the values are different, matching fails and false is returned
        return false
    }
    return isStructure(a.Left,b.Left) && isStructure(a.Right,b.Right)
}

The following algorithms are very difficult. I don't know how long I can study them thoroughly. What is certain is that the update time must be later than that in Chapter 2 and Chapter 3.

Topics: Go Algorithm