TypeScript Algorithm Questions Actual Combat - Sword Pointing to Offer (5)

One pen, one pair of hands, one Leetcode for one night!

In this article, we will use TypeScript to solve the algorithm problem of Jianzhi offer. The problems cover a wide variety of topics, including arrays, strings, linked lists, trees, sorting and searching, and more. We'll use TypeScript's strongly typed and object-oriented features to solve these problems, and walk through practical code examples to demonstrate how to use TypeScript to solve algorithmic problems.

All the questions come from the Likou question bank: "Jianzhi Offer (2nd Edition)" The questions included in this chapter are (difficulty is my personal feeling, unofficial standard):

topic difficulty
balanced binary tree Simple
number of occurrences of a number in an array medium
Number of Occurrences of a Number in an Array II medium
two numbers whose sum is s Simple
A sequence of continuous positive numbers whose sum is s Simple
flip word order Simple
The maximum value of the sliding window medium
max queue size medium
n number of dice difficulty
straight in poker Simple

1. Balanced binary tree

1.1, topic description

Enter the root node of a binary tree to determine whether the tree is a balanced binary tree. If the depth difference between the left and right subtrees of any node in a binary tree does not exceed 1, then it is a balanced binary tree.

insert image description here

insert image description here

1.2. Solution

When recursively calculating the depth of the left subtree and the right subtree, add judgment Math.abs(left - right) > 1, if >1, it means that the binary tree is already unbalanced at this time, and set the global variable res to false

function isBalanced(root: TreeNode | null): boolean {
    
    
    let res = true;
    if(root == null)
        return true;
    getHeight(root);
    function getHeight(root: TreeNode | null):number{
    
    
        if(root == null)
            return 0;
        let left = getHeight(root.left);
        let right = getHeight(root.right);
        if(Math.abs(left - right) > 1)
            res = false;
        return Math.max(left, right) + 1;
    }
    return res;
};

Second, the number of occurrences of numbers in the array

2.1, topic description

In an integer array nums, all but two numbers appear twice. Please write a program to find these two numbers that appear only once. The required time complexity is O(n), and the space complexity is O(1).

Example 1:

Input: nums = [4,1,4,6]
Output: [1,6] or [6,1]

Example 2:
Input: nums = [1,2,10,4,1,4,3,3]
Output: [2,10] or [10,2]

2.2. Solution

The first thing that comes to mind is the hash table method, maintaining a hash Map, keyfor nums[i], valuefor the number of occurrences:

function singleNumbers(nums: number[]): number[] {
    
    
    let myMap: Map<number, number> = new Map();
    for(let i = 0; i < nums.length; i++){
    
    
        myMap.set(nums[i], myMap.get(nums[i])? myMap.get(nums[i]) + 1 : 1)
    }
    let res = [];
    for(let i = 0; i < nums.length; i++) {
    
    
        if(myMap.get(nums[i]) == 1)
            res.push(nums[i]);
    }
    return res;
};

But the title requires time complexity O(N) and space complexity O(1), so the conditions are not met.
Reference: https://leetcode.cn/problems/shu-zu-zhong-shu-zi-chu-xian-de-ci-shu-lcof/solutions/572857/jian-zhi-offer-56-i-shu- zu-zhong-shu-zi-tykom/?envType=featured-list&envId=xb9nqhhg
XOR method to answer.

First of all, the XOR operation satisfies the commutative law , a⊕b=b⊕aand a⊕a=0if there is only one number that appears only once in the title, then a⊕b⊕c⊕d⊕a⊕c⊕d = a⊕a⊕c⊕c⊕d⊕d⊕c = cthe c that appears once can be completely calculated. The code is as follows:

function singleNumbers(nums: number[]): number[] {
    
    
    let x = 0;
    for(let i = 0; i < nums.length; i++){
    
    
        x ^= nums[i];
    }
    console.log(x);
    return [1,2];
};

However, what the title requires is that there are two numbers that appear only once, that is a⊕b⊕c⊕d⊕a⊕c⊕d⊕z = b⊕z, at this time, x obtains the value of the XOR operation of b and z. xIf it is obtained at this time 00100, it means that b and z are different in the third-to-last digit in binary, and if it is 00110, it means that b and z are different in the second-to-last and third-to-last digits in binary.

Since two numbers appear once in the array, then we can divide the array into two sub-arrays, and only one number that appears once in the sub-array can be XORed to get the answer. The array should be divided into two sub-arrays (sub-arrays There is only one number that appears once inside the array), which can be divided according to the nature of x, either by the highest 1 digit of x or by the lowest 1 digit of x:

