The second day of algorithm practice in 2022 winter vacation (1.11)

Posted by MsAngel on Tue, 11 Jan 2022 12:47:41 +0100

**
Preface: prepared by Golang

leetcode

Title: 162 Find peak

Binary decomposition method: time complexity: O(log n), space complexity: O(1)
Logic Description:
When looking for the peak, because the title says that just look for one. And nums[-1] and nums[len (nums)] are expressed as negative infinity, so the following situations can be considered:

  1. When the whole array is increasing or decreasing, the peak must be the head element or tail element, because the edge boundaries are negative infinite, and the adjacent numbers are smaller than themselves.
  2. When the array length is 1, the value starting with left is returned directly.
  3. When the peak is in the middle of the array (there are elements next to it), we consider using dichotomy. The meaning of the question also says that the time complexity must be log n, so it indicates that dichotomy is used. There are many ways to find dichotomy. The most important thing is how to write the boundary conditions (when the left and right change). If you want to find a peak, Then the number we are looking for must be the last value of an incremental sequence, so the idea can come out at this time. When num [mid] < num [mid + 1], the value of left should go to the right (which side is big and which direction is going, and the large interval must contain the peak of the incremental sequence)
func findPeakElement(nums []int) int {
    left, right := 0, len(nums)-1
    for left < right {
        mid := (left + right) / 2
        if nums[mid] < nums[mid+1] {
            left = mid + 1
        } else {
            right = mid
        }
    }
    return left
}

Title: 153 Find the smallest value in the rotation sort array

Linear solution: time complexity: O(n), space complexity: O(1)
Logic Description:
If the time complexity is O(n), it is actually relatively simple. The complex is a little boundary condition, which directly traverses from the front to the back. Because the array itself is orderly and only rotates a little, if num [i] > num [i + 1], then num [i + 1] is the minimum value we want to find. When the array length is 1, the unique element is returned directly.

func findMin(nums []int) int {
    ans := nums[0]
    if len(nums) == 1 {
        return nums[0]
    }
    for i := 0;i < len(nums)-1;i++ {
        if nums[i] > nums[i+1] {
            ans = nums[i+1]
        }
    }
    return ans
}

Binary decomposition method: time complexity: O(log n), space complexity: O(1)
Logic Description:
The most important thing is to clarify the judgment conditions. According to the meaning of the question, we can try to infer:

  1. When num [mid] < num [high], it indicates that the minimum value is at or before mid, so high = mid
  2. When num [mid] > num [high], it indicates that the minimum value is after mid (mid is not included at this time)
  3. Cycle through the search in turn. When low == high, the minimum value has been found.
func findMin(nums []int) int {
    low, high := 0, len(nums) - 1
    for low < high {
        mid := (high + low) / 2
        if nums[mid] < nums[high] {
            high = mid
        } else {
            low = mid + 1
        }
    }
    return nums[low]
}

Title: Sword finger Offer II 004 A number that appears only once

Sorting comparison solution: time complexity: O(nlog n), time complexity: O(1)

Logic Description:
Sort first, and then find the elements with unequal adjacent left and right.

func singleNumber(nums []int) int {
    if len(nums) == 1 {
        return nums[0]
    }
    sort.Ints(nums)
    if nums[0] != nums[1] {
        return nums[0]
    }
    index := -1
	for i := 1;i < len(nums)-1;i++ {
		if nums[i-1] != nums[i] && i+1 < len(nums) && nums[i] != nums[i+1] {
			index = i
		}
	}
    if index == -1 { index = len(nums)-1 }
    return nums[index]
}

map solution: time complexity: O(n), time complexity: O(n)

func singleNumber(nums []int) int {
	// Create a map, key in the element value, and save the number of occurrences
    freq := map[int]int{}
    // If the same elements appear, add one to the number
    for _, v := range nums {
        freq[v]++
    }
    // The output map value has only 1 number
    for num, occ := range freq {
        if occ == 1 {
            return num
        }
    }
    return 0 // No, the data guarantees that an element appears only once
}

Binary solution: time complexity: O(nlog k), space complexity: O(1)
Because the problem indicates the maximum range, the maximum value of log k is fixed (32), so the time complexity can also be said to be 32n = O(n)

Logic Description:
The binary solution is adopted and the characteristics of bit operator are used to solve:
First, we can make sure that all the values in the array are within 2 ^ 32-1, so the outer loop is 32.
Secondly, understand bit operators and the characteristics of binary,

Our final answer can also be composed of 32-bit binary digits (0 / 1)
Core algorithm: we need to use total to add the numbers on the same binary bit of all the numbers in the array. How to find this number? Num > > I & 1, Num > > I is the result of an array value shifted I bits to the right. At this time, the i-th binary value we want is on the last bit of num > > I. how to pick out this last bit, that is, then sum with 1:

