Style Guide
This document is discussed with @hsins.
See the definition of Convention over configuration in Wikipedia.
Disclaimer
General
- We adopt
lowerCamelCase for both functions and variables, which is against the rule of Google C++ Style Guide and PEP 8 -- Style Guide for Python Code. However, to be consistent with the LeetCode OJ system, which uses lowerCamelCase for over 99% of the time, we stick with lowerCamelCase in this case. Remember the most important thing is to be consistent all the time.
- We omit importing and brackets, and this is for shorter paragraph. In a real situation, retain importing let you quickly know where is this function/variable from and brackets make the indentation safer, so you might keep both of them.
C++
- Code can't be compiled, it's just for demonstrating purposes.
- Even though there is
auto keyword in C++ 11, for types like int and char, we tend to explicitly declare them. However, you might use auto most of the time in the real world.
- We skip including.
- We use
int to traverse the array most of the time for simplicity. However, you should note the difference between size_t and int, and in the real world, that matters.
- We omit
std:: to access the standard library for a short paragraph.
Java
- Code can't be compiled, it's just for demonstrating purposes.
- Even though there is
var keyword in Java, for types like int and char, we tend to explicitly declare them so people can have an idea of what the type is at first glance.
- We skip importing.
Python
- We omit prefixes like
collections. and math.. However, this might not be a good practice. Just want to keep the paragraph short.
- We prefix private functions with
_ and this might seem tedious. However, we tend to use something like Mypy and Pyre to do static type checking in the real world.
- We pass the argument
--indent-size=2 to autopep8 for a better viewing experience.
Fundamental
Rules
- Class:
UpperCamelCase
- Function:
lowerCamelCase
- Variable:
lowerCamelCase
- Constant:
UPPERCASE with underline
- Field:
lowerCamelCase
- Database:
SELECT * FROM name_table
Examples
1
2
3
4
5
6
7
8
9
10
11
12
13
14 | // Class
class MyClass { ... }
// Function
function myFunction() { ... }
// Variable
int myVariable;
// Constant
#define MY_CONSTANT;
// Database Table
SELECT * FROM my_table
|
Template
Rules
- There should only be one public function.
-
Declare the variables in the proper scope as slow 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.
-
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 function(s)
Schematic Template
We use C++ to demo the idea.
- 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 the return statement.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29 | class Solution {
public:
// There should only be one public function.
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 function(s)
helper() { ... }
dfs() { ... }
};
|
Example (873. Length of Longest Fibonacci Subsequence):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 | class Solution {
public:
int lenLongestFibSubseq(vector<int>& A) {
const int n = A.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[A[i]] = i;
for (int j = 0; j < n; ++j)
for (int k = j + 1; k < n; ++k) {
const int ai = A[k] - A[j];
if (ai < A[j] && numToIndex.count(ai)) {
const int i = numToIndex[ai];
dp[j][k] = dp[i][j] + 1;
ans = max(ans, dp[j][k]);
}
}
return ans;
}
};
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 | class Solution {
public:
int lenLongestFibSubseq(vector<int>& A) {
// Only get the value of size or length
// when we use it twice or more times.
// Add `const`, and separate this line from next section a blank line.
const int n = A.size();
// Declare the variables in the proper scope as slow as possible.
// Declare `ans` as soon as possible.
// General Order:
// ans -> dp -> STL -> pointers (TBD)
//
// Graph Order:
// ans -> graph -> inDegree -> state -> q -> seen
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[A[i]] = i;
for (int j = 0; j < n; ++j)
for (int k = j + 1; k < n; ++k) {
const int ai = A[k] - A[j]; // use const
if (ai < A[j] && numToIndex.count(ai)) {
const int i = numToIndex[ai]; // use const
dp[j][k] = dp[i][j] + 1;
ans = max(ans, dp[j][k]);
}
}
return ans;
}
};
|
Boundary Conditions
| // 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 Value
Data Structures
| // C++
unordered_set<string> seen;
unordered_map<char, int> count; // numToIndex, prefixToIndex
vector<int> count; // sometimes it's a better choice than `unordered_map`
stack<char> stack;
queue<TreeNode*> q;
deque<TreeNode*> q;
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];
Stack<Character> stack = new Stack<>();
Queue<Integer> q = new LinkedList<>();
Deque<Integer> q = new ArrayDeque<>();
Queue<ListNode> minHeap = new PriorityQueue<>((a, b) -> a.val - b.val);
|
| # Python
seen = set() # or wordSet = set() if you like
count = {}
count = collections.defaultdict(int)
count = collections.defaultdict(list)
count = collections.Counter()
q = collections.deque([root])
q = collections.deque([root])
stack = []
minHeap = []
|
Two Pointers / Sliding Windows
- 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;
|
Binary Search
- Always prefer to one character to represent index variables.
- Always prefer to use
[l, r) pattern.
1
2
3
4
5
6
7
8
9
10
11
12
13
14 | int l = 0;
int r = nums.size(); // or nums.size() - 1
while (l < r) {
const int m = l + (r - l) / 2;
if (f(m)) // optional
return m; // optional
if (g(m))
l = m + 1; // new range [m + 1, r)
else
r = m; // new range [l, m)
}
return l; // nums[l]
|
ListNode
1
2
3
4
5
6
7
8
9
10
11
12
13
14 | ListNode dummy(0); // allocated on stack instead of heap
ListNode* curr;
ListNode* prev;
ListNode* next;
ListNode* slow;
ListNode* fast;
ListNode* head;
ListNode* tail;
ListNode* l1;
ListNode* l2;
|
2D Vector / 2 Strings
| // 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();
|
Traversing
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 | // vector<int> nums;
for (int i = 0; i < nums.size(); ++i) { ... }
for (const int num : nums) { ... }
// vector<string> words;
for (const auto& 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
// unordered_map<char, int> map;
for (const auto& [key, value] : map) { ... }
// ListNode* head;
for (auto curr = head; curr; curr = curr->next) { ... }
|
Others
- Always prefer to use
str.length() over str.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 and total, in that order.
- Initialize vector with
0 or false implicitly.
constexpr is used if possible.
const is used if we get value of size() or length().
const auto is used when we iterate through a map.
- Use
& whenever possible except int and char because reference typically takes 4 bytes, while int takes 2/4 bytes and char takes 1 byte.
- Prefer to name variables in a "adjective + noun" order. For example,
maxLeft is better than leftMax.
- If a block is really small, for example, before a
bfs() call, sometimes we don't add extra blank lines.