function singleNumbers(nums: number[]): number[] {
    
    
    let x = 0;
    let res1 = 0, res2 = 0;
    for(let i = 0; i < nums.length; i++){
    
    
        x ^= nums[i];
    }
    let oneIndex = 1;
    while((x & oneIndex) == 0){
    
    
        oneIndex = oneIndex << 1; // m * 2
    }
    for(let i = 0; i < nums.length; i++){
    
    
        if(nums[i] & oneIndex)
            res1 = res1 ^ nums[i];
        else
            res2 = res2 ^ nums[i];
    }
    return [res1, res2];
};

3. The number of occurrences of numbers in the array II

3.1, topic description

In an array nums, except for one number that only appears once, all other numbers appear three times. Please find the number that appears only once.

Example 1:

Input: nums = [3,4,3,3]
Output: 4

Example 2:
Input: nums = [9,1,7,9,7,9,7]
Output: 1

3.2. Solution

The hash table method can also be solved, and the hash method of the previous question can be directly applied. There is a bit operation method provided by the master on Likou, which uses the design of the modulo three counter (knowledge of digital electricity), but I don't understand it very well.

A sorting method is provided here:
first, sort()sort the array using the sorted array. The sorted array should satisfy the same number and it will be sorted by a multiple of 3. Re-traverse the sorted array and traverse it with a "distance" of 3 times. Judgment nums[i] == nums[i + 2]Whether it is established or not:

function singleNumber(nums: number[]): number {
    
    
    if(nums.length == 1)
        return nums[0];
    nums.sort();
    for(let i = 0; i < nums.length; i += 3){
    
    
        if(nums[i] != nums[i + 2])
            return nums[i];
    }
    return 1;
};

4. Two numbers whose sum is s

4.1, topic description

Input an array sorted in ascending order and a number s, find two numbers in the array such that their sum is exactly s. If the sum of multiple pairs of numbers is equal to s, just output any pair.

Example 1:

Input: nums = [2,7,11,15], target = 9
Output: [2,7] or [7,2]

Example 2:
Input: nums = [10,26,30,31,47,60], target = 40
Output: [10,30] or [30,10]

4.2. Solution

Using the double-pointer method, since the array is already sorted, the left pointer points to the minimum value, and the right pointer points to the maximum value. If the current minimum value + maximum value is greater than the current value, the right pointer targetmoves one bit to the left. If the current minimum value + maximum value If less than target, the left pointer moves one bit to the right:

function twoSum(nums: number[], target: number): number[] {
    
    
    let left = 0;
    let right = nums.length;
    while(nums[left] + nums[right] != target){
    
    
        if(nums[left] + nums[right] < target)
            left++;
        else{
    
    
            right--;
        }
    }
    return [nums[left], nums[right]];
};

5. The sequence of continuous positive numbers whose sum is s

5.1, topic description

Input a positive integer targetand output all targetconsecutive positive integer sequences (contain at least two numbers) whose sum is .

The numbers in the sequence are arranged from small to large, and different sequences are arranged according to the first number from small to large.

Example 1:
Input: target = 9
Output: [[2,3,4],[4,5]]

Example 2:
Input: target = 15
Output: [[1,2,3,4,5],[4,5,6],[7,8]]

5.2. Solution

Use the sliding window method and maintain the sum of the values ​​in the current sliding window. If the sum is greater than the value, targetmove the left boundary to the right. If the sum is less than, targetmove the right boundary to the right. If the sum of the current window is equal to the value targetin the window, record the value and Push into the result set, and shift the right boundary to the right.

function findContinuousSequence(target: number): number[][] {
    
    
    let left = 1;
    let right = 1;
    let sum = 0;
    let res: number[][] = [];
    while(left <= target / 2){
    
    
        if(sum < target){
    
    
            sum = sum + right;
            right++
        }
        else if(sum > target){
    
    
            sum = sum - left;
            left++;
        }
        else{
    
    
            let tmpRes: number[] = [];
            for(let i = left; i < right; i++){
    
    
                tmpRes.push(i);
            }
            res.push(tmpRes);
            sum = sum - left;
            left ++;
        }
    }
    return res;
};

6. Flip word order

6.1, topic description

Enter an English sentence, flip the order of the words in the sentence, but keep the order of the characters within the word. For simplicity, punctuation is treated like normal letters. For example, input the string "I am a student.", then output "student. a am I".

Example 1:
Input: "the sky is blue"
Output: "blue is sky the"

Example 2:
Input: " hello world!"
Output: "world! hello"
Explanation: The input string can contain extra spaces before or after, but the reversed characters cannot be included.

Example 3:
Input: "a good example"
Output: "example good a"
Explanation: If there is an extra space between two words, reduce the space between the reversed words to only one.

6.2, problem solution

Using the split function that comes with js, you can convert the string to " "

