Offer to come, dig friends take it! I am participating in the 2022 Spring Recruit Punch card activity. Click here for details.
1. Time complexity and space complexity
An algorithm is a set of methods used to manipulate data and solve a program problem. The advantages and disadvantages of the algorithm can be measured by time complexity and space complexity. In general, time complexity is more problematic than space complexity, so it is time complexity that is studied more.
1.1 Time Complexity
Generally speaking, the corresponding elapsed time can be obtained by running the program once, but the results may vary depending on machine performance, data size and other factors. Theoretically, it is only necessary to obtain a time evaluation index to obtain the general trend of algorithm time consumption.
In general, the time an algorithm takes is proportional to the number of code statements executed, and the more times the code is executed, the longer it takes. We call the number of times an algorithm is executed the time frequency, which is called T(n). The asymptotic time complexity (time complexity for short) is represented by capital O, so it is also called the big O notation. The time complexity of the algorithm is T(n)=O(f(n)). It means that T(n) approaches f(n) as n approaches positive infinity. Common time complexity is O(1) constant; O(log n) log type; O(n) linear type; O(nlog n) linear log type; O(n2) square; O(2n) exponential.
The figure above shows the growth trend of different types of functions. It can be seen that with the continuous expansion of problem size N, time complexity also increases. Common algorithm in time complexity from small to large order: Ο (1) < Ο < Ο (log n) (n) < Ο nlog (n) < Ο (n2) < < Ο (2 ^ n). Value is noted that the size comparison of algorithm time complexity is only in terms of growth trend, and the opposite result may occur before a certain threshold value.
The time complexity is usually calculated by finding the execution statement and counting the number of times the statement is executed, denoted by big O. Where, when represented by big O, 1 is used to represent The Times of constant level execution, only the highest order term of the function is retained, and the coefficient in front of the highest order is removed.
int j = 0; / / 1.
for (int i = 0; i < n; i++) { / / 2.
j = i; / / 3.
j++; / / 4.
}
Copy the code
In the above code, the frequency of statement ① is 1, the frequency of statement ② is N, the frequency of statement ③ is n-1, and the frequency of statement ④ is n-1, so the time frequency T(n)=1+ N +n-1+n-1=3n-1, and the ellipse coefficient and constant term get T(n)=O(n).
1.2 Space Complexity
Space complexity refers to the size of the memory required to execute an algorithm. Similar to time complexity, space complexity is also estimated and is expressed in terms of S(n)=O(f(n)). S(n) is the space complexity and the required storage space is expressed in terms of F (n).
int j = 0;
int[] m = new int[n];
for (int i = 1; i <= n; ++i) {
j = i;
j++;
}
Copy the code
In the code above, only when m is created, the array space is allocated, and no memory space is added in the for loop, so the space complexity S(n)=O(n).
2. The title
2.1 Sum of two numbers
Leetcode address
Instead of returning two numbers whose sum is equal to the target, you cannot change the position of the subscripts in the array, so you cannot use the double pointer method. In the process of writing specific code, the use of map, and then the array of data traversal, map does not save and for the target number of another number as a key, and the number of the subscript as a value; If the number is present in the map, the map retrieves the value and the index of the number into the array and returns it.
var twoSum = function (nums, target) {
let map = new Map(a)for (let i = 0; i < nums.length; i++) {
if(! map.has(nums[i])) { map.set(target - nums[i], i) }else {
return [map.get(nums[i]), i]
}
}
return[]}.Copy the code
2.2 Sum of three numbers
Leetcode address
When finding the sum of three numbers, we use the two-pointer method, so first we need to arrange them in ascending order. The first number is obtained by traversing the array, the second and third numbers are obtained by double-pointer method, and the three numbers that meet the conditions are placed in the array each time. It is worth noting that the meaning of the question clearly states that repeated triples cannot be included, so every number determined needs to be de-duplicated to prevent repetition. In addition, after obtaining the triplet that matches the problem, it is necessary to remove the subscript first and then move it.
var threeSum = function (nums) {
if(nums.length<3) return []
nums.sort((a, b) = > a - b)
let res = []
for (let i = 0; i < nums.length - 1; i++) {
if (i > 0 && nums[i] === nums[i - 1]) continue / / to heavy
let l = i + 1, r = nums.length - 1
while (l < r) {
let sum = nums[i] + nums[l] + nums[r]
if (sum === 0) {
res.push([nums[i], nums[l], nums[r]])
while (l < r && nums[l] === nums[l + 1]) { / / to heavy
l++
}
while (l < r && nums[r] === nums[r - 1]) { / / to heavy
r--
}
l++
r--
} else if (sum < 0) {
l++
} else {
r--
}
}
}
return res
};
Copy the code
2.3 Sum of four numbers
Leetcode address
After looking at the sum of three numbers, the sum of four numbers is easy to understand. The first two numbers are obtained through array traversal, and the last two are obtained through double Pointers.
var fourSum = function (nums, target) {
if (nums.length < 4) return []
nums.sort((a, b) = > a - b)
let res = []
for (let i = 0; i < nums.length - 2; i++) {
if (i > 0 && nums[i] === nums[i - 1]) continue
for (let j = i + 1; j < nums.length - 1; j++) {
if (j > i + 1 && nums[j] === nums[j - 1]) continue
let l = j + 1, r = nums.length - 1
while (l < r) {
let sum = nums[i] + nums[j] + nums[l] + nums[r]
if (sum === target) {
res.push([nums[i], nums[j], nums[l], nums[r]])
while (l < r && nums[l] === nums[l + 1]) {
l++
}
while (l < r && nums[r] === nums[r - 1]) {
r--
}
l++
r--
} else if (sum < target) {
l++
} else {
r--
}
}
}
}
return res
}
Copy the code
2.4 The sum of the nearest three numbers
Leetcode address
It’s kind of like a sum of three, but the other thing to do here is, every time you get the sum of three you have to compare it to the previous one, and find the closest one.
var threeSumClosest = function (nums, target) {
nums.sort((a,b) = >a-b)
let res=nums[0]+nums[1]+nums[nums.length-1]
for(let i=0; i<nums.length-1; i++){let l=i+1,r=nums.length-1
while(l<r){
let sum=nums[i]+nums[l]+nums[r]
if(sum<target){
l++
}else{
r--
}
if(Math.abs(target-res)>Math.abs(target-sum)){
res=sum
}
}
}
return res
};
Copy the code
2.5 Forward traversal of binary trees
Leetcode address
The first thing to know is that a pre-traversal gets the root node, then the left subtree node, and finally the right subtree node. Of course the recursive way is relatively simple, if interested in iterative writing can be studied.
var preorderTraversal = function (root) {
let res = []
function traversal(tree) {
if(! tree)return
res.push(tree.val)
traversal(tree.left)
traversal(tree.right)
}
traversal(root)
return res
};
Copy the code
2.6 Middle order traversal of binary trees
Leetcode address
The middle-order traversal first obtains the nodes of the left subtree, then the root node, and finally the nodes of the right subtree.
var inorderTraversal = function(root) {
let result=[]
function inorder(tree){
if(! tree)return
inorder(tree.left)
result.push(tree.val)
inorder(tree.right)
}
inorder(root)
return result
};
Copy the code
2.7 Backward traversal of binary trees
Leetcode address
The preceding traversal obtains the node of the left subtree first, then the node of the right subtree, and finally the root node.
var postorderTraversal = function(root) {
let result=[]
function postorder(tree){
if(! tree)return
postorder(tree.left)
postorder(tree.right)
result.push(tree.val)
}
postorder(root)
return result
};
Copy the code
2.8 Maximum depth of a binary tree
Leetcode address
With DFS, return 0 if the node is empty, otherwise get the depth of the left and right subtrees and calculate larger values, increment 1 since the current node is not empty.
var maxDepth = function (root) {
if(! root)return 0
return Math.max(maxDepth(root.left), maxDepth(root.right)) + 1
};
Copy the code
2.9 Balanced binary trees
Leetcode address
This is the same thing as finding the maximum depth association of a binary tree. If the node is empty, it is directly judged as a balanced binary tree; otherwise, it is judged whether the height difference between the left and right subtrees is greater than 1; otherwise, it is not a balanced binary tree; otherwise, it continues to recursively judge the left and right subtrees.
var isBalanced = function (root) {
if(! root)return true
function getHeight(tree) {
if(! tree)return 0
return Math.max(getHeight(tree.left), getHeight(tree.right)) + 1
}
if (Math.abs(getHeight(root.left) - getHeight(root.right)) > 1) {
return false
}
return isBalanced(root.left) && isBalanced(root.right)
};
Copy the code
2.10 Mirroring of binary trees
Leetcode address
Returns null if the node is empty, otherwise gets a mirror image of the left and right subtrees and swaps the positions of the subtrees.
var mirrorTree = function (root) {
if(! root)return null;
let left = mirrorTree(root.right);
let right = mirrorTree(root.left);
root.left = left;
root.right = right;
return root;
}
Copy the code
2.11 Symmetric binary trees
If both trees are empty, it is a symmetric binary tree. If one is empty and the other is not empty, it is not a symmetric binary tree. Otherwise, you need to compare the values of the two nodes. If they are equal then we need to recursively compare the right node of the first tree to the left node of the second tree.
var isSymmetric = function (root) {
if(! root)return true
function compare(left, right) {
if(! left && ! right)return true
else if(! left || ! right)return false
return left.val === right.val && compare(left.left, right.right) && compare(left.right, right.left)
}
return compare(root, root)
};
Copy the code
2.12 Merge two ordered lists
Leetcode address If one list is empty, return another list. If the node value of one list is larger than that of the other list, merge the larger list and the result of the merge with the other list to return the list.
var mergeTwoLists = function (list1, list2) {
if(! list1)return list2
if(! list2)return list1
if (list1.val < list2.val) {
list1.next = mergeTwoLists(list1.next, list2)
return list1
} else {
list2.next = mergeTwoLists(list1, list2.next)
return list2
}
};
Copy the code
2.13 Intersecting linked lists
Leetcode address
The problem uses the idea of a double pointer. If either list is empty, null is returned. In the case that neither of the two linked lists is empty, the Pointers A and B point to headA and headB respectively. If the two Pointers a and B are not equal, the judgment will be made all the time. Pointer A is not empty and points to the next node, and if it is empty, it points to headB. If Pointers A and B are equal (both may be null), return one of the Pointers, and this is the intersecting node (may be null).
var getIntersectionNode = function (headA, headB) {
if (headA === null || headB === null) {
return null
}
let a = headA, b = headB
while(a ! == b) { a = a ! = =null? a.next : headB b = b ! = =null ? b.next : headA
}
return a
};
Copy the code
2.14 Deleting a Node from a linked list
Leetcode address
To delete node, change the value of node to node.next.
var deleteNode = function (node) {
node.val = node.next.val
node.next = node.next.next
};
Copy the code
2.15 Circular linked lists
Leetcode address
This problem uses the idea of fast and slow Pointers. Iterating with fast Pointers returns NULL if the next node of the fast pointer is NULL, otherwise fast Pointers take two steps and slow Pointers take one. When the fast and slow Pointers meet, it indicates that there is a ring, so the fast Pointers reset the head node, and then the fast and slow Pointers do not take another step, and when they meet again, it is the entrance of the ring.
var detectCycle = function (head) {
let fast = head, slow = head
while (fast) {
if (fast.next === null) return null
fast = fast.next.next
slow = slow.next
if (fast === slow) {
fast = head
while (true) {
if (fast === slow) return slow
fast = fast.next
slow = slow.next
}
}
}
return null
};
Copy the code
2.16 Print the linked list from end to end
Leetcode address
Use an array to store the printed result, traverse each node of the list, insert the result from the head of the array, the array is printed from the end.
var reversePrint = function (head) {
let res = []
while (head) {
res.unshift(head.val)
head = head.next
}
return res
};
Copy the code
The KTH last node in the linked list
Leetcode address
The idea of a fast or slow pointer is used. First let the fast pointer take k steps, after k steps, the fast and slow Pointers together, return the slow pointer.
var getKthFromEnd = function (head, k) {
let fast = head,slow = head
while (fast) {
fast = fast.next
k--
if (k < 0) {
slow = slow.next
}
}
return slow
}
Copy the code
2.18 Reverse the linked list
Leetcode address
Make prev the first node of head, point prev to head. Next, head. Next and head to the previous node
var reverseList = function (head) {
let prev = null
while (head) {
[head.next, prev, head] = [prev, head, head.next]
}
return prev
};
Copy the code
2.19 take the stairs
Leetcode address
The idea of dynamic programming can be used in this problem. First of all, we can think of f(x) as the number of ways to climb x stairs, so we can get the equation f(x)=f(x-1)+f(x-2), which means that the number of ways to climb X stairs is the number of ways to climb X stairs plus the number of ways to climb x stairs. If an array is used to store the number of options for each staircase, the space complexity can be obtained as O(n), but in fact, only the number of options for X stairs needs to be obtained, so the space complexity can be reduced to O(1) by using the idea of rolling array.
var climbStairs = function (n) {
let prev = 1, cur = 1
for (let i = 2; i <= n; i++) {
const temp = cur
cur += prev
prev = temp
}
return cur
};
Copy the code
2.20 Fibonacci sequence
Leetcode address
Similar to climbing stairs, it should be said that climbing stairs is an application of Fibonacci numbers.
var fib = function (n) {
if (n < 2) return n
let prev = 0, cur = 1
for (let i = 2; i <= n; i++) {
const temp = cur
cur = (cur + prev) % 1000000007
prev = temp
}
return cur
};
Copy the code
2.21 Maximum sum of contiguous subarrays
Leetcode address
This problem uses the idea of dynamic programming. Iterate over the number group, compare each number and the cumulative value after increasing the number, take a larger value, and then sum the value and compare to get a larger value.
var maxSubArray = function (nums) {
let prev = 0, sum = nums[0]
for (const num of nums) {
prev = Math.max(prev + num, num)
sum = Math.max(prev, sum)
}
return sum
}
Copy the code
2.22 and are consecutive positive sequences of S
Leetcode address
This topic adopts the idea of sliding window. First find the middle value mid and execute the loop when I is less than or equal to mid. When the cumulative value is less than the target value, the cumulative value increases, and the value is stored in the temporary array temp. When the cumulative value is greater than or equal to the target value, the temporary array temp can be placed in the result array if the cumulative value is equal to the target value, and the cumulative value is decremented by decreasing the value of the first element in the temp array. Decreases to less than the target value and increases again.
var findContinuousSequence = function (target) {
let mid = Math.ceil(target / 2)
let i = 1, sum = 0, res = [], temp = []
while (i <= mid) {
while (sum < target) {
sum += i
temp.push(i++)
}
while (sum >= target) {
if (sum === target) {
res.push([...temp])
}
sum -= temp.shift()
}
}
return res
}
Copy the code
2.23 The oldest string without repeating characters
Leetcode address
This topic adopts the idea of sliding window. Iterate over each element of the number group and store it in an array. When encountering an element that already exists in the array, delete the number before the repeated element and the current element. Each iteration compares the length of the array to the length of the previous array, taking a larger value.
var lengthOfLongestSubstring = function (s) {
let arr = [], max = 0
for (const char of s) {
const index = arr.indexOf(char)
if(index ! = = -1) {
arr.splice(0, index + 1)
}
arr.push(char)
max = Math.max(arr.length, max)
}
return max
}
Copy the code
2.24 Sort a number that occurs only once in an array
Leetcode address
I saw that xOR was a quick solution, so I just used it.
var singleNonDuplicate = function (nums) {
return nums.reduce((acc, cur) = > acc ^ cur)
}
Copy the code
2.25 The KTH largest node of the binary search tree
Leetcode address
Using the characteristics of binary search tree, the value of the node of the left subtree is smaller than the value of the root node, and the value of the node of the right subtree is larger than the value of the root node, then the array sequence from large to small can be obtained through inverse middle order traversal. And because the problem only needs to get the KTH big node, the recursive operation can be stopped after the value of this node is obtained.
var kthLargest = function (root, k) {
let res = root.val
function traversal(tree) {
if(! tree)return
traversal(tree.right)
k--
if (k === 0) {
res = tree.val
return
}
traversal(tree.left)
}
traversal(root)
return res
}
Copy the code
References cloud.tencent.com/developer/a…