具体示例请看原文:S.O.L.I.D: The First 5 Principles of Object Oriented Design
S.O.L.I.D 代表着:
S – Single-responsiblity principle 单一责任原则
一个类应有且仅有一个理由去变更,即一个类只应用于一个工作。
O – Open-closed principle 开闭原则
对象或实体只应该开放扩展,而不该开放修改。
我的理解是应该避免越俎代庖的事情发生,不应该在调用某类实体的地方去关心某个对象该方法的具体实现。例如有两个Shape类的继承类Circle及Square,在对各自计算面积的时候,具体计算面积的方法不是调用者需要关心的,否则每增加一种继承类都要去修改调用方法。
L – Liskov substitution principle 李斯柯夫替代原则
令q(x)是关于类型T的对象x的可证明属性。那么对于T的子类型S的对象y,q(y)也应该是可证明的。
这个是官话,简单来说就是每个子类都应该可替代他们的父类,所谓“子承父业”。
I – Interface segregation principle 接口分离原则
永远不应该强迫用户实现它不使用的接口,或不应该强迫用户依赖于他们不使用的方法。
比如不应该让一个二维平面的类去实现一个计算体积的方法。
D – Dependency Inversion Principle 依赖倒置原则
实体必须依赖于抽象,而不是具象。高级别的模块不能依赖于低级别模块,而是需要依赖于某种抽象。
比如一个Gateway类需要通过某种网络IO接收数据,它应该依赖于一种IO接口的抽象而不是具体的某种IO(比如UDP IO),否则当你想换一种IO做同样的事情时你就会很头疼。
Category: 不学无术
654. Maximum Binary Tree
Given an integer array with no duplicates. A maximum tree building on this array is defined as follow:
- The root is the maximum number in the array.
- The left subtree is the maximum tree constructed from left part subarray divided by the maximum number.
- The right subtree is the maximum tree constructed from right part subarray divided by the maximum number.
Construct the maximum tree by the given array and output the root node of this tree.
/** * Definition for a binary tree node. * struct TreeNode { * int val; * TreeNode *left; * TreeNode *right; * TreeNode(int x) : val(x), left(NULL), right(NULL) {} * }; */ class Solution { public: TreeNode* subMaxBinTree(vector<int>::iterator begin, vector<int>::iterator end) { if (begin == end) return nullptr; auto maxElemIt = std::max_element(begin, end); TreeNode* root = new TreeNode(*maxElemIt); root->left = subMaxBinTree(begin, maxElemIt); root->right = subMaxBinTree(maxElemIt+1, end); return root; } TreeNode* constructMaximumBinaryTree(vector<int>& nums) { if (nums.empty()) return nullptr; return subMaxBinTree(nums.begin(), nums.end()); } };
Sometimes we have two classes that need to call each other’s somewhat function, for example
template <typename A> struct B { A* p_a_ = nullptr; }; template <typename B> struct A { B* p_b_ = nullptr; };
but as you can see A depends on B’s type to initialize and vise versa. If we declare the types as above we’ll not get a chance to create any instance.
There are at least two ways to resolve the issue.
I.Template template
A quick introduction about template template can be found here.
Declare either of {A, B} with template of template which accepts the other’s template argument.
template <template<typename> typename A_t> struct B { using A = A_t<B>; A* p_a_ = nullptr; }; template <typename B> struct A { B* p_b_ = nullptr; }; //and declare like this using B_t = B<A>; B_t b_instance; A<B_t> A_instance;
II.Type traits
The other resolution is create a type trait struct which acts like a bridge that links the type of each other.
template <typename foobar_tratis> struct foobar_notifier { using inst_t = typename foobar_tratis::foobar_inst_t; inst_t* inst_ = nullptr; void foobar() { std::cout << "foobar_notifier" << std::endl; } }; template <typename foobar_tratis> struct foobar_inst { using notifier_t = typename foobar_tratis::foobar_notifier_t; notifier_t* notifier_ = nullptr; void foobar() { std::cout << "foobar_inst" << std::endl; } }; struct foobar_tratis { using foobar_notifier_t = foobar_notifier<md_inst_traits>; using foobar_inst_t = foobar_inst<md_inst_traits>; }; /// and declare like this foobar_inst<foobar_tratis> inst2; foobar_notifier<foobar_tratis> notifier2;
Here I gathered some of (what I thought are) best answers to LeetCode questions:
- https://leetcode.com/problems/wiggle-subsequence/discuss/84887/C++-0ms-O(N)-dynamic-programming-solution
- an O(n) solution
- https://leetcode.com/problems/trapping-rain-water/description/
- an O(n) solution of a similar (but harder) question, found in the book <算法竞赛入门经典(第二版) pp249例8-18
- basic idea is to find a maximal height which can reserve the water for each position i, if the max height equals to the unit then no rain can be collected at i
(vice versa, from right to left)
LeetCode 560. Subarray Sum Equals K
https://leetcode.com/problems/subarray-sum-equals-k/description/
O(N^2)的方法想必所有人都会,这里有个用到prefix sum的O(N)方法。
试想一下,如果当前检查到end位置且目前所有项的和为sum,如果有那么一个(start, end)子序列的和为k,那么肯定有一个(0, start-1)的序列它的和是sum – k.
[a, b, c] \___k__/ [a, b, c, d, e, f] /sum - k\
class Solution { public: int subarraySum(vector<int>& nums, int k) { unordered_map<int, int> cum_sums; // <cum_sum, count> cum_sums[0] = 1; int sum = 0; int count = 0; for (int i = 0; i < nums.size(); ++i) { sum += nums[i]; if (cum_sums.count(sum - k) > 0) count += cum_sums[sum - k]; cum_sums[sum]++; } return count; } };
类似的东西还有:
674. Longest Continuous Increasing Subsequence
https://leetcode.com/problems/longest-continuous-increasing-subsequence/description/
300. Longest Increasing Subsequence
https://leetcode.com/problems/longest-increasing-subsequence/description/
longest path increasing by 1
https://www.geeksforgeeks.org/find-the-longest-path-in-a-matrix-with-given-constraints/
下面的代码是这个题的:
329. Longest Increasing Path in a Matrix
https://leetcode.com/problems/longest-increasing-path-in-a-matrix/description/
主要思路就是瞎JB搜索,不过某一个位置的结果可以记录下来给后续的重复搜索使用。
class Solution { public: int subLIP(vector<vector<int>>& matrix, vector<vector<int>>& sub_results, int row, int col) { if (row < 0 || row >= matrix.size()) return 0; if (col < 0 || col >= matrix[0].size()) return 0; if (sub_results[row][col] != -1) return sub_results[row][col]; // go left int maxResult = -1; if (col > 0 && matrix[row][col - 1] > matrix[row][col]) maxResult = max(maxResult, 1 + subLIP(matrix, sub_results, row, col - 1)); // go right if (col < matrix[0].size() - 1 && matrix[row][col + 1] > matrix[row][col]) maxResult = max(maxResult, 1 + subLIP(matrix, sub_results, row, col + 1)); // go up if (row > 0 && matrix[row - 1][col] > matrix[row][col]) maxResult = max(maxResult, 1 + subLIP(matrix, sub_results, row - 1, col)); // go down if (row < matrix.size() - 1 && matrix[row + 1][col] > matrix[row][col]) maxResult = max(maxResult, 1 + subLIP(matrix, sub_results, row + 1, col)); sub_results[row][col] = max(1, maxResult); return sub_results[row][col]; } int longestIncreasingPath(vector<vector<int>>& matrix) { vector<vector<int>> sub_results; sub_results.resize(matrix.size()); for (int i = 0; i < matrix.size(); ++i) { sub_results[i].resize(matrix[0].size(), -1); // -1 means unknown } int result = 0; for (int r = 0; r < matrix.size(); ++r) { for (int c = 0; c < matrix[r].size(); ++c) { result = max(result, subLIP(matrix, sub_results, r, c)); } } return result; } };
LeetCode 200. Number of Islands
https://leetcode.com/problems/number-of-islands/description/
DFS of a map, O(N^2)
class Solution { public: bool visit(vector<vector<char>>& grid, int row, int col) { // if has any island, return true // DFS on grid const int rows = grid.size(); if (rows == 0 || row >= rows || row < 0) return false; const int cols = grid[0].size(); if (cols == 0 || col >= cols || col < 0) return false; if (grid[row][col] == 'V') return false; else if (grid[row][col] == '0') return false; grid[row][col] = 'V'; visit(grid, row, col-1); visit(grid, row-1, col); visit(grid, row+1, col); visit(grid, row, col+1); return true; } int numIslands(vector<vector<char>>& grid) { // mark visited as 'V' int result = 0; for (int row = 0; row < grid.size(); ++row) { for (int col = 0; col < grid[0].size(); ++col) { if (grid[row][col] == 'V' || grid[row][col] == '0') { continue; } if (visit(grid, row, col)) result++; } } return result; } };
LeetCode 76. Minimum Window Substring
76. Minimum Window Substring
思路:
构造一个哈希表结构统计我们的需求,也就是字符串t中每个字符的数量,方便起见如果有需求则哈希表的对应值-1。另外存储一下当前解的起始位置START以及最优解的位置。
扫描字符串s,在位置s[i]时如果能够满足需求(即哈希表中没有负值),则从START开始清除无用的字符直至无法满足需求位置。比如需求是A:1, B:2,START开始的字符是DEFBAB,则清除DEF。清除完成后更新START值并跟当前已知的最优解比较,更新最优解。
啰嗦一句,似乎相当一部分substring类型的题目都能用hash table + two pointers的方式解决。
class Solution { public: static string minWindow(string s, string t) { int count_map[128] = {0}; set<char> req_set; int start_index = -1, min_start_index = 0; int min_end_index = INT_MAX; for (const char& c : t) { count_map[c] --; req_set.insert(c); } for (int i = 0; i < s.size(); ++i) { const char c = s[i]; count_map[c] ++; bool require_sufficed = true; for (const char& c : req_set) { if (count_map[c] < 0) { require_sufficed = false; break; } } if (!require_sufficed) continue; // clear left for (int j = start_index + 1; j <= i; ++j) { if (--count_map[s[j]] < 0) { start_index = j; break; } start_index = j; } if (i - start_index < min_end_index - min_start_index) { min_start_index = start_index; min_end_index = i; } } if (min_end_index == INT_MAX) return ""; else return s.substr(min_start_index, min_end_index - min_start_index + 1); } };
LeetCode 432. All O`one Data Structure
几万年没做LeetCode的题了,更新一下。
https://leetcode.com/problems/all-oone-data-structure/description/
这道题的思路是用一个双向链表(list)来存储一个以value排序的数据结构,使用哈希表(unordered_map)来存储key到链表iterator的映射:
需要注意特殊处理value=1的Node的情况。
代码写得比较毛糙,但是确实是O(1)的复杂度,如果要优化的话map的插入可以用emplace()之类的。
注意:除了value=1的节点,其余节点的keys为空的时候要把该节点删除掉,否则getMaxKey()和getMinKey()就需要O(n)的遍历操作了。
struct KvNode { unordered_set<string> keys; int value = 1; }; class AllOne { private: list<KvNode> value_key; // arranged by val in ascending order using list_it_t = list<KvNode>::iterator; unordered_map<string, list_it_t> val_map; list_it_t init_node; //node with value one public: /** Initialize your data structure here. */ AllOne() { value_key.push_back(KvNode()); init_node = value_key.begin(); } /** Inserts a new key <Key> with value 1. Or increments an existing key by 1. */ void inc(const string& key) { if (val_map.find(key) == val_map.end()) { // `create' new node with value 1 init_node->keys.insert(key); val_map[key] = init_node; } else { // find existing node auto node_it = val_map[key]; node_it->keys.erase(key); //remove from current bulk auto next_it = std::next(node_it, 1); if (next_it == value_key.end() || next_it->value != node_it->value + 1) { // no valid valued bulk, create one KvNode node; node.keys.insert(key); node.value = node_it->value + 1; val_map[key] = value_key.insert(next_it, node); } else { next_it->keys.insert(key); val_map[key] = next_it; } if (node_it->keys.empty() && node_it->value != 1) { // remove node_it value_key.erase(node_it); } } } /** Decrements an existing key by 1. If Key's value is 1, remove it from the data structure. */ void dec(string key) { auto it_val_map = val_map.find(key); if (it_val_map == val_map.end()) { // key does not exist return; } else if (it_val_map->second->value == 1) { auto node_it = it_val_map->second; node_it->keys.erase(key); if (node_it->keys.empty() && node_it->value != 1) { value_key.erase(node_it); } val_map.erase(it_val_map); } else { auto node_it = it_val_map->second; auto prev_it = std::prev(node_it, 1); node_it->keys.erase(key); if (prev_it->value != node_it->value - 1) { // create one KvNode node; node.keys.insert(key); node.value = node_it->value - 1; val_map[key] = value_key.insert(node_it, node); } else { prev_it->keys.insert(key); val_map[key] = prev_it; } if (node_it->keys.empty() && node_it->value != 1) { value_key.erase(node_it); } } } /** Returns one of the keys with maximal value. */ string getMaxKey() { auto it_max = std::prev(value_key.end(), 1); if (!it_max->keys.empty()) return *(it_max->keys.begin()); else return ""; } /** Returns one of the keys with Minimal value. */ string getMinKey() { auto it_min = value_key.begin(); if (it_min->value == 1 && it_min->keys.empty()) ++it_min; if (it_min != value_key.end() && !it_min->keys.empty()) return *(it_min->keys.begin()); else return ""; } }; /** * Your AllOne object will be instantiated and called as such: * AllOne obj = new AllOne(); * obj.inc(key); * obj.dec(key); * string param_3 = obj.getMaxKey(); * string param_4 = obj.getMinKey(); */
Bayes Network: D-Separation
某一变量的已知/未知会影响其他变量的相互独立性。
来自Udacity的CS217课