function reverseWords(s: string): string {
    
    
    let arr = s.split(' ');
    let res = "";
    for(let i = arr.length - 1; i > -1; i--){
    
    
        if(arr[i]!='')
            res = res + arr[i] + ' ';
    }
    return res.substring(0, res.length - 1);
};

Seven, the maximum value of the sliding window

7.1, topic description

Given an array nums and a sliding window size k, find the maximum value over all sliding windows.
Example:

Input: nums = [1,3,-1,-3,5,3,6,7], and k = 3
Output: [3,3,5,5,6,7]
Explanation:

The position of the sliding window maximum value
[1 3 -1] -3 5 3 6 7 3
1 [3 -1 -3] 5 3 6 7 3
1 3 [-1 -3 5] 3 6 7 5
1 3 -1 [-3 5 3] 6 7 5
1 3 -1 -3 [5 3 6] 7 6
1 3 -1 -3 5 [3 6 7] 7

7.2. Solution

Since the nature of the sliding window is actually very similar to a queue, we can maintain a monotonically decreasing queue as an aid for judging the maximum value. The length of the queue is k. When the queue is empty, the number is pushed into the queue. When the subsequent number comes in, judge The number you want to enter the queue and the size of the current element at the end of the queue maintain a monotonic queue:

  • If the element at the end of the queue is smaller than the newly added element, the element at the end of the queue will be popped up until the element at the end of the queue is greater than the newly added element
  • If the element at the end of the queue is greater than the newly added element, it will enter the queue normally

After the window is formed, when the left changes, it is necessary to judge whether the current boundary value is the first element of the queue. If so, after the next sliding window, the first element of the queue is no longer in the window, and the queue is obtained.

function maxSlidingWindow(nums: number[], k: number): number[] {
    
    
    if(k === 1)
        return nums;
    let tmpQue = [];
    let res = [];
    let right = 0;
    for(right = 0; right < nums.length; right++){
    
    
        // 维护单调递减队列
        while(tmpQue.length != 0 && tmpQue[tmpQue.length - 1] <  nums[right]){
    
    
            tmpQue.pop();
        }
        tmpQue.push(nums[right]);

        let left = right - k + 1;
        if(left < 0)  // 窗口尚未形成
            continue;
        else{
    
      // 窗口已经形成
            res.push(tmpQue[0]);
            // 如果左边界是当前最大值
            if(nums[left] == tmpQue[0]){
    
    
                tmpQue.shift();
            }
        }
    }
    return res;
};

Eight, the maximum value of the queue

8.1, topic description

Please define a queue and implement the function max_value to get the maximum value in the queue. The amortized time complexity of the functions max_value, push_back and pop_front is required to be O(1).

If the queue is empty, pop_front and max_value need to return -1

输入:
[“MaxQueue”,“push_back”,“push_back”,“max_value”,“pop_front”,“max_value”]
[[],[1],[2],[],[],[]]
输出: [null,null,null,2,1,2]

Input:
["MaxQueue", "pop_front", "max_value"]
[[],[],[]]
Output: [null,-1,-1]

8.2, problem solution

According to the example, the meaning of the title is as follows:

  • "MaxQueue" : generate queue without input
  • "push_back" : Pass elements to the queue, you need to input a number, and output null
  • "max_value" : Find the maximum value in the queue, without input, output the maximum value in the current queue
  • "pop_front" : delete the head element of the queue and output a number

Its essence is similar to the problem of finding the maximum value of the sliding window. This queue can be regarded as a sliding window. Entering the queue means moving the right edge of the window to the right, and leaving the queue means moving the left edge of the window to the right. Maintains a monotonically decreasing queue, thus helping to store the maximum value of the queue.

class MaxQueue {
    
    
    myQueue:number[];
    maxQueue:number[];
    constructor() {
    
    
        this.myQueue = [];
        this.maxQueue = [];
    }

    max_value(): number {
    
    
        if(this.myQueue.length == 0)
            return -1;
        return this.maxQueue[0];
    }

    push_back(value: number): void {
    
    
        this.myQueue.push(value);
        while(this.maxQueue.length != 0 && this.maxQueue[this.maxQueue.length - 1] < value){
    
    
            this.maxQueue.pop();
        }
        this.maxQueue.push(value);
    }

    pop_front(): number {
    
    
        if(this.myQueue.length == 0)
            return -1;
        let res = this.myQueue.shift();
        if(res == this.maxQueue[0])
            this.maxQueue.shift();
        return res;
    }
}

/**
 * Your MaxQueue object will be instantiated and called as such:
 * var obj = new MaxQueue()
 * var param_1 = obj.max_value()
 * obj.push_back(value)
 * var param_3 = obj.pop_front()
 */

Nine, n dice points

9.1, topic description

Throw n dice on the ground, and the sum of the points of all the dice facing up is s. Enter n and print out the probabilities for all possible values ​​of s.

You need to return the answer with an array of floating point numbers, where the i-th element represents the probability of the i-th smallest of the set of points that can be thrown by the n dice.

Input: 1
Output: [0.16667,0.16667,0.16667,0.16667,0.16667,0.16667]

Input: 2
Output: [0.02778,0.05556,0.08333,0.11111,0.13889,0.16667,0.13889,0.11111,0.08333,0.05556,0.02778]

9.2, problem solution

The meaning of this question is, input 1, which is 1 dice, and the probability of casting each value (1~6), that is, 1/6 = 0.16667
input 2, which is 2 dice, at this time the probability of 2 dice The sum ranges from 2 to 12, and the probability of being 2 is (both dice are 1), that is, 1/6*1/6 = 1/36 = 0.02778. and so on.

If the violent method is used, there are 6 × 6 = 36 cases of 2 dice, and the 36 cases are aggregated into 11 results, but if the title n=11, then the 11 dice have 6 to the 11th power, and they are aggregated into 61 results As a result, if so, the time complexity can be very high.

Choose the dynamic programming method here: set dp[n][j] to indicate the number of times the sum of points is j
after throwing the nth dice, then

  • dp[1][1] = 1,dp[1][2] = 1 ... ... dp[1][6] = 1;
  • dp[2][1] = dp[1][1](在第一次投出1的情况下,此次投出1) = 1; dp[2][3] = dp[1][1](在第一次投出1的情况下,此次投出2) + dp[1][2](在第一次投出2的情况下,此次投出1) = 2; dp[2][4] = dp[1][1] + dp[1][2] + dp[1][3] = 3 ....
  • Reasoning leads to the state transition equation: d[n][j] = dp[n - 1][j - 1] + dp[n - 1][j - 2] + dp[n - 1][j - 3 ] + … + dp[n - 1][j - 6] , that is, after the nth dice is cast, the probability that the sum is j appears, after the n-1th dice is cast, the nth dice casts 1 , the sum of the cases of 2,3,4,5,6.

So it can be coded like this:

function dicesProbability(n: number): number[] {
    
    
     let dp = new Array(n + 1).fill(0).map(i => new Array(6 * n + 1).fill(0));
     for(let j = 1; j <= 6; j++){
    
    
         dp[1][j] = 1;
     }
     for(let i = 2; i <= n; i++){
    
    
        //  最小值到最大值
         for(let j = i; j<=6*i; j++){
    
    
            //  第i个骰子从1到6的情况
             for(let k = 1; k <= 6; k++){
    
    
                //  不要算dp[k - 1][j - k]为负的情况,不可能存在
                if(j - k <= 0){
    
    
                    break;
                 }
                dp[i][j] = dp[i - 1][j - k] + dp [i][j];
             }
         }
     }
    let all = Math.pow(6, n);
    let res:number[] = [];
    // 收集n到6*n的结果集次数
    for(let i = n; i <= 6 *n; i++){
    
    
        res.push(dp[n][i]/all);
    }
    return res;
};

10. Straight in poker

10.1, topic description

Randomly draw 5 cards from several decks of playing cards to determine whether it is a straight, that is, whether the 5 cards are continuous. 2-10 is the number itself, A is 1, J is 11, Q is 12, K is 13, and the big and small kings are 0, which can be regarded as any number. A cannot be considered as 14.

Input: [1,2,3,4,5]
Output: True

Input: [0,0,1,2,5]
Output: True

10.2. Solution

What should be noted in this question is that A is 1, so 10 JQKA cannot be considered a straight, and 0 is the big and small king, and it can be all five.
To play a straight, there are two characteristics:

  1. Except 0 can be repeated, other numbers cannot be repeated (otherwise there will be a pair in the sequence, such as 3,4,4,5,6, 0,3,4,5,5 does not meet the requirements)
  2. The maximum value minus the minimum value should be less than 5, such as 2,3,4,5,6, 2,0,0,4,5
  3. The maximum number is 14 and the minimum is 0
function isStraight(nums: number[]): boolean {
    
    
    let storeSet: Set<number> = new Set();
    let max = 1, min = 14;
    for(let i = 0 ; i < nums.length; i++){
    
    
        if(nums[i] !=0){
    
    
            // 判断是否出现其他不在扑克牌的数字
            if(nums[i] > 14 || nums[i] < 1){
    
    
                return false;
            }
            // 判断是否有重复数字(除0之外)
            if(storeSet.has(nums[i]))
                return false;
            storeSet.add(nums[i]);
            max = Math.max(max, nums[i]);
            min = Math.min(min,nums[i])
        }
    }
    return max - min < 5;
};

Guess you like

Origin blog.csdn.net/air__Heaven/article/details/131679010