Table of contents
- 1. Balanced binary tree
- Second, the number of occurrences of numbers in the array
- 3. The number of occurrences of numbers in the array II
- 4. Two numbers whose sum is s
- 5. The sequence of continuous positive numbers whose sum is s
- 6. Flip word order
- Seven, the maximum value of the sliding window
- Eight, the maximum value of the queue
- Nine, n dice points
- 10. Straight in poker
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.
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
, key
for nums[i]
, value
for 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⊕a
and a⊕a=0
if 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 = c
the 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. x
If 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 target
moves 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 target
and output all target
consecutive 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, target
move the left boundary to the right. If the sum is less than, target
move the right boundary to the right. If the sum of the current window is equal to the value target
in 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:
- 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)
- The maximum value minus the minimum value should be less than 5, such as 2,3,4,5,6, 2,0,0,4,5
- 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;
};