1.Judgement Count/Even Number Writing:
Relevant topic: Swordfinger Offer 21. Adjusting the order of arrays so that odd numbers precede even numbers
https://leetcode-cn.com/problems/diao-zheng-shu-zu-shun-xu-shi-qi-shu-wei-yu-ou-shu-qian-mian-lcof/
Normal Writing: Module (Module 2)
if( i % 2 != 0 ) //Odd Number if( i% 2 == 0 ) //Even numbers
Optimized Writing: Bit Operations (using 1 and operations)
if( i & 1 == 1 ) if( i & 1 == 0 )
Bit operations are more efficient than modulo operations.
2.Shift operation'>':
Corresponding Title: Offer of Swordfinger 15.Number of 1 in Binary
https://leetcode-cn.com/problems/er-jin-zhi-zhong-1de-ge-shu-lcof/solution/
2.1 A low-level error with the shift operator'>':
To shift an unsigned integer,
The correct writing is:
n = n >> 1; Or: n >>= 1;
Wrong Writing:
n >> 1;
This is equivalent to an expression placed directly here without updating the value of n:
n + 1;
The value of the 2.2 parameter can also be modified:
void func(int n) { cout << n; //n = 5;Print the value of the incoming argument n = 6; cout << n; //n = 6;The value of the parameter is modified, and the modified value is printed } int main() { func(5); return 0; }
Summary:
When you encounter some binary-related problems, such as finding the number of 0/1 bits in an integer, the first thing you think about is the shift operation.
3.Classic Question: Reverse One-Way Chain List:
Related topic: Swordfinger Offer 24. Reverse Chain List
https://leetcode-cn.com/problems/fan-zhuan-lian-biao-lcof/
Method 1: Recursive method:
class Solution { public: ListNode* reverseList(ListNode* head) { if(head == nullptr || head->next == nullptr) return head; ListNode* newHead = reverseList(head->next); //newHead assigns values only in the last level of recursion and returns all the way back as the end result head->next->next = head; head->next = nullptr; //This step is to prevent the table from ringing return newHead; } };
Method 2: Iteration:
class Solution { public: ListNode* reverseList(ListNode* head) { ListNode* cur = head; ListNode* prev = nullptr; while(cur) { ListNode* next = cur->next; cur->next = prev; prev = cur; cur = next; } return prev; } };
4.count and find methods for unordered_map:
Relevant topic: Swordfinger Offer 39. Numbers that occur more than half of the time in the array
https://leetcode-cn.com/problems/shu-zu-zhong-chu-xian-ci-shu-chao-guo-yi-ban-de-shu-zi-lcof/
Calculate the number of elements corresponding to a key in the map using the count method.
The map type has a special price comparison between the two methods and is different from other STL classes:
One is map.find(key);,One is map.count(key);.
map.find(key); map.count(key);
In addition, in algorithm questions, unordered_map is used instead of map where elements are not required to be ordered.
5.Quick Sort:
The basic idea of quick sorting is:
The array is divided into two separate parts by one-step sorting, where one element has a smaller value than the other; then the intervals of the two subdivisions are sorted to achieve the entire array order.
Quick sorting is used most in engineering code, using the idea of division.
Quick sorting is also the most efficient of these O(N*log(N)) sorting algorithms.
class Solution { public: void quickSort(vector<int>& nums, int begin, int end) { if(begin >= end) return; //terminator int pivot = partition(nums, begin, end); //process current layer quickSort(nums, begin, pivot - 1); //drill down quickSort(nums, pivot + 1, end); } int partition(vector<int>& nums, int begin, int end) { int counter = begin; int pivot = end; for(int i = begin; i < end; ++i) { if(nums[i] < nums[pivot]) { swap(nums[i], nums[counter++]); //The target of the fast and slow pointer //counter is that counter was previously smaller than nums[pivot] } } swap(nums[counter], nums[pivot]); return counter; } };
6.Merge Sort:
Merge is similar to fast row, but the order of steps is opposite.
Merge: first sort left and right subsequences, then merge two ordered subarrays;
Quick Row: Allocate left and right subarrays first, and then sort (divide) the left and right subarrays memorially.
class Solution { public: void mergeSort(vector<int>& nums, int begin, int end) { if(begin >= end) return ; //terminator int mid = begin + (end - begin) / 2; mergeSort(nums, begin, mid); //drill down mergeSort(nums, mid + 1, end); mergeArray(nums, begin, mid, end); } void mergeArray(vector<int>& nums, int begin, int mid, int end) { vector<int> temp; temp.resize(end - begin + 1); int i = begin, m = mid; //Part One int j = mid + 1, n = end; //Part Two int k = 0; while(i <= m && j <= n) { temp[k++] = nums[i] < nums[j] ? nums[i++] : nums[j++]; } while(i <= m) { temp[k++] = nums[i++]; } while(j <= n) { temp[k++] = nums[j++]; } for(int i = 0; i < k; ++i) { nums[begin + i] = temp[i]; } } };
7.How to write bubble sort:
8.When a vector s are initialized directly using an iterator, it is a trailing iterator:
Relevant topic: Swordfinger Offer 40.Minimum k Number
https://leetcode-cn.com/problems/zui-xiao-de-kge-shu-lcof/
The vector Container type in STL supports direct initialization using the scope iterator of another container, such as the following example:
int main() { vector<int> array; vector<int> ret(arr.begin(), arr.begin() + k); }
It should be noted that in direct initialization, the iterator is also a "tail-end iterator". That is, the real range of initialization is from (arr.begin()) to (arr.begin() + k-1), excluding the k-th element.
Similarly, direct initialization using arrays works the same way:
int main() { int array[] = {1,2,3,4}; vector<int> vec(array, array + sizeof(array)/sizeof(int)); }
9.Traversal of a binary tree:
Related Topics: Offer 32 - II.Print Binary Tree II Top to Bottom
https://leetcode-cn.com/problems/cong-shang-dao-xia-da-yin-er-cha-shu-ii-lcof/
Why traverse a binary tree?
Just like traversing arrays and chains, the only purpose is to find an element in the data structure.
Traversing through arrays and linked lists is simple, using pointers and iterators to traverse the entire array/linked list from front to back because they are "one-dimensional" storage structures or graphically linear data structures.
The structure of a binary tree (N-fork tree) is not linear, so it is impossible to traverse and search in the same way as arrays and chains.
For a search of a set of data, unless the data structure supports a specific lookup operation (for example, unordered_map lookup finds a location based on a hash formula with a time complexity of O(1)), then a traversal search is required.
There are several ways to traverse a tree as a data structure:
Pre-order traversal, middle-order traversal, post-order traversal, these are "depth first traversal";
Sequence traversal, this is "breadth first traversal".
9.1 Depth-first traversal and breadth-first traversal:
Depending on the order of access to the nodes in the tree, tree traversal is divided into "depth first traversal" and "breadth first traversal", or "depth first search" (DFS, Depth First Search) and "breadth first search" (BFS, Breadth First Search).
Writing of depth-first traversal: stack (iteration) or recursion;
Writing for breadth-first traversal: Queue (Iteration).
9.1.1 Depth-first Traversal Writing Template:
Method 1: Iteration method: (around root)
The idea of "backtracking" is used in the iteration method of depth-first traversal.
The so-called "backtrace" means that when traversing, you reserve a backtrack (backtrace point) for yourself. When the road reaches the end, you can jump directly to the backtrace point.
Each right right node on the stack in a depth-first traversal is a backtrace point.
Definition of tree node:
typedef struct TreeNode { int val; TreeNode* left; TreeNode* right; TreeNode(int n) : val(n) {} TreeNode(int n, TreeNode* l, TreeNode* r) : val(n), left(l), right(r) {} } TreeNode;
A DFS traversal in "left-right" order: (If you need the order of "left-right" or "left-right" traversal, just adjust the order in which the vector s are pushed)
vector<int> binaryTreeDfs(TreeNode* root) { vector<int> result; //Save results stack<TreeNode*> s; s.push(root); while(!s.empty()) { TreeNode* cur = s.top(); s.pop(); result.push_back(cur->val); if(cur->right) s.push(cur->right); //Backtrace Point if(cur->left) s.push(cur->left); } return result; }
Method 2: Recursive method:
(around root)
vector<int> result; void binaryTreeDfs(TreeNode* root) { travelTree(root); return; } void travelTree(TreeNode* root) { if(!root) return; result.push_back(root->val); travelTree(root->left); travelTree(root->right); }
In the recursive example above, result.push_back(val) is placed in the middle of two recursive functions, which is "left root right", followed by "left root right root".
9.1.2 Writing Template for Width First Traversal:
A code template with breadth-first traversal:
The key point is to use a queue.
vector<int> binaryTreeBfs(TreeNode* root) { vector<int> result; queue<TreeNode*> q; q.push(root); while(!q.empty()) { TreeNode* cur = q.front(); q.pop(); result.push_back(cur->val); if(cur->left) q.push(cur->left); if(cur->right) q.push(cur->right); } return result; }
9.1.3 Layer-by-layer printing with breadth-first traversal:
One variant of breadth-first traversal is Layer-by-Layer Printing, which requires an additional for loop to process one layer at a time in queue.size().
One thing to note is that the cyclic condition queue.size in the for loop needs to be saved in a separate local variable because the cyclic condition is modified within the for loop.
class Solution { public: vector<vector<int>> levelOrder(TreeNode* root) { vector<vector<int>> result; if(root == nullptr) return result; queue<TreeNode*> q; q.push(root); while(!q.empty()) { result.push_back(vector<int>()); //Push in an empty vector first int curQueueSize = q.size(); //Keep in mind that if you change the "cyclic condition" in for, you should use a temporary variable to save the cyclic condition before entering the for cycle. This is the same principle as "iterator failure" for(int i = 0; i < curQueueSize; ++i) { TreeNode* cur = q.front(); q.pop(); result.back().push_back(cur->val); if(cur->left) q.push(cur->left); if(cur->right) q.push(cur->right); } } return result; } };