for instance:
If we ask for the first digit of 5 (the first digit from right to left, that is, from low to high, and the lowest digit is 0)
The binary writing method of 5 is: 101, then 5 > > 1 is 010. The whole is moved back by one bit and 0 is filled in front. At this time, the first bit we want, that is, 0, is now at the end. Next, perform & operation on 010 and 1, that is, let 010 and 001 perform & operation. In the above table, it is said that only when both bits are 1 is 1. In other cases, it is 0. Then:
010(5 >> 1)
001(1)
000 (result)
This is not the first thing we want, and so on

Now back to the meaning of the question, we add up all the values on the same bit in the array, and finally find the remainder with 3, because there is only one number, and the others are three. The result after the remainder must be a binary value of our answer. If the result after the remainder is equal to 1, return to bit i from the end through the < < operator, To perform or operation with ans, fill in 1. It's ok if it's equal to 0, because the value of a bit of binary is 0 by default. And so on. Splice the values of each bit together. Finally, the ANS we really want. Finally, just return.

func singleNumber(nums []int) int {
    ans := 0
    for i := 0; i <= 31; i++ {
        total := 0
        for _, num := range nums {
            total += num >> i & 1
        }
        if total%3 == 1 {
            ans |= 1 << i
        }
    }
    return ans
}

Title: Sword finger Offer II 005 Maximum product of word length


Bit operation + pre calculation solution: time complexity: O((m+n) * n), space complexity: O(n)

Logic Description:
It can be thought that a binary bit can replace whether a letter appears. 1 has it and 0 has none
The pre calculation process is actually the same as the ans filling 1 in the above topic. First, traverse the entire array, take out each string, and then traverse each string. If a character appears, put a position bit 1. If the string is abce, Then take bit 1 of positions 0, 1, 2 and 4 (also from low to high, of course, from high to low, because this problem is not the sum of two numbers, but to judge the same position without repetition),
Core statement 1: bitmask | = 1 < < (words [i] [J] - 'a')
words[i][j] - 'a' is to find out how many bits need to be entered, and then 1 < < (words[i][j] - 'a') is to put the binary position bit 1. Finally, through the or operation, fill 1 into the bitmask, and so on. Every word in the loop, there is a unique bitmask corresponding to it, and then put it into the masksMap. Why is there an if condition? Because the meaning of the question is to find the maximum value, For example, the words "ab" and "aaabbbab" are the same bitmap, so for the maximum value, of course, we take the length of the long word.
Core statement 2: (X & Y) = = 0
This is a judgment condition. After the previous preparations are completed, the next step is to go to the comparison link. Traverse the masksMap through two circular statements and compare them one by one. How can we know the difference between the two words? This is the role of core statement 2. If you are familiar with the usage of and operation, you can know at a glance. If you don't understand it very well, I can easily understand that when the value of each bit of the two binary numbers is different, it will finally return 0. If one or more bits are different, it will return another number. It is relatively simple. If the judgment conditions are available, when the two numbers are 0 after the operation, multiply the length, and then take the maximum value at any time, and finally return.

func maxProduct(words []string) int {
	n := len(words)
	masksMap := make(map[int]int)
	for i := 0; i < n; i++ {
		bitMask := 0
		for j := 0; j < len(words[i]); j++ {
			bitMask |= 1 << (words[i][j] - 'a')
		}
		if len(words[i]) > masksMap[bitMask] {
			masksMap[bitMask] = len(words[i])
		}
	}
	ans := 0
	for x, xItem := range masksMap {
		for y, yItem := range masksMap {
			if (x & y) == 0 {
				len := xItem * yItem
				if len > ans {
					ans = len
				}
			}
		}
	}
	return ans
}

Title: Sword finger Offer II 006 Sort the sum of two numbers in the array

Double pointer solution: time complexity: O(n), space complexity: O(1)
Logic Description:
It is relatively simple. The sequence is incremented, with double pointers, left and right. The initial values are the beginning and end. When the sum of the two numbers is greater than the target, right –. When the sum of the two numbers is less than the target, left + + until it is equal to the target, then load the left and right into the ans array, and finally return.

func twoSum(numbers []int, target int) []int {
    ans := make([]int, 2)
    lp, rp := 0, len(numbers)-1
    for {
        if(numbers[lp]+numbers[rp] > target){
            rp--
        } else if(numbers[lp]+numbers[rp] < target){
            lp++
        } else {
            ans[0] = lp
            ans[1] = rp
            break
        }
    }
    return ans
}

Topics: Go Algorithm data structure leetcode