This document has been reviewed and discussed with @hsins.
For further reading, please see the definition of Convention over configuration in Wikipedia.
- In our coding convention, we use
lowerCamelCase
for both functions and variables. While this may deviate from the rules laid out in the Google C++ Style Guide and PEP 8 -- Style Guide for Python Code, it is done to maintain consistency with the LeetCode OJ system, which employslowerCamelCase
for over 99% of the time. Remember, the most important thing is to be consistent at all times. - In code snippets, import statements and brackets may be omitted for brevity (arguably), but they should be retained in real-world coding situations for clarity and maintainability.
- The code is only for demonstration and cannot be compiled.
- Explicitly declaring types such as
int
,char
, andstring
is often preferable over using theauto
keyword introduced in C++ 11. #include
statements are omitted in code snippets for brevity.- Understanding the difference between
size_t
andint
is crucial when traversing arrays in practical applications, as it can affect the outcome. - Qualifying with
std::
prefix when accessing standard libraries is recommended for readability and maintainability in real-world coding situations, as it clearly indicates the source of the identifier being used.
- The code is only for demonstration and cannot be compiled.
- Explicitly declaring types such as
int
,char
, andString
is often preferable over using thevar
keyword introduced Java 10. import
statements are omitted in code snippets for brevity.
- Private functions are prefixed with
_
, which may seem tedious. However, in the real world, we use tools like Mypy, Pytype, and Pyre for static type checking. - We pass the argument
--indent-size=2
to autopep8 for a better viewing experience. import
statements are omitted in code snippets for brevity.
- The naming of LeetCode is quite a mess and inconsistently uses plurals and
singulars together. I'll use my best judgment to name tables in
UpperCamelCase
in the plural form and fields inlower_snake_case
. - Proper indentation will be taken care of and treated seriously.
- No puzzling table abbreviation.
- Class:
UpperCamelCase
- Function:
lowerCamelCase
- Variable:
lowerCamelCase
- Constant:
kUpperCamelCase
// Class
class MyClass { ... }
// Function
function myFunction() { ... }
// Variable
int myVariable;
// Constant
constexpr int kMod = 1'000'000'007;
We use C++ to demonstrate the idea, and the same concepts apply to Java and Python.
-
Declare the variables in the proper scope as slow as possible.
- Declare
constexprt
as soon as possible. - Declare
const
variables as soon as possible. - Declare
ans
as soon as possible. - Since LeetCode is just an online judge system rather than a big project, we don't scatter all variables in different sections. However, we still sort the variables based on the time we first use each of them.
- Declare
-
Code section (there should be one blank line between each sections.)
-
public
- boundary conditions
- initial variables
- There may be many kernels separated with one blank line, but there shouldn't be any blank line in each kernel.
- return
-
private
- private variables
- private functions
-
We use C++ to demonstrate the idea, and the same concepts apply to Java and Python.
- No blank lines between variables initialization.
- Blank one single line between each section. However, if there's no sec 12, no blank line between sec 11 and sec 13.
- If the last statement is not a paragraph (
for
loop most of the case), then no blank lines between it and thereturn
statement.
class Solution {
public:
func() {
// (sec 0) boundary conditions
// (sec 1) initial variables
// (sec 10) constexpr/const (size/length)
// (sec 11) ans
// (sec 12) declaration & operation
// (sec 13) purely declaration
// (sec 2) kernels
// (sec 3) modify original initial variables
// (sec 4) kernels
// (sec n) return
}
private:
// private variables
// private functions
helper() { ... }
anotherHelper() { ... }
};
Example (873. Length of Longest Fibonacci Subsequence):
-
code:
class Solution { public: int lenLongestFibSubseq(vector<int>& arr) { const int n = arr.size(); int ans = 0; vector<vector<int>> dp(n, vector<int>(n, 2)); unordered_map<int, int> numToIndex; for (int i = 0; i < n; ++i) numToIndex[arr[i]] = i; for (int j = 0; j < n; ++j) for (int k = j + 1; k < n; ++k) { const int ai = arr[k] - arr[j]; if (ai < arr[j] && numToIndex.count(ai)) { const int i = numToIndex[ai]; dp[j][k] = dp[i][j] + 1; ans = max(ans, dp[j][k]); } } return ans; } };
-
code (explanation):
class Solution { public: int lenLongestFibSubseq(vector<int>& arr) { // Only get the value of size() or length() when we use it twice or more times. // Remember to add `const` whenever possible. const int n = arr.size(); int ans = 0; vector<vector<int>> dp(n, vector<int>(n, 2)); unordered_map<int, int> numToIndex; for (int i = 0; i < n; ++i) numToIndex[arr[i]] = i; for (int j = 0; j < n; ++j) for (int k = j + 1; k < n; ++k) { const int ai = arr[k] - arr[j]; // Add `const`. if (ai < arr[j] && numToIndex.count(ai)) { const int i = numToIndex[ai]; // Add `const`. dp[j][k] = dp[i][j] + 1; ans = max(ans, dp[j][k]); } } return ans; } };
// Linked-List
if (l1 == nullptr && l2 == nullptr) { ... }
if (l1 != nullptr || l2 != nullptr) { ... }
// String
if (str.empty()) { ... }
if (str.length() <= 2) { ... }
// Vector
if (vec.size() <= 2) { ... }
return ans;
return {};
// C++
unordered_set<string> seen;
unordered_map<char, int> count;
vector<int> count;
stack<char> stack;
queue<TreeNode*> q;
deque<TreeNode*> minQ;
auto compare = [](const ListNode* a, const ListNode* b) {
return a->val > b->val;
};
priority_queue<ListNode*, vector<ListNode*>, decltype(compare)> minHeap(compare);
// Java
Set<String> seen = new HashSet<>();
Map<Character, Integer> count = new HashMap<>();
int[] count = new int[n];
Deque<Character> stack = new ArrayDeque<>(); // Do not use `Stack`.
Queue<Integer> q = new LinkedList<>();
Deque<Integer> minQ = new ArrayDeque<>();
Queue<ListNode> minHeap = new PriorityQueue<>((a, b) -> a.val - b.val);
# Python
seen = set()
count = {}
count = collections.defaultdict(int)
count = collections.defaultdict(list)
count = collections.Counter()
minQ = collections.deque([root])
stack = []
minHeap = []
- Always prefer to one character to represent index variables.
- Use
i
,j
,k
in the loop, in that order.
int i = 0;
for (const int num : nums) { ... }
for (int i = 0, j = 0; i < n; ++i) { ... }
int k = 0;
for (int i = 0; i < n; ++i)
for (int j = i; j < n; ++j) { ... }
int l = 0;
int r = nums.size() - 1;
class UnionFind {
public:
UnionFind(int n) : count(n), id(n), rank(n) {
iota(begin(id), end(id), 0);
}
void unionByRank(int u, int v) {
const int i = find(u);
const int j = find(v);
if (i == j)
return;
if (rank[i] < rank[j]) {
id[i] = id[j];
} else if (rank[i] > rank[j]) {
id[j] = id[i];
} else {
id[i] = id[j];
++rank[j];
}
--count;
}
int getCount() const {
return count;
}
private:
int count;
vector<int> id;
vector<int> rank;
int find(int u) {
return id[u] == u ? u : id[u] = find(id[u]);
}
};
- If a graph has a clear tree structure, we name it a
tree
. Otherwise, we name it agraph
. - Always use
(u, v)
to represent an edge regardless what is stated in the problem.
vector<vector<int>> graph(n);
for (const vector<int>& edge : edges) {
const int u = edge[0];
const int v = edge[1];
graph[u].push_back(v);
graph[v].push_back(u);
}
void dijkstra(const vector<vector<pair<int, int>>>& graph, int src) {
vector<int> dist(graph.size(), INT_MAX);
using P = pair<int, int>; // (d, u)
priority_queue<P, vector<P>, greater<>> minHeap;
dist[src] = 0;
minHeap.emplace(0, src);
while (!minHeap.empty()) {
const auto [d, u] = minHeap.top();
minHeap.pop();
for (const auto& [v, w] : graph[u])
if (d + w < dist[v]) {
dist[v] = d + w;
minHeap.emplace(dist[v], v);
}
}
}
- Always prefer to one character to represent index variables.
- Always prefer to use
[l, r)
pattern.
int l = 0;
int r = nums.size();
while (l < r) {
const int m = l + (r - l) / 2;
if (f(m))
l = m + 1; // new range [m + 1, r)
else
r = m; // new range [l, m)
}
return l;
// Allocate the memory on the stack instead of the heap.
ListNode dummy(0);
ListNode* curr;
ListNode* prev;
ListNode* next;
ListNode* slow;
ListNode* fast;
ListNode* head;
ListNode* tail;
ListNode* l1;
ListNode* l2;
// 2D Vector
const int m = matrix.size();
const int n = matrix[0].size();
// If there're two strings.
const int m = str1.length();
const int n = str2.length();
// If there's only a string.
const int n = str.length();
// vector<int> nums;
for (int i = 0; i < nums.size(); ++i) { ... }
for (const int num : nums) { ... }
// vector<string> words;
for (const string& word : words) { ... }
// string str;
for (int i = 0; i < str.length(); ++i) { ... }
for (const char c : str) { ... }
// unordered_set<int> numsSet;
for (const int num : numsSet) { ... }
// structured binding in C++17
// unordered_map<char, int> map;
for (const auto& [key, value] : map) { ... }
// ListNode* head;
for (ListNode* curr = head; curr; curr = curr->next) { ... }
-
Always prefer to use
str.length()
overstr.size()
. -
Always use camelCase nomenclature when not listed above.
// C++ int currNum; int maxProfit; TreeNode* currNode;
-
When there's confliction in expression and function or reserved key word:
// C++ mini, std::min() maxi, std::max()
# Python mini, min maxi, max summ, sum
-
When there are two maps/stacks, use meaningful names.
// C++ unordered_map<char, int> countA; unordered_map<char, int> countB;
-
When we need to count something, use
sum
,count
andtotal
, in that order. -
Initialize vector with
0
orfalse
implicitly. -
constexpr
is used if possible. -
const
is used if possible. -
const auto
is used when we iterate through aunordered_map
ormap
. -
Use
&
whenever possible exceptint
andchar
because reference typically takes 4 bytes, whileint
takes 2/4 bytes andchar
takes 1 byte. -
Prefer to name variables in a "adjective + noun" order. For example,
maxLeft
is better thanleftMax
. -
If a block is really small, for example, before a
bfs()
call, sometimes we don't add extra blank lines. This is not a hard rule.