diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 000000000..a4fc1b08e --- /dev/null +++ b/.travis.yml @@ -0,0 +1,28 @@ +group: travis_latest +language: python +cache: pip +python: + - 2.7 + - 3.6 + #- nightly + #- pypy + #- pypy3 +matrix: + allow_failures: + - python: 3.6 + - python: nightly + - python: pypy + - python: pypy3 +install: + #- pip install -r requirements.txt + - pip install flake8 # pytest # add another testing frameworks later +before_script: + # stop the build if there are Python syntax errors or undefined names + - flake8 . --count --select=E901,E999,F821,F822,F823 --show-source --statistics + # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide + - flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics +script: + - true # pytest --capture=sys # add other tests here +notifications: + on_success: change + on_failure: change # `always` will be the setting once code changes slow down diff --git a/C++/01-matrix.cpp b/C++/01-matrix.cpp new file mode 100644 index 000000000..11b49a9b3 --- /dev/null +++ b/C++/01-matrix.cpp @@ -0,0 +1,81 @@ +// Time: O(m * n) +// Space: O(m * n) + +class Solution { +public: + vector> updateMatrix(vector>& matrix) { + queue> queue; + for (int i = 0; i < matrix.size(); ++i) { + for (int j = 0; j < matrix[0].size(); ++j) { + if (matrix[i][j] == 0) { + queue.emplace(i, j); + } + else { + matrix[i][j] = numeric_limits::max(); + } + } + } + + const vector> dirs = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}}; + while (!queue.empty()) { + auto cell = queue.front(); + queue.pop(); + for (const auto& dir : dirs) { + auto i = cell.first + dir.first; + auto j = cell.second + dir.second; + if (i < 0 || i >= matrix.size() || j < 0 || j >= matrix[0].size() || + matrix[i][j] <= matrix[cell.first][cell.second] + 1) { + continue; + } + queue.emplace(i, j); + matrix[i][j] = matrix[cell.first][cell.second] + 1; + } + } + + return matrix; + } +}; + +// Time: O(m * n) +// Space: O(m * n) +// dp solution +class Solution2 { +public: + vector> updateMatrix(vector>& matrix) { + vector > dp(matrix.size(), + vector(matrix[0].size(), + numeric_limits::max() - 10000)); + + for (int i = 0; i < matrix.size(); ++i) { + for (int j = 0; j < matrix[i].size(); ++j) { + if (matrix[i][j] == 0) { + dp[i][j] = 0; + } else { + if (i > 0) { + dp[i][j] = min(dp[i][j], dp[i - 1][j] + 1); + } + if (j > 0) { + dp[i][j] = min(dp[i][j], dp[i][j - 1] + 1); + } + } + } + } + + for (int i = matrix.size() - 1; i >= 0; --i) { + for (int j = matrix[i].size() - 1; j >= 0; --j) { + if (matrix[i][j] == 0) { + dp[i][j] = 0; + } else { + if (i < matrix.size() - 1) { + dp[i][j] = min(dp[i][j], dp[i + 1][j] + 1); + } + if (j < matrix[i].size() - 1) { + dp[i][j] = min(dp[i][j], dp[i][j + 1] + 1); + } + } + } + } + + return dp; + } +}; diff --git a/C++/1-bit-and-2-bit-characters.cpp b/C++/1-bit-and-2-bit-characters.cpp new file mode 100644 index 000000000..cc075fea6 --- /dev/null +++ b/C++/1-bit-and-2-bit-characters.cpp @@ -0,0 +1,14 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + bool isOneBitCharacter(vector& bits) { + auto parity = 0; + for (int i = static_cast(bits.size()) - 2; + i >= 0 && bits[i]; --i) { + parity ^= bits[i]; + } + return parity == 0; + } +}; diff --git a/C++/132-pattern.cpp b/C++/132-pattern.cpp new file mode 100644 index 000000000..c854b5dd1 --- /dev/null +++ b/C++/132-pattern.cpp @@ -0,0 +1,21 @@ +// Time: O(n) +// Space: O(n) + +class Solution { +public: + bool find132pattern(vector& nums) { + int ak = numeric_limits::min(); + stack st; + for (int i = nums.size() - 1; i >= 0; --i) { + if (nums[i] < ak) { + return true; + } else { + while (!st.empty() && nums[i] > st.top()) { + ak = st.top(), st.pop(); + } + } + st.emplace(nums[i]); + } + return false; + } +}; diff --git a/C++/2-keys-keyboard.cpp b/C++/2-keys-keyboard.cpp new file mode 100644 index 000000000..01343232d --- /dev/null +++ b/C++/2-keys-keyboard.cpp @@ -0,0 +1,18 @@ +// Time: O(sqrt(n)) +// Space: O(1) + +class Solution { +public: + int minSteps(int n) { + auto result = 0; + // the answer is the sum of prime factors + for (auto p = 2 ; p * p <= n; ++p) { + while (n % p == 0) { + result += p; + n /= p; + } + } + result += (n > 1) ? n : 0; + return result; + } +}; diff --git a/C++/24-game.cpp b/C++/24-game.cpp new file mode 100644 index 000000000..0bd48e540 --- /dev/null +++ b/C++/24-game.cpp @@ -0,0 +1,205 @@ +// Time: O(n^3 * 4^n) = O(1), n = 4 +// Space: O(n^2) = O(1) + +class Solution { +public: + bool judgePoint24(vector& nums) { + vector doubles; + std::transform(nums.begin(), nums.end(), std::back_inserter(doubles), + [](const int num) { return double(num); }); + return dfs(doubles); + } + +private: + bool dfs(const vector& nums) { + if (nums.size() == 1) { + return fabs(nums[0] - 24) < 1e-6; + } + static unordered_map> ops = + { + {'+', std::plus()}, + {'-', std::minus()}, + {'*', std::multiplies()}, + {'/', std::divides()}, + }; + for (int i = 0; i < nums.size(); ++i) { + for (int j = 0; j < nums.size(); ++j) { + if (i == j) { + continue; + } + vector next_nums; + for (int k = 0; k < nums.size(); ++k) { + if (k == i || k == j) { + continue; + } + next_nums.emplace_back(nums[k]); + } + for (const auto& op : ops) { + if (((op.first == '+' || op.first == '*') && i > j) || + (op.first == '/' && nums[j] == 0)) { + continue; + } + next_nums.emplace_back(op.second(nums[i], nums[j])); + if (dfs(next_nums)) { + return true; + } + next_nums.pop_back(); + } + } + } + return false; + } +}; + + +class Fraction { +public: + Fraction() = default; + Fraction(int n) + : Fraction(n, 1) + { + } + Fraction(int n, int d) + : numerator_(n) + , denominator_(d) + { + } + ~Fraction() = default; + + void set_num(int value) { numerator_ = value; } + void set_den(int value) { denominator_ = value; } + int get_num() const { return numerator_; } + int get_den() const { return denominator_; } + void reduce(); + int calculate_gcd(int, int) const; +private: + int numerator_, denominator_; +}; + +void Fraction::reduce() +{ + const auto gcd = calculate_gcd(numerator_, denominator_); + numerator_ = numerator_ / gcd; + denominator_ = denominator_ / gcd; +} + +int Fraction::calculate_gcd(int a, int b) const +{ + a = std::abs(a); + b = std::abs(b); + while (b != 0) { + int tmp = b; + b = a % b; + a = tmp; + } + return a; +} + +Fraction operator+(const Fraction& lhs, const Fraction& rhs) +{ + Fraction result{}; + + result.set_num((lhs.get_num() * rhs.get_den()) + (lhs.get_den() * rhs.get_num())); + result.set_den(lhs.get_den() * rhs.get_den()); + + result.reduce(); + + return result; +} + +Fraction operator-(const Fraction& lhs, const Fraction& rhs) +{ + Fraction result{}; + + result.set_num((lhs.get_num() * rhs.get_den()) - (lhs.get_den() * rhs.get_num())); + result.set_den(lhs.get_den() * rhs.get_den()); + + result.reduce(); + + return result; +} + +Fraction operator*(const Fraction& lhs, const Fraction& rhs) +{ + Fraction result{}; + + result.set_num(lhs.get_num() * rhs.get_num()); + result.set_den(lhs.get_den() * rhs.get_den()); + + result.reduce(); + + return result; +} + +Fraction operator/(const Fraction& lhs, const Fraction& rhs) +{ + Fraction result{}; + + result.set_num(lhs.get_num() * rhs.get_den()); + result.set_den(lhs.get_den() * rhs.get_num()); + + result.reduce(); + + return result; +} + +bool operator==(const Fraction &lhs, const Fraction &rhs) { + return (((lhs.get_num() * rhs.get_den()) - (rhs.get_num() * lhs.get_den())) == 0); +} + +std::ostream &operator<<(std::ostream &os, const Fraction &value) { + os << value.get_num() << "/" << value.get_den(); + return os; +} + +// Time: O(n^3 * 4^n) = O(1), n = 4 +// Space: O(n^2) = O(1) +class Solution2 { +public: + bool judgePoint24(vector& nums) { + vector fraction_nums; + std::transform(nums.begin(), nums.end(), std::back_inserter(fraction_nums), + [](const int num) { return Fraction(num); }); + return dfs(fraction_nums); + } + +private: + bool dfs(const vector& nums) { + if (nums.size() == 1) { + return nums[0] == 24; + } + static unordered_map> ops = + { + {'+', std::plus()}, + {'-', std::minus()}, + {'*', std::multiplies()}, + {'/', std::divides()}, + }; + for (int i = 0; i < nums.size(); ++i) { + for (int j = 0; j < nums.size(); ++j) { + if (i == j) { + continue; + } + vector next_nums; + for (int k = 0; k < nums.size(); ++k) { + if (k == i || k == j) { + continue; + } + next_nums.emplace_back(nums[k]); + } + for (const auto& op : ops) { + if (((op.first == '+' || op.first == '*') && i > j) || + (op.first == '/' && nums[j] == 0)) { + continue; + } + next_nums.emplace_back(op.second(nums[i], nums[j])); + if (dfs(next_nums)) { + return true; + } + next_nums.pop_back(); + } + } + } + return false; + } +}; diff --git a/C++/3sum-closest.cpp b/C++/3sum-closest.cpp new file mode 100644 index 000000000..02e0aaa7e --- /dev/null +++ b/C++/3sum-closest.cpp @@ -0,0 +1,44 @@ +// Time: O(n^2) +// Space: O(1) + +class Solution { +public: + /** + * @param numbers: Give an array numbers of n integer + * @param target: An integer + * @return: return the sum of the three integers, the sum closest target. + */ + int threeSumClosest(vector nums, int target) { + int ans = numeric_limits::max(); + int min_diff = numeric_limits::max(); + + // Make nums in increasing order. Time: O(nlogn) + sort(nums.begin(), nums.end()); + + for (int i = 0; i < static_cast(nums.size()) - 2; ++i) { + if (i == 0 || nums[i] != nums[i - 1]) { // Skip duplicated. + int j = i + 1; + int k = nums.size() - 1; + + while (j < k) { // Time: O(n) for each i. + const auto sum = nums[i] + nums[j] + nums[k]; + + if (sum > target) { // Should decrease sum. + --k; + } else if (sum < target) { // Should increase sum. + ++j; + } else { + return target; + } + + if (abs(sum - target) < min_diff) { + min_diff = abs(sum - target); + ans = sum; + } + } + } + } + + return ans; + } +}; diff --git a/C++/3sum-smaller.cpp b/C++/3sum-smaller.cpp new file mode 100644 index 000000000..db32ead82 --- /dev/null +++ b/C++/3sum-smaller.cpp @@ -0,0 +1,25 @@ +// Time: O(n^2) +// Space: O(1) + +class Solution { +public: + int threeSumSmaller(vector& nums, int target) { + sort(nums.begin(), nums.end()); + const int n = nums.size(); + + int count = 0; + for (int k = 2; k < n; ++k) { + int i = 0, j = k - 1; + while (i < j) { // Two Pointers, linear time. + if (nums[i] + nums[j] + nums[k] >= target) { + --j; + } else { + count += j - i; + ++i; + } + } + } + + return count; + } +}; diff --git a/C++/3sum.cpp b/C++/3sum.cpp new file mode 100644 index 000000000..d1889e4a8 --- /dev/null +++ b/C++/3sum.cpp @@ -0,0 +1,41 @@ +// Time: O(n^2) +// Space: O(1) + +class Solution { +public: + /** + * @param numbers : Give an array numbers of n integer + * @return : Find all unique triplets in the array which gives the sum of zero. + */ + vector> threeSum(vector &nums) { + vector> ans; + const int target = 0; + + // Make nums in increasing order. Time: O(nlogn) + sort(nums.begin(), nums.end()); + + for (int i = 0; i < static_cast(nums.size()) - 2; ++i) { + if (i == 0 || nums[i] != nums[i - 1]) { // Skip duplicated. + for (int j = i + 1, k = nums.size() - 1; j < k; ) { // Time: O(n) for each i. + if (j - 1 > i && nums[j] == nums[j - 1]) { // Skip duplicated. + ++j; + } else if (k + 1 < nums.size() && nums[k] == nums[k + 1]) { // Skip duplicated. + --k; + } else { + const auto sum = nums[i] + nums[j] + nums[k]; + if (sum > target) { // Should decrease sum. + --k; + } else if (sum < target) { // Should increase sum. + ++j; + } else { + ans.push_back({nums[i], nums[j], nums[k]}); + ++j, --k; + } + } + } + } + } + + return ans; + } +}; diff --git a/C++/4-keys-keyboard.cpp b/C++/4-keys-keyboard.cpp new file mode 100644 index 000000000..6ddccef1d --- /dev/null +++ b/C++/4-keys-keyboard.cpp @@ -0,0 +1,39 @@ +// Time: O(1) +// Space: O(1) + +class Solution { +public: + int maxA(int N) { + if (N < 7) { + return N; + } + if (N == 10) { // the following rule doesn't hold when N = 10 + return 20; + } + auto n = N / 5 + 1; // n3 + n4 increases one every 5 keys + // (1) n = n3 + n4 + // (2) N + 1 = 4 * n3 + 5 * n4 + // 5 x (1) - (2) => 5*n - N - 1 = n3 + auto n3 = 5 * n - N - 1; + auto n4 = n - n3; + return pow(3, n3) * pow(4, n4); + } +}; + + +// Time: O(n) +// Space: O(1) +class Solution2 { +public: + int maxA(int N) { + if (N < 7) { + return N; + } + vector dp(6); + iota(dp.begin(), dp.end(), 0); + for (int i = 7; i <= N; ++i) { + dp[i % 6] = max(dp[(i - 4) % 6] * 3, dp[(i - 5) % 6] * 4); + } + return dp[N % 6]; + } +}; diff --git a/C++/4sum-ii.cpp b/C++/4sum-ii.cpp new file mode 100644 index 000000000..925945787 --- /dev/null +++ b/C++/4sum-ii.cpp @@ -0,0 +1,23 @@ +// Time: O(n^2) +// Space: O(n^2) + +class Solution { +public: + int fourSumCount(vector& A, vector& B, vector& C, vector& D) { + unordered_map A_B_sum; + for (const auto& a : A) { + for (const auto& b : B) { + ++A_B_sum[a + b]; + } + } + int result = 0; + for (const auto& c : C) { + for (const auto& d : D) { + if (A_B_sum.find(-c - d) != A_B_sum.end()) { + result += A_B_sum[-c - d]; + } + } + } + return result; + } +}; diff --git a/C++/4sum.cpp b/C++/4sum.cpp new file mode 100644 index 000000000..3687abf2d --- /dev/null +++ b/C++/4sum.cpp @@ -0,0 +1,79 @@ +// Time: O(n^3) +// Space: O(1) + +class Solution { +public: + vector > fourSum(vector &num, int target) { + int len = num.size(); + int left, right, sum; + sort(num.begin(), num.end()); + vector> res; + for (int i = 0; i < len - 3; ++i) { + if (i && num[i] == num[i - 1]) { + continue; + } + for (int j = i + 1; j < len - 2; ++j) { + if (j != i + 1 && num[j] == num[j - 1]) { + continue; + } + sum = target - num[i] - num[j]; + left = j + 1, right = len - 1; + while (left < right) { + if (num[left] + num[right] == sum) { + res.push_back({num[i], num[j], num[left], num[right]}); + ++left, --right; + while (left < right && num[left] == num[left - 1]) { + ++left; + } + while (left < right && num[right] == num[right + 1]) { + --right; + } + } else { + if (num[left] + num[right] > sum) { + --right; + } else { + ++left; + } + } + } + } + } + return res; + } +}; + +// Time: O(n^4) +// Space: O(n^2) +class Solution2 { +public: + vector > fourSum(vector &num, int target) { + vector> ans; + if (num.size() < 4) { + return ans; + } + sort(num.begin(), num.end()); + unordered_multimap> cache; + + for (int i = 0; i < num.size(); ++i) { + for (int j = i + 1; j < num.size(); ++j) { + cache.emplace(num[i] + num[j], make_pair(i, j)); + } + } + + for (auto i = cache.begin(); i != cache.end(); ++i) { + auto a = i->second.first; + auto b = i->second.second; + auto range = cache.equal_range(target - i->first); + for (auto j = range.first; j != range.second; ++j) { + auto c = j->second.first; + auto d = j->second.second; + if (b < c) { + ans.push_back({num[a], num[b], num[c], num[d]}); + } + } + } + sort(ans.begin(), ans.end()); + ans.erase(unique(ans.begin(), ans.end()), ans.end()); + return ans; + } +}; diff --git a/C++/accounts-merge.cpp b/C++/accounts-merge.cpp new file mode 100644 index 000000000..b4d27cc95 --- /dev/null +++ b/C++/accounts-merge.cpp @@ -0,0 +1,63 @@ +// Time: O(nlogn), n is the number of total emails, and the max length of email is 320, p.s. {64}@{255} +// Space: O(n) + +class Solution { +public: + vector> accountsMerge(vector>& accounts) { + UnionFind union_find; + unordered_map email_to_name; + unordered_map email_to_id; + for (const auto& account : accounts) { + const auto& name = account[0]; + for (int i = 1; i < account.size(); ++i) { + if (!email_to_id.count(account[i])) { + email_to_name[account[i]] = name; + email_to_id[account[i]] = union_find.get_id(); + } + union_find.union_set(email_to_id[account[1]], email_to_id[account[i]]); + } + } + + unordered_map> lookup; + for (const auto& kvp : email_to_name) { + const auto& email = kvp.first; + lookup[union_find.find_set(email_to_id[email])].emplace(email); + } + vector> result; + for (const auto& kvp : lookup) { + const auto& emails = kvp.second; + vector tmp{email_to_name[*emails.begin()]}; + for (const auto& email : emails) { + tmp.emplace_back(email); + } + result.emplace_back(move(tmp)); + } + return result; + } + +private: + class UnionFind { + public: + int get_id() { + set_.emplace_back(set_.size()); + return set_.size() - 1; + } + + int find_set(const int x) { + if (set_[x] != x) { + set_[x] = find_set(set_[x]); // Path compression. + } + return set_[x]; + } + + void union_set(const int x, const int y) { + int x_root = find_set(x), y_root = find_set(y); + if (x_root != y_root) { + set_[min(x_root, y_root)] = max(x_root, y_root); + } + } + + private: + vector set_; + }; +}; diff --git a/C++/add-and-search-word-data-structure-design.cpp b/C++/add-and-search-word-data-structure-design.cpp new file mode 100644 index 000000000..1968871e1 --- /dev/null +++ b/C++/add-and-search-word-data-structure-design.cpp @@ -0,0 +1,58 @@ +// Time: O(min(n, h)), per operation +// Space: O(min(n, h)) + +class WordDictionary { +public: + struct TrieNode { + bool isString = false; + unordered_map leaves; + }; + + WordDictionary() { + root_ = new TrieNode(); + root_->isString = true; + } + + // Adds a word into the data structure. + void addWord(string word) { + auto* p = root_; + for (const auto& c : word) { + if (p->leaves.find(c) == p->leaves.cend()) { + p->leaves[c] = new TrieNode; + } + p = p->leaves[c]; + } + p->isString = true; + } + + // Returns if the word is in the data structure. A word could + // contain the dot character '.' to represent any one letter. + bool search(string word) { + return searchWord(word, root_, 0); + } + + bool searchWord(string word, TrieNode *node, int s) { + if (s == word.length()) { + return node->isString; + } + // Match the char. + if (node->leaves.find(word[s]) != node->leaves.end()) { + return searchWord(word, node->leaves[word[s]], s + 1); + } else if (word[s] == '.') { // Skip the char. + for (const auto& i : node->leaves) { + if (searchWord(word, i.second, s + 1)) { + return true; + } + } + } + return false; + } + +private: + TrieNode *root_; +}; + +// Your WordDictionary object will be instantiated and called as such: +// WordDictionary wordDictionary; +// wordDictionary.addWord("word"); +// wordDictionary.search("pattern"); diff --git a/C++/add-binary.cpp b/C++/add-binary.cpp new file mode 100644 index 000000000..7a273e67d --- /dev/null +++ b/C++/add-binary.cpp @@ -0,0 +1,57 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + string addBinary(string a, string b) { + string res; + size_t res_len = max(a.length(), b.length()) ; + + size_t carry = 0; + for (int i = 0; i < res_len; ++i) { + const size_t a_bit_i = i < a.length() ? a[a.length() - 1 - i] - '0' : 0; + const size_t b_bit_i = i < b.length() ? b[b.length() - 1 - i] - '0' : 0; + size_t sum = carry + a_bit_i + b_bit_i; + carry = sum / 2; + sum %= 2; + res.push_back('0' + sum); + } + if (carry) { + res.push_back('0' + carry); + } + reverse(res.begin(), res.end()); + + return res; + } +}; + +// Iterator solution. +class Solution2 { +public: + string addBinary(string a, string b) { + size_t carry = 0; + string res; + + for (auto a_it = a.rbegin(), b_it = b.rbegin(); a_it != a.rend() || b_it != b.rend();) { + const size_t a_bit_i = (a_it != a.rend()) ? *a_it - '0' : 0; + const size_t b_bit_i = (b_it != b.rend()) ? *b_it - '0' : 0; + size_t sum = a_bit_i + b_bit_i + carry; + carry = sum / 2; + sum %= 2; + res.push_back('0' + sum); + + if (a_it != a.rend()) { + ++a_it; + } + if (b_it != b.rend()) { + ++b_it; + } + } + if (carry) { + res.push_back('0' + carry); + } + reverse(res.begin(), res.end()); + + return res; + } +}; diff --git a/C++/add-bold-tag-in-string.cpp b/C++/add-bold-tag-in-string.cpp new file mode 100644 index 000000000..cbcf265af --- /dev/null +++ b/C++/add-bold-tag-in-string.cpp @@ -0,0 +1,95 @@ +// Time: O(n * d * l), l is the average string length +// Space: O(n) + +// 16ms +class Solution { +public: + string addBoldTag(string s, vector& dict) { + vector lookup(s.length()); + for (const auto& d: dict) { + auto pos = -1; + while ((pos = s.find(d, pos + 1)) != string::npos) { + fill(lookup.begin() + pos, lookup.begin() + pos + d.length(), true); + } + } + string result; + for (int i = 0; i < s.length(); ++i) { + if (lookup[i] && (i == 0 || !lookup[i - 1])) { + result += ""; + } + result.push_back(s[i]); + if (lookup[i] && (i == (s.length() - 1) || !lookup[i + 1])) { + result += ""; + } + } + return result; + } +}; + +// Time: O(n * l), l is the average string length +// Space: O(t) , t is the size of trie +// trie solution, 142ms +class Solution2 { +public: + string addBoldTag(string s, vector& dict) { + TrieNode trie; + for (const auto& word : dict) { + trie.Insert(word); + } + + vector lookup(s.length()); + for (int i = 0; i < s.length(); ++i) { + auto curr = ≜ + int k = i - 1; + for (int j = i; j < s.length(); ++j) { + if (!curr->leaves.count(s[j])) { + break; + } + curr = curr->leaves[s[j]]; + if (curr->isString) { + k = j; + } + } + fill(lookup.begin() + i, lookup.begin() + k + 1, true); + } + + string result; + for (int i = 0; i < s.length(); ++i) { + if (lookup[i] && (i == 0 || !lookup[i - 1])) { + result += ""; + } + result.push_back(s[i]); + if (lookup[i] && (i == (s.length() - 1) || !lookup[i + 1])) { + result += ""; + } + } + return result; + } + +private: + struct TrieNode { + bool isString; + unordered_map leaves; + + TrieNode() : isString{false} {} + + void Insert(const string& s) { + auto* p = this; + for (const auto& c : s) { + if (!p->leaves[c]) { + p->leaves[c] = new TrieNode; + } + p = p->leaves[c]; + } + p->isString = true; + } + + ~TrieNode() { + for (auto& kvp : leaves) { + if (kvp.second) { + delete kvp.second; + } + } + } + }; +}; diff --git a/C++/add-digits.cpp b/C++/add-digits.cpp new file mode 100644 index 000000000..fe74f6818 --- /dev/null +++ b/C++/add-digits.cpp @@ -0,0 +1,9 @@ +// Time: O(1) +// Space: O(1) + +class Solution { +public: + int addDigits(int num) { + return (num - 1) % 9 + 1; + } +}; diff --git a/C++/add-one-row-to-tree.cpp b/C++/add-one-row-to-tree.cpp new file mode 100644 index 000000000..32e307c79 --- /dev/null +++ b/C++/add-one-row-to-tree.cpp @@ -0,0 +1,28 @@ +// Time: O(n) +// Space: O(h) + +/** + * 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* addOneRow(TreeNode* root, int v, int d) { + if (d == 0 || d == 1) { + auto node = new TreeNode(v); + (d == 1 ? node->left : node->right) = root; + return node; + } + if (root && d >= 2) { + root->left = addOneRow(root->left, v, d > 2 ? d - 1 : 1); + root->right = addOneRow(root->right, v, d > 2 ? d - 1 : 0); + } + return root; + } +}; + diff --git a/C++/add-strings.cpp b/C++/add-strings.cpp new file mode 100644 index 000000000..11c811ee6 --- /dev/null +++ b/C++/add-strings.cpp @@ -0,0 +1,25 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + string addStrings(string num1, string num2) { + string result; + + for (int i = num1.size() - 1, j = num2.size() - 1, carry = 0; + i >= 0 || j >= 0 || carry; + carry /= 10) { + + if (i >= 0) { + carry += num1[i--] - '0'; + } + if (j >= 0) { + carry += num2[j--] - '0'; + } + result += to_string(carry % 10); + } + reverse(result.begin(), result.end()); + + return result; + } +}; diff --git a/C++/add-two-numbers-ii.cpp b/C++/add-two-numbers-ii.cpp new file mode 100644 index 000000000..a1f4707d9 --- /dev/null +++ b/C++/add-two-numbers-ii.cpp @@ -0,0 +1,51 @@ +// Time: O(m + n) +// Space: O(m + n) + +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode(int x) : val(x), next(NULL) {} + * }; + */ +class Solution { +public: + ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) { + stack stk1, stk2; + while (l1) { + stk1.emplace(l1->val); + l1 = l1->next; + } + while (l2) { + stk2.emplace(l2->val); + l2 = l2->next; + } + + ListNode *prev = nullptr, *head = nullptr; + int sum = 0; + while (!stk1.empty() || !stk2.empty()) { + sum /= 10; + if (!stk1.empty()) { + sum += stk1.top(); + stk1.pop(); + } + + if (!stk2.empty()) { + sum += stk2.top(); + stk2.pop(); + } + + head = new ListNode(sum % 10); + head->next = prev; + prev = head; + } + + if (sum >= 10) { + head = new ListNode(sum / 10); + head->next = prev; + } + + return head; + } +}; diff --git a/C++/add-two-numbers.cpp b/C++/add-two-numbers.cpp new file mode 100644 index 000000000..1715de77e --- /dev/null +++ b/C++/add-two-numbers.cpp @@ -0,0 +1,31 @@ +// Time: O(n) +// Space: O(1) + +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode(int x) : val(x), next(NULL) {} + * }; + */ +class Solution { +public: + ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) { + ListNode dummy{0}; + auto curr = &dummy; + + auto carry = 0; + while (l1 || l2 || carry) { + auto a = l1? l1->val : 0, b = l2? l2->val : 0; + auto val = carry + a + b; + curr->next = new ListNode(val % 10); + carry = val / 10; + l1 = l1 ? l1->next : nullptr; + l2 = l2 ? l2->next : nullptr; + curr = curr->next; + } + + return dummy.next; + } +}; diff --git a/C++/addBinary.cpp b/C++/addBinary.cpp deleted file mode 100644 index 585618ce6..000000000 --- a/C++/addBinary.cpp +++ /dev/null @@ -1,29 +0,0 @@ -// Time Complexity: O(n) -// Space Complexity: O(1) - -class Solution { - public: - string addBinary(string a, string b) { - size_t carry = 0; - string ans; - - for(auto ai = a.rbegin(), bi = b.rbegin(); ai != a.rend() || bi != b.rend();) { - const size_t av = (ai != a.rend())? *ai - '0' : 0; - const size_t bv = (bi != b.rend())? *bi - '0' : 0; - const size_t val = (av + bv + carry) % 2; - carry = (av + bv + carry) / 2; - ans.push_back( val + '0' ); - - if(ai != a.rend()) - ++ai; - if(bi != b.rend()) - ++bi; - } - if(carry) - ans.push_back('1'); - - reverse(ans.begin(), ans.end()); - - return ans; - } -}; diff --git a/C++/addTwoNumbers.cpp b/C++/addTwoNumbers.cpp deleted file mode 100644 index b921dc8b8..000000000 --- a/C++/addTwoNumbers.cpp +++ /dev/null @@ -1,33 +0,0 @@ -// Time Complexity: O(n) -// Space Complexity: O(1) - -/** - * Definition for singly-linked list. - * struct ListNode { - * int val; - * ListNode *next; - * ListNode(int x) : val(x), next(NULL) {} - * }; - */ -class Solution { - public: - ListNode *addTwoNumbers(ListNode *l1, ListNode *l2) { - ListNode dummy(INT_MIN); - ListNode *p = &dummy; - int carry = 0; - - for(; l1 || l2; p = p->next) { - const int v1 = (l1)? l1->val : 0; - const int v2 = (l2)? l2->val : 0; - p->next = new ListNode((v1 + v2 + carry) % 10); - carry = (v1 + v2 + carry) / 10; - if(l1) l1 = l1->next; - if(l2) l2 = l2->next; - } - - if(carry) - p->next = new ListNode(carry); - - return dummy.next; - } -}; diff --git a/C++/additive-number.cpp b/C++/additive-number.cpp new file mode 100644 index 000000000..4be80dd35 --- /dev/null +++ b/C++/additive-number.cpp @@ -0,0 +1,52 @@ +// Time: O(n^3) +// Space: O(n) + +class Solution { +public: + bool isAdditiveNumber(string num) { + for (int i = 1; i < num.length(); ++i) { + for (int j = i + 1; j < num.length(); ++j) { + string s1 = num.substr(0, i), s2 = num.substr(i, j - i); + if ((s1.length() > 1 && s1[0] == '0') || + (s2.length() > 1 && s2[0] == '0')) { + continue; + } + + string next = add(s1, s2); + string cur = s1 + s2 + next; + while (cur.length() < num.length()) { + s1 = s2; + s2 = next; + next = add(s1, s2); + cur += next; + } + if (cur == num) { + return true; + } + } + } + return false; + } + +private: + string add(const string& m, const string& n) { + string res; + int res_length = max(m.length(), n.length()) ; + + int carry = 0; + for (int i = 0; i < res_length; ++i) { + int m_digit_i = i < m.length() ? m[m.length() - 1 - i] - '0' : 0; + int n_digit_i = i < n.length() ? n[n.length() - 1 - i] - '0' : 0; + int sum = carry + m_digit_i + n_digit_i; + carry = sum / 10; + sum %= 10; + res.push_back('0' + sum); + } + if (carry) { + res.push_back('0' + carry); + } + reverse(res.begin(), res.end()); + + return res; + } +}; diff --git a/C++/advantage-shuffle.cpp b/C++/advantage-shuffle.cpp new file mode 100644 index 000000000..96cb01fad --- /dev/null +++ b/C++/advantage-shuffle.cpp @@ -0,0 +1,35 @@ +// Time: O(nlogn) +// Space: O(n) + +class Solution { +public: + vector advantageCount(vector& A, vector& B) { + vector sortedA(A.cbegin(), A.cend()); + sort(sortedA.begin(), sortedA.end()); + vector sortedB(B.cbegin(), B.cend()); + sort(sortedB.begin(), sortedB.end()); + + unordered_map> candidates; + vector others; + int j = 0; + for (const auto& a : sortedA) { + if (a > sortedB[j]) { + candidates[sortedB[j]].emplace_back(a); + ++j; + } else { + others.emplace_back(a); + } + } + vector result; + for (const auto& b : B) { + if (!candidates[b].empty()) { + result.emplace_back(candidates[b].back()); + candidates[b].pop_back(); + } else { + result.emplace_back(others.back()); + others.pop_back(); + } + } + return result; + } +}; diff --git a/C++/alien-dictionary.cpp b/C++/alien-dictionary.cpp new file mode 100644 index 000000000..f7a8bc8f4 --- /dev/null +++ b/C++/alien-dictionary.cpp @@ -0,0 +1,222 @@ +// Time: O(n) +// Space: O(|V|+|E|) = O(26 + 26^2) = O(1) + +// BFS solution. +class Solution { +public: + string alienOrder(vector& words) { + unordered_set nodes; + unordered_map> in_degree, out_degree; + queue zero_in_degree_queue; + for (const auto& word : words) { + for (const auto& c : word) { + nodes.emplace(c); + } + } + for (int i = 1; i < words.size(); ++i) { + if (words[i - 1].length() > words[i].length() && + words[i - 1].substr(0, words[i].length()) == words[i]) { + return ""; + } + findEdges(words[i - 1], words[i], &in_degree, &out_degree); + } + for (const auto& node : nodes) { + if (in_degree.find(node) == in_degree.end()) { + zero_in_degree_queue.emplace(node); + } + } + + // BFS + string result; + while (!zero_in_degree_queue.empty()) { + const auto& precedence = zero_in_degree_queue.front(); + zero_in_degree_queue.pop(); + result.push_back(precedence); + + if (out_degree.find(precedence) != out_degree.end()) { + for (const auto& c : out_degree[precedence]) { + in_degree[c].erase(precedence); + if (in_degree[c].empty()) { + zero_in_degree_queue.emplace(c); + } + } + out_degree.erase(precedence); + } + } + + if (!out_degree.empty()) { + return ""; + } + + return result; + } + +private: + // Construct the graph. + void findEdges(const string &word1, const string &word2, + unordered_map> *in_degree, + unordered_map> *out_degree) { + const int len = min(word1.length(), word2.length()); + for (int i = 0; i < len; ++i) { + if (word1[i] != word2[i]) { + (*in_degree)[word2[i]].emplace(word1[i]); + (*out_degree)[word1[i]].emplace(word2[i]); + break; + } + } + } +}; + +// DFS solution. +class Solution2 { +public: + string alienOrder(vector& words) { + // Find ancestors of each node by DFS. + unordered_set nodes; + unordered_map> ancestors; + for (int i = 0; i < words.size(); ++i) { + for (const auto& c : words[i]) { + nodes.emplace(c); + } + if (i > 0) { + findEdges(words[i - 1], words[i], &ancestors); + } + } + + // Output topological order by DFS. + string result; + unordered_map visited; + for (const auto& node : nodes) { + if (topSortDFS(node, node, &ancestors, &visited, &result)) { + return ""; + } + } + + return result; + } + +private: + // Construct the graph. + void findEdges(const string &word1, const string &word2, + unordered_map> *ancestors) { + const int len = min(word1.length(), word2.length()); + for (int i = 0; i < len; ++i) { + if (word1[i] != word2[i]) { + (*ancestors)[word2[i]].emplace_back(word1[i]); + break; + } + } + } + + // Topological sort, return whether there is a cycle. + bool topSortDFS(const char& root, + const char& node, + unordered_map> *ancestors, + unordered_map *visited, + string *result) { + if (visited->emplace(make_pair(node, root)).second) { + for (auto& ancestor: (*ancestors)[node]) { + if (topSortDFS(root, ancestor, ancestors, visited, result)) { + return true; + } + } + result->push_back(node); + } else if ((*visited)[node] == root) { + // Visited from the same root in the DFS path. + // So it is cyclic. + return true; + } + return false; + } +}; + +// DFS with adjacency matrix solution. +class Solution3 { +public: + string alienOrder(vector& words) { + string result; + vector> graph(26, vector(26)); + findDependency(words, &graph); + findOrder(&graph, &result); + return result; + } + +private: + void findEdges(const string &word1, const string &word2, vector> *graph) { + const int len = min(word1.length(), word2.length()); + for (int i = 0; i < len; ++i) { + if (word1[i] != word2[i]) { + (*graph)[word1[i] - 'a'][word2[i] - 'a'] = true; + break; + } + } + } + + // Construct the graph. + void findDependency(const vector& words, vector> *graph) { + for (const auto& c : words[0]) { + (*graph)[c - 'a'][c - 'a'] = true; + } + for (int i = 1; i < words.size(); ++i) { + for (const auto& c : words[i]) { + (*graph)[c - 'a'] [c - 'a'] = true; + } + findEdges(words[i - 1], words[i], graph); + } + } + + // Topological sort, return whether there is a cycle. + bool topSortDFS(string *result, vector *visited, + vector> *graph, const int root) { + if ((*visited)[root]) { + result->clear(); + return true; + } + (*visited)[root] = true; + for (int i = 0; i < 26; ++i) { + if (i != root && (*graph)[root][i]) { + if (topSortDFS(result, visited, graph, i)) { + return true; + } + } + } + (*graph)[root][root] = false; + result->push_back(root + 'a'); + return false; + } + + void findOrder(vector> *graph, string *result) { + for (int i = 0; i < 26; ++i) { + // Find a root node. + bool root_node = (*graph)[i][i]; + if ((*graph)[i][i]) { + for (int j = 0; j < 26; ++j) { + if (j != i && (*graph)[j][i]) { + root_node = false; + break; + } + } + } + if (root_node) { + string reversed_order = ""; + vector visited(26, false); + if (topSortDFS(&reversed_order, &visited, graph, i)) { + result->clear(); + return; + } else { + result->append(reversed_order); + } + } + } + + // If there is any unvisited node, return "". + for (int i = 0; i < 26; ++i) { + if ((*graph)[i][i]) { + result->clear(); + return; + } + } + // The order should be reversed. + reverse(result->begin(), result->end()); + } +}; diff --git a/C++/all-nodes-distance-k-in-binary-tree.cpp b/C++/all-nodes-distance-k-in-binary-tree.cpp new file mode 100644 index 000000000..5fac4dc05 --- /dev/null +++ b/C++/all-nodes-distance-k-in-binary-tree.cpp @@ -0,0 +1,49 @@ +// Time: O(n) +// Space: O(n) + +/** + * 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: + vector distanceK(TreeNode* root, TreeNode* target, int K) { + unordered_map> neighbors; + dfs(nullptr, root, &neighbors); + + vector bfs{target->val}; + unordered_set lookup{target->val}; + for (int i = 0; i < K; ++i) { + vector curr; + for (const auto& node : bfs) { + for (const auto& nei : neighbors[node]) { + if (!lookup.count(nei)) { + curr.emplace_back(nei); + lookup.emplace(nei); + } + } + } + swap(bfs, curr); + } + return bfs; + } + +private: + void dfs(TreeNode *parent, TreeNode *child, + unordered_map> *neighbors) { + if (!child) { + return; + } + if (parent) { + (*neighbors)[parent->val].emplace_back(child->val); + (*neighbors)[child->val].emplace_back(parent->val); + } + dfs(child, child->left, neighbors); + dfs(child, child->right, neighbors); + } +}; diff --git a/C++/all-oone-data-structure.cpp b/C++/all-oone-data-structure.cpp new file mode 100644 index 000000000..f3f81ccb5 --- /dev/null +++ b/C++/all-oone-data-structure.cpp @@ -0,0 +1,79 @@ +// Time: O(1), per operation +// Space: O(k) + +class AllOne { +public: + /** Initialize your data structure here. */ + AllOne() { + + } + + /** Inserts a new key with value 1. Or increments an existing key by 1. */ + void inc(string key) { + if (!bucketOfKey_.count(key)) { + bucketOfKey_[key] = buckets_.insert(buckets_.begin(), {0, {key}}); + } + + auto next = bucketOfKey_[key], bucket = next++; + if (next == buckets_.end() || next->value > bucket->value + 1) { + next = buckets_.insert(next, {bucket->value + 1, {}}); + } + next->keys.insert(key); + bucketOfKey_[key] = next; + + bucket->keys.erase(key); + if (bucket->keys.empty()) { + buckets_.erase(bucket); + } + } + + /** Decrements an existing key by 1. If Key's value is 1, remove it from the data structure. */ + void dec(string key) { + if (!bucketOfKey_.count(key)) { + return; + } + + auto prev = bucketOfKey_[key], bucket = prev--; + bucketOfKey_.erase(key); + if (bucket->value > 1) { + if (bucket == buckets_.begin() || prev->value < bucket->value - 1) { + prev = buckets_.insert(bucket, {bucket->value - 1, {}}); + } + prev->keys.insert(key); + bucketOfKey_[key] = prev; + } + + bucket->keys.erase(key); + if (bucket->keys.empty()) { + buckets_.erase(bucket); + } + } + + /** Returns one of the keys with maximal value. */ + string getMaxKey() { + return buckets_.empty() ? "" : *(buckets_.rbegin()->keys.begin()); + } + + /** Returns one of the keys with Minimal value. */ + string getMinKey() { + return buckets_.empty() ? "" : *(buckets_.begin()->keys.begin()); + } + +private: + struct Bucket { + int value; + unordered_set keys; + }; + list buckets_; + unordered_map::iterator> bucketOfKey_; +}; + +/** + * 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(); + */ + diff --git a/C++/all-paths-from-source-to-target.cpp b/C++/all-paths-from-source-to-target.cpp new file mode 100644 index 000000000..ec4e12698 --- /dev/null +++ b/C++/all-paths-from-source-to-target.cpp @@ -0,0 +1,28 @@ +// Time: O(p + r * n), p is the count of all the possible paths in graph, +// r is the count of the result. +// Space: O(n) + +class Solution { +public: + vector> allPathsSourceTarget(vector>& graph) { + vector> result; + vector path{0}; + dfs(graph, 0, &path, &result); + return result; + } + +private: + void dfs(const vector>& graph, + int curr, vector *path, + vector> *result) { + if (curr == graph.size() - 1) { + result->emplace_back(*path); + return; + } + for (const auto& node: graph[curr]) { + path->emplace_back(node); + dfs(graph, node, path, result); + path->pop_back(); + } + } +}; diff --git a/C++/ambiguous-coordinates.cpp b/C++/ambiguous-coordinates.cpp new file mode 100644 index 000000000..ec2780b88 --- /dev/null +++ b/C++/ambiguous-coordinates.cpp @@ -0,0 +1,45 @@ +// Time: O(n^4) +// Space: O(n^2) + +class Solution { +public: + vector ambiguousCoordinates(string S) { + vector result; + for (int i = 1; i < S.length() - 2; ++i) { + const auto& lefts = make(S, 1, i); + const auto& rights = make(S, i + 1, S.length() - 2 - i); + for (const auto& left : lefts) { + for (const auto& right : rights) { + string s; + s += "("; + s += left; + s += ", "; + s += right; + s += ")"; + result.emplace_back(move(s)); + } + } + } + return result; + } + +private: + // Time: O(n^2) + // Space: O(n^2) + vector make(const string& S, int i, int n) { + vector result; + for (int d = 1; d <= n; ++d) { + const auto& left = S.substr(i, d); + const auto& right = S.substr(i + d, n - d); + if ((left.front() != '0' || left == "0") && + right.back() != '0') { + string s; + s += left; + s += (!right.empty() ? "." : ""); + s += right; + result.emplace_back(move(s)); + } + } + return result; + } +}; diff --git a/C++/anagrams.cpp b/C++/anagrams.cpp deleted file mode 100644 index 1ca89dc01..000000000 --- a/C++/anagrams.cpp +++ /dev/null @@ -1,22 +0,0 @@ -// Time Complexity: O(klogk * n) ~= O(n), k is length of string, n is number of strings -// Space Complexity: O(k * n) ~= O(n) - -class Solution { - public: - vector anagrams(vector &strs) { - vector ans; - unordered_map > group; - for(auto s : strs) { - string k = s; - sort(k.begin(), k.end()); - group[k].push_back(s); - } - - for(auto it = group.cbegin(); it != group.cend(); ++it) { - if(it->second.size() > 1) - ans.insert(ans.end(), it->second.begin(), it->second.end()); - } - - return ans; - } -};` diff --git a/C++/android-unlock-patterns.cpp b/C++/android-unlock-patterns.cpp new file mode 100644 index 000000000..6e49d0f97 --- /dev/null +++ b/C++/android-unlock-patterns.cpp @@ -0,0 +1,208 @@ +// Time: O(9^2 * 2^9) +// Space: O(9 * 2^9) + +// DP solution. +class Solution { +public: + int numberOfPatterns(int m, int n) { + // dp[i][j]: i is the set of the numbers in binary representation, + // dp[i][j] is the number of ways ending with the number j. + vector> dp(1 << 9 , vector(9, 0)); + for (int i = 0; i < 9; ++i) { + dp[merge(0, i)][i] = 1; + } + + int res = 0; + for (int used = 0; used < dp.size(); ++used) { + const auto number = number_of_keys(used); + if (number > n) { + continue; + } + for (int i = 0; i < 9; ++i) { + if (!contain(used, i)) { + continue; + } + if (m <= number && number <= n) { + res += dp[used][i]; + } + + const auto x1 = i / 3; + const auto y1 = i % 3; + for (int j = 0; j < 9; ++j) { + if (contain(used, j)) { + continue; + } + const auto x2 = j / 3; + const auto y2 = j % 3; + if (((x1 == x2 && abs(y1 - y2) == 2) || + (y1 == y2 && abs(x1 - x2) == 2) || + (abs(x1 - x2) == 2 && abs(y1 - y2) == 2)) && + !contain(used, convert((x1 + x2) / 2, (y1 + y2) / 2))) { + continue; + } + dp[merge(used, j)][j] += dp[used][i]; + } + } + } + + return res; + } + +private: + inline int merge(int i, int j) { + return i | (1 << j); + } + + inline int number_of_keys(int i) { + int number = 0; + for (; i; i &= i - 1) { + ++number; + } + return number; + } + + inline bool contain(int i, int j) { + return i & (1 << j); + } + + inline int convert(int i, int j) { + return 3 * i + j; + } +}; + + +// Time: O(9^2 * 2^9) +// Space: O(9 * 2^9) +// DP solution. +class Solution2 { +public: + int numberOfPatterns(int m, int n) { + // dp[i][j]: i is the set of the numbers in binary representation, + // dp[i][j] is the number of ways ending with the number j. + vector> dp(1 << 9 , vector(9, 0)); + for (int i = 0; i < 9; ++i) { + dp[merge(0, i)][i] = 1; + } + + int res = 0; + for (int used = 0; used < dp.size(); ++used) { + const auto number = number_of_keys(used); + if (number > n) { + continue; + } + for (int i = 0; i < 9; ++i) { + if (!contain(used, i)) { + continue; + } + + const auto x1 = i / 3; + const auto y1 = i % 3; + for (int j = 0; j < 9; ++j) { + if (i == j || !contain(used, j)) { + continue; + } + const auto x2 = j / 3; + const auto y2 = j % 3; + if (((x1 == x2 && abs(y1 - y2) == 2) || + (y1 == y2 && abs(x1 - x2) == 2) || + (abs(x1 - x2) == 2 && abs(y1 - y2) == 2)) && + !contain(used, convert((x1 + x2) / 2, (y1 + y2) / 2))) { + continue; + } + dp[used][i] += dp[exclude(used, i)][j]; + } + if (m <= number && number <= n) { + res += dp[used][i]; + } + } + } + + return res; + } + +private: + inline int merge(int i, int j) { + return i | (1 << j); + } + + inline int number_of_keys(int i) { + int number = 0; + for (; i; i &= i - 1) { + ++number; + } + return number; + } + + inline bool contain(int i, int j) { + return i & (1 << j); + } + + inline int exclude(int i, int j) { + return i & ~(1 << j); + } + + inline int convert(int i, int j) { + return 3 * i + j; + } +}; + + +// Time: O(9!) +// Space: O(9) +// Backtracking solution. +class Solution3 { +public: + int numberOfPatterns(int m, int n) { + int number = 0; + // 1, 3, 5, 7 + number += 4 * numberOfPatternsHelper(m, n, 1, merge(0, 0), 0); + // 2, 4, 6, 8 + number += 4 * numberOfPatternsHelper(m, n, 1, merge(0, 1), 1); + // 5 + number += numberOfPatternsHelper(m, n, 1, merge(0, 4), 4); + return number; + } + +private: + int numberOfPatternsHelper(int m, int n, int level, int used, int i) { + int number = 0; + if (level > n) { + return number; + } + if (level >= m) { + ++number; + } + + const auto x1 = i / 3; + const auto y1 = i % 3; + for (int j = 0; j < 9; ++j) { + if (contain(used, j)) { + continue; + } + const auto x2 = j / 3; + const auto y2 = j % 3; + if (((x1 == x2 && abs(y1 - y2) == 2) || + (y1 == y2 && abs(x1 - x2) == 2) || + (abs(x1 - x2) == 2 && abs(y1 - y2) == 2)) && + !contain(used, convert((x1 + x2) / 2, (y1 + y2) / 2))) { + continue; + } + number += numberOfPatternsHelper(m, n, level + 1, merge(used, j), j); + } + + return number; + } + +private: + inline int merge(int i, int j) { + return i | (1 << j); + } + + inline bool contain(int i, int j) { + return i & (1 << j); + } + + inline int convert(int i, int j) { + return 3 * i + j; + } +}; diff --git a/C++/arithmetic-slices-ii-subsequence.cpp b/C++/arithmetic-slices-ii-subsequence.cpp new file mode 100644 index 000000000..5e20aacf1 --- /dev/null +++ b/C++/arithmetic-slices-ii-subsequence.cpp @@ -0,0 +1,21 @@ +// Time: O(n^2) +// Space: O(n * d) + +class Solution { +public: + int numberOfArithmeticSlices(vector& A) { + int result = 0; + vector> dp(A.size()); + for (int i = 1; i < A.size(); ++i) { + for (int j = 0; j < i; ++j) { + const auto diff = static_cast(A[i]) - A[j]; + ++dp[i][diff]; + if (dp[j].count(diff)) { + dp[i][diff] += dp[j][diff]; + result += dp[j][diff]; + } + } + } + return result; + } +}; diff --git a/C++/arithmetic-slices.cpp b/C++/arithmetic-slices.cpp new file mode 100644 index 000000000..f6430fbac --- /dev/null +++ b/C++/arithmetic-slices.cpp @@ -0,0 +1,16 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + int numberOfArithmeticSlices(vector& A) { + int res = 0, i = 0; + for (int i = 0; i + 2 < A.size(); ++i) { + const auto start = i; + while (i + 2 < A.size() && A[i + 2] + A[i] == 2 * A[i + 1]) { + res += (i++) - start + 1; + } + } + return res; + } +}; diff --git a/C++/arranging-coins.cpp b/C++/arranging-coins.cpp new file mode 100644 index 000000000..f25a3e364 --- /dev/null +++ b/C++/arranging-coins.cpp @@ -0,0 +1,27 @@ +// Time: O(logn) +// Space: O(1) + +class Solution { +public: + int arrangeCoins(int n) { + return static_cast((sqrt(8.0 * n + 1) - 1) / 2); + } +}; + +// Time: O(logn) +// Space: O(1) +class Solution2 { +public: + int arrangeCoins(int n) { + long long left = 1, right = n; + while (left <= right) { + const auto mid = left + (right - left) / 2; + if (2L * n < mid * (mid + 1)) { + right = mid - 1; + } else { + left = mid + 1; + } + } + return left - 1; + } +}; diff --git a/C++/array-nesting.cpp b/C++/array-nesting.cpp new file mode 100644 index 000000000..2dc891643 --- /dev/null +++ b/C++/array-nesting.cpp @@ -0,0 +1,22 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + int arrayNesting(vector& nums) { + auto result = 0; + for (const auto& num : nums) { + if (num != numeric_limits::max()) { + auto start = num, count = 0; + while (nums[start] != numeric_limits::max()) { + auto temp = start; + start = nums[start]; + nums[temp] = numeric_limits::max(); + ++count; + } + result = max(result, count); + } + } + return result; + } +}; diff --git a/C++/array-partition-i.cpp b/C++/array-partition-i.cpp new file mode 100644 index 000000000..2bd831e8f --- /dev/null +++ b/C++/array-partition-i.cpp @@ -0,0 +1,34 @@ +// Time: O(r), r is the range size of the integers +// Space: O(r) + +class Solution { +public: + int arrayPairSum(vector& nums) { + const auto LEFT = -10000; + const auto RIGHT = 10000; + vector lookup(RIGHT - LEFT + 1, 0); + for (const auto& num: nums) { + ++lookup[num - LEFT]; + } + auto r = 0, result = 0; + for (int i = LEFT; i <= RIGHT; ++i) { + result += (lookup[i - LEFT] + 1 - r) / 2 * i; + r = (lookup[i - LEFT] + r) % 2; + } + return result; + } +}; + +// Time: O(nlogn) +// Space: O(1) +class Solution2 { +public: + int arrayPairSum(vector& nums) { + sort(nums.begin(), nums.end()); + auto result = 0; + for (auto i = 0; i < nums.size(); i += 2) { + result += nums[i]; + } + return result; + } +}; diff --git a/C++/assign-cookies.cpp b/C++/assign-cookies.cpp new file mode 100644 index 000000000..3daae8a7c --- /dev/null +++ b/C++/assign-cookies.cpp @@ -0,0 +1,22 @@ +// Time: O(nlogn) +// Space: O(1) + +class Solution { +public: + int findContentChildren(vector& g, vector& s) { + sort(g.begin(), g.end()); + sort(s.begin(), s.end()); + + int result = 0; + for (int i = 0, j = 0; j < s.size(); ++j) { + if (i == g.size()) { + break; + } + if (s[j] >= g[i]) { + ++i; + ++result; + } + } + return result; + } +}; diff --git a/C++/asteroid-collision.cpp b/C++/asteroid-collision.cpp new file mode 100644 index 000000000..6a647dff4 --- /dev/null +++ b/C++/asteroid-collision.cpp @@ -0,0 +1,26 @@ +// Time: O(n) +// Space: O(n) + +class Solution { +public: + vector asteroidCollision(vector& asteroids) { + vector result; + for (const auto& asteroid : asteroids) { + bool is_exploded = false; + while (!result.empty() && asteroid < 0 && 0 < result.back()) { + if (result.back() < -asteroid) { + result.pop_back(); + continue; + } else if (result.back() == -asteroid) { + result.pop_back(); + } + is_exploded = true; + break; + } + if (!is_exploded) { + result.emplace_back(asteroid); + } + } + return result; + } +}; diff --git a/C++/atoi.cpp b/C++/atoi.cpp deleted file mode 100644 index f2256ec0e..000000000 --- a/C++/atoi.cpp +++ /dev/null @@ -1,35 +0,0 @@ - -// LeetCode, String to Integer (atoi) -// Complexity: -// O(n) time -// O(1) space - -class Solution { -public: - int atoi(const char *str) { - int num = 0; - int sign = 1; - const int n = strlen(str); - int i = 0; - while (str[i] == ' ' && i < n) i++; - // parse sign - if (str[i] == '+') i++; - if (str[i] == '-') { - sign = -1; - i++; - } - - for (; i < n; i++) { - // handle non-digital character - if (str[i] < '0' || str[i] > '9') - break; - // handle overflow - if ( num > INT_MAX / 10 - || (num == INT_MAX / 10 && (str[i] - '0') > INT_MAX % 10)) { - return sign == -1 ? INT_MIN : INT_MAX; - } - num = num * 10 + str[i] - '0'; - } - return num * sign; - } -}; \ No newline at end of file diff --git a/C++/average-of-levels-in-binary-tree.cpp b/C++/average-of-levels-in-binary-tree.cpp new file mode 100644 index 000000000..4a9e68d8f --- /dev/null +++ b/C++/average-of-levels-in-binary-tree.cpp @@ -0,0 +1,38 @@ +// Time: O(n) +// Space: O(h) + +/** + * 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: + vector averageOfLevels(TreeNode* root) { + vector result; + vector q; + q.emplace_back(root); + while (!q.empty()) { + long long sum = 0, count = 0; + vector next_q; + for (const auto& n : q) { + sum += n->val; + ++count; + if (n->left) { + next_q.emplace_back(n->left); + } + if (n->right) { + next_q.emplace_back(n->right); + } + } + swap(q, next_q); + result.emplace_back(sum * 1.0 / count); + } + return result; + } +}; + diff --git a/C++/backspace-string-compare.cpp b/C++/backspace-string-compare.cpp new file mode 100644 index 000000000..16516df59 --- /dev/null +++ b/C++/backspace-string-compare.cpp @@ -0,0 +1,33 @@ +// Time: O(m + n) +// Space: O(1) + +class Solution { +public: + bool backspaceCompare(string S, string T) { + int skipS = 0, skipT = 0; + for (int i = S.length() - 1, j = T.length() - 1; + i >= 0 || j >= 0; + --i, --j) { + auto x = findNextChar(S, &i, &skipS); + auto y = findNextChar(T, &j, &skipT); + if (x != y) { + return false; + } + } + return true; + } + +private: + char findNextChar(const string& s, int *i, int *skip) { + for (; *i >= 0; --(*i)) { + if (s[*i] == '#') { + ++(*skip); + } else if ((*skip) > 0) { + --(*skip); + } else { + return s[*i]; + } + } + return '\0'; + } +}; diff --git a/C++/base-7.cpp b/C++/base-7.cpp new file mode 100644 index 000000000..e568211c3 --- /dev/null +++ b/C++/base-7.cpp @@ -0,0 +1,31 @@ +// Time: O(1) +// Space: O(1) + +class Solution { +public: + string convertToBase7(int num) { + if (num < 0) { + return string("-").append(convertToBase7(-num)); + } + string result; + while (num) { + result.append(to_string(num % 7)); + num /= 7; + } + reverse(result.begin(), result.end()); + return result.empty() ? "0" : result; + } +}; + +class Solution2 { +public: + string convertToBase7(int num) { + if (num < 0) { + return string("-").append(convertToBase7(-num)); + } + if (num < 7) { + return to_string(num); + } + return convertToBase7(num / 7).append(to_string(num % 7)); + } +}; diff --git a/C++/baseball-game.cpp b/C++/baseball-game.cpp new file mode 100644 index 000000000..b696962d5 --- /dev/null +++ b/C++/baseball-game.cpp @@ -0,0 +1,21 @@ +// Time: O(n) +// Space: O(n) + +class Solution { +public: + int calPoints(vector& ops) { + vector records; + for (const auto& op : ops) { + if (op == "+") { + records.emplace_back(records[records.size() - 2] + records.back()); + } else if (op == "D") { + records.emplace_back(2 * records.back()); + } else if (op == "C") { + records.pop_back(); + } else { + records.emplace_back(stoi(op)); + } + } + return accumulate(records.begin(), records.end(), 0); + } +}; diff --git a/C++/basic-calculator-ii.cpp b/C++/basic-calculator-ii.cpp new file mode 100644 index 000000000..e9cb14484 --- /dev/null +++ b/C++/basic-calculator-ii.cpp @@ -0,0 +1,59 @@ +// Time: O(n) +// Space: O(n) + +// Support +, -, *, /. +class Solution { +public: + int calculate(string s) { + stack operands; + stack operators; + string operand; + for (int i = s.length() - 1; i >= 0; --i) { + if (isdigit(s[i])) { + operand.push_back(s[i]); + if (i == 0 || !isdigit(s[i - 1])) { + reverse(operand.begin(), operand.end()); + operands.emplace(stol(operand)); + operand.clear(); + } + } else if (s[i] == ')' || s[i] == '*' || + s[i] == '/') { + operators.emplace(s[i]); + } else if (s[i] == '+' || s[i] == '-') { + while (!operators.empty() && (operators.top() == '*' || + operators.top() == '/')) { + compute(operands, operators); + } + operators.emplace(s[i]); + } else if (s[i] == '(') { + // operators at least one element, i.e. ')'. + while (operators.top() != ')') { + compute(operands, operators); + } + operators.pop(); + } + } + while (!operators.empty()) { + compute(operands, operators); + } + return operands.top(); + } + + void compute(stack& operands, stack& operators) { + const int64_t left = operands.top(); + operands.pop(); + const int64_t right = operands.top(); + operands.pop(); + const char op = operators.top(); + operators.pop(); + if (op == '+') { + operands.emplace(left + right); + } else if (op == '-') { + operands.emplace(left - right); + } else if (op == '*') { + operands.emplace(left * right); + } else if (op == '/') { + operands.emplace(left / right); + } + } +}; diff --git a/C++/basic-calculator-iii.cpp b/C++/basic-calculator-iii.cpp new file mode 100644 index 000000000..2461788f6 --- /dev/null +++ b/C++/basic-calculator-iii.cpp @@ -0,0 +1,60 @@ +// Time: O(n) +// Space: O(n) + +// Support +, -, *, /. +class Solution { +public: + int calculate(string s) { + stack operands; + stack operators; + string operand; + for (int i = s.length() - 1; i >= 0; --i) { + if (isdigit(s[i])) { + operand.push_back(s[i]); + if (i == 0 || !isdigit(s[i - 1])) { + reverse(operand.begin(), operand.end()); + operands.emplace(stol(operand)); + operand.clear(); + } + } else if (s[i] == ')' || s[i] == '*' || + s[i] == '/') { + operators.emplace(s[i]); + } else if (s[i] == '+' || s[i] == '-') { + while (!operators.empty() && (operators.top() == '*' || + operators.top() == '/')) { + compute(operands, operators); + } + operators.emplace(s[i]); + } else if (s[i] == '(') { + // operators at least one element, i.e. ')'. + while (operators.top() != ')') { + compute(operands, operators); + } + operators.pop(); + } + } + while (!operators.empty()) { + compute(operands, operators); + } + return operands.top(); + } + + template + void compute(stack& operands, stack& operators) { + const auto left = operands.top(); + operands.pop(); + const auto right = operands.top(); + operands.pop(); + const char op = operators.top(); + operators.pop(); + if (op == '+') { + operands.emplace(left + right); + } else if (op == '-') { + operands.emplace(left - right); + } else if (op == '*') { + operands.emplace(left * right); + } else if (op == '/') { + operands.emplace(left / right); + } + } +}; diff --git a/C++/basic-calculator-iv.cpp b/C++/basic-calculator-iv.cpp new file mode 100644 index 000000000..ac1904426 --- /dev/null +++ b/C++/basic-calculator-iv.cpp @@ -0,0 +1,206 @@ +// Time: +: O(d * t), t is the number of terms, d is the average degree of terms +// -: O(d * t) +// *: O(d * t^2) +// eval: O(d * t) +// to_list: O(d * tlogt) +// Space: O(e + d * t), e is the number of evalvars + +class Poly { +public: + Poly() {} + + Poly(const string& expr) { + vector key; + if (is_number(expr)) { + polies_[key] = stoi(expr); + } else { + key.emplace_back(expr); + ++polies_[key]; + } + } + + Poly operator+(const Poly &rhs) const { // Time: O(d * t) + Poly result; + for (const auto& kvp : polies_) { + result.update(kvp.first, kvp.second); + } + for (const auto& kvp : rhs.polies_) { + result.update(kvp.first, kvp.second); + } + return result; + } + + Poly operator-(const Poly &rhs) const { // Time: O(d * t) + Poly result; + for (const auto& kvp : polies_) { + result.update(kvp.first, kvp.second); + } + for (const auto& kvp : rhs.polies_) { + result.update(kvp.first, -kvp.second); + } + return result; + } + + Poly operator*(const Poly &rhs) const { // Time: O(d * t^2) + Poly result; + for (const auto& kvp1 : polies_) { + for (const auto& kvp2 : rhs.polies_) { + result.update(merge(kvp1.first, kvp2.first), + kvp1.second * kvp2.second); + } + } + return result; + } + + Poly eval(const unordered_map& lookup) const { // Time: O(d * t) + Poly result; + for (const auto& kvp : polies_) { + vector key; + int c = kvp.second; + for (const auto& token : kvp.first) { + if (lookup.count(token)) { + c *= lookup.at(token); + } else { + key.emplace_back(token); + } + } + result.update(key, c); + } + return result; + } + + operator vector() const { // Time: O(d * tlogt) + map, int, Compare>> sorted(polies_.begin(), polies_.end()); + vector result; + for (const auto& kvp : sorted) { + vector tmp(kvp.first); + tmp.emplace(tmp.begin(), to_string(kvp.second)); + result.emplace_back(join(tmp, "*")); + } + return result; + } + +private: + bool is_number(const std::string &s) const { + return !s.empty() && std::all_of(s.begin(), s.end(), ::isdigit); + } + + void update(const vector& key, int val) { + polies_[key] += val; + if (polies_[key] == 0) { + polies_.erase(key); + } + } + + vector merge(const vector& arr1, const vector& arr2) const { // Time: O(d) + vector result; + int i = 0, j = 0; + while (i < arr1.size() || j < arr2.size()) { + if (j == arr2.size()) { + result.emplace_back(arr1[i++]); + } else if (i == arr1.size()) { + result.emplace_back(arr2[j++]); + } else if (arr1[i] < arr2[j]) { + result.emplace_back(arr1[i++]); + } else { + result.emplace_back(arr2[j++]); + } + } + return result; + } + + string join(const vector& strings, const string& delim) const { + if (strings.empty()) { + return ""; + } + ostringstream imploded; + copy(strings.begin(), prev(strings.end()), ostream_iterator(imploded, delim.c_str())); + return imploded.str() + *prev(strings.end()); + } + + template + class Compare { + public: + bool operator() + (const ContType& x,const ContType& y) { + return x.size() != y.size() ? x.size() > y.size() : x < y; + } + }; + + template + struct Hash { + size_t operator()(const ContType& v) const { + size_t seed = 0; + for (const auto& i : v) { + seed ^= std::hash{}(i) + 0x9e3779b9 + (seed<<6) + (seed>>2); + } + return seed; + } + }; + + unordered_map, int, Hash>> polies_; +}; + +class Solution { +public: + vector basicCalculatorIV(string expression, vector& evalvars, vector& evalints) { + unordered_map lookup; + for (int i = 0; i < evalvars.size(); ++i) { + lookup[evalvars[i]] = evalints[i]; + } + return parse(expression).eval(lookup); + } + +private: + Poly parse(const string& s) { + if (s.empty()) { + return Poly(); + } + stack operands; + stack operators; + string operand; + for (int i = s.length() - 1; i >= 0; --i) { + if (isalnum(s[i])) { + operand.push_back(s[i]); + if (i == 0 || !isalnum(s[i - 1])) { + reverse(operand.begin(), operand.end()); + operands.emplace(Poly(operand)); + operand.clear(); + } + } else if (s[i] == ')' || s[i] == '*') { + operators.emplace(s[i]); + } else if (s[i] == '+' || s[i] == '-') { + while (!operators.empty() && operators.top() == '*') { + compute(operands, operators); + } + operators.emplace(s[i]); + } else if (s[i] == '(') { + while (operators.top() != ')') { + compute(operands, operators); + } + operators.pop(); + } + } + while (!operators.empty()) { + compute(operands, operators); + } + return operands.top(); + } + + template + void compute(stack& operands, stack& operators) { + const auto left = operands.top(); + operands.pop(); + const auto right = operands.top(); + operands.pop(); + const char op = operators.top(); + operators.pop(); + if (op == '+') { + operands.emplace(left + right); + } else if (op == '-') { + operands.emplace(left - right); + } else if (op == '*') { + operands.emplace(left * right); + } + } +}; diff --git a/C++/basic-calculator.cpp b/C++/basic-calculator.cpp new file mode 100644 index 000000000..1201fe524 --- /dev/null +++ b/C++/basic-calculator.cpp @@ -0,0 +1,106 @@ +// Time: O(n) +// Space: O(n) + +// Support +, -, *, /. +class Solution { +public: + int calculate(string s) { + stack operands; + stack operators; + string operand; + for (int i = s.length() - 1; i >= 0; --i) { + if (isdigit(s[i])) { + operand.push_back(s[i]); + if (i == 0 || !isdigit(s[i - 1])) { + reverse(operand.begin(), operand.end()); + operands.emplace(stol(operand)); + operand.clear(); + } + } else if (s[i] == ')' || s[i] == '*' || + s[i] == '/') { + operators.emplace(s[i]); + } else if (s[i] == '+' || s[i] == '-') { + while (!operators.empty() && (operators.top() == '*' || + operators.top() == '/')) { + compute(operands, operators); + } + operators.emplace(s[i]); + } else if (s[i] == '(') { + // operators at least one element, i.e. ')'. + while (operators.top() != ')') { + compute(operands, operators); + } + operators.pop(); + } + } + while (!operators.empty()) { + compute(operands, operators); + } + return operands.top(); + } + + void compute(stack& operands, stack& operators) { + const int64_t left = operands.top(); + operands.pop(); + const int64_t right = operands.top(); + operands.pop(); + const char op = operators.top(); + operators.pop(); + if (op == '+') { + operands.emplace(left + right); + } else if (op == '-') { + operands.emplace(left - right); + } else if (op == '*') { + operands.emplace(left * right); + } else if (op == '/') { + operands.emplace(left / right); + } + } +}; + +// Time: O(n) +// Space: O(n) +// Only support +, -. +class Solution2 { +public: + int calculate(string s) { + stack operands; + stack operators; + string operand; + for (int i = s.length() - 1; i >= 0; --i) { + if (isdigit(s[i])) { + operand.push_back(s[i]); + if (i == 0 || !isdigit(s[i - 1])) { + reverse(operand.begin(), operand.end()); + operands.emplace(stoi(operand)); + operand.clear(); + } + } else if (s[i] == ')' || s[i] == '+' || s[i] == '-') { + operators.emplace(s[i]); + } else if (s[i] == '(') { + while (operators.top() != ')') { + compute(operands, operators); + } + operators.pop(); + } + } + while (!operators.empty()) { + compute(operands, operators); + } + return operands.top(); + } + + void compute(stack& operands, stack& operators) { + const int left = operands.top(); + operands.pop(); + const int right = operands.top(); + operands.pop(); + const char op = operators.top(); + operators.pop(); + if (op == '+') { + operands.emplace(left + right); + } else if (op == '-') { + operands.emplace(left - right); + } + } +}; diff --git a/C++/battleships-in-a-board.cpp b/C++/battleships-in-a-board.cpp new file mode 100644 index 000000000..f3bd89683 --- /dev/null +++ b/C++/battleships-in-a-board.cpp @@ -0,0 +1,21 @@ +// Time: O(m * n) +// Space: O(1) + +class Solution { +public: + int countBattleships(vector>& board) { + if (board.empty() || board[0].empty()) { + return 0; + } + + int cnt = 0; + for (int i = 0; i < board.size(); ++i) { + for (int j = 0; j < board[0].size(); ++j) { + cnt += board[i][j] == 'X' && + (i == 0 || board[i - 1][j] != 'X') && + (j == 0 || board[i][j - 1] != 'X'); + } + } + return cnt; + } +}; diff --git a/C++/beautiful-arrangement-ii.cpp b/C++/beautiful-arrangement-ii.cpp new file mode 100644 index 000000000..26afe6cca --- /dev/null +++ b/C++/beautiful-arrangement-ii.cpp @@ -0,0 +1,17 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + vector constructArray(int n, int k) { + vector result; + int left = 1, right = n; + while (left <= right) { + result.emplace_back(k % 2 ? left++ : right--); + if (k > 1) { + --k; + } + } + return result; + } +}; diff --git a/C++/beautiful-arrangement.cpp b/C++/beautiful-arrangement.cpp new file mode 100644 index 000000000..b2ca85574 --- /dev/null +++ b/C++/beautiful-arrangement.cpp @@ -0,0 +1,27 @@ +// Time: O(n!) +// Space: O(n) + +class Solution { +public: + int countArrangement(int N) { + vector arrangement(N); + iota(arrangement.begin(), arrangement.end(), 1); + return countArrangementHelper(N, &arrangement); + } + +private: + int countArrangementHelper(int n, vector *arrangement) { + if (n <= 0) { + return 1; + } + int count = 0; + for (int i = 0; i < n; ++i) { + if ((*arrangement)[i] % n == 0 || n % (*arrangement)[i] == 0) { + swap((*arrangement)[i], (*arrangement)[n - 1]); + count += countArrangementHelper(n - 1, arrangement); + swap((*arrangement)[i], (*arrangement)[n - 1]); + } + } + return count; + } +}; diff --git a/C++/best-meeting-point.cpp b/C++/best-meeting-point.cpp new file mode 100644 index 000000000..dc83344b0 --- /dev/null +++ b/C++/best-meeting-point.cpp @@ -0,0 +1,30 @@ +// Time: O(m * n) +// Space: O(m + n) + +class Solution { +public: + int minTotalDistance(vector>& grid) { + vector x, y; + for (int i = 0; i < grid.size(); ++i) { + for (int j = 0; j < grid[0].size(); ++j) { + if (grid[i][j]) { + x.emplace_back(i); + y.emplace_back(j); + } + } + } + nth_element(x.begin(), x.begin() + x.size() / 2, x.end()); + nth_element(y.begin(), y.begin() + y.size() / 2, y.end()); + const int mid_x = x[x.size() / 2]; + const int mid_y = y[y.size() / 2]; + int sum = 0; + for (int i = 0; i < grid.size(); ++i) { + for (int j = 0; j < grid[0].size(); ++j) { + if (grid[i][j]) { + sum += abs(mid_x - i) + abs(mid_y - j); + } + } + } + return sum; + } +}; diff --git a/C++/best-time-to-buy-and-sell-stock-with-cooldown.cpp b/C++/best-time-to-buy-and-sell-stock-with-cooldown.cpp new file mode 100644 index 000000000..b68cc89bb --- /dev/null +++ b/C++/best-time-to-buy-and-sell-stock-with-cooldown.cpp @@ -0,0 +1,44 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + int maxProfit(vector& prices) { + if (prices.empty()) { + return 0; + } + vector buy(2), sell(2), coolDown(2); + buy[0] = -prices[0]; + for (int i = 1; i < prices.size(); ++i) { + // Bought before or buy today. + buy[i % 2] = max(buy[(i - 1) % 2], coolDown[(i - 1) % 2] - prices[i]); + // Sell today. + sell[i % 2] = buy[(i - 1) % 2] + prices[i]; + // Sold before yesterday or sold yesterday. + coolDown[i % 2] = max(coolDown[(i - 1) % 2], sell[(i - 1) % 2]); + } + return max(coolDown[(prices.size() - 1) % 2], sell[(prices.size() - 1) % 2]); + } +}; + +// Time: O(n) +// Space: O(n) +class Solution2 { +public: + int maxProfit(vector& prices) { + if (prices.empty()) { + return 0; + } + vector buy(prices.size()), sell(prices.size()), coolDown(prices.size()); + buy[0] = -prices[0]; + for (int i = 1; i < prices.size(); ++i) { + // Bought before or buy today. + buy[i] = max(buy[i - 1], coolDown[i - 1] - prices[i]); + // Sell today. + sell[i] = buy[i - 1] + prices[i]; + // Sold before yesterday or sold yesterday. + coolDown[i] = max(coolDown[i - 1], sell[i - 1]); + } + return max(coolDown[prices.size() - 1], sell[prices.size() - 1]); + } +}; diff --git a/C++/best-time-to-buy-and-sell-stock-with-transaction-fee.cpp b/C++/best-time-to-buy-and-sell-stock-with-transaction-fee.cpp new file mode 100644 index 000000000..a4e840a92 --- /dev/null +++ b/C++/best-time-to-buy-and-sell-stock-with-transaction-fee.cpp @@ -0,0 +1,14 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + int maxProfit(vector& prices, int fee) { + int cash = 0, hold = -prices[0]; + for (int i = 1; i < prices.size(); ++i) { + cash = max(cash, hold + prices[i] - fee); + hold = max(hold, cash - prices[i]); + } + return cash; + } +}; diff --git a/C++/best-time-to-buy-and-sell-stock.cpp b/C++/best-time-to-buy-and-sell-stock.cpp new file mode 100644 index 000000000..9105909a9 --- /dev/null +++ b/C++/best-time-to-buy-and-sell-stock.cpp @@ -0,0 +1,21 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + int maxProfit(vector &prices) { + if (prices.empty()) { + return 0; + } + + int hold1 = numeric_limits::min(); + int release1 = numeric_limits::min(); + + for (const auto& p : prices) { + hold1 = max(hold1, -p); + release1 = max(release1, hold1 + p); + } + + return release1; + } +}; diff --git a/C++/binary-gap.cpp b/C++/binary-gap.cpp new file mode 100644 index 000000000..084fdf2a1 --- /dev/null +++ b/C++/binary-gap.cpp @@ -0,0 +1,19 @@ +// Time: O(logn) = O(1) due to n is a 32-bit number +// Space: O(1) + +class Solution { +public: + int binaryGap(int N) { + int result = 0; + int last = -1; + for (int i = 0; i < 32; ++i) { + if ((N >> i) & 1) { + if (last != -1) { + result = max(result, i - last); + } + last = i; + } + } + return result; + } +}; diff --git a/C++/binary-number-with-alternating-bits.cpp b/C++/binary-number-with-alternating-bits.cpp new file mode 100644 index 000000000..35309ddfd --- /dev/null +++ b/C++/binary-number-with-alternating-bits.cpp @@ -0,0 +1,18 @@ +// Time: O(1) +// Space: O(1) + +class Solution { +public: + bool hasAlternatingBits(int n) { + auto curr = n % 2; + n /= 2; + while (n > 0) { + if (curr == n % 2) { + return false; + } + curr = n % 2; + n /= 2; + } + return true; + } +}; diff --git a/C++/binary-search-tree-iterator.cpp b/C++/binary-search-tree-iterator.cpp new file mode 100644 index 000000000..0bcf46624 --- /dev/null +++ b/C++/binary-search-tree-iterator.cpp @@ -0,0 +1,49 @@ +// Time: O(1), amortized +// Space: O(h) + +/** + * Definition for binary tree + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode(int x) : val(x), left(NULL), right(NULL) {} + * }; + */ +class BSTIterator { +public: + BSTIterator(TreeNode *root) : cur_(root) { + } + + /** @return whether we have a next smallest number */ + bool hasNext() { + return !s_.empty() || cur_ != nullptr; + } + + /** @return the next smallest number */ + int next() { + // Go to the left most descendant. + while (cur_ != nullptr) { + s_.emplace(cur_); + cur_ = cur_->left; + } + cur_ = s_.top(); // Left most node. + s_.pop(); + + const auto *node = cur_; + cur_ = cur_->right; // Visit right child. + + return node->val; + } + +private: + stack s_; + TreeNode *cur_; +}; + +/** + * Your BSTIterator will be called like this: + * BSTIterator i = BSTIterator(root); + * while (i.hasNext()) cout << i.next(); + */ + diff --git a/C++/binary-search.cpp b/C++/binary-search.cpp new file mode 100644 index 000000000..53b02fe7a --- /dev/null +++ b/C++/binary-search.cpp @@ -0,0 +1,20 @@ +// Time: O(logn) +// Space: O(1) + +class Solution { +public: + int search(vector& nums, int target) { + int left = 0, right = nums.size() - 1; + while (left <= right) { + auto mid = left + (right-left) / 2; + if (nums[mid] > target) { + right = mid - 1; + } else if (nums[mid] < target) { + left = mid + 1; + } else { + return mid; + } + } + return -1; + } +}; diff --git a/C++/binary-tree-inorder-traversal.cpp b/C++/binary-tree-inorder-traversal.cpp new file mode 100644 index 000000000..28c341587 --- /dev/null +++ b/C++/binary-tree-inorder-traversal.cpp @@ -0,0 +1,66 @@ +// Time: O(n) +// Space: O(1) + +/** + * 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: + vector inorderTraversal(TreeNode* root) { + vector res; + TreeNode *curr = root; + while (curr) { + if (!curr->left) { + res.emplace_back(curr->val); + curr = curr->right; + } else { + TreeNode *node = curr->left; + while (node->right && node->right != curr) { + node = node->right; + } + if (!node->right) { + node->right = curr; + curr = curr->left; + } else { + res.emplace_back(curr->val); + node->right = nullptr; + curr = curr->right; + } + } + } + return res; + } +}; + +// Time: O(n) +// Space: O(h) +class Solution2 { +public: + vector inorderTraversal(TreeNode* root) { + vector res; + stack> s; + s.emplace(root, false); + while (!s.empty()) { + bool visited; + tie(root, visited) = s.top(); + s.pop(); + if (root == nullptr) { + continue; + } + if (visited) { + res.emplace_back(root->val); + } else { + s.emplace(root->right, false); + s.emplace(root, true); + s.emplace(root->left, false); + } + } + return res; + } +}; diff --git a/C++/binary-tree-level-order-traversal.cpp b/C++/binary-tree-level-order-traversal.cpp new file mode 100644 index 000000000..96e751a8c --- /dev/null +++ b/C++/binary-tree-level-order-traversal.cpp @@ -0,0 +1,42 @@ +// Time: O(n) +// Space: O(n) + +/** + * 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: + vector> levelOrder(TreeNode* root) { + vector> result; + queue que; + + if (root != nullptr) { + que.emplace(root); + } + + while (!que.empty()) { + vector level; + int size = que.size(); + for (int i = 0; i < size; i++) { + auto *front = que.front(); + que.pop(); + level.emplace_back(front->val); + if (front->left != nullptr) { + que.emplace(front->left); + } + if (front->right != nullptr) { + que.emplace(front->right); + } + } + result.emplace_back(move(level)); + } + + return result; + } +}; diff --git a/C++/binary-tree-longest-consecutive-sequence-ii.cpp b/C++/binary-tree-longest-consecutive-sequence-ii.cpp new file mode 100644 index 000000000..a1b18a229 --- /dev/null +++ b/C++/binary-tree-longest-consecutive-sequence-ii.cpp @@ -0,0 +1,46 @@ +// Time: O(n) +// Space: O(h) + +/** + * 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: + int longestConsecutive(TreeNode* root) { + int max_len = 0; + longestConsecutiveHelper(root, &max_len); + return max_len; + } + + pair longestConsecutiveHelper(TreeNode *root, int *max_len) { + if (!root) { + return {0, 0}; + } + const pair left_len = longestConsecutiveHelper(root->left, max_len); + const pair right_len = longestConsecutiveHelper(root->right, max_len); + + int cur_inc_len = 1, cur_dec_len = 1; + if (root->left) { + if (root->left->val == root->val + 1) { + cur_inc_len = max(cur_inc_len, left_len.first + 1); + } else if (root->left->val == root->val - 1){ + cur_dec_len = max(cur_dec_len, left_len.second + 1); + } + } + if (root->right) { + if (root->right->val == root->val + 1) { + cur_inc_len = max(cur_inc_len, right_len.first + 1); + } else if (root->right->val == root->val - 1) { + cur_dec_len = max(cur_dec_len, right_len.second + 1); + } + } + *max_len = max(*max_len, cur_dec_len + cur_inc_len - 1); + return {cur_inc_len, cur_dec_len}; + } +}; diff --git a/C++/binary-tree-longest-consecutive-sequence.cpp b/C++/binary-tree-longest-consecutive-sequence.cpp new file mode 100644 index 000000000..75fc8356c --- /dev/null +++ b/C++/binary-tree-longest-consecutive-sequence.cpp @@ -0,0 +1,39 @@ +// Time: O(n) +// Space: O(h) + +/** + * 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: + int longestConsecutive(TreeNode* root) { + int max_len = 0; + longestConsecutiveHelper(root, &max_len); + return max_len; + } + + int longestConsecutiveHelper(TreeNode *root, int *max_len) { + if (!root) { + return 0; + } + + const int left_len = longestConsecutiveHelper(root->left, max_len); + const int right_len = longestConsecutiveHelper(root->right, max_len); + + int cur_len = 1; + if (root->left && root->left->val == root->val + 1) { + cur_len = max(cur_len, left_len + 1); + } + if (root->right && root->right->val == root->val + 1) { + cur_len = max(cur_len, right_len + 1); + } + *max_len = max(*max_len, max(cur_len, max(left_len, right_len))); + return cur_len; + } +}; diff --git a/C++/binary-tree-paths.cpp b/C++/binary-tree-paths.cpp new file mode 100644 index 000000000..44dd1a57f --- /dev/null +++ b/C++/binary-tree-paths.cpp @@ -0,0 +1,47 @@ +// Time: O(n * h) +// Space: O(h) + +/** + * 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: + vector binaryTreePaths(TreeNode* root) { + vector result; + vector path; + binaryTreePathsRecu(root, &path, &result); + return result; + } + + void binaryTreePathsRecu(TreeNode *node, vector *path, vector *result) { + if (!node) { + return; + } + + if (!node->left && !node->right) { + string ans = ""; + for (const auto& n : *path) { + ans.append(to_string(n->val).append("->")); + } + result->emplace_back(move(ans.append(to_string(node->val)))); + } + + if (node->left) { + path->emplace_back(node); + binaryTreePathsRecu(node->left, path, result); + path->pop_back(); + } + + if (node->right) { + path->emplace_back(node); + binaryTreePathsRecu(node->right, path, result); + path->pop_back(); + } + } +}; diff --git a/C++/binary-tree-postorder-traversal.cpp b/C++/binary-tree-postorder-traversal.cpp new file mode 100644 index 000000000..c3e127e7b --- /dev/null +++ b/C++/binary-tree-postorder-traversal.cpp @@ -0,0 +1,81 @@ +// Time: O(n) +// Space: O(1) + +/** + * 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: + vector postorderTraversal(TreeNode* root) { + vector res; + TreeNode dummy(INT_MIN); + dummy.left = root; + auto *cur = &dummy; + while (cur) { + if (!cur->left) { + cur = cur->right; + } else { + auto *node = cur->left; + while (node->right && node->right != cur) { + node = node->right; + } + if (!node->right) { + node->right = cur; + cur = cur->left; + } else { + const auto& v = trace_back(cur->left, node); + res.insert(res.end(), v.cbegin(), v.cend()); + node->right = nullptr; + cur = cur->right; + } + } + } + return res; + } + +private: + vector trace_back(const TreeNode *from, const TreeNode *to) { + vector res; + auto *cur = from; + while (cur != to) { + res.emplace_back(cur->val); + cur = cur->right; + } + res.emplace_back(to->val); + reverse(res.begin(), res.end()); + return res; + } +}; + +// Time: O(n) +// Space: O(h) +class Solution2 { +public: + vector postorderTraversal(TreeNode* root) { + vector res; + stack> s; + s.emplace(root, false); + while (!s.empty()) { + bool visited; + tie(root, visited) = s.top(); + s.pop(); + if (root == nullptr) { + continue; + } + if (visited) { + res.emplace_back(root->val); + } else { + s.emplace(root, true); + s.emplace(root->right, false); + s.emplace(root->left, false); + } + } + return res; + } +}; diff --git a/C++/binary-tree-preorder-traversal.cpp b/C++/binary-tree-preorder-traversal.cpp new file mode 100644 index 000000000..007fbab11 --- /dev/null +++ b/C++/binary-tree-preorder-traversal.cpp @@ -0,0 +1,66 @@ +// Time: O(n) +// Space: O(1) + +/** + * 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: + vector preorderTraversal(TreeNode* root) { + vector res; + auto *curr = root; + while (curr) { + if (!curr->left) { + res.emplace_back(curr->val); + curr = curr->right; + } else { + auto *node = curr->left; + while (node->right && node->right != curr) { + node = node->right; + } + if (!node->right) { + res.emplace_back(curr->val); + node->right = curr; + curr = curr->left; + } else { + node->right = nullptr; + curr = curr->right; + } + } + } + return res; + } +}; + +// Time: O(n) +// Space: O(h) +class Solution2 { +public: + vector preorderTraversal(TreeNode* root) { + vector res; + stack> s; + s.emplace(root, false); + while (!s.empty()) { + bool visited; + tie(root, visited) = s.top(); + s.pop(); + if (root == nullptr) { + continue; + } + if (visited) { + res.emplace_back(root->val); + } else { + s.emplace(root->right, false); + s.emplace(root->left, false); + s.emplace(root, true); + } + } + return res; + } +}; diff --git a/C++/binary-tree-pruning.cpp b/C++/binary-tree-pruning.cpp new file mode 100644 index 000000000..cfb22cd9e --- /dev/null +++ b/C++/binary-tree-pruning.cpp @@ -0,0 +1,26 @@ +// Time: O(n) +// Space: O(h) + +/** + * 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* pruneTree(TreeNode* root) { + if (!root) { + return nullptr; + } + root->left = pruneTree(root->left); + root->right = pruneTree(root->right); + if (!root->left && !root->right && root->val == 0) { + return nullptr; + } + return root; + } +}; diff --git a/C++/binary-tree-tilt.cpp b/C++/binary-tree-tilt.cpp new file mode 100644 index 000000000..9511fcb34 --- /dev/null +++ b/C++/binary-tree-tilt.cpp @@ -0,0 +1,30 @@ +// Time: O(n) +// Space: O(n) + +/** + * 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: + int findTilt(TreeNode* root) { + int tilt = 0; + postOrderTraverse(root, &tilt); + return tilt; + } +private: + int postOrderTraverse(TreeNode* root, int *tilt) { + if (!root) { + return 0; + } + auto left = postOrderTraverse(root->left, tilt); + auto right = postOrderTraverse(root->right, tilt); + *tilt += abs(left - right); + return left + right + root->val; + } +}; diff --git a/C++/binary-tree-vertical-order-traversal.cpp b/C++/binary-tree-vertical-order-traversal.cpp new file mode 100644 index 000000000..2d7036587 --- /dev/null +++ b/C++/binary-tree-vertical-order-traversal.cpp @@ -0,0 +1,40 @@ +// Time: O(n) +// Space: O(n) + +/** + * 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: + vector> verticalOrder(TreeNode* root) { + unordered_map> cols; + vector> queue{{root, 0}}; + for (int i = 0; i < queue.size(); ++i) { + TreeNode *node; + int j; + tie(node, j) = queue[i]; + if (node) { + cols[j].emplace_back(node->val); + queue.push_back({node->left, j - 1}); + queue.push_back({node->right, j + 1}); + } + } + int min_idx = numeric_limits::max(), + max_idx = numeric_limits::min(); + for (const auto& kvp : cols) { + min_idx = min(min_idx, kvp.first); + max_idx = max(max_idx, kvp.first); + } + vector> res; + for (int i = min_idx; !cols.empty() && i <= max_idx; ++i) { + res.emplace_back(move(cols[i])); + } + return res; + } +}; diff --git a/C++/binary-trees-with-factors.cpp b/C++/binary-trees-with-factors.cpp new file mode 100644 index 000000000..3a451ba9b --- /dev/null +++ b/C++/binary-trees-with-factors.cpp @@ -0,0 +1,23 @@ +// Time: O(n^2) +// Space: O(n) + +class Solution { +public: + int numFactoredBinaryTrees(vector& A) { + static const int M = 1e9 + 7; + sort(A.begin(), A.end()); + unordered_map dp; + for (int i = 0; i < A.size(); ++i) { + dp[A[i]] = 1; + for (int j = 0; j < i; ++j) { + if (A[i] % A[j] == 0 && dp.count(A[i] / A[j])) { + dp[A[i]] = (dp[A[i]] + dp[A[j]] * dp[A[i] / A[j]]) % M; + } + } + } + return accumulate(dp.cbegin(), dp.cend(), 0L, + [](int64_t x, const pair& p) { + return x + p.second; + }) % M; + } +}; diff --git a/C++/binary-watch.cpp b/C++/binary-watch.cpp new file mode 100644 index 000000000..f033592ae --- /dev/null +++ b/C++/binary-watch.cpp @@ -0,0 +1,28 @@ +// Time: O(1) +// Space: O(1) + +class Solution { +public: + vector readBinaryWatch(int num) { + vector result; + for (int h = 0; h < 12; ++h) { + for (int m = 0; m < 60; ++m) { + if (bit_count(h) + bit_count(m) == num) { + const auto hour = to_string(h); + const auto minute = m < 10 ? "0" + to_string(m) : to_string(m); + result.emplace_back(hour + ":" + minute); + } + } + } + return result; + } + +private: + int bit_count(int bits) { + int count = 0; + for (; bits; bits &= bits - 1) { + ++count; + } + return count; + } +}; diff --git a/C++/bitwise-and-of-numbers-range.cpp b/C++/bitwise-and-of-numbers-range.cpp new file mode 100644 index 000000000..a05ce4491 --- /dev/null +++ b/C++/bitwise-and-of-numbers-range.cpp @@ -0,0 +1,12 @@ +// Time: O(1) +// Space: O(1) + +class Solution { +public: + int rangeBitwiseAnd(int m, int n) { + while (m < n) { // Remove the last bit 1 until n <= m. + n &= n - 1; + } + return n; + } +}; diff --git a/C++/bold-words-in-string.cpp b/C++/bold-words-in-string.cpp new file mode 100644 index 000000000..71cbedbe9 --- /dev/null +++ b/C++/bold-words-in-string.cpp @@ -0,0 +1,94 @@ +// Time: O(n * l), n is the length of S, l is the average length of words +// Space: O(t) , t is the size of trie + +class Solution { +public: + string boldWords(vector& words, string S) { + TrieNode trie; + for (const auto& word : words) { + trie.Insert(word); + } + + vector lookup(S.length()); + for (int i = 0; i < S.length(); ++i) { + auto curr = ≜ + int k = i - 1; + for (int j = i; j < S.length(); ++j) { + if (!curr->leaves[S[j] - 'a']) { + break; + } + curr = curr->leaves[S[j] - 'a']; + if (curr->isString) { + k = j; + } + } + fill(lookup.begin() + i, lookup.begin() + k + 1, true); + } + + string result; + for (int i = 0; i < S.length(); ++i) { + if (lookup[i] && (i == 0 || !lookup[i - 1])) { + result += ""; + } + result.push_back(S[i]); + if (lookup[i] && (i == (S.length() - 1) || !lookup[i + 1])) { + result += ""; + } + } + return result; + } + +private: + struct TrieNode { + bool isString; + vector leaves; + + TrieNode() : isString{false}, leaves(26) {} + + void Insert(const string& s) { + auto* p = this; + for (const auto& c : s) { + if (!p->leaves[c - 'a']) { + p->leaves[c - 'a'] = new TrieNode; + } + p = p->leaves[c - 'a']; + } + p->isString = true; + } + + ~TrieNode() { + for (auto& node : leaves) { + if (node) { + delete node; + } + } + } + }; +}; + + +// Time: O(n * d * l), l is the average length of words +// Space: O(n) +class Solution2 { +public: + string boldWords(vector& words, string S) { + vector lookup(S.length()); + for (const auto& d: words) { + auto pos = -1; + while ((pos = S.find(d, pos + 1)) != string::npos) { + fill(lookup.begin() + pos, lookup.begin() + pos + d.length(), true); + } + } + string result; + for (int i = 0; i < S.length(); ++i) { + if (lookup[i] && (i == 0 || !lookup[i - 1])) { + result += ""; + } + result.push_back(S[i]); + if (lookup[i] && (i == (S.length() - 1) || !lookup[i + 1])) { + result += ""; + } + } + return result; + } +}; diff --git a/C++/bomb-enemy.cpp b/C++/bomb-enemy.cpp new file mode 100644 index 000000000..d3b58e527 --- /dev/null +++ b/C++/bomb-enemy.cpp @@ -0,0 +1,50 @@ +// Time: O(m * n) +// Space: O(m * n) + +class Solution { +public: + int maxKilledEnemies(vector>& grid) { + int result = 0; + if (grid.empty() || grid[0].empty()) { + return result; + } + + vector> down{grid.size(), vector(grid[0].size())}; + vector> right{grid.size(), vector(grid[0].size())}; + for (int i = grid.size() - 1; i >= 0; --i) { + for (int j = grid[0].size() - 1; j >= 0; --j) { + if (grid[i][j] != 'W') { + if (i + 1 < grid.size()) { + down[i][j] = down[i + 1][j]; + } + if (j + 1 < grid[0].size()) { + right[i][j] = right[i][j + 1]; + } + if (grid[i][j] == 'E') { + ++down[i][j]; + ++right[i][j]; + } + } + } + } + + int left = 0; + vector up(grid[0].size()); + for (int i = 0; i < grid.size(); ++i) { + left = 0; + for (int j = 0; j < grid[0].size(); ++j) { + if (grid[i][j] == 'W') { + up[j] = 0; + left = 0; + } else if (grid[i][j] == 'E') { + ++up[j]; + ++left; + } else { + result = max(result, left + up[j] + right[i][j] + down[i][j]); + } + } + } + + return result; + } +}; diff --git a/C++/boundary-of-binary-tree.cpp b/C++/boundary-of-binary-tree.cpp new file mode 100644 index 000000000..a372556b4 --- /dev/null +++ b/C++/boundary-of-binary-tree.cpp @@ -0,0 +1,65 @@ +// Time: O(n) +// Space: O(h) + +/** + * 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: + vector boundaryOfBinaryTree(TreeNode* root) { + if (!root) { + return {}; + } + + vector nodes; + nodes.emplace_back(root->val); + leftBoundary(root->left, &nodes); + leaves(root->left, &nodes); + leaves(root->right, &nodes); + rightBoundary(root->right, &nodes); + return nodes; + } + +private: + void leftBoundary(TreeNode *root, vector *nodes) { + if (!root || (!root->left && !root->right)) { + return; + } + nodes->emplace_back(root->val); + if (!root->left) { + leftBoundary(root->right, nodes); + } else { + leftBoundary(root->left, nodes); + } + } + + void rightBoundary(TreeNode *root, vector *nodes) { + if (!root || (!root->right && !root->left)) { + return; + } + if (!root->right) { + rightBoundary(root->left, nodes); + } else { + rightBoundary(root->right, nodes); + } + nodes->emplace_back(root->val); + } + + void leaves(TreeNode *root, vector *nodes) { + if (!root) { + return; + } + if (!root->left && !root->right) { + nodes->emplace_back(root->val); + return; + } + leaves(root->left, nodes); + leaves(root->right, nodes); + } +}; diff --git a/C++/brick-wall.cpp b/C++/brick-wall.cpp new file mode 100644 index 000000000..55a8d5e83 --- /dev/null +++ b/C++/brick-wall.cpp @@ -0,0 +1,16 @@ +// Time: O(n), n is the total number of the bricks +// Space: O(m), m is the total number different widths + +class Solution { +public: + int leastBricks(vector>& wall) { + unordered_map widths; + auto result = wall.size(); + for (const auto& row : wall) { + for (auto i = 0, width = 0; i < row.size() - 1; ++i) { + result = min(result, wall.size() - (++widths[width += row[i]])); + } + } + return result; + } +}; diff --git a/C++/bricks-falling-when-hit.cpp b/C++/bricks-falling-when-hit.cpp new file mode 100644 index 000000000..5fb3931a6 --- /dev/null +++ b/C++/bricks-falling-when-hit.cpp @@ -0,0 +1,95 @@ +// Time: O(r * c) +// Space: O(r * c) + +class Solution { +public: + vector hitBricks(vector>& grid, vector>& hits) { + static const vector> directions{{-1, 0}, { 1, 0}, + { 0, 1}, { 0, -1}}; + const int R = grid.size(); + const int C = grid[0].size(); + const auto index = [&C](int r, int c) { return r * C + c; }; + + vector> hit_grid(grid); + for (const auto& hit : hits) { + hit_grid[hit[0]][hit[1]] = 0; + } + + UnionFind union_find(R * C); + for (int r = 0; r < hit_grid.size(); ++r) { + for (int c = 0; c < hit_grid[r].size(); ++c) { + if (!hit_grid[r][c]) { + continue; + } + if (r == 0) { + union_find.union_set(index(r, c), R * C); + } + if (r && hit_grid[r - 1][c]) { + union_find.union_set(index(r, c), index(r - 1, c)); + } + if (c && hit_grid[r][c - 1]) { + union_find.union_set(index(r, c), index(r, c - 1)); + } + } + } + + vector result; + for (int i = hits.size() - 1; i >= 0; --i) { + const auto r = hits[i][0], c = hits[i][1]; + const auto prev_roof = union_find.top(); + if (grid[r][c] == 0) { + result.emplace_back(0); + continue; + } + for (const auto& d : directions) { + const auto nr = r + d.first, nc = c + d.second; + if (0 <= nr && nr < R && + 0 <= nc && nc < C && + hit_grid[nr][nc]) { + union_find.union_set(index(r, c), index(nr, nc)); + } + } + if (r == 0) { + union_find.union_set(index(r, c), R * C); + } + hit_grid[r][c] = 1; + result.emplace_back(max(0, union_find.top() - prev_roof - 1)); + } + reverse(result.begin(), result.end()); + return result; + } + +private: + class UnionFind { + public: + UnionFind(const int n) : set_(n + 1), size_(n + 1, 1) { + iota(set_.begin(), set_.end(), 0); + size_.back() = 0; + } + + int find_set(const int x) { + if (set_[x] != x) { + set_[x] = find_set(set_[x]); // Path compression. + } + return set_[x]; + } + + bool union_set(const int x, const int y) { + int x_root = find_set(x), y_root = find_set(y); + if (x_root == y_root) { + return false; + } + set_[min(x_root, y_root)] = max(x_root, y_root); + size_[max(x_root, y_root)] += size_[min(x_root, y_root)]; + return true; + } + + int top() { + return size_[find_set(size_.size() - 1)]; + } + + private: + vector set_; + vector size_; + }; +}; diff --git a/C++/buddy-strings.cpp b/C++/buddy-strings.cpp new file mode 100644 index 000000000..738d07a66 --- /dev/null +++ b/C++/buddy-strings.cpp @@ -0,0 +1,22 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + bool buddyStrings(string A, string B) { + if (A.length() != B.length()) { + return false; + } + vector diff; + for (int i = 0; i < A.length(); ++i) { + if (A[i] != B[i]) { + diff.emplace_back(i); + if (diff.size() > 2) { + return false; + } + } + } + return (diff.empty() && unordered_set(A.begin(), A.end()).size() < A.size()) || + (diff.size() == 2 && A[diff[0]] == B[diff[1]] && A[diff[1]] == B[diff[0]]); + } +}; diff --git a/C++/buildTreeWithPostOrder.cpp b/C++/buildTreeWithPostOrder.cpp deleted file mode 100644 index 7510393a9..000000000 --- a/C++/buildTreeWithPostOrder.cpp +++ /dev/null @@ -1,35 +0,0 @@ -// Time Complexity: O(n) -// Space Complexity: O(logn) - -/** - * Definition for binary tree - * struct TreeNode { - * int val; - * TreeNode *left; - * TreeNode *right; - * TreeNode(int x) : val(x), left(NULL), right(NULL) {} - * }; - */ -class Solution { - public: - TreeNode *buildTree(vector &inorder, vector &postorder) { - return buildTree(begin(inorder), end(inorder), begin(postorder), end(postorder)); - } - private: - template - TreeNode *buildTree(InputIterator in_first, InputIterator in_last, InputIterator post_first, InputIterator post_last) { - if(in_first == in_last) - return NULL; - if(post_first == post_last) - return NULL; - - auto root = new TreeNode(*prev(post_last)); - auto inRootPos = find(in_first, in_last, *prev(post_last)); - auto leftSize = distance(in_first, inRootPos); - root->left = buildTree(in_first, inRootPos, post_first, next(post_first, leftSize)); - root->right = buildTree(next(inRootPos), in_last, next(post_first, leftSize), prev(post_last)); - - return root; - } - -}; diff --git a/C++/buildTreeWithPreOrder.cpp b/C++/buildTreeWithPreOrder.cpp deleted file mode 100644 index e5c1b0dde..000000000 --- a/C++/buildTreeWithPreOrder.cpp +++ /dev/null @@ -1,35 +0,0 @@ -// Time Complexity: O(n) -// Space Complexity: O(logn) - -/** - * Definition for binary tree - * struct TreeNode { - * int val; - * TreeNode *left; - * TreeNode *right; - * TreeNode(int x) : val(x), left(NULL), right(NULL) {} - * }; - */ -class Solution { - public: - TreeNode* buildTree(vector& preorder, vector& inorder) { - return buildTree(begin(preorder), end(preorder), begin(inorder), end(inorder)); - } - - private: - template - TreeNode* buildTree(InputIterator pre_first, InputIterator pre_last, InputIterator in_first, InputIterator in_last) { - if(pre_first == pre_last) - return NULL; - if(in_first == in_last) - return NULL; - - auto root = new TreeNode(*pre_first); - auto inRootPos = find(in_first, in_last, *pre_first); - auto leftSize = distance(in_first, inRootPos); - root->left = buildTree(next(pre_first), next(pre_first, leftSize + 1), in_first, inRootPos); - root->right = buildTree(next(pre_first, leftSize + 1), pre_last, next(inRootPos), in_last); - - return root; - } -}; diff --git a/C++/bulb-switcher-ii.cpp b/C++/bulb-switcher-ii.cpp new file mode 100644 index 000000000..fca08182a --- /dev/null +++ b/C++/bulb-switcher-ii.cpp @@ -0,0 +1,14 @@ +// Time: O(1) +// Space: O(1) + +class Solution { +public: + int flipLights(int n, int m) { + if (m == 0) return 1; + if (n == 1) return 2; + if (m == 1 && n == 2) return 3; + if (m == 1 || n == 2) return 4; + if (m == 2) return 7; + return 8; + } +}; diff --git a/C++/bulb-switcher.cpp b/C++/bulb-switcher.cpp new file mode 100644 index 000000000..f640a2f85 --- /dev/null +++ b/C++/bulb-switcher.cpp @@ -0,0 +1,10 @@ +// Time: O(1) +// Space: O(1) + +class Solution { +public: + int bulbSwitch(int n) { + // The number of full squares. + return static_cast(sqrt(n)); + } +}; diff --git a/C++/bulls-and-cows.cpp b/C++/bulls-and-cows.cpp new file mode 100644 index 000000000..373ec2c6d --- /dev/null +++ b/C++/bulls-and-cows.cpp @@ -0,0 +1,48 @@ +// Time: O(n) +// Space: O(10) = O(1) + +// One pass solution. +class Solution { +public: + string getHint(string secret, string guess) { + unordered_map s_lookup, g_lookup; + int A = 0, B = 0; + const int n = min(secret.length(), guess.length()); + for (int i = 0; i < n; ++i) { + const char s = secret[i]; + const char g = guess[i]; + if (s == g) { + ++A; + } else { + (s_lookup[g] > 0) ? --s_lookup[g], ++B : ++g_lookup[g]; + (g_lookup[s] > 0) ? --g_lookup[s], ++B : ++s_lookup[s]; + } + } + return to_string(A).append("A").append(to_string(B)).append("B"); + } +}; + +// Two pass solution. +class Solution2 { +public: + string getHint(string secret, string guess) { + unordered_map lookup; + int A = 0, B = 0; + for (const auto& s : secret) { + ++lookup[s]; + } + for (const auto& g : guess) { + if (lookup[g]) { + --lookup[g]; + ++B; + } + } + const int n = min(secret.length(), guess.length()); + for (int i = 0; i < n; ++i) { + if (secret[i] == guess[i]) { + ++A, --B; + } + } + return to_string(A).append("A").append(to_string(B)).append("B"); + } +}; diff --git a/C++/burst-balloons.cpp b/C++/burst-balloons.cpp new file mode 100644 index 000000000..91f5e5dde --- /dev/null +++ b/C++/burst-balloons.cpp @@ -0,0 +1,29 @@ +// Time: O(n^3) +// Space: O(n^2) + +class Solution { +public: + int maxCoins(vector& nums) { + vector coins; + coins.emplace_back(1); + for (const auto& n : nums) { + if (n > 0) { + coins.emplace_back(n); + } + } + coins.emplace_back(1); + + vector> max_coins(coins.size(), vector(coins.size())); + for (int k = 2; k < coins.size(); ++k) { + for (int left = 0; left < coins.size() - k; ++left) { + for (int i = left + 1, right = left + k; i < right; ++i) { + max_coins[left][right] = max(max_coins[left][right], + coins[left] * coins[i] * coins[right] + + max_coins[left][i] + max_coins[i][right]); + } + } + } + + return max_coins[0][coins.size() - 1]; + } +}; diff --git a/C++/bus-routes.cpp b/C++/bus-routes.cpp new file mode 100644 index 000000000..815c35f2f --- /dev/null +++ b/C++/bus-routes.cpp @@ -0,0 +1,42 @@ +// Time: O(|V| + |E|) +// Space: O(|V| + |E|) + +class Solution { +public: + int numBusesToDestination(vector>& routes, int S, int T) { + if (S == T) { + return 0; + } + unordered_map> to_route; + for (int i = 0; i < routes.size(); ++i) { + for (const auto& stop : routes[i]) { + to_route[stop].emplace(i); + } + } + + int result = 1; + vector q{S}; + unordered_set lookup{S}; + while (!q.empty()) { + vector next_q; + for (const auto& stop : q) { + for (const auto& i : to_route[stop]) { + for (const auto& next_stop : routes[i]) { + if (lookup.count(next_stop)) { + continue; + } + if (next_stop == T) { + return result; + } + next_q.emplace_back(next_stop); + to_route[next_stop].erase(i); + lookup.emplace(next_stop); + } + } + } + swap(q, next_q); + ++result; + } + return -1; + } +}; diff --git a/C++/can-i-win.cpp b/C++/can-i-win.cpp new file mode 100644 index 000000000..4906b6232 --- /dev/null +++ b/C++/can-i-win.cpp @@ -0,0 +1,35 @@ +// Time: O(n!) +// Space: O(n) + +class Solution { +public: + bool canIWin(int maxChoosableInteger, int desiredTotal) { + if ((1 + maxChoosableInteger) * (maxChoosableInteger / 2) < desiredTotal) { + return false; + } + unordered_map lookup; + return canIWinHelper(maxChoosableInteger, desiredTotal, 0, &lookup); + } + +private: + int canIWinHelper(int maxChoosableInteger, int desiredTotal, + int visited, unordered_map *lookup) { + + if (lookup->find(visited) != lookup->end()) { + return (*lookup)[visited]; + } + int mask = 1; + for (int i = 0; i < maxChoosableInteger; ++i) { + if (!(visited & mask)) { + if (i + 1 >= desiredTotal || + !canIWinHelper(maxChoosableInteger, desiredTotal - (i + 1), visited | mask, lookup)) { + (*lookup)[visited] = true; + return true; + } + } + mask <<= 1; + } + (*lookup)[visited] = false; + return false; + } +}; diff --git a/C++/can-place-flowers.cpp b/C++/can-place-flowers.cpp new file mode 100644 index 000000000..76c0dbc48 --- /dev/null +++ b/C++/can-place-flowers.cpp @@ -0,0 +1,20 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + bool canPlaceFlowers(vector& flowerbed, int n) { + for (int i = 0; i < flowerbed.size(); ++i) { + if (flowerbed[i] == 0 && (i == 0 || flowerbed[i - 1] == 0) && + (i == flowerbed.size() - 1 || flowerbed[i + 1] == 0)) { + flowerbed[i] = 1; + --n; + } + if (n <= 0) { + return true; + } + } + return false; + } +}; + diff --git a/C++/candy-crush.cpp b/C++/candy-crush.cpp new file mode 100644 index 000000000..702541a4b --- /dev/null +++ b/C++/candy-crush.cpp @@ -0,0 +1,48 @@ +// Time: O((R * C)^2) +// Space: O(1) + +class Solution { +public: + vector> candyCrush(vector>& board) { + const auto R = board.size(), C = board[0].size(); + bool changed = true; + + while (changed) { + changed = false; + + for (int r = 0; r < R; ++r) { + for (int c = 0; c + 2 < C; ++c) { + auto v = abs(board[r][c]); + if (v != 0 && v == abs(board[r][c + 1]) && v == abs(board[r][c + 2])) { + board[r][c] = board[r][c + 1] = board[r][c + 2] = -v; + changed = true; + } + } + } + + for (int r = 0; r + 2 < R; ++r) { + for (int c = 0; c < C; ++c) { + auto v = abs(board[r][c]); + if (v != 0 && v == abs(board[r + 1][c]) && v == abs(board[r + 2][c])) { + board[r][c] = board[r + 1][c] = board[r + 2][c] = -v; + changed = true; + } + } + } + + for (int c = 0; c < C; ++c) { + int empty_r = R - 1; + for (int r = R - 1; r >= 0; --r) { + if (board[r][c] > 0) { + board[empty_r--][c] = board[r][c]; + } + } + for (int r = empty_r; r >= 0; --r) { + board[r][c] = 0; + } + } + } + + return board; + } +}; diff --git a/C++/candy.cpp b/C++/candy.cpp index e1f297fc1..fb0cc4180 100644 --- a/C++/candy.cpp +++ b/C++/candy.cpp @@ -1,28 +1,20 @@ -// Time Complexity: O(n) -// Space Complexity: O(n) +// Time: O(n) +// Space: O(n) class Solution { - public: - int candy(vector &ratings) { - const int n = ratings.size(); - vector increment(n, 0); - - // left to right - for(int i = 1, inc = 0; i < n; ++i) { - if(ratings[i] > ratings[i - 1]) - increment[i] = max(++inc, increment[i]); - else - inc = 0; +public: + int candy(vector& ratings) { + vector candies(ratings.size(), 1); + for (int i = 1; i < ratings.size(); ++i) { + if (ratings[i] > ratings[i - 1]) { + candies[i] = candies[i - 1] + 1; } - - // right to left - for(int i = n - 2, inc = 0; i >= 0; --i) { - if(ratings[i] > ratings[i + 1]) - increment[i] = max(++inc, increment[i]); - else - inc = 0; + } + for (int i = ratings.size() - 2; i >= 0; --i) { + if (ratings[i] > ratings[i + 1] && candies[i] <= candies[i + 1]) { + candies[i] = candies[i + 1] + 1; } - - return accumulate(increment.begin(), increment.end(), n); } + return accumulate(candies.cbegin(), candies.cend(), 0); + } }; diff --git a/C++/car-fleet.cpp b/C++/car-fleet.cpp new file mode 100644 index 000000000..003eba1e7 --- /dev/null +++ b/C++/car-fleet.cpp @@ -0,0 +1,23 @@ +// Time: O(nlogn) +// Space: O(n) + +class Solution { +public: + int carFleet(int target, vector& position, vector& speed) { + vector> cars; + for (int i = 0; i < position.size(); ++i) { + cars.emplace_back(position[i], + static_cast(target - position[i]) / speed[i]); + } + sort(cars.begin(), cars.end()); + int result = 0; + double curr = 0; + for (int i = position.size() - 1; i >= 0 ; --i) { + if (cars[i].second > curr) { + curr = cars[i].second; + ++result; + } + } + return result; + } +}; diff --git a/C++/card-flipping-game.cpp b/C++/card-flipping-game.cpp new file mode 100644 index 000000000..17a29c47b --- /dev/null +++ b/C++/card-flipping-game.cpp @@ -0,0 +1,26 @@ +// Time: O(n) +// Space: O(n) + +class Solution { +public: + int flipgame(vector& fronts, vector& backs) { + unordered_set same; + for (int i = 0; i < fronts.size(); ++i) { + if (fronts[i] == backs[i]) { + same.emplace(fronts[i]); + } + } + int result = numeric_limits::max(); + for (const auto& n : fronts) { + if (!same.count(n)) { + result = min(result, n); + } + } + for (const auto& n : backs) { + if (!same.count(n)) { + result = min(result, n); + } + } + return result != numeric_limits::max() ? result : 0; + } +}; diff --git a/C++/chalkboard-xor-gam.cpp b/C++/chalkboard-xor-gam.cpp new file mode 100644 index 000000000..fab1e2406 --- /dev/null +++ b/C++/chalkboard-xor-gam.cpp @@ -0,0 +1,11 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + bool xorGame(vector& nums) { + return accumulate(nums.cbegin(), nums.cend(), + 0, std::bit_xor()) == 0 || + nums.size() % 2 == 0; + } +}; diff --git a/C++/champagne-tower.cpp b/C++/champagne-tower.cpp new file mode 100644 index 000000000..14cd8b371 --- /dev/null +++ b/C++/champagne-tower.cpp @@ -0,0 +1,17 @@ +// Time: O(n^2) = O(1), since n is at most 99 +// Space: O(n) = O(1) + +class Solution { +public: + double champagneTower(int poured, int query_row, int query_glass) { + vector result(1 + query_row); + result[0] = poured; + for (int i = 1; i <= query_row; ++i) { + for(int j = i; j >= 0; --j) { + result[j] = max((result[j] - 1), 0.0) / 2 + + max((result[j - 1] - 1), 0.0) / 2; + } + } + return min(result[query_glass], 1.0); + } +}; diff --git a/C++/cheapest-flights-within-k-stops.cpp b/C++/cheapest-flights-within-k-stops.cpp new file mode 100644 index 000000000..985bf3b87 --- /dev/null +++ b/C++/cheapest-flights-within-k-stops.cpp @@ -0,0 +1,42 @@ +// Time: O((|E| + |V|) * log|V|) = O(|E| * log|V|) +// Space: O(|E| + |V|) = O(|E|) + +class Solution { +public: + int findCheapestPrice(int n, vector>& flights, int src, int dst, int K) { + using P = pair; + unordered_map> adj; + for (const auto& flight : flights) { + int u, v, w; + tie(u, v, w) = make_tuple(flight[0], flight[1], flight[2]); + adj[u].emplace_back(v, w); + } + + unordered_map> best; + using T = tuple; + priority_queue, greater> min_heap; + min_heap.emplace(0, src, K + 1); + while (!min_heap.empty()) { + int result, u, k; + tie(result, u, k) = min_heap.top(); min_heap.pop(); + if (k < 0 || + (best.count(u) && best[u].count(k) && best[u][k] < result)) { + continue; + } + if (u == dst) { + return result; + } + for (const auto& kvp : adj[u]) { + int v, w; + tie(v, w) = kvp; + if (!best.count(v) || + !best[v].count(k - 1) || + result + w < best[v][k - 1]) { + best[v][k - 1] = result + w; + min_heap.emplace(result + w, v, k - 1); + } + } + } + return -1; + } +}; diff --git a/C++/cherry-pickup.cpp b/C++/cherry-pickup.cpp new file mode 100644 index 000000000..5c44956c3 --- /dev/null +++ b/C++/cherry-pickup.cpp @@ -0,0 +1,39 @@ +// Time: O(n^3) +// Space: O(n^2) + +class Solution { +public: + int cherryPickup(vector>& grid) { + // dp holds the max # of cherries two k-length paths can pickup. + // The two k-length paths arrive at (i, k - i) and (j, k - j), + // respectively. + const int n = grid.size(); + vector> dp(n, vector(n, -1)); + dp[0][0] = grid[0][0]; + + const int max_len = 2 * (n - 1); + for (int k = 1; k <= max_len; ++k) { + for (int i = min(k, n - 1); i >= max(0, k - n + 1); --i) { // 0 <= i < n, 0 <= k-i < n + for (int j = min(k , n - 1); j >= i; --j) { // i <= j < n, 0 <= k-j < n + if (grid[i][k - i] == -1 || + grid[j][k - j] == -1) { + dp[i][j] = -1; + continue; + } + int cnt = grid[i][k - i] + ((i == j) ? 0 : grid[j][k - j]); + int max_cnt = -1; + static const vector> directions{{0, 0}, {-1, 0}, {0, -1}, {-1, -1}}; + for (const auto& direction : directions) { + const auto ii = i + direction.first; + const auto jj = j + direction.second; + if (ii >= 0 && jj >= 0 && dp[ii][jj] >= 0) { + max_cnt = max(max_cnt, dp[ii][jj] + cnt); + } + } + dp[i][j] = max_cnt; + } + } + } + return max(dp[n - 1][n - 1], 0); + } +}; diff --git a/C++/circular-array-loop.cpp b/C++/circular-array-loop.cpp new file mode 100644 index 000000000..fe2d41662 --- /dev/null +++ b/C++/circular-array-loop.cpp @@ -0,0 +1,40 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + bool circularArrayLoop(vector& nums) { + for (int i = 0; i < nums.size(); ++i) { + if (nums[i] == 0) { + continue; + } + int slow = i, fast = i; + while (nums[next(nums, slow)] * nums[i] > 0 && + nums[next(nums, fast)] * nums[i] > 0 && + nums[next(nums, next(nums, fast))] * nums[i] > 0) { + + slow = next(nums, slow); + fast = next(nums, next(nums, fast)); + if (slow == fast) { + if (slow == next(nums, slow)) { + break; + } + return true; + } + } + slow = i; + int val = nums[i]; + while (nums[slow] * val > 0) { + int tmp = next(nums, slow); + nums[slow] = 0; + slow = tmp; + } + } + return false; + } + +private: + int next(const vector& nums, int i) { + return ((i + nums[i]) + nums.size()) % nums.size(); + } +}; diff --git a/C++/closest-binary-search-tree-value-ii.cpp b/C++/closest-binary-search-tree-value-ii.cpp new file mode 100644 index 000000000..6b0da1019 --- /dev/null +++ b/C++/closest-binary-search-tree-value-ii.cpp @@ -0,0 +1,70 @@ +// Time: O(h + k) +// Space: O(h) + +/** + * 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: + vector closestKValues(TreeNode* root, double target, int k) { + // The forward or backward iterator. + const auto backward = [](const vector& s) { return s.back()->left; }; + const auto forward = [](const vector& s) { return s.back()->right; }; + const auto closest = [&target](const TreeNode* a, const TreeNode* b) { + return abs(a->val - target) < abs(b->val - target); + }; + + // Build the stack to the closest node. + vector s; + while (root) { + s.emplace_back(root); + root = target < root->val ? root->left : root->right; + } + + // Get the stack to the next smaller node. + vector forward_stack(s.cbegin(), next(min_element(s.cbegin(), s.cend(), closest))); + vector backward_stack(forward_stack); + nextNode(backward_stack, backward, forward); + + // Get the closest k values by advancing the iterators of the stacks. + vector result; + for (int i = 0; i < k; ++i) { + if (!forward_stack.empty() && + (backward_stack.empty() || closest(forward_stack.back(), backward_stack.back()))) { + result.emplace_back(forward_stack.back()->val); + nextNode(forward_stack, forward, backward); + } else if (!backward_stack.empty() && + (forward_stack.empty() || !closest(forward_stack.back(), backward_stack.back()))) { + result.emplace_back(backward_stack.back()->val); + nextNode(backward_stack, backward, forward); + } + } + return result; + } + + // Helper to make a stack to the next node. + template + void nextNode(vector& s, const T& child1, const U& child2) { + if (!s.empty()) { + if (child2(s)) { + s.emplace_back(child2(s)); + while (child1(s)) { + s.emplace_back(child1(s)); + } + } else { + auto child = s.back(); + s.pop_back(); + while (!s.empty() && child == child2(s)) { + child = s.back(); + s.pop_back(); + } + } + } + } +}; diff --git a/C++/closest-binary-search-tree-value.cpp b/C++/closest-binary-search-tree-value.cpp new file mode 100644 index 000000000..0ab4ebb70 --- /dev/null +++ b/C++/closest-binary-search-tree-value.cpp @@ -0,0 +1,34 @@ +// Time: O(h) +// Space: O(1) + +/** + * 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: + int closestValue(TreeNode* root, double target) { + double gap = numeric_limits::max(); + int closest = numeric_limits::max(); + + while (root) { + if (abs(static_cast(root->val) - target) < gap) { + gap = abs(root->val - target); + closest = root->val; + } + if (target == root->val) { + break; + } else if (target < root->val) { + root = root->left; + } else { + root = root->right; + } + } + return closest; + } +}; diff --git a/C++/closest-leaf-in-a-binary-tree.cpp b/C++/closest-leaf-in-a-binary-tree.cpp new file mode 100644 index 000000000..8fecbb19c --- /dev/null +++ b/C++/closest-leaf-in-a-binary-tree.cpp @@ -0,0 +1,63 @@ +// Time: O(n) +// Space: O(n) + +/** + * 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: + int findClosestLeaf(TreeNode* root, int k) { + unordered_map> neighbors; + unordered_set leaves; + traverse(root, &neighbors, &leaves); + vector q{k}; + unordered_set lookup{k}; + while (!q.empty()) { + vector next_q; + for (const auto& u : q) { + if (leaves.count(u)) { + return u; + } + for (const auto& v : neighbors[u]) { + if (lookup.count(v)) { + continue; + } + lookup.emplace(v); + next_q.emplace_back(v); + } + } + swap(q, next_q); + } + return 0; + } + +private: + void traverse(TreeNode *node, + unordered_map> *neighbors, + unordered_set *leaves) { + + if (!node) { + return; + } + if (!node->left && !node->right) { + leaves->emplace(node->val); + return; + } + if (node->left) { + (*neighbors)[node->val].emplace_back(node->left->val); + (*neighbors)[node->left->val].emplace_back(node->val); + traverse(node->left, neighbors, leaves); + } + if (node->right) { + (*neighbors)[node->val].emplace_back(node->right->val); + (*neighbors)[node->right->val].emplace_back(node->val); + traverse(node->right, neighbors, leaves); + } + } +}; diff --git a/C++/coin-change-2.cpp b/C++/coin-change-2.cpp new file mode 100644 index 000000000..a35a5c633 --- /dev/null +++ b/C++/coin-change-2.cpp @@ -0,0 +1,16 @@ +// Time: O(n * m) +// Space: O(m) + +class Solution { +public: + int change(int amount, vector& coins) { + vector dp(amount + 1); + dp[0] = 1; + for (const auto& coin : coins) { + for (int i = coin; i <= amount; ++i) { + dp[i] += dp[i - coin]; + } + } + return dp[amount]; + } +}; diff --git a/C++/coin-change.cpp b/C++/coin-change.cpp new file mode 100644 index 000000000..ff87539f0 --- /dev/null +++ b/C++/coin-change.cpp @@ -0,0 +1,21 @@ +// Time: O(n * k), n is the number of coins, k is the amount of money +// Space: O(k) + +// DP solution. (164ms) +class Solution { +public: + int coinChange(vector& coins, int amount) { + vector amounts(amount + 1, numeric_limits::max()); + amounts[0] = 0; + for (int i = 0; i <= amount; ++i) { + if (amounts[i] != numeric_limits::max()) { + for (const auto& coin : coins) { + if (coin <= numeric_limits::max() - i && i + coin <= amount) { + amounts[i + coin] = min(amounts[i + coin], amounts[i] + 1); + } + } + } + } + return amounts[amount] == numeric_limits::max() ? -1 : amounts[amount]; + } +}; diff --git a/C++/coin-path.cpp b/C++/coin-path.cpp new file mode 100644 index 000000000..e0536c3e1 --- /dev/null +++ b/C++/coin-path.cpp @@ -0,0 +1,38 @@ +// Time: O(n * B) +// Space: O(n) + +class Solution { +public: + vector cheapestJump(vector& A, int B) { + vector result; + if (A.empty() || A.back() == -1) { + return result; + } + const int n = A.size(); + vector dp(n, numeric_limits::max()), next(n, -1); + dp[n - 1] = A[n - 1]; + for (int i = n - 2; i >= 0; --i) { + if (A[i] == -1) { + continue; + } + for (int j = i + 1; j <= min(i + B, n - 1); ++j) { + if (dp[j] == numeric_limits::max()) { + continue; + } + if (A[i] + dp[j] < dp[i]) { + dp[i] = A[i] + dp[j]; + next[i] = j; + } + } + } + if (dp[0] == numeric_limits::max()) { + return result; + } + int k = 0; + while (k != -1) { + result.emplace_back(k + 1); + k = next[k]; + } + return result; + } +}; diff --git a/C++/combination-sum-iii.cpp b/C++/combination-sum-iii.cpp new file mode 100644 index 000000000..b9009b338 --- /dev/null +++ b/C++/combination-sum-iii.cpp @@ -0,0 +1,27 @@ +// Time: O(k * C(n, k)) +// Space: O(k) + +class Solution { +public: + vector > combinationSum3(int k, int n) { + vector> res; + vector combination; + combinationSum3(res, combination, 1, k, n); + return res; + } +private: + void combinationSum3(vector > &res, vector &combination, int start, int k, int n) { + if (!k && !n) { + res.push_back(combination); + return; + } else if (k < 0) { + return; + } + + for (int i = start; i < 10 && n >= k * i + k * (k - 1) / 2; ++i) { + combination.push_back(i); + combinationSum3(res, combination, i + 1, k - 1, n - i); + combination.pop_back(); + } + } +}; diff --git a/C++/combination-sum-iv.cpp b/C++/combination-sum-iv.cpp new file mode 100644 index 000000000..e7cc8b806 --- /dev/null +++ b/C++/combination-sum-iv.cpp @@ -0,0 +1,19 @@ +// Time: O(nlogn + n * t), t is the value of target. +// Space: O(t) + +class Solution { +public: + int combinationSum4(vector& nums, int target) { + vector dp(target + 1, 0); + dp[0] = 1; + sort(nums.begin(), nums.end()); + + for (int i = 1; i <= target; ++i) { + for (int j = 0; j < nums.size() && nums[j] <= i; ++j) { + dp[i] += dp[i - nums[j]]; + } + } + + return dp[target]; + } +}; diff --git a/C++/compare-version-numbers.cpp b/C++/compare-version-numbers.cpp new file mode 100644 index 000000000..e15efbc72 --- /dev/null +++ b/C++/compare-version-numbers.cpp @@ -0,0 +1,22 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + int compareVersion(string version1, string version2) { + const int n1 = version1.length(), n2 = version2.length(); + for (int i = 0, j = 0; i < n1 || j < n2; ++i, ++j) { + int v1 = 0, v2 = 0; + while (i < n1 && version1[i] != '.') { + v1 = v1 * 10 + version1[i++] - '0'; + } + while (j < n2 && version2[j] != '.') { + v2 = v2 * 10 + version2[j++] - '0'; + } + if (v1 != v2) { + return v1 > v2 ? 1 : -1; + } + } + return 0; + } +}; diff --git a/C++/complex-number-multiplication.cpp b/C++/complex-number-multiplication.cpp new file mode 100644 index 000000000..d3127e59b --- /dev/null +++ b/C++/complex-number-multiplication.cpp @@ -0,0 +1,18 @@ +// Time: O(1) +// Space: O(1) + +class Solution { +public: + string complexNumberMultiply(string a, string b) { + int ra, ia, rb, ib; + char op; + stringstream ssa(a), ssb(b); + ssa >> ra >> op >> ia; + ssb >> rb >> op >> ib; + string result = to_string(ra * rb - ia * ib); + result += "+"; + result += to_string(ra * ib + rb * ia); + result += "i"; + return result; + } +}; diff --git a/C++/concatenated-words.cpp b/C++/concatenated-words.cpp new file mode 100644 index 000000000..ecb47e897 --- /dev/null +++ b/C++/concatenated-words.cpp @@ -0,0 +1,29 @@ +// Time: O(n * l^2) +// Space: O(n * l) + +class Solution { +public: + vector findAllConcatenatedWordsInADict(vector& words) { + unordered_set lookup(words.begin(), words.end()); + vector result; + for (const auto& word : words) { + vector dp(word.length() + 1); + dp[0] = true; + for (int i = 0; i < word.length(); ++i) { + if (!dp[i]) { + continue; + } + for (int j = i + 1; j <= word.length(); ++j) { + if (j - i < word.length() && lookup.count(word.substr(i, j - i))) { + dp[j] = true; + } + } + if (dp[word.length()]) { + result.emplace_back(word); + break; + } + } + } + return result; + } +}; diff --git a/C++/consecutive-numbers-sum.cpp b/C++/consecutive-numbers-sum.cpp new file mode 100644 index 000000000..800f8031a --- /dev/null +++ b/C++/consecutive-numbers-sum.cpp @@ -0,0 +1,24 @@ +// Time: O(sqrt(n)) +// Space: O(1) + +class Solution { +public: + int consecutiveNumbersSum(int N) { + int result = 1; + while (N % 2 == 0) { + N /= 2; + } + for (int i = 3; i * i <= N; i += 2) { + int count = 0; + while (N % i == 0) { + N /= i; + ++count; + } + result *= count + 1; + } + if (N > 1) { + result *= 2; + } + return result; + } +}; diff --git a/C++/construct-binary-tree-from-inorder-and-postorder-traversal.cpp b/C++/construct-binary-tree-from-inorder-and-postorder-traversal.cpp new file mode 100644 index 000000000..399af20c8 --- /dev/null +++ b/C++/construct-binary-tree-from-inorder-and-postorder-traversal.cpp @@ -0,0 +1,43 @@ +// Time: O(n) +// Space: O(n) + +/** + * 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* buildTree(vector& inorder, vector& postorder) { + unordered_map in_entry_idx_map; + for (size_t i = 0; i < inorder.size(); ++i) { + in_entry_idx_map.emplace(inorder[i], i); + } + return ReconstructPostInOrdersHelper(postorder, 0, postorder.size(), inorder, 0, inorder.size(), + in_entry_idx_map); + } + + TreeNode * ReconstructPostInOrdersHelper(const vector& postorder, size_t post_s, size_t post_e, + const vector& inorder, size_t in_s, size_t in_e, + const unordered_map& in_entry_idx_map) { + if (post_s == post_e || in_s == in_e) { + return nullptr; + } + + auto idx = in_entry_idx_map.at(postorder[post_e - 1]); + auto left_tree_size = idx - in_s; + + TreeNode *node = new TreeNode(postorder[post_e - 1]); + // Recursively builds the left subtree. + node->left =ReconstructPostInOrdersHelper(postorder, post_s, post_s + left_tree_size, + inorder, in_s, idx, in_entry_idx_map); + // Recursively builds the right subtree. + node->right = ReconstructPostInOrdersHelper(postorder, post_s + left_tree_size, post_e - 1, + inorder, idx + 1, in_e, in_entry_idx_map); + return node; + } +}; diff --git a/C++/construct-binary-tree-from-preorder-and-inorder-traversal.cpp b/C++/construct-binary-tree-from-preorder-and-inorder-traversal.cpp new file mode 100644 index 000000000..f6281ebbb --- /dev/null +++ b/C++/construct-binary-tree-from-preorder-and-inorder-traversal.cpp @@ -0,0 +1,43 @@ +// Time: O(n) +// Space: O(n) + +/** + * 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* buildTree(vector& preorder, vector& inorder) { + unordered_map in_entry_idx_map; + for (size_t i = 0; i < inorder.size(); ++i) { + in_entry_idx_map.emplace(inorder[i], i); + } + return ReconstructPreInOrdersHelper(preorder, 0, preorder.size(), inorder, 0, inorder.size(), + in_entry_idx_map); + } + + // Reconstructs the binary tree from pre[pre_s : pre_e - 1] and + // in[in_s : in_e - 1]. + TreeNode *ReconstructPreInOrdersHelper(const vector& preorder, size_t pre_s, size_t pre_e, + const vector& inorder, size_t in_s, size_t in_e, + const unordered_map& in_entry_idx_map) { + if (pre_s == pre_e || in_s == in_e) { + return nullptr; + } + + auto idx = in_entry_idx_map.at(preorder[pre_s]); + auto left_tree_size = idx - in_s; + + auto node = new TreeNode(preorder[pre_s]); + node->left = ReconstructPreInOrdersHelper(preorder, pre_s + 1, pre_s + 1 + left_tree_size, + inorder, in_s, idx, in_entry_idx_map); + node->right = ReconstructPreInOrdersHelper(preorder, pre_s + 1 + left_tree_size, pre_e, + inorder, idx + 1, in_e, in_entry_idx_map); + return node; + } +}; diff --git a/C++/construct-binary-tree-from-string.cpp b/C++/construct-binary-tree-from-string.cpp new file mode 100644 index 000000000..0f2d1ce36 --- /dev/null +++ b/C++/construct-binary-tree-from-string.cpp @@ -0,0 +1,43 @@ +// Time: O(n) +// Space: O(h) + +/** + * 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* str2tree(string s) { + int i = 0; + return s.empty() ? nullptr : str2treeHelper(s, &i); + } + +private: + TreeNode* str2treeHelper(const string& s, int *i) { + auto start = *i; + if (s[*i] == '-') { + ++(*i); + } + while (*i < s.length() && isdigit(s[*i])) { + ++(*i); + } + + auto node = new TreeNode(stoi(s.substr(start, *i - start))); + if (*i < s.length() && s[*i] == '(') { + ++(*i); + node->left = str2treeHelper(s, i); + ++(*i); + } + if (*i < s.length() && s[*i] == '(') { + ++(*i); + node->right = str2treeHelper(s, i); + ++(*i); + } + return node; + } +}; diff --git a/C++/construct-quad-tree.cpp b/C++/construct-quad-tree.cpp new file mode 100644 index 000000000..cfe3f6888 --- /dev/null +++ b/C++/construct-quad-tree.cpp @@ -0,0 +1,56 @@ +// Time: O(n) +// Space: O(h) + +/* +// Definition for a QuadTree node. +class Node { +public: + bool val; + bool isLeaf; + Node* topLeft; + Node* topRight; + Node* bottomLeft; + Node* bottomRight; + + Node() {} + + Node(bool _val, bool _isLeaf, Node* _topLeft, Node* _topRight, Node* _bottomLeft, Node* _bottomRight) { + val = _val; + isLeaf = _isLeaf; + topLeft = _topLeft; + topRight = _topRight; + bottomLeft = _bottomLeft; + bottomRight = _bottomRight; + } +}; +*/ +class Solution { +public: + Node* construct(vector>& grid) { + if (grid.empty()) { + return nullptr; + } + return dfs(grid, 0, 0, grid.size()); + } + +private: + Node* dfs(const vector>& grid, + int x, int y, int l) { + if (l == 1) { + return new Node(grid[x][y] == 1, true, nullptr, nullptr, nullptr, nullptr); + } + int half = l / 2; + auto topLeftNode = dfs(grid, x, y, half); + auto topRightNode = dfs(grid, x, y + half, half); + auto bottomLeftNode = dfs(grid, x + half, y, half); + auto bottomRightNode = dfs(grid, x + half, y + half, half); + if (topLeftNode->isLeaf && topRightNode->isLeaf && + bottomLeftNode->isLeaf && bottomRightNode->isLeaf && + topLeftNode->val == topRightNode->val && + topRightNode->val == bottomLeftNode->val && + bottomLeftNode->val == bottomRightNode->val) { + return new Node(topLeftNode->val, true, nullptr, nullptr, nullptr, nullptr); + } + return new Node(true, false, topLeftNode, topRightNode, bottomLeftNode, bottomRightNode); + } +}; diff --git a/C++/construct-string-from-binary-tree.cpp b/C++/construct-string-from-binary-tree.cpp new file mode 100644 index 000000000..36d5fe510 --- /dev/null +++ b/C++/construct-string-from-binary-tree.cpp @@ -0,0 +1,32 @@ +// Time: O(n) +// Space: O(h) + +/** + * 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: + string tree2str(TreeNode* t) { + if (!t) { + return ""; + } + + auto s = to_string(t->val); + + if (t->left || t->right) { + s += "(" + tree2str(t->left) + ")"; + } + + if (t->right) { + s += "(" + tree2str(t->right) + ")"; + } + + return s; + } +}; diff --git a/C++/construct-the-rectangle.cpp b/C++/construct-the-rectangle.cpp new file mode 100644 index 000000000..0940be3de --- /dev/null +++ b/C++/construct-the-rectangle.cpp @@ -0,0 +1,13 @@ +// Time: O(1) +// Space: O(1) + +class Solution { +public: + vector constructRectangle(int area) { + int w = sqrt(area); + while (area % w) { + --w; + } + return {area / w, w}; + } +}; diff --git a/C++/contain-virus.cpp b/C++/contain-virus.cpp new file mode 100644 index 000000000..57d2bc5e5 --- /dev/null +++ b/C++/contain-virus.cpp @@ -0,0 +1,101 @@ +// Time: O((m * n)^(4/3)), days = O((m * n)^(1/3)) +// Space: O(m * n) + +class Solution { +public: + int containVirus(vector>& grid) { + int result = 0; + while (true) { + P_SET lookup; + vector regions, frontiers; + vector perimeters; + for (int r = 0; r < grid.size(); ++r) { + for (int c = 0; c < grid[r].size(); ++c) { + const auto& p = make_pair(r, c); + if (grid[r][c] == 1 && lookup.count(p) == 0) { + regions.emplace_back(); + frontiers.emplace_back(); + perimeters.emplace_back(); + dfs(grid, p, &lookup, ®ions, &frontiers, &perimeters); + } + } + } + if (regions.empty()) { + break; + } + + int triage_idx = 0; + for (int i = 0; i < frontiers.size(); ++i) { + if (frontiers[i].size() > frontiers[triage_idx].size()) { + triage_idx = i; + } + } + for (int i = 0; i < regions.size(); ++i) { + if (i == triage_idx) { + result += perimeters[i]; + for (const auto& p : regions[i]) { + grid[p.first][p.second] = -1; + } + continue; + } + for (const auto& p : regions[i]) { + for (const auto& d : directions) { + int nr = p.first + d.first; + int nc = p.second + d.second; + if (nr < 0 || nr >= grid.size() || + nc < 0 || nc >= grid[nr].size()) { + continue; + } + if (grid[nr][nc] == 0) { + grid[nr][nc] = 1; + } + } + } + } + } + + return result; + } + +private: + template + struct PairHash { + size_t operator()(const pair& p) const { + size_t seed = 0; + seed ^= std::hash{}(p.first) + 0x9e3779b9 + (seed<<6) + (seed>>2); + seed ^= std::hash{}(p.second) + 0x9e3779b9 + (seed<<6) + (seed>>2); + return seed; + } + }; + using P = pair; + using P_SET = unordered_set>; + + void dfs(const vector>& grid, + const P& p, + P_SET *lookup, + vector *regions, + vector *frontiers, + vector *perimeters) { + + if (lookup->count(p)) { + return; + } + lookup->emplace(p); + regions->back().emplace(p); + for (const auto& d : directions) { + int nr = p.first + d.first; + int nc = p.second + d.second; + if (nr < 0 || nr >= grid.size() || + nc < 0 || nc >= grid[nr].size()) { + continue; + } + if (grid[nr][nc] == 1) { + dfs(grid, make_pair(nr, nc), lookup, regions, frontiers, perimeters); + } else if (grid[nr][nc] == 0) { + frontiers->back().emplace(nr, nc); + ++perimeters->back(); + } + } + } + const vector

directions = {{0, -1}, {0, 1}, {-1, 0}, {1, 0}}; +}; diff --git a/C++/container-with-most-water.cpp b/C++/container-with-most-water.cpp new file mode 100644 index 000000000..acaa98efe --- /dev/null +++ b/C++/container-with-most-water.cpp @@ -0,0 +1,20 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + int maxArea(vector& height) { + int i = 0, j = height.size() - 1, max_area = 0; + while (i < j) { + max_area = max(max_area, min(height[i], height[j]) * (j - i)); + if (height[i] > height[j]) { + --j; + } else if (height[i] < height[j]) { + ++i; + } else { // height[i] == height[j]. + ++i, --j; + } + } + return max_area; + } +}; diff --git a/C++/contains-duplicate-ii.cpp b/C++/contains-duplicate-ii.cpp new file mode 100644 index 000000000..b70ad1928 --- /dev/null +++ b/C++/contains-duplicate-ii.cpp @@ -0,0 +1,22 @@ +// Time: O(n) +// Space: O(n) + +class Solution { +public: + bool containsNearbyDuplicate(vector& nums, int k) { + unordered_map lookup; + for (int i = 0; i < nums.size(); ++i) { + if (lookup.find(nums[i]) == lookup.end()) { + lookup[nums[i]] = i; + } else { + // It the value occurs before, check the difference. + if (i - lookup[nums[i]] <= k) { + return true; + } + // Update the index of the value. + lookup[nums[i]] = i; + } + } + return false; + } +}; diff --git a/C++/contains-duplicate-iii.cpp b/C++/contains-duplicate-iii.cpp new file mode 100644 index 000000000..a8e790d61 --- /dev/null +++ b/C++/contains-duplicate-iii.cpp @@ -0,0 +1,32 @@ +// Time: O(nlogk) +// Space: O(k) + +class Solution { +public: + bool containsNearbyAlmostDuplicate(vector& nums, int k, int t) { + if (k < 0 || t < 0) { + return false; + } + + queue window; + multiset bst; + for (int i = 0; i < nums.size(); ++i) { + // Only keep at most k elements. + if (bst.size() > k) { + int num = window.front(); + window.pop(); + bst.erase(bst.find(num)); + } + // Every search costs time: O(logk). + const auto it = bst.lower_bound(nums[i] - t); + if (it == bst.cend() || (*it - nums[i]) > t) { + // Not found. + window.emplace(nums[i]); + bst.emplace(nums[i]); + } else { + return true; + } + } + return false; + } +}; diff --git a/C++/contains-duplicate.cpp b/C++/contains-duplicate.cpp new file mode 100644 index 000000000..524f4067c --- /dev/null +++ b/C++/contains-duplicate.cpp @@ -0,0 +1,20 @@ +// Time: O(n) +// Space: O(n) + +class Solution { +public: + bool containsDuplicate(vector& nums) { + unordered_set nums_set(nums.begin(), nums.end()); + return nums_set.size() != nums.size(); + } +}; + +// Time: O(nlogn) +// Space: O(1) +class Solution2 { +public: + bool containsDuplicate(vector& nums) { + sort(nums.begin(), nums.end()); + return unique(nums.begin(), nums.end()) != nums.end(); + } +}; diff --git a/C++/contiguous-array.cpp b/C++/contiguous-array.cpp new file mode 100644 index 000000000..cd067f052 --- /dev/null +++ b/C++/contiguous-array.cpp @@ -0,0 +1,20 @@ +// Time: O(n) +// Space: O(n) + +class Solution { +public: + int findMaxLength(vector& nums) { + int result = 0, count = 0; + unordered_map lookup; + lookup[0] = -1; + for (int i = 0; i < nums.size(); ++i) { + count += nums[i] == 1 ? 1 : -1; + if (lookup.count(count)) { + result = max(result, i - lookup[count]); + } else { + lookup[count] = i; + } + } + return result; + } +}; diff --git a/C++/continuous-subarray-sum.cpp b/C++/continuous-subarray-sum.cpp new file mode 100644 index 000000000..9b0155131 --- /dev/null +++ b/C++/continuous-subarray-sum.cpp @@ -0,0 +1,25 @@ +// Time: O(n) +// Space: O(k) + +class Solution { +public: + bool checkSubarraySum(vector& nums, int k) { + int count = 0; + unordered_map lookup; + lookup[0] = -1; + for (int i = 0; i < nums.size(); ++i) { + count += nums[i]; + if (k != 0) { + count %= k; + } + if (lookup.count(count)) { + if (i - lookup[count] > 1) { + return true; + } + } else { + lookup[count] = i; + } + } + return false; + } +}; diff --git a/C++/convert-a-number-to-hexadecimal.cpp b/C++/convert-a-number-to-hexadecimal.cpp new file mode 100644 index 000000000..8808b9328 --- /dev/null +++ b/C++/convert-a-number-to-hexadecimal.cpp @@ -0,0 +1,25 @@ +// Time: O(logn) +// Space: O(1) + +class Solution { +public: + string toHex(int num) { + if (!num) { + return "0"; + } + + string result; + while (num && result.length() != sizeof(int) * 2) { + int hex = num & 15; + if (hex < 10) { + result.push_back('0' + hex); + } else { + result.push_back('a' + hex - 10); + } + num >>= 4; + } + reverse(result.begin(), result.end()); + + return result; + } +}; diff --git a/C++/convert-binary-search-tree-to-sorted-doubly-linked-list.cpp b/C++/convert-binary-search-tree-to-sorted-doubly-linked-list.cpp new file mode 100644 index 000000000..e635b36ab --- /dev/null +++ b/C++/convert-binary-search-tree-to-sorted-doubly-linked-list.cpp @@ -0,0 +1,42 @@ +// Time: O(n) +// Space: O(h) + +/* +// Definition for a Node. +class Node { +public: + int val; + Node* left; + Node* right; + + Node() {} + + Node(int _val, Node* _left, Node* _right) { + val = _val; + left = _left; + right = _right; + } +}; +*/ +class Solution { +public: + Node* treeToDoublyList(Node* root) { + if (!root) { + return nullptr; + } + Node *left_head = root, *left_tail = root; + Node *right_head = root, *right_tail = root; + if (root->left) { + left_head = treeToDoublyList(root->left); + left_tail = left_head->left; + } + if (root->right) { + right_head = treeToDoublyList(root->right); + right_tail = right_head->left; + } + left_tail->right = root, right_head->left = root; + root->left = left_tail, root->right = right_head; + left_head->left = right_tail, right_tail->right = left_head; + return left_head; + } +}; diff --git a/C++/convert-bst-to-greater-tree.cpp b/C++/convert-bst-to-greater-tree.cpp new file mode 100644 index 000000000..c66042d2a --- /dev/null +++ b/C++/convert-bst-to-greater-tree.cpp @@ -0,0 +1,35 @@ +// Time: O(n) +// Space: O(h) + +/** + * 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* convertBST(TreeNode* root) { + int cur_sum = 0; + convertBSTHelper(root, &cur_sum); + return root; + } + +private: + void convertBSTHelper(TreeNode* root, int *cur_sum) { + if (!root) { + return; + } + + if (root->right) { + convertBSTHelper(root->right, cur_sum); + } + root->val = (*cur_sum += root->val); + if (root->left) { + convertBSTHelper(root->left, cur_sum); + } + } +}; diff --git a/C++/convert-sorted-array-to-binary-search-tree.cpp b/C++/convert-sorted-array-to-binary-search-tree.cpp new file mode 100644 index 000000000..35f3ad883 --- /dev/null +++ b/C++/convert-sorted-array-to-binary-search-tree.cpp @@ -0,0 +1,29 @@ +// Time: O(n) +// Space: O(logn) + +/** + * 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* sortedArrayToBST(vector& nums) { + return sortedArrayToBSTHelper(nums, 0, nums.size() - 1); + } + +private: + TreeNode *sortedArrayToBSTHelper(vector &nums, int start, int end) { + if (start <= end) { + TreeNode *node = new TreeNode(nums[start + (end - start) / 2]); + node->left = sortedArrayToBSTHelper(nums, start, start + (end - start) / 2 - 1); + node->right = sortedArrayToBSTHelper(nums, start + (end - start) / 2 + 1, end); + return node; + } + return nullptr; + } +}; diff --git a/C++/convert-sorted-list-to-binary-search-tree.cpp b/C++/convert-sorted-list-to-binary-search-tree.cpp new file mode 100644 index 000000000..d173c96c6 --- /dev/null +++ b/C++/convert-sorted-list-to-binary-search-tree.cpp @@ -0,0 +1,47 @@ +// Time: O(n) +// Space: O(logn) + +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode(int x) : val(x), next(NULL) {} + * }; + */ +/** + * 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* sortedListToBST(ListNode* head) { + auto curr = head; + int n = 0; + while (curr) { + curr = curr->next; + ++n; + } + return BuildBSTFromSortedDoublyListHelper(&head, 0, n); + } + + TreeNode * BuildBSTFromSortedDoublyListHelper(ListNode **head, int s, int e) { + if (s == e) { + return nullptr; + } + + int m = s + ((e - s) / 2); + auto left = BuildBSTFromSortedDoublyListHelper(head, s, m); + auto curr = new TreeNode((*head)->val); + + *head = (*head)->next; + curr->left = left; + curr->right = BuildBSTFromSortedDoublyListHelper(head, m + 1, e); + return curr; + } +}; diff --git a/C++/convex-polygon.cpp b/C++/convex-polygon.cpp new file mode 100644 index 000000000..3582c3fdb --- /dev/null +++ b/C++/convex-polygon.cpp @@ -0,0 +1,25 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + bool isConvex(vector>& points) { + const auto det = [](const vector>& A) { + return A[0][0]*A[1][1] - A[0][1]*A[1][0]; + }; + long n = points.size(), prev = 0, curr; + for (int i = 0; i < n; ++i) { + vector> A; + for (int j = 1; j < 3; ++j) { + A.push_back({points[(i + j) % n][0] - points[i][0], points[(i + j) % n][1] - points[i][1]}); + } + if (curr = det(A)) { + if (curr * prev < 0) { + return false; + } + prev = curr; + } + } + return true; + } +}; diff --git a/C++/copy-list-with-random-pointer.cpp b/C++/copy-list-with-random-pointer.cpp new file mode 100644 index 000000000..e894b1c75 --- /dev/null +++ b/C++/copy-list-with-random-pointer.cpp @@ -0,0 +1,40 @@ +// Time: O(n) +// Space: O(1) + +/** + * Definition for singly-linked list with a random pointer. + * struct RandomListNode { + * int label; + * RandomListNode *next, *random; + * RandomListNode(int x) : label(x), next(NULL), random(NULL) {} + * }; + */ +class Solution { +public: + RandomListNode *copyRandomList(RandomListNode *head) { + // Insert the copied node after the original one. + for (auto *curr = head; curr; curr = curr->next->next) { + auto *node = new RandomListNode(curr->label); + node->next = curr->next; + curr->next = node; + } + + // Update random node. + for (auto *curr = head; curr; curr = curr->next->next) { + if (curr->random) { + curr->next->random = curr->random->next; + } + } + + // Seperate the copied nodes from original ones. + RandomListNode dummy(0); + for (auto *curr = head, *copy_curr = &dummy; + curr; + copy_curr = copy_curr->next, curr = curr->next) { + copy_curr->next = curr->next; + curr->next = curr->next->next; + } + + return dummy.next; + } +}; diff --git a/C++/copyRandomList.cpp b/C++/copyRandomList.cpp deleted file mode 100644 index ae69f7482..000000000 --- a/C++/copyRandomList.cpp +++ /dev/null @@ -1,40 +0,0 @@ -// Time Complexity: O(n) -// Space Complexity: O(1) - -/** - * Definition for singly-linked list with a random pointer. - * struct RandomListNode { - * int label; - * RandomListNode *next, *random; - * RandomListNode(int x) : label(x), next(NULL), random(NULL) {} - * }; - */ -class Solution { - public: - RandomListNode *copyRandomList(RandomListNode *head) { - // insert the copied node after the original one - for(RandomListNode *cur = head; cur; cur = cur->next->next) { - RandomListNode *node = new RandomListNode(cur->label); - node->next = cur->next; - cur->next = node; - } - - // update random node - for(RandomListNode *cur = head; cur; cur = cur->next->next) { - if(cur->random) { - cur->next->random = cur->random->next; - } - } - - // seperate the copied nodes from original ones - RandomListNode dummy(INT_MIN); - for( RandomListNode *cur = head, *copy_cur = &dummy; - cur; - copy_cur = copy_cur->next, cur = cur->next) { - copy_cur->next = cur->next; - cur->next = cur->next->next; - } - - return dummy.next; - } -}; diff --git a/C++/coundAndSay.cpp b/C++/coundAndSay.cpp deleted file mode 100644 index d1285589d..000000000 --- a/C++/coundAndSay.cpp +++ /dev/null @@ -1,24 +0,0 @@ -// Time Complexity: O(n^2) -// Space Complexity: O(n) - -class Solution { - public: - string countAndSay(int n) { - string s{"1"}; - while(--n) { - s = getNext(s); - } - return s; - } - - private: - string getNext(const string &s) { - stringstream ss; - for(auto i = s.begin(); i != s.end();) { - auto j = find_if(i, s.end(), bind1st(not_equal_to(), *i)); - ss << distance(i, j) << *i; - i = j; - } - return ss.str(); - } -}; diff --git a/C++/count-and-say.cpp b/C++/count-and-say.cpp new file mode 100644 index 000000000..491b0f2d9 --- /dev/null +++ b/C++/count-and-say.cpp @@ -0,0 +1,25 @@ +// Time: O(n * 2^n) +// Space: O(2^n) + +class Solution { +public: + string countAndSay(int n) { + string seq{"1"}; + for (int i = 0; i < n - 1; ++i) { + seq = getNext(seq); + } + return seq; + } + +private: + string getNext(const string& seq) { + string next_seq; + for(auto i = seq.cbegin(); i != seq.cend();) { + auto j = find_if(i, seq.cend(), bind1st(not_equal_to(), *i)); + next_seq.append(to_string(distance(i, j))); + next_seq.push_back(*i); + i = j; + } + return next_seq; + } +}; diff --git a/C++/count-binary-substrings.cpp b/C++/count-binary-substrings.cpp new file mode 100644 index 000000000..defb0a3a3 --- /dev/null +++ b/C++/count-binary-substrings.cpp @@ -0,0 +1,19 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + int countBinarySubstrings(string s) { + auto result = 0, prev = 0, curr = 1; + for (int i = 1; i < s.length(); ++i) { + if (s[i - 1] != s[i]) { + result += min(prev, curr); + prev = curr, curr = 1; + } else { + ++curr; + } + } + result += min(prev, curr); + return result; + } +}; diff --git a/C++/count-complete-tree-nodes.cpp b/C++/count-complete-tree-nodes.cpp new file mode 100644 index 000000000..ec755d366 --- /dev/null +++ b/C++/count-complete-tree-nodes.cpp @@ -0,0 +1,59 @@ +// Time: O(h * logn) = O((logn)^2) +// Space: O(1) + +/** + * 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: + int countNodes(TreeNode* root) { + if (root == nullptr) { + return 0; + } + + TreeNode *node = root; + int level = 0; + while (node->left != nullptr) { + node = node->left; + ++level; + } + + // Binary search. + int left = pow(2, level), right = pow(2, level + 1); + while (left < right) { + int mid = left + (right - left) / 2; + if (!exist(root, mid)) { + right = mid; + } else { + left = mid + 1; + } + } + return left - 1; + } + + // Check if the nth node exist. + bool exist(TreeNode *root, int n) { + int k = 1; + while (k <= n) { + k <<= 1; + } + k >>= 2; + + TreeNode *node = root; + while (k > 0) { + if ((n & k) == 0) { + node = node->left; + } else { + node = node->right; + } + k >>= 1; + } + return node != nullptr; + } +}; diff --git a/C++/count-different-palindromic-subsequences.cpp b/C++/count-different-palindromic-subsequences.cpp new file mode 100644 index 000000000..7301aaa3f --- /dev/null +++ b/C++/count-different-palindromic-subsequences.cpp @@ -0,0 +1,89 @@ +// Time: O(n^2) +// Space: O(n) +class Solution { +public: + int countPalindromicSubsequences(string S) { + static const int P = 1e9 + 7; + static const string chars = "abcd"; + vector>> dp(3, vector>(S.size(), vector(4))); + for (int len = 1; len <= S.size(); ++len) { + for (int i = 0; i + len <= S.size(); ++i) { + for (const auto& c : chars) { + dp[len % 3][i][c - 'a'] = 0; + if (len == 1) { + dp[len % 3][i][c - 'a'] = S[i] == c; + } else { + if (S[i] != c) { + dp[len % 3][i][c - 'a'] = dp[(len - 1) % 3][i + 1][c - 'a']; + } else if (S[i + len - 1] != c) { + dp[len % 3][i][c - 'a'] = dp[(len - 1) % 3][i][c - 'a']; + } else { + dp[len % 3][i][c - 'a'] = 2; + if (len > 2) { + for (const auto& cc : chars) { + dp[len % 3][i][c - 'a'] += dp[(len - 2) % 3][i + 1][cc - 'a']; + dp[len % 3][i][c - 'a'] %= P; + } + } + } + } + } + } + } + int result = 0; + for (const auto& c : chars) { + result = (result + dp[S.size() % 3][0][c - 'a']) % P; + } + return result; + } +}; + +// Time: O(n^2) +// Space: O(n^2) +class Solution2 { +public: + int countPalindromicSubsequences(string S) { + vector> prv(S.length(), vector(4, -1)); + vector> nxt(S.length(), vector(4, -1)); + vector last(4, -1); + for (int i = 0; i < S.length(); ++i) { + last[S[i] - 'a'] = i; + prv[i] = last; + } + last = vector(4, -1); + for (int i = S.length() - 1; i >= 0; --i) { + last[S[i] - 'a'] = i; + nxt[i] = last; + } + vector> lookup(S.length(), vector(S.length(), -1)); + return dp(0, S.length() - 1, prv, nxt, &lookup) - 1; + } + +private: + int dp(const int i, const int j, + const vector>& prv, + const vector>& nxt, + vector> *lookup) { + + if ((*lookup)[i][j] != -1) { + return (*lookup)[i][j]; + } + auto result = 1; + if (i <= j) { + for (int x = 0; x < 4; ++x) { + auto i0 = nxt[i][x]; + auto j0 = prv[j][x]; + if (i <= i0 && i0 <= j) { + result = (result + 1) % P; + } + if (i0 != -1 && j0 != -1 && i0 < j0) { + result = (result + dp(i0 + 1, j0 - 1, prv, nxt, lookup)) % P; + } + } + } + result %= P; + (*lookup)[i][j] = result; + return result; + } + static const int P = 1e9 + 7; +}; diff --git a/C++/count-numbers-with-unique-digits.cpp b/C++/count-numbers-with-unique-digits.cpp new file mode 100644 index 000000000..6b9b1e216 --- /dev/null +++ b/C++/count-numbers-with-unique-digits.cpp @@ -0,0 +1,18 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + int countNumbersWithUniqueDigits(int n) { + if (n == 0) { + return 1; + } + int count = 10; // f(1) = 10 + for (int k = 2, fk = 9; k <= n; ++k) { + // f(k) = f(k - 1) * (10 - (k - 1)) + fk *= 10 - (k - 1); + count += fk; + } + return count; // sum(f(k), k=1~n) + } +}; diff --git a/C++/count-of-range-sum.cpp b/C++/count-of-range-sum.cpp new file mode 100644 index 000000000..15e48abef --- /dev/null +++ b/C++/count-of-range-sum.cpp @@ -0,0 +1,86 @@ +// Time: O(nlogn) +// Space: O(n) + +// Divide and Conquer solution. +class Solution { +public: + int countRangeSum(vector& nums, int lower, int upper) { + vector sums(nums.size() + 1); + for (int i = 0; i < nums.size(); ++i) { + sums[i + 1] = sums[i] + nums[i]; + } + return countAndMergeSort(&sums, 0, sums.size(), lower, upper); + } + + int countAndMergeSort(vector *sums, int start, int end, int lower, int upper) { + if (end - start <= 1) { // The number of range [start, end) of which size is less than 2 is always 0. + return 0; + } + int mid = start + (end - start) / 2; + int count = countAndMergeSort(sums, start, mid, lower, upper) + + countAndMergeSort(sums, mid, end, lower, upper); + int j = mid, k = mid, r = mid; + vector tmp; + for (int i = start; i < mid; ++i) { + // Count the number of range sums that lie in [lower, upper]. + while (k < end && (*sums)[k] - (*sums)[i] < lower) { + ++k; + } + while (j < end && (*sums)[j] - (*sums)[i] <= upper) { + ++j; + } + count += j - k; + + // Merge the two sorted arrays into tmp. + while (r < end && (*sums)[r] < (*sums)[i]) { + tmp.emplace_back((*sums)[r++]); + } + tmp.emplace_back((*sums)[i]); + } + // Copy tmp back to sums. + copy(tmp.begin(), tmp.end(), sums->begin() + start); + return count; + } +}; + +// Divide and Conquer solution. +class Solution2 { +public: + int countRangeSum(vector& nums, int lower, int upper) { + vector sums(nums.size() + 1); + for (int i = 0; i < nums.size(); ++i) { + sums[i + 1] = sums[i] + nums[i]; + } + return countAndMergeSort(&sums, 0, sums.size() - 1, lower, upper); + } + + int countAndMergeSort(vector *sums, int start, int end, int lower, int upper) { + if (end - start <= 0) { // The number of range [start, end] of which size is less than 2 is always 0. + return 0; + } + int mid = start + (end - start) / 2; + int count = countAndMergeSort(sums, start, mid, lower, upper) + + countAndMergeSort(sums, mid + 1, end, lower, upper); + int j = mid + 1, k = mid + 1, r = mid + 1; + vector tmp; + for (int i = start; i <= mid; ++i) { + // Count the number of range sums that lie in [lower, upper]. + while (k <= end && (*sums)[k] - (*sums)[i] < lower) { + ++k; + } + while (j <= end && (*sums)[j] - (*sums)[i] <= upper) { + ++j; + } + count += j - k; + + // Merge the two sorted arrays into tmp. + while (r <= end && (*sums)[r] < (*sums)[i]) { + tmp.emplace_back((*sums)[r++]); + } + tmp.emplace_back((*sums)[i]); + } + // Copy tmp back to sums. + copy(tmp.begin(), tmp.end(), sums->begin() + start); + return count; + } +}; diff --git a/C++/count-of-smaller-numbers-after-self.cpp b/C++/count-of-smaller-numbers-after-self.cpp new file mode 100644 index 000000000..dcfe10ed2 --- /dev/null +++ b/C++/count-of-smaller-numbers-after-self.cpp @@ -0,0 +1,162 @@ +// Time: O(nlogn) +// Space: O(n) + +// BST solution. (40ms) +class Solution { +public: + class BSTreeNode { + public: + int val, count; + BSTreeNode *left, *right; + BSTreeNode(int val, int count) { + this->val = val; + this->count = count; + this->left = this->right = nullptr; + } + }; + + vector countSmaller(vector& nums) { + vector res(nums.size()); + + BSTreeNode *root = nullptr; + + // Insert into BST and get left count. + for (int i = nums.size() - 1; i >= 0; --i) { + BSTreeNode *node = new BSTreeNode(nums[i], 0); + root = insertNode(root, node); + res[i] = query(root, nums[i]); + } + + return res; + } + + // Insert node into BST. + BSTreeNode* insertNode(BSTreeNode* root, BSTreeNode* node) { + if (root == nullptr) { + return node; + } + BSTreeNode* curr = root; + while (curr) { + // Insert left if smaller. + if (node->val < curr->val) { + ++curr->count; // Increase the number of left children. + if (curr->left != nullptr) { + curr = curr->left; + } else { + curr->left = node; + break; + } + } else { // Insert right if larger or equal. + if (curr->right != nullptr) { + curr = curr->right; + } else { + curr->right = node; + break; + } + } + } + return root; + } + + // Query the smaller count of the value. + int query(BSTreeNode* root, int val) { + if (root == nullptr) { + return 0; + } + int count = 0; + BSTreeNode* curr = root; + while (curr) { + // Insert left. + if (val < curr->val) { + curr = curr->left; + } else if (val > curr->val) { + count += 1 + curr->count; // Count the number of the smaller nodes. + curr = curr->right; + } else { // Equal. + return count + curr->count; + } + } + return 0; + } +}; + +// Time: O(nlogn) +// Space: O(n) +// BIT solution. (56ms) +class Solution2 { +public: + vector countSmaller(vector& nums) { + // Get the place (position in the ascending order) of each number. + vector sorted_nums(nums), places(nums.size()); + sort(sorted_nums.begin(), sorted_nums.end()); + for (int i = 0; i < nums.size(); ++i) { + places[i] = + lower_bound(sorted_nums.begin(), sorted_nums.end(), nums[i]) - + sorted_nums.begin(); + } + // Count the smaller elements after the number. + vector bit(nums.size() + 1), ans(nums.size()); + for (int i = nums.size() - 1; i >= 0; --i) { + ans[i] = query(bit, places[i]); + add(bit, places[i] + 1, 1); + } + return ans; + } + +private: + void add(vector& bit, int i, int val) { + for (; i < bit.size(); i += lower_bit(i)) { + bit[i] += val; + } + } + + int query(const vector& bit, int i) { + int sum = 0; + for (; i > 0; i -= lower_bit(i)) { + sum += bit[i]; + } + return sum; + } + + int lower_bit(int i) { + return i & -i; + } +}; + +// Time: O(nlogn) +// Space: O(n) +// Divide and Conquer solution. (80ms) +class Solution3 { +public: + vector countSmaller(vector& nums) { + vector counts(nums.size()); + vector> num_idxs; + for (int i = 0; i < nums.size(); ++i) { + num_idxs.emplace_back(nums[i], i); + } + countAndMergeSort(&num_idxs, 0, num_idxs.size() - 1, &counts); + return counts; + } + + void countAndMergeSort(vector> *num_idxs, int start, int end, vector *counts) { + if (end - start <= 0) { // The number of range [start, end] of which size is less than 2 doesn't need sort. + return; + } + int mid = start + (end - start) / 2; + countAndMergeSort(num_idxs, start, mid, counts); + countAndMergeSort(num_idxs, mid + 1, end, counts); + + int r = mid + 1; + vector> tmp; + for (int i = start; i <= mid; ++i) { + // Merge the two sorted arrays into tmp. + while (r <= end && (*num_idxs)[r].first < (*num_idxs)[i].first) { + tmp.emplace_back((*num_idxs)[r++]); + } + tmp.emplace_back((*num_idxs)[i]); + (*counts)[(*num_idxs)[i].second] += r - (mid + 1); + } + // Copy tmp back to num_idxs. + copy(tmp.begin(), tmp.end(), num_idxs->begin() + start); + } +}; diff --git a/C++/count-primes.cpp b/C++/count-primes.cpp new file mode 100644 index 000000000..0a6378427 --- /dev/null +++ b/C++/count-primes.cpp @@ -0,0 +1,31 @@ +// Time: O(n) +// Space: O(n) + +class Solution { +public: + int countPrimes(int n) { + if (n <= 2) { + return 0; + } + + auto num = n / 2; + vector is_prime(n, true); + + for (int i = 3; i * i < n; i += 2) { + if (!is_prime[i]) { + continue; + } + + for (int j = i * i; j < n; j += 2 * i) { + if (!is_prime[j]) { + continue; + } + + --num; + is_prime[j] = false; + } + } + + return num; + } +}; diff --git a/C++/count-the-repetitions.cpp b/C++/count-the-repetitions.cpp new file mode 100644 index 000000000..feb30272f --- /dev/null +++ b/C++/count-the-repetitions.cpp @@ -0,0 +1,30 @@ +// Time: O(s1 * min(s2, n1)) +// Space: O(s2) + +class Solution { +public: + int getMaxRepetitions(string s1, int n1, string s2, int n2) { + vector repeatCount(s2.size() + 1, 0); + unordered_map lookup; + int j = 0, count = 0; + for (int k = 1; k <= n1; ++k) { + for (int i = 0; i < s1.size(); ++i) { + if (s1[i] == s2[j]) { + j = (j + 1) % s2.size(); + count += (j == 0); + } + } + + if (lookup.find(j) != lookup.end()) { // cyclic + int i = lookup[j]; + int prefixCount = repeatCount[i]; + int patternCount = (count - repeatCount[i]) * ((n1 - i) / (k - i)); + int suffixCount = repeatCount[i + (n1 - i) % (k - i)] - repeatCount[i]; + return (prefixCount + patternCount + suffixCount) / n2; + } + lookup[j] = k; + repeatCount[k] = count; + } + return repeatCount[n1] / n2; // not cyclic iff n1 <= s2 + } +}; diff --git a/C++/count-univalue-subtrees.cpp b/C++/count-univalue-subtrees.cpp new file mode 100644 index 000000000..885d96cd2 --- /dev/null +++ b/C++/count-univalue-subtrees.cpp @@ -0,0 +1,38 @@ +// Time: O(n) +// Space: O(h) + +/** + * 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: + int countUnivalSubtrees(TreeNode* root) { + int count = 0; + isUnivalSubtrees(root, &count); + return count; + } + + bool isUnivalSubtrees(TreeNode* root, int *count) { + if (root == nullptr) { + return true; + } + bool left = isUnivalSubtrees(root->left, count); + bool right = isUnivalSubtrees(root->right, count); + if (isSame(root, root->left, left) && + isSame(root, root->right, right)) { + ++(*count); + return true; + } + return false; + } + + bool isSame(TreeNode* root, TreeNode* child, bool is_uni) { + return child == nullptr || (is_uni && root->val == child->val); + } +}; diff --git a/C++/counting-bits.cpp b/C++/counting-bits.cpp new file mode 100644 index 000000000..bdc3f7c71 --- /dev/null +++ b/C++/counting-bits.cpp @@ -0,0 +1,31 @@ +// Time: O(n) +// Space: O(n) + +class Solution { +public: + vector countBits(int num) { + vector res{0}; + for (int i = 1; i <= num; ++i) { + res.emplace_back((i & 1) + res[i >> 1]); + } + return res; + } +}; + +// Time: O(n) +// Space: O(n) +class Solution2 { +public: + vector countBits(int num) { + vector res{0}; + for (int i = 0, cnt = res.size(); + res.size() <= num; + i = (i + 1) % cnt) { + if (i == 0) { + cnt = res.size(); + } + res.emplace_back(res[i] + 1); + } + return res; + } +}; diff --git a/C++/couples-holding-hands.cpp b/C++/couples-holding-hands.cpp new file mode 100644 index 000000000..e0a353055 --- /dev/null +++ b/C++/couples-holding-hands.cpp @@ -0,0 +1,34 @@ +// Time: O(n) +// Space: O(n) + +class Solution { +public: + int minSwapsCouples(vector& row) { + int N = row.size() / 2; + vector> couples(N); + for (int seat = 0; seat < row.size(); ++seat) { + couples[row[seat] / 2].emplace_back(seat / 2); + } + vector> adj(N); + for (const auto& couple : couples) { + adj[couple[0]].emplace_back(couple[1]); + adj[couple[1]].emplace_back(couple[0]); + } + + int result = 0; + for (int couch = 0; couch < N; ++couch) { + if (adj[couch].empty()) { + continue; + } + int couch1 = couch; + int couch2 = adj[couch1].back(); adj[couch1].pop_back(); + while (couch2 != couch) { + ++result; + adj[couch2].erase(find(adj[couch2].begin(), adj[couch2].end(), couch1)); + couch1 = couch2; + couch2 = adj[couch1].back(); adj[couch1].pop_back(); + } + } + return result; // also equals to N - (# of cycles) + } +}; diff --git a/C++/course-schedule-ii.cpp b/C++/course-schedule-ii.cpp new file mode 100644 index 000000000..198122315 --- /dev/null +++ b/C++/course-schedule-ii.cpp @@ -0,0 +1,52 @@ +// Time: O(|V| + |E||) +// Space: O(|E|) + +// Topological sort solution. +class Solution { +public: + vector findOrder(int numCourses, vector>& prerequisites) { + vector res; + // Store courses with in-degree zero. + queue zeroInDegree; + + // in-degree, out-degree + unordered_map> inDegree; + unordered_map> outDegree; + for (int i = 0; i < prerequisites.size(); ++i) { + inDegree[prerequisites[i].first].insert(prerequisites[i].second); + outDegree[prerequisites[i].second].insert(prerequisites[i].first); + } + + // Put all the courses with in-degree zero into queue. + for(int i = 0; i < numCourses; ++i) { + if(inDegree.find(i) == inDegree.end()) { + zeroInDegree.push(i); + } + } + + // V+E + while(!zeroInDegree.empty()) { + // Take the course which prerequisites are all taken. + int prerequisite = zeroInDegree.front(); + res.emplace_back(prerequisite); + zeroInDegree.pop(); + for (const auto & course: outDegree[prerequisite]) { + // Update info of all the courses with the taken prerequisite. + inDegree[course].erase(prerequisite); + // If all the prerequisites are taken, add the course to the queue. + if (inDegree[course].empty()) { + zeroInDegree.push(course); + } + } + // Mark the course as taken. + outDegree.erase(prerequisite); + } + + // All of the courses have been taken. + if (!outDegree.empty()) { + return {}; + } + + return res; + } +}; diff --git a/C++/course-schedule-iii.cpp b/C++/course-schedule-iii.cpp new file mode 100644 index 000000000..9a996fa62 --- /dev/null +++ b/C++/course-schedule-iii.cpp @@ -0,0 +1,23 @@ +// Time: O(nlogn) +// Space: O(k), k is the number of courses you can take + +class Solution { +public: + int scheduleCourse(vector>& courses) { + sort(courses.begin(), courses.end(), + [](const vector& a, const vector& b) { + return a[1] < b[1]; + }); + priority_queue max_heap; + int now = 0; + for (const auto& course : courses) { + max_heap.emplace(course[0]); + now += course[0]; + if (now > course[1]) { + now -= max_heap.top(), max_heap.pop(); + } + } + return heap.size(); + } +}; + diff --git a/C++/course-schedule.cpp b/C++/course-schedule.cpp new file mode 100644 index 000000000..2cf78f602 --- /dev/null +++ b/C++/course-schedule.cpp @@ -0,0 +1,50 @@ +// Time: O(|V| + |E|) +// Space: O(|E|) + +// Topological sort solution. +class Solution { +public: + bool canFinish(int numCourses, vector>& prerequisites) { + // Store courses with in-degree zero. + queue zeroInDegree; + + // in-degree, out-degree + unordered_map> inDegree; + unordered_map> outDegree; + for (int i = 0; i < prerequisites.size(); ++i) { + inDegree[prerequisites[i][0]].insert(prerequisites[i][1]); + outDegree[prerequisites[i][1]].insert(prerequisites[i][0]); + } + + // Put all the courses with in-degree zero into queue. + for(int i = 0; i < numCourses; ++i) { + if(inDegree.find(i) == inDegree.end()) { + zeroInDegree.push(i); + } + } + + // V+E + while(!zeroInDegree.empty()) { + // Take the course which prerequisites are all taken. + int prerequisite = zeroInDegree.front(); + zeroInDegree.pop(); + for (const auto & course: outDegree[prerequisite]) { + // Update info of all the courses with the taken prerequisite. + inDegree[course].erase(prerequisite); + // If all the prerequisites are taken, add the course to the queue. + if (inDegree[course].empty()) { + zeroInDegree.push(course); + } + } + // Mark the course as taken. + outDegree.erase(prerequisite); + } + + // All of the courses have been taken. + if (!outDegree.empty()) { + return false; + } + + return true; + } +}; diff --git a/C++/cracking-the-safe.cpp b/C++/cracking-the-safe.cpp new file mode 100644 index 000000000..93dbe4fcb --- /dev/null +++ b/C++/cracking-the-safe.cpp @@ -0,0 +1,80 @@ +// Time: O(k^n) +// Space: O(k^n) + +// https://en.wikipedia.org/wiki/De_Bruijn_sequence +// https://en.wikipedia.org/wiki/Lyndon_word +class Solution { +public: + string crackSafe(int n, int k) { + const int M = pow(k, n - 1); + vector P; + for (int i = 0; i < k; ++i) { + for (int q = 0; q < M; ++q) { + P.emplace_back(q * k + i); // rotate: i*k^(n-1) + q => q*k + i + } + } + const int total = pow(k, n); + string result(n - 1, '0' + k - 1); + for (int i = 0; i < total; ++i) { + int j = i; + // concatenation in lexicographic order of Lyndon words + while (P[j] >= 0) { + result.push_back('0' + j / M); + auto Pj = P[j]; + P[j] = -1; + j = Pj; + } + } + return result; + } +}; + +// Time: O(n * k^n) +// Space: O(n * k^n) +class Solution2 { +public: + string crackSafe(int n, int k) { + string result(n, '0' + k - 1); + unordered_set lookup; + lookup.emplace(result); + const int total = pow(k, n); + while (lookup.size() < total) { + const auto& node = result.substr(result.length() - n + 1); + for (int i = 0; i < k; ++i) { + const auto& neighbor = node + to_string(i); + if (!lookup.count(neighbor)) { + lookup.emplace(neighbor); + result.push_back('0' + i); + break; + } + } + } + return result; + } +}; + +// Time: O(n * k^n) +// Space: O(n * k^n) +class Solution3 { +public: + string crackSafe(int n, int k) { + unordered_set lookup; + string result(n - 1, '0' + k - 1); + auto node = result; + dfs(k, node, &lookup, &result); + return result; + } + +private: + void dfs(int k, const string& node, unordered_set *lookup, string *result) { + for (int i = 0; i < k; ++i) { + const auto& neighbor = node + to_string(i); + if (!lookup->count(neighbor)) { + lookup->emplace(neighbor); + result->push_back('0' + i); + dfs(k, neighbor.substr(1), lookup, result); + break; + } + } + } +}; diff --git a/C++/create-maximum-number.cpp b/C++/create-maximum-number.cpp new file mode 100644 index 000000000..4ebd28884 --- /dev/null +++ b/C++/create-maximum-number.cpp @@ -0,0 +1,84 @@ +// Time: O(k * (m + n + k)) ~ O(k * (m + n + k^2)) +// Space: O(m + n + k^2) + +// DP + Greedy solution. +class Solution { +public: + vector maxNumber(vector& nums1, vector& nums2, int k) { + const int m = nums1.size(), n = nums2.size(); + vector> max_numbers1(k + 1), max_numbers2(k + 1); + maxNumberDP(nums1, max(0, k - n), min(k, m), &max_numbers1); // O(k * m) time, O(m + k^2) space. + maxNumberDP(nums2, max(0, k - m), min(k, n), &max_numbers2); // O(k * n) time, O(n + k^2) space. + + vector res(k); + for (int i = max(0, k - n); i <= min(k, m); ++i) { // k * O(k) ~ k * O(k^2) time + vector tmp(k); + merge(max_numbers1[i], max_numbers2[k - i], &tmp); + if (tmp > res) { + res = move(tmp); + } + } + return res; + } + +private: + void maxNumberDP(vector nums, int start, int end, vector> *max_numbers) { + (*max_numbers)[end] = maxNumber(nums, end); + for (int i = end - 1; i >= start; --i) { + (*max_numbers)[i] = deleteNumber((*max_numbers)[i + 1]); + } + } + + // Time: O(n) + // Space: O(n) + vector maxNumber(const vector& nums, int k) { + vector res; + int drop = nums.size() - k; + for (const auto& num : nums) { + while (drop > 0 && !res.empty() && res.back() < num) { + res.pop_back(); + --drop; + } + res.emplace_back(num); + } + res.resize(k); + return res; + } + + // Time: O(n) + // Space: O(n) + vector deleteNumber(const vector& nums) { + vector res(nums); + for (int i = 0; i < res.size(); ++i) { + if (i == res.size() - 1 || res[i] < res[i + 1]) { + res.erase(res.begin() + i); + break; + } + } + return res; + } + + // Time: O(k) ~ O(k^2) + // Space: O(1) + void merge(const vector& vec1, const vector& vec2, vector *res) { + auto first1 = vec1.begin(), last1 = vec1.end(), + first2 = vec2.begin(), last2 = vec2.end(); + auto result = res->begin(); + while (first1 != last1 || first2 != last2) { + if (greater(first1, last1, first2, last2)) { + *result++ = *first1++; + } else { + *result++ = *first2++; + } + } + } + + template + bool greater(IT first1, IT last1, IT first2, IT last2) { + while (first1 != last1 && first2 != last2 && *first1 == *first2) { + ++first1; + ++first2; + } + return (first2 == last2) || (first1 != last1 && *first1 > *first2); + } +}; diff --git a/C++/custom-sort-string.cpp b/C++/custom-sort-string.cpp new file mode 100644 index 000000000..da155af38 --- /dev/null +++ b/C++/custom-sort-string.cpp @@ -0,0 +1,23 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + string customSortString(string S, string T) { + unordered_set s(S.cbegin(), S.cend()); + unordered_map counter; + for (const auto& c : T) { + ++counter[c]; + } + string result; + for (const auto& c : S) { + result += string(counter[c], c); + } + for (const auto& kvp : counter) { + if (!s.count(kvp.first)) { + result += string(kvp.second, kvp.first); + } + } + return result; + } +}; diff --git a/C++/cut-off-trees-for-golf-event.cpp b/C++/cut-off-trees-for-golf-event.cpp new file mode 100644 index 000000000..68555feaf --- /dev/null +++ b/C++/cut-off-trees-for-golf-event.cpp @@ -0,0 +1,153 @@ +// Time: O(t * (logt + m * n)), t is the number of trees +// Space: O(t + m * n) + +// Solution Reference: +// 1. https://discuss.leetcode.com/topic/103532/my-python-solution-inspired-by-a-algorithm/2 +// 2. https://discuss.leetcode.com/topic/103562/python-solution-based-on-wufangjie-s-hadlock-s-algorithm +// 3. https://en.wikipedia.org/wiki/A*_search_algorithm +// 4. https://cg2010studio.files.wordpress.com/2011/12/dijkstra-vs-a-star.png +class Solution { +public: + int cutOffTree(vector>& forest) { + const auto m = forest.size(), n = forest[0].size(); + priority_queue>, + vector>>, + greater>> > min_heap; + + for (int i = 0; i < m; ++i) { + for (int j = 0; j < n; ++j) { + if (forest[i][j] > 1) { + min_heap.emplace(forest[i][j], make_pair(i, j)); + } + } + } + + pair start; + int result = 0; + while (!min_heap.empty()) { + auto tree = min_heap.top(); min_heap.pop(); + int step = minStep(forest, start, tree.second, m, n); + if (step < 0) { + return -1; + } + result += step; + start = tree.second; + } + return result; + } + +private: + int minStep(const vector>& forest, + const pair& start, + const pair& end, + const int m, const int n) { + + int min_steps = abs(start.first - end.first) + abs(start.second - end.second); + unordered_set lookup; + vector> closer{start}, detour; + while (true) { + if (closer.empty()) { // cannot find a path in the closer expansions + if (detour.empty()) { // no other possible path + return -1; + } + // try other possible paths in detour expansions with extra 2-step cost + min_steps += 2; + swap(closer, detour); + } + int i, j; + tie(i, j) = closer.back(); closer.pop_back(); + if (make_pair(i, j) == end) { + return min_steps; + } + if (!lookup.count(i * n + j)) { + lookup.emplace(i * n + j); + vector> expansions = {{i + 1, j}, {i - 1, j}, {i, j + 1}, {i, j - 1}}; + for (const auto& expansion : expansions) { + int I, J; + tie(I, J) = expansion; + if (0 <= I && I < m && 0 <= J && J < n && + forest[I][J] && !lookup.count(I * n + J)) { + bool is_closer = dot({I - i, J - j}, {end.first - i, end.second - j}) > 0; + is_closer ? closer.emplace_back(I, J) : detour.emplace_back(I, J); + } + } + } + } + + return min_steps; + } + + inline int dot(const pair& a, const pair& b) { + return a.first * b.first + a.second * b.second; + } +}; + + +// Time: O(t * (logt + m * n)), t is the number of trees +// Space: O(t + m * n) +class Solution2 { +public: + int cutOffTree(vector>& forest) { + const auto m = forest.size(), n = forest[0].size(); + priority_queue>, + vector>>, + greater>> > min_heap; + + for (int i = 0; i < m; ++i) { + for (int j = 0; j < n; ++j) { + if (forest[i][j] > 1) { + min_heap.emplace(forest[i][j], make_pair(i, j)); + } + } + } + + pair start; + int result = 0; + while (!min_heap.empty()) { + auto tree = min_heap.top(); min_heap.pop(); + int step = minStep(forest, start, tree.second, m, n); + if (step < 0) { + return -1; + } + result += step; + start = tree.second; + } + return result; + } + +private: + int minStep(const vector>& forest, + const pair& start, + const pair& end, + const int m, const int n) { + + int min_steps = 0; + unordered_set lookup; + queue> q; + q.emplace(start); + lookup.emplace(start.first * n + start.second); + while (!q.empty()) { + int size = q.size(); + for (int i = 0; i < size; ++i) { + auto curr = q.front(); q.pop(); + if (curr == end) { + return min_steps; + } + static const vector> directions{{0, -1}, {0, 1}, + {-1, 0}, {1, 0}}; + for (const auto& direction : directions) { + int i = curr.first + direction.first; + int j = curr.second + direction.second; + if (i < 0 || i >= m || j < 0 || j >= n || + !forest[i][j] || lookup.count(i * n + j)) { + continue; + } + q.emplace(i, j); + lookup.emplace(i * n + j); + } + } + ++min_steps; + } + return -1; + } +}; diff --git a/C++/daily-temperatures.cpp b/C++/daily-temperatures.cpp new file mode 100644 index 000000000..18a0d1bf6 --- /dev/null +++ b/C++/daily-temperatures.cpp @@ -0,0 +1,19 @@ +// Time: O(n) +// Space: O(n) + +class Solution { +public: + vector dailyTemperatures(vector& temperatures) { + vector result(temperatures.size()); + stack stk; + for (int i = 0; i < temperatures.size(); ++i) { + while (!stk.empty() && + temperatures[stk.top()] < temperatures[i]) { + const auto idx = stk.top(); stk.pop(); + result[idx] = i - idx; + } + stk.emplace(i); + } + return result; + } +}; diff --git a/C++/data-stream-as-disjoint-intervals.cpp b/C++/data-stream-as-disjoint-intervals.cpp new file mode 100644 index 000000000..4561488a1 --- /dev/null +++ b/C++/data-stream-as-disjoint-intervals.cpp @@ -0,0 +1,165 @@ +// Time: addNum: O(logn), getIntervals: O(n), n is the number of disjoint intervals. +// Space: O(n) + +/** + * Definition for an interval. + * struct Interval { + * int start; + * int end; + * Interval() : start(0), end(0) {} + * Interval(int s, int e) : start(s), end(e) {} + * }; + */ +// Using set. +class SummaryRanges { +public: + /** Initialize your data structure here. */ + SummaryRanges() { + + } + + void addNum(int val) { + auto it = intervals_.upper_bound(Interval(val, val)); + int start = val, end = val; + if (it != intervals_.begin() && prev(it)->end + 1 >= val) { + --it; + } + while (it != intervals_.end() && end + 1 >= it->start) { + start = min(start, it->start); + end = max(end, it->end); + it = intervals_.erase(it); + } + intervals_.insert(it, Interval(start, end)); + } + + vector getIntervals() { + return {intervals_.cbegin(), intervals_.cend()}; + } + +private: + struct Compare { + bool operator() (const Interval& a, const Interval& b) { + return a.start < b.start; + } + }; + set intervals_; +}; + + +// Using map. +class SummaryRanges { +public: + /** Initialize your data structure here. */ + SummaryRanges() { + + } + + void addNum(int val) { + auto it = intervals_.upper_bound(val); + int start = val, end = val; + if (it != intervals_.begin() && prev(it)->second + 1 >= val) { + --it; + } + while (it != intervals_.end() && end + 1 >= it->first) { + start = min(start, it->first); + end = max(end, it->second); + it = intervals_.erase(it); + } + intervals_[start] = end; + } + + vector getIntervals() { + vector result; + for (const auto& kvp : intervals_) { + result.emplace_back(kvp.first, kvp.second); + } + return result; + } + +private: + map intervals_; +}; + + +// Time: addNum: O(n), getIntervals: O(n), n is the number of disjoint intervals. +// Space: O(n) +class SummaryRanges2 { +public: +public: + /** Initialize your data structure here. */ + SummaryRanges2() { + + } + + void addNum(int val) { + auto it = upper_bound(intervals_.begin(), intervals_.end(), Interval(val, val), Compare()); + int start = val, end = val; + if (it != intervals_.begin() && prev(it)->end + 1 >= val) { + --it; + } + while (it != intervals_.end() && end + 1 >= it->start) { + start = min(start, it->start); + end = max(end, it->end); + it = intervals_.erase(it); + } + intervals_.insert(it, Interval(start, end)); + } + + vector getIntervals() { + return intervals_; + } + +private: + struct Compare { + bool operator() (const Interval& a, const Interval& b) { + return a.start < b.start; + } + }; + vector intervals_; +}; + + +// Time: addNum: O(logs), getIntervals: O(s), s is the data stream's size. +// Space: O(s) +class SummaryRanges3 { +public: + /** Initialize your data structure here. */ + SummaryRanges3() { + + } + + void addNum(int val) { + nums_.emplace(val); + } + + vector getIntervals() { + vector result; + if (nums_.empty()) { + return result; + } + auto start = *nums_.begin(), end = *nums_.begin(); + for (auto it = next(nums_.begin()); it != nums_.end(); ++it) { + if (it != nums_.end() && *it == end + 1) { + end = *it; + } else { + result.emplace_back(start, end); + if (it != nums_.end()) { + start = end = *it; + } + } + } + result.emplace_back(start, end); + return result; + } + +private: + set nums_; +}; + +/** + * Your SummaryRanges object will be instantiated and called as such: + * SummaryRanges obj = new SummaryRanges(); + * obj.addNum(val); + * vector param_2 = obj.getIntervals(); + */ + diff --git a/C++/decode-string.cpp b/C++/decode-string.cpp new file mode 100644 index 000000000..07bee9973 --- /dev/null +++ b/C++/decode-string.cpp @@ -0,0 +1,32 @@ +// Time: O(n) +// Space: O(h), h is the depth of the recursion + +class Solution { +public: + string decodeString(string s) { + string curr; + stack nums; + stack strs; + int n = 0; + for (const auto& c: s) { + if (isdigit(c)) { + n = n * 10 + c - '0'; + } else if (c == '[') { + nums.emplace(n); + n = 0; + strs.emplace(curr); + curr.clear(); + } else if (c == ']') { + for (; nums.top() > 0; --nums.top()) { + strs.top() += curr; + } + nums.pop(); + curr = strs.top(); + strs.pop(); + } else { + curr += c; + } + } + return strs.empty() ? curr : strs.top(); + } +}; diff --git a/C++/decode-ways-ii.cpp b/C++/decode-ways-ii.cpp new file mode 100644 index 000000000..40e586fb5 --- /dev/null +++ b/C++/decode-ways-ii.cpp @@ -0,0 +1,35 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + int numDecodings(string s) { + static const int M = 1000000007; + static const int W = 3; + vector dp(W); + dp[0] = 1; + dp[1] = s[0] == '*' ? 9 : (s[0] != '0' ? dp[0] : 0); + for (int i = 1; i < s.length(); ++i) { + if (s[i] == '*') { + dp[(i + 1) % W] = 9 * dp[i % W]; + if (s[i - 1] == '1') { + dp[(i + 1) % W] = (dp[(i + 1) % W] + 9 * dp[(i - 1) % W]) % M; + } else if (s[i - 1] == '2') { + dp[(i + 1) % W] = (dp[(i + 1) % W] + 6 * dp[(i - 1) % W]) % M; + } else if (s[i - 1] == '*') { + dp[(i + 1) % W] = (dp[(i + 1) % W] + 15 * dp[(i - 1) % W]) % M; + } + } else { + dp[(i + 1) % W] = s[i] != '0' ? dp[i % W] : 0; + if (s[i - 1] == '1') { + dp[(i + 1) % W] = (dp[(i + 1) % W] + dp[(i - 1) % W]) % M; + } else if (s[i - 1] == '2' && s[i] <= '6') { + dp[(i + 1) % W] = (dp[(i + 1) % W] + dp[(i - 1) % W]) % M; + } else if (s[i - 1] == '*') { + dp[(i + 1) % W] = (dp[(i + 1) % W] + (s[i] <= '6' ? 2 : 1) * dp[(i - 1) % W]) % M; + } + } + } + return static_cast(dp[s.length() % W]); + } +}; diff --git a/C++/decode-ways.cpp b/C++/decode-ways.cpp new file mode 100644 index 000000000..38f4d9368 --- /dev/null +++ b/C++/decode-ways.cpp @@ -0,0 +1,30 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + int numDecodings(string s) { + if (s.empty()) { + return 0; + } + + int prev = 0; // f[n - 2] + int cur = 1; // f[n - 1] + + for (int i = 0; i < s.length(); ++i) { + if (s[i] == '0') { + cur = 0; // f[n - 1] = 0 + } + if (i == 0 || + !(s[i - 1] == '1' || (s[i - 1] == '2' && s[i] <= '6'))) { + prev = 0; // f[n - 2] = 0 + } + + int tmp = cur; + cur += prev; // f[n] = f[n - 1] + f[n - 2] + prev = tmp; + } + + return cur; + } +}; diff --git a/C++/decoded-string-at-index.cpp b/C++/decoded-string-at-index.cpp new file mode 100644 index 000000000..e2ac1d933 --- /dev/null +++ b/C++/decoded-string-at-index.cpp @@ -0,0 +1,29 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + string decodeAtIndex(string S, int K) { + uint64_t n = 0; + for (int i = 0; i < S.length(); ++i) { + if (isdigit(S[i])) { + n *= S[i] - '0'; + } else { + ++n; + } + } + + for (int i = S.length() - 1; i >= 0; --i) { + K %= n; + if (K == 0 && isalpha(S[i])) { + return (string) "" + S[i]; + } + + if (isdigit(S[i])) { + n /= S[i] - '0'; + } else { + --n; + } + } + } +}; diff --git a/C++/degree-of-an-array.cpp b/C++/degree-of-an-array.cpp new file mode 100644 index 000000000..4f8ca21a9 --- /dev/null +++ b/C++/degree-of-an-array.cpp @@ -0,0 +1,29 @@ +// Time: O(n) +// Space: O(n) + +class Solution { +public: + int findShortestSubArray(vector& nums) { + unordered_map left, right; + unordered_map counts; + for (int i = 0; i < nums.size(); ++i) { + if (left.count(nums[i]) == 0) { + left[nums[i]] = i; + } + right[nums[i]] = i; + ++counts[nums[i]]; + } + auto degree = max_element(counts.begin(), counts.end(), + [](const pair& a, + const pair& b) { + return a.second < b.second; + })->second; + auto result = numeric_limits::max(); + for (const auto& kvp : counts) { + if (kvp.second == degree) { + result = min(result, right[kvp.first] - left[kvp.first] + 1); + } + } + return result; + } +}; diff --git a/C++/delete-and-earn.cpp b/C++/delete-and-earn.cpp new file mode 100644 index 000000000..d4e2b2505 --- /dev/null +++ b/C++/delete-and-earn.cpp @@ -0,0 +1,19 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + int deleteAndEarn(vector& nums) { + vector vals(10001); + for (const auto& num : nums) { + vals[num] += num; + } + int val_i = vals[0], val_i_1 = 0, val_i_2 = 0; + for (int i = 1; i < vals.size(); ++i) { + val_i_2 = val_i_1; + val_i_1 = val_i; + val_i = max(vals[i] + val_i_2, val_i_1); + } + return val_i; + } +}; diff --git a/C++/delete-node-in-a-bst.cpp b/C++/delete-node-in-a-bst.cpp new file mode 100644 index 000000000..237fb89b7 --- /dev/null +++ b/C++/delete-node-in-a-bst.cpp @@ -0,0 +1,43 @@ +// Time: O(h) +// Space: O(h) + +/** + * 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* deleteNode(TreeNode* root, int key) { + if (!root) { + return nullptr; + } + if (root->val > key) { + root->left = deleteNode(root->left, key); + } else if (root->val < key) { + root->right = deleteNode(root->right, key); + } else { + if (!root->left) { + auto right = root->right; + delete root; + return right; + } else if (!root->right) { + auto left = root->left; + delete root; + return left; + } else { + auto successor = root->right; + while (successor->left) { + successor = successor->left; + } + root->val = successor->val; + root->right = deleteNode(root->right, successor->val); + } + } + return root; + } +}; diff --git a/C++/delete-node-in-a-linked-list.cpp b/C++/delete-node-in-a-linked-list.cpp new file mode 100644 index 000000000..17f100faf --- /dev/null +++ b/C++/delete-node-in-a-linked-list.cpp @@ -0,0 +1,23 @@ +// Time: O(1) +// Space: O(1) + +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode(int x) : val(x), next(NULL) {} + * }; + */ +class Solution { +public: + void deleteNode(ListNode* node) { + if (!node || !node->next) { + return; + } + auto node_to_delete = node->next; + node->val = node->next->val; + node->next = node->next->next; + delete node_to_delete; + } +}; diff --git a/C++/delete-operation-for-two-strings.cpp b/C++/delete-operation-for-two-strings.cpp new file mode 100644 index 000000000..46a33c247 --- /dev/null +++ b/C++/delete-operation-for-two-strings.cpp @@ -0,0 +1,19 @@ +// Time: O(m * n) +// Space: O(n) + +class Solution { +public: + int minDistance(string word1, string word2) { + const auto m = word1.length(); + const auto n = word2.length(); + + vector> dp(2, vector(n + 1)); + for (int i = 0; i < m; ++i) { + for (int j = 0; j < n; ++j) { + dp[(i + 1) % 2][j + 1] = max(max(dp[i % 2][j + 1], dp[(i + 1) % 2][j]), + dp[i % 2][j] + (word1[i] == word2[j])); + } + } + return m + n - 2 * dp[m % 2][n]; + } +}; diff --git a/C++/deleteDuplicatesII.cpp b/C++/deleteDuplicatesII.cpp deleted file mode 100644 index f1e228b71..000000000 --- a/C++/deleteDuplicatesII.cpp +++ /dev/null @@ -1,45 +0,0 @@ -// Time Complexity: O(n) -// Space Complexity: O(1) - -/** - * Definition for singly-linked list. - * struct ListNode { - * int val; - * ListNode *next; - * ListNode(int x) : val(x), next(NULL) {} - * }; - */ -class Solution { - public: - ListNode *deleteDuplicates(ListNode *head) { - if(!head) - return NULL; - ListNode dummy(INT_MIN); - dummy.next = head; - ListNode *pre2nd = &dummy; - ListNode *pre1st = head; - ListNode *cur = pre1st->next; - bool isDup = false; - - while(pre1st) { - if(cur && pre1st->val == cur->val) { - pre2nd->next = cur; // remove previous first node - delete pre1st; - pre1st = NULL; - isDup = true; - } - else if(isDup){ - pre2nd->next = cur; // remove previous first node - delete pre1st; - pre1st = NULL; - isDup = false; - } - - if(pre1st) pre2nd = pre1st; - pre1st = cur; - if(cur) cur = cur->next; - } - - return dummy.next; - } -}; diff --git a/C++/design-circular-deque.cpp b/C++/design-circular-deque.cpp new file mode 100644 index 000000000..b92954285 --- /dev/null +++ b/C++/design-circular-deque.cpp @@ -0,0 +1,92 @@ +// Time: O(1) +// Space: O(k) + +class MyCircularDeque { +public: + /** Initialize your data structure here. Set the size of the deque to be k. */ + MyCircularDeque(int k) : + start_(0), + size_(0), + buffer_(k, 0) { + + } + + /** Adds an item at the front of Deque. Return true if the operation is successful. */ + bool insertFront(int value) { + if (isFull()) { + return false; + } + start_ = (start_ - 1 + buffer_.size()) % buffer_.size(); + buffer_[start_] = value; + ++size_; + return true; + } + + /** Adds an item at the rear of Deque. Return true if the operation is successful. */ + bool insertLast(int value) { + if (isFull()) { + return false; + } + buffer_[(start_ + size_) % buffer_.size()] = value; + ++size_; + return true; + } + + /** Deletes an item from the front of Deque. Return true if the operation is successful. */ + bool deleteFront() { + if (isEmpty()) { + return false; + } + start_ = (start_ + 1) % buffer_.size(); + --size_; + return true; + } + + /** Deletes an item from the rear of Deque. Return true if the operation is successful. */ + bool deleteLast() { + if (isEmpty()) { + return false; + } + --size_; + return true; + } + + /** Get the front item from the deque. */ + int getFront() { + return isEmpty() ? -1 : buffer_[start_]; + } + + /** Get the last item from the deque. */ + int getRear() { + return isEmpty() ? -1 : buffer_[(start_ + size_ - 1) % buffer_.size()]; + } + + /** Checks whether the circular deque is empty or not. */ + bool isEmpty() { + return size_ == 0; + } + + /** Checks whether the circular deque is full or not. */ + bool isFull() { + return size_ == buffer_.size(); + } + +private: + int start_; + int size_; + vector buffer_; +}; + +/** + * Your MyCircularDeque object will be instantiated and called as such: + * MyCircularDeque obj = new MyCircularDeque(k); + * bool param_1 = obj.insertFront(value); + * bool param_2 = obj.insertLast(value); + * bool param_3 = obj.deleteFront(); + * bool param_4 = obj.deleteLast(); + * int param_5 = obj.getFront(); + * int param_6 = obj.getRear(); + * bool param_7 = obj.isEmpty(); + * bool param_8 = obj.isFull(); + */ + diff --git a/C++/design-circular-queue.cpp b/C++/design-circular-queue.cpp new file mode 100644 index 000000000..4b10e11f9 --- /dev/null +++ b/C++/design-circular-queue.cpp @@ -0,0 +1,70 @@ +// Time: O(1) +// Space: O(k) + +class MyCircularQueue { +public: + /** Initialize your data structure here. Set the size of the queue to be k. */ + MyCircularQueue(int k) : + start_(0), + size_(0), + buffer_(k, 0) { + + } + + /** Insert an element into the circular queue. Return true if the operation is successful. */ + bool enQueue(int value) { + if (isFull()) { + return false; + } + buffer_[(start_ + size_) % buffer_.size()] = value; + ++size_; + return true; + } + + /** Delete an element from the circular queue. Return true if the operation is successful. */ + bool deQueue() { + if (isEmpty()) { + return false; + } + start_ = (start_ + 1) % buffer_.size(); + --size_; + return true; + } + + /** Get the front item from the queue. */ + int Front() { + return isEmpty() ? -1 : buffer_[start_]; + } + + /** Get the last item from the queue. */ + int Rear() { + return isEmpty() ? -1 : buffer_[(start_ + size_ - 1) % buffer_.size()]; + } + + /** Checks whether the circular queue is empty or not. */ + bool isEmpty() { + return size_ == 0; + } + + /** Checks whether the circular queue is full or not. */ + bool isFull() { + return size_ == buffer_.size(); + } + +private: + int start_; + int size_; + vector buffer_; +}; + +/** + * Your MyCircularQueue object will be instantiated and called as such: + * MyCircularQueue obj = new MyCircularQueue(k); + * bool param_1 = obj.enQueue(value); + * bool param_2 = obj.deQueue(); + * int param_3 = obj.Front(); + * int param_4 = obj.Rear(); + * bool param_5 = obj.isEmpty(); + * bool param_6 = obj.isFull(); + */ + diff --git a/C++/design-compressed-string-iterator.cpp b/C++/design-compressed-string-iterator.cpp new file mode 100644 index 000000000..729715b34 --- /dev/null +++ b/C++/design-compressed-string-iterator.cpp @@ -0,0 +1,39 @@ +// Time: O(1) +// Space: O(1) + +class StringIterator { +public: + StringIterator(string compresult_sedString) + : result_(compresult_sedString), + num_(0), + ch_(' ') { + + } + + char next() { + if (!hasNext()) { + return ' '; + } + if (num_ == 0) { + result_ >> ch_ >> num_; + } + --num_; + return ch_; + } + + bool hasNext() { + return !result_.eof() || num_ != 0; + } + +private: + istringstream result_; + int num_; + char ch_; +}; + +/** + * Your StringIterator object will be instantiated and called as such_: + * StringIterator obj = new StringIterator(compresult_sedString); + * ch_ar param_1 = obj.next(); + * bool param_2 = obj.hasNext(); + */ diff --git a/C++/design-excel-sum-formula.cpp b/C++/design-excel-sum-formula.cpp new file mode 100644 index 000000000..2a627293e --- /dev/null +++ b/C++/design-excel-sum-formula.cpp @@ -0,0 +1,95 @@ +// Time: set: O((r * c)^2) +// get: O(1) +// sum: O((r * c)^2) +// Space: O(r * c) + +class Excel { +public: + Excel(int H, char W) : Exl_(H + 1, vector(W - 'A' + 1)) { + } + + // Time: O((r * c)^2) + void set(int r, char c, int v) { + auto col = c - 'A'; + reset_dependency(r, col); + update_others(r, col, v); + } + + // Time: O(1) + int get(int r, char c) { + return Exl_[r][c - 'A']; + } + + // Time: O((r * c)^2) + int sum(int r, char c, vector strs) { + auto col = c - 'A'; + reset_dependency(r, col); + auto result = calc_and_update_dependency(r, col, strs); + update_others(r, col, result); + return result; + } + +private: + // Time: O(r * c) + void reset_dependency(int r, int col) { + auto key = r * 26 + col; + if (bward_.count(key)) { + for (const auto& k : bward_[key]) { + fward_[k].erase(key); + } + bward_.erase(key); + } + } + + // Time: O(r * c * l), l is the length of strs + int calc_and_update_dependency(int r, int col, const vector& strs) { + auto result = 0; + for (const auto& s : strs) { + int p = s.find(':'), left, right, top, bottom; + left = s[0] - 'A'; + right = s[p + 1] - 'A'; + top = (p == string::npos) ? stoi(s.substr(1)) : stoi(s.substr(1, p - 1)); + bottom = stoi(s.substr(p + 2)); + for (int i = top; i <= bottom; ++i) { + for (int j = left; j <= right; ++j) { + result += Exl_[i][j]; + ++fward_[i * 26 + j][r * 26 + col]; + bward_[r * 26 + col].emplace(i * 26 + j); + } + } + } + return result; + } + + // Time: O((r * c)^2) + void update_others(int r, int col, int v) { + auto prev = Exl_[r][col]; + Exl_[r][col] = v; + queue> q; + q.emplace(make_pair(r * 26 + col, v - prev)); + while (!q.empty()) { + int key, diff; + tie(key, diff) = q.front(), q.pop(); + if (fward_.count(key)) { + for (auto it = fward_[key].begin(); it != fward_[key].end(); ++it) { + int k, count; + tie(k, count) = *it; + q.emplace(make_pair(k, diff * count)); + Exl_[k / 26][k % 26] += diff * count; + } + } + } + } + + unordered_map> fward_; + unordered_map> bward_; + vector> Exl_; +}; + +/** + * Your Excel object will be instantiated and called as such: + * Excel obj = new Excel(H, W); + * obj.set(r,c,v); + * int param_2 = obj.get(r,c); + * int param_3 = obj.sum(r,c,strs); + */ diff --git a/C++/design-hashmap.cpp b/C++/design-hashmap.cpp new file mode 100644 index 000000000..6327a9b10 --- /dev/null +++ b/C++/design-hashmap.cpp @@ -0,0 +1,55 @@ +// Time: O(1) +// Space: O(n) + +class MyHashMap { +public: + /** Initialize your data structure here. */ + MyHashMap() : data_(10000) { + + } + + /** value will always be positive. */ + void put(int key, int value) { + auto& list = data_[key % data_.size()]; + auto it = find_if(list.begin(), list.end(), + [&](const pair& i) { return i.first == key; } ); + if (it != list.end()) { + it->second = value; + } else { + list.emplace_back(key, value); + } + } + + /** Returns the value to which the specified key is mapped, or -1 if this map contains no mapping for the key */ + int get(int key) { + auto& list = data_[key % data_.size()]; + auto it = find_if(list.begin(), list.end(), + [&](const pair& i) { return i.first == key; } ); + if (it != list.end()) { + return it->second; + } else { + return -1; + } + } + + /** Removes the mapping of the specified value key if this map contains a mapping for the key */ + void remove(int key) { + auto& list = data_[key % data_.size()]; + auto it = find_if(list.begin(), list.end(), + [&](const pair& i) { return i.first == key; } ); + if (it != list.end()) { + list.erase(it); + } + } + +private: + vector>> data_; +}; + +/** + * Your MyHashMap object will be instantiated and called as such: + * MyHashMap obj = new MyHashMap(); + * obj.put(key,value); + * int param_2 = obj.get(key); + * obj.remove(key); + */ diff --git a/C++/design-hashset.cpp b/C++/design-hashset.cpp new file mode 100644 index 000000000..c0d824f67 --- /dev/null +++ b/C++/design-hashset.cpp @@ -0,0 +1,45 @@ +// Time: O(1) +// Space: O(n) + +class MyHashSet { +public: + /** Initialize your data structure here. */ + MyHashSet() : data_(10000) { + + } + + void add(int key) { + auto& list = data_[key % data_.size()]; + auto it = find(list.begin(), list.end(), key); + if (it == list.end()) { + list.emplace_back(key); + } + } + + void remove(int key) { + auto& list = data_[key % data_.size()]; + auto it = find(list.begin(), list.end(), key); + if (it != list.end()) { + list.erase(it); + } + } + + /** Returns true if this set did not already contain the specified element */ + bool contains(int key) { + auto& list = data_[key % data_.size()]; + auto it = find(list.begin(), list.end(), key); + return it != list.end(); + } + +private: + vector> data_; +}; + +/** + * Your MyHashSet object will be instantiated and called as such: + * MyHashSet obj = new MyHashSet(); + * obj.add(key); + * obj.remove(key); + * bool param_3 = obj.contains(key); + */ + diff --git a/C++/design-hit-counter.cpp b/C++/design-hit-counter.cpp new file mode 100644 index 000000000..22038d828 --- /dev/null +++ b/C++/design-hit-counter.cpp @@ -0,0 +1,45 @@ +// Time: O(1), amortized +// Space: O(k), k is the count of seconds. + +class HitCounter { +public: + /** Initialize your data structure here. */ + HitCounter() : count_(0) { + + } + + /** Record a hit. + @param timestamp - The current timestamp (in seconds granularity). */ + void hit(int timestamp) { + getHits(timestamp); + if (!dq_.empty() && dq_.back().first == timestamp) { + ++dq_.back().second; + } else { + dq_.emplace_back(timestamp, 1); + } + ++count_; + } + + /** Return the number of hits in the past 5 minutes. + @param timestamp - The current timestamp (in seconds granularity). */ + int getHits(int timestamp) { + while (!dq_.empty() && dq_.front().first <= timestamp - k_) { + count_ -= dq_.front().second; + dq_.pop_front(); + } + return count_; + } + +private: + const int k_ = 300; + int count_; + deque> dq_; +}; + +/** + * Your HitCounter object will be instantiated and called as such: + * HitCounter obj = new HitCounter(); + * obj.hit(timestamp); + * int param_2 = obj.getHits(timestamp); + */ + diff --git a/C++/design-in-memory-file-system.cpp b/C++/design-in-memory-file-system.cpp new file mode 100644 index 000000000..65423bd9f --- /dev/null +++ b/C++/design-in-memory-file-system.cpp @@ -0,0 +1,93 @@ +// Time: ls: O(l + klogk), l is the path length, k is the number of entries in the last level directory +// mkdir: O(l) +// addContentToFile: O(l + c), c is the content size +// readContentFromFile: O(l + c) +// Space: O(n + s), n is the number of dir/file nodes, s is the total content size. + +class FileSystem { +public: + FileSystem() : root_(new TrieNode()) { + + } + + vector ls(string path) { + auto curr = getNode(path); + + if (curr->isFile) { + return {split(path, '/').back()}; + } + + vector result; + for (const auto& child : curr->children) { + result.emplace_back(child.first); + } + sort(result.begin(), result.end()); + return result; + } + + void mkdir(string path) { + auto curr = putNode(path); + curr->isFile = false; + } + + void addContentToFile(string filePath, string content) { + auto curr = putNode(filePath); + curr->isFile = true; + curr->content += content; + } + + string readContentFromFile(string filePath) { + return getNode(filePath)->content; + } + +private: + struct TrieNode { + bool isFile; + unordered_map children; + string content; + }; + + TrieNode *root_; + + TrieNode *getNode(const string& path) { + vector strs = split(path, '/'); + auto curr = root_; + for (const auto& str : strs) { + curr = curr->children[str]; + } + return curr; + } + + TrieNode *putNode(const string& path) { + vector strs = split(path, '/'); + auto curr = root_; + for (const auto& str : strs) { + if (!curr->children.count(str)) { + curr->children[str] = new TrieNode(); + } + curr = curr->children[str]; + } + return curr; + } + + vector split(const string& s, const char delim) { + vector tokens; + stringstream ss(s); + string token; + while (getline(ss, token, delim)) { + if (!token.empty()) { + tokens.emplace_back(token); + } + } + return tokens; + } +}; + +/** + * Your FileSystem object will be instantiated and called as such: + * FileSystem obj = new FileSystem(); + * vector param_1 = obj.ls(path); + * obj.mkdir(path); + * obj.addContentToFile(filePath,content); + * string param_4 = obj.readContentFromFile(filePath); + */ diff --git a/C++/design-linked-list.cpp b/C++/design-linked-list.cpp new file mode 100644 index 000000000..3b9408906 --- /dev/null +++ b/C++/design-linked-list.cpp @@ -0,0 +1,110 @@ +// Time: O(n) +// Space: O(n) + +struct Node { + Node(int value) : + val(value), + next(nullptr), + prev(nullptr) {} + int val; + Node *next; + Node *prev; +}; + +class MyLinkedList { +public: + /** Initialize your data structure here. */ + MyLinkedList() : size_(0) { + head_ = tail_ = new Node(-1); + head_->next = tail_; + tail_->prev = head_; + } + + /** Get the value of the index-th node in the linked list. If the index is invalid, return -1. */ + int get(int index) { + if (0 <= index && index <= size_ / 2) { + return forward(0, index, head_->next)->val; + } else if (size_ / 2 < index && index < size_) { + return backward(size_, index, tail_)->val; + } + return -1; + } + + /** Add a node of value val before the first element of the linked list. + After the insertion, the new node will be the first node of the linked list. */ + void addAtHead(int val) { + add(head_, val); + } + + /** Append a node of value val to the last element of the linked list. */ + void addAtTail(int val) { + add(tail_->prev, val); + } + + /** Add a node of value val before the index-th node in the linked list. + If index equals to the length of linked list, + the node will be appended to the end of linked list. + If index is greater than the length, the node will not be inserted. */ + void addAtIndex(int index, int val) { + if (0 <= index && index <= size_ / 2) { + add(forward(0, index, head_->next)->prev, val); + } else if (size_ / 2 < index && index <= size_) { + add(backward(size_, index, tail_)->prev, val); + } + } + + /** Delete the index-th node in the linked list, if the index is valid. */ + void deleteAtIndex(int index) { + if (0 <= index && index <= size_ / 2) { + remove(forward(0, index, head_->next)); + } else if (size_ / 2 < index && index < size_) { + remove(backward(size_, index, tail_)); + } + } + +private: + void add(Node *preNode, int val) { + auto node = new Node(val); + node->prev = preNode; + node->next = preNode->next; + node->prev->next = node->next->prev = node; + ++size_; + } + + void remove(Node *node) { + node->prev->next = node->next; + node->next->prev = node->prev; + --size_; + } + + Node *forward(int start, int end, Node *curr) { + while (start != end) { + ++start; + curr = curr->next; + } + return curr; + } + + Node *backward(int start, int end, Node *curr) { + while (start != end) { + --start; + curr = curr->prev; + } + return curr; + } + + Node *head_; + Node *tail_; + int size_; +}; + +/** + * Your MyLinkedList object will be instantiated and called as such: + * MyLinkedList obj = new MyLinkedList(); + * int param_1 = obj.get(index); + * obj.addAtHead(val); + * obj.addAtTail(val); + * obj.addAtIndex(index,val); + * obj.deleteAtIndex(index); + */ + diff --git a/C++/design-log-storage-system.cpp b/C++/design-log-storage-system.cpp new file mode 100644 index 000000000..cba11e80c --- /dev/null +++ b/C++/design-log-storage-system.cpp @@ -0,0 +1,44 @@ +// Time: put: O(logn), n is the size of the total logs +// retrieve: O(logn + d), d is the size of the found logs +// Space: O(n) + +class LogSystem { +public: + LogSystem() { + granularity_["Year"] = 4; + granularity_["Month"] = 7; + granularity_["Day"] = 10; + granularity_["Hour"] = 13; + granularity_["Minute"] = 16; + granularity_["Second"] = 19; + } + + void put(int id, string timestamp) { + lookup_.emplace(timestamp, id); + } + + vector retrieve(string s, string e, string gra) { + s = s.substr(0, granularity_[gra]) + s_filter_.substr(granularity_[gra]); + e = e.substr(0, granularity_[gra]) + e_filter_.substr(granularity_[gra]); + + vector result; + auto end = lookup_.upper_bound(e); + for (auto it = lookup_.lower_bound(s); it != end; ++it) { + result.emplace_back(it->second); + } + return result; + } + +private: + string s_filter_ = "0001:01:01:00:00:00"; + string e_filter_ = "9999:12:31:23:59:59"; + unordered_map granularity_; + multimap lookup_; +}; + +/** + * Your LogSystem object will be instantiated and called as such: + * LogSystem obj = new LogSystem(); + * obj.put(id,timestamp); + * vector param_2 = obj.retrieve(s,e,gra); + */ diff --git a/C++/design-phone-directory.cpp b/C++/design-phone-directory.cpp new file mode 100644 index 000000000..19f77a75b --- /dev/null +++ b/C++/design-phone-directory.cpp @@ -0,0 +1,56 @@ +// init: Time: O(n), Space: O(n) +// get: Time: O(1), Space: O(1) +// check: Time: O(1), Space: O(1) +// release: Time: O(1), Space: O(1) + +class PhoneDirectory { +public: + /** Initialize your data structure here + @param maxNumbers - The maximum numbers that can be stored in the phone directory. */ + PhoneDirectory(int maxNumbers) : + curr_{0}, numbers_(maxNumbers), used_(maxNumbers) { // Time: O(n), Space: O(n) + + iota(numbers_.begin(), numbers_.end(), 0); + } + + /** Provide a number which is not assigned to anyone. + @return - Return an available number. Return -1 if none is available. */ + int get() { // Time: O(1), Space: O(1) + if (curr_ == numbers_.size()) { + return -1; + } + const auto number = numbers_[curr_++]; + used_[number] = true; + return number; + } + + /** Check if a number is available or not. */ + bool check(int number) { // Time: O(1), Space: O(1) + if (number < 0 || number >= numbers_.size()) { + return false; + } + return !used_[number]; + } + + /** Recycle or release a number. */ + void release(int number) { // Time: O(1), Space: O(1) + if (number < 0 || number >= numbers_.size() || !used_[number]) { + return; + } + used_[number] = false; + numbers_[--curr_] = number ; + } + +private: + int curr_; + vector numbers_; + vector used_; +}; + +/** + * Your PhoneDirectory object will be instantiated and called as such: + * PhoneDirectory obj = new PhoneDirectory(maxNumbers); + * int param_1 = obj.get(); + * bool param_2 = obj.check(number); + * obj.release(number); + */ diff --git a/C++/design-search-autocomplete-system.cpp b/C++/design-search-autocomplete-system.cpp new file mode 100644 index 000000000..a93f873d9 --- /dev/null +++ b/C++/design-search-autocomplete-system.cpp @@ -0,0 +1,92 @@ +// Time: O(p^2), p is the length of the prefix +// Space: O(p * t + s), t is the number of nodes of trie +// , s is the size of the sentences + +class AutocompleteSystem { +public: + AutocompleteSystem(vector sentences, vector times) : cur_node_(&trie_) { + for (int i = 0; i < sentences.size(); ++i) { + sentence_to_count_[sentences[i]] = times[i]; + trie_.insert(sentences[i], sentence_to_count_[sentences[i]]); + } + } + + vector input(char c) { + vector result; + if (c == '#') { + ++sentence_to_count_[search_]; + trie_.insert(search_, sentence_to_count_[search_]); + cur_node_ = &trie_; + search_.clear(); + } else { + search_.push_back(c); + if (cur_node_) { + if (!cur_node_->leaves_.count(c)) { + cur_node_ = nullptr; + return {}; + } + cur_node_ = cur_node_->leaves_[c]; + for (const auto& p : cur_node_->infos_) { + result.emplace_back(p.second); + } + } + } + return result; + } + +private: + class TrieNode { + public: + static const int TOP_COUNT = 3; + + ~TrieNode() { + for (auto& kv : leaves_) { + if (kv.second) { + delete kv.second; + } + } + } + + // Time: O(s) + void insert(const string& s, int times) { + auto* cur = this; + cur->add_info(s, times); + for (const auto& c : s) { + if (!cur->leaves_.count(c)) { + cur->leaves_[c] = new TrieNode; + } + cur = cur->leaves_[c]; + cur->add_info(s, times); + } + } + + // Time: O(1) + void add_info(const string& s, int times) { + auto it = find_if(infos_.begin(), infos_.end(), + [&s, ×](const pair& p){ return p.second == s;} ); + if (it != infos_.end()) { + it->first = -times; + } else { + infos_.emplace_back(-times, s); + } + sort(infos_.begin(), infos_.end()); + if (infos_.size() > TOP_COUNT) { + infos_.pop_back(); + } + } + + vector> infos_; + unordered_map leaves_; + }; + + TrieNode trie_; + TrieNode *cur_node_; + string search_; + unordered_map sentence_to_count_; +}; + +/** + * Your AutocompleteSystem object will be instantiated and called as such: + * AutocompleteSystem obj = new AutocompleteSystem(sentences, times); + * vector param_1 = obj.input(c); + */ \ No newline at end of file diff --git a/C++/design-snake-game.cpp b/C++/design-snake-game.cpp new file mode 100644 index 000000000..432e19114 --- /dev/null +++ b/C++/design-snake-game.cpp @@ -0,0 +1,67 @@ +// Time: O(1) per move +// Space: O(s), s is the current length of the snake. + +class SnakeGame { +public: + /** Initialize your data structure here. + @param width - screen width + @param height - screen height + @param food - A list of food positions + E.g food = [[1,1], [1,0]] means the first food is positioned at [1,1], the second is at [1,0]. */ + SnakeGame(int width, int height, vector> food) : + width_{width}, height_{height}, score_{0}, + food_{food.begin(), food.end()}, snake_{{0, 0}} { + + lookup_.emplace(0); + } + + /** Moves the snake. + @param direction - 'U' = Up, 'L' = Left, 'R' = Right, 'D' = Down + @return The game's score after the move. Return -1 if game over. + Game over when snake crosses the screen boundary or bites its body. */ + int move(string direction) { + const auto x = snake_.back().first + direction_[direction].first; + const auto y = snake_.back().second + direction_[direction].second; + const auto tail = snake_.back(); + + auto it = lookup_.find(hash(snake_.front().first, snake_.front().second)); + lookup_.erase(it); + snake_.pop_front(); + if (!valid(x, y)) { + return -1; + } else if (!food_.empty() && food_.front().first == x && food_.front().second == y) { + ++score_; + food_.pop_front(); + snake_.push_front(tail); + lookup_.emplace(hash(tail.first, tail.second)); + } + snake_.push_back({x, y}); + lookup_.emplace(hash(x, y)); + return score_; + } + +private: + bool valid(int x, int y) { + if (x < 0 || x >= height_ || y < 0 || y >= width_) { + return false; + } + return lookup_.find(hash(x, y)) == lookup_.end(); + } + + int hash(int x, int y) { + return x * width_ + y; + } + + int width_, height_, score_; + deque> food_, snake_; + unordered_multiset lookup_; + unordered_map> direction_ = {{"U", {-1, 0}}, {"L", {0, -1}}, + {"R", {0, 1}}, {"D", {1, 0}}}; +}; + +/** + * Your SnakeGame object will be instantiated and called as such: + * SnakeGame obj = new SnakeGame(width, height, food); + * int param_1 = obj.move(direction); + */ + diff --git a/C++/design-tic-tac-toe.cpp b/C++/design-tic-tac-toe.cpp new file mode 100644 index 000000000..c1c98efb0 --- /dev/null +++ b/C++/design-tic-tac-toe.cpp @@ -0,0 +1,46 @@ +// Time: O(1), per move. +// Space: O(n^2) + +class TicTacToe { +public: + /** Initialize your data structure here. */ + TicTacToe(int n) : rows_(n, {0, 0}), cols_(n, {0, 0}), + diagonal_(2, 0), anti_diagonal_(2, 0) { + } + + /** Player {player} makes a move at ({row}, {col}). + @param row The row of the board. + @param col The column of the board. + @param player The player, can be either 1 or 2. + @return The current winning condition, can be either: + 0: No one wins. + 1: Player 1 wins. + 2: Player 2 wins. */ + int move(int row, int col, int player) { + const auto i = player - 1; + ++rows_[row][i], ++cols_[col][i]; + if (row == col) { + ++diagonal_[i]; + } + if (col == rows_.size() - row - 1) { + ++anti_diagonal_[i]; + } + if (rows_[row][i] == rows_.size() || + cols_[col][i] == cols_.size() || + diagonal_[i] == rows_.size() || + anti_diagonal_[i] == cols_.size()) { + return player; + } + return 0; + } + +private: + vector> rows_, cols_; + vector diagonal_, anti_diagonal_; +}; + +/** + * Your TicTacToe object will be instantiated and called as such: + * TicTacToe obj = new TicTacToe(n); + * int param_1 = obj.move(row,col,player); + */ diff --git a/C++/design-twitter.cpp b/C++/design-twitter.cpp new file mode 100644 index 000000000..5a37925f6 --- /dev/null +++ b/C++/design-twitter.cpp @@ -0,0 +1,82 @@ +// Time: O(klogu), k is most recently number of tweets, +// u is the number of the user's following. +// Space: O(t + f), t is the total number of tweets, +// f is the total number of followings. + +class Twitter { +public: + /** Initialize your data structure here. */ + Twitter() : time_(0) { + + } + + /** Compose a new tweet. */ + void postTweet(int userId, int tweetId) { + messages_[userId].emplace_back(make_pair(++time_, tweetId)); + } + + /** Retrieve the 10 most recent tweet ids in the user's news feed. Each item in the news feed must be posted by users who the user followed or by the user herself. Tweets must be ordered from most recent to least recent. */ + vector getNewsFeed(int userId) { + using RIT = deque>::reverse_iterator; + priority_queue> heap; + + if (messages_[userId].size()) { + heap.emplace(make_tuple(messages_[userId].rbegin()->first, + messages_[userId].rbegin(), + messages_[userId].rend())); + } + for (const auto& id : followings_[userId]) { + if (messages_[id].size()) { + heap.emplace(make_tuple(messages_[id].rbegin()->first, + messages_[id].rbegin(), + messages_[id].rend())); + } + } + vector res; + while (!heap.empty() && res.size() < number_of_most_recent_tweets_) { + const auto& top = heap.top(); + size_t t; + RIT begin, end; + tie(t, begin, end) = top; + heap.pop(); + + auto next = begin + 1; + if (next != end) { + heap.emplace(make_tuple(next->first, next, end)); + } + + res.emplace_back(begin->second); + } + return res; + } + + /** Follower follows a followee. If the operation is invalid, it should be a no-op. */ + void follow(int followerId, int followeeId) { + if (followerId != followeeId && !followings_[followerId].count(followeeId)) { + followings_[followerId].emplace(followeeId); + } + } + + /** Follower unfollows a followee. If the operation is invalid, it should be a no-op. */ + void unfollow(int followerId, int followeeId) { + if (followings_[followerId].count(followeeId)) { + followings_[followerId].erase(followeeId); + } + } + +private: + const size_t number_of_most_recent_tweets_ = 10; + unordered_map> followings_; + unordered_map>> messages_; + size_t time_; +}; + +/** + * Your Twitter object will be instantiated and called as such: + * Twitter obj = new Twitter(); + * obj.postTweet(userId,tweetId); + * vector param_2 = obj.getNewsFeed(userId); + * obj.follow(followerId,followeeId); + * obj.unfollow(followerId,followeeId); + */ + diff --git a/C++/detect-capital.cpp b/C++/detect-capital.cpp new file mode 100644 index 000000000..59f287a8f --- /dev/null +++ b/C++/detect-capital.cpp @@ -0,0 +1,10 @@ +// Time: O(l) +// Space: O(1) + +class Solution { +public: + bool detectCapitalUse(string word) { + int count = count_if(word.begin(), word.end(), [](char c){ return isupper(c); }); + return count == word.length() || count == 0 || (count == 1 && isupper(word[0])); + } +}; diff --git a/C++/detectCycle.cpp b/C++/detectCycle.cpp deleted file mode 100644 index 3e066a2da..000000000 --- a/C++/detectCycle.cpp +++ /dev/null @@ -1,33 +0,0 @@ -// Time Complexity: O(n) -// Space Complexity: O(1) - -/** - * Definition for singly-linked list. - * struct ListNode { - * int val; - * ListNode *next; - * ListNode(int x) : val(x), next(NULL) {} - * }; - */ -class Solution { - public: - ListNode *detectCycle(ListNode *head) { - ListNode *slow = head, *fast = head; - - while(fast && fast->next) { - slow = slow->next; - fast = fast->next->next; - - if(slow == fast) { - ListNode *slow2 = head; - while(slow2 != slow) { - slow2 = slow2->next; - slow = slow->next; - } - return slow2; - } - } - - return NULL; - } -}; diff --git a/C++/diagonal-traverse.cpp b/C++/diagonal-traverse.cpp new file mode 100644 index 000000000..989cc0aa6 --- /dev/null +++ b/C++/diagonal-traverse.cpp @@ -0,0 +1,39 @@ +// Time: O(m * n) +// Space: O(1) + +class Solution { +public: + vector findDiagonalOrder(vector>& matrix) { + if (matrix.empty() || matrix[0].empty()) { + return {}; + } + + vector result; + int row = 0, col = 0, d = 0; + vector> dirs = {{-1, 1}, {1, -1}}; + + for (int i = 0; i < matrix.size() * matrix[0].size(); ++i) { + result.emplace_back(matrix[row][col]); + row += dirs[d][0]; + col += dirs[d][1]; + + if (row >= static_cast(matrix.size())) { + row = matrix.size() - 1; + col += 2; + d = 1 - d; + } else if (col >= static_cast(matrix[0].size())) { + col = matrix[0].size() - 1; + row += 2; + d = 1 - d; + } else if (row < 0) { + row = 0; + d = 1 - d; + } else if (col < 0) { + col = 0; + d = 1 - d; + } + } + + return result; + } +}; diff --git a/C++/diameter-of-binary-tree.cpp b/C++/diameter-of-binary-tree.cpp new file mode 100644 index 000000000..74c1199db --- /dev/null +++ b/C++/diameter-of-binary-tree.cpp @@ -0,0 +1,31 @@ +// Time: O(n) +// Space: O(h) + +/** + * 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: + int diameterOfBinaryTree(TreeNode* root) { + int diameter = 0; + depth(root, &diameter); + return diameter; + } + +private: + int depth(TreeNode *root, int *diameter) { + if (!root) { + return 0; + } + auto left = depth(root->left, diameter); + auto right = depth(root->right, diameter); + *diameter = max(*diameter, left + right); + return 1 + max(left, right); + } +}; diff --git a/C++/different-ways-to-add-parentheses.cpp b/C++/different-ways-to-add-parentheses.cpp new file mode 100644 index 000000000..91f62e9ae --- /dev/null +++ b/C++/different-ways-to-add-parentheses.cpp @@ -0,0 +1,76 @@ +// Time: O(n * (C(2n, n) - C(2n, n - 1))), this is at most +// Space: O(n * (C(2n, n) - C(2n, n - 1))) + +class Solution { + public: + vector diffWaysToCompute(string input) { + vector>> lookup(input.length() + 1, + vector>(input.length() + 1)); + return diffWaysToComputeRecu(input, 0, input.length(), lookup); + } + + vector diffWaysToComputeRecu(const string& input, + const int start, const int end, + vector>>& lookup) { + if (!lookup[start][end].empty()) { + return lookup[start][end]; + } + vector result; + for (int i = start; i < end; ++i) { + const auto cur = input[i]; + if (cur == '+' || cur == '-' || cur == '*') { + auto left = diffWaysToComputeRecu(input, start, i, lookup); + auto right = diffWaysToComputeRecu(input, i + 1, end, lookup); + for (const auto& num1 : left) { + for (const auto& num2 : right) { + if (cur == '+') { + result.emplace_back(num1 + num2); + } else if (cur == '-') { + result.emplace_back(num1 - num2); + } else { + result.emplace_back(num1 * num2); + } + } + } + } + } + // If the input string contains only number. + if (result.empty()) { + result.emplace_back(stoi(input.substr(start, end - start))); + } + lookup[start][end] = result; + return lookup[start][end]; + } +}; + +// Time: O(n * (C(2n, n) - C(2n, n - 1))), this is at least +// Space: O(C(2n, n) - C(2n, n - 1)) +class Solution2 { + public: + vector diffWaysToCompute(string input) { + vector result; + for (int i = 0; i < input.length(); ++i) { + const auto cur = input[i]; + if (cur == '+' || cur == '-' || cur == '*') { + auto left = diffWaysToCompute(input.substr(0, i)); + auto right = diffWaysToCompute(input.substr(i + 1)); + for (const auto& num1 : left) { + for (const auto& num2 : right) { + if (cur == '+') { + result.emplace_back(num1 + num2); + } else if (cur == '-') { + result.emplace_back(num1 - num2); + } else { + result.emplace_back(num1 * num2); + } + } + } + } + } + // If the input string contains only number. + if (result.empty()) { + result.emplace_back(stoi(input)); + } + return result; + } +}; diff --git a/C++/distribute-candies.cpp b/C++/distribute-candies.cpp new file mode 100644 index 000000000..ffbda99da --- /dev/null +++ b/C++/distribute-candies.cpp @@ -0,0 +1,13 @@ +// Time: O(n) +// Space: O(n) + +class Solution { +public: + int distributeCandies(vector& candies) { + unordered_set lookup; + for (const auto& candy: candies) { + lookup.emplace(candy); + } + return min(lookup.size(), candies.size() / 2); + } +}; diff --git a/C++/divide-two-integers.cpp b/C++/divide-two-integers.cpp new file mode 100644 index 000000000..79baca979 --- /dev/null +++ b/C++/divide-two-integers.cpp @@ -0,0 +1,78 @@ +// Time: O(logn) = O(1) +// Space: O(1) + +// Only using integer type. +class Solution { +public: + int divide(int dividend, int divisor) { + // Handle corner case. + if (dividend == numeric_limits::min() && divisor == -1) { + return numeric_limits::max(); + } + + int a = dividend > 0 ? -dividend : dividend; + int b = divisor > 0 ? -divisor : divisor; + + int shift = 0; + while (b << shift < 0 && shift < 32) { + ++shift; + } + shift -= 1; + + int result = 0; + while (shift >= 0) { + if (a <= b << shift) { + a -= b << shift; + result += 1 << shift; + } + --shift; + } + + result = (dividend ^ divisor) >> 31 ? -result : result; + return result; + } +}; + +// Time: O(logn) = O(1) +// Space: O(1) +// Using long long type. +class Solution2 { +public: + int divide(int dividend, int divisor) { + long long result = 0; + long long a = llabs(dividend); + long long b = llabs(divisor); + + int shift = 31; + while (shift >= 0) { + if (a >= b << shift) { + a -= b << shift; + result += 1LL << shift; + } + --shift; + } + + result = ((dividend ^ divisor) >> 31) ? -result : result; + return min(result, static_cast(numeric_limits::max())); + } +}; + +// Time: O(logn) = O(1) +// Space: O(1) +// a / b = exp^(ln(a) - ln(b)) +class Solution3 { +public: + int divide(int dividend, int divisor) { + if (dividend == 0) { + return 0; + } + if (divisor == 0) { + return numeric_limits::max(); + } + + long long result = exp(log(fabs(dividend)) - log(fabs(divisor))); + + result = ((dividend ^ divisor) >> 31) ? -result : result; + return min(result, static_cast(numeric_limits::max())); + } +}; diff --git a/C++/divide.cpp b/C++/divide.cpp deleted file mode 100644 index 988507e84..000000000 --- a/C++/divide.cpp +++ /dev/null @@ -1,23 +0,0 @@ -// Time Complexity: O(logn) -// Space Complexity: O(1) - -class Solution { - public: - int divide(int dividend, int divisor) { - long long a = dividend >= 0 ? dividend : -(long long)dividend; - long long b = divisor >= 0 ? divisor : -(long long)divisor; - - long long result = 0; - while(a >= b) { - long long c = b; - int i = 0; - for(; a >= c; c <<= 1, ++i); - if(i > 0) { - a -= c >> 1; - result += 1 << (i - 1); - } - } - - return ((dividend ^ divisor) >> 31)? -result : result; - } -}; diff --git a/C++/domino-and-tromino-tiling.cpp b/C++/domino-and-tromino-tiling.cpp new file mode 100644 index 000000000..73337cdeb --- /dev/null +++ b/C++/domino-and-tromino-tiling.cpp @@ -0,0 +1,68 @@ +// Time: O(logn) +// Space: O(logn) + +class Solution { +public: + int numTilings(int N) { + vector> T{{1, 0, 0, 1}, // #(|) = #(|) + #(=) + {1, 0, 1, 0}, // #(「) = #(|) + #(L) + {1, 1, 0, 0}, // #(L) = #(|) + #(「) + {1, 1, 1, 0}}; // #(=) = #(|) + #(「) + #(L) + return matrixExpo(T, N)[0][0]; // T^N * [1, 0, 0, 0] + } + +private: + vector> matrixExpo(const vector>& A, int pow) { + if (pow == 0) { + vector> I(A.size(), vector(A.size())); + for (int i = 0; i < A.size(); ++i) { + I[i][i] = 1; + } + return I; + } + if (pow == 1) { + return A; + } + if (pow % 2 == 1) { + return matrixMult(matrixExpo(A, pow - 1), A); + } + const auto& B = matrixExpo(A, pow / 2); + return matrixMult(B, B); + } + + vector> matrixMult(const vector>& A, const vector>& B) { + vector> result(A.size(), vector(A.size())); + for (int i = 0; i < A.size(); ++i) { + for (int j = 0; j < B[0].size(); ++j) { + int64_t entry = 0; + for (int k = 0; k < B.size(); ++k) { + entry = (static_cast(A[i][k]) * B[k][j] % M + entry) % M; + } + result[i][j] = static_cast(entry); + } + } + return result; + } + const int M = 1e9 + 7; +}; + +// Time: O(n) +// Space: O(1) +class Solution2 { +public: + int numTilings(int N) { + // Prove: + // dp[n] = dp[n-1](|) + dp[n-2](=) + 2*(dp[n-3](「」) + ... + d[0](「 = ... = 」)) + // = dp[n-1] + dp[n-2] + dp[n-3] + dp[n-3] + 2*(dp[n-4] + ... + d[0]) + // = dp[n-1] + dp[n-3] + (dp[n-2] + dp[n-3] + 2*(dp[n-4] + ... + d[0]) + // = dp[n-1] + dp[n-3] + dp[n-1] + // = 2*dp[n-1] + dp[n-3] + const int M = 1e9 + 7; + vector dp(3); + dp[0] = 1, dp[1] = 1, dp[2] = 2; + for (int i = 3; i <= N; ++i) { + dp[i % 3] = (2 * dp[(i - 1) % 3] % M + dp[(i - 3) % 3]) % M; + } + return dp[N % 3]; + } +}; diff --git a/C++/dota2-senate.cpp b/C++/dota2-senate.cpp new file mode 100644 index 000000000..e81597cce --- /dev/null +++ b/C++/dota2-senate.cpp @@ -0,0 +1,19 @@ +// Time: O(n) +// Space: O(n) + +class Solution { +public: + string predictPartyVictory(string senate) { + queue radiant, dire; + const auto& n = senate.length(); + for (int i = 0; i < n; ++i) { + (senate[i] == 'R') ? radiant.emplace(i) : dire.emplace(i); + } + while (!radiant.empty() && !dire.empty()) { + int r_idx = radiant.front(), d_idx = dire.front(); + radiant.pop(), dire.pop(); + (r_idx < d_idx) ? radiant.emplace(r_idx + n) : dire.emplace(d_idx + n); + } + return (radiant.size() > dire.size()) ? "Radiant" : "Dire"; + } +}; diff --git a/C++/elimination-game.cpp b/C++/elimination-game.cpp new file mode 100644 index 000000000..0541d375f --- /dev/null +++ b/C++/elimination-game.cpp @@ -0,0 +1,17 @@ +// Time: O(logn) +// Space: O(1) + +class Solution { +public: + int lastRemaining(int n) { + int start = 1; + + for (int step = 2, direction = 1; n > 1; + n /= 2, step *= 2, direction *= -1) { + + start += direction * (step * (n / 2) - step / 2); + } + + return start; + } +}; diff --git a/C++/employee-free-time.cpp b/C++/employee-free-time.cpp new file mode 100644 index 000000000..85cc530db --- /dev/null +++ b/C++/employee-free-time.cpp @@ -0,0 +1,37 @@ +// Time: O(m * logn), m is the number of schedule, n is the number of employees, m >= n +// Space: O(n) + +/** + * Definition for an interval. + * struct Interval { + * int start; + * int end; + * Interval() : start(0), end(0) {} + * Interval(int s, int e) : start(s), end(e) {} + * }; + */ +class Solution { +public: + vector employeeFreeTime(vector>& schedule) { + vector result; + using P = pair>; + priority_queue, greater

> min_heap; + for (int i = 0; i < schedule.size(); ++i) { + min_heap.emplace(schedule[i][0].start, make_pair(i, 0)); + } + int last_end = -1; + while (!min_heap.empty()) { + int t; + pair p; + tie(t, p) = min_heap.top(); min_heap.pop(); + if (0 <= last_end && last_end < t) { + result.emplace_back(last_end, t); + } + last_end = max(last_end, schedule[p.first][p.second].end); + if (p.second + 1 < schedule[p.first].size()) { + min_heap.emplace(schedule[p.first][p.second + 1].start, make_pair(p.first, p.second + 1)); + } + } + return result; + } +}; diff --git a/C++/employee-importance.cpp b/C++/employee-importance.cpp new file mode 100644 index 000000000..85c48ae24 --- /dev/null +++ b/C++/employee-importance.cpp @@ -0,0 +1,54 @@ +// Time: O(n) +// Space: O(h) + +/* +// Employee info +class Employee { +public: + // It's the unique ID of each node. + // unique id of this employee + int id; + // the importance value of this employee + int importance; + // the id of direct subordinates + vector subordinates; +}; +*/ +class Solution { +public: + int getImportance(vector employees, int id) { + return dfs(employees, id); + } + +private: + int dfs(const vector& employees, const int id) { + if (!employees[id - 1]) { + return 0; + } + auto result = employees[id - 1]->importance; + for (const auto& id : employees[id - 1]->subordinates) { + result += getImportance(employees, id); + } + return result; + } +}; + +// Time: O(n) +// Space: O(w), w is the max number of nodes in the levels of the tree +class Solution2 { +public: + int getImportance(vector employees, int id) { + auto result = 0; + queue q; + q.emplace(id); + while (!q.empty()) { + const auto curr = q.front(); q.pop(); + const auto& employee = employees[curr - 1]; + result += employee->importance; + for (const auto& id : employee->subordinates) { + q.emplace(id); + } + } + return result; + } +}; diff --git a/C++/encode-and-decode-strings.cpp b/C++/encode-and-decode-strings.cpp new file mode 100644 index 000000000..0722b0c5d --- /dev/null +++ b/C++/encode-and-decode-strings.cpp @@ -0,0 +1,42 @@ +// Time: O(n) +// Space: O(1) + +class Codec { +public: + + // Encodes a list of strings to a single string. + string encode(vector& strs) { + string s; + for (size_t i = 0; i < strs.size(); ++i) { + size_t len = strs[i].length(); + string tmp; + for (size_t i = 0, mask = 0xff; i < sizeof(size_t); ++i, mask <<= 8) { + tmp.push_back(len & mask); + } + reverse(tmp.begin(), tmp.end()); + s.append(tmp); + s.append(strs[i]); + } + + return s; + } + + // Decodes a single string to a list of strings. + vector decode(string s) { + vector strs; + size_t pos = 0; + + while (pos + sizeof(size_t) <= s.length()) { + size_t len = 0; + for (size_t i = 0; i < sizeof(size_t); ++i) { + len <<= 8; + len += static_cast(s[pos++]); + } + + strs.push_back(s.substr(pos, len)); + pos += len; + } + + return strs; + } +}; diff --git a/C++/encode-and-decode-tinyurl.cpp b/C++/encode-and-decode-tinyurl.cpp new file mode 100644 index 000000000..8a5cb1d12 --- /dev/null +++ b/C++/encode-and-decode-tinyurl.cpp @@ -0,0 +1,42 @@ +// Time: O(1) +// Space: O(n) + +class Solution { +public: + Solution() : gen_((random_device())()) { + } + + // Encodes a URL to a shortened URL. + string encode(string longUrl) { + string key = getRand(); + while (lookup_.count(key)) { + key = getRand(); + } + lookup_[key] = longUrl; + return "http://tinyurl.com/" + key; + } + + // Decodes a shortened URL to its original URL. + string decode(string shortUrl) { + return lookup_[shortUrl.substr(tiny_url.length())]; + } + +private: + string getRand() { + string random; + for (int i = 0; i < random_length; ++i) { + random += alphabet_[uniform_int_distribution{0, alphabet_.length() - 1}(gen_)]; + } + return random; + } + + const int random_length = 6; + const string tiny_url = "http://tinyurl.com/"; + const string alphabet_ = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; + default_random_engine gen_; + unordered_map lookup_; +}; + +// Your Solution object will be instantiated and called as such: +// Solution solution; +// solution.decode(solution.encode(url)); diff --git a/C++/encode-n-ary-tree-to-binary-tree.cpp b/C++/encode-n-ary-tree-to-binary-tree.cpp new file mode 100644 index 000000000..b6dc5c03a --- /dev/null +++ b/C++/encode-n-ary-tree-to-binary-tree.cpp @@ -0,0 +1,83 @@ +// Time: O(n) +// Space: O(h) + +/* +// Definition for a Node. +class Node { +public: + int val = NULL; + vector children; + + Node() {} + + Node(int _val, vector _children) { + val = _val; + children = _children; + } +}; +*/ +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode(int x) : val(x), left(NULL), right(NULL) {} + * }; + */ +class Codec { +public: + + // Encodes an n-ary tree to a binary tree. + TreeNode* encode(Node* root) { + if (root == nullptr) { + return nullptr; + } + auto node = new TreeNode(root->val); + if (!root->children.empty()) { + node->right = encodeHelper(root->children[0], root, 0); + } + return node; + } + + // Decodes your binary tree to an n-ary tree. + Node* decode(TreeNode* root) { + if (root == nullptr) { + return nullptr; + } + vector children; + auto node = new Node(root->val, children); + decodeHelper(root->right, node); + return node; + } + +private: + TreeNode *encodeHelper(Node *root, Node *parent, int index) { + if (root == nullptr) { + return nullptr; + } + auto node = new TreeNode(root->val); + if (index + 1 < parent->children.size()) { + node->left = encodeHelper(parent->children[index + 1], parent, index + 1); + } + if (!root->children.empty()) { + node->right = encodeHelper(root->children[0], root, 0); + } + return node; + } + + void decodeHelper(TreeNode* root, Node* parent) { + if (!root) { + return; + } + vector children; + auto node = new Node(root->val, children); + decodeHelper(root->right, node); + parent->children.push_back(node); + decodeHelper(root->left, parent); + } +}; + +// Your Codec object will be instantiated and called as such: +// Codec codec; +// codec.decode(codec.encode(root)); diff --git a/C++/encode-string-with-shortest-length.cpp b/C++/encode-string-with-shortest-length.cpp new file mode 100644 index 000000000..44c312b7e --- /dev/null +++ b/C++/encode-string-with-shortest-length.cpp @@ -0,0 +1,35 @@ +// Time: O(n^3) on average +// Space: O(n^2) + +class Solution { +public: + string encode(string s) { + vector> dp(s.length(), vector(s.length())); + for (int len = 1; len <= s.length(); ++len) { + for (int i = 0; i + len - 1 < s.length(); ++i) { + int j = i + len - 1; + dp[i][j] = s.substr(i, len); + for (int k = i; k < j; ++k) { + if (dp[i][k].length() + dp[k + 1][j].length() < dp[i][j].length()) { + dp[i][j] = dp[i][k] + dp[k + 1][j]; + } + } + string encoded_string = encode_substr(dp, s, i, j); + if (encoded_string.length() < dp[i][j].length()) { + dp[i][j] = encoded_string; + } + } + } + return dp[0][s.length() - 1]; + } + +private: + string encode_substr(const vector>& dp, const string& s, int i, int j) { + string temp = s.substr(i, j - i + 1); + auto pos = (temp + temp).find(temp, 1); // O(n) on average + if (pos >= temp.length()) { + return temp; + } + return to_string(temp.length() / pos) + '[' + dp[i][i + pos - 1] + ']'; + } +}; diff --git a/C++/equal-tree-partition.cpp b/C++/equal-tree-partition.cpp new file mode 100644 index 000000000..2555131fd --- /dev/null +++ b/C++/equal-tree-partition.cpp @@ -0,0 +1,35 @@ +// Time: O(n) +// Space: O(n) + +/** + * 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: + bool checkEqualTree(TreeNode* root) { + unordered_map lookup; + auto total = getSumHelper(root, &lookup); + if (total == 0) { + return lookup[total] > 1; + } + return total % 2 == 0 && lookup.count(total / 2); + } + +private: + int getSumHelper(TreeNode* node, unordered_map *lookup) { + if (!node) { + return 0; + } + int total = node->val + + getSumHelper(node->left, lookup) + + getSumHelper(node->right, lookup); + ++(*lookup)[total]; + return total; + } +}; diff --git a/C++/erect-the-fence.cpp b/C++/erect-the-fence.cpp new file mode 100644 index 000000000..e3dd4133a --- /dev/null +++ b/C++/erect-the-fence.cpp @@ -0,0 +1,56 @@ +// Time: O(nlogn) +// Space: O(n) + +/** + * Definition for a point. + * struct Point { + * int x; + * int y; + * Point() : x(0), y(0) {} + * Point(int a, int b) : x(a), y(b) {} + * }; + */ + +// Monotone Chain Algorithm +class Solution { +public: + vector outerTrees(vector& points) { + const auto orientation = [](const Point& p, const Point& q, const Point& r) { + return (q.y - p.y) * (r.x - q.x) - + (q.x - p.x) * (r.y - q.y); + }; + const auto cmp = [](const Point& p, const Point& q) { + return p.x == q.x ? p.y < q.y : p.x < q.x; + }; + const auto eq = [](const Point &p1, const Point &p2) { + return p1.x == p2.x && p1.y == p2.y; + }; + + vector hull; + sort(points.begin(), points.end(), cmp); + + for (int i = 0; i < points.size(); ++i) { + while (hull.size() >= 2 && + orientation(hull[hull.size() - 2], + hull[hull.size() - 1], + points[i]) > 0) { + hull.pop_back(); + } + hull.emplace_back(points[i]); + } + + for (int i = points.size() - 1; i >= 0; --i) { + while (hull.size() >= 2 && + orientation(hull[hull.size() - 2], + hull[hull.size() - 1], + points[i]) > 0) { + hull.pop_back(); + } + hull.emplace_back(points[i]); + } + + sort(hull.begin(), hull.end(), cmp); + hull.erase(unique(hull.begin(), hull.end(), eq), hull.end()); + return hull; + } +}; diff --git a/C++/escape-the-ghosts.cpp b/C++/escape-the-ghosts.cpp new file mode 100644 index 000000000..cb8b72eb3 --- /dev/null +++ b/C++/escape-the-ghosts.cpp @@ -0,0 +1,13 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + bool escapeGhosts(vector>& ghosts, vector& target) { + const auto& total = abs(target[0]) + abs(target[1]); + return all_of(ghosts.cbegin(), ghosts.cend(), + [&target, &total](const vector& ghost) { + return total < abs(target[0] - ghost[0]) + abs(target[1] - ghost[1]); + }); + } +}; diff --git a/C++/evalRPN.cpp b/C++/evalRPN.cpp deleted file mode 100644 index 6f04b0e26..000000000 --- a/C++/evalRPN.cpp +++ /dev/null @@ -1,30 +0,0 @@ -// Time Space: O(n) -// Space Space: O(logn) - -class Solution { - public: - int evalRPN(vector &tokens) { - stack s; - for(auto tok : tokens) { - if(!is_operator(tok)) { - s.push(tok); - } - else { - int y = stoi(s.top()); - s.pop(); - int x = stoi(s.top()); - s.pop(); - if(tok[0] == '+') x += y; - else if (tok[0] == '-') x -= y; - else if (tok[0] == '*') x *= y; - else x /= y; - s.push(to_string(x)); - } - } - return stoi(s.top()); - } - private: - bool is_operator(const string &op) { - return op.length() == 1 && string("+-*/").find(op) != string::npos; - } -}; diff --git a/C++/evaluate-division.cpp b/C++/evaluate-division.cpp new file mode 100644 index 000000000..7db86be31 --- /dev/null +++ b/C++/evaluate-division.cpp @@ -0,0 +1,48 @@ +// Time: O(e + q * |V|!), |V| is the number of variables +// Space: O(e) + +class Solution { +public: + vector calcEquation(vector> equations, + vector& values, vector> query) { + + unordered_map> lookup; + for (int i = 0; i < values.size(); ++i) { + lookup[equations[i].first].emplace(equations[i].second, values[i]); + if (values[i] != 0) { + lookup[equations[i].second].emplace(equations[i].first, 1 / values[i]); + } + } + + vector result; + for (const auto& i : query) { + unordered_set visited; + const auto tmp = check(i.first, i.second, lookup, &visited); + if (tmp.first) { + result.emplace_back(tmp.second); + } else { + result.emplace_back(-1); + } + } + return result; + } + +private: + pair check(string up, string down, + unordered_map> &lookup, + unordered_set *visited) { + if (lookup[up].find(down) != lookup[up].end()) { + return {true, lookup[up][down]}; + } + for (const auto& q : lookup[up]) { + if (!visited->count(q.first)) { + visited->emplace(q.first); + const auto tmp = check(q.first, down, lookup, visited); + if (tmp.first) { + return {true, q.second * tmp.second}; + } + } + } + return {false, 0}; + } +}; diff --git a/C++/evaluate-reverse-polish-notation.cpp b/C++/evaluate-reverse-polish-notation.cpp new file mode 100644 index 000000000..6d1b33020 --- /dev/null +++ b/C++/evaluate-reverse-polish-notation.cpp @@ -0,0 +1,38 @@ +// Time: O(n) +// Space: O(n) + +class Solution { +public: + int evalRPN(vector& tokens) { + if (tokens.empty()) { + return 0; + } + stack s; + for (const auto& tok : tokens) { + if (!is_operator(tok)) { + s.emplace(tok); + } else { + auto&& y = stoi(s.top()); + s.pop(); + auto&& x = stoi(s.top()); + s.pop(); + if (tok[0] == '+') { + x += y; + } else if (tok[0] == '-') { + x -= y; + } else if (tok[0] == '*') { + x *= y; + } else { + x /= y; + } + s.emplace(to_string(x)); + } + } + return stoi(s.top()); + } + +private: + bool is_operator(const string& op) { + return op.length() == 1 && string("+-*/").find(op) != string::npos; + } +}; diff --git a/C++/exam-room.cpp b/C++/exam-room.cpp new file mode 100644 index 000000000..91ef117a4 --- /dev/null +++ b/C++/exam-room.cpp @@ -0,0 +1,337 @@ +// Time: seat: O(logn), +// leave: O(logn) +// Space: O(n) + +class ExamRoom { +public: + ExamRoom(int N) : num_(N) { + segment_iters_[make_pair(-1, num_)] = + max_bst_.emplace(make_shared(num_, 0, -1, num_)); + seats_[-1] = make_pair(-1, num_); + seats_[num_] = make_pair(-1, num_); + } + + int seat() { + const auto curr = *max_bst_.begin(); max_bst_.erase(max_bst_.begin()); + segment_iters_.erase(make_pair(curr->l, curr->r)); + if (curr->l == -1 && curr->r == num_) { + segment_iters_[make_pair(curr->l + 1, curr->r)] = max_bst_.emplace( + make_shared(curr->r - 1, + curr->r - 1, + curr->l + 1, curr->r)); + } else if (curr->l == -1) { + segment_iters_[make_pair(curr->l + 1, curr->r)] = max_bst_.emplace( + make_shared(curr->r / 2, + curr->r / 2, + curr->l + 1, curr->r)); + } else if (curr->r == num_) { + segment_iters_[make_pair(curr->l, curr->r - 1)] = max_bst_.emplace( + make_shared((curr->r - 1 - curr->l) / 2, + (curr->r - 1 - curr->l) / 2 + curr->l, + curr->l, curr->r - 1)); + } else { + segment_iters_[make_pair(curr->l, curr->pos)] = max_bst_.emplace( + make_shared((curr->pos - curr->l) / 2, + (curr->pos - curr->l) / 2 + curr->l, + curr->l, curr->pos)); + segment_iters_[make_pair(curr->pos, curr->r)] = max_bst_.emplace( + make_shared((curr->r - curr->pos) / 2, + (curr->r - curr->pos) / 2 + curr->pos, + curr->pos, curr->r)); + } + seats_[curr->pos] = make_pair(curr->l, curr->r); + seats_[curr->l].second = curr->pos; + seats_[curr->r].first = curr->pos; + return curr->pos; + } + + void leave(int p) { + const auto neighbors = seats_[p]; + seats_.erase(p); + const auto& left_segment = make_pair(neighbors.first, p); + if (segment_iters_.count(left_segment)) { + max_bst_.erase(segment_iters_[left_segment]); segment_iters_.erase(left_segment); + } + const auto& right_segment = make_pair(p, neighbors.second); + if (segment_iters_.count(right_segment)) { + max_bst_.erase(segment_iters_[right_segment]); segment_iters_.erase(right_segment); + } + + if (neighbors.first == -1 && neighbors.second == num_) { + segment_iters_[neighbors] = max_bst_.emplace( + make_shared(neighbors.second, + neighbors.first + 1, + neighbors.first, neighbors.second)); + } else if (neighbors.first == -1) { + segment_iters_[neighbors] = max_bst_.emplace( + make_shared(neighbors.second, + neighbors.first + 1, + neighbors.first, neighbors.second)); + } else if (neighbors.second == num_) { + segment_iters_[neighbors] = max_bst_.emplace( + make_shared(neighbors.second - 1 - neighbors.first, + neighbors.second - 1, + neighbors.first, neighbors.second)); + } else { + segment_iters_[neighbors] = max_bst_.emplace( + make_shared((neighbors.second - neighbors.first) / 2, + (neighbors.second - neighbors.first) / 2 + neighbors.first, + neighbors.first, neighbors.second)); + } + seats_[neighbors.first].second = neighbors.second; + seats_[neighbors.second].first = neighbors.first; + } + +private: + struct Segment { + int dis; + int pos; + int l; + int r; + Segment(int dis, int pos, int l, int r) : + dis(dis), pos(pos), l(l), r(r) { + } + }; + + template + struct Compare { + bool operator()(const T& a, const T& b) { + return a->dis == b->dis ? + less()(a->l, b->l) : + greater()(a->dis, b->dis); + } + }; + + template + struct PairHash { + size_t operator()(const pair& p) const { + size_t seed = 0; + seed ^= std::hash{}(p.first) + 0x9e3779b9 + (seed<<6) + (seed>>2); + seed ^= std::hash{}(p.second) + 0x9e3779b9 + (seed<<6) + (seed>>2); + return seed; + } + }; + + int num_; + using S = shared_ptr; + multiset> max_bst_; + unordered_map> seats_; + unordered_map, multiset>::iterator, PairHash> segment_iters_; +}; + +// Time: seat: O(logn) on average, +// leave: O(logn) +// Space: O(n) +class ExamRoom2 { +public: + ExamRoom2(int N) : num_(N) { + max_bst_.emplace(make_shared(num_, 0, -1, num_)); + seats_[-1] = make_pair(-1, num_); + seats_[num_] = make_pair(-1, num_); + } + + int seat() { + while (!seats_.count((*max_bst_.cbegin())->l) || + !seats_.count((*max_bst_.cbegin())->r) || + seats_[(*max_bst_.cbegin())->l].second != (*max_bst_.cbegin())->r || + seats_[(*max_bst_.cbegin())->r].first != (*max_bst_.cbegin())->l) { + max_bst_.erase(max_bst_.begin()); // lazy deletion + } + + const auto curr = *max_bst_.begin(); max_bst_.erase(max_bst_.begin()); + if (curr->l == -1 && curr->r == num_) { + max_bst_.emplace( + make_shared(curr->r - 1, + curr->r - 1, + curr->l + 1, curr->r)); + } else if (curr->l == -1) { + max_bst_.emplace( + make_shared(curr->r / 2, + curr->r / 2, + curr->l + 1, curr->r)); + } else if (curr->r == num_) { + max_bst_.emplace( + make_shared((curr->r - 1 - curr->l) / 2, + (curr->r - 1 - curr->l) / 2 + curr->l, + curr->l, curr->r - 1)); + } else { + max_bst_.emplace( + make_shared((curr->pos - curr->l) / 2, + (curr->pos - curr->l) / 2 + curr->l, + curr->l, curr->pos)); + max_bst_.emplace( + make_shared((curr->r - curr->pos) / 2, + (curr->r - curr->pos) / 2 + curr->pos, + curr->pos, curr->r)); + } + seats_[curr->pos] = make_pair(curr->l, curr->r); + seats_[curr->l].second = curr->pos; + seats_[curr->r].first = curr->pos; + return curr->pos; + } + + void leave(int p) { + const auto neighbors = seats_[p]; + seats_.erase(p); + if (neighbors.first == -1 && neighbors.second == num_) { + max_bst_.emplace( + make_shared(neighbors.second, + neighbors.first + 1, + neighbors.first, neighbors.second)); + } else if (neighbors.first == -1) { + max_bst_.emplace( + make_shared(neighbors.second, + neighbors.first + 1, + neighbors.first, neighbors.second)); + } else if (neighbors.second == num_) { + max_bst_.emplace( + make_shared(neighbors.second - 1 - neighbors.first, + neighbors.second - 1, + neighbors.first, neighbors.second)); + } else { + max_bst_.emplace( + make_shared((neighbors.second - neighbors.first) / 2, + (neighbors.second - neighbors.first) / 2 + neighbors.first, + neighbors.first, neighbors.second)); + } + seats_[neighbors.first].second = neighbors.second; + seats_[neighbors.second].first = neighbors.first; + } + +private: + struct Segment { + int dis; + int pos; + int l; + int r; + Segment(int dis, int pos, int l, int r) : + dis(dis), pos(pos), l(l), r(r) { + } + }; + + template + struct Compare { + bool operator()(const T& a, const T& b) { + return a->dis == b->dis ? + less()(a->l, b->l) : + greater()(a->dis, b->dis); + } + }; + + int num_; + using S = shared_ptr; + multiset> max_bst_; + unordered_map> seats_; +}; + +// Time: seat: O(logn) on average, +// leave: O(logn) +// Space: O(n) +class ExamRoom3 { +public: + ExamRoom3(int N) : num_(N) { + max_heap_.emplace(make_shared(num_, 0, -1, num_)); + seats_[-1] = make_pair(-1, num_); + seats_[num_] = make_pair(-1, num_); + } + + int seat() { + while (!seats_.count(max_heap_.top()->l) || + !seats_.count(max_heap_.top()->r) || + seats_[max_heap_.top()->l].second != max_heap_.top()->r || + seats_[max_heap_.top()->r].first != max_heap_.top()->l) { + max_heap_.pop(); // lazy deletion + } + + const auto curr = max_heap_.top(); max_heap_.pop(); + if (curr->l == -1 && curr->r == num_) { + max_heap_.emplace( + make_shared(curr->r - 1, + curr->r - 1, + curr->l + 1, curr->r)); + } else if (curr->l == -1) { + max_heap_.emplace( + make_shared(curr->r / 2, + curr->r / 2, + curr->l + 1, curr->r)); + } else if (curr->r == num_) { + max_heap_.emplace( + make_shared((curr->r - 1 - curr->l) / 2, + (curr->r - 1 - curr->l) / 2 + curr->l, + curr->l, curr->r - 1)); + } else { + max_heap_.emplace( + make_shared((curr->pos - curr->l) / 2, + (curr->pos - curr->l) / 2 + curr->l, + curr->l, curr->pos)); + max_heap_.emplace( + make_shared((curr->r - curr->pos) / 2, + (curr->r - curr->pos) / 2 + curr->pos, + curr->pos, curr->r)); + } + seats_[curr->pos] = make_pair(curr->l, curr->r); + seats_[curr->l].second = curr->pos; + seats_[curr->r].first = curr->pos; + return curr->pos; + } + + void leave(int p) { + const auto neighbors = seats_[p]; + seats_.erase(p); + if (neighbors.first == -1 && neighbors.second == num_) { + max_heap_.emplace( + make_shared(neighbors.second, + neighbors.first + 1, + neighbors.first, neighbors.second)); + } else if (neighbors.first == -1) { + max_heap_.emplace( + make_shared(neighbors.second, + neighbors.first + 1, + neighbors.first, neighbors.second)); + } else if (neighbors.second == num_) { + max_heap_.emplace( + make_shared(neighbors.second - 1 - neighbors.first, + neighbors.second - 1, + neighbors.first, neighbors.second)); + } else { + max_heap_.emplace( + make_shared((neighbors.second - neighbors.first) / 2, + (neighbors.second - neighbors.first) / 2 + neighbors.first, + neighbors.first, neighbors.second)); + } + seats_[neighbors.first].second = neighbors.second; + seats_[neighbors.second].first = neighbors.first; + } + +private: + struct Segment { + int dis; + int pos; + int l; + int r; + Segment(int dis, int pos, int l, int r) : + dis(dis), pos(pos), l(l), r(r) { + } + }; + + template + struct Compare { + bool operator()(const T& a, const T& b) { + return a->dis == b->dis ? + greater()(a->l, b->l) : + less()(a->dis, b->dis); + } + }; + + int num_; + using S = shared_ptr; + priority_queue, Compare> max_heap_; + unordered_map> seats_; +}; + +/** + * Your ExamRoom object will be instantiated and called as such: + * ExamRoom obj = new ExamRoom(N); + * int param_1 = obj.seat(); + * obj.leave(p); + */ diff --git a/C++/excel-sheet-column-number.cpp b/C++/excel-sheet-column-number.cpp new file mode 100644 index 000000000..a3b84c8cf --- /dev/null +++ b/C++/excel-sheet-column-number.cpp @@ -0,0 +1,14 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + int titleToNumber(string s) { + int number = 0; + for (const auto& c : s) { + number *= 26; + number += c - 'A' + 1; + } + return number; + } +}; diff --git a/C++/excel-sheet-column-title.cpp b/C++/excel-sheet-column-title.cpp new file mode 100644 index 000000000..a3342a427 --- /dev/null +++ b/C++/excel-sheet-column-title.cpp @@ -0,0 +1,32 @@ +// Time: O(logn) +// Space: O(1) + +// Iterative solution. +class Solution { +public: + string convertToTitle(int n) { + string result; + int dvd{n}; + + while (dvd) { + result.push_back((dvd - 1) % 26 + 'A'); + dvd = (dvd - 1) / 26; + } + reverse(result.begin(), result.end()); + + return result; + } +}; + +// Time: O((logn)^2) +// Space: O(logn) +// Recursive solution. +class Solution2 { +public: + string convertToTitle(int n) { + if (n == 0) { + return ""; + } + return convertToTitle((n - 1) / 26) + static_cast((n - 1) % 26 + 'A'); + } +}; diff --git a/C++/exclusive-time-of-functions.cpp b/C++/exclusive-time-of-functions.cpp new file mode 100644 index 000000000..84329b6c4 --- /dev/null +++ b/C++/exclusive-time-of-functions.cpp @@ -0,0 +1,38 @@ +// Time: O(n) +// Space: O(n) + +class Solution { +public: + vector exclusiveTime(int n, vector& logs) { + vector result(n); + stack stk; + int prev = 0; + for (const auto& log : logs) { + vector tokens = split(log, ':'); + if (tokens[1] == "start") { + if (!stk.empty()) { + result[stk.top()] += stoi(tokens[2]) - prev; + } + stk.emplace(stoi(tokens[0])); + prev = stoi(tokens[2]); + } else { + result[stk.top()] += stoi(tokens[2]) - prev + 1; + stk.pop(); + prev = stoi(tokens[2]) + 1; + } + } + return result; + } + +private: + vector split(const string& s, const char delim) { + vector tokens; + stringstream ss(s); + string token; + while (getline(ss, token, delim)) { + tokens.emplace_back(token); + } + return tokens; + } +}; + diff --git a/C++/expression-add-operators.cpp b/C++/expression-add-operators.cpp new file mode 100644 index 000000000..71d9f46e0 --- /dev/null +++ b/C++/expression-add-operators.cpp @@ -0,0 +1,64 @@ +// Time: O(4^n) +// Space: O(n) + +class Solution { +public: + vector addOperators(string num, int target) { + vector result; + vector expr; + int val = 0; + string val_str; + for (int i = 0; i < num.length(); ++i) { + val = val * 10 + num[i] - '0'; + val_str.push_back(num[i]); + // Avoid overflow and "00...". + if (to_string(val) != val_str) { + break; + } + expr.emplace_back(val_str); + addOperatorsDFS(num, target, i + 1, 0, val, &expr, &result); + expr.pop_back(); + } + return result; + } + + void addOperatorsDFS(const string& num, const int& target, const int& pos, + const int& operand1, const int& operand2, + vector *expr, vector *result) { + if (pos == num.length() && operand1 + operand2 == target) { + result->emplace_back(join(*expr)); + } else { + int val = 0; + string val_str; + for (int i = pos; i < num.length(); ++i) { + val = val * 10 + num[i] - '0'; + val_str.push_back(num[i]); + // Avoid overflow and "00...". + if (to_string(val) != val_str) { + break; + } + + // Case '+': + expr->emplace_back("+" + val_str); + addOperatorsDFS(num, target, i + 1, operand1 + operand2, val, expr, result); + expr->pop_back(); + + // Case '-': + expr->emplace_back("-" + val_str); + addOperatorsDFS(num, target, i + 1, operand1 + operand2, -val, expr, result); + expr->pop_back(); + + // Case '*': + expr->emplace_back("*" + val_str); + addOperatorsDFS(num, target, i + 1, operand1, operand2 * val, expr, result); + expr->pop_back(); + } + } + } + + string join(const vector& expr) { + ostringstream stream; + copy(expr.cbegin(), expr.cend(), ostream_iterator(stream)); + return stream.str(); + } +}; diff --git a/C++/expressive-words.cpp b/C++/expressive-words.cpp new file mode 100644 index 000000000..317913ff5 --- /dev/null +++ b/C++/expressive-words.cpp @@ -0,0 +1,44 @@ +// Time: O(n + s), n is the sum of all word lengths, s is the length of S +// Space: O(l + s) + +class Solution { +public: + int expressiveWords(string S, vector& words) { + int result = 0; + const auto& R = RLE(S); + for (const auto& word : words) { + const auto& R2 = RLE(word); + if (R.key != R2.key) { + continue; + } + int i; + for (i = 0; i < R.counts.size(); ++i) { + const auto& c1 = R.counts[i]; + const auto& c2 = R2.counts[i]; + if (c1 < max(c2, 3) && c1 != c2) { + break; + } + } + if (i == R.counts.size()) { + ++result; + } + } + return result; + } + +private: + struct RLE { + string key; + vector counts; + RLE(const string& s) { + int prev = -1; + for (int i = 0; i < s.length(); ++i) { + if (i == s.length() - 1 || s[i] != s[i + 1]) { + key.push_back(s[i]); + counts.emplace_back(i - prev); + prev = i; + } + } + } + }; +}; diff --git a/C++/factor-combinations.cpp b/C++/factor-combinations.cpp new file mode 100644 index 000000000..a177f2aa1 --- /dev/null +++ b/C++/factor-combinations.cpp @@ -0,0 +1,26 @@ +// Time: O(nlogn) = logn * n^(1/2) * n^(1/4) * ... * 1 +// Space: O(logn) + +// DFS solution. +class Solution { + public: + vector> getFactors(int n) { + vector> result; + vector factors; + getResult(n, &result, &factors); + return result; + } + + void getResult(const int n, vector> *result, vector *factors) { + for (int i = factors->empty() ? 2 : factors->back(); i <= n / i; ++i) { + if (n % i == 0) { + factors->emplace_back(i); + factors->emplace_back(n / i); + result->emplace_back(*factors); + factors->pop_back(); + getResult(n / i, result, factors); + factors->pop_back(); + } + } + } + }; diff --git a/C++/factorial-trailing-zeroes.cpp b/C++/factorial-trailing-zeroes.cpp new file mode 100644 index 000000000..a9ea04b70 --- /dev/null +++ b/C++/factorial-trailing-zeroes.cpp @@ -0,0 +1,14 @@ +// Time: O(logn) = O(1) +// Space: O(1) + +class Solution { +public: + int trailingZeroes(int n) { + int number = 0; + while (n > 0) { + number += n / 5; + n /= 5; + } + return number; + } +}; diff --git a/C++/falling-squares.cpp b/C++/falling-squares.cpp new file mode 100644 index 000000000..ea0e0ba35 --- /dev/null +++ b/C++/falling-squares.cpp @@ -0,0 +1,230 @@ +// Time: O(nlogn) +// Space: O(n) + +class Solution { +public: + vector fallingSquares(vector>& positions) { + vector result; + map heights; + int maxH = heights[-1] = 0; + for (const auto& p : positions) { + auto it0 = heights.upper_bound(p.first); + auto it1 = heights.lower_bound(p.first + p.second); + int h0 = prev(it0)->second; + int h1 = prev(it1)->second; + for (auto it = it0; it != it1; ++it) { + h0 = max(h0, it->second); + } + heights.erase(it0, it1); + heights[p.first] = h0 + p.second; + heights[p.first + p.second] = h1; + maxH = max(maxH, h0 + p.second); + result.emplace_back(maxH); + } + return result; + } +}; + +// Time: O(nlogn) +// Space: O(n) +// Segment Tree solution. +class Solution2 { +public: + vector fallingSquares(vector>& positions) { + set index; + for (const auto& position : positions) { + index.emplace(position.first); + index.emplace(position.first + position.second - 1); + } + SegmentTree tree(index.size()); + auto max_height = 0; + vector result; + for (const auto& position : positions) { + const auto L = distance(index.begin(), index.find(position.first)); + const auto R = distance(index.begin(), index.find(position.first + position.second - 1)); + const auto h = tree.query(L, R) + position.second; + tree.update(L, R, h); + max_height = max(max_height, h); + result.emplace_back(max_height); + } + return result; + } + +private: + class SegmentTree { + public: + SegmentTree(int N) + : N_(N), + tree_(2 * N), + lazy_(N) + { + H_ = 1; + while ((1 << H_) < N) { + ++H_; + } + } + + void update(int L, int R, int h) { + L += N_; R += N_; + int L0 = L, R0 = R; + while (L <= R) { + if ((L & 1) == 1) { + apply(L++, h); + } + if ((R & 1) == 0) { + apply(R--, h); + } + L >>= 1; R >>= 1; + } + pull(L0); pull(R0); + } + + int query(int L, int R) { + L += N_; R += N_; + auto result = 0; + push(L); push(R); + while (L <= R) { + if ((L & 1) == 1) { + result = max(result, tree_[L++]); + } + if ((R & 1) == 0) { + result = max(result, tree_[R--]); + } + L >>= 1; R >>= 1; + } + return result; + } + + private: + int N_, H_; + vector tree_, lazy_; + + void apply(int x, int val) { + tree_[x] = max(tree_[x], val); + if (x < N_) { + lazy_[x] = max(tree_[x], val); + } + } + + void pull(int x) { + while (x > 1) { + x >>= 1; + tree_[x] = max(tree_[x * 2], tree_[x * 2 + 1]); + tree_[x] = max(tree_[x], lazy_[x]); + } + } + + void push(int x) { + for (int h = H_; h > 0; --h) { + int y = x >> h; + if (lazy_[y] > 0) { + apply(y * 2, lazy_[y]); + apply(y * 2 + 1, lazy_[y]); + lazy_[y] = 0; + } + } + } + }; +}; + + +// Time: O(n * sqrt(n)) +// Space: O(n) +class Solution3 { +public: + vector fallingSquares(vector>& positions) { + set index; + for (const auto& position : positions) { + index.emplace(position.first); + index.emplace(position.first + position.second - 1); + } + const auto W = index.size(); + const auto B = static_cast(sqrt(W)); + vector heights(W); + vector blocks(B + 2), blocks_read(B + 2); + + auto max_height = 0; + vector result; + for (const auto& position : positions) { + const auto L = distance(index.begin(), index.find(position.first)); + const auto R = distance(index.begin(), index.find(position.first + position.second - 1)); + const auto h = query(B, L, R, heights, blocks, blocks_read) + position.second; + update(B, h, L, R, &heights, &blocks, &blocks_read); + max_height = max(max_height, h); + result.emplace_back(max_height); + } + return result; + } + +private: + int query(const int B, + int left, int right, + const vector& heights, + const vector& blocks, const vector& blocks_read) { + int result = 0; + while (left % B > 0 && left <= right) { + result = max(result, max(heights[left], blocks[left / B])); + result = max(result, blocks[left / B]); + ++left; + } + while (right % B != B - 1 && left <= right) { + result = max(result, max(heights[right], blocks[right / B])); + --right; + } + while (left <= right) { + result = max(result, max(blocks[left / B], blocks_read[left / B])); + left += B; + } + return result; + } + + void update(const int B, const int h, + int left, int right, + vector *heights, + vector *blocks, vector *blocks_read) { + while (left % B > 0 && left <= right) { + (*heights)[left] = max((*heights)[left], h); + (*blocks_read)[left / B] = max((*blocks_read)[left / B], h); + ++left; + } + while (right % B != B - 1 && left <= right) { + (*heights)[right] = max((*heights)[right], h); + (*blocks_read)[right / B] = max((*blocks_read)[right / B], h); + --right; + } + while (left <= right) { + (*blocks)[left / B] = max((*blocks)[left / B], h); + left += B; + } + } +}; + + +// Time: O(n^2) +// Space: O(n) +class Solution4 { +public: + vector fallingSquares(vector>& positions) { + vector heights(positions.size()); + for (int i = 0; i < positions.size(); ++i) { + int left_i, size_i; + tie(left_i, size_i) = positions[i]; + int right_i = left_i + size_i; + heights[i] += size_i; + for (int j = i + 1; j < positions.size(); ++j) { + int left_j, size_j; + tie(left_j, size_j) = positions[j]; + int right_j = left_j + size_j; + if (left_j < right_i and left_i < right_j) { // intersect + heights[j] = max(heights[j], heights[i]); + } + } + } + + vector result; + for (const auto& height : heights) { + result.emplace_back(result.empty() ? height : max(result.back(), height)); + } + return result; + } +}; diff --git a/C++/find-all-anagrams-in-a-string.cpp b/C++/find-all-anagrams-in-a-string.cpp new file mode 100644 index 000000000..2be245df7 --- /dev/null +++ b/C++/find-all-anagrams-in-a-string.cpp @@ -0,0 +1,28 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + vector findAnagrams(string s, string p) { + vector result; + if (p.empty() || s.empty()) { + return result; + } + + vector cnts(26); + for (const auto& c : p) { + ++cnts[c - 'a']; + } + + for (int left = 0, right = 0; right < s.length(); ++right) { + --cnts[s[right] - 'a']; + while (left <= right && cnts[s[right] - 'a'] < 0) { + ++cnts[s[left++] - 'a']; + } + if (right - left + 1 == p.length()) { + result.emplace_back(left); + } + } + return result; + } +}; diff --git a/C++/find-all-duplicates-in-an-array.cpp b/C++/find-all-duplicates-in-an-array.cpp new file mode 100644 index 000000000..8d6c78400 --- /dev/null +++ b/C++/find-all-duplicates-in-an-array.cpp @@ -0,0 +1,38 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + vector findDuplicates(vector& nums) { + vector result; + for (const auto& i : nums) { + if (nums[abs(i) - 1] < 0) { + result.emplace_back(abs(i)); + } else { + nums[abs(i) - 1] *= -1; + } + } + return result; + } +}; + +class Solution2 { +public: + vector findDuplicates(vector& nums) { + vector result; + int i = 0; + while (i < nums.size()) { + if (nums[i] != nums[nums[i] - 1]) { + swap(nums[i], nums[nums[i] - 1]); + } else { + ++i; + } + } + for (i = 0; i < nums.size(); ++i) { + if (i != nums[i] - 1) { + result.emplace_back(nums[i]); + } + } + return result; + } +}; diff --git a/C++/find-all-numbers-disappeared-in-an-array.cpp b/C++/find-all-numbers-disappeared-in-an-array.cpp new file mode 100644 index 000000000..920f3420f --- /dev/null +++ b/C++/find-all-numbers-disappeared-in-an-array.cpp @@ -0,0 +1,23 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + vector findDisappearedNumbers(vector& nums) { + for (int i = 0; i < nums.size(); ++i) { + if (nums[abs(nums[i]) - 1] > 0) { + nums[abs(nums[i]) - 1] *= -1; + } + } + + vector result; + for (int i = 0; i < nums.size(); ++i) { + if (nums[i] > 0) { + result.emplace_back(i + 1); + } else { + nums[i] *= -1; + } + } + return result; + } +}; diff --git a/C++/find-anagram-mappings.cpp b/C++/find-anagram-mappings.cpp new file mode 100644 index 000000000..1fa2f037b --- /dev/null +++ b/C++/find-anagram-mappings.cpp @@ -0,0 +1,18 @@ +// Time: O(n) +// Space: O(n) + +class Solution { +public: + vector anagramMappings(vector& A, vector& B) { + unordered_map> lookup; + for (int i = 0; i < B.size(); ++i) { + lookup[B[i]].emplace(i); + } + vector result; + for (const auto& n : A) { + result.emplace_back(lookup[n].back()); + lookup[n].pop(); + } + return result; + } +}; diff --git a/C++/find-and-replace-in-string.cpp b/C++/find-and-replace-in-string.cpp new file mode 100644 index 000000000..a5f4363a1 --- /dev/null +++ b/C++/find-and-replace-in-string.cpp @@ -0,0 +1,25 @@ +// Time: O(n + m), m is the number of targets +// Space: O(n) + +class Solution { +public: + string findReplaceString(string S, vector& indexes, vector& sources, vector& targets) { + vector> bucket(S.size()); + for (int i = 0; i < indexes.size(); ++i) { + if (S.find(sources[i], indexes[i]) == indexes[i]) { + bucket[indexes[i]] = {sources[i].size(), targets[i]}; + } + } + string result; + int last = 0; + for (int i = 0; i < S.length(); ++i) { + if (bucket[i].first) { + result += bucket[i].second; + last = i + bucket[i].first; + } else if (i >= last) { + result.push_back(S[i]); + } + } + return result; + } +}; diff --git a/C++/find-bottom-left-tree-value.cpp b/C++/find-bottom-left-tree-value.cpp new file mode 100644 index 000000000..b30755d7b --- /dev/null +++ b/C++/find-bottom-left-tree-value.cpp @@ -0,0 +1,59 @@ +// Time: O(n) +// Space: O(h) + +/** + * 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: + int findBottomLeftValue(TreeNode* root) { + int result = 0, max_depth = 0; + findBottomLeftValueHelper(root, 0, &max_depth, &result); + return result; + } + +private: + void findBottomLeftValueHelper(TreeNode *root, int curr_depth, int *max_depth, int *bottom_left_value) { + if (!root) { + return; + } + if (!root->left && !root->right && + curr_depth + 1 > *max_depth) { + *max_depth = curr_depth + 1; + *bottom_left_value = root->val; + return; + } + + findBottomLeftValueHelper(root->left, curr_depth + 1, max_depth, bottom_left_value); + findBottomLeftValueHelper(root->right, curr_depth + 1, max_depth, bottom_left_value); + } +}; + +// Time: O(n) +// Space: O(n) +class Solution2 { +public: + int findBottomLeftValue(TreeNode* root) { + queue q; + q.emplace(root); + TreeNode *node = nullptr; + while (!q.empty()) { + node = q.front(); + q.pop(); + if (node->right) { + q.emplace(node->right); + } + if (node->left) { + q.emplace(node->left); + } + } + return node->val; + } +}; diff --git a/C++/find-duplicate-file-in-system.cpp b/C++/find-duplicate-file-in-system.cpp new file mode 100644 index 000000000..fff191f24 --- /dev/null +++ b/C++/find-duplicate-file-in-system.cpp @@ -0,0 +1,28 @@ +// Time: O(n * l), l is the average length of file content +// Space: O(n * l) + +class Solution { +public: + vector> findDuplicate(vector& paths) { + unordered_map> files; + for (const auto& path : paths) { + stringstream ss(path); + string root; + string s; + getline(ss, root, ' '); + while (getline(ss, s, ' ')) { + auto fileName = root + '/' + s.substr(0, s.find('(')); + auto fileContent = s.substr(s.find('(') + 1, s.find(')') - s.find('(') - 1); + files[fileContent].emplace_back(fileName); + } + } + + vector> result; + for (const auto& file : files) { + if (file.second.size() > 1) { + result.emplace_back(file.second); + } + } + return result; + } +}; diff --git a/C++/find-duplicate-subtrees.cpp b/C++/find-duplicate-subtrees.cpp new file mode 100644 index 000000000..5b14ff2b4 --- /dev/null +++ b/C++/find-duplicate-subtrees.cpp @@ -0,0 +1,127 @@ +// Time: O(n) +// Space: O(n) + +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode(int x) : val(x), left(NULL), right(NULL) {} + * }; + */ + +namespace std{ + namespace + { + + // Code from boost + // Reciprocal of the golden ratio helps spread entropy + // and handles duplicates. + // See Mike Seymour in magic-numbers-in-boosthash-combine: + // http://stackoverflow.com/questions/4948780 + + template + inline void hash_combine(std::size_t& seed, T const& v) + { + seed ^= std::hash()(v) + 0x9e3779b9 + (seed<<6) + (seed>>2); + } + + // Recursive template code derived from Matthieu M. + template ::value - 1> + struct HashValueImpl + { + static void apply(size_t& seed, Tuple const& tuple) + { + HashValueImpl::apply(seed, tuple); + hash_combine(seed, std::get(tuple)); + } + }; + + template + struct HashValueImpl + { + static void apply(size_t& seed, Tuple const& tuple) + { + hash_combine(seed, std::get<0>(tuple)); + } + }; + } + + template + struct hash> + { + size_t + operator()(std::tuple const& tt) const + { + size_t seed = 0; + HashValueImpl >::apply(seed, tt); + return seed; + } + + }; +} + +class Solution { +public: + vector findDuplicateSubtrees(TreeNode* root) { + unordered_map> trees; + unordered_map, int> lookup; + getid(root, &lookup, &trees); + + vector result; + for (const auto& kvp : trees) { + if (kvp.second.size() > 1) { + result.emplace_back(kvp.second[0]); + } + } + return result; + } + +private: + int getid(TreeNode *root, + unordered_map, int> *lookup, + unordered_map> *trees) { + auto node_id = 0; + if (root) { + const auto& data = make_tuple(root->val, + getid(root->left, lookup, trees), + getid(root->right, lookup, trees)); + if (!lookup->count(data)) { + (*lookup)[data] = lookup->size() + 1; + } + node_id = (*lookup)[data]; + (*trees)[node_id].emplace_back(root); + } + return node_id; + } +}; + +// Time: O(n * h) +// Space: O(n * h) +class Solution2 { +public: + vector findDuplicateSubtrees(TreeNode* root) { + unordered_map lookup; + vector result; + postOrderTraversal(root, &lookup, &result); + return result; + } + +private: + string postOrderTraversal(TreeNode* node, unordered_map* lookup, vector *result) { + if (!node) { + return ""; + } + string s = "("; + s += postOrderTraversal(node->left, lookup, result); + s += to_string(node->val); + s += postOrderTraversal(node->right, lookup, result); + s += ")"; + if ((*lookup)[s] == 1) { + result->emplace_back(node); + } + ++(*lookup)[s]; + return s; + } +}; diff --git a/C++/find-eventual-safe-states.cpp b/C++/find-eventual-safe-states.cpp new file mode 100644 index 000000000..0880c4b09 --- /dev/null +++ b/C++/find-eventual-safe-states.cpp @@ -0,0 +1,38 @@ +// Time: O(|V| + |E|) +// Space: O(|V|) + +class Solution { +public: + enum Color { WHITE, GRAY, BLACK }; + + vector eventualSafeNodes(vector>& graph) { + vector result; + unordered_map lookup; + for (int i = 0; i < graph.size(); ++i) { + if (dfs(graph, i, &lookup)) { + result.emplace_back(i); + } + } + return result; + } + +private: + bool dfs(const vector>& graph, int node, + unordered_map *lookup) { + if ((*lookup)[node] != WHITE) { + return (*lookup)[node] == BLACK; + } + (*lookup)[node] = GRAY; + for (const auto& child : graph[node]) { + if ((*lookup)[child] == BLACK) { + continue; + } + if ((*lookup)[child] == GRAY || + !dfs(graph, child, lookup)) { + return false; + } + } + (*lookup)[node] = BLACK; + return true; + } +}; diff --git a/C++/find-k-closest-elements.cpp b/C++/find-k-closest-elements.cpp new file mode 100644 index 000000000..1b674d85a --- /dev/null +++ b/C++/find-k-closest-elements.cpp @@ -0,0 +1,14 @@ +// Time: O(logn + k) +// Space: O(1) + +class Solution { +public: + vector findClosestElements(vector& arr, int k, int x) { + auto i = distance(arr.begin(), lower_bound(arr.begin(), arr.end(), x)); + int left = i - 1, right = i; + while (k--) { + (right >= arr.size() || (left >= 0 && abs(arr[left] - x) <= abs(arr[right] - x) )) ? --left : ++right; + } + return vector(arr.begin() + left + 1, arr.begin() + right); + } +}; diff --git a/C++/find-k-pairs-with-smallest-sums.cpp b/C++/find-k-pairs-with-smallest-sums.cpp new file mode 100644 index 000000000..dbde1b312 --- /dev/null +++ b/C++/find-k-pairs-with-smallest-sums.cpp @@ -0,0 +1,37 @@ +// Time: O(k * log(min(n, m, k))), where n is the size of num1, and m is the size of num2. +// Space: O(min(n, m, k)) + +class Solution { +public: + vector> kSmallestPairs(vector& nums1, vector& nums2, int k) { + vector> pairs; + if (nums1.size() > nums2.size()) { + vector> tmp = kSmallestPairs(nums2, nums1, k); + for (const auto& pair : tmp) { + pairs.emplace_back(pair.second, pair.first); + } + return pairs; + } + + using P = pair>; + priority_queue, greater

> q; + auto push = [&nums1, &nums2, &q](int i, int j) { + if (i < nums1.size() && j < nums2.size()) { + q.emplace(nums1[i] + nums2[j], make_pair(i, j)); + } + }; + + push(0, 0); + while (!q.empty() && pairs.size() < k) { + auto tmp = q.top(); q.pop(); + int i, j; + tie(i, j) = tmp.second; + pairs.emplace_back(nums1[i], nums2[j]); + push(i, j + 1); + if (j == 0) { + push(i + 1, 0); // at most queue min(m, n) space. + } + } + return pairs; + } +}; diff --git a/C++/find-k-th-smallest-pair-distance.cpp b/C++/find-k-th-smallest-pair-distance.cpp new file mode 100644 index 000000000..72ca5a7a6 --- /dev/null +++ b/C++/find-k-th-smallest-pair-distance.cpp @@ -0,0 +1,32 @@ +// Time: O(nlogn + nlogw), n = len(nums), w = max(nums) - min(nums) +// Space: O(1) + +// Binary search with sliding window solution +class Solution { +public: + int smallestDistancePair(vector& nums, int k) { + sort(nums.begin(), nums.end()); + int left = 0, right = nums.back() - nums.front() + 1; + while (left < right) { + const auto mid = left + (right - left) / 2; + if (possible(mid, nums, k)) { + right = mid; + } else { + left = mid + 1; + } + } + return left; + } + +private: + bool possible(const int guess, const vector& nums, const int k) { + int count = 0, left = 0; + for (int right = 0; right < nums.size(); ++right) { + while ((nums[right] - nums[left]) > guess) { + ++left; + } + count += right - left; + } + return count >= k; + } +}; diff --git a/C++/find-largest-value-in-each-tree-row.cpp b/C++/find-largest-value-in-each-tree-row.cpp new file mode 100644 index 000000000..396ad8e3d --- /dev/null +++ b/C++/find-largest-value-in-each-tree-row.cpp @@ -0,0 +1,64 @@ +// Time: O(n) +// Space: O(h) + +/** + * 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: + vector largestValues(TreeNode* root) { + vector result; + largestValuesHelper(root, 0, &result); + return result; + } +private: + void largestValuesHelper(TreeNode* root, int depth, vector *result) { + if (!root) { + return; + } + if (depth == result->size()) { + result->emplace_back(root->val); + } else { + (*result)[depth] = max((*result)[depth], root->val); + } + largestValuesHelper(root->left, depth + 1, result); + largestValuesHelper(root->right, depth + 1, result); + } + }; + +// Time: O(n) +// Space: O(n) +class Solution2 { +public: + vector largestValues(TreeNode* root) { + if (!root) { + return {}; + } + vector result; + vector curr, next; + curr.emplace_back(root); + while (!curr.empty()) { + int max_val = numeric_limits::min(); + next.clear(); + for (const auto& node : curr) { + max_val = max(max_val, node->val); + if (node->left) { + next.emplace_back(node->left); + } + if (node->right) { + next.emplace_back(node->right); + } + } + result.emplace_back(max_val); + swap(curr, next); + } + return result; + } +}; diff --git a/C++/find-leaves-of-binary-tree.cpp b/C++/find-leaves-of-binary-tree.cpp new file mode 100644 index 000000000..2d3e41ed6 --- /dev/null +++ b/C++/find-leaves-of-binary-tree.cpp @@ -0,0 +1,34 @@ +// Time: O(n) +// Space: O(h) + +/** + * 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: + vector> findLeaves(TreeNode* root) { + vector> result; + findLeavesHelper(root, &result); + return result; + } + +private: + int findLeavesHelper(TreeNode *node, vector> *result) { + if (node == nullptr) { + return -1; + } + const int level = 1 + max(findLeavesHelper(node->left, result), + findLeavesHelper(node->right, result)); + if (result->size() < level + 1){ + result->emplace_back(); + } + (*result)[level].emplace_back(node->val); + return level; + } +}; diff --git a/C++/find-median-from-data-stream.cpp b/C++/find-median-from-data-stream.cpp new file mode 100644 index 000000000..1735e3596 --- /dev/null +++ b/C++/find-median-from-data-stream.cpp @@ -0,0 +1,81 @@ +// Time: O(nlogn) for total n addNums, O(logn) per addNum, O(1) per findMedian. +// Space: O(n), total space + +// Heap solution. +class MedianFinder { +public: + + // Adds a number into the data structure. + void addNum(int num) { + // Balance smaller half and larger half. + if (max_heap_.empty() || num > max_heap_.top()) { + min_heap_.emplace(num); + if (min_heap_.size() > max_heap_.size() + 1) { + max_heap_.emplace(min_heap_.top()); + min_heap_.pop(); + } + } else { + max_heap_.emplace(num); + if (max_heap_.size() > min_heap_.size()) { + min_heap_.emplace(max_heap_.top()); + max_heap_.pop(); + } + } + } + + // Returns the median of current data stream + double findMedian() { + return min_heap_.size() == max_heap_.size() ? + (max_heap_.top() + min_heap_.top()) / 2.0 : + min_heap_.top(); + + } + +private: + // min_heap_ stores the larger half seen so far. + priority_queue, greater> min_heap_; + // max_heap_ stores the smaller half seen so far. + priority_queue, less> max_heap_; +}; + +// BST solution. +class MedianFinder2 { +public: + + // Adds a number into the data structure. + void addNum(int num) { + // Balance smaller half and larger half. + if (max_bst_.empty() || num > *max_bst_.cbegin()) { + min_bst_.emplace(num); + if (min_bst_.size() > max_bst_.size() + 1) { + max_bst_.emplace(*min_bst_.cbegin()); + min_bst_.erase(min_bst_.cbegin()); + } + } else { + max_bst_.emplace(num); + if (max_bst_.size() > min_bst_.size()) { + min_bst_.emplace(*max_bst_.cbegin()); + max_bst_.erase(max_bst_.cbegin()); + } + } + } + + // Returns the median of current data stream + double findMedian() { + return min_bst_.size() == max_bst_.size() ? + (*max_bst_.cbegin() + *min_bst_.cbegin()) / 2.0 : + *min_bst_.cbegin(); + + } + +private: + // min_bst_ stores the larger half seen so far. + multiset> min_bst_; + // max_bst_ stores the smaller half seen so far. + multiset> max_bst_; +}; + +// Your MedianFinder object will be instantiated and called as such: +// MedianFinder mf; +// mf.addNum(1); +// mf.findMedian(); diff --git a/C++/find-minimum-in-rotated-sorted-array-ii.cpp b/C++/find-minimum-in-rotated-sorted-array-ii.cpp new file mode 100644 index 000000000..970ce0da1 --- /dev/null +++ b/C++/find-minimum-in-rotated-sorted-array-ii.cpp @@ -0,0 +1,23 @@ +// Time: O(logn) +// Space: O(1) + +class Solution { +public: + int findMin(vector& nums) { + int left = 0, right = nums.size() - 1; + + // Find min left s.t. nums[left] < nums[left']. + while (left < right && nums[left] >= nums[right]) { + int mid = left + (right - left) / 2; + if (nums[mid] == nums[left]) { + ++left; + } else if (nums[mid] < nums[left]) { + right = mid; + } else { + left = mid + 1; + } + } + + return nums[left]; + } +}; diff --git a/C++/find-minimum-in-rotated-sorted-array.cpp b/C++/find-minimum-in-rotated-sorted-array.cpp new file mode 100644 index 000000000..79e571a3a --- /dev/null +++ b/C++/find-minimum-in-rotated-sorted-array.cpp @@ -0,0 +1,21 @@ +// Time: O(logn) +// Space: O(1) + +class Solution { +public: + int findMin(vector& nums) { + int left = 0, right = nums.size() - 1; + + // Find min left s.t. nums[left] < nums[left']. + while (left < right && nums[left] >= nums[right]) { + int mid = left + (right - left) / 2; + if (nums[mid] < nums[left]) { + right = mid; + } else { + left = mid + 1; + } + } + + return nums[left]; + } +}; diff --git a/C++/find-mode-in-binary-search-tree.cpp b/C++/find-mode-in-binary-search-tree.cpp new file mode 100644 index 000000000..6f7961e69 --- /dev/null +++ b/C++/find-mode-in-binary-search-tree.cpp @@ -0,0 +1,51 @@ +// Time: O(n) +// Space: O(1) + +/** + * 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: + vector findMode(TreeNode* root) { + if (!root) { + return {}; + } + + vector result; + TreeNode *prev = nullptr; + int cnt = 1, max_cnt = 0; + inorder(root, &prev, &cnt, &max_cnt, &result); + return result; + } + +private: + void inorder(TreeNode *root, TreeNode **prev, int *cnt, int *max_cnt, vector *result) { + if (root == nullptr) { + return; + } + + inorder(root->left, prev, cnt, max_cnt, result); + if (*prev) { + if (root->val == (*prev)->val) { + ++(*cnt); + } else { + *cnt = 1; + } + } + if (*cnt > *max_cnt) { + *max_cnt = *cnt; + result->clear(); + result->emplace_back(root->val); + } else if (*cnt == *max_cnt) { + result->emplace_back(root->val); + } + *prev = root; + inorder(root->right, prev, cnt, max_cnt, result); + } +}; diff --git a/C++/find-peak-element.cpp b/C++/find-peak-element.cpp new file mode 100644 index 000000000..466c5240d --- /dev/null +++ b/C++/find-peak-element.cpp @@ -0,0 +1,20 @@ +// Time: O(logn) +// Space: O(1) + +class Solution { +public: + int findPeakElement(vector& nums) { + int left = 0, right = nums.size() - 1; + + while (left < right) { + const auto mid = left + (right - left) / 2; + if (nums[mid] > nums[mid + 1]) { + right = mid; + } else { + left = mid + 1; + } + } + + return left; + } +}; diff --git a/C++/find-permutation.cpp b/C++/find-permutation.cpp new file mode 100644 index 000000000..31c295061 --- /dev/null +++ b/C++/find-permutation.cpp @@ -0,0 +1,18 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + vector findPermutation(string s) { + vector result; + for (int i = 0; i <= s.length(); ++i) { + if (i == s.length() || s[i] == 'I') { + const int k = result.size(); + for (int j = i + 1; j > k; --j) { + result.emplace_back(j); + } + } + } + return result; + } +}; diff --git a/C++/find-pivot-index.cpp b/C++/find-pivot-index.cpp new file mode 100644 index 000000000..7539acee5 --- /dev/null +++ b/C++/find-pivot-index.cpp @@ -0,0 +1,17 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + int pivotIndex(vector& nums) { + const auto total = accumulate(nums.begin(), nums.end(), 0); + auto left_sum = 0; + for (int i = 0; i < nums.size(); ++i) { + if (left_sum == (total - left_sum - nums[i])) { + return i; + } + left_sum += nums[i]; + } + return -1; + } +}; diff --git a/C++/find-right-interval.cpp b/C++/find-right-interval.cpp new file mode 100644 index 000000000..9dfd70af0 --- /dev/null +++ b/C++/find-right-interval.cpp @@ -0,0 +1,31 @@ +// Time: O(nlogn) +// Space: O(n) + +/** + * Definition for an interval. + * struct Interval { + * int start; + * int end; + * Interval() : start(0), end(0) {} + * Interval(int s, int e) : start(s), end(e) {} + * }; + */ +class Solution { +public: + vector findRightInterval(vector& intervals) { + map lookup; + vector result; + for (int i = 0; i < intervals.size(); ++i) { + lookup[intervals[i].start] = i; + } + for (const auto& interval : intervals) { + const auto it = lookup.lower_bound(interval.end); + if (it == lookup.end()) { + result.emplace_back(-1); + } else { + result.emplace_back(it->second); + } + } + return result; + } +}; diff --git a/C++/find-smallest-letter-greater-than-target.cpp b/C++/find-smallest-letter-greater-than-target.cpp new file mode 100644 index 000000000..2b1434be2 --- /dev/null +++ b/C++/find-smallest-letter-greater-than-target.cpp @@ -0,0 +1,10 @@ +// Time: O(logn) +// Space: O(1) + +class Solution { +public: + char nextGreatestLetter(vector& letters, char target) { + const auto cit = upper_bound(letters.cbegin(), letters.cend(), target); + return cit != letters.cend() ? *cit : letters.front(); + } +}; diff --git a/C++/find-the-celebrity.cpp b/C++/find-the-celebrity.cpp new file mode 100644 index 000000000..ec96b517d --- /dev/null +++ b/C++/find-the-celebrity.cpp @@ -0,0 +1,26 @@ +// Time: O(n) +// Space: O(1) + +// Forward declaration of the knows API. +bool knows(int a, int b); + +class Solution { +public: + int findCelebrity(int n) { + int candidate = 0; + // Find the candidate. + for (int i = 1; i < n; ++i) { + if (knows(candidate, i)) { + candidate = i; // All candidates < i are not celebrity candidates. + } + } + // Verify the candidate. + for (int i = 0; i < n; ++i) { + if (i != candidate && + (knows(candidate, i) || !knows(i, candidate))) { + return -1; + } + } + return candidate; + } +}; diff --git a/C++/find-the-closest-palindrome.cpp b/C++/find-the-closest-palindrome.cpp new file mode 100644 index 000000000..0e4f79f59 --- /dev/null +++ b/C++/find-the-closest-palindrome.cpp @@ -0,0 +1,28 @@ +// Time: O(l) +// Space: O(l) + +class Solution { +public: + string nearestPalindromic(string n) { + const auto l = n.size(); + unordered_set candidates; + candidates.emplace(static_cast(pow(10, l)) + 1); + candidates.emplace(static_cast(pow(10, l - 1)) - 1); + auto prefix = stol(n.substr(0, (l + 1) / 2)); + for (long long i = -1; i <= 1; ++i) { + auto p = to_string(prefix + i); + auto pp = p + string(p.rbegin() + (l % 2), p.rend()); + candidates.emplace(stol(pp)); + } + long long num = stol(n), closest_val = numeric_limits::max(); + candidates.erase(num); + for (const auto& val : candidates) { + if (abs(val - num) < abs(closest_val - num)) { + closest_val = val; + } else if (abs(val - num) == abs(closest_val - num)) { + closest_val = min(closest_val, val); + } + } + return to_string(closest_val); + } +}; diff --git a/C++/find-the-derangement-of-an-array.cpp b/C++/find-the-derangement-of-an-array.cpp new file mode 100644 index 000000000..2293e7719 --- /dev/null +++ b/C++/find-the-derangement-of-an-array.cpp @@ -0,0 +1,16 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + int findDerangement(int n) { + static const int M = 1000000007; + long long mul = 1, sum = 0; + for (int i = n; i >= 0; --i) { + sum = (sum + M + (i % 2 == 0 ? 1 : -1) * mul) % M; + mul = (mul * i) % M; + } + return static_cast(sum); + } +}; + diff --git a/C++/find-the-difference.cpp b/C++/find-the-difference.cpp new file mode 100644 index 000000000..bfa3a01ca --- /dev/null +++ b/C++/find-the-difference.cpp @@ -0,0 +1,10 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + char findTheDifference(string s, string t) { + return accumulate(s.cbegin(), s.cend(), 0, std::bit_xor()) ^ + accumulate(t.cbegin(), t.cend(), 0, std::bit_xor()); + } +}; diff --git a/C++/find-the-duplicate-number.cpp b/C++/find-the-duplicate-number.cpp new file mode 100644 index 000000000..958d5e0df --- /dev/null +++ b/C++/find-the-duplicate-number.cpp @@ -0,0 +1,76 @@ +// Time: O(n) +// Space: O(1) + +// Two pointers method, same as Linked List Cycle II. +class Solution { +public: + int findDuplicate(vector& nums) { + int slow = nums[0]; + int fast = nums[nums[0]]; + while (slow != fast) { + slow = nums[slow]; + fast = nums[nums[fast]]; + } + + fast = 0; + while (slow != fast) { + slow = nums[slow]; + fast = nums[fast]; + } + return slow; + } +}; + +// Time: O(nlogn) +// Space: O(1) +// Binary search method. +class Solution2 { +public: + int findDuplicate(vector& nums) { + int left = 1, right = nums.size(); + + while (left <= right) { + const int mid = left + (right - left) / 2; + // Get count of num <= mid. + int count = 0; + for (const auto& num : nums) { + if (num <= mid) { + ++count; + } + } + if (count > mid) { + right = mid - 1; + } else { + left = mid + 1; + } + } + return left; + } +}; + +// Time: O(n) +// Space: O(n) +class Solution3 { +public: + int findDuplicate(vector& nums) { + int duplicate = 0; + // Mark the value as visited by negative. + for (auto& num : nums) { + if (nums[abs(num) - 1] > 0) { + nums[abs(num) - 1] *= -1; + } else { + duplicate = abs(num); + break; + } + } + // Rollback the value. + for (auto& num : nums) { + if (nums[abs(num) - 1] < 0) { + nums[abs(num) - 1] *= -1; + } else { + break; + } + } + return duplicate; + } +}; diff --git a/C++/findMedianSortedArrays.cpp b/C++/findMedianSortedArrays.cpp deleted file mode 100644 index d48822f66..000000000 --- a/C++/findMedianSortedArrays.cpp +++ /dev/null @@ -1,33 +0,0 @@ -// LeetCode, Median of Two Sorted Arrays -// Complexity: -// O(log(m+n)) -// O(log(m+n)) - -class Solution { -public: - double findMedianSortedArrays(int A[], int m, int B[], int n) { - int total = m + n; - if (total & 0x1) - return find_kth(A, m, B, n, total / 2 + 1); - else - return (find_kth(A, m, B, n, total / 2) - + find_kth(A, m, B, n, total / 2 + 1)) / 2.0; - } - -private: - static int find_kth(int A[], int m, int B[], int n, int k) { - //always assume that m is equal or smaller than n - if (m > n) return find_kth(B, n, A, m, k); - if (m == 0) return B[k - 1]; - if (k == 1) return min(A[0], B[0]); - - //divide k into two parts - int ia = min(k / 2, m), ib = k - ia; - if (A[ia - 1] < B[ib - 1]) - return find_kth(A + ia, m - ia, B, n, k - ia); - else if (A[ia - 1] > B[ib - 1]) - return find_kth(A, m, B + ib, n - ib, k - ib); - else - return A[ia - 1]; - } -}; \ No newline at end of file diff --git a/C++/findMinimumInRotatedSortedArray.cpp b/C++/findMinimumInRotatedSortedArray.cpp deleted file mode 100644 index 8fd41d8b8..000000000 --- a/C++/findMinimumInRotatedSortedArray.cpp +++ /dev/null @@ -1,29 +0,0 @@ -// LeetCode, SFind Minimum in Rotated Sorted Array -// Complexity: -// O(logn) time -// O(1) space - -class Solution { -public: - int findMin(vector &num) { - int start = 0, end = num.size(); - - while (start < end) { - if (num[start] <= num[end - 1]) - return num[start]; - - int mid = start + (end - start)/2; - - if (num[mid] >= num[start]) { - start = mid + 1; - } else { - if (mid == end - 1) - return num[mid]; - else - end = mid + 1; - } - } - - return num[start]; - } -}; \ No newline at end of file diff --git a/C++/findSubstring.cpp b/C++/findSubstring.cpp deleted file mode 100644 index 749cc5378..000000000 --- a/C++/findSubstring.cpp +++ /dev/null @@ -1,32 +0,0 @@ -// Time Complexity: O((m - n * k) * n * k) ~ O(m * n * k), where m is string length, n is dict size, k is word length -// Space Complexity: O( n * k) -class Solution { - public: - vector findSubstring(string s, vector &dict) { - const size_t wordLength = dict.front().length(); - const size_t catLength = wordLength * dict.size(); - vector result; - - if(s.length() < catLength) return result; - - unordered_map wordCount; - - for(auto const & word : dict) ++wordCount[word]; - - for(auto i = begin(s); i <= prev(end(s), catLength); ++i) { - unordered_map unused(wordCount); - - for(auto j = i; j != next(i, catLength); j += wordLength) { - auto pos = unused.find(string(j, next(j, wordLength))); - - if(pos == unused.end()) break; - - if(--pos->second == 0) unused.erase(pos); - } - - if(unused.size() == 0) result.push_back(distance(begin(s), i)); - } - - return result; - } -}; diff --git a/C++/first-bad-version.cpp b/C++/first-bad-version.cpp new file mode 100644 index 000000000..6d143fef7 --- /dev/null +++ b/C++/first-bad-version.cpp @@ -0,0 +1,21 @@ +// Time: O(logn) +// Space: O(1) + +// Forward declaration of isBadVersion API. +bool isBadVersion(int version); + +class Solution { +public: + int firstBadVersion(int n) { + int left = 1, right = n; + while (left <= right) { + int mid = left + (right - left) / 2; + if (isBadVersion(mid)) { + right = mid - 1; + } else { + left = mid + 1; + } + } + return left; + } +}; diff --git a/C++/first-missing-positive.cpp b/C++/first-missing-positive.cpp new file mode 100644 index 000000000..48112803e --- /dev/null +++ b/C++/first-missing-positive.cpp @@ -0,0 +1,25 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + int firstMissingPositive(vector& nums) { + int i = 0; + bucketSort(&nums); + for (; i < nums.size() && nums[i] == i + 1; ++i); + return i + 1; + } + +private: + void bucketSort(vector *nums) { + int i = 0; + while (i < nums->size()) { + if ((*nums)[i] > 0 && (*nums)[i] <= nums->size() && + (*nums)[i] != (*nums)[(*nums)[i] - 1]) { + swap((*nums)[i], (*nums)[(*nums)[i] - 1]); + } else { + ++i; + } + } + } +}; diff --git a/C++/first-unique-character-in-a-string.cpp b/C++/first-unique-character-in-a-string.cpp new file mode 100644 index 000000000..7e06b6a11 --- /dev/null +++ b/C++/first-unique-character-in-a-string.cpp @@ -0,0 +1,25 @@ +// Time: O(n) +// Space: O(n) + +// One-pass solution. +class Solution { +public: + int firstUniqChar(string s) { + using IT = list::iterator; + + list candidates; + unordered_map lookup; + for (int i = 0; i < s.length(); ++i) { + const auto c = s[i]; + if (lookup.count(c)) { + if (lookup[c] != candidates.end()) { + candidates.erase(lookup[c]); + } + lookup[c] = candidates.end(); + } else { + lookup[c] = candidates.emplace(candidates.end(), i); + } + } + return candidates.empty() ? -1 : candidates.front(); + } +}; diff --git a/C++/firstMissingPositive.cpp b/C++/firstMissingPositive.cpp deleted file mode 100644 index 2c82d651f..000000000 --- a/C++/firstMissingPositive.cpp +++ /dev/null @@ -1,21 +0,0 @@ -// Time Complexity: O(n) -// Space Complexity: O(1) - -class Solution { - public: - int firstMissingPositive(int A[], int n) { - int i; - bucketSort(A, n); - for(i = 0; i < n && A[i] == i + 1; ++i); - return i + 1; - } - - private: - void bucketSort(int A[], int n) { - for(int i = 0; i < n; ++i) { - for (; A[i] != i + 1 && A[i] > 0 && A[i] <= n && A[i] != A[A[i] - 1];) { - swap(A[i], A[A[i] - 1]); - } - } - } -}; diff --git a/C++/fizz-buzz.cpp b/C++/fizz-buzz.cpp new file mode 100644 index 000000000..d7477648c --- /dev/null +++ b/C++/fizz-buzz.cpp @@ -0,0 +1,23 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + vector fizzBuzz(int n) { + vector result; + + for (int i = 1; i <= n; ++i) { + if (i % 15 == 0) { + result.emplace_back("FizzBuzz"); + } else if (i % 5 == 0) { + result.emplace_back("Buzz"); + } else if (i % 3 == 0) { + result.emplace_back("Fizz"); + } else { + result.emplace_back(to_string(i)); + } + } + + return result; + } +}; diff --git a/C++/flatten-2d-vector.cpp b/C++/flatten-2d-vector.cpp new file mode 100644 index 000000000..55c3780bb --- /dev/null +++ b/C++/flatten-2d-vector.cpp @@ -0,0 +1,38 @@ +// Time: O(1) +// Space: O(1) + +class Vector2D { +public: + Vector2D(vector>& vec2d) : vec(vec2d) { + x = vec.begin(); + if (x != vec.end()) { + y = x->begin(); + adjustNextIter(); + } + } + + int next() { + const auto ret = *y; + ++y; + adjustNextIter(); + return ret; + } + + bool hasNext() { + return x != vec.end() && y != x->end(); + } + + void adjustNextIter() { + while (x != vec.end() && y == x->end()) { + ++x; + if (x != vec.end()) { + y = x->begin(); + } + } + } + +private: + vector>& vec; + vector>::iterator x; + vector::iterator y; +}; diff --git a/C++/flatten-a-multilevel-doubly-linked-list.cpp b/C++/flatten-a-multilevel-doubly-linked-list.cpp new file mode 100644 index 000000000..5cc969f35 --- /dev/null +++ b/C++/flatten-a-multilevel-doubly-linked-list.cpp @@ -0,0 +1,45 @@ +// Time: O(n) +// Space: O(1) + +/* +// Definition for a Node. +class Node { +public: + int val = NULL; + Node* prev = NULL; + Node* next = NULL; + Node* child = NULL; + + Node() {} + + Node(int _val, Node* _prev, Node* _next, Node* _child) { + val = _val; + prev = _prev; + next = _next; + child = _child; + } +}; +*/ +class Solution { +public: + Node* flatten(Node* head) { + for (auto curr = head; curr; curr = curr->next) { + if (!curr->child) { + continue; + } + auto curr_next = curr->next; + curr->child->prev = curr; + curr->next = curr->child; + auto last_child = curr; + while (last_child->next) { + last_child = last_child->next; + } + if (curr_next) { + last_child->next = curr_next; + curr_next->prev = last_child; + } + curr->child = nullptr; + } + return head; + } +}; diff --git a/C++/flatten-nested-list-iterator.cpp b/C++/flatten-nested-list-iterator.cpp new file mode 100644 index 000000000..951d80c3a --- /dev/null +++ b/C++/flatten-nested-list-iterator.cpp @@ -0,0 +1,96 @@ +// Time: O(n), n is the number of the integers. +// Space: O(h), h is the depth of the nested lists. + +/** + * // This is the interface that allows for creating nested lists. + * // You should not implement it, or speculate about its implementation + * class NestedInteger { + * public: + * // Return true if this NestedInteger holds a single integer, rather than a nested list. + * bool isInteger() const; + * + * // Return the single integer that this NestedInteger holds, if it holds a single integer + * // The result is undefined if this NestedInteger holds a nested list + * int getInteger() const; + * + * // Return the nested list that this NestedInteger holds, if it holds a nested list + * // The result is undefined if this NestedInteger holds a single integer + * const vector &getList() const; + * }; + */ + +// Using stack and iterator. +class NestedIterator { +public: + using IT = vector::const_iterator; + NestedIterator(vector &nestedList) { + depth_.emplace(nestedList.cbegin(), nestedList.cend()); + } + + int next() { + return (depth_.top().first++)->getInteger(); + } + + bool hasNext() { + while (!depth_.empty()) { + auto& cur = depth_.top(); + if (cur.first == cur.second) { + depth_.pop(); + } else if (cur.first->isInteger()) { + return true; + } else { + auto& nestedList = cur.first->getList(); + ++cur.first; + depth_.emplace(nestedList.cbegin(), nestedList.cend()); + } + } + return false; + } + +private: + stack> depth_; +}; + +// Time: O(n) +// Space: O(n) +// Using stack. +class NestedIterator2 { +public: + NestedIterator2(vector &nestedList) { + for (int i = static_cast(nestedList.size()) - 1; i >= 0; --i) { + nodes_.emplace(&nestedList[i]); + } + } + + int next() { + const auto result = nodes_.top()->getInteger(); + nodes_.pop(); + return result; + } + + bool hasNext() { + while (!nodes_.empty()) { + auto *cur = nodes_.top(); + if (cur->isInteger()) { + return true; + } + nodes_.pop(); + auto& children = cur->getList(); + for (int i = static_cast(children.size()) - 1; i >= 0; --i) { + nodes_.emplace(&children[i]); + } + } + return false; + } + +private: + stack nodes_; +}; + + +/** + * Your NestedIterator object will be instantiated and called as such: + * NestedIterator i(nestedList); + * while (i.hasNext()) cout << i.next(); + */ + diff --git a/C++/flip-game-ii.cpp b/C++/flip-game-ii.cpp new file mode 100644 index 000000000..4c0bc5f01 --- /dev/null +++ b/C++/flip-game-ii.cpp @@ -0,0 +1,143 @@ +// Time: O(n + c^2), c is max length of consecutive '+' +// Space: O(c) + +// The best theory solution (DP, O(n + c^2)) could be seen here: +// https://leetcode.com/discuss/64344/theory-matters-from-backtracking-128ms-to-dp-0ms +class Solution { +public: + bool canWin(string s) { + replace(s.begin(), s.end(), '-', ' '); + istringstream in(s); + int g_final = 0; + vector g; // Sprague-Grundy function of 0 ~ maxlen, O(n) space + for (string t; in >> t; ) { // Split the string + int p = t.size(); + while (g.size() <= p) { // O(c) time + string x{t}; + int i = 0, j = g.size() - 2; + while (i <= j) { // The S-G value of all subgame states, O(c) time + // Theorem 2: g[game] = g[subgame1]^g[subgame2]^g[subgame3]...; + x[g[i++] ^ g[j--]] = '-'; + } + // Find first missing number. + g.emplace_back(x.find('+')); + } + g_final ^= g[p]; + } + return g_final; // Theorem 1: First player must win iff g(current_state) != 0 + } +}; + + +// Time: O(n + c^3 * 2^c * logc), n is length of string, c is count of "++" +// Space: O(c * 2^c) +// hash solution. +class Solution2 { +public: + struct multiset_hash { + std::size_t operator() (const multiset& set) const { + string set_string; + for (const auto& i : set) { + set_string.append(to_string(i) + " "); + } + return hash()(set_string); + } + }; + + bool canWin(string s) { + const int n = s.length(); + multiset consecutives; + for (int i = 0; i < n - 1; ++i) { // O(n) time + if (s[i] == '+') { + int c = 1; + for (; i < n - 1 && s[i + 1] == '+'; ++i, ++c); + if (c >= 2) { + consecutives.emplace(c); + } + } + } + return canWinHelper(consecutives); + } + +private: + bool canWinHelper(const multiset& consecutives) { // O(2^c) time + if (!lookup_.count(consecutives)) { + bool is_win = false; + for (auto it = consecutives.cbegin(); !is_win && it != consecutives.cend(); ++it) { // O(c) time + const int c = *it; + multiset next_consecutives(consecutives); + next_consecutives.erase(next_consecutives.find(c)); + for (int i = 0; !is_win && i < c - 1; ++i) { // O(clogc) time + if (i >= 2) { + next_consecutives.emplace(i); + } + if (c - 2 - i >= 2) { + next_consecutives.emplace(c - 2 - i); + } + is_win = !canWinHelper(next_consecutives); + if (i >= 2) { + next_consecutives.erase(next_consecutives.find(i)); + } + if (c - 2 - i >= 2) { + next_consecutives.erase(next_consecutives.find(c - 2 - i)); + } + lookup_[consecutives] = is_win; // O(c) time + } + } + } + return lookup_[consecutives]; + } + unordered_map, bool, multiset_hash> lookup_; +}; + + +// Time: O(n + c * n * 2^c), try all the possible game strings, +// and each string would have c choices to become the next string +// Space: O(n * 2^c), keep all the possible game strings +// hash solution. +class Solution3 { +public: + bool canWin(string s) { + if (!lookup_.count(s)) { + const int n = s.length(); + bool is_win = false; + for (int i = 0; !is_win && i < n - 1; ++i) { + if (s[i] == '+') { + for (; !is_win && i < n - 1 && s[i + 1] == '+'; ++i) { + s[i] = s[i + 1] = '-'; + is_win = !canWin(s); + s[i] = s[i + 1] = '+'; + lookup_[s] = is_win; + } + } + } + } + return lookup_[s]; + } +private: + unordered_map lookup_; +}; + + +// Time: O(n * c!), n is length of string, c is count of "++" +// Space: O(c), recursion would be called at most c in depth. +// Besides, no extra space in each depth for the modified string. +class Solution4 { +public: + bool canWin(string s) { + const int n = s.length(); + bool is_win = false; + for (int i = 0; !is_win && i < n - 1; ++i) { // O(n) time + if (s[i] == '+') { + for (; !is_win && i < n - 1 && s[i + 1] == '+'; ++i) { // O(c) time + s[i] = s[i + 1] = '-'; + // t(n, c) = c * t(n, c - 1) + n = ... = c! * t(n, 0) + n * c! * (1/0! + 1/1! + ... 1/c!) + // = n * c! + n * c! * O(e) = O(n * c!) + is_win = !canWin(s); + s[i] = s[i + 1] = '+'; + } + } + } + return is_win; + } +}; diff --git a/C++/flip-game.cpp b/C++/flip-game.cpp new file mode 100644 index 000000000..9d01c5804 --- /dev/null +++ b/C++/flip-game.cpp @@ -0,0 +1,20 @@ + // Time: O(c * n + n) = O(n * (c+1)), n is length of string, c is count of "++" + // Space: O(1), no extra space excluding the result which requires at most O(n^2) space + + class Solution { + public: + vector generatePossibleNextMoves(string s) { + vector res; + int n = s.length(); + for (int i = 0; i < n - 1; ++i) { // O(n) time + if (s[i] == '+') { + for (;i < n - 1 && s[i + 1] == '+'; ++i) { // O(c) time + s[i] = s[i + 1] = '-'; + res.emplace_back(s); // O(n) to copy a string + s[i] = s[i + 1] = '+'; + } + } + } + return res; + } + }; diff --git a/C++/flipping-an-image.cpp b/C++/flipping-an-image.cpp new file mode 100644 index 000000000..db029043a --- /dev/null +++ b/C++/flipping-an-image.cpp @@ -0,0 +1,18 @@ +// Time: O(n^2) +// Space: O(1) + +class Solution { +public: + vector> flipAndInvertImage(vector>& A) { + for (auto& row : A) { + for (int i = 0; i < (row.size() + 1) / 2; ++i) { + row[i] ^= 1; + if (i != row.size() + ~i) { + row[row.size() + ~i] ^= 1; + swap(row[i], row[row.size() + ~i]); + } + } + } + return A; + } +}; diff --git a/C++/flood-fill.cpp b/C++/flood-fill.cpp new file mode 100644 index 000000000..0d86958c5 --- /dev/null +++ b/C++/flood-fill.cpp @@ -0,0 +1,27 @@ +// Time: O(m * n) +// Space: O(m * n) + +class Solution { +public: + vector> floodFill(vector>& image, int sr, int sc, int newColor) { + int color = image[sr][sc]; + if (color == newColor) return image; + dfs(&image, sr, sc, newColor, color); + return image; + } + +private: + void dfs(vector> *image, int r, int c, int newColor, int color) { + static const vector> directions{{-1, 0}, { 1, 0}, + { 0, 1}, { 0, -1}}; + if (r < 0 || r >= image->size() || + c < 0 || c >= (*image)[0].size() || + (*image)[r][c] != color) { + return; + } + (*image)[r][c] = newColor; + for (const auto& d : directions) { + dfs(image, r + d.first, c + d.second, newColor, color); + } + } +}; diff --git a/C++/fourSum.cpp b/C++/fourSum.cpp deleted file mode 100644 index 0b62b1364..000000000 --- a/C++/fourSum.cpp +++ /dev/null @@ -1,34 +0,0 @@ -// Time Complexity: O(n^2) -// Space Complexity: O(n^2) - -class Solution { - public: - vector > fourSum(vector &num, int target) { - vector> ans; - if (num.size() < 4) - return ans; - sort(num.begin(), num.end()); - unordered_multimap> cache; - - for (int i = 0; i + 1 < num.size(); ++i) - for (int j = i + 1; j < num.size(); ++j) - cache.insert(make_pair(num[i] + num[j], make_pair(i, j))); - - for (auto i = cache.begin(); i != cache.end(); ++i) { - int x = target - i->first; - auto range = cache.equal_range(x); - for (auto j = range.first; j != range.second; ++j) { - auto a = i->second.first; - auto b = i->second.second; - auto c = j->second.first; - auto d = j->second.second; - if (b < c) { - ans.push_back({ num[a], num[b], num[c], num[d] }); - } - } - } - sort(ans.begin(), ans.end()); - ans.erase(unique(ans.begin(), ans.end()), ans.end()); - return ans; - } -}; diff --git a/C++/fraction-addition-and-subtraction.cpp b/C++/fraction-addition-and-subtraction.cpp new file mode 100644 index 000000000..3c1bb0230 --- /dev/null +++ b/C++/fraction-addition-and-subtraction.cpp @@ -0,0 +1,19 @@ +// Time: O(nlogx), x is the max denominator +// Space: O(n) + +class Solution { +public: + string fractionAddition(string expression) { + istringstream iss(expression); + int A = 0, B = 1, a, b; + char _; + while (iss >> a >> _ >> b) { + A = A * b + a * B; + B *= b; + auto g = abs(__gcd(A, B)); + A /= g; + B /= g; + } + return to_string(A) + '/' + to_string(B); + } +}; diff --git a/C++/fraction-to-recurring-decimal.cpp b/C++/fraction-to-recurring-decimal.cpp new file mode 100644 index 000000000..f4f2f6171 --- /dev/null +++ b/C++/fraction-to-recurring-decimal.cpp @@ -0,0 +1,34 @@ +// Time: O(logn), where logn is the length of result strings +// Space: O(1) + +class Solution { +public: + string fractionToDecimal(int numerator, int denominator) { + string result; + if ((numerator ^ denominator) >> 31 && numerator != 0) { + result = "-"; + } + + auto dvd = llabs(numerator); + auto dvs = llabs(denominator); + result += to_string(dvd / dvs); + dvd %= dvs; + if (dvd > 0) { + result += "."; + } + + unordered_map lookup; + while (dvd && !lookup.count(dvd)) { + lookup[dvd] = result.length(); + dvd *= 10; + result += to_string(dvd / dvs); + dvd %= dvs; + } + + if (lookup.count(dvd)) { + result.insert(lookup[dvd], "("); + result.push_back(')'); + } + return result; + } +}; diff --git a/C++/freedom-trail.cpp b/C++/freedom-trail.cpp new file mode 100644 index 000000000..8387cee40 --- /dev/null +++ b/C++/freedom-trail.cpp @@ -0,0 +1,26 @@ +// Time: O(k) ~ O(k * r^2) +// Space: O(r) + +class Solution { +public: + int findRotateSteps(string ring, string key) { + unordered_map> lookup; + for (int i = 0; i < ring.size(); ++i) { + lookup[ring[i]].emplace_back(i); + } + + vector> dp(2, vector (ring.size())); + for (int i = 1; i <= key.size(); ++i) { + fill(dp[i % 2].begin(), dp[i % 2].end(), numeric_limits::max()); + for (const auto& j : lookup[key[i - 1]]) { + for (const auto& k : (i > 1 ? lookup[key[i - 2]] : vector(1))) { + int min_dist = min((k + ring.size() - j) % ring.size(), + (j + ring.size() - k) % ring.size()) + + dp[(i - 1) % 2][k]; + dp[i % 2][j] = min(dp[i % 2][j], min_dist); + } + } + } + return *min_element(dp[key.size() % 2].begin(), dp[key.size() % 2].end()) + key.size(); + } +}; diff --git a/C++/friend-circles.cpp b/C++/friend-circles.cpp new file mode 100644 index 000000000..3a58b0c26 --- /dev/null +++ b/C++/friend-circles.cpp @@ -0,0 +1,48 @@ +// Time: O(n^2) +// Space: O(n) + +class Solution { +public: + int findCircleNum(vector>& M) { + UnionFind circles(M.size()); + for (int i = 0; i < M.size(); ++i) { + for (int j = 0; j < M[i].size(); ++j) { + if (M[i][j] && i != j) { + circles.union_set(i, j); + } + } + } + return circles.size(); + } + +private: + class UnionFind { + public: + UnionFind(const int n) : set_(n), count_(n) { + iota(set_.begin(), set_.end(), 0); + } + + int find_set(const int x) { + if (set_[x] != x) { + set_[x] = find_set(set_[x]); // Path compression. + } + return set_[x]; + } + + void union_set(const int x, const int y) { + int x_root = find_set(x), y_root = find_set(y); + if (x_root != y_root) { + set_[min(x_root, y_root)] = max(x_root, y_root); + --count_; + } + } + + int size() const { + return count_; + } + + private: + vector set_; + int count_; + }; +}; diff --git a/C++/friends-of-appropriate-ages.cpp b/C++/friends-of-appropriate-ages.cpp new file mode 100644 index 000000000..b1cdfe6c6 --- /dev/null +++ b/C++/friends-of-appropriate-ages.cpp @@ -0,0 +1,27 @@ +// Time: O(a^2 + n), a is the number of ages, +// n is the number of people +// Space: O(a) + +class Solution { +public: + int numFriendRequests(vector& ages) { + unordered_map count; + for (const auto &age : ages) { + ++count[age]; + } + int result = 0; + for (const auto &a: count) { + for (const auto &b: count) { + if (request(a.first, b.first)) { + result += a.second * (b.second - (a.first == b.first ? 1 : 0)); + } + } + } + return result; + } + +private: + bool request(int a, int b) { + return 0.5 * a + 7 < b && b <= a; + } +}; diff --git a/C++/frog-jump.cpp b/C++/frog-jump.cpp new file mode 100644 index 000000000..e9dcb774a --- /dev/null +++ b/C++/frog-jump.cpp @@ -0,0 +1,29 @@ +// Time: O(n) ~ O(n^2) +// Space: O(n) + +class Solution { +public: + bool canCross(vector& stones) { + if (stones[1] != 1) { + return false; + } + + unordered_map> last_jump_units; + for (const auto& s: stones) { + last_jump_units.emplace(s, {unordered_set()}); + } + last_jump_units[1].emplace(1); + + for (int i = 0; i + 1 < stones.size(); ++i) { + for (const auto& j : last_jump_units[stones[i]]) { + for (const auto& k : {j - 1, j, j + 1}) { + if (k > 0 && last_jump_units.count(stones[i] + k)) { + last_jump_units[stones[i] + k].emplace(k); + } + } + } + } + + return !last_jump_units[stones.back()].empty(); + } +}; diff --git a/C++/game-of-life.cpp b/C++/game-of-life.cpp new file mode 100644 index 000000000..df22a3591 --- /dev/null +++ b/C++/game-of-life.cpp @@ -0,0 +1,33 @@ +// Time: O(m * n) +// Space: O(1) + +class Solution { +public: + void gameOfLife(vector>& board) { + const int m = board.size(), n = m ? board[0].size() : 0; + for (int i = 0; i < m; ++i) { + for (int j = 0; j < n; ++j) { + int count = 0; + // Count live cells in 3x3 block. + for (int I = max(i - 1, 0); I < min(i + 2, m); ++I) { + for (int J = max(j - 1, 0); J < min(j + 2, n); ++J) { + count += board[I][J] & 1; + } + } + // if (count == 4 && board[i][j]) means: + // Any live cell with three live neighbors lives. + // if (count == 3) means: + // Any live cell with two live neighbors. + // Any dead cell with exactly three live neighbors lives. + if ((count == 4 && board[i][j]) || count == 3) { + board[i][j] |= 2; // Mark as live. + } + } + } + for (int i = 0; i < m; ++i) { + for (int j = 0; j < n; ++j) { + board[i][j] >>= 1; // Update to the next state. + } + } + } +}; diff --git a/C++/generalized-abbreviation.cpp b/C++/generalized-abbreviation.cpp new file mode 100644 index 000000000..afb3f9335 --- /dev/null +++ b/C++/generalized-abbreviation.cpp @@ -0,0 +1,29 @@ +// Time: O(n * 2^n) +// Space: O(n) + +class Solution { +public: + vector generateAbbreviations(string word) { + vector res; + string cur; + generateAbbreviationsHelper(word, 0, &cur, &res); + return res; + } + + void generateAbbreviationsHelper(const string& word, int i, string *cur, vector *res) { + if (i == word.length()) { + res->emplace_back(*cur); + return; + } + cur->push_back(word[i]); + generateAbbreviationsHelper(word, i + 1, cur, res); + cur->pop_back(); + if (cur->empty() || not isdigit(cur->back())) { + for (int l = 1; i + l <= word.length(); ++l) { + cur->append(to_string(l)); + generateAbbreviationsHelper(word, i + l, cur, res); + cur->resize(cur->length() - to_string(l).length()); + } + } + } +}; diff --git a/C++/generate-random-point-in-a-circle.cpp b/C++/generate-random-point-in-a-circle.cpp new file mode 100644 index 000000000..668764f69 --- /dev/null +++ b/C++/generate-random-point-in-a-circle.cpp @@ -0,0 +1,34 @@ +// Time: O(1) +// Space: O(1) + +class Solution { +public: + Solution(double radius, double x_center, double y_center) : + radius_(radius), + x_center_(x_center), + y_center_(y_center), + gen_((random_device())()), + uni_(0.0, 1.0) { + + } + + vector randPoint() { + const auto r = radius_ * sqrt(uni_(gen_)); + const auto theta = (2 * M_PI) * uni_(gen_); + return {r * cos(theta) + x_center_, + r * sin(theta) + y_center_}; + } + +private: + double radius_; + double x_center_; + double y_center_; + default_random_engine gen_; + uniform_real_distribution uni_; +}; + +/** + * Your Solution object will be instantiated and called as such: + * Solution obj = new Solution(radius, x_center, y_center); + * vector param_1 = obj.randPoint(); + */ diff --git a/C++/generateMatrix.cpp b/C++/generateMatrix.cpp deleted file mode 100644 index 9ef1713f0..000000000 --- a/C++/generateMatrix.cpp +++ /dev/null @@ -1,36 +0,0 @@ -// Time Complexity: O(n^2) -// Space Complexity: O(n^2) - -class Solution { - public: - vector > generateMatrix(int n) { - vector > v(n, vector(n, 0)); - enum Action {RIGHT, DOWN, LEFT, UP}; - Action action = RIGHT; - for(int i = 0, j = 0, cnt = 0, total = n * n; cnt < total;) { - v[i][j] = ++cnt; - - switch(action) { - case RIGHT: - if(j + 1 < n && v[i][j + 1] == 0) ++j; - else action = DOWN, ++i; - break; - case DOWN: - if(i + 1 < n && v[i + 1][j] == 0) ++i; - else action = LEFT, --j; - break; - case LEFT: - if(j - 1 >= 0 && v[i][j - 1] == 0) --j; - else action = UP, --i; - break; - case UP: - if(i - 1 >= 0 && v[i - 1][j] == 0) --i; - else action = RIGHT, ++j; - break; - default: - break; - } - } - return v; - } -}; diff --git a/C++/generateTrees.cpp b/C++/generateTrees.cpp deleted file mode 100644 index 00c304e8b..000000000 --- a/C++/generateTrees.cpp +++ /dev/null @@ -1,41 +0,0 @@ -// Time Complexity: O( (2n, n) / n ) ~= O( 4^n / n^(3/2) ) -// Space Complexity: O( (2n, n) ) ~= O( 4^n / n^(1/2) ) - -/** - * Definition for binary tree - * struct TreeNode { - * int val; - * TreeNode *left; - * TreeNode *right; - * TreeNode(int x) : val(x), left(NULL), right(NULL) {} - * }; - */ -class Solution { - public: - vector generateTrees(int n) { - return generate(1, n); - } - private: - vector generate(int begin, int end) { - vector subTree; - if(begin > end) { - subTree.push_back(NULL); - } - - for(int k = begin; k <= end; ++k) { - vector leftSubTree = generate(begin, k - 1); - vector rightSubTree = generate(k + 1, end); - - for(auto i : leftSubTree) { - for(auto j : rightSubTree) { - TreeNode *node = new TreeNode(k); - node->left = i; - node->right = j; - subTree.push_back(node); - } - } - } - - return subTree; - } -}; diff --git a/C++/getPermutation.cpp b/C++/getPermutation.cpp deleted file mode 100644 index 66f6b30f0..000000000 --- a/C++/getPermutation.cpp +++ /dev/null @@ -1,44 +0,0 @@ -// Time Complexity: O(n^2) -// Space Complexity: O(n) - -class Solution { - public: - string getPermutation(int n, int k) { - string s(n, '0'); - - for(int i = 0; i < n; ++i) { - s[i] += i + 1; - } - - return kth_permutation(s, k); - } - - private: - int factorial(int n) { - int sum = 1; - for(int i = n; i >= 1; --i) { - sum *= i; - } - return sum; - } - - // Cantor Encoding - template - Sequence kth_permutation(const Sequence &seq, int k) { - const int n = seq.size(); - Sequence ans; - Sequence S(seq); - int base = factorial(n - 1); - --k; - - for(int i = n - 1; i > 0; k %= base, base /= i, --i) { - auto a = next(S.begin(), k / base); - ans.push_back(*a); - S.erase(a); - } - - ans.push_back(S[0]); - - return ans; - } -}; diff --git a/C++/global-and-local-inversions.cpp b/C++/global-and-local-inversions.cpp new file mode 100644 index 000000000..560d49fd8 --- /dev/null +++ b/C++/global-and-local-inversions.cpp @@ -0,0 +1,14 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + bool isIdealPermutation(vector& A) { + for (int i = 0; i < A.size(); ++i) { + if (abs(A[i] - i) > 1) { + return false; + } + } + return true; + } +}; diff --git a/C++/goat-latin.cpp b/C++/goat-latin.cpp new file mode 100644 index 000000000..f74c09720 --- /dev/null +++ b/C++/goat-latin.cpp @@ -0,0 +1,33 @@ +// Time: O(n + w^2), n = w * l, +// n is the length of S, +// w is the number of words, +// l is the average of word lengths +// Space: O(l) + +class Solution { +public: + string toGoatLatin(string S) { + unordered_set vowel{'a', 'e', 'i', 'o', 'u', 'A', 'E', 'I', 'O', 'U'}; + string result, word; + int count = 0; + for (int i = 0; i < S.length(); ++i) { + if (S[i] != ' ') { + word.push_back(S[i]); + if (i != S.length() - 1) { + continue; + } + } + if (!vowel.count(word.front())) { + word.push_back(word.front()); + word = word.substr(1); + } + word += "ma"; + word += string(++count, 'a'); + result += word; + result += " "; + word.clear(); + } + result.pop_back(); + return result; + } +}; diff --git a/C++/graph-valid-tree.cpp b/C++/graph-valid-tree.cpp new file mode 100644 index 000000000..ded8ab274 --- /dev/null +++ b/C++/graph-valid-tree.cpp @@ -0,0 +1,73 @@ +// Time: O(|V| + |E|) +// Space: O(|V| + |E|) + +// Same complexity, but faster version. +class Solution { +public: + bool validTree(int n, vector>& edges) { + if (edges.size() != n - 1) { + return false; + } + + unordered_map> neighbors; + for (const auto& edge : edges) { + neighbors[edge.first].emplace_back(edge.second); + neighbors[edge.second].emplace_back(edge.first); + } + + unordered_set visited; + queue q; + q.emplace(0); + while (!q.empty()) { + const int i = q.front(); + q.pop(); + visited.emplace(i); + for (const auto& node : neighbors[i]) { + if (!visited.count(node)) { + visited.emplace(node); + q.emplace(node); + } + } + } + return visited.size() == n; + } +}; + +// Time: O(|V| + |E|) +// Space: O(|V| + |E|) +class Solution2 { +public: + struct node { + int parent = -1; + vectorneighbors; + }; + + bool validTree(int n, vector>& edges) { + unordered_map nodes; + for (const auto& edge : edges) { + nodes[edge.first].neighbors.emplace_back(edge.second); + nodes[edge.second].neighbors.emplace_back(edge.first); + } + + unordered_set visited; + queue q; + q.emplace(0); + while (!q.empty()) { + const int i = q.front(); + q.pop(); + visited.emplace(i); + for (const auto& node : nodes[i].neighbors) { + if (node != nodes[i].parent) { + if (visited.find(node) != visited.end()) { + return false; + } else { + visited.emplace(node); + nodes[node].parent = i; + q.emplace(node); + } + } + } + } + return visited.size() == n; + } +}; diff --git a/C++/gray-code.cpp b/C++/gray-code.cpp new file mode 100644 index 000000000..e88269a02 --- /dev/null +++ b/C++/gray-code.cpp @@ -0,0 +1,30 @@ +// Time: (2^n) +// Space: O(1) + +class Solution { +public: + vector grayCode(int n) { + vector result = {0}; + for (int i = 0; i < n; ++i) { + for (int j = result.size() - 1; j >= 0; --j) { + result.emplace_back(1 << i | result[j]); + } + } + return result; + } +}; + +// Time: (2^n) +// Space: O(1) +// Proof of closed form formula could be found here: +// http://math.stackexchange.com/questions/425894/proof-of-closed-form-formula-to-convert-a-binary-number-to-its-gray-code +class Solution2 { +public: + vector grayCode(int n) { + vector result; + for (int i = 0; i < 1 << n; ++i) { + result.emplace_back(i >> 1 ^ i); + } + return result; + } +}; diff --git a/C++/group-anagrams.cpp b/C++/group-anagrams.cpp new file mode 100644 index 000000000..dbac16f6a --- /dev/null +++ b/C++/group-anagrams.cpp @@ -0,0 +1,26 @@ +// Time: O(n * glogg), g is the max size of groups. +// Space: O(n) + +class Solution { +public: + vector> groupAnagrams(vector& strs) { + unordered_map> groups; + for (const auto& str : strs) { + string tmp{str}; + sort(tmp.begin(), tmp.end()); + groups[tmp].emplace_back(str); + } + + vector> anagrams; + for (const auto& kvp : groups) { + vector group; + for (const auto& str : kvp.second) { + group.emplace_back(str); + } + sort(group.begin(), group.end()); + anagrams.emplace_back(move(group)); + } + + return anagrams; + } +}; diff --git a/C++/group-shifted-strings.cpp b/C++/group-shifted-strings.cpp new file mode 100644 index 000000000..47dd9e7e1 --- /dev/null +++ b/C++/group-shifted-strings.cpp @@ -0,0 +1,31 @@ +// Time: O(nlogn) +// Space: O(n) + +class Solution { +public: + vector> groupStrings(vector& strings) { + unordered_map> groups; + for (const auto& str : strings) { // Grouping. + groups[hashStr(str)].insert(str); + } + + vector> result; + for (const auto& kvp : groups) { + vector group; + for (auto& str : kvp.second) { // Sorted in a group. + group.emplace_back(move(str)); + } + result.emplace_back(move(group)); + } + + return result; + } + + string hashStr(string str) { + const char base = str[0]; + for (auto& c : str) { + c = 'a' + ((c - base) >= 0 ? c - base : c - base + 26); + } + return str; + } +}; diff --git a/C++/guess-number-higher-or-lower-ii.cpp b/C++/guess-number-higher-or-lower-ii.cpp new file mode 100644 index 000000000..972e05964 --- /dev/null +++ b/C++/guess-number-higher-or-lower-ii.cpp @@ -0,0 +1,18 @@ +// Time: O(n^2) +// Space: O(n^2) + +class Solution { +public: + int getMoneyAmount(int n) { + vector> pay(n + 1, vector(n)); + for (int i = n - 1; i >= 0; --i) { + for (int j = i + 1; j < n; ++j) { + pay[i][j] = numeric_limits::max(); + for (int k = i; k <= j; ++k) { + pay[i][j] = min(pay[i][j], k + 1 + max(pay[i][k - 1], pay[k + 1][j])); + } + } + } + return pay[0][n - 1]; + } +}; diff --git a/C++/guess-number-higher-or-lower.cpp b/C++/guess-number-higher-or-lower.cpp new file mode 100644 index 000000000..8e41a9ba7 --- /dev/null +++ b/C++/guess-number-higher-or-lower.cpp @@ -0,0 +1,23 @@ +// Time: O(logn) +// Space: O(1) + +// Forward declaration of guess API. +// @param num, your guess +// @return -1 if my number is lower, 1 if my number is higher, otherwise return 0 +int guess(int num); + +class Solution { +public: + int guessNumber(int n) { + int left = 1, right = n; + while (left <= right) { + const auto mid = left + (right - left) / 2; + if (guess(mid) <= 0) { + right = mid - 1; + } else { + left = mid + 1; + } + } + return left; + } +}; diff --git a/C++/guess-the-word.cpp b/C++/guess-the-word.cpp new file mode 100644 index 000000000..76ab6b522 --- /dev/null +++ b/C++/guess-the-word.cpp @@ -0,0 +1,135 @@ +// Time: O(n^2) +// Space: O(n) + +/** + * // This is the Master's API interface. + * // You should not implement it, or speculate about its implementation + * class Master { + * public: + * int guess(string word); + * }; + */ +class Solution { +public: + void findSecretWord(vector& wordlist, Master& master) { + vector> H(wordlist.size(), vector(wordlist.size())); + for (int i = 0; i < wordlist.size(); ++i) { + for (int j = 0; j < wordlist.size(); ++j) { + H[i][j] = match(wordlist[i], wordlist[j]); + } + } + + vector possible(wordlist.size()); + iota(possible.begin(), possible.end(), 0); + int n = 0; + while (n < 6) { + auto guess = solve(H, possible); + n = master.guess(wordlist[guess]); + vector new_possible; + for (const auto& j : possible) { + if (H[guess][j] == n) { + new_possible.emplace_back(j); + } + } + possible = new_possible; + } + } + +private: + int solve(const vector>& H, + const vector& possible) { + + vector min_max_group = possible; + int best_guess = -1; + for (const auto& guess : possible) { + vector> groups(7); + for (const auto& j : possible) { + if (j != guess) { + groups[H[guess][j]].emplace_back(j); + } + } + int max_group_i = 0; + for (int i = 0; i < groups.size(); ++i) { + if (groups[i].size() > groups[max_group_i].size()) { + max_group_i = i; + } + } + if (groups[max_group_i].size() < min_max_group.size()) { + min_max_group = groups[max_group_i]; + best_guess = guess; + } + } + return best_guess; + } + + int match(const string& a, const string& b) { + int matches = 0; + for (int i = 0; i < a.length(); ++i) { + if (a[i] == b[i]) { + ++matches; + } + } + return matches; + } +}; + +// Time: O(n^2) +// Space: O(n) +class Solution2 { +public: + void findSecretWord(vector& wordlist, Master& master) { + vector> H(wordlist.size(), vector(wordlist.size())); + for (int i = 0; i < wordlist.size(); ++i) { + for (int j = 0; j < wordlist.size(); ++j) { + H[i][j] = match(wordlist[i], wordlist[j]); + } + } + + vector possible(wordlist.size()); + iota(possible.begin(), possible.end(), 0); + int n = 0; + while (n < 6) { + auto guess = solve(H, possible); + n = master.guess(wordlist[guess]); + vector new_possible; + for (const auto& j : possible) { + if (H[guess][j] == n) { + new_possible.emplace_back(j); + } + } + possible = new_possible; + } + } + +private: + int solve(const vector>& H, + const vector& possible) { + + vector min_max_group = possible; + int best_guess = -1; + for (const auto& guess : possible) { + vector> groups(7); + for (const auto& j : possible) { + if (j != guess) { + groups[H[guess][j]].emplace_back(j); + } + } + int max_group_i = 0; + if (groups[max_group_i].size() < min_max_group.size()) { + min_max_group = groups[max_group_i]; + best_guess = guess; + } + } + return best_guess; + } + + int match(const string& a, const string& b) { + int matches = 0; + for (int i = 0; i < a.length(); ++i) { + if (a[i] == b[i]) { + ++matches; + } + } + return matches; + } +}; diff --git a/C++/h-index-ii.cpp b/C++/h-index-ii.cpp new file mode 100644 index 000000000..c7c7cc9f2 --- /dev/null +++ b/C++/h-index-ii.cpp @@ -0,0 +1,20 @@ +// Time: O(logn) +// Space: O(1) + +class Solution { +public: + int hIndex(vector& citations) { + const int n = citations.size(); + int left = 0; + int right = n - 1; + while (left <= right) { + const auto mid = left + (right - left) / 2; + if (citations[mid] >= n - mid) { + right = mid - 1; + } else { + left = mid + 1; + } + } + return n - left; + } +}; diff --git a/C++/h-index.cpp b/C++/h-index.cpp new file mode 100644 index 000000000..2f6d86c42 --- /dev/null +++ b/C++/h-index.cpp @@ -0,0 +1,46 @@ +// Time: O(n) +// Space: O(n) + +// Counting sort. +class Solution { +public: + int hIndex(vector& citations) { + const auto n = citations.size(); + vector count(n + 1, 0); + for (const auto& x : citations) { + // Put all x >= n in the same bucket. + if (x >= n) { + ++count[n]; + } else { + ++count[x]; + } + } + + int h = 0; + for (int i = n; i >= 0; --i) { + h += count[i]; + if (h >= i) { + return i; + } + } + return h; + } +}; + +// Time: O(nlogn) +// Space: O(1) +class Solution2 { +public: + int hIndex(vector& citations) { + sort(citations.begin(), citations.end(), greater()); + int h = 0; + for (const auto& x : citations) { + if (x >= h + 1) { + ++h; + } else { + break; + } + } + return h; + } +}; diff --git a/C++/hamming-distance.cpp b/C++/hamming-distance.cpp new file mode 100644 index 000000000..32424d864 --- /dev/null +++ b/C++/hamming-distance.cpp @@ -0,0 +1,13 @@ +// Time: O(1) +// Space: O(1) + +class Solution { +public: + int hammingDistance(int x, int y) { + int distance = 0; + for (int z = x ^ y; z; z &= z - 1) { + ++distance; + } + return distance; + } +}; diff --git a/C++/hand-of-straights.cpp b/C++/hand-of-straights.cpp new file mode 100644 index 000000000..dfba85d6b --- /dev/null +++ b/C++/hand-of-straights.cpp @@ -0,0 +1,32 @@ +// Time: O(nlogn) +// Space: O(n) + +class Solution { +public: + bool isNStraightHand(vector& hand, int W) { + if (hand.size() % W) { + return false; + } + + unordered_map counts; + for (const auto& i : hand) { + ++counts[i]; + } + + priority_queue, greater> min_heap(hand.begin(), hand.end()); + for (int i = 0; i < hand.size() / W; ++i) { + while (counts[min_heap.top()] == 0) { + min_heap.pop(); + } + int start = min_heap.top(); min_heap.pop(); + for (int j = 0; j < W; ++j) { + --counts[start]; + if (counts[start] < 0) { + return false; + } + ++start; + } + } + return true; + } +}; diff --git a/C++/happy-number.cpp b/C++/happy-number.cpp new file mode 100644 index 000000000..b251abeb6 --- /dev/null +++ b/C++/happy-number.cpp @@ -0,0 +1,23 @@ +// Time: O(k), where k is the steps to be happy number +// Space: O(k) + +class Solution { +public: + bool isHappy(int n) { + unordered_set visited; + while (n != 1 && !visited.count(n)) { + visited.emplace(n); + n = nextNumber(n); + } + return n == 1; + } + + int nextNumber(int n) { + int sum = 0; + while (n) { + sum += pow(n % 10, 2); + n /= 10; + } + return sum; + } +}; diff --git a/C++/hasCycle.cpp b/C++/hasCycle.cpp deleted file mode 100644 index 4b8df99f5..000000000 --- a/C++/hasCycle.cpp +++ /dev/null @@ -1,27 +0,0 @@ -// Time Complexity: O(n) -// Space Complexity: O(1) - -/** - * Definition for singly-linked list. - * struct ListNode { - * int val; - * ListNode *next; - * ListNode(int x) : val(x), next(NULL) {} - * }; - */ -class Solution { - public: - bool hasCycle(ListNode *head) { - ListNode *slow = head, *fast = head; - - while(fast && fast->next) { - slow = slow->next; - fast = fast->next->next; - - if(slow == fast) - return true; - } - - return false; - } -}; diff --git a/C++/heaters.cpp b/C++/heaters.cpp new file mode 100644 index 000000000..894800bea --- /dev/null +++ b/C++/heaters.cpp @@ -0,0 +1,23 @@ +// Time: O((m + n) * logn), m is the number of the houses, n is the number of the heaters. +// Space: O(1) + +class Solution { +public: + int findRadius(vector& houses, vector& heaters) { + sort(heaters.begin(), heaters.end()); + int min_radius = 0; + for (const auto& house : houses) { + auto equal_or_larger = lower_bound(heaters.cbegin(), heaters.cend(), house); + auto curr_radius = numeric_limits::max(); + if (equal_or_larger != heaters.cend()) { + curr_radius = *equal_or_larger - house; + } + if (equal_or_larger != heaters.cbegin()) { + auto smaller = prev(equal_or_larger); + curr_radius = min(curr_radius, house - *smaller); + } + min_radius = max(min_radius, curr_radius); + } + return min_radius; + } +}; diff --git a/C++/house-robber-ii.cpp b/C++/house-robber-ii.cpp new file mode 100644 index 000000000..5bb26728c --- /dev/null +++ b/C++/house-robber-ii.cpp @@ -0,0 +1,27 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + int rob(vector& nums) { + if (nums.size() == 0) { + return 0; + } + if (nums.size() == 1) { + return nums[0]; + } + + return max(robRange(nums, 0, nums.size() - 1), // Include the first one of nums without the last one. + robRange(nums, 1, nums.size())); // Include the last one of nums without the first one. + } + + int robRange(vector& nums, int start, int end) { + int num_i = nums[start], num_i_1 = 0, num_i_2 = 0; + for (int i = start + 1; i < end; ++i) { + num_i_2 = num_i_1; + num_i_1 = num_i; + num_i = max(nums[i] + num_i_2, num_i_1); + } + return num_i; + } +}; diff --git a/C++/house-robber-iii.cpp b/C++/house-robber-iii.cpp new file mode 100644 index 000000000..2e4746f21 --- /dev/null +++ b/C++/house-robber-iii.cpp @@ -0,0 +1,30 @@ +// Time: O(n) +// Space: O(h) + +/** + * 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: + int rob(TreeNode* root) { + auto res = robHelper(root); + return max(res.first, res.second); + } + +private: + pair robHelper(TreeNode* root) { + if (!root) { + return {0, 0}; + } + auto left = robHelper(root->left); + auto right = robHelper(root->right); + return {root->val + left.second + right.second, + max(left.first, left.second) + max(right.first, right.second)}; + } +}; diff --git a/C++/image-overlap.cpp b/C++/image-overlap.cpp new file mode 100644 index 000000000..205ca8eed --- /dev/null +++ b/C++/image-overlap.cpp @@ -0,0 +1,26 @@ +// Time: O(n^4) +// Space: O(n^2) + +class Solution { +public: + int largestOverlap(vector>& A, vector>& B) { + vector count(pow(2 * A.size() - 1, 2)); + for (int i = 0; i < A.size(); ++i) { + for (int j = 0; j < A[i].size(); ++j) { + if (!A[i][j]) { + continue; + } + for (int m = 0; m < B.size(); ++m) { + for (int n = 0; n < B[m].size(); ++n) { + if (!B[m][n]) { + continue; + } + ++count[(A.size() - 1 + i - m) * (2 * A.size() - 1) + + A.size() - 1 + j - n]; + } + } + } + } + return *max_element(count.cbegin(), count.cend()); + } +}; diff --git a/C++/image-smoother.cpp b/C++/image-smoother.cpp new file mode 100644 index 000000000..2ff85ed0d --- /dev/null +++ b/C++/image-smoother.cpp @@ -0,0 +1,34 @@ +// Time: O(m * n) +// Space: O(1) + +class Solution { +public: + vector> imageSmoother(vector>& M) { + const auto& m = M.size(), &n = M[0].size(); + vector> result(M); + for (int i = 0; i < m; ++i) { + for (int j = 0; j < n; ++j) { + result[i][j] = getGray(M, i, j); + } + } + return result; + } + +private: + int getGray(const vector>& M, int i, int j) { + const auto& m = M.size(), &n = M[0].size(); + double total = 0.0; + int count = 0; + for (int r = -1; r < 2; ++r) { + for (int c = -1; c < 2; ++c) { + const auto& ii = i + r; + const auto& jj = j + c; + if (0 <= ii && ii < m && 0 <= jj && jj < n) { + total += M[ii][jj]; + ++count; + } + } + } + return static_cast(total / count); + } +}; diff --git a/C++/implement-magic-dictionary.cpp b/C++/implement-magic-dictionary.cpp new file mode 100644 index 000000000..11345c08d --- /dev/null +++ b/C++/implement-magic-dictionary.cpp @@ -0,0 +1,82 @@ +// Time: O(n), n is the length of the word +// Space: O(d) + +class MagicDictionary { +public: + /** Initialize your data structure here. */ + MagicDictionary() { + + } + + /** Build a dictionary through a list of words */ + void buildDict(vector dict) { + string result; + for (const auto& s : dict) { + trie_.Insert(s); + } + } + + /** Returns if there is any word in the trie that equals to the given word after modifying exactly one character */ + bool search(string word) { + return find(word, &trie_, 0, true); + } + +private: + struct TrieNode { + bool isString = false; + unordered_map leaves; + + void Insert(const string& s) { + auto* p = this; + for (const auto& c : s) { + if (p->leaves.find(c) == p->leaves.cend()) { + p->leaves[c] = new TrieNode; + } + p = p->leaves[c]; + } + p->isString = true; + } + + ~TrieNode() { + for (auto& kv : leaves) { + if (kv.second) { + delete kv.second; + } + } + } + }; + + bool find(const string& word, TrieNode *curr, int i, bool mistakeAllowed) { + if (i == word.length()) { + return curr->isString && !mistakeAllowed; + } + + if (!curr->leaves.count(word[i])) { + return mistakeAllowed ? + any_of(curr->leaves.begin(), curr->leaves.end(), + [&](const pair& kvp) { + return find(word, kvp.second, i + 1, false); + }) : + false; + } + + if (mistakeAllowed) { + return find(word, curr->leaves[word[i]], i + 1, true) || + any_of(curr->leaves.begin(), curr->leaves.end(), + [&](const pair& kvp) { + return kvp.first != word[i] && find(word, kvp.second, i + 1, false); + }); + } + return find(word, curr->leaves[word[i]], i + 1, false); + } + + TrieNode trie_; +}; + +/** + * Your MagicDictionary object will be instantiated and called as such: + * MagicDictionary obj = new MagicDictionary(); + * obj.buildDict(dict); + * bool param_2 = obj.search(word); + */ + diff --git a/C++/implement-queue-using-stacks.cpp b/C++/implement-queue-using-stacks.cpp new file mode 100644 index 000000000..0392d858c --- /dev/null +++ b/C++/implement-queue-using-stacks.cpp @@ -0,0 +1,39 @@ +// Time: O(1), amortized +// Space: O(n) + +class Queue { +public: + // Push element x to the back of queue. + void push(int x) { + A_.emplace(x); + } + + // Removes the element from in front of queue. + void pop(void) { + peek(); + B_.pop(); + } + + // Get the front element. + int peek(void) { + if (B_.empty()) { + // Transfers the elements in A_ to B_. + while (!A_.empty()) { + B_.emplace(A_.top()); + A_.pop(); + } + } + if (B_.empty()) { // B_ is still empty! + throw length_error("empty queue"); + } + return B_.top(); + } + + // Return whether the queue is empty. + bool empty(void) { + return A_.empty() && B_.empty(); + } + + private: + stack A_, B_; +}; diff --git a/C++/implement-rand10-using-rand7.cpp b/C++/implement-rand10-using-rand7.cpp new file mode 100644 index 000000000..5dd79ad31 --- /dev/null +++ b/C++/implement-rand10-using-rand7.cpp @@ -0,0 +1,47 @@ +// Time: O(1.199), counted by statistics, limit would be O(log10/log7) = O(1.183) +// Space: O(1) + +// The rand7() API is already defined for you. +// int rand7(); +// @return a random integer in the range 1 to 7 + +// Reference: https://leetcode.com/problems/implement-rand10-using-rand7/discuss/151567/C++JavaPython-Average-1.199-Call-rand7-Per-rand10 +class Solution { +public: + int rand10() { + while (cache_.empty()) { + generate(); + } + auto result = cache_.back(); cache_.pop_back(); + return result; + } + +private: + void generate() { + static const int n = 19; + uint64_t curr = 0, range = static_cast(pow(7, n)); + for (int i = 0; i < n; ++i) { + curr += static_cast(pow(7, i)) * (rand7() - 1); + } + while (curr < range / 10 * 10) { + cache_.emplace_back(curr % 10 + 1); + curr /= 10; + range /= 10; + } + } + vector cache_; +}; + +// Time: O(2 * (1 + (9/49) + (9/49)^2 + ...)) = O(2/(1-(9/49)) = O(2.45) +// Space: O(1) +class Solution2 { +public: + int rand10() { + while (true) { + int x = (rand7() - 1) * 7 + (rand7() - 1); + if (x < 40) { + return x % 10 + 1; + } + } + } +}; diff --git a/C++/implement-stack-using-queues.cpp b/C++/implement-stack-using-queues.cpp new file mode 100644 index 000000000..baf0b5789 --- /dev/null +++ b/C++/implement-stack-using-queues.cpp @@ -0,0 +1,66 @@ +// Time: push: O(n), pop: O(1), top: O(1) +// Space: O(n) + +class Stack { +public: + queue q_; + + // Push element x onto stack. + void push(int x) { // O(n) + q_.emplace(x); + for (int i = 0; i < q_.size() - 1; ++i) { + q_.emplace(q_.front()); + q_.pop(); + } + } + + // Remove the element on top of the stack. + void pop() { // O(1) + q_.pop(); + } + + // Get the top element. + int top() { // O(1) + return q_.front(); + } + + // Return whether the stack is empty. + bool empty() { // O(1) + return q_.empty(); + } +}; + +// Time: push: O(1), pop: O(n), top: O(1) +// Space: O(n) +class Stack2 { +public: + queue q_; + int top_; + + // Push element x onto stack. + void push(int x) { // O(1) + q_.emplace(x); + top_ = x; + } + + // Remove the element on top of the stack. + void pop() { // O(n) + for (int i = 0; i < q_.size() - 1; ++i) { + top_ = q_.front(); + q_.emplace(top_); + q_.pop(); + } + q_.pop(); + } + + // Get the top element. + int top() { // O(1) + return top_; + } + + // Return whether the stack is empty. + bool empty() { // O(1) + return q_.empty(); + } +}; + diff --git a/C++/implement-strstr.cpp b/C++/implement-strstr.cpp new file mode 100644 index 000000000..831645f8b --- /dev/null +++ b/C++/implement-strstr.cpp @@ -0,0 +1,62 @@ +// Time: O(n + k) +// Space: O(k) + +// Wiki of KMP algorithm: +// http://en.wikipedia.org/wiki/Knuth-Morris-Pratt_algorithm +class Solution { +public: + int strStr(string haystack, string needle) { + if (needle.empty()) { + return 0; + } + + return KMP(haystack, needle); + } + + int KMP(const string& text, const string& pattern) { + const vector prefix = getPrefix(pattern); + int j = -1; + for (int i = 0; i < text.length(); ++i) { + while (j > -1 && pattern[j + 1] != text[i]) { + j = prefix[j]; + } + if (pattern[j + 1] == text[i]) { + ++j; + } + if (j == pattern.length() - 1) { + return i - j; + } + } + return -1; + } + + vector getPrefix(const string& pattern) { + vector prefix(pattern.length(), -1); + int j = -1; + for (int i = 1; i < pattern.length(); ++i) { + while (j > -1 && pattern[j + 1] != pattern[i]) { + j = prefix[j]; + } + if (pattern[j + 1] == pattern[i]) { + ++j; + } + prefix[i] = j; + } + return prefix; + } +}; + + +// Time: O(n * k) +// Space: O(k) +class Solution2 { +public: + int strStr(string haystack, string needle) { + for (int i = 0; i + needle.length() < haystack.length() + 1; ++i) { + if (haystack.substr(i, needle.length()) == needle) { + return i; + } + } + return -1; + } +}; diff --git a/C++/implement-trie-prefix-tree.cpp b/C++/implement-trie-prefix-tree.cpp new file mode 100644 index 000000000..8a94245dd --- /dev/null +++ b/C++/implement-trie-prefix-tree.cpp @@ -0,0 +1,66 @@ +// Time: O(n), per operation +// Space: O(1) + +class TrieNode { +public: + // Initialize your data structure here. + TrieNode() : is_string(false) { + + } + bool is_string; + unordered_map leaves; +}; + +class Trie { +public: + Trie() { + root_ = new TrieNode(); + } + + // Inserts a word into the trie. + void insert(string word) { + auto *cur = root_; + for (const auto& c : word) { + if (!cur->leaves.count(c)) { + cur->leaves[c] = new TrieNode(); + } + cur = cur->leaves[c]; + } + cur->is_string = true; + } + + // Returns if the word is in the trie. + bool search(string word) { + auto *node = childSearch(word); + if (node) { + return node->is_string; + } + return false; + } + + // Returns if there is any word in the trie + // that starts with the given prefix. + bool startsWith(string prefix) { + return childSearch(prefix); + } + + TrieNode *childSearch(const string& word) { + auto *cur = root_; + for (const auto& c : word) { + if (cur->leaves.count(c)) { + cur = cur->leaves[c]; + } else { + return nullptr; + } + } + return cur; + } + +private: + TrieNode *root_; +}; + +// Your Trie object will be instantiated and called as such: +// Trie trie; +// trie.insert("somestring"); +// trie.search("key"); diff --git a/C++/increasing-subsequences.cpp b/C++/increasing-subsequences.cpp new file mode 100644 index 000000000..50cd7c944 --- /dev/null +++ b/C++/increasing-subsequences.cpp @@ -0,0 +1,30 @@ +// Time: O(n * 2^n) +// Space: O(n^2) + +class Solution { +public: + vector> findSubsequences(vector& nums) { + vector> result; + vector seq; + findSubsequencesHelper(nums, 0, &seq, &result); + return result; + } + + void findSubsequencesHelper(const vector& nums, int i, + vector *seq, + vector> *result) { + if (seq->size() >= 2) { + result->emplace_back(*seq); + } + unordered_set lookup; + for (; i < nums.size(); ++i) { + if ((seq->empty() || nums[i] >= seq->back()) && + lookup.find(nums[i]) == lookup.end()) { + lookup.emplace(nums[i]); + seq->emplace_back(nums[i]); + findSubsequencesHelper(nums, i + 1, seq, result); + seq->pop_back(); + } + } + } +}; diff --git a/C++/increasing-triplet-subsequence.cpp b/C++/increasing-triplet-subsequence.cpp new file mode 100644 index 000000000..8b6066111 --- /dev/null +++ b/C++/increasing-triplet-subsequence.cpp @@ -0,0 +1,42 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + bool increasingTriplet(vector& nums) { + int min = numeric_limits::max(), a = numeric_limits::max(), b = numeric_limits::max(); + for (const auto& c : nums) { + if (min >= c) { + min = c; + } else if (b >= c) { + a = min, b = c; + } else { // a < b < c + return true; + } + } + return false; + } +}; + +// Time: O(n * logk) +// Space: O(k) +// Generalization of k-uplet. +class Solution_Generalization { +public: + bool increasingTriplet(vector& nums) { + return increasingKUplet(nums, 3); + } + +private: + bool increasingKUplet(const vector& nums, const size_t k) { + vector inc(k - 1, numeric_limits::max()); + for (const auto& num : nums) { + auto it = lower_bound(inc.begin(), inc.end(), num); + if (distance(inc.begin(), it) >= k - 1) { + return true; + } + *it = num; + } + return k == 0; + } +}; diff --git a/C++/inorder-successor-in-bst.cpp b/C++/inorder-successor-in-bst.cpp new file mode 100644 index 000000000..7abac51c1 --- /dev/null +++ b/C++/inorder-successor-in-bst.cpp @@ -0,0 +1,38 @@ +// Time: O(h) +// Space: O(1) + +/** + * 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* inorderSuccessor(TreeNode* root, TreeNode* p) { + // If it has right subtree. + if (p && p->right) { + p = p->right; + while (p->left) { + p = p->left; + } + return p; + } + + // Search from root. + TreeNode *successor = nullptr; + while (root && root != p) { + if (root->val > p->val) { + successor = root; + root = root->left; + } else { + root = root->right; + } + } + + return successor; + } +}; diff --git a/C++/insert-delete-getrandom-o1-duplicates-allowed.cpp b/C++/insert-delete-getrandom-o1-duplicates-allowed.cpp new file mode 100644 index 000000000..7e27d4891 --- /dev/null +++ b/C++/insert-delete-getrandom-o1-duplicates-allowed.cpp @@ -0,0 +1,103 @@ +// Time: O(1) +// Space: O(n) + +class RandomizedCollection { +public: + /** Initialize your data structure here. */ + RandomizedCollection() { + + } + + /** Inserts a value to the collection. Returns true if the collection did not already contain the specified element. */ + bool insert(int val) { + bool has = used_.count(val); + + list_.emplace_back(val); + used_[val].emplace_back(list_.size() - 1); + + return !has; + } + + /** Removes a value from the collection. Returns true if the collection contained the specified element. */ + bool remove(int val) { + if (!used_.count(val)) { + return false; + } + + used_[list_.back()].back() = used_[val].back(); + swap(list_[used_[val].back()], list_.back()); + + used_[val].pop_back(); + if (used_[val].empty()) { + used_.erase(val); + } + list_.pop_back(); + + return true; + } + + /** Get a random element from the collection. */ + int getRandom() { + return list_[rand() % list_.size()]; + } + +private: + vector list_; + unordered_map> used_; +}; + + +// Time: O(1) +// Space: O(n) +class RandomizedCollection2 { +public: + /** Initialize your data structure here. */ + RandomizedCollection2() { + + } + + /** Inserts a value to the collection. Returns true if the collection did not already contain the specified element. */ + bool insert(int val) { + bool has = used_.count(val); + + list_.emplace_back(val); + used_.emplace(val, list_.size() - 1); + + return !has; + } + + /** Removes a value from the collection. Returns true if the collection contained the specified element. */ + bool remove(int val) { + if (!used_.count(val)) { + return false; + } + + auto it_to_delete = used_.find(val); + auto it_to_back = used_.find(list_.back()); + it_to_back->second = it_to_delete->second; + swap(list_[it_to_delete->second], list_.back()); + + used_.erase(it_to_delete); + list_.pop_back(); + + return true; + } + + /** Get a random element from the collection. */ + int getRandom() { + return list_[rand() % list_.size()]; + } + +private: + vector list_; + unordered_multimap used_; +}; + +/** + * Your RandomizedCollection object will be instantiated and called as such: + * RandomizedCollection obj = new RandomizedCollection(); + * bool param_1 = obj.insert(val); + * bool param_2 = obj.remove(val); + * int param_3 = obj.getRandom(); + */ + diff --git a/C++/insert-delete-getrandom-o1.cpp b/C++/insert-delete-getrandom-o1.cpp new file mode 100644 index 000000000..cbc097811 --- /dev/null +++ b/C++/insert-delete-getrandom-o1.cpp @@ -0,0 +1,55 @@ +// Time: O(1) +// Space: O(n) + +class RandomizedSet { +public: + /** Initialize your data structure here. */ + RandomizedSet() { + + } + + /** Inserts a value to the set. Returns true if the set did not already contain the specified element. */ + bool insert(int val) { + if (used_.count(val)) { + return false; + } + + set_.emplace_back(val); + used_[val] = set_.size() - 1; + + return true; + } + + /** Removes a value from the set. Returns true if the set contained the specified element. */ + bool remove(int val) { + if (!used_.count(val)) { + return false; + } + + used_[set_.back()] = used_[val]; + swap(set_[used_[val]], set_.back()); + + used_.erase(val); + set_.pop_back(); + + return true; + } + + /** Get a random element from the set. */ + int getRandom() { + return set_[rand() % set_.size()]; + } + +private: + vector set_; + unordered_map used_; +}; + +/** + * Your RandomizedSet object will be instantiated and called as such: + * RandomizedSet obj = new RandomizedSet(); + * bool param_1 = obj.insert(val); + * bool param_2 = obj.remove(val); + * int param_3 = obj.getRandom(); + */ + diff --git a/C++/insert-interval.cpp b/C++/insert-interval.cpp new file mode 100644 index 000000000..8a5bc9120 --- /dev/null +++ b/C++/insert-interval.cpp @@ -0,0 +1,35 @@ +// Time: O(n) +// Space: O(1) + +/** + * Definition for an interval. + * struct Interval { + * int start; + * int end; + * Interval() : start(0), end(0) {} + * Interval(int s, int e) : start(s), end(e) {} + * }; + */ +class Solution { +public: + vector insert(vector& intervals, Interval newInterval) { + size_t i = 0; + vector result; + // Insert intervals appeared before newInterval. + while (i < intervals.size() && newInterval.start > intervals[i].end) { + result.emplace_back(intervals[i++]); + } + + // Merge intervals that overlap with newInterval. + while (i < intervals.size() && newInterval.end >= intervals[i].start) { + newInterval = {min(newInterval.start, intervals[i].start), + max(newInterval.end, intervals[i].end)}; + ++i; + } + result.emplace_back(newInterval); + + // Insert intervals appearing after newInterval. + result.insert(result.end(), intervals.cbegin() + i, intervals.cend()); + return result; + } +}; diff --git a/C++/insert-into-a-binary-search-tree.cpp b/C++/insert-into-a-binary-search-tree.cpp new file mode 100644 index 000000000..cbf16a683 --- /dev/null +++ b/C++/insert-into-a-binary-search-tree.cpp @@ -0,0 +1,53 @@ +// Time: O(h) +// Space: O(1) + +/** + * 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* insertIntoBST(TreeNode* root, int val) { + TreeNode *curr = root, *parent = nullptr; + while (curr) { + parent = curr; + if (val <= curr->val) { + curr = curr->left; + } else { + curr = curr->right; + } + } + if (!parent) { + root = new TreeNode(val); + } else if (val <= parent->val) { + parent->left = new TreeNode(val); + } else { + parent->right = new TreeNode(val); + } + return root; + } +}; + + +// Time: O(h) +// Space: O(h) +class Solution2 { +public: + TreeNode* insertIntoBST(TreeNode* root, int val) { + if (!root) { + root = new TreeNode(val); + } else { + if (val <= root->val) { + root->left = insertIntoBST(root->left, val); + } else { + root->right = insertIntoBST(root->right, val); + } + } + return root; + } +}; diff --git a/C++/insert-into-a-cyclic-sorted-list.cpp b/C++/insert-into-a-cyclic-sorted-list.cpp new file mode 100644 index 000000000..40644c85c --- /dev/null +++ b/C++/insert-into-a-cyclic-sorted-list.cpp @@ -0,0 +1,56 @@ +// Time: O(n) +// Space: O(1) + +/* +// Definition for a Node. +class Node { +public: + int val; + Node* next; + + Node() {} + + Node(int _val, Node* _next) { + val = _val; + next = _next; + } +}; +*/ +class Solution { +public: + Node* insert(Node* head, int insertVal) { + if (head == nullptr) { + auto node = new Node(insertVal, nullptr); + node->next = node; + return node; + } + auto curr = head; + while (true) { + if (curr->val < curr->next->val) { + if (curr->val <= insertVal && + insertVal <= curr->next->val) { + insertAfter(curr, insertVal); + break; + } + } else if (curr->val > curr->next->val) { + if (curr->val <= insertVal || + insertVal <= curr->next->val) { + insertAfter(curr, insertVal); + break; + } + } else { + if (curr->next == head) { + insertAfter(curr, insertVal); + break; + } + } + curr = curr->next; + } + return head; + } + +private: + void insertAfter(Node *node, int val) { + node->next = new Node(val, node->next); + } +}; diff --git a/C++/insert.cpp b/C++/insert.cpp deleted file mode 100644 index 12a2a6e10..000000000 --- a/C++/insert.cpp +++ /dev/null @@ -1,37 +0,0 @@ -// Time Complexity: O(n) -// Space Complexity: O(1) - -/** - * Definition for an interval. - * struct Interval { - * int start; - * int end; - * Interval() : start(0), end(0) {} - * Interval(int s, int e) : start(s), end(e) {} - * }; - */ -class Solution { - public: - vector insert(vector &intervals, Interval newInterval) { - vector ans; - auto n = intervals.size(); - for(int i = 0; i < n; ++i) { - if (newInterval.end < intervals[i].start) { // not overlapped - ans.push_back(newInterval); - for(; i < n; ++i) - ans.push_back(intervals[i]); - return ans; - } - else if (newInterval.start > intervals[i].end) { // not overlapped - ans.push_back(intervals[i]); - } - else { // merge - newInterval.start = min(newInterval.start, intervals[i].start); - newInterval.end = max(newInterval.end, intervals[i].end); - } - } - - ans.push_back(newInterval); - return ans; - } -}; diff --git a/C++/insertion-sort-list.cpp b/C++/insertion-sort-list.cpp new file mode 100644 index 000000000..d329155e5 --- /dev/null +++ b/C++/insertion-sort-list.cpp @@ -0,0 +1,37 @@ +// Time: O(n^2) +// Space: O(1) + +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode(int x) : val(x), next(NULL) {} + * }; + */ +class Solution { + public: + ListNode *insertionSortList(ListNode *head) { + ListNode dummy{numeric_limits::min()}; + + auto curr = head; + ListNode *position = nullptr; + + while (curr) { + position = findInsertPosition(&dummy, curr->val); + ListNode *tmp = curr->next; + curr->next = position->next; + position->next = curr; + curr = tmp; + } + + return dummy.next; + } + + ListNode* findInsertPosition(ListNode *head, int x) { + ListNode *prev = nullptr; + for (auto curr = head; curr && curr->val <= x; + prev = curr, curr = curr->next); + return prev; + } +}; diff --git a/C++/insertionSortList.cpp b/C++/insertionSortList.cpp deleted file mode 100644 index 776d6ded6..000000000 --- a/C++/insertionSortList.cpp +++ /dev/null @@ -1,38 +0,0 @@ -// Time Complexity: O(n^2) -// Space Complexity: O(1) - -/** - * Definition for singly-linked list. - * struct ListNode { - * int val; - * ListNode *next; - * ListNode(int x) : val(x), next(NULL) {} - * }; - */ -class Solution { - public: - ListNode *insertionSortList(ListNode *head) { - ListNode dummy(INT_MIN); - - ListNode *cur = head; - ListNode *prev = NULL; - ListNode *pos = head; - - while(cur) { - pos = findInsertPos(&dummy, cur->val); - ListNode *tmp = cur->next; - cur->next = pos->next; - pos->next = cur; - cur = tmp; - } - - return dummy.next; - } - - ListNode* findInsertPos(ListNode *head, int x) { - ListNode *pre = NULL; - for (ListNode *cur = head; cur && cur->val <= x; - pre = cur, cur = cur->next); - return pre; - } -}; diff --git a/C++/integer-break.cpp b/C++/integer-break.cpp new file mode 100644 index 000000000..824ae28ea --- /dev/null +++ b/C++/integer-break.cpp @@ -0,0 +1,63 @@ +// Time: O(logn), pow is O(logn). +// Space: O(1) + +class Solution { +public: + int integerBreak(int n) { + if (n < 4) { + return n - 1; + } + + // Proof. + // 1. Let n = a1 + a2 + ... + ak, product = a1 * a2 * ... * ak + // - For each ai >= 4, we can always maximize the product by: + // ai <= 2 * (ai - 2) + // - For each aj >= 5, we can always maximize the product by: + // aj <= 3 * (aj - 3) + // + // Conclusion 1: + // - For n >= 4, the max of the product must be in the form of + // 3^a * 2^b, s.t. 3a + 2b = n + // + // 2. To maximize the product = 3^a * 2^b s.t. 3a + 2b = n + // - For each b >= 3, we can always maximize the product by: + // 3^a * 2^b <= 3^(a+2) * 2^(b-3) s.t. 3(a+2) + 2(b-3) = n + // + // Conclusion 2: + // - For n >= 4, the max of the product must be in the form of + // 3^Q * 2^R, 0 <= R < 3 s.t. 3Q + 2R = n + // i.e. + // if n = 3Q + 0, the max of the product = 3^Q * 2^0 + // if n = 3Q + 2, the max of the product = 3^Q * 2^1 + // if n = 3Q + 2*2, the max of the product = 3^Q * 2^2 + + int res = 0; + if (n % 3 == 0) { // n = 3Q + 0, the max is 3^Q * 2^0 + res = pow(3, n / 3); + } else if (n % 3 == 2) { // n = 3Q + 2, the max is 3^Q * 2^1 + res = pow(3, n / 3) * 2; + } else { // n = 3Q + 4, the max is 3^Q * 2^2 + res = pow(3, n / 3 - 1) * 4; + } + return res; + } +}; + +// Time: O(n) +// Space: O(1) +// DP solution. +class Solution2 { +public: + int integerBreak(int n) { + if (n < 4) { + return n - 1; + } + + // integerBreak(n) = max(integerBreak(n - 2) * 2, integerBreak(n - 3) * 3) + vector res{0, 1, 2, 3}; + for (int i = 4; i <= n; ++i) { + res[i % 4] = max(res[(i - 2) % 4] * 2, res[(i - 3) % 4] * 3); + } + return res[n % 4]; + } +}; diff --git a/C++/integer-replacement.cpp b/C++/integer-replacement.cpp new file mode 100644 index 000000000..9539e1c8f --- /dev/null +++ b/C++/integer-replacement.cpp @@ -0,0 +1,55 @@ +// Time: O(logn) +// Space: O(1) + +// Iterative solution. +class Solution { +public: + int integerReplacement(int n) { + if (n == 2147483647) { + return 2 + integerReplacement(n / 2 + 1); + } + + int result = 0; + while (n != 1) { + const auto b = n & 3; + if (n == 3) { + --n; + } else if (b == 3) { + ++n; + } else if (b == 1) { + --n; + } else { + n /= 2; + } + ++result; + } + return result; + } +}; + +// Time: O(logn) +// Space: O(logn) +// Recursive solution +class Solution2 { +public: + int integerReplacement(int n) { + if (n == 2147483647) { + return 2 + integerReplacement(n / 2 + 1); + } + + if (n < 4) { + switch (n % 4) { + case 0: case 1: return 0; + case 2: return 1; + case 3: return 2; + } + } + + switch (n % 4) { + case 0: case 2: return integerReplacement(n / 2) + 1; + case 1: return integerReplacement((n - 1) / 4) + 3; + case 3: return integerReplacement((n + 1) / 4) + 3; + } + return 0; + } +}; diff --git a/C++/integer-to-english-words.cpp b/C++/integer-to-english-words.cpp new file mode 100644 index 000000000..f311b5242 --- /dev/null +++ b/C++/integer-to-english-words.cpp @@ -0,0 +1,65 @@ +// Time: O(logn) = O(1), n is the value of the integer, which is less than 2^31 - 1 +// Space: O(1) + +class Solution { +public: + string numberToWords(int num) { + if (num == 0) { + return "Zero"; + } + const unordered_map lookup = {{0, "Zero"}, {1, "One"}, {2, "Two"}, + {3, "Three"}, {4, "Four"}, {5, "Five"}, + {6, "Six"}, {7, "Seven"}, {8, "Eight"}, + {9, "Nine"}, {10, "Ten"}, {11, "Eleven"}, + {12, "Twelve"}, {13, "Thirteen"}, {14, "Fourteen"}, + {15, "Fifteen"}, {16, "Sixteen"}, {17, "Seventeen"}, + {18, "Eighteen"}, {19, "Nineteen"}, {20, "Twenty"}, + {30, "Thirty"}, {40, "Forty"}, {50, "Fifty"}, + {60, "Sixty"}, {70, "Seventy"}, {80, "Eighty"}, + {90, "Ninety"}}; + const vector unit{"", "Thousand", "Million", "Billion"}; + + vector res; + int i = 0; + while (num) { + const int cur = num % 1000; + if (num % 1000) { + res.emplace_back(threeDigits(cur, lookup, unit[i])); + } + num /= 1000; + ++i; + } + reverse(res.begin(), res.end()); + return join(res, " "); + } + + string join(const vector& strings, const string& delim) { + if (strings.empty()) { + return ""; + } + ostringstream imploded; + copy(strings.begin(), prev(strings.end()), ostream_iterator(imploded, delim.c_str())); + return imploded.str() + *prev(strings.end()); + } + + string threeDigits(const int& num, const unordered_map& lookup, const string& unit) { + vector res; + if (num / 100) { + res.emplace_back(lookup.find(num / 100)->second + " " + "Hundred"); + } + if (num % 100) { + res.emplace_back(twoDigits(num % 100, lookup)); + } + if (!unit.empty()) { + res.emplace_back(unit); + } + return join(res, " "); + } + + string twoDigits(const int& num, const unordered_map& lookup) { + if (lookup.find(num) != lookup.end()) { + return lookup.find(num)->second; + } + return lookup.find((num / 10) * 10)->second + " " + lookup.find(num % 10)->second; + } +}; diff --git a/C++/integer-to-roman.cpp b/C++/integer-to-roman.cpp new file mode 100644 index 000000000..4b4d4fc9c --- /dev/null +++ b/C++/integer-to-roman.cpp @@ -0,0 +1,24 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + string intToRoman(int num) { + const vector nums{1000, 900, 500, 400, 100, 90, + 50, 40, 10, 9, 5, 4, 1}; + const vector romans{"M", "CM", "D", "CD", "C", "XC", "L", + "XL", "X", "IX", "V", "IV", "I"}; + + string result; + int i = 0; + while (num > 0) { + int times = num / nums[i]; + while (times--) { + num -= nums[i]; + result.append(romans[i]); + } + ++i; + } + return result; + } +}; diff --git a/C++/intersection-of-two-arrays-ii.cpp b/C++/intersection-of-two-arrays-ii.cpp new file mode 100644 index 000000000..c79fd27cc --- /dev/null +++ b/C++/intersection-of-two-arrays-ii.cpp @@ -0,0 +1,124 @@ +// If the given array is not sorted and the memory is unlimited: +// - Time: O(m + n) +// - Space: O(min(m, n)) +// elif the given array is already sorted: +// if m << n or m >> n: +// - Time: O(min(m, n) * log(max(m, n))) +// - Space: O(1) +// else: +// - Time: O(m + n) +// - Soace: O(1) +// else: (the given array is not sorted and the memory is limited) +// - Time: O(max(m, n) * log(max(m, n))) +// - Space: O(1) + +// If the given array is not sorted and the memory is unlimited. +// Time: O(m + n) +// Space: O(min(m, n)) +// Hash solution. +class Solution { +public: + vector intersect(vector& nums1, vector& nums2) { + if (nums1.size() > nums2.size()) { + return intersect(nums2, nums1); + } + + unordered_map lookup; + for (const auto& i : nums1) { + ++lookup[i]; + } + + vector result; + for (const auto& i : nums2) { + if (lookup[i] > 0) { + result.emplace_back(i); + --lookup[i]; + } + } + + return result; + } +}; + + +// If the given array is already sorted, and the memory is limited, and (m << n or m >> n). +// Time: O(min(m, n) * log(max(m, n))) +// Space: O(1) +// Binary search solution. +class Solution { +public: + vector intersect(vector& nums1, vector& nums2) { + if (nums1.size() > nums2.size()) { + return intersect(nums2, nums1); + } + + // Make sure it is sorted, doesn't count in time. + sort(nums1.begin(), nums1.end()); + sort(nums2.begin(), nums2.end()); + + vector result; + auto it = nums2.cbegin(); + for (const auto& i : nums1) { + it = lower_bound(it, nums2.cend(), i); + if (it != nums2.end() && *it == i) { + result.emplace_back(*it++); + } + } + + return result; + } +}; + + +// If the given array is already sorted, and the memory is limited or m ~ n. +// Time: O(m + n) +// Soace: O(1) +// Two pointers solution. +class Solution { +public: + vector intersect(vector& nums1, vector& nums2) { + vector result; + // Make sure it is sorted, doesn't count in time. + sort(nums1.begin(), nums1.end()); + sort(nums2.begin(), nums2.end()); + auto it1 = nums1.cbegin(), it2 = nums2.cbegin(); + while (it1 != nums1.cend() && it2 != nums2.cend()) { + if (*it1 < *it2) { + ++it1; + } else if (*it1 > *it2) { + ++it2; + } else { + result.emplace_back(*it1); + ++it1, ++it2; + } + } + return result; + } +}; + + +// If the given array is not sorted, and the memory is limited. +// Time: O(max(m, n) * log(max(m, n))) +// Space: O(1) +// Two pointers solution. +class Solution { +public: + vector intersect(vector& nums1, vector& nums2) { + vector result; + // O(max(m, n) * log(max(m, n))) + sort(nums1.begin(), nums1.end()); + sort(nums2.begin(), nums2.end()); + auto it1 = nums1.cbegin(), it2 = nums2.cbegin(); + while (it1 != nums1.cend() && it2 != nums2.cend()) { + if (*it1 < *it2) { + ++it1; + } else if (*it1 > *it2) { + ++it2; + } else { + result.emplace_back(*it1); + ++it1, ++it2; + } + } + return result; + } +}; diff --git a/C++/intersection-of-two-arrays.cpp b/C++/intersection-of-two-arrays.cpp new file mode 100644 index 000000000..a9f84bfc4 --- /dev/null +++ b/C++/intersection-of-two-arrays.cpp @@ -0,0 +1,79 @@ +// Time: O(m + n) +// Space: O(min(m, n)) + +// Hash solution. +class Solution { +public: + vector intersection(vector& nums1, vector& nums2) { + if (nums1.size() > nums2.size()) { + return intersection(nums2, nums1); + } + + unordered_set lookup{nums1.cbegin(), nums1.cend()}; + + vector result; + for (const auto& i : nums2) { + if (lookup.count(i)) { + result.emplace_back(i); + lookup.erase(i); + } + } + + return result; + } +}; + + +// Time: O(max(m, n) * log(max(m, n))) +// Space: O(1) +// Binary search solution. +class Solution2 { +public: + vector intersection(vector& nums1, vector& nums2) { + if (nums1.size() > nums2.size()) { + return intersection(nums2, nums1); + } + + sort(nums1.begin(), nums1.end()); + sort(nums2.begin(), nums2.end()); + + vector result; + auto it = nums2.cbegin(); + for (const auto& i : nums1) { + it = lower_bound(it, nums2.cend(), i); + if (it != nums2.end() && *it == i) { + result.emplace_back(*it); + it = upper_bound(it, nums2.cend(), i); + } + } + + return result; + } +}; + + +// Time: O(max(m, n) * log(max(m, n))) +// Space: O(1) +// Two pointers solution. +class Solution3 { +public: + vector intersection(vector& nums1, vector& nums2) { + vector result; + sort(nums1.begin(), nums1.end()); + sort(nums2.begin(), nums2.end()); + auto it1 = nums1.cbegin(), it2 = nums2.cbegin(); + while (it1 != nums1.cend() && it2 != nums2.cend()) { + if (*it1 < *it2) { + ++it1; + } else if (*it1 > *it2) { + ++it2; + } else { + if (result.empty() || result.back() != *it1) { + result.emplace_back(*it1); + } + ++it1, ++it2; + } + } + return result; + } +}; diff --git a/C++/intersection-of-two-linked-lists.cpp b/C++/intersection-of-two-linked-lists.cpp new file mode 100644 index 000000000..d5dda7208 --- /dev/null +++ b/C++/intersection-of-two-linked-lists.cpp @@ -0,0 +1,44 @@ +// Time: O(m + n) +// Space: O(1) + +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode(int x) : val(x), next(NULL) {} + * }; + */ +class Solution { +public: + ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) { + ListNode *curA = headA, *curB = headB; + ListNode *begin = nullptr, *tailA = nullptr, *tailB = nullptr; + while (curA && curB) { + if (curA == curB) { + begin = curA; + break; + } + + if (curA->next) { + curA = curA->next; + } else if (!tailA) { + tailA = curA; + curA = headB; + } else { + break; + } + + if (curB->next) { + curB = curB->next; + } else if (!tailB) { + tailB = curB; + curB = headA; + } else { + break; + } + } + + return begin; + } +}; diff --git a/C++/invert-binary-tree.cpp b/C++/invert-binary-tree.cpp new file mode 100644 index 000000000..f08fa7b42 --- /dev/null +++ b/C++/invert-binary-tree.cpp @@ -0,0 +1,77 @@ +// Time: O(n) +// Space: O(h) + +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode(int x) : val(x), left(NULL), right(NULL) {} + * }; + */ + +// Time: O(n) +// Space: O(w), w is the max number of nodes of the levels. +// BFS solution. +class Solution { +public: + TreeNode* invertTree(TreeNode* root) { + if (root != nullptr) { + queue nodes; + nodes.emplace(root); + while (!nodes.empty()) { + auto node = nodes.front(); + nodes.pop(); + swap(node->left, node->right); + if (node->left != nullptr) { + nodes.emplace(node->left); + } + if (node->right != nullptr) { + nodes.emplace(node->right); + } + } + } + return root; + } +}; + +// Time: O(n) +// Space: O(h) +// Stack solution. +class Solution2 { +public: + TreeNode* invertTree(TreeNode* root) { + if (root != nullptr) { + stack nodes; + nodes.emplace(root); + while (!nodes.empty()) { + auto node = nodes.top(); + nodes.pop(); + swap(node->left, node->right); + if (node->left != nullptr) { + nodes.emplace(node->left); + } + if (node->right != nullptr) { + nodes.emplace(node->right); + } + } + } + return root; + } +}; + +// Time: O(n) +// Space: O(h) +// DFS, Recursive solution. +class Solution3 { +public: + TreeNode* invertTree(TreeNode* root) { + if (root != nullptr) { + swap(root->left, root->right); + invertTree(root->left); + invertTree(root->right); + } + return root; + } +}; diff --git a/C++/ip-to-cidr.cpp b/C++/ip-to-cidr.cpp new file mode 100644 index 000000000..baa111fbc --- /dev/null +++ b/C++/ip-to-cidr.cpp @@ -0,0 +1,60 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + vector ipToCIDR(string ip, int range) { + auto start = ipToInt(ip); + vector result; + while (range > 0) { + auto mask = max(33 - bitLength(start & ~(start - 1)), + 33 - bitLength(range)); + result.emplace_back(intToIP(start) + "/" + to_string(mask)); + start += 1 << (32 - mask); + range -= 1 << (32 - mask); + } + return result; + } + +private: + uint32_t ipToInt(const string& ip) { + uint32_t result = 0; + for (const auto& n : split(ip, '.')) { + result = 256 * result + stoi(n); + } + return result; + } + + string intToIP(uint32_t n) { + string ip; + for (const auto& i : {24, 16, 8, 0}) { + ip += to_string((n >> i) % 256); + ip += "."; + } + ip.pop_back(); + return ip; + } + + vector split(const string& s, const char delim) { + vector tokens; + stringstream ss(s); + string token; + while (getline(ss, token, delim)) { + tokens.emplace_back(token); + } + return tokens; + } + + uint32_t bitLength(uint32_t n) { + uint32_t left = 1, right = 32; + while (left <= right) { + auto mid = left + (right - left) / 2; + if ((1 << mid) > n) { + right = mid - 1; + } else { + left = mid + 1; + } + } + return left; + } +}; diff --git a/C++/ipo.cpp b/C++/ipo.cpp new file mode 100644 index 000000000..d0d1d19cf --- /dev/null +++ b/C++/ipo.cpp @@ -0,0 +1,26 @@ +// Time: O(nlogn) +// Space: O(n) + +class Solution { +public: + int findMaximizedCapital(int k, int W, vector& Profits, vector& Capital) { + vector> future; + for (int i = 0; i < Profits.size(); ++i) { + future.emplace_back(Capital[i], Profits[i]); + } + sort(future.begin(), future.end(), greater>()); + + priority_queue curr; + while (k--) { + while (!future.empty() && future.back().first <= W) { + curr.emplace(future.back().second); + future.pop_back(); + } + if (!curr.empty()) { + W += curr.top(); + curr.pop(); + } + } + return W; + } +}; diff --git a/C++/is-graph-bipartite.cpp b/C++/is-graph-bipartite.cpp new file mode 100644 index 000000000..b6041c969 --- /dev/null +++ b/C++/is-graph-bipartite.cpp @@ -0,0 +1,28 @@ +// Time: O(|V| + |E|) +// Space: O(|V|) + +class Solution { +public: + bool isBipartite(vector>& graph) { + unordered_map color; + for (int node = 0; node < graph.size(); ++node) { + if (color.count(node)) { + continue; + } + vector stack{node}; + color[node] = 0; + while (!stack.empty()) { + int curr = stack.back(); stack.pop_back(); + for (const auto& neighbor : graph[curr]) { + if (!color.count(neighbor)) { + stack.emplace_back(neighbor); + color[neighbor] = color[curr] ^ 1; + } else if (color[neighbor] == color[curr]) { + return false; + } + } + } + } + return true; + } +}; diff --git a/C++/is-subsequence.cpp b/C++/is-subsequence.cpp new file mode 100644 index 000000000..9be890d17 --- /dev/null +++ b/C++/is-subsequence.cpp @@ -0,0 +1,23 @@ +// Time: O(n) +// Space: O(1) + +// Greedy solution. +class Solution { +public: + bool isSubsequence(string s, string t) { + if (s.empty()) { + return true; + } + + int i = 0; + for (const auto& c : t) { + if (c == s[i]) { + ++i; + } + if (i == s.length()) { + break; + } + } + return i == s.length(); + } +}; diff --git a/C++/isPalindrome.cpp b/C++/isPalindrome.cpp deleted file mode 100644 index b2506097f..000000000 --- a/C++/isPalindrome.cpp +++ /dev/null @@ -1,22 +0,0 @@ -// Time Complexity: O(n) -// Space Complexity: O(1) - -class Solution { - public: - bool isPalindrome(int x) { - if(x < 0) - return false; - - int d = 1; - for(; x / d >= 10 ; d *= 10); - - for(; x > 0; x = (x % d) / 10, d /= 100) { - int q = x / d; - int r = x % 10; - if(q != r) - return false; - } - - return true; - } -}; diff --git a/C++/isPalindromeII.cpp b/C++/isPalindromeII.cpp deleted file mode 100644 index 557697794..000000000 --- a/C++/isPalindromeII.cpp +++ /dev/null @@ -1,24 +0,0 @@ -// Time Complexity: O(n) -// Space Complexity: O(1) - -class Solution { - public: - bool isPalindrome(string s) { - transform(s.begin(), s.end(), s.begin(), ::tolower); - auto left = s.begin(); - auto right = prev(s.end()); - for(; left < right;) { - if(!isalnum(*left)) - ++left; - else if(!isalnum(*right)) - --right; - else if(*left != *right) - return false; - else { - ++left; - --right; - } - } - return true; - } -}; diff --git a/C++/isValid.cpp b/C++/isValid.cpp deleted file mode 100644 index 2f132d8bc..000000000 --- a/C++/isValid.cpp +++ /dev/null @@ -1,25 +0,0 @@ -// Time Complexity: O(n) -// Space Complexity: O(1) - -class Solution { - public: - bool isValid(string s) { - const string left = "([{"; - const string right = ")]}"; - stack stack; - for(auto c : s) { - if(left.find(c) != string::npos) { - stack.push(c); - } - else if (right.find(c) != string::npos){ - if(!stack.empty() && stack.top() == left[right.find(c)]) { - stack.pop(); - } - else - return false; - } - } - - return stack.empty(); - } -}; diff --git a/C++/isValidBST.cpp b/C++/isValidBST.cpp deleted file mode 100644 index cb8359ba7..000000000 --- a/C++/isValidBST.cpp +++ /dev/null @@ -1,35 +0,0 @@ -// Time Complexity: O(n) -// Space Complexity: O(logn) - -/** - * Definition for binary tree - * struct TreeNode { - * int val; - * TreeNode *left; - * TreeNode *right; - * TreeNode(int x) : val(x), left(NULL), right(NULL) {} - * }; - */ -class Solution { - public: - bool isValidBST(TreeNode *root) { - if(!root) - return true; - - if(!isValidBST(root->left)) - return false; - - if(last && last != root && last->val >= root->val) - return false; - - last = root; - - if(!isValidBST(root->right)) - return false; - - return true; - } - - private: - TreeNode *last; -}; diff --git a/C++/isValidSudoku.cpp b/C++/isValidSudoku.cpp deleted file mode 100644 index 021c8fa83..000000000 --- a/C++/isValidSudoku.cpp +++ /dev/null @@ -1,47 +0,0 @@ -// Time Complexity: O(n^2) -// Space Complexity: O(n) - -class Solution { - public: - bool isValidSudoku(vector > &board) { - bool used[9]; - - for(int i = 0; i < 9; ++i) { - fill(used, used + 9, false); - for(int j = 0; j < 9; ++j) { - if(!check(board[i][j], used)) - return false; - } - - fill(used, used + 9, false); - for(int j = 0; j < 9; ++j) { - if(!check(board[j][i], used)) - return false; - } - } - - for(int r = 0; r < 3; ++r) { - for(int c = 0; c < 3; ++c) { - fill(used, used + 9, false); - for(int i = 3 * r; i < 3 * (r + 1); ++i) { - for(int j = 3 * c; j < 3 * (c + 1); ++j) { - if(!check(board[i][j], used)) - return false; - } - } - } - } - - return true; - } - - private: - bool check(char c, bool used[9]) { - if(c != '.') { - if(used[c - '1']) - return false; - used[c - '1'] = true; - } - return true; - } -}; diff --git a/C++/island-perimeter.cpp b/C++/island-perimeter.cpp new file mode 100644 index 000000000..2b60cee51 --- /dev/null +++ b/C++/island-perimeter.cpp @@ -0,0 +1,23 @@ +// Time: O(m * n) +// Space: O(1) + +class Solution { +public: + int islandPerimeter(vector>& grid) { + int count = 0, repeat = 0; + for (int i = 0; i < grid.size(); ++i) { + for (int j = 0; j < grid[i].size(); ++j) { + if (grid[i][j] == 1) { + ++count; + if (i != 0 && grid[i - 1][j] == 1) { + ++repeat; + } + if (j != 0 && grid[i][j - 1] == 1) { + ++repeat; + } + } + } + } + return 4 * count - 2 * repeat; + } +}; diff --git a/C++/isomorphic-strings.cpp b/C++/isomorphic-strings.cpp new file mode 100644 index 000000000..1a100d7cf --- /dev/null +++ b/C++/isomorphic-strings.cpp @@ -0,0 +1,22 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + bool isIsomorphic(string s, string t) { + if (s.length() != t.length()) { + return false; + } + vector s2t(256, 0), t2s(256, 0); + for (int i = 0; i < s.length(); ++i) { + if (s2t[s[i]] == 0 && t2s[t[i]] == 0) { + s2t[s[i]] = t[i]; + t2s[t[i]] = s[i]; + } else if (s2t[s[i]] != t[i]) { + // Contradict mapping. + return false; + } + } + return true; + } +}; diff --git a/C++/jewels-and-stones.cpp b/C++/jewels-and-stones.cpp new file mode 100644 index 000000000..7b6c21e6d --- /dev/null +++ b/C++/jewels-and-stones.cpp @@ -0,0 +1,17 @@ +// Time: O(m + n) +// Space: O(n) + +class Solution { +public: + int numJewelsInStones(string J, string S) { + unordered_set lookup; + for (const auto& j : J) { + lookup.emplace(j); + } + int result = 0; + for (const auto& s : S) { + result += lookup.count(s); + } + return result; + } +}; diff --git a/C++/judge-route-circle.cpp b/C++/judge-route-circle.cpp new file mode 100644 index 000000000..26c9262c7 --- /dev/null +++ b/C++/judge-route-circle.cpp @@ -0,0 +1,18 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + bool judgeCircle(string moves) { + auto v = 0, h = 0; + for (const auto& move : moves) { + switch (move) { + case 'U': ++v; break; + case 'D': --v; break; + case 'R': ++h; break; + case 'L': --h; break; + } + } + return v == 0 && h == 0; + } +}; diff --git a/C++/k-diff-pairs-in-an-array.cpp b/C++/k-diff-pairs-in-an-array.cpp new file mode 100644 index 000000000..0d1d32fd5 --- /dev/null +++ b/C++/k-diff-pairs-in-an-array.cpp @@ -0,0 +1,22 @@ +// Time: O(n) +// Space: O(n) + +class Solution { +public: + int findPairs(vector& nums, int k) { + if (k < 0) { + return 0; + } + unordered_set result, lookup; + for (const auto& num : nums) { + if (lookup.count(num - k)) { + result.emplace(num - k); + } + if (lookup.count(num + k)) { + result.emplace(num); + } + lookup.emplace(num); + } + return result.size(); + } +}; diff --git a/C++/k-empty-slots.cpp b/C++/k-empty-slots.cpp new file mode 100644 index 000000000..97cc23a15 --- /dev/null +++ b/C++/k-empty-slots.cpp @@ -0,0 +1,22 @@ +// Time: O(n) +// Space: O(n) + +class Solution { +public: + int kEmptySlots(vector& flowers, int k) { + vector days(flowers.size()); + for (int i = 0; i < flowers.size(); ++i) { + days[flowers[i] - 1] = i; + } + auto result = numeric_limits::max(); + for (int i = 0, left = 0, right = k + 1; right < days.size(); ++i) { + if (days[i] < days[left] || days[i] <= days[right]) { + if (i == right) { + result = min(result, max(days[left], days[right])); + } + left = i, right = k + 1 + i; + } + } + return (result == numeric_limits::max()) ? -1 : result + 1; + } +}; diff --git a/C++/k-inverse-pairs-array.cpp b/C++/k-inverse-pairs-array.cpp new file mode 100644 index 000000000..bc3647ed8 --- /dev/null +++ b/C++/k-inverse-pairs-array.cpp @@ -0,0 +1,23 @@ +// Time: O(n * k) +// Space: O(k) + +class Solution { +public: + int kInversePairs(int n, int k) { + static const int M = 1000000007; + vector> dp(2, vector(k + 1)); + dp[0][0] = 1; + for (int i = 1; i <= n; ++i) { + dp[i % 2] = vector(k + 1); + dp[i % 2][0] = 1; + for (int j = 1; j <= k; ++j) { + dp[i % 2][j] = (dp[i % 2][j - 1] + dp[(i - 1) % 2][j]) % M; + if (j - i >= 0) { + dp[i % 2][j] = (dp[i % 2][j] - dp[(i - 1) % 2][j - i] + M) % M; + } + } + } + return dp[n % 2][k]; + } +}; + diff --git a/C++/k-similar-strings.cpp b/C++/k-similar-strings.cpp new file mode 100644 index 000000000..f56a2ca92 --- /dev/null +++ b/C++/k-similar-strings.cpp @@ -0,0 +1,39 @@ +// Time: O(n * n!/(c_a!*...*c_z!), n is the length of A, B, +// c_a...c_z is the count of each alphabet, +// n = sum(c_a...c_z) +// Space: O(n * n!/(c_a!*...*c_z!) + +class Solution { +public: + int kSimilarity(string A, string B) { + queue q; + unordered_set lookup; + + lookup.emplace(A); + q.emplace(A); + int result = 0; + while (!q.empty()) { + for (int size = q.size() - 1; size >= 0; --size) { + auto s = q.front(); q.pop(); + if (s == B) { + return result; + } + int i; + for (i = 0; s[i] == B[i]; ++i); + for (int j = i + 1; j < s.length(); ++j){ + if (s[j] == B[j] || s[i] != B[j]) { + continue; + } + swap(s[i], s[j]); + if (!lookup.count(s)) { + lookup.emplace(s); + q.emplace(s); + } + swap(s[i], s[j]); + } + } + ++result; + } + return result; + } +}; diff --git a/C++/k-th-smallest-in-lexicographical-order.cpp b/C++/k-th-smallest-in-lexicographical-order.cpp new file mode 100644 index 000000000..48f9fbfb9 --- /dev/null +++ b/C++/k-th-smallest-in-lexicographical-order.cpp @@ -0,0 +1,92 @@ +// Time: O(logn) +// Space: O(logn) + +class Solution { +public: + int findKthNumber(int n, int k) { + int result = 0; + + vector cnts(10); + for (int i = 1; i <= 9; ++i) { + cnts[i] = cnts[i - 1] * 10 + 1; + } + + vector nums; + for (int i = n; i > 0; i /= 10) { + nums.emplace_back(i % 10); + } + int total = n; + int target = 0; + for (int i = nums.size() - 1; i >= 0 && k; --i) { + target = target * 10 + nums[i]; + const auto start = i == nums.size() - 1 ? 1 : 0; + for (int j = start; j <= 9; ++j) { + const auto candidate = result * 10 + j; + int num; + if (candidate < target) { + num = cnts[i + 1]; + } else if (candidate > target) { + num = cnts[i]; + } else { + num = total - cnts[i + 1] * (j - start) - cnts[i] * (9 - j); + } + if (k > num) { + k -= num; + } else { + result = candidate; + --k; + total = num - 1; + break; + } + } + } + return result; + } +}; + +// Time: O(logn * logn) +// Space: O(logn) +class Solution2 { +public: + int findKthNumber(int n, int k) { + int result = 0; + int index = 0; + findKthNumberHelper(n, k, 0, &index, &result); + return result; + } + +private: + bool findKthNumberHelper(int n, int k, int cur, int *index, int *result) { + if (cur) { + ++(*index); + if (*index == k) { + *result = cur; + return true; + } + } + for (int i = (cur == 0 ? 1 : 0); i <= 9; ++i, cur /= 10) { + cur = cur * 10 + i; + int cnt = count(n, cur); + if (k > cnt + *index) { + *index += cnt; + continue; + } + if (cur <= n && findKthNumberHelper(n, k, cur, index, result)) { + return true; + } + } + return false; + } + + int count(int n, long long prefix) { // Time: O(logn) + int result = 0; + int number = 1; + while (prefix <= n) { + result += number; + prefix *= 10; + number *= 10; + } + result -= max(number / 10 - (n - prefix / 10 + 1), static_cast(0)); + return result; + } +}; diff --git a/C++/k-th-smallest-prime-fraction.cpp b/C++/k-th-smallest-prime-fraction.cpp new file mode 100644 index 000000000..9ed6dda15 --- /dev/null +++ b/C++/k-th-smallest-prime-fraction.cpp @@ -0,0 +1,47 @@ +// Time: O(nlogr) +// Space: O(1) + +// Another cool O(n) solution by using quick select with median of median could be found here: +// https://leetcode.com/problems/k-th-smallest-prime-fraction/discuss/115545/O(n) + +class Solution { +public: + vector kthSmallestPrimeFraction(vector& A, int K) { + vector result; + double left = 0.0, right = 1.0; + while (right - left > 1e-8) { + double mid = left + (right - left) / 2.0; + if (check(mid, A, K, &result)) { + right = mid; + } else { + left = mid; + } + if (!result.empty()) { + break; + } + } + return result; + } + +private: + bool check(double mid, const vector& A, int K, vector *result) { + vector tmp(2); + int count = 0; + for (int i = 0, j = 0; i < A.size(); ++i) { + for (; j < A.size(); ++j) { + if (i < j && A[i] < A[j] * mid) { + if (tmp[0] == 0 || tmp[0] * A[j] < tmp[1] * A[i]) { + tmp[0] = A[i]; + tmp[1] = A[j]; + } + break; + } + } + count += A.size() - j; + } + if (count == K) { + *result = move(tmp); + } + return count >= K; + } +}; diff --git a/C++/k-th-symbol-in-grammar.cpp b/C++/k-th-symbol-in-grammar.cpp new file mode 100644 index 000000000..b3cc69ee9 --- /dev/null +++ b/C++/k-th-symbol-in-grammar.cpp @@ -0,0 +1,18 @@ +// Time: O(logn) = O(1) because n is 32-bit integer +// Space: O(1) + +class Solution { +public: + int kthGrammar(int N, int K) { + return bitCount(K - 1) % 2; + } + +private: + int bitCount(int n) { + int count = 0; + for (; n; n &= n - 1) { + ++count; + } + return count; + } +}; diff --git a/C++/keyboard-row.cpp b/C++/keyboard-row.cpp new file mode 100644 index 000000000..d6820da13 --- /dev/null +++ b/C++/keyboard-row.cpp @@ -0,0 +1,30 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + vector findWords(vector& words) { + static const vector> rows{{'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p'}, + {'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l'}, + {'z', 'x', 'c', 'v', 'b' ,'n', 'm'}}; + + vector result; + for (const auto& word : words) { + int k = 0; + for (int i = 0; i < rows.size(); ++i) { + if (rows[i].count(tolower(word[0]))) { + k = i; + break; + } + } + result.emplace_back(word); + for (const auto& c: word) { + if (!rows[k].count(tolower(c))) { + result.pop_back(); + break; + } + } + } + return result; + } +}; diff --git a/C++/keys-and-rooms.cpp b/C++/keys-and-rooms.cpp new file mode 100644 index 000000000..7572c880b --- /dev/null +++ b/C++/keys-and-rooms.cpp @@ -0,0 +1,23 @@ +// Time: O(n!) +// Space: O(n) + +class Solution { +public: + bool canVisitAllRooms(vector>& rooms) { + unordered_set lookup = {0}; + vector stack = {0}; + while (!stack.empty()) { + auto node = stack.back(); stack.pop_back(); + for (const auto& nei : rooms[node]) { + if (!lookup.count(nei)) { + lookup.emplace(nei); + if (lookup.size() == rooms.size()) { + return true; + } + stack.emplace_back(nei); + } + } + } + return lookup.size() == rooms.size(); + } +}; diff --git a/C++/kill-process.cpp b/C++/kill-process.cpp new file mode 100644 index 000000000..4f4e82766 --- /dev/null +++ b/C++/kill-process.cpp @@ -0,0 +1,50 @@ +// Time: O(n) +// Space: O(n) + +// DFS solution. +class Solution { +public: + vector killProcess(vector& pid, vector& ppid, int kill) { + vector result; + unordered_map> children; + for (int i = 0; i < pid.size(); ++i) { + children[ppid[i]].emplace(pid[i]); + } + killAll(kill, children, &result); + return result; + } + +private: + void killAll(int pid, unordered_map>& children, vector *killed) { + killed->emplace_back(pid); + for (const auto& child : children[pid]) { + killAll(child, children, killed); + } + } +}; + +// Time: O(n) +// Space: O(n) +// BFS solution. +class Solution { +public: + vector killProcess(vector& pid, vector& ppid, int kill) { + vector result; + unordered_map> children; + for (int i = 0; i < pid.size(); ++i) { + children[ppid[i]].emplace(pid[i]); + } + + queue q; + q.emplace(kill); + while (!q.empty()) { + const auto p = q.front(); q.pop(); + result.emplace_back(p); + for (const auto& child : children[p]) { + q.emplace(child); + } + } + + return result; + } +}; diff --git a/C++/knight-probability-in-chessboard.cpp b/C++/knight-probability-in-chessboard.cpp new file mode 100644 index 000000000..8d95b4ed9 --- /dev/null +++ b/C++/knight-probability-in-chessboard.cpp @@ -0,0 +1,27 @@ +// Time: O(k * n^2) +// Space: O(n^2) + +class Solution { +public: + double knightProbability(int N, int K, int r, int c) { + static const vector> directions = + {{ 1, 2}, { 1, -2}, { 2, 1}, { 2, -1}, + {-1, 2}, {-1, -2}, {-2, 1}, {-2, -1}}; + vector>> dp(2, vector>(N, vector(N, 1.0l))); + for (int step = 1; step <= K; ++step) { + for (int i = 0; i < N; ++i) { + for (int j = 0; j < N; ++j) { + dp[step % 2][i][j] = 0; + for (const auto& direction : directions) { + auto rr = i + direction.first; + auto cc = j + direction.second; + if (0 <= cc && cc < N && 0 <= rr && rr < N) { + dp[step % 2][i][j] += 0.125l * dp[(step - 1) % 2][rr][cc]; + } + } + } + } + } + return dp[K % 2][r][c]; + } +}; diff --git a/C++/koko-eating-bananas.cpp b/C++/koko-eating-bananas.cpp new file mode 100644 index 000000000..7dc8f5ec9 --- /dev/null +++ b/C++/koko-eating-bananas.cpp @@ -0,0 +1,27 @@ +// Time: O(nlogr) +// Space: O(1) + +class Solution { +public: + int minEatingSpeed(vector& piles, int H) { + int left = 1, right = *max_element(piles.cbegin(), piles.cend()); + while (left <= right) { + const auto mid = left + (right - left) / 2; + if (possible(piles, H, mid)) { + right = mid - 1; + } else { + left = mid + 1; + } + } + return left; + } + +private: + bool possible(const vector& piles, int H, int K) { + int time = 0; + for (const auto& pile : piles) { + time += (pile - 1) / K + 1; + } + return time <= H; + } +}; diff --git a/C++/kth-largest-element-in-a-stream.cpp b/C++/kth-largest-element-in-a-stream.cpp new file mode 100644 index 000000000..1b1d69cfe --- /dev/null +++ b/C++/kth-largest-element-in-a-stream.cpp @@ -0,0 +1,31 @@ +// Time: O(nlogk) +// Space: O(k) + +class KthLargest { +public: + KthLargest(int k, vector nums) : + k_(k) { + for (const auto& num : nums) { + add(num); + } + } + + int add(int val) { + min_heap_.emplace(val); + if (min_heap_.size() > k_) { + min_heap_.pop(); + } + return min_heap_.top(); + } + +private: + const int k_; + priority_queue, greater> min_heap_; +}; + +/** + * Your KthLargest object will be instantiated and called as such: + * KthLargest obj = new KthLargest(k, nums); + * int param_1 = obj.add(val); + */ + diff --git a/C++/kth-largest-element-in-an-array.cpp b/C++/kth-largest-element-in-an-array.cpp new file mode 100644 index 000000000..caa643239 --- /dev/null +++ b/C++/kth-largest-element-in-an-array.cpp @@ -0,0 +1,47 @@ +// Time: O(n) ~ O(n^2) +// Space: O(1) + +class Solution { +public: + int findKthLargest(vector& nums, int k) { + int left = 0, right = nums.size() - 1; + default_random_engine gen((random_device())()); + while (left <= right) { + // Generates a random int in [left, right]. + uniform_int_distribution dis(left, right); + int pivot_idx = dis(gen); + int new_pivot_idx = PartitionAroundPivot(left, right, pivot_idx, &nums); + if (new_pivot_idx == k - 1) { + return nums[new_pivot_idx]; + } else if (new_pivot_idx > k - 1) { + right = new_pivot_idx - 1; + } else { // new_pivot_idx < k - 1. + left = new_pivot_idx + 1; + } + } + } + + int PartitionAroundPivot(int left, int right, int pivot_idx, vector* nums) { + auto& nums_ref = *nums; + int pivot_value = nums_ref[pivot_idx]; + int new_pivot_idx = left; + swap(nums_ref[pivot_idx], nums_ref[right]); + for (int i = left; i < right; ++i) { + if (nums_ref[i] > pivot_value) { + swap(nums_ref[i], nums_ref[new_pivot_idx++]); + } + } + swap(nums_ref[right], nums_ref[new_pivot_idx]); + return new_pivot_idx; + } +}; + +// Time: O(n) ~ O(n^2) +// Space: O(1) +class Solution2 { +public: + int findKthLargest(vector& nums, int k) { + nth_element(nums.begin(), next(nums.begin(), k - 1), nums.end(), greater()); + return *next(nums.begin(), k - 1); + } +}; diff --git a/C++/kth-smallest-element-in-a-bst.cpp b/C++/kth-smallest-element-in-a-bst.cpp new file mode 100644 index 000000000..57901862d --- /dev/null +++ b/C++/kth-smallest-element-in-a-bst.cpp @@ -0,0 +1,65 @@ +// Time: O(max(h, k)) +// Space: O(min(h, k)) + +/** + * 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: + int kthSmallest(TreeNode* root, int k) { + deque s; + TreeNode *cur = root; + int rank = 0; + while (!s.empty() || cur) { + if (cur) { + s.emplace_back(cur); + if (s.size() > k) { + s.pop_front(); + } + cur = cur->left; + } else { + cur = s.back(); + s.pop_back(); + if (++rank == k) { + return cur->val; + } + cur = cur->right; + } + } + + return INT_MIN; + } +}; + +// Time: O(max(h, k)) +// Space: O(h) +class Solution2 { +public: + int kthSmallest(TreeNode* root, int k) { + stack s; + TreeNode *cur = root; + int rank = 0; + while (!s.empty() || cur) { + if (cur) { + s.emplace(cur); + cur = cur->left; + } else { + cur = s.top(); + s.pop(); + if (++rank == k) { + return cur->val; + } + cur = cur->right; + } + } + + return INT_MIN; + } +}; diff --git a/C++/kth-smallest-element-in-a-sorted-matrix.cpp b/C++/kth-smallest-element-in-a-sorted-matrix.cpp new file mode 100644 index 000000000..580af5978 --- /dev/null +++ b/C++/kth-smallest-element-in-a-sorted-matrix.cpp @@ -0,0 +1,36 @@ +// Time: O(k * log(min(n, m, k))), with n x m matrix +// Space: O(min(n, m, k)) + +class Solution { +public: + int kthSmallest(vector>& matrix, int k) { + int kth_smallest = 0; + + using P = pair>; + priority_queue, greater

> q; + auto push = [&matrix, &q](int i, int j) { + if (matrix.size() > matrix[0].size()) { + if (i < matrix[0].size() && j < matrix.size()) { + q.emplace(matrix[j][i], make_pair(i, j)); + } + } else { + if (i < matrix.size() && j < matrix[0].size()) { + q.emplace(matrix[i][j], make_pair(i, j)); + } + } + }; + + push(0, 0); + while (!q.empty() && k--) { + auto tmp = q.top(); q.pop(); + kth_smallest = tmp.first; + int i, j; + tie(i, j) = tmp.second; + push(i, j + 1); + if (j == 0) { + push(i + 1, 0); + } + } + return kth_smallest; + } +}; diff --git a/C++/kth-smallest-number-in-multiplication-table.cpp b/C++/kth-smallest-number-in-multiplication-table.cpp new file mode 100644 index 000000000..bc3e788a4 --- /dev/null +++ b/C++/kth-smallest-number-in-multiplication-table.cpp @@ -0,0 +1,27 @@ +// Time: O(m * log(m * n)) +// Space: O(1) + +class Solution { +public: + int findKthNumber(int m, int n, int k) { + int left = 1, right = m * n; + while (left <= right) { + const auto mid = left + (right - left) / 2; + if (count(mid, m, n) >= k) { + right = mid - 1; + } else { + left = mid + 1; + } + } + return left; + } + +private: + int count(int target, int m, int n) { + auto count = 0; + for (int i = 1; i <= m; ++i) { + count += min(target / i , n); + } + return count; + } +}; diff --git a/C++/largest-bst-subtree.cpp b/C++/largest-bst-subtree.cpp new file mode 100644 index 000000000..0fbe79f6c --- /dev/null +++ b/C++/largest-bst-subtree.cpp @@ -0,0 +1,51 @@ +// Time: O(n) +// Space: O(h) + +/** + * 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: + int largestBSTSubtree(TreeNode* root) { + if (!root) { + return 0; + } + + int max_size = 1; + largestBSTSubtreeHelper(root, &max_size); + return max_size; + } + +private: + tuple largestBSTSubtreeHelper(TreeNode* root, int *max_size) { + if (!root->left && !root->right) { + return make_tuple(1, root->val, root->val); + } + + int left_size = 0, left_min = root->val, left_max = root->val; + if (root->left) { + tie(left_size, left_min, left_max) = largestBSTSubtreeHelper(root->left, max_size); + } + + int right_size = 0, right_min = root->val, right_max = root->val; + if (root->right) { + tie(right_size, right_min, right_max) = largestBSTSubtreeHelper(root->right, max_size); + } + + int size = 0; + if ((!root->left || left_size > 0) && + (!root->right || right_size > 0) && + left_max <= root->val && root->val <= right_min) { + size = 1 + left_size + right_size; + *max_size = max(*max_size, size); + } + + return make_tuple(size, left_min, right_max); + } +}; diff --git a/C++/largest-divisible-subset.cpp b/C++/largest-divisible-subset.cpp new file mode 100644 index 000000000..3a8ad35a2 --- /dev/null +++ b/C++/largest-divisible-subset.cpp @@ -0,0 +1,38 @@ +// Time: O(n^2) +// Space: O(n) + +class Solution { +public: + vector largestDivisibleSubset(vector& nums) { + if (nums.empty()) { + return {}; + } + + sort(nums.begin(), nums.end()); + // dp[i]: the size of the largest distinct subset of + // the first i+1 numbers including nums[i] + vector dp(nums.size() , 1); + vector prev(nums.size(), -1); + int largest_idx = 0; + for (int i = 0; i < nums.size(); ++i) { + for (int j = 0; j < i; ++j) { + if (nums[i] % nums[j] == 0) { + if (dp[i] < dp[j] + 1) { + dp[i] = dp[j] + 1; + prev[i] = j; + } + } + } + if (dp[largest_idx] < dp[i]) { + largest_idx = i; + } + } + + vector result; + for (int i = largest_idx; i != -1; i = prev[i]) { + result.emplace_back(nums[i]); + } + reverse(result.begin(), result.end()); + return result; + } +}; diff --git a/C++/largest-number-at-least-twice-of-others.cpp b/C++/largest-number-at-least-twice-of-others.cpp new file mode 100644 index 000000000..0916f320c --- /dev/null +++ b/C++/largest-number-at-least-twice-of-others.cpp @@ -0,0 +1,15 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + int dominantIndex(vector& nums) { + const auto result = distance(nums.begin(), max_element(nums.begin(),nums.end())); + for (int i = 0; i < nums.size(); ++i) { + if (i != result && 2 * nums[i] > nums[result]) { + return -1; + } + } + return result; + } +}; diff --git a/C++/largest-number.cpp b/C++/largest-number.cpp new file mode 100644 index 000000000..44ded1bb0 --- /dev/null +++ b/C++/largest-number.cpp @@ -0,0 +1,25 @@ +// Time: O(nlogn) +// Space: O(1) + +class Solution { +public: + string largestNumber(vector& nums) { + // sort numbers + sort(nums.begin(), nums.end(), [](const int &i, const int &j) { + return to_string(i) + to_string(j) > to_string(j) + to_string(i); + }); + + // combine the numbers + string max_num; + for (const auto& i : nums) { + max_num.append(to_string(i)); + } + + // special case: start with zero (e.g. [0, 0]) + if (!max_num.empty() && max_num[0] == '0') { + return "0"; + } + + return max_num; + } +}; diff --git a/C++/largest-palindrome-product.cpp b/C++/largest-palindrome-product.cpp new file mode 100644 index 000000000..f6513ef75 --- /dev/null +++ b/C++/largest-palindrome-product.cpp @@ -0,0 +1,29 @@ +// Time: O(10^(2n)) +// Space: O(n) + +class Solution { +public: + int largestPalindrome(int n) { + if (n == 1) { + return 9; + } + int upper = pow(10, n) - 1; + int lower = pow(10, n - 1); + for (int i = upper; i >= lower; --i) { + auto candidate = buildPalindrome(i); + for (long long j = upper; j * j >= candidate; --j) { + if (candidate % j == 0) { + return candidate % 1337; + } + } + } + return -1; + } + +private: + long long buildPalindrome(int n) { + string s = to_string(n); + reverse(s.begin(), s.end()); + return stoll(to_string(n) + s); + } +}; diff --git a/C++/largest-plus-sign.cpp b/C++/largest-plus-sign.cpp new file mode 100644 index 000000000..a89efde1d --- /dev/null +++ b/C++/largest-plus-sign.cpp @@ -0,0 +1,48 @@ +// Time: O(n^2) +// Space: O(n^2) + +class Solution { +public: + int orderOfLargestPlusSign(int N, vector>& mines) { + unordered_set, PairHash> lookup; + for (const auto& mine : mines) { + lookup.emplace(mine[0], mine[1]); + } + vector> dp(N, vector(N)); + int result = 0; + for (int i = 0; i < N; ++i) { + for (int j = 0, l = 0; j < N; ++j) { + l = lookup.count(make_pair(i, j)) ? 0 : l + 1; + dp[i][j] = l; + } + for (int j = N - 1, l = 0; j >= 0; --j) { + l = lookup.count(make_pair(i, j)) ? 0 : l + 1; + dp[i][j] = min(dp[i][j], l); + } + } + + for (int j = 0; j < N; ++j) { + for (int i = 0, l = 0; i < N; ++i) { + l = lookup.count(make_pair(i, j)) ? 0 : l + 1; + dp[i][j] = min(dp[i][j], l); + } + for (int i = N - 1, l = 0; i >= 0; --i) { + l = lookup.count(make_pair(i, j)) ? 0 : l + 1; + dp[i][j] = min(dp[i][j], l); + result = max(result, dp[i][j]); + } + } + return result; + } + +private: + template + struct PairHash { + size_t operator()(const pair& p) const { + size_t seed = 0; + seed ^= std::hash{}(p.first) + 0x9e3779b9 + (seed<<6) + (seed>>2); + seed ^= std::hash{}(p.second) + 0x9e3779b9 + (seed<<6) + (seed>>2); + return seed; + } + }; +}; diff --git a/C++/largest-rectangle-in-histogram.cpp b/C++/largest-rectangle-in-histogram.cpp new file mode 100644 index 000000000..42ca352da --- /dev/null +++ b/C++/largest-rectangle-in-histogram.cpp @@ -0,0 +1,25 @@ +// Time: O(n) +// Space: O(n) + +class Solution { +public: + int largestRectangleArea(vector& heights) { + stack increasing_heights; + int max_area = 0; + + for (int i = 0; i <= heights.size();) { + if (increasing_heights.empty() || + (i < heights.size() && heights[i] > heights[increasing_heights.top()])) { + increasing_heights.emplace(i); + ++i; + } else { + auto h = heights[increasing_heights.top()]; + increasing_heights.pop(); + auto left = increasing_heights.empty() ? -1 : increasing_heights.top(); + max_area = max(max_area, h * (i - left - 1)); + } + } + + return max_area; + } +}; diff --git a/C++/largest-sum-of-averages.cpp b/C++/largest-sum-of-averages.cpp new file mode 100644 index 000000000..4808b9d63 --- /dev/null +++ b/C++/largest-sum-of-averages.cpp @@ -0,0 +1,29 @@ +// Time: O(k * n^2) +// Space: O(n) + +class Solution { +public: + double largestSumOfAverages(vector& A, int K) { + vector accum_sum; + accum_sum.emplace_back(A[0]); + for (int i = 1; i < A.size(); ++i) { + accum_sum.emplace_back(A[i] + accum_sum.back()); + } + + vector> dp(2, vector(A.size())); + for (int k = 1; k <= K; ++k) { + for (int i = k - 1; i < A.size(); ++i) { + if (k == 1) { + dp[k % 2][i] = static_cast(accum_sum[i]) / (i + 1); + } else { + for (int j = k - 2 ; j < i; ++j) { + dp[k % 2][i] = max(dp[k % 2][i], + dp[(k - 1) % 2][j] + + static_cast(accum_sum[i] - accum_sum[j]) / (i - j)); + } + } + } + } + return dp[K % 2].back(); + } +}; diff --git a/C++/largest-triangle-area.cpp b/C++/largest-triangle-area.cpp new file mode 100644 index 000000000..2b42facfb --- /dev/null +++ b/C++/largest-triangle-area.cpp @@ -0,0 +1,23 @@ +// Time: O(n^3) +// Space: O(1) + +class Solution { +public: + double largestTriangleArea(vector>& points) { + double result = 0.0; + for (int i = 0; i < points.size() - 2; ++i) { + for (int j = i + 1; j < points.size() - 1; ++j) { + for (int k = j + 1; k < points.size(); ++k) { + result = max(result, + 0.5 * abs(points[i][0] * points[j][1] + + points[j][0] * points[k][1] + + points[k][0] * points[i][1] - + points[j][0] * points[i][1] - + points[k][0] * points[j][1] - + points[i][0] * points[k][1])); + } + } + } + return result; + } +}; diff --git a/C++/largestRectangleArea.cpp b/C++/largestRectangleArea.cpp deleted file mode 100644 index 584336bd0..000000000 --- a/C++/largestRectangleArea.cpp +++ /dev/null @@ -1,25 +0,0 @@ -// Time Complexity: O(n) -// Space Complexity: O(n) - -Solution { - public: - int largestRectangleArea(vector &height) { - const int n = height.size(); - stack s; - int ans = 0; - if(n == 0) return 0; - - height.push_back(0); - - for(int i = 0; i < n + 1;) { - if(s.empty() || height[s.top()] < height[i]) - s.push(i++); - else { - int tmp = s.top(); - s.pop(); - ans = max(ans, height[tmp] * (s.empty()? i : i - s.top() - 1)); - } - } - return ans; - } -}; diff --git a/C++/leaf-similar-trees.cpp b/C++/leaf-similar-trees.cpp new file mode 100644 index 000000000..113f5ed28 --- /dev/null +++ b/C++/leaf-similar-trees.cpp @@ -0,0 +1,40 @@ +// Time: O(n) +// Space: O(h) + +/** + * 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: + bool leafSimilar(TreeNode* root1, TreeNode* root2) { + vector s1{root1} , s2{root2}; + while (!s1.empty() && !s2.empty()) { + if (dfs(s1) != dfs(s2)) { + return false; + } + } + return s1.empty() && s2.empty(); + } + +private: + int dfs(vector& s) { + while (true) { + const auto node = s.back(); s.pop_back(); + if (node->right) { + s.emplace_back(node->right); + } + if (node->left) { + s.emplace_back(node->left); + } + if (!node->left && !node->right) { + return node->val; + } + } + } +}; diff --git a/C++/lemonade-change.cpp b/C++/lemonade-change.cpp new file mode 100644 index 000000000..1823c122a --- /dev/null +++ b/C++/lemonade-change.cpp @@ -0,0 +1,30 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + bool lemonadeChange(vector& bills) { + int five = 0, ten = 0; + for (const auto& bill : bills) { + if (bill == 5) { + ++five; + } else if (bill == 10) { + if (!five) { + return false; + } + --five; + ++ten; + } else { + if (five && ten) { + --five; + --ten; + } else if (five >= 3) { + five -= 3; + } else { + return false; + } + } + } + return true; + } +}; diff --git a/C++/length-of-last-word.cpp b/C++/length-of-last-word.cpp new file mode 100644 index 000000000..05684d6ca --- /dev/null +++ b/C++/length-of-last-word.cpp @@ -0,0 +1,12 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + int lengthOfLastWord(string s) { + const auto is_space = [](const char c) { return isspace(c); }; + const auto it = find_if_not(s.rbegin(), s.rend(), is_space); + const auto jt = find_if(it, s.rend(), is_space); + return distance(it, jt); + } +}; diff --git a/C++/length-of-longest-fibonacci-subsequence.cpp b/C++/length-of-longest-fibonacci-subsequence.cpp new file mode 100644 index 000000000..a2e9d12c4 --- /dev/null +++ b/C++/length-of-longest-fibonacci-subsequence.cpp @@ -0,0 +1,20 @@ +// Time: O(n^2) +// Space: O(n) + +class Solution { +public: + int lenLongestFibSubseq(vector& A) { + unordered_set lookup(A.cbegin(), A.cend()); + int result = 0; + for (int i = 0; i < A.size(); ++i) { + for (int j = i + 1; j < A.size(); ++j) { + int x = A[i], y = A[j], l = 2; + while (lookup.count(x + y)) { + y = x + y, x = y - x, ++l; + } + result = max(result, l); + } + } + return result > 2 ? result : 0; + } +}; diff --git a/C++/lengthOfLastWord.cpp b/C++/lengthOfLastWord.cpp deleted file mode 100644 index fc6ce929c..000000000 --- a/C++/lengthOfLastWord.cpp +++ /dev/null @@ -1,16 +0,0 @@ -// Time Complexity: O(n) -// Space Complexity: O(1) - -class Solution { - public: - int lengthOfLastWord(const char *s) { - int len = 0; - for(; *s; ++s) { - if (*s != ' ') - ++len; - else if (*(s+1) && *(s+1) != ' ') - len = 0; - } - return len; - } -}; diff --git a/C++/lengthOfLongestSubstring.cpp b/C++/lengthOfLongestSubstring.cpp deleted file mode 100644 index 4d7486d17..000000000 --- a/C++/lengthOfLongestSubstring.cpp +++ /dev/null @@ -1,21 +0,0 @@ -// Time Complexity: O(n) -// Space Complexity: O(1) - -class Solution { - public: - int lengthOfLongestSubstring(string s) { - vector last(26, -1); - int start = 0; - int ans = 0; - - for(int i = 0; i < s.size(); ++i) { - if(last[s[i] - 'a'] >= start) { // meet a repeated character - ans = max(i - start, ans); // recount max length of substring - start = last[s[i] - 'a'] + 1; // update start index next to the repeated one - } - last[s[i] - 'a'] = i; // update last index - } - - return max(static_cast(s.size()) - start, ans); // recount max length of substring due to end - } -}; diff --git a/C++/letter-case-permutation.cpp b/C++/letter-case-permutation.cpp new file mode 100644 index 000000000..9214affd3 --- /dev/null +++ b/C++/letter-case-permutation.cpp @@ -0,0 +1,24 @@ +// Time: O(n * 2^n) +// Space: O(1) + +class Solution { +public: + vector letterCasePermutation(string S) { + vector result{""}; + for (const auto& c : S) { + if (isalpha(c)) { + const auto& size = result.size(); + for (int i = 0; i < size; ++i) { + result.emplace_back(result[i]); + result[i] += tolower(c); + result.back() += toupper(c); + } + } else { + for (auto &s : result) { + s += c; + } + } + } + return result; + } +}; diff --git a/C++/lexicographical-numbers.cpp b/C++/lexicographical-numbers.cpp new file mode 100644 index 000000000..8edaf8dcd --- /dev/null +++ b/C++/lexicographical-numbers.cpp @@ -0,0 +1,31 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + vector lexicalOrder(int n) { + vector result; + + for (int i = 1, num = 1; result.size() < n; i = num + 1) { + for (int k = 0; i * pow(10, k) <= n; ++k) { + result.emplace_back(i * pow(10, k)); + } + + for (num = result.back() + 1; num <= n && num % 10; ++num) { + result.emplace_back(num); + } + + if (num % 10 == 0) { + --num; + } else { + num /= 10; + } + + while (num % 10 == 9) { + num /= 10; + } + } + + return result; + } +}; diff --git a/C++/lfu-cache.cpp b/C++/lfu-cache.cpp new file mode 100644 index 000000000..6f454c313 --- /dev/null +++ b/C++/lfu-cache.cpp @@ -0,0 +1,69 @@ +// Time: O(1), per operation +// Space: O(k), k is the capacity of cache + +class LFUCache { +public: + LFUCache(int capacity) : capa_(capacity), size_(0), min_freq_(0) { + } + + int get(int key) { + if (!key_to_nodeit_.count(key)) { + return -1; + } + + auto new_node = *key_to_nodeit_[key]; + auto& freq = std::get(new_node); + freq_to_nodes_[freq].erase(key_to_nodeit_[key]); + if (freq_to_nodes_[freq].empty()) { + freq_to_nodes_.erase(freq); + if (min_freq_ == freq) { + ++min_freq_; + } + } + ++freq; + freq_to_nodes_[freq].emplace_back(move(new_node)); + key_to_nodeit_[key] = prev(freq_to_nodes_[freq].end()); + + return std::get(*key_to_nodeit_[key]); + } + + void put(int key, int value) { + if (capa_ <= 0) { + return; + } + + if (get(key) != -1) { + std::get(*key_to_nodeit_[key]) = value; + return; + } + + if (size_ == capa_) { + key_to_nodeit_.erase(std::get(freq_to_nodes_[min_freq_].front())); + freq_to_nodes_[min_freq_].pop_front(); + if (freq_to_nodes_[min_freq_].empty()) { + freq_to_nodes_.erase(min_freq_); + } + --size_; + } + + min_freq_ = 1; + freq_to_nodes_[min_freq_].emplace_back(key, value, min_freq_); + key_to_nodeit_[key] = prev(freq_to_nodes_[min_freq_].end()); + ++size_; + } + +private: + enum Data {KEY, VAL, FREQ}; + int capa_; + int size_; + int min_freq_; + unordered_map>> freq_to_nodes_; // freq to list of {key, val, freq} + unordered_map>::iterator> key_to_nodeit_; // key to list iterator +}; + +/** + * Your LFUCache object will be instantiated and called as such: + * LFUCache obj = new LFUCache(capacity); + * int param_1 = obj.get(key); + * obj.put(key,value); + */ diff --git a/C++/license-key-formatting.cpp b/C++/license-key-formatting.cpp new file mode 100644 index 000000000..f365c7b27 --- /dev/null +++ b/C++/license-key-formatting.cpp @@ -0,0 +1,20 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + string licenseKeyFormatting(string S, int K) { + string result; + for (auto it = S.rbegin(); it < S.rend(); ++it) { + if (*it == '-') { + continue; + } + if (result.length() % (K + 1) == K) { + result += '-'; + } + result += toupper(*it); + } + reverse(result.begin(), result.end()); + return result; + } +}; diff --git a/C++/line-reflection.cpp b/C++/line-reflection.cpp new file mode 100644 index 000000000..986d8f904 --- /dev/null +++ b/C++/line-reflection.cpp @@ -0,0 +1,59 @@ +// Time: O(n) +// Space: O(n) + +// Hash solution. +class Solution { +public: + bool isReflected(vector>& points) { + if (points.empty()) { + return true; + } + unordered_map> groups_by_y; + int left = numeric_limits::max(); + int right = numeric_limits::min(); + for (const auto& p : points) { + groups_by_y[p.second].emplace(p.first); + left = min(left, p.first); + right = max(right, p.first); + } + const auto mid = left + right; + for (const auto& kvp : groups_by_y) { + for (const auto& x : kvp.second) { + if (kvp.second.count(mid - x) == 0) { + return false; + } + } + } + return true; + } +}; + +// Time: O(nlogn) +// Space: O(1) +// Two pointers solution. +class Solution2 { +public: + bool isReflected(vector>& points) { + if (points.empty()) { + return true; + } + sort(points.begin(), points.end()); + sort(points.begin(), points.begin() + distance(points.begin(), points.end()) / 2, + [](const pair& a, const pair& b) { + if (a.first == b.first) { + return a.second > b.second; + } + return a.first < b.first; + }); + + const auto mid = points.front().first + points.back().first; + for (int left = 0, right = points.size() - 1; left <= right; ++left, --right) { + if ((mid != points[left].first + points[right].first) || + (points[left].first != points[right].first && + points[left].second != points[right].second)) { + return false; + } + } + return true; + } +}; diff --git a/C++/linked-list-components.cpp b/C++/linked-list-components.cpp new file mode 100644 index 000000000..2b30f20b0 --- /dev/null +++ b/C++/linked-list-components.cpp @@ -0,0 +1,28 @@ +// Time: O(m + n), m is the number of G, n is the number of nodes +// Space: O(m) + +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode(int x) : val(x), next(NULL) {} + * }; + */ +class Solution { +public: + int numComponents(ListNode* head, vector& G) { + unordered_set lookup(G.cbegin(), G.cend()); + ListNode dummy(-1); + dummy.next = head; + auto curr = &dummy; + int result = 0; + while (curr && curr->next) { + if (!lookup.count(curr->val) && lookup.count(curr->next->val)) { + ++result; + } + curr = curr->next; + } + return result; + } +}; diff --git a/C++/linked-list-cycle-ii.cpp b/C++/linked-list-cycle-ii.cpp new file mode 100644 index 000000000..3acc2385a --- /dev/null +++ b/C++/linked-list-cycle-ii.cpp @@ -0,0 +1,30 @@ +// Time: O(n) +// Space: O(1) + +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode(int x) : val(x), next(NULL) {} + * }; + */ +class Solution { +public: + ListNode *detectCycle(ListNode *head) { + ListNode *slow = head, *fast = head; + + while (fast && fast->next) { + slow = slow->next, fast = fast->next->next; + if (slow == fast) { // There is a cycle. + slow = head; + // Both pointers advance at the same time. + while (slow != fast) { + slow = slow->next, fast = fast->next; + } + return slow; // slow is the begin of cycle. + } + } + return nullptr; // No cycle. + } +}; diff --git a/C++/linked-list-cycle.cpp b/C++/linked-list-cycle.cpp new file mode 100644 index 000000000..84d3a8383 --- /dev/null +++ b/C++/linked-list-cycle.cpp @@ -0,0 +1,25 @@ +// Time: O(n) +// Space: O(1) + +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode(int x) : val(x), next(NULL) {} + * }; + */ +class Solution { +public: + bool hasCycle(ListNode *head) { + ListNode *slow = head, *fast = head; + + while (fast && fast->next) { + slow = slow->next, fast = fast->next->next; + if (slow == fast) { // There is a cycle. + return true; + } + } + return false; // No cycle. + } +}; diff --git a/C++/linked-list-random-node.cpp b/C++/linked-list-random-node.cpp new file mode 100644 index 000000000..9e5809495 --- /dev/null +++ b/C++/linked-list-random-node.cpp @@ -0,0 +1,32 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + /** @param head The linked list's head. Note that the head is guanranteed to be not null, so it contains at least one node. */ + Solution(ListNode* head) : head_(head) { + + } + + /** Returns a random node's value. */ + int getRandom() { + auto reservoir = -1; + auto n = 0; + for (auto curr = head_; curr; curr = curr->next) { + if (rand() % ++n == 0) { + reservoir = curr->val; + } + } + return reservoir; + } + +private: + ListNode *head_; +}; + +/** + * Your Solution object will be instantiated and called as such: + * Solution obj = new Solution(head); + * int param_1 = obj.getRandom(); + */ + diff --git a/C++/logger-rate-limiter.cpp b/C++/logger-rate-limiter.cpp new file mode 100644 index 000000000..8872e6bad --- /dev/null +++ b/C++/logger-rate-limiter.cpp @@ -0,0 +1,57 @@ +// Time: O(1), amortized +// Space: O(k), k is the max number of printed messages in last 10 seconds + +class Logger { +public: + /** Initialize your data structure here. */ + Logger() { + + } + + /** Returns true if the message should be printed in the given timestamp, otherwise returns false. The timestamp is in seconds granularity. */ + bool shouldPrintMessage(int timestamp, string message) { + while (!dq_.empty() && dq_.front().first <= timestamp - 10) { + printed_.erase(dq_.front().second); + dq_.pop_front(); + } + if (printed_.count(message)) { + return false; + } + dq_.emplace_back(timestamp, message); + printed_.emplace(message); + return true; + } + +private: + deque> dq_; + unordered_set printed_; +}; + +// Time: O(1) +// Space: O(n), n is the number of total unique messages +class Logger2 { +public: + /** Initialize your data structure here. */ + Logger() { + + } + + /** Returns true if the message should be printed in the given timestamp, otherwise returns false. The timestamp is in seconds granularity. */ + bool shouldPrintMessage(int timestamp, string message) { + if (message_time_.count(message) && + timestamp < message_time_[message] + 10) { + return false; + } + message_time_[message] = timestamp; + return true; + } + +private: + unordered_map message_time_; +}; + +/** + * Your Logger object will be instantiated and called as such: + * Logger obj = new Logger(); + * bool param_1 = obj.shouldPrintMessage(timestamp,message); + */ diff --git a/C++/lonely-pixel-i.cpp b/C++/lonely-pixel-i.cpp new file mode 100644 index 000000000..709853593 --- /dev/null +++ b/C++/lonely-pixel-i.cpp @@ -0,0 +1,27 @@ +// Time: O(m * n) +// Space: O(m + n) + +class Solution { +public: + int findLonelyPixel(vector>& picture) { + vector rows = vector(picture.size()); + vector cols = vector(picture[0].size()); + + for (int i = 0; i < picture.size(); ++i) { + for (int j = 0; j < picture[0].size(); ++j) { + rows[i] += picture[i][j] == 'B'; + cols[j] += picture[i][j] == 'B'; + } + } + + int result = 0; + for (int i = 0; i < picture.size(); ++i) { + if (rows[i] == 1) { + for (int j = 0; j < picture[0].size() && rows[i] > 0; ++j) { + result += picture[i][j] == 'B' && cols[j] == 1; + } + } + } + return result; + } +}; diff --git a/C++/lonely-pixel-ii.cpp b/C++/lonely-pixel-ii.cpp new file mode 100644 index 000000000..08c23dd68 --- /dev/null +++ b/C++/lonely-pixel-ii.cpp @@ -0,0 +1,35 @@ +// Time: O(m * n) +// Space: O(m * n) + +class Solution { +public: + int findBlackPixel(vector>& picture, int N) { + vector rows = vector(picture.size()); + vector cols = vector(picture[0].size()); + + unordered_map lookup; + vector srows; + for (int i = 0; i < picture.size(); ++i) { + string s; + for (int j = 0; j < picture[0].size(); ++j) { + if (picture[i][j] == 'B') { + ++rows[i]; + ++cols[j]; + } + s.push_back(picture[i][j]); + } + ++lookup[s]; + srows.emplace_back(move(s)); + } + + int result = 0; + for (int i = 0; i < picture.size(); ++i) { + if (rows[i] == N && lookup[srows[i]] == N) { + for (int j = 0; j < picture[0].size(); ++j) { + result += picture[i][j] == 'B' && cols[j] == N; + } + } + } + return result; + } +}; diff --git a/C++/longest-absolute-file-path.cpp b/C++/longest-absolute-file-path.cpp new file mode 100644 index 000000000..aca011c53 --- /dev/null +++ b/C++/longest-absolute-file-path.cpp @@ -0,0 +1,29 @@ +// Time: O(n) +// Space: O(d), d is the max depth of the paths + +class Solution { +public: + int lengthLongestPath(string input) { + input.push_back('\n'); + + size_t max_len = 0; + unordered_map path_len; + path_len[0] = 0; + + for (auto i = input.find("\n"), prev_i = 0ul; + i != string::npos; + prev_i = i + 1, i = input.find("\n", i + 1)) { + + const auto line = input.substr(prev_i, i - prev_i); + const auto name = line.substr(line.find_first_not_of("\t")); + const auto depth = line.length() - name.length(); + + if (name.find('.') != string::npos) { + max_len = max(max_len, path_len[depth] + name.length()); + } else { + path_len[depth + 1] = path_len[depth] + name.length() + 1; + } + } + return max_len; + } +}; diff --git a/C++/longest-common-prefix.cpp b/C++/longest-common-prefix.cpp new file mode 100644 index 000000000..aa362d047 --- /dev/null +++ b/C++/longest-common-prefix.cpp @@ -0,0 +1,20 @@ +// Time: O(n * k), k is the length of the common prefix +// Space: O(1) + +class Solution { +public: + string longestCommonPrefix(vector& strs) { + if (strs.empty()) { + return ""; + } + + for (int i = 0; i < strs[0].length(); ++i) { + for (const auto& str : strs) { + if (i >= str.length() || str[i] != strs[0][i]) { + return strs[0].substr(0, i); + } + } + } + return strs[0]; + } +}; diff --git a/C++/longest-consecutive-sequence.cpp b/C++/longest-consecutive-sequence.cpp new file mode 100644 index 000000000..253cc2c97 --- /dev/null +++ b/C++/longest-consecutive-sequence.cpp @@ -0,0 +1,58 @@ +// Time: O(n) +// Space: O(n) + +class Solution { +public: + int longestConsecutive(vector& nums) { + // unprocessed_entries records the existence of each entry in num. + unordered_set unprocessed_entries; + for (const auto& num : nums) { + unprocessed_entries.emplace(num); + } + + int max_interval_size = 0; + while (!unprocessed_entries.empty()) { + int num = *unprocessed_entries.begin(); + unprocessed_entries.erase(num); + + // Finds the lower bound of the largest range containing a. + int lower_bound = num - 1; + while (unprocessed_entries.count(lower_bound)) { + unprocessed_entries.erase(lower_bound); + --lower_bound; + } + + // Finds the upper bound of the largest range containing a. + int upper_bound = num + 1; + while (unprocessed_entries.count(upper_bound)) { + unprocessed_entries.erase(upper_bound); + ++upper_bound; + } + max_interval_size = + max(max_interval_size, upper_bound - lower_bound - 1); + } + return max_interval_size; + } +}; + +// Time: O(n) +// Space: O(n) +class Solution2 { +public: + int longestConsecutive(vector &nums) { + if (nums.empty()) { + return 0; + } + unordered_map hash; + int ans{1}; + for (const auto& i : nums) { + if (!hash[i]) { + hash[i] = 1; + int leftbound{hash[i - 1]}, rightbound{hash[i + 1]}; // Get neighbor info. + hash[i - leftbound] = hash[i + rightbound] = 1 + leftbound + rightbound; // Update left and right bound info. + ans = max(ans, 1 + leftbound + rightbound); + } + } + return ans; + } +}; diff --git a/C++/longest-continuous-increasing-subsequence.cpp b/C++/longest-continuous-increasing-subsequence.cpp new file mode 100644 index 000000000..02f6415b3 --- /dev/null +++ b/C++/longest-continuous-increasing-subsequence.cpp @@ -0,0 +1,17 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + int findLengthOfLCIS(vector& nums) { + int result = 0, count = 0; + for (int i = 0; i < nums.size(); ++i) { + if (i == 0 || nums[i - 1] < nums[i]) { + result = max(result, ++count); + } else { + count = 1; + } + } + return result; + } +}; diff --git a/C++/longest-harmonious-subsequence.cpp b/C++/longest-harmonious-subsequence.cpp new file mode 100644 index 000000000..d8a4f9e03 --- /dev/null +++ b/C++/longest-harmonious-subsequence.cpp @@ -0,0 +1,19 @@ +// Time: O(n) +// Space: O(n) + +class Solution { +public: + int findLHS(vector& nums) { + unordered_map lookup; + auto result = 0; + for (const auto& num : nums) { + ++lookup[num]; + for (const auto& diff : {-1, 1}) { + if (lookup.count(num + diff)) { + result = max(result, lookup[num] + lookup[num + diff]); + } + } + } + return result; + } +}; diff --git a/C++/longest-increasing-path-in-a-matrix.cpp b/C++/longest-increasing-path-in-a-matrix.cpp new file mode 100644 index 000000000..a6e09e1ca --- /dev/null +++ b/C++/longest-increasing-path-in-a-matrix.cpp @@ -0,0 +1,46 @@ +// Time: O(m * n) +// Space: O(m * n) + +// DFS + Memorization solution. +class Solution { +public: + int longestIncreasingPath(vector>& matrix) { + if (matrix.empty()) { + return 0; + } + + int res = 0; + vector> max_lengths(matrix.size(), vector(matrix[0].size())); + for (int i = 0; i < matrix.size(); ++i) { + for (int j = 0; j < matrix[0].size(); ++j) { + res = max(res, longestpath(matrix, i, j, &max_lengths)); + } + } + + return res; + } + +private: + int longestpath(const vector>& matrix, const int i, const int j, + vector> *max_lengths) { + if ((*max_lengths)[i][j] > 0) { + return (*max_lengths)[i][j]; + } + + int max_depth = 0; + const vector> directions{{0, -1}, {0, 1}, + {-1, 0}, {1, 0}}; + for (const auto& d : directions) { + const int x = i + d.first, y = j + d.second; + if (x >= 0 && x < matrix.size() && + y >= 0 && y < matrix[0].size() && + matrix[x][y] < matrix[i][j]) { + max_depth = max(max_depth, + longestpath(matrix, x, y, max_lengths)); + } + } + + (*max_lengths)[i][j] = max_depth + 1; + return (*max_lengths)[i][j]; + } +}; diff --git a/C++/longest-increasing-subsequence.cpp b/C++/longest-increasing-subsequence.cpp new file mode 100644 index 000000000..aff8c30bf --- /dev/null +++ b/C++/longest-increasing-subsequence.cpp @@ -0,0 +1,87 @@ +// Time: O(nlogn) +// Space: O(n) + +// Binary search solution with STL. +class Solution { +public: + int lengthOfLIS(vector& nums) { + vector LIS; + + for (const auto& num : nums) { + insert(&LIS, num); + } + + return LIS.size(); + } + +private: + void insert(vector *LIS, const int target) { + // Find the first index "left" which satisfies LIS[left] >= target + auto it = lower_bound(LIS->begin(), LIS->end(), target); + + // If not found, append the target. + if (it == LIS->end()) { + LIS->emplace_back(target); + } else { + *it = target; + } + } +}; + +// Binary search solution. +class Solution2 { +public: + int lengthOfLIS(vector& nums) { + vector LIS; + + for (const auto& num : nums) { + insert(&LIS, num); + } + + return LIS.size(); + } + +private: + void insert(vector *LIS, const int target) { + int left = 0, right = LIS->size() - 1; + auto comp = [](int x, int target) { return x >= target; }; + + // Find the first index "left" which satisfies LIS[left] >= target + while (left <= right) { + int mid = left + (right - left) / 2; + if (comp((*LIS)[mid], target)) { + right = mid - 1; + } else { + left = mid + 1; + } + } + + // If not found, append the target. + if (left == LIS->size()) { + LIS->emplace_back(target); + } else { + (*LIS)[left] = target; + } + } +}; + +// Time: O(n^2) +// Space: O(n) +// Traditional DP solution. +class Solution3 { +public: + int lengthOfLIS(vector& nums) { + const int n = nums.size(); + vector dp(n, 1); // dp[i]: the length of LIS ends with nums[i] + int res = 0; + for (int i = 0; i < n; ++i) { + for (int j = 0; j < i; ++j) { + if (nums[j] < nums[i]) { + dp[i] = max(dp[i], dp[j] + 1); + } + } + res = max(res, dp[i]); + } + return res; + } +}; diff --git a/C++/longest-line-of-consecutive-one-in-matrix.cpp b/C++/longest-line-of-consecutive-one-in-matrix.cpp new file mode 100644 index 000000000..a59c4c433 --- /dev/null +++ b/C++/longest-line-of-consecutive-one-in-matrix.cpp @@ -0,0 +1,26 @@ +// Time: O(m * n) +// Space: O(n) + +class Solution { +public: + int longestLine(vector>& M) { + if (M.empty()) { + return 0; + } + int result = 0; + vector>> dp(2, vector>(M[0].size(), vector(4))); + for (int i = 0; i < M.size(); ++i) { + for (int j = 0; j < M[0].size(); ++j) { + dp[i % 2][j][0] = dp[i % 2][j][1] = dp[i % 2][j][2] = dp[i % 2][j][3] = 0; + if (M[i][j] == 1) { + dp[i % 2][j][0] = j > 0 ? dp[i % 2][j - 1][0] + 1 : 1; + dp[i % 2][j][1] = i > 0 ? dp[(i - 1) % 2][j][1] + 1 : 1; + dp[i % 2][j][2] = (i > 0 && j > 0) ? dp[(i - 1) % 2][j - 1][2] + 1 : 1; + dp[i % 2][j][3] = (i > 0 && j < M[0].size() - 1) ? dp[(i - 1) % 2][j + 1][3] + 1 : 1; + result = max(result, max(dp[i % 2][j][0], max(dp[i % 2][j][1], max(dp[i % 2][j][2], dp[i % 2][j][3])))); + } + } + } + return result; + } +}; diff --git a/C++/longest-mountain-in-array.cpp b/C++/longest-mountain-in-array.cpp new file mode 100644 index 000000000..efb714ce9 --- /dev/null +++ b/C++/longest-mountain-in-array.cpp @@ -0,0 +1,21 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + int longestMountain(vector& A) { + int result = 0, up_len = 0, down_len = 0; + for (int i = 1; i < A.size(); ++i) { + if ((down_len && A[i - 1] < A[i]) || + A[i - 1] == A[i]) { + up_len = down_len = 0; + } + up_len += A[i - 1] < A[i]; + down_len += A[i - 1] > A[i]; + if (up_len && down_len) { + result= max(result, up_len + down_len + 1); + } + } + return result; + } +}; diff --git a/C++/longest-palindrome.cpp b/C++/longest-palindrome.cpp new file mode 100644 index 000000000..019ceb2f8 --- /dev/null +++ b/C++/longest-palindrome.cpp @@ -0,0 +1,13 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + int longestPalindrome(string s) { + int odds = 0; + for (auto c = 'A'; c <= 'z'; ++c) { + odds += count(s.cbegin(), s.cend(), c) & 1; + } + return s.length() - odds + (odds > 0); + } +}; diff --git a/C++/longest-palindromic-subsequence.cpp b/C++/longest-palindromic-subsequence.cpp new file mode 100644 index 000000000..852f3ff0c --- /dev/null +++ b/C++/longest-palindromic-subsequence.cpp @@ -0,0 +1,19 @@ +// Time: O(n^2) +// Space: O(n) + +class Solution { +public: + int longestPalindromeSubseq(string s) { + vector> dp(2, vector(s.size(), 1)); + for (int i = s.length() - 2; i >= 0; --i) { + for (int j = i + 1; j < s.length(); ++j) { + if (s[i] == s[j]) { + dp[i % 2][j] = (i + 1 <= j - 1) ? 2 + dp[(i + 1) % 2][j - 1] : 2; + } else { + dp[i % 2][j] = max(dp[(i + 1) % 2][j], dp[i % 2][j - 1]); + } + } + } + return dp[0][s.length() - 1]; + } +}; diff --git a/C++/longest-palindromic-substring.cpp b/C++/longest-palindromic-substring.cpp new file mode 100644 index 000000000..bd3851f2b --- /dev/null +++ b/C++/longest-palindromic-substring.cpp @@ -0,0 +1,53 @@ +// Time: O(n) +// Space: O(n) + +// Manacher's Algorithm. +class Solution { +public: + string longestPalindrome(string s) { + string T = preProcess(s); + const int n = T.length(); + vector P(n); + int C = 0, R = 0; + for (int i = 1; i < n - 1; ++i) { + int i_mirror = 2 * C - i; // equals to i' = C - (i-C) + + P[i] = (R > i) ? min(R - i, P[i_mirror]) : 0; + + // Attempt to expand palindrome centered at i + while (T[i + 1 + P[i]] == T[i - 1 - P[i]]) { + ++P[i]; + } + + // If palindrome centered at i expands the past R, + // adjust center based on expanded palindrome. + if (i + P[i] > R) { + C = i; + R = i + P[i]; + } + } + + // Find the maximum element in P. + int max_i = 0; + for (int i = 1; i < n - 1; ++i) { + if (P[i] > P[max_i]) { + max_i = i; + } + } + + return s.substr((max_i - P[max_i]) / 2, P[max_i]); + } + +private: + string preProcess(const string& s) { + if (s.empty()) { + return "^$"; + } + string ret = "^"; + for (int i = 0; i < s.length(); ++i) { + ret += "#" + s.substr(i, 1); + } + ret += "#$"; + return ret; + } +}; diff --git a/C++/longest-repeating-character-replacement.cpp b/C++/longest-repeating-character-replacement.cpp new file mode 100644 index 000000000..78fe53af1 --- /dev/null +++ b/C++/longest-repeating-character-replacement.cpp @@ -0,0 +1,25 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + int characterReplacement(string s, int k) { + vector cache(26); + + int i = 0, j = 0, times = k, res = 0; + for (; j < s.size(); ++j) { + ++cache[s[j] - 'A']; + if (s[j] != s[i]) { + --times; + if (times < 0) { + res = max(res, j - i); + while (i < j && times < 0) { + --cache[s[i++] - 'A']; + times = k - (j - i + 1 - cache[s[i] - 'A']); + } + } + } + } + return max(res, j - i + min(i, times)); + } +}; diff --git a/C++/longest-substring-with-at-least-k-repeating-characters.cpp b/C++/longest-substring-with-at-least-k-repeating-characters.cpp new file mode 100644 index 000000000..b2f107d16 --- /dev/null +++ b/C++/longest-substring-with-at-least-k-repeating-characters.cpp @@ -0,0 +1,40 @@ +// Time: O(26 * n) = O(n) +// Space: O(26) = O(1) + +// Recursive solution. +class Solution { +public: + int longestSubstring(string s, int k) { + return longestSubstringHelper(s, k, 0, s.size()); + } + +private: + int longestSubstringHelper(const string& s, int k, int start, int end) { + vector count(26); + for (int i = start; i < end; ++i) { + ++count[s[i] - 'a']; + } + + int max_len = 0; + for (int i = start; i < end;) { + while (i < end && count[s[i] - 'a'] < k) { + ++i; + } + if (i == end) { + break; + } + + int j = i; + while (j < end && count[s[j] - 'a'] >= k) { + ++j; + } + if (i == start && j == end) { + return end - start; + } + + max_len = max(max_len, longestSubstringHelper(s, k, i, j)); + i = j; + } + return max_len; + } +}; diff --git a/C++/longest-substring-with-at-most-k-distinct-characters.cpp b/C++/longest-substring-with-at-most-k-distinct-characters.cpp new file mode 100644 index 000000000..c9ce78198 --- /dev/null +++ b/C++/longest-substring-with-at-most-k-distinct-characters.cpp @@ -0,0 +1,25 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + int lengthOfLongestSubstringKDistinct(string s, int k) { + int longest = 0, start = 0, distinct_count = 0; + array visited = {0}; + for (int i = 0; i < s.length(); ++i) { + if (visited[s[i]] == 0) { + ++distinct_count; + } + ++visited[s[i]]; + while (distinct_count > k) { + --visited[s[start]]; + if (visited[s[start]] == 0) { + --distinct_count; + } + ++start; + } + longest = max(longest, i - start + 1); + } + return longest; + } +}; diff --git a/C++/longest-substring-with-at-most-two-distinct-characters.cpp b/C++/longest-substring-with-at-most-two-distinct-characters.cpp new file mode 100644 index 000000000..d4eb2484c --- /dev/null +++ b/C++/longest-substring-with-at-most-two-distinct-characters.cpp @@ -0,0 +1,26 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + int lengthOfLongestSubstringTwoDistinct(string s) { + const int k = 2; + int longest = 0, start = 0, distinct_count = 0; + array visited = {0}; + for (int i = 0; i < s.length(); ++i) { + if (visited[s[i]] == 0) { + ++distinct_count; + } + ++visited[s[i]]; + while (distinct_count > k) { + --visited[s[start]]; + if (visited[s[start]] == 0) { + --distinct_count; + } + ++start; + } + longest = max(longest, i - start + 1); + } + return longest; + } +}; diff --git a/C++/longest-substring-without-repeating-characters.cpp b/C++/longest-substring-without-repeating-characters.cpp new file mode 100644 index 000000000..95aa6b37b --- /dev/null +++ b/C++/longest-substring-without-repeating-characters.cpp @@ -0,0 +1,25 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + int lengthOfLongestSubstring(string s) { + // Record the last occurrence of each char. + unordered_map last_occurrence; + size_t starting_idx = 0, ans = 0; + for (size_t i = 0; i < s.size(); ++i) { + auto it(last_occurrence.find(s[i])); + if (it == last_occurrence.cend()) { + last_occurrence.emplace_hint(it, s[i], i); + } else { // s[i] appeared before. Check its validity. + if (it->second >= starting_idx) { + ans = max(ans, i - starting_idx); + starting_idx = it->second + 1; + } + it->second = i; + } + } + ans = max(ans, s.size() - starting_idx); + return ans; + } +}; diff --git a/C++/longest-uncommon-subsequence-i.cpp b/C++/longest-uncommon-subsequence-i.cpp new file mode 100644 index 000000000..24b695e99 --- /dev/null +++ b/C++/longest-uncommon-subsequence-i.cpp @@ -0,0 +1,12 @@ +// Time: O(min(a, b)) +// Space: O(1) + +class Solution { +public: + int findLUSlength(string a, string b) { + if (a == b) { + return -1; + } + return max(a.length(), b.length()); + } +}; diff --git a/C++/longest-uncommon-subsequence-ii.cpp b/C++/longest-uncommon-subsequence-ii.cpp new file mode 100644 index 000000000..1641f6074 --- /dev/null +++ b/C++/longest-uncommon-subsequence-ii.cpp @@ -0,0 +1,34 @@ +// Time: O(l * n^2) +// Space: O(1) + +class Solution { +public: + int findLUSlength(vector& strs) { + sort(strs.begin(), strs.end(), + [](const string& a, const string& b) { return a.length() > b.length(); }); + for (int i = 0; i < strs.size(); ++i) { + bool all_of = true; + for (int j = 0; strs[j].length() >= strs[i].length() && j < strs.size(); ++j) { + if (i != j && isSubsequence(strs[i], strs[j])) { + all_of = false; + break; + } + } + if (all_of) { + return strs[i].length(); + } + } + return -1; + } + +private: + bool isSubsequence(const string& a, const string& b) { + int i = 0; + for (int j = 0; j < b.length() && i < a.length(); ++j) { + if (a[i] == b[j]) { + ++i; + } + } + return i == a.length(); + } +}; diff --git a/C++/longest-univalue-path.cpp b/C++/longest-univalue-path.cpp new file mode 100644 index 000000000..a4b652a43 --- /dev/null +++ b/C++/longest-univalue-path.cpp @@ -0,0 +1,33 @@ +// Time: O(n) +// Space: O(h) + +/** + * 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: + int longestUnivaluePath(TreeNode* root) { + auto result = 0; + dfs(root, &result); + return result; + } + +private: + int dfs(TreeNode *node, int *result) { + if (!node) { + return 0; + } + auto left = dfs(node->left, result); + auto right = dfs(node->right, result); + left = node->left && node->val == node->left->val ? left + 1 : 0; + right = node->right && node->val == node->right->val ? right + 1 : 0; + *result = max(*result, left + right); + return max(left, right); + } +}; diff --git a/C++/longest-valid-parentheses.cpp b/C++/longest-valid-parentheses.cpp new file mode 100644 index 000000000..d8c4d84b2 --- /dev/null +++ b/C++/longest-valid-parentheses.cpp @@ -0,0 +1,32 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + int longestValidParentheses(string s) { + return max(length(s.begin(), s.end(), '('), length(s.rbegin(), s.rend(), ')')); + } + +private: + template + int length(T begin, T end, char c) { + int len = 0, depth = 0; + T start = begin; + for (T it = begin; it != end; ++it) { + if (*it == c) { + ++depth; + } else { + --depth; + if (depth < 0) { + start = next(it); + depth = 0; + } else { + if (depth == 0) { + len = max(len, static_cast(distance(start, it)) + 1); + } + } + } + } + return len; + } +}; diff --git a/C++/longest-word-in-dictionary-through-deleting.cpp b/C++/longest-word-in-dictionary-through-deleting.cpp new file mode 100644 index 000000000..4be187803 --- /dev/null +++ b/C++/longest-word-in-dictionary-through-deleting.cpp @@ -0,0 +1,25 @@ +// Time: O((d * l) * logd), l is the average length of words +// Space: O(1) + +class Solution { +public: + string findLongestWord(string s, vector& d) { + sort(d.begin(), d.end(), + [](const string& a, const string&b) { + return a.length() != b.length() ? a.length() > b.length() : a < b; + }); + + for (const auto& word : d) { + int i = 0; + for (const auto& c : s) { + if (i < word.length() && word[i] == c) { + ++i; + } + } + if (i == word.length()) { + return word; + } + } + return ""; + } +}; diff --git a/C++/longest-word-in-dictionary.cpp b/C++/longest-word-in-dictionary.cpp new file mode 100644 index 000000000..2d1baf26f --- /dev/null +++ b/C++/longest-word-in-dictionary.cpp @@ -0,0 +1,66 @@ +// Time: O(n), n is the total sum of the lengths of words +// Space: O(t), t is the number of nodes in trie + +class Solution { +public: + string longestWord(vector& words) { + TrieNode trie; + for (int i = 0; i < words.size(); ++i) { + trie.Insert(words[i], i); + } + + // DFS + stack stk; + for (const auto& node : trie.leaves) { + if (node) { + stk.emplace(node); + } + } + + string result; + while (!stk.empty()) { + const auto curr = stk.top(); stk.pop(); + if (curr->isString) { + const auto& word = words[curr->val]; + if (word.size() > result.size() || (word.size() == result.size() && word < result)) { + result = word; + } + for (const auto& node : curr->leaves) { + if (node) { + stk.emplace(node); + } + } + } + } + return result; + } + +private: + struct TrieNode { + bool isString; + int val; + vector leaves; + + TrieNode() : isString{false}, val{0}, leaves(26) {} + + void Insert(const string& s, const int i) { + auto* p = this; + for (const auto& c : s) { + if (!p->leaves[c - 'a']) { + p->leaves[c - 'a'] = new TrieNode; + } + p = p->leaves[c - 'a']; + } + p->isString = true; + p->val = i; + } + + ~TrieNode() { + for (auto& node : leaves) { + if (node) { + delete node; + } + } + } + }; +}; diff --git a/C++/longestConsecutive.cpp b/C++/longestConsecutive.cpp deleted file mode 100644 index 08a929e02..000000000 --- a/C++/longestConsecutive.cpp +++ /dev/null @@ -1,22 +0,0 @@ -// Time Complexity: O(n) -// Space Complexity: O(n) - -class Solution { - public: - int longestConsecutive(vector &num) { - if (num.size() == 0) - return 0; - unordered_map hash; - int ans{1}; - for (auto &i: num) { - if (hash[i] != 0) { - continue; - } - hash[i] = 1; - int leftbound{hash[i - 1]}, rightbound{hash[i + 1]}; // get neighbor info - hash[i - leftbound] = hash[i + rightbound] = 1 + leftbound + rightbound; // update left and right bound info - ans = max(ans, 1 + leftbound + rightbound); - } - return ans; - } -}; diff --git a/C++/longestPalindrome.cpp b/C++/longestPalindrome.cpp deleted file mode 100644 index 51e4acc9b..000000000 --- a/C++/longestPalindrome.cpp +++ /dev/null @@ -1,52 +0,0 @@ -// Time Complexity: O(n) -// Space Complexity: O(n) - -class Solution { - public: - // Manacher's Algorithm - string longestPalindrome(string s) { - string T = preProcess(s); - int n = T.length(); - vector P(n); - int C = 0, R = 0; - for (int i = 1; i < n-1; i++) { - int i_mirror = 2*C-i; // equals to i' = C - (i-C) - - P[i] = (R > i) ? min(R-i, P[i_mirror]) : 0; - - // Attempt to expand palindrome centered at i - while (T[i + 1 + P[i]] == T[i - 1 - P[i]]) - P[i]++; - - // If palindrome centered at i expand past R, - // adjust center based on expanded palindrome. - if (i + P[i] > R) { - C = i; - R = i + P[i]; - } - } - - // Find the maximum element in P. - int maxLen = 0; - int centerIndex = 0; - for (int i = 1; i < n-1; i++) { - if (P[i] > maxLen) { - maxLen = P[i]; - centerIndex = i; - } - } - - return s.substr((centerIndex - 1 - maxLen)/2, maxLen); - } - private: - string preProcess(string s) { - int n = s.length(); - if (n == 0) return "^$"; - string ret = "^"; - for (int i = 0; i < n; i++) - ret += "#" + s.substr(i, 1); - - ret += "#$"; - return ret; - } -}; diff --git a/C++/longestValidParentheses.cpp b/C++/longestValidParentheses.cpp deleted file mode 100644 index 9f18d84fb..000000000 --- a/C++/longestValidParentheses.cpp +++ /dev/null @@ -1,49 +0,0 @@ -// Time Complexity: O(n) -// Space Complexity: O(1) - -class Solution { - public: - int longestValidParentheses(string s) { - int ans = 0; - - int depth = 0; - int last = -1; - for(int i = 0; i < s.length(); ++i) { - if(s[i] == '(') { - ++depth; - } - else { - --depth; - if(depth < 0) { - last = i; - depth = 0; - } - else { - if(depth == 0) - ans = max(ans, i - last); - } - } - } - - depth = 0; - last = s.size(); - for(int i = s.length() - 1; i >= 0; --i) { - if(s[i] == ')') { - ++depth; - } - else { - --depth; - if(depth < 0) { - last = i; - depth = 0; - } - else { - if(depth == 0) - ans = max(ans, last - i); - } - } - } - - return ans; - } -}; diff --git a/C++/loud-and-rich.cpp b/C++/loud-and-rich.cpp new file mode 100644 index 000000000..534ec108d --- /dev/null +++ b/C++/loud-and-rich.cpp @@ -0,0 +1,33 @@ +// Time: O(q + r) +// Space: O(q + r) + +class Solution { +public: + vector loudAndRich(vector>& richer, vector& quiet) { + vector> graph(quiet.size()); + for (const auto& r : richer) { + graph[r[1]].emplace_back(r[0]); + } + vector result(quiet.size(), -1); + for (int i = 0; i < quiet.size(); ++i) { + dfs(graph, quiet, i, &result); + } + return result; + } + +private: + int dfs(const vector>& graph, + const vector& quiet, + int node, vector *result) { + if ((*result)[node] == -1) { + (*result)[node] = node; + for (const auto& nei : graph[node]) { + int smallest_person = dfs(graph, quiet, nei, result); + if (quiet[smallest_person] < quiet[(*result)[node]]) { + (*result)[node] = smallest_person; + } + } + } + return (*result)[node]; + } +}; diff --git a/C++/lowest-common-ancestor-of-a-binary-search-tree.cpp b/C++/lowest-common-ancestor-of-a-binary-search-tree.cpp new file mode 100644 index 000000000..ef1f95021 --- /dev/null +++ b/C++/lowest-common-ancestor-of-a-binary-search-tree.cpp @@ -0,0 +1,27 @@ +// Time: O(h) +// Space: O(1) + +/** + * 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* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) { + auto s = min(p->val, q->val); + auto b = max(p->val, q->val); + + while (root->val < s || root->val > b) { + // Keep searching since root is outside of [s, b]. + root = s <= root->val ? root->left : root->right; + } + + // s <= root->val && root->val <= b. + return root; + } +}; diff --git a/C++/lowest-common-ancestor-of-a-binary-tree.cpp b/C++/lowest-common-ancestor-of-a-binary-tree.cpp new file mode 100644 index 000000000..d01235a99 --- /dev/null +++ b/C++/lowest-common-ancestor-of-a-binary-tree.cpp @@ -0,0 +1,29 @@ +// Time: O(n) +// Space: O(h) + +/** + * 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* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) { + if (!root || root == p || root == q) { + return root; + } + TreeNode *left = lowestCommonAncestor(root->left, p, q); + TreeNode *right = lowestCommonAncestor(root->right, p, q); + // 1. If the current subtree contains both p and q, + // return their LCA. + // 2. If only one of them is in that subtree, + // return that one of them. + // 3. If neither of them is in that subtree, + // return the node of that subtree. + return left ? (right ? root : left) : right; + } +}; diff --git a/C++/lru-cache.cpp b/C++/lru-cache.cpp new file mode 100644 index 000000000..bdc844863 --- /dev/null +++ b/C++/lru-cache.cpp @@ -0,0 +1,45 @@ +// Time: O(1), per operation. +// Space: O(k), k is the capacity of cache. + +#include + +class LRUCache { +public: + LRUCache(int capacity) : capa_(capacity) { + } + + int get(int key) { + if (map_.find(key) != map_.end()) { + // It key exists, update it. + const auto value = map_[key]->second; + update(key, value); + return value; + } else { + return -1; + } + } + + void put(int key, int value) { + // If cache is full while inserting, remove the last one. + if (map_.find(key) == map_.end() && list_.size() == capa_) { + auto del = list_.back(); list_.pop_back(); + map_.erase(del.first); + } + update(key, value); + } + +private: + list> list_; // key, value + unordered_map>::iterator> map_; // key, list iterator + int capa_; + + // Update (key, iterator of (key, value)) pair + void update(int key, int value) { + auto it = map_.find(key); + if (it != map_.end()) { + list_.erase(it->second); + } + list_.emplace_front(key, value); + map_[key] = list_.begin(); + } +}; diff --git a/C++/magic-squares-in-grid.cpp b/C++/magic-squares-in-grid.cpp new file mode 100644 index 000000000..9cc35ff71 --- /dev/null +++ b/C++/magic-squares-in-grid.cpp @@ -0,0 +1,46 @@ +// Time: O(m * n) +// Space: O(1) + +class Solution { +public: + int numMagicSquaresInside(vector>& grid) { + int result = 0; + for (int i = 0; i + k - 1 < grid.size(); ++i) { + for (int j = 0; j + k - 1 < grid[j].size(); ++j) { + if (magic(grid, i, j)) { + ++result; + } + } + } + return result; + } + +private: + static const int k = 3; + + bool magic(const vector>& grid, int r, int c) { + const int expect = k * (k * k + 1) / 2; + unordered_set nums; + int min_num = numeric_limits::max(); + int sum_diag = 0, sum_anti = 0; + for (int i = 0; i < k; ++i) { + sum_diag += grid[r + i][c + i]; + sum_anti += grid[r + i][c + k - 1 - i]; + int sum_r = 0, sum_c = 0; + for (int j = 0; j < k; ++j) { + min_num = min(min_num, grid[r + i][c + j]); + nums.emplace(grid[r + i][c + j]); + sum_r += grid[r + i][c + j]; + sum_c += grid[r + j][c + i]; + } + if (!(sum_r == sum_c && + sum_c == expect)) { + return false; + } + } + return sum_diag == sum_anti && + sum_anti == expect && + nums.size() == k * k && + min_num == 1; + } +}; diff --git a/C++/magical-string.cpp b/C++/magical-string.cpp new file mode 100644 index 000000000..90b95c430 --- /dev/null +++ b/C++/magical-string.cpp @@ -0,0 +1,13 @@ +// Time: O(n) +// Space: O(n) + +class Solution { +public: + int magicalString(int n) { + string S = "122"; + for (int i = 2; S.length() < n; ++i) { + S += string(S[i] - '0', S.back() ^ 3); + } + return count(S.begin(), S.begin() + n, '1'); + } +}; diff --git a/C++/majority-element-ii.cpp b/C++/majority-element-ii.cpp new file mode 100644 index 000000000..93406d205 --- /dev/null +++ b/C++/majority-element-ii.cpp @@ -0,0 +1,51 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + vector majorityElement(vector& nums) { + int k = 3; + const int n = nums.size(); + unordered_map hash; + + for (const auto& i : nums) { + ++hash[i]; + // Detecting k items in hash, at least one of them must have exactly + // one in it. We will discard those k items by one for each. + // This action keeps the same mojority numbers in the remaining numbers. + // Because if x / n > 1 / k is true, then (x - 1) / (n - k) > 1 / k is also true. + if (hash.size() == k) { + auto it = hash.begin(); + while (it != hash.end()) { + if (--(it->second) == 0) { + hash.erase(it++); + } else { + ++it; + } + } + } + } + + // Resets hash for the following counting. + for (auto& it : hash) { + it.second = 0; + } + + // Counts the occurrence of each candidate integer. + for (const auto& i : nums) { + auto it = hash.find(i); + if (it != hash.end()) { + ++it->second; + } + } + + // Selects the integer which occurs > [n / k] times. + vector ret; + for (const pair& it : hash) { + if (it.second > n / k) { + ret.emplace_back(it.first); + } + } + return ret; + } +}; diff --git a/C++/majority-element.cpp b/C++/majority-element.cpp new file mode 100644 index 000000000..4d0d38cb8 --- /dev/null +++ b/C++/majority-element.cpp @@ -0,0 +1,21 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + int majorityElement(vector& nums) { + int ans = nums[0], cnt = 1; + for (const auto& i : nums) { + if (i == ans) { + ++cnt; + } else { + --cnt; + if (cnt == 0) { + ans = i; + cnt = 1; + } + } + } + return ans; + } +}; diff --git a/C++/making-a-large-island.cpp b/C++/making-a-large-island.cpp new file mode 100644 index 000000000..02af3f7a1 --- /dev/null +++ b/C++/making-a-large-island.cpp @@ -0,0 +1,60 @@ +// Time: O(n^2) +// Space: O(n^2) + +class Solution { +public: + int largestIsland(vector>& grid) { + int result = 0; + unordered_map area; + int index = 2; + for (int r = 0; r < grid.size(); ++r) { + for (int c = 0; c < grid[r].size(); ++c) { + if (grid[r][c] != 1) { + continue; + } + area[index] = dfs(r, c, index, &grid); + result = max(result, area[index++]); + } + } + + for (int r = 0; r < grid.size(); ++r) { + for (int c = 0; c < grid[r].size(); ++c) { + if (grid[r][c] != 0) { + continue; + } + unordered_set seen; + for (const auto& d :directions) { + int nr = r + d.first, nc = c + d.second; + if (0 <= nr && nr < grid.size() && + 0 <= nc && nc < grid[0].size() && + grid[nr][nc] > 1) { + seen.emplace(grid[nr][nc]); + } + } + int sum = 0; + for (const auto& i : seen) { + sum += area[i]; + } + result = max(result, 1 + sum); + } + } + return result; + } + +private: + const vector> directions{{0, -1}, {0, 1}, {-1, 0}, {1, 0}}; + + int dfs(int r, int c, int index, vector> *grid) { + if (!(0 <= r && r < grid->size() && + 0 <= c && c < (*grid)[0].size() && + (*grid)[r][c] == 1)) { + return 0; + } + int result = 1; + (*grid)[r][c] = index; + for (const auto& d :directions) { + result += dfs(r + d.first, c + d.second, index, grid); + } + return result; + } +}; diff --git a/C++/map-sum-pairs.cpp b/C++/map-sum-pairs.cpp new file mode 100644 index 000000000..3c3ee4aca --- /dev/null +++ b/C++/map-sum-pairs.cpp @@ -0,0 +1,79 @@ +// Time: O(n), n is the length of key +// Space: O(t), t is the number of nodes in trie + +class MapSum { +public: + /** Initialize your data structure here. */ + MapSum() { + + } + + void insert(string key, int val) { + trie_.Insert(key, val); + } + + int sum(string prefix) { + return trie_.GetCount(prefix); + } + +private: + struct TrieNode { + bool isString = false; + int count = 0; + int val = 0; + unordered_map leaves; + + void Insert(const string& s, const int val) { + const auto delta = val - GetVal(s); + auto* p = this; + for (const auto& c : s) { + if (p->leaves.find(c) == p->leaves.cend()) { + p->leaves[c] = new TrieNode; + } + p = p->leaves[c]; + p->count += delta; + } + p->val = val; + p->isString = true; + } + + int GetVal(const string& s) const { + auto* p = this; + for (const auto& c : s) { + if (p->leaves.find(c) == p->leaves.cend()) { + return 0; + } + p = p->leaves.at(c); + } + return p->isString ? p->val : 0; + } + + int GetCount(const string& s) const { + auto* p = this; + for (const auto& c : s) { + if (p->leaves.find(c) == p->leaves.cend()) { + return 0; + } + p = p->leaves.at(c); + } + return p->count; + } + + ~TrieNode() { + for (auto& kv : leaves) { + if (kv.second) { + delete kv.second; + } + } + } + }; + TrieNode trie_; +}; + +/** + * Your MapSum object will be instantiated and called as such: + * MapSum obj = new MapSum(); + * obj.insert(key,val); + * int param_2 = obj.sum(prefix); + */ + diff --git a/C++/masking-personal-information.cpp b/C++/masking-personal-information.cpp new file mode 100644 index 000000000..0fc04861c --- /dev/null +++ b/C++/masking-personal-information.cpp @@ -0,0 +1,25 @@ +// Time: O(1) +// Space: O(1) + +class Solution { +public: + string maskPII(string S) { + auto at = S.find("@"); + if (at != string::npos) { + transform(S.begin(), S.end(), S.begin(), ::tolower); + return S.substr(0, 1) + "*****" + S.substr(at - 1); + } + string digits; + for (const auto& c : S) { + if (::isdigit(c)) { + digits.push_back(c); + } + } + string local{"***-***-"}; + local += digits.substr(digits.length() - 4); + if (digits.length() == 10) { + return local; + } + return "+" + string(digits.length() - 10, '*') + "-" + local; + } +}; diff --git a/C++/matchsticks-to-square.cpp b/C++/matchsticks-to-square.cpp new file mode 100644 index 000000000..565a4211c --- /dev/null +++ b/C++/matchsticks-to-square.cpp @@ -0,0 +1,40 @@ +// Time: O(n * s * 2^n), s is the number of subset of which sum equals to side length. +// Space: O(n * (2^n + s)) + +class Solution { +public: + bool makesquare(vector& nums) { + int sum = accumulate(nums.begin(), nums.end(), 0); + if (sum % 4) { + return false; + } + + const auto side_len = sum / 4; + const auto all = (1 << nums.size()) - 1; + + vector used_subsets; + vector valid_half_subsets(1 << nums.size()); + + for (int subset = 0; subset <= all; ++subset) { + int subset_sum = 0; + for (int i = 0; i < nums.size(); ++i) { + if (subset & (1 << i)) { + subset_sum += nums[i]; + } + } + if (subset_sum == side_len) { + for (const auto& used_subset : used_subsets) { + if ((used_subset & subset) == 0) { + int valid_half_subset = used_subset | subset; + valid_half_subsets[valid_half_subset] = true; + if (valid_half_subsets[all ^ valid_half_subset]) { + return true; + } + } + } + used_subsets.emplace_back(subset); + } + } + return false; + } +}; diff --git a/C++/max-area-of-island.cpp b/C++/max-area-of-island.cpp new file mode 100644 index 000000000..da9e5aa59 --- /dev/null +++ b/C++/max-area-of-island.cpp @@ -0,0 +1,35 @@ +// Time: O(m * n) +// Space: O(m * n), the max depth of dfs may be m * n + +class Solution { +public: + int maxAreaOfIsland(vector>& grid) { + int result = 0; + for (int i = 0; i < grid.size(); ++i) { + for (int j = 0; j < grid[0].size(); ++j) { + int area = 0; + if (dfs(i, j, &grid, &area)) { + result = max(result, area); + } + } + } + return result; + } + +private: + bool dfs(const int i, const int j, vector> *grid, int *area) { + static const vector> directions{{-1, 0}, { 1, 0}, + { 0, 1}, { 0, -1}}; + if (i < 0 || i >= grid->size() || + j < 0 || j >= (*grid)[0].size() || + (*grid)[i][j] <= 0) { + return false; + } + (*grid)[i][j] *= -1; + ++(*area); + for (const auto& d : directions) { + dfs(i + d.first, j + d.second, grid, area); + } + return true; + } +}; diff --git a/C++/max-chunks-to-make-sorted-ii.cpp b/C++/max-chunks-to-make-sorted-ii.cpp new file mode 100644 index 000000000..6eaef9ab0 --- /dev/null +++ b/C++/max-chunks-to-make-sorted-ii.cpp @@ -0,0 +1,21 @@ +// Time: O(nlogn) +// Space: O(n) + +class Solution { +public: + int maxChunksToSorted(vector& arr) { + vector idxs(arr.size()); + iota(idxs.begin(), idxs.end(), 0); + sort(idxs.begin(), idxs.end(), + [&](int i1, int i2) { + return arr[i1] != arr[i2] ? arr[i1] < arr[i2] : i1 < i2; + }); + + int result = 0; + for (auto i = 0, max_i = 0; i < idxs.size(); ++i) { + max_i = max(max_i, idxs[i]); + if (max_i == i) ++result; + } + return result; + } +}; diff --git a/C++/max-chunks-to-make-sorted.cpp b/C++/max-chunks-to-make-sorted.cpp new file mode 100644 index 000000000..822cf69e5 --- /dev/null +++ b/C++/max-chunks-to-make-sorted.cpp @@ -0,0 +1,14 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + int maxChunksToSorted(vector& arr) { + int result = 0; + for (int i = 0, max_i = 0; i < arr.size(); ++i) { + max_i = max(max_i, arr[i]); + if (max_i == i) ++result; + } + return result; + } +}; diff --git a/C++/max-consecutive-ones-ii.cpp b/C++/max-consecutive-ones-ii.cpp new file mode 100644 index 000000000..289e662d5 --- /dev/null +++ b/C++/max-consecutive-ones-ii.cpp @@ -0,0 +1,19 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + int findMaxConsecutiveOnes(vector& nums) { + int result = 0, prev = 0, curr = 0; + for (const auto& n : nums) { + if (n == 0) { + result = max(result, prev + curr + 1); + prev = curr; + curr = 0; + } else { + ++curr; + } + } + return min(max(result, prev + curr + 1), static_cast(nums.size())); + } +}; diff --git a/C++/max-consecutive-ones.cpp b/C++/max-consecutive-ones.cpp new file mode 100644 index 000000000..5510733e3 --- /dev/null +++ b/C++/max-consecutive-ones.cpp @@ -0,0 +1,14 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + int findMaxConsecutiveOnes(vector& nums) { + int result = 0, local_max = 0; + for (const auto& n : nums) { + local_max = n ? local_max + 1 : 0; + result = max(result, local_max); + } + return result; + } +}; diff --git a/C++/max-increase-to-keep-city-skyline.cpp b/C++/max-increase-to-keep-city-skyline.cpp new file mode 100644 index 000000000..b94d2a94d --- /dev/null +++ b/C++/max-increase-to-keep-city-skyline.cpp @@ -0,0 +1,23 @@ +// Time: O(n^2) +// Space: O(n) + +class Solution { +public: + int maxIncreaseKeepingSkyline(vector>& grid) { + vector row_maxes(grid.size()); + vector col_maxes(grid[0].size()); + for (int r = 0; r < grid.size(); ++r) { + for (int c = 0; c < grid[r].size(); ++c) { + row_maxes[r] = max(row_maxes[r], grid[r][c]); + col_maxes[c] = max(col_maxes[c], grid[r][c]); + } + } + int result = 0; + for (int r = 0; r < grid.size(); ++r) { + for (int c = 0; c < grid[r].size(); ++c) { + result += min(row_maxes[r], col_maxes[c]) - grid[r][c]; + } + } + return result; + } +}; diff --git a/C++/max-points-on-a-line.cpp b/C++/max-points-on-a-line.cpp new file mode 100644 index 000000000..816321f52 --- /dev/null +++ b/C++/max-points-on-a-line.cpp @@ -0,0 +1,44 @@ +// Time: O(n^2) +// Space: O(n) + +/** + * Definition for a point. + * struct Point { + * int x; + * int y; + * Point() : x(0), y(0) {} + * Point(int a, int b) : x(a), y(b) {} + * }; + */ +class Solution { +public: + int maxPoints(vector& points) { + int max_points = 0; + for (int i = 0; i < points.size(); ++i) { + unordered_map slope_count; + const auto& start = points[i]; + int same = 1; + + for (int j = i + 1; j < points.size(); ++j) { + const auto& end = points[j]; + if (start.x == end.x && start.y == end.y) { + ++same; + } else { + auto slope = numeric_limits::max(); + if (start.x - end.x != 0) { + slope = (start.y - end.y) * 1.0 / (start.x - end.x); + } + ++slope_count[slope]; + } + } + + int current_max = same; + for (const auto& kvp : slope_count) { + current_max = max(current_max, kvp.second + same); + } + max_points = max(max_points, current_max); + } + + return max_points; + } +}; diff --git a/C++/max-stack.cpp b/C++/max-stack.cpp new file mode 100644 index 000000000..d62fdf41e --- /dev/null +++ b/C++/max-stack.cpp @@ -0,0 +1,63 @@ +// Time: push: O(logn) +// pop: O(logn) +// popMax: O(logn) +// top: O(1) +// peekMax: O(1) +// Space: O(n), n is the number of values in the current stack + +class MaxStack { +public: + /** initialize your data structure here. */ + MaxStack() { + + } + + void push(int x) { + const auto idx = idx_to_val_.empty() ? 0 : idx_to_val_.cbegin()->first + 1; + idx_to_val_[idx] = x; + val_to_idxs_[x].emplace_back(idx); + } + + int pop() { + const auto val = idx_to_val_.cbegin()->second; + remove(val); + return val; + } + + int top() { + return idx_to_val_.cbegin()->second; + } + + int peekMax() { + return val_to_idxs_.cbegin()->first; + } + + int popMax() { + const auto val = val_to_idxs_.cbegin()->first; + remove(val); + return val; + } + +private: + void remove(const int val) { + const auto idx = val_to_idxs_[val].back(); + val_to_idxs_[val].pop_back(); + if (val_to_idxs_[val].empty()) { + val_to_idxs_.erase(val); + } + idx_to_val_.erase(idx); + } + + map> idx_to_val_; + map, greater> val_to_idxs_; +}; + +/** + * Your MaxStack object will be instantiated and called as such: + * MaxStack obj = new MaxStack(); + * obj.push(x); + * int param_2 = obj.pop(); + * int param_3 = obj.top(); + * int param_4 = obj.peekMax(); + * int param_5 = obj.popMax(); + */ diff --git a/C++/max-sum-of-sub-matrix-no-larger-than-k.cpp b/C++/max-sum-of-sub-matrix-no-larger-than-k.cpp new file mode 100644 index 000000000..1fd294eca --- /dev/null +++ b/C++/max-sum-of-sub-matrix-no-larger-than-k.cpp @@ -0,0 +1,39 @@ +// Time: O(min(m, n)^2 * max(m, n) * log(max(m, n))) +// Space: O(max(m, n)) + +class Solution { +public: + int maxSumSubmatrix(vector>& matrix, int k) { + if (matrix.empty()) { + return 0; + } + + const int m = min(matrix.size(), matrix[0].size()); + const int n = max(matrix.size(), matrix[0].size()); + int result = numeric_limits::min(); + + for (int i = 0; i < m; ++i) { + vector sums(n, 0); + for (int j = i; j < m; ++j) { + for (int l = 0; l < n; ++l) { + sums[l] += (m == matrix.size()) ? matrix[j][l] : matrix[l][j]; + } + + // Find the max subarray no more than K. + set accu_sum_set; + accu_sum_set.emplace(0); + int accu_sum = 0; + for (int sum : sums) { + accu_sum += sum; + auto it = accu_sum_set.lower_bound(accu_sum - k); + if (it != accu_sum_set.end()) { + result = max(result, accu_sum - *it); + } + accu_sum_set.emplace(accu_sum); + } + } + } + + return result; + } +}; diff --git a/C++/maxArea.cpp b/C++/maxArea.cpp deleted file mode 100644 index a6c8fb46a..000000000 --- a/C++/maxArea.cpp +++ /dev/null @@ -1,21 +0,0 @@ -// Time Complexity: O(n) -// Space Complexity: O(1) - -class Solution { - public: - int maxArea(vector &height) { - int start = 0, end = height.size() - 1, ans = 0; - - while(start < end) { - if(height[start] <= height[end]) { - ans = max(ans, height[start] * (end - start)); - start++; - } - if(height[start] > height[end]) { - ans = max(ans, height[end] * (end - start)); - end--; - } - } - return ans; - } -}; diff --git a/C++/maxProfitI.cpp b/C++/maxProfitI.cpp deleted file mode 100644 index c7d4dea53..000000000 --- a/C++/maxProfitI.cpp +++ /dev/null @@ -1,21 +0,0 @@ -// Time Complexity: O(n) -// Space Complexity: O(1) - -class Solution { - public: - int maxProfit(vector &prices) { - const int n = prices.size(); - - if(n < 2) - return 0; - - // Greedy Algorithm - int ans = 0; - for(int i = 1, valley = prices[0]; i < n; ++i) { - ans = max(ans, prices[i] - valley); - valley = min(valley, prices[i]); - } - - return ans; - } -}; diff --git a/C++/maximal-rectangle.cpp b/C++/maximal-rectangle.cpp new file mode 100644 index 000000000..1f127f593 --- /dev/null +++ b/C++/maximal-rectangle.cpp @@ -0,0 +1,88 @@ +// Time: O(m * n) +// Space: O(n) + +// Ascending stack solution. +class Solution { +public: + int maximalRectangle(vector > &matrix) { + if (matrix.empty() || matrix[0].empty()) { + return 0; + } + + int res = 0; + vector height(matrix[0].size(), 0); + for (int i = 0; i < matrix.size(); ++i) { + for (int j = 0; j < matrix[0].size(); ++j) { + height[j] = matrix[i][j] == '1' ? height[j] + 1 : 0; + } + res = max(res, largestRectangleArea(height)); + } + + return res; + } + +private: + int largestRectangleArea(const vector &height) { + stack increasing_height; + int max_area = 0; + + for (int i = 0; i <= height.size();) { + if (increasing_height.empty() || + (i < height.size() && height[i] > height[increasing_height.top()])) { + increasing_height.emplace(i); + ++i; + } else { + auto h = height[increasing_height.top()]; + increasing_height.pop(); + auto left = increasing_height.empty() ? -1 : increasing_height.top(); + max_area = max(max_area, h * (i - left - 1)); + } + } + + return max_area; + } +}; + +// Time: O(m * n) +// Space: O(n) +// DP solution. +class Solution2 { +public: + int maximalRectangle(vector > &matrix) { + if (matrix.empty()) { + return 0; + } + + const int m = matrix.size(); + const int n = matrix.front().size(); + int res = 0; + vector H(n, 0); // Height of all ones rectangle include matrix[i][j]. + vector L(n, 0); // Left closed bound of all ones rectangle include matrix[i][j]. + vector R(n, n); // Right open bound of all ones rectangle include matrix[i][j]. + + for (int i = 0; i < m; ++i) { + int left = 0, right = n; + for (int j = 0; j < n; ++j) { + if (matrix[i][j] == '1') { + ++H[j]; // Update height. + L[j] = max(L[j], left); // Update left bound. + } else { + left = j + 1; + H[j] = L[j] = 0; + R[j] = n; + } + } + + for (int j = n - 1; j >= 0; --j) { + if (matrix[i][j] == '1') { + R[j] = min(R[j], right); // Update right bound. + res = max(res, H[j] * (R[j] - L[j])); + } else { + right = j; + } + } + } + + return res; + } +}; diff --git a/C++/maximal-square.cpp b/C++/maximal-square.cpp new file mode 100644 index 000000000..8fdafb1a0 --- /dev/null +++ b/C++/maximal-square.cpp @@ -0,0 +1,115 @@ +// Time: O(n^2) +// Space: O(n) + +// DP with rolling window. +class Solution { +public: + int maximalSquare(vector>& A) { + if (A.empty()) { + return 0; + } + const int m = A.size(), n = A[0].size(); + vector> size(2, vector(n, 0)); + int max_size = 0; + + for (int j = 0; j < n; ++j) { + size[0][j] = A[0][j] - '0'; + max_size = max(max_size, size[0][j]); + } + for (int i = 1; i < m; ++i) { + size[i % 2][0] = A[i][0] - '0'; + for (int j = 1; j < n; ++j) { + if (A[i][j] == '1') { + size[i % 2][j] = min(size[i % 2][j - 1], + min(size[(i - 1) % 2][j], + size[(i - 1) % 2][j - 1])) + 1; + max_size = max(max_size, size[i % 2][j]); + } else { + size[i % 2][j] = 0; + } + } + } + return max_size * max_size; + } +}; + +// Time: O(n^2) +// Space: O(n^2) +// DP. +class Solution2 { +public: + int maximalSquare(vector>& A) { + if (A.empty()) { + return 0; + } + const int m = A.size(), n = A[0].size(); + vector> size(m, vector(n, 0)); + int max_size = 0; + + for (int j = 0; j < n; ++j) { + size[0][j] = A[0][j] - '0'; + max_size = max(max_size, size[0][j]); + } + for (int i = 1; i < m; ++i) { + size[i][0] = A[i][0] - '0'; + for (int j = 1; j < n; ++j) { + if (A[i][j] == '1') { + size[i][j] = min(size[i][j - 1], + min(size[i - 1][j], + size[i - 1][j - 1])) + 1; + max_size = max(max_size, size[i][j]); + } else { + size[i][j] = 0; + } + } + } + return max_size * max_size; + } +}; + +// Time: O(n^2) +// Space: O(n^2) +// DP. +class Solution3 { +public: + struct MaxHW { + int h, w; + }; + + int maximalSquare(vector>& A) { + if (A.empty()) { + return 0; + } + + // DP table stores (h, w) for each (i, j). + vector> table(A.size(), vector(A.front().size())); + for (int i = A.size() - 1; i >= 0; --i) { + for (int j = A[i].size() - 1; j >= 0; --j) { + // Find the largest h such that (i, j) to (i + h - 1, j) are feasible. + // Find the largest w such that (i, j) to (i, j + w - 1) are feasible. + table[i][j] = A[i][j] == '1' + ? MaxHW{i + 1 < A.size() ? table[i + 1][j].h + 1 : 1, + j + 1 < A[i].size() ? table[i][j + 1].w + 1 : 1} + : MaxHW{0, 0}; + } + } + + // A table stores the length of largest square for each (i, j). + vector> s(A.size(), vector(A.front().size(), 0)); + int max_square_area = 0; + for (int i = A.size() - 1; i >= 0; --i) { + for (int j = A[i].size() - 1; j >= 0; --j) { + int side = min(table[i][j].h, table[i][j].w); + if (A[i][j]) { + // Get the length of largest square with bottom-left corner (i, j). + if (i + 1 < A.size() && j + 1 < A[i + 1].size()) { + side = min(s[i + 1][j + 1] + 1, side); + } + s[i][j] = side; + max_square_area = max(max_square_area, side * side); + } + } + } + return max_square_area; + } +}; diff --git a/C++/maximalRectangle.cpp b/C++/maximalRectangle.cpp deleted file mode 100644 index 829905865..000000000 --- a/C++/maximalRectangle.cpp +++ /dev/null @@ -1,46 +0,0 @@ -// Time Complexity: O(m * n) -// Space Complexity: O(n) - -class Solution { - public: - int maximalRectangle(vector > &matrix) { - if(matrix.empty()) - return 0; - - const int m = matrix.size(); - const int n = matrix.front().size(); - - int ans = 0; - - vector H(n, 0); // height of all ones rectangle include matrix[i][j] - vector L(n, 0); // left closed bound of all ones rectangle include matrix[i][j] - vector R(n, n); // right open bound of all onces rectangle include matrix[i][j] - - for(int i = 0; i < m; ++i) { - int left = 0, right = n; - for(int j = 0; j < n; ++j) { - if(matrix[i][j] == '1') { - ++H[j]; // update height - L[j] = max(L[j], left); // update left bound - } - else { - left = j + 1; - H[j] = L[j] = 0; - R[j] = n; - } - } - - for(int j = n - 1; j >= 0; --j) { - if(matrix[i][j] == '1') { - R[j] = min(R[j], right); // update right bound - ans = max(ans, H[j] * (R[j] - L[j])); - } - else { - right = j; - } - } - } - - return ans; - } -}; diff --git a/C++/maximize-distance-to-closest-person.cpp b/C++/maximize-distance-to-closest-person.cpp new file mode 100644 index 000000000..e5626f443 --- /dev/null +++ b/C++/maximize-distance-to-closest-person.cpp @@ -0,0 +1,20 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + int maxDistToClosest(vector& seats) { + int prev = -1, result = 1; + for (int i = 0; i < seats.size(); ++i) { + if (seats[i]) { + if (prev < 0) { + result = i; + } else { + result = max(result, (i - prev) / 2); + } + prev = i; + } + } + return max(result, static_cast(seats.size()) - 1 - prev); + } +}; diff --git a/C++/maximum-average-subarray-i.cpp b/C++/maximum-average-subarray-i.cpp new file mode 100644 index 000000000..4407e3d1f --- /dev/null +++ b/C++/maximum-average-subarray-i.cpp @@ -0,0 +1,19 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + double findMaxAverage(vector& nums, int k) { + double sum = 0; + for (int i = 0; i < k; ++i) { + sum += nums[i]; + } + double result = sum; + for (int i = k; i < nums.size(); ++i) { + sum += nums[i] - nums[i-k]; + result = max(result, sum); + } + return result / k; + } +}; + diff --git a/C++/maximum-average-subarray-ii.cpp b/C++/maximum-average-subarray-ii.cpp new file mode 100644 index 000000000..3d8d658e8 --- /dev/null +++ b/C++/maximum-average-subarray-ii.cpp @@ -0,0 +1,74 @@ +// Time: O(n) +// Space: O(n) + +class Solution { +public: + double findMaxAverage(vector& nums, int k) { + double left = *min_element(nums.begin(), nums.end()); + double delta = numeric_limits::max(); + while (delta > 1e-5) { + delta = getDelta(left, nums, k); + left += delta; + } + return left; + } + +private: + double getDelta(double avg, const vector& nums, int k) { + vector accu(nums.size() + 1); + int minval_pos = -1; + double delta = 0.0; + for (int i = 0; i < nums.size(); ++i) { + accu[i + 1] = nums[i] + accu[i] - avg; + if (i >= k - 1) { + if (minval_pos == -1 || accu[i - k + 1] < accu[minval_pos]) { + minval_pos = i - k + 1; + } + if (accu[i+1] - accu[minval_pos] >= 0) { + delta = max(delta, (accu[i + 1] - accu[minval_pos]) / (i + 1 - minval_pos)); + } + } + } + return delta; + } +}; + + +// Time: O(nlogm), m is (max_val - min_val) +// Space: O(1) +class Solution2 { +public: + double findMaxAverage(vector& nums, int k) { + double left = *min_element(nums.begin(), nums.end()); + double right = *max_element(nums.begin(), nums.end()); + while (right - left > 1e-5) { + double mid = left + (right - left) / 2; + if (isMidLargerOrEqualToTarget(mid, nums, k)) { + right = mid; + } else { + left = mid; + } + } + return left; + } + +private: + bool isMidLargerOrEqualToTarget(double mid, const vector& nums, int k) { + double sum = 0, prev = 0, min_sum = 0; + for (int i = 0; i < k; ++i) { + sum += nums[i] - mid; + } + if (sum > 0) { + return false; + } + for (int i = k; i < nums.size(); ++i) { + sum += nums[i] - mid; + prev += nums[i - k] - mid; + min_sum = min(prev, min_sum); + if (sum > min_sum) { + return false; + } + } + return true; + } +}; \ No newline at end of file diff --git a/C++/maximum-binary-tree.cpp b/C++/maximum-binary-tree.cpp new file mode 100644 index 000000000..2b275d427 --- /dev/null +++ b/C++/maximum-binary-tree.cpp @@ -0,0 +1,31 @@ +// Time: O(n) +// Space: O(n) + +/** + * 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* constructMaximumBinaryTree(vector& nums) { + // https://github.com/kamyu104/LintCode/blob/master/C++/max-tree.cpp + vector nodeStack; + for (int i = 0; i < nums.size(); ++i) { + auto node = new TreeNode(nums[i]); + while (!nodeStack.empty() && nums[i] > nodeStack.back()->val) { + node->left = nodeStack.back(); + nodeStack.pop_back(); + } + if (!nodeStack.empty()) { + nodeStack.back()->right = node; + } + nodeStack.emplace_back(node); + } + return nodeStack.front(); + } +}; diff --git a/C++/maxDepth.cpp b/C++/maximum-depth-of-binary-tree.cpp similarity index 51% rename from C++/maxDepth.cpp rename to C++/maximum-depth-of-binary-tree.cpp index df822474a..23c2cdc8d 100644 --- a/C++/maxDepth.cpp +++ b/C++/maximum-depth-of-binary-tree.cpp @@ -1,5 +1,8 @@ +// Time: O(n) +// Space: O(h) + /** - * Definition for binary tree + * Definition for a binary tree node. * struct TreeNode { * int val; * TreeNode *left; @@ -9,9 +12,10 @@ */ class Solution { public: - int maxDepth(TreeNode *root) { - if(!root) + int maxDepth(TreeNode* root) { + if (!root) { return 0; - return max(maxDepth(root->left), maxDepth(root->right))+1; + } + return max(maxDepth(root->left), maxDepth(root->right)) + 1; } }; diff --git a/C++/maximum-depth-of-n-ary-tree.cpp b/C++/maximum-depth-of-n-ary-tree.cpp new file mode 100644 index 000000000..48fd5d13b --- /dev/null +++ b/C++/maximum-depth-of-n-ary-tree.cpp @@ -0,0 +1,31 @@ +// Time: O(n) +// Space: O(h) + +/* +// Definition for a Node. +class Node { +public: + int val; + vector children; + + Node() {} + + Node(int _val, vector _children) { + val = _val; + children = _children; + } +}; +*/ +class Solution { +public: + int maxDepth(Node* root) { + if (root == nullptr) { + return 0; + } + int depth = 0; + for (const auto& child : root->children) { + depth = max(depth, maxDepth(child)); + } + return 1 + depth; + } +}; diff --git a/C++/maximum-distance-in-arrays.cpp b/C++/maximum-distance-in-arrays.cpp new file mode 100644 index 000000000..f5af49463 --- /dev/null +++ b/C++/maximum-distance-in-arrays.cpp @@ -0,0 +1,19 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + int maxDistance(vector>& arrays) { + int result = 0; + int min_val = arrays[0].front(), max_val = arrays[0].back(); + for (int i = 1; i < arrays.size(); ++i) { + result = max(result, + max(max_val - arrays[i].front(), + arrays[i].back() - min_val)); + min_val = min(min_val, arrays[i].front()); + max_val = max(max_val, arrays[i].back()); + } + return result; + } +}; + diff --git a/C++/maximum-gap.cpp b/C++/maximum-gap.cpp new file mode 100644 index 000000000..07d005c73 --- /dev/null +++ b/C++/maximum-gap.cpp @@ -0,0 +1,92 @@ +// Time: O(n) +// Space: O(n) + +class Solution { +public: + struct Bucket { + int max = numeric_limits::min(); + int min = numeric_limits::max(); + }; + + int maximumGap(vector& nums) { + if (nums.size() < 2) { + return 0; + } + + // Init bucket. + int max_val = *max_element(nums.cbegin(), nums.cend()); + int min_val = *min_element(nums.cbegin(), nums.cend()); + int gap = max(1, static_cast((max_val - min_val) / + (nums.size() - 1))); + vector buckets((max_val - min_val) / gap + 1); + + // Find the bucket where the n should be put. + for (const auto& n : nums) { + // min_val / max_val is in the first / last bucket. + if (n == max_val || n == min_val) { + continue; + } + int i = (n - min_val) / gap; + buckets[i].min = min(buckets[i].min, n); + buckets[i].max = max(buckets[i].max, n); + } + + // Maximum gap should not be smaller than any gap inside the bucket. + // i.e. max_gap >= (max_val - min_val) / (count - 1) + // Thus, only count each bucket gap between the first and the last bucket. + int max_gap = 0, pre_bucket_max = min_val; + for (const auto& bucket : buckets) { + if (bucket.min != numeric_limits::max()) { + max_gap = max(max_gap, bucket.min - pre_bucket_max); + pre_bucket_max = bucket.max; + } + } + // Count the last bucket. + max_gap = max(max_gap, max_val - pre_bucket_max); + + return max_gap; + } +}; + +// Time: O(nlogn) +// Space: O(n) +class Solution2 { +public: + int maximumGap(vector& nums) { + if (nums.size() < 2) { + return 0; + } + + // Init bucket. + int max_val = *max_element(nums.cbegin(), nums.cend()); + int min_val = *min_element(nums.cbegin(), nums.cend()); + int gap = max(1, static_cast((max_val - min_val) / + (nums.size() - 1))); + map> bucket; + using ValueType = enum {MIN, MAX}; + + // Find the bucket where the n should be put. + for (const auto& n : nums) { + // min_val / max_val is in the first / last bucket. + if (n == max_val || n == min_val) { + continue ; + } + int i = (n - min_val) / gap; + bucket[i][MIN] = min(!bucket[i][MIN] ? numeric_limits::max() : + bucket[i][MIN], n); + bucket[i][MAX] = max(!bucket[i][MAX] ? numeric_limits::min() : + bucket[i][MAX], n); + } + + // Count each bucket gap between the first and the last bucket. + int max_gap = 0, pre_bucket_max = min_val; + for (auto& kvp : bucket) { + max_gap = max(max_gap, kvp.second[MIN] - pre_bucket_max); + pre_bucket_max = (kvp.second)[MAX]; + } + // Count the last bucket. + max_gap = max(max_gap, max_val - pre_bucket_max); + + return max_gap; + } +}; diff --git a/C++/maximum-length-of-pair-chain.cpp b/C++/maximum-length-of-pair-chain.cpp new file mode 100644 index 000000000..e834b846e --- /dev/null +++ b/C++/maximum-length-of-pair-chain.cpp @@ -0,0 +1,21 @@ +// Time: O(nlogn) +// Space: O(1) + +class Solution { +public: + int findLongestChain(vector>& pairs) { + sort(pairs.begin(), pairs.end(), + [](const vector& a, const vector& b) { + return a[1] < b[1]; + }); + int cnt = 0; + for (int i = 0, j = 0; j < pairs.size(); ++j) { + if (j == 0 || pairs[i][1] < pairs[j][0]) { + ++cnt; + i = j; + } + } + return cnt; + } +}; + diff --git a/C++/maximum-length-of-repeated-subarray.cpp b/C++/maximum-length-of-repeated-subarray.cpp new file mode 100644 index 000000000..2264817f8 --- /dev/null +++ b/C++/maximum-length-of-repeated-subarray.cpp @@ -0,0 +1,101 @@ +// Time: O(m * n) +// Space: O(min(m, n)) + +// dp solution (99 ms) +class Solution { +public: + int findLength(vector& A, vector& B) { + if (A.size() < B.size()) { + return findLength(B, A); + } + int result = 0; + vector> dp(2, vector(B.size() + 1)); + for (int i = 0; i < A.size(); ++i) { + for (int j = 0; j < B.size(); ++j) { + if (A[i] == B[j]) { + dp[(i + 1) % 2][j + 1] = dp[i % 2][j] + 1; + result = max(result, dp[(i + 1) % 2][j + 1]); + } else { + dp[(i + 1) % 2][j + 1] = 0; + } + } + } + return result; + } +}; + + +// Time: O(m * n * log(min(m, n))) +// Space: O(min(m, n)) +// Binary search + rolling hash solution (36 ms) +class Solution2 { +public: + int findLength(vector& A, vector& B) { + if (A.size() > B.size()) { + return findLength(B, A); + } + int left = 0, right = min(A.size(), B.size()) + 1; + while (left < right) { + const auto mid = left + (right-left) / 2; + if (!check(mid, A, B)) { // find the min idx such that check(idx) == false + right = mid; + } else { + left = mid + 1; + } + } + return left - 1; + } + +private: + bool check(const int guess, const vector& A, const vector& B) { + unordered_map> hashes; + int i = 0; + for (const auto& hash_a : rolling_hashes(A, guess)) { + hashes[hash_a].emplace_back(i++); + } + int j = 0; + for (const auto& hash_b : rolling_hashes(B, guess)) { + for (const auto& i : hashes[hash_b]) { + if (equal(A.begin() + i, A.begin() + i + guess, B.begin() + j)) { + return true; + } + } + ++j; + } + return false; + } + + vector rolling_hashes(const vector& source, const int length) { + static const uint64_t M = 1000000007; + static const uint64_t p = 113; + static const uint64_t p_inv = pow(p, M - 2, M); + vector result(source.size() - length + 1); + uint64_t hash = 0, power = 1; + if (length == 0) { + return result; + } + for (int i = 0; i < source.size(); ++i) { + hash = (hash + source[i] * power) % M; + if (i < length - 1) { + power = (power * p) % M; + } else { + result[i - (length - 1)] = hash; + hash = (hash - source[i - (length - 1)]) * p_inv % M; + } + } + return result; + } + + uint64_t pow(uint64_t a,uint64_t b, uint64_t m) { + a %= m; + uint64_t result = 1; + while (b) { + if (b & 1) { + result = (result * a) % m; + } + a = (a * a) % m; + b >>= 1; + } + return result; + } +}; diff --git a/C++/maximum-product-of-three-numbers.cpp b/C++/maximum-product-of-three-numbers.cpp new file mode 100644 index 000000000..57dc25757 --- /dev/null +++ b/C++/maximum-product-of-three-numbers.cpp @@ -0,0 +1,33 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + int maximumProduct(vector& nums) { + auto min1 = numeric_limits::max(); + auto min2 = numeric_limits::max(); + auto max1 = numeric_limits::min(); + auto max2 = numeric_limits::min(); + auto max3 = numeric_limits::min(); + for (const auto& n: nums) { + if (n <= min1) { + min2 = min1; + min1 = n; + } else if (n <= min2) { + min2 = n; + } + if (n >= max1) { + max3 = max2; + max2 = max1; + max1 = n; + } else if (n >= max2) { + max3 = max2; + max2 = n; + } else if (n >= max3) { + max3 = n; + } + } + return max(min1 * min2 * max1, max1 * max2 * max3); + } +}; + diff --git a/C++/maximum-product-of-word-lengths.cpp b/C++/maximum-product-of-word-lengths.cpp new file mode 100644 index 000000000..bad7e7817 --- /dev/null +++ b/C++/maximum-product-of-word-lengths.cpp @@ -0,0 +1,65 @@ +// Time: O(n) ~ O(n^2) +// Space: O(n) + +// Counting Sort + Pruning + Bit Manipulation +class Solution { +public: + int maxProduct(vector& words) { + words = counting_sort(words); + vector bits(words.size()); + for (int i = 0; i < words.size(); ++i) { + for (const auto& c : words[i]) { + bits[i] |= (1 << (c - 'a')); + } + } + int max_product = 0; + for (int i = 0; i + 1 < words.size() && pow(words[i].length(), 2) > max_product; ++i) { + for (int j = i + 1; j < words.size() && words[i].length() * words[j].length() > max_product; ++j) { + if (!(bits[i] & bits[j])) { + max_product = words[i].length() * words[j].length(); + } + } + } + return max_product; + } + + vector counting_sort(const vector& words) { + const int k = 1000; // k is max length of words in the dictionary + vector> buckets(k); + for (const auto& word : words) { + buckets[word.length()].emplace_back(word); + } + vector res; + for (int i = k - 1; i >= 0; --i) { + if (!buckets[i].empty()) { + move(buckets[i].begin(), buckets[i].end(), back_inserter(res)); + } + } + return res; + } +}; + +// Time: O(nlogn) ~ O(n^2) +// Space: O(n) +// Sorting + Pruning + Bit Manipulation +class Solution2 { +public: + int maxProduct(vector& words) { + sort(words.begin(), words.end(), [](const string& a, const string& b) { return a.length() > b.length(); }); + vector bits(words.size()); + for (int i = 0; i < words.size(); ++i) { + for (const auto& c : words[i]) { + bits[i] |= (1 << (c - 'a')); + } + } + int max_product = 0; + for (int i = 0; i + 1 < words.size() && pow(words[i].length(), 2) > max_product; ++i) { + for (int j = i + 1; j < words.size() && words[i].length() * words[j].length() > max_product; ++j) { + if (!(bits[i] & bits[j])) { + max_product = words[i].length() * words[j].length(); + } + } + } + return max_product; + } +}; diff --git a/C++/maximum-size-subarray-sum-equals-k.cpp b/C++/maximum-size-subarray-sum-equals-k.cpp new file mode 100644 index 000000000..9784071ef --- /dev/null +++ b/C++/maximum-size-subarray-sum-equals-k.cpp @@ -0,0 +1,22 @@ +// Time: O(n) +// Space: O(n) + +class Solution { +public: + int maxSubArrayLen(vector& nums, int k) { + unordered_map sums; + int cur_sum = 0, max_len = 0; + for (int i = 0; i < nums.size(); ++i) { + cur_sum += nums[i]; + if (cur_sum == k) { + max_len = i + 1; + } else if (sums.find(cur_sum - k) != sums.end()) { + max_len = max(max_len, i - sums[cur_sum - k]); + } + if (sums.find(cur_sum) == sums.end()) { + sums[cur_sum] = i; // Only keep the smallest index. + } + } + return max_len; + } +}; diff --git a/C++/maximum-sum-of-3-non-overlapping-subarrays.cpp b/C++/maximum-sum-of-3-non-overlapping-subarrays.cpp new file mode 100644 index 000000000..aedc67d4b --- /dev/null +++ b/C++/maximum-sum-of-3-non-overlapping-subarrays.cpp @@ -0,0 +1,46 @@ +// Time: O(n) +// Space: O(n) + +class Solution { +public: + vector maxSumOfThreeSubarrays(vector& nums, int k) { + const auto n = nums.size(); + vector accu = {0}; + for (const auto& num : nums) { + accu.emplace_back(accu.back() + num); + } + + vector left_pos(n); + for (int i = k, total = accu[k] - accu[0]; i < n; ++i) { + if (accu[i + 1] - accu[i + 1 - k] > total) { + left_pos[i] = i + 1 - k; + total = accu[i + 1] - accu[i + 1 - k]; + } else { + left_pos[i] = left_pos[i - 1]; + } + } + + vector right_pos(n, n - k); + for (int i = n - k - 1, total = accu[n] - accu[n - k]; i >= 0; --i) { + if (accu[i + k] - accu[i] > total) { + right_pos[i] = i; + total = accu[i + k] - accu[i]; + } else { + right_pos[i] = right_pos[i + 1]; + } + } + + vector result(3); + for (int i = k, max_sum = 0; i <= n - 2 * k; ++i) { + auto left = left_pos[i - 1], right = right_pos[i + k]; + auto total = (accu[i + k] - accu[i]) + + (accu[left + k] - accu[left]) + + (accu[right + k] - accu[right]); + if (total > max_sum) { + max_sum = total; + result = {left, i, right}; + } + } + return result; + } +}; diff --git a/C++/maximum-swap.cpp b/C++/maximum-swap.cpp new file mode 100644 index 000000000..5282aeb2e --- /dev/null +++ b/C++/maximum-swap.cpp @@ -0,0 +1,21 @@ +// Time: O(logn), logn is the length of the number string +// Space: O(logn) + +class Solution { +public: + int maximumSwap(int num) { + string digits = to_string(num); + int left = 0, right = 0; + int max_idx = digits.length() - 1; + for (int i = digits.length() - 1; i >= 0; --i) { + if (digits[i] > digits[max_idx]) { + max_idx = i; + } else if (digits[max_idx] > digits[i]) { + left = i; + right = max_idx; + } + } + swap(digits[left], digits[right]); + return stoi(digits); + } +}; diff --git a/C++/maximum-vacation-days.cpp b/C++/maximum-vacation-days.cpp new file mode 100644 index 000000000..ed61426a7 --- /dev/null +++ b/C++/maximum-vacation-days.cpp @@ -0,0 +1,24 @@ +// Time: O(n^2 * k) +// Space: O(k) + +class Solution { +public: + int maxVacationDays(vector>& flights, vector>& days) { + if (days.empty() || flights.empty()) { + return 0; + } + vector> dp(2, vector(days.size())); + for (int week = days[0].size() - 1; week >= 0; --week) { + for (int cur_city = 0; cur_city < days.size(); ++cur_city) { + dp[week % 2][cur_city] = days[cur_city][week] + dp[(week + 1) % 2][cur_city]; + for (int dest_city = 0; dest_city < days.size(); ++dest_city) { + if (flights[cur_city][dest_city] == 1) { + dp[week % 2][cur_city] = max(dp[week % 2][cur_city], + days[dest_city][week] + dp[(week + 1) % 2][dest_city]); + } + } + } + } + return dp[0][0]; + } +}; diff --git a/C++/maximum-width-of-binary-tree.cpp b/C++/maximum-width-of-binary-tree.cpp new file mode 100644 index 000000000..711af584a --- /dev/null +++ b/C++/maximum-width-of-binary-tree.cpp @@ -0,0 +1,33 @@ +// Time: O(n) +// Space: O(h) + +/** + * 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: + int widthOfBinaryTree(TreeNode* root) { + vector leftmosts; + return dfs(root, 1, 0, &leftmosts); + } + +private: + int dfs(TreeNode* node, int id, int depth, vector *leftmosts) { + if (!node) { + return 0; + } + if (depth >= leftmosts->size()) { + leftmosts->emplace_back(id); + } + int result = id - (*leftmosts)[depth] + 1; + result = max(result, dfs(node->left, id * 2, depth + 1, leftmosts)); + result = max(result, dfs(node->right, id * 2 + 1, depth + 1, leftmosts)); + return result; + } +}; diff --git a/C++/maximum-xor-of-two-numbers-in-an-array.cpp b/C++/maximum-xor-of-two-numbers-in-an-array.cpp new file mode 100644 index 000000000..8822939f9 --- /dev/null +++ b/C++/maximum-xor-of-two-numbers-in-an-array.cpp @@ -0,0 +1,25 @@ +// Time: O(n) +// Space: O(n) + +class Solution { +public: + int findMaximumXOR(vector& nums) { + int result = 0; + + for (int i = 31; i >= 0; --i) { + result <<= 1; + unordered_set prefixes; + for (const auto& n : nums) { + prefixes.emplace(n >> i); + } + for (const auto& p : prefixes) { + if (prefixes.count((result | 1) ^ p)) { + ++result; + break; + } + } + } + + return result; + } +}; diff --git a/C++/median-of-two-sorted-arrays.cpp b/C++/median-of-two-sorted-arrays.cpp new file mode 100644 index 000000000..d119db59e --- /dev/null +++ b/C++/median-of-two-sorted-arrays.cpp @@ -0,0 +1,93 @@ +// Time: O(log(min(m, n))) +// Space: O(1) + +class Solution { +public: + double findMedianSortedArrays(vector& nums1, vector& nums2) { + if ((nums1.size() + nums2.size()) % 2 == 1) { + return findKthInTwoSortedArrays(nums1, nums2, (nums1.size() + nums2.size()) / 2 + 1); + } else { + return (findKthInTwoSortedArrays(nums1, nums2, (nums1.size() + nums2.size()) / 2) + + findKthInTwoSortedArrays(nums1, nums2, (nums1.size() + nums2.size()) / 2 + 1)) / 2.0; + } + } + + int findKthInTwoSortedArrays(const vector& A, const vector& B, + int k) { + const int m = A.size(); + const int n = B.size(); + + // Make sure m is the smaller one. + if (m > n) { + return findKthInTwoSortedArrays(B, A, k); + } + + int left = 0; + int right = m; + // Find a partition of A and B + // where min left s.t. A[left] >= B[k - 1 - left]. Thus A[left] is the (k+1)-th or above element. + while (left < right) { + int mid = left + (right - left) / 2; + if (0 <= k - 1 - mid && k - 1 - mid < n && A[mid] >= B[k - 1 - mid]) { + right = mid; + } else { + left = mid + 1; + } + } + + int Ai_minus_1 = left - 1 >= 0 ? A[left - 1] : numeric_limits::min(); + int Bj = k - 1 - left >= 0 ? B[k - 1 - left] : numeric_limits::min(); + + // kth element would be A[left - 1] or B[k - 1 - left]. + return max(Ai_minus_1, Bj); + } +}; + +// Time: O(log(max(m, n)) * log(max_val - min_val)) +// Space: O(1) +// Generic solution. +class Solution_Generic { +public: + double findMedianSortedArrays(vector& nums1, vector& nums2) { + vector *> arrays{&nums1, &nums2}; + if ((nums1.size() + nums2.size()) % 2 == 1) { + return findKthInSortedArrays(arrays, (nums1.size() + nums2.size()) / 2 + 1); + } else { + return (findKthInSortedArrays(arrays, (nums1.size() + nums2.size()) / 2) + + findKthInSortedArrays(arrays, (nums1.size() + nums2.size()) / 2 + 1)) / 2.0; + } + } + +private: + int findKthInSortedArrays(const vector *>& arrays, int k) { + int left = numeric_limits::max(); + int right = numeric_limits::min(); + for (const auto array : arrays) { + if (!array->empty()) { + left = min(left, array->front()); + right = max(right, array->back()); + } + } + // left xxxxxxxooooooo right, find first xo or oo + while (left <= right) { + const auto mid = left + (right - left) / 2; + if (match(arrays, mid, k)) { + right = mid - 1; + } else { + left = mid + 1; + } + } + return left; + } + + bool match(const vector *>& arrays, int num, int target) { + int res = 0; + for (const auto array : arrays) { + if (!array->empty()) { + res += distance(upper_bound(array->cbegin(), array->cend(), num), + array->cend()); + } + } + return res < target; + } +}; diff --git a/C++/meeting-rooms-ii.cpp b/C++/meeting-rooms-ii.cpp new file mode 100644 index 000000000..e6489db1b --- /dev/null +++ b/C++/meeting-rooms-ii.cpp @@ -0,0 +1,40 @@ +// Time: O(nlogn) +// Space: O(n) + +/** + * Definition for an interval. + * struct Interval { + * int start; + * int end; + * Interval() : start(0), end(0) {} + * Interval(int s, int e) : start(s), end(e) {} + * }; + */ +class Solution { +public: + int minMeetingRooms(vector& intervals) { + vector starts, ends; + for (const auto& i : intervals) { + starts.emplace_back(i.start); + ends.emplace_back(i.end); + } + + sort(starts.begin(), starts.end()); + sort(ends.begin(), ends.end()); + + int min_rooms = 0, cnt_rooms = 0; + int s = 0, e = 0; + while (s < starts.size()) { + if (starts[s] < ends[e]) { + ++cnt_rooms; // Acquire a room. + // Update the min number of rooms. + min_rooms = max(min_rooms, cnt_rooms); + ++s; + } else { + --cnt_rooms; // Release a room. + ++e; + } + } + return min_rooms; + } +}; diff --git a/C++/meeting-rooms.cpp b/C++/meeting-rooms.cpp new file mode 100644 index 000000000..7568e7990 --- /dev/null +++ b/C++/meeting-rooms.cpp @@ -0,0 +1,25 @@ +// Time: O(nlogn) +// Space: O(n) + +/** + * Definition for an interval. + * struct Interval { + * int start; + * int end; + * Interval() : start(0), end(0) {} + * Interval(int s, int e) : start(s), end(e) {} + * }; + */ +class Solution { +public: + bool canAttendMeetings(vector& intervals) { + sort(intervals.begin(), intervals.end(), + [](const Interval& x, const Interval& y) { return x.start < y.start; }); + for (int i = 1; i < intervals.size(); ++i) { + if (intervals[i].start < intervals[i - 1].end) { + return false; + } + } + return true; + } +}; diff --git a/C++/merge-intervals.cpp b/C++/merge-intervals.cpp new file mode 100644 index 000000000..09b707d65 --- /dev/null +++ b/C++/merge-intervals.cpp @@ -0,0 +1,36 @@ +// Time: O(nlogn) +// Space: O(1) + +/** + * Definition for an interval. + * struct Interval { + * int start; + * int end; + * Interval() : start(0), end(0) {} + * Interval(int s, int e) : start(s), end(e) {} + * }; + */ +class Solution { +public: + vector merge(vector& intervals) { + if (intervals.empty()) { + return intervals; + } + + sort(intervals.begin(), intervals.end(), + [](const Interval& a, const Interval& b) { + return a.start < b.start; + }); + + vector result{intervals[0]}; + for (int i = 1; i < intervals.size(); ++i) { + if (intervals[i].start <= result.back().end) { + result.back().end = max(result.back().end, intervals[i].end); + } else { + result.emplace_back(intervals[i]); + } + } + + return result; + } +}; diff --git a/C++/merge-k-sorted-lists.cpp b/C++/merge-k-sorted-lists.cpp new file mode 100644 index 000000000..b508c84cf --- /dev/null +++ b/C++/merge-k-sorted-lists.cpp @@ -0,0 +1,134 @@ +// Time: O(n * logk) +// Space: O(1) + +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode(int x) : val(x), next(NULL) {} + * }; + */ + +// Merge two by two solution. +class Solution { +public: + ListNode *mergeKLists(vector &lists) { + if (lists.empty()) { + return nullptr; + } + + int left = 0, right = lists.size() - 1; + while (right > 0) { + if (left >= right) { + left = 0; + } else { + lists[left] = mergeTwoLists(lists[left], lists[right]); + ++left; + --right; + } + } + return lists[0]; + } + +private: + ListNode *mergeTwoLists(ListNode *l1, ListNode *l2) { + ListNode dummy{0}; + auto curr = &dummy; + + while (l1 && l2) { + if (l1->val <= l2->val) { + curr->next = l1; + l1 = l1->next; + } else { + curr->next = l2; + l2 = l2->next; + } + curr = curr->next; + } + curr->next = l1 ? l1 : l2; + + return dummy.next; + } +}; + + +// Time: O(n * logk) +// Space: O(logk) +// Divide and Conquer solution. +class Solution2 { +public: + ListNode *mergeKLists(vector &lists) { + return mergeKListsHelper(lists, 0, lists.size() - 1); + } + +private: + ListNode *mergeKListsHelper(const vector &lists, int begin, int end) { + if (begin > end) { + return nullptr; + } + if (begin == end) { + return lists[begin]; + } + return mergeTwoLists(mergeKListsHelper(lists, begin, (begin + end) / 2), + mergeKListsHelper(lists, (begin + end) / 2 + 1, end)); + } + + ListNode *mergeTwoLists(ListNode *l1, ListNode *l2) { + ListNode dummy{0}; + auto curr = &dummy; + + while (l1 && l2) { + if (l1->val <= l2->val) { + curr->next = l1; + l1 = l1->next; + } else { + curr->next = l2; + l2 = l2->next; + } + curr = curr->next; + } + curr->next = l1 ? l1 : l2; + + return dummy.next; + } +}; + + +// Time: O(n * logk) +// Space: O(k) +// Heap solution. +class Solution3 { +public: + ListNode* mergeKLists(vector& lists) { + ListNode dummy(0); + auto *cur = &dummy; + + struct Compare { + bool operator() (const ListNode *a, const ListNode *b) { + return a->val > b->val; + } + }; + + // Use min heap to keep the smallest node of each list + priority_queue, Compare> min_heap; + for (const auto& n : lists) { + if (n) { + min_heap.emplace(n); + } + } + + while (!min_heap.empty()) { + // Get min of k lists. + auto *node = min_heap.top(); + min_heap.pop(); + cur->next = node; + cur = cur->next; + if (node->next) { + min_heap.emplace(node->next); + } + } + + return dummy.next; + } +}; diff --git a/C++/merge-sorted-array.cpp b/C++/merge-sorted-array.cpp new file mode 100644 index 000000000..a5f688ddb --- /dev/null +++ b/C++/merge-sorted-array.cpp @@ -0,0 +1,25 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + void merge(vector& nums1, int m, vector& nums2, int n) { + int i = m + n; + while (m > 0 && n > 0) { + if (nums1[m - 1] > nums2[n - 1]) { + nums1[i - 1] = nums1[m - 1]; + --m; + } else { + nums1[i - 1] = nums2[n - 1]; + --n; + } + --i; + } + + while (n > 0) { + nums1[i - 1] = nums2[n - 1]; + --n; + --i; + } + } +}; diff --git a/C++/merge-two-binary-trees.cpp b/C++/merge-two-binary-trees.cpp new file mode 100644 index 000000000..dde23b16f --- /dev/null +++ b/C++/merge-two-binary-trees.cpp @@ -0,0 +1,27 @@ +// Time: O(n) +// Space: O(h) + +/** + * 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* mergeTrees(TreeNode* t1, TreeNode* t2) { + if (!t1) { + return t2; + } + if (!t2) { + return t1; + } + t1->val += t2->val; + t1->left = mergeTrees(t1->left, t2->left); + t1->right = mergeTrees(t1->right, t2->right); + return t1; + } +}; diff --git a/C++/merge-two-sorted-lists.cpp b/C++/merge-two-sorted-lists.cpp new file mode 100644 index 000000000..a11d16b63 --- /dev/null +++ b/C++/merge-two-sorted-lists.cpp @@ -0,0 +1,32 @@ +// Time: O(n) +// Space: O(1) + +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode(int x) : val(x), next(NULL) {} + * }; + */ +class Solution { +public: + ListNode *mergeTwoLists(ListNode *l1, ListNode *l2) { + ListNode dummy{0}; + auto curr = &dummy; + + while (l1 && l2) { + if (l1->val <= l2->val) { + curr->next = l1; + l1 = l1->next; + } else { + curr->next = l2; + l2 = l2->next; + } + curr = curr->next; + } + curr->next = l1 ? l1 : l2; + + return dummy.next; + } +}; diff --git a/C++/merge.cpp b/C++/merge.cpp deleted file mode 100644 index b161ee7ac..000000000 --- a/C++/merge.cpp +++ /dev/null @@ -1,45 +0,0 @@ -// Time Complexity: O(n^2) -// Space Complexity: O(1) - -/** - * Definition for an interval. - * struct Interval { - * int start; - * int end; - * Interval() : start(0), end(0) {} - * Interval(int s, int e) : start(s), end(e) {} - * }; - */ -class Solution { - public: - vector merge(vector &intervals) { - vector ans; - for(auto i : intervals) { - ans = insert(ans, i); - } - return ans; - } - private: - vector insert(vector &intervals, Interval newInterval) { - vector ans; - auto n = intervals.size(); - for(int i = 0; i < n; ++i) { - if (newInterval.end < intervals[i].start) { // not overlapped - ans.push_back(newInterval); - for(; i < n; ++i) - ans.push_back(intervals[i]); - return ans; - } - else if (newInterval.start > intervals[i].end) { // not overlapped - ans.push_back(intervals[i]); - } - else { // merge - newInterval.start = min(newInterval.start, intervals[i].start); - newInterval.end = max(newInterval.end, intervals[i].end); - } - } - - ans.push_back(newInterval); - return ans; - } -}; diff --git a/C++/mergeKLists.cpp b/C++/mergeKLists.cpp deleted file mode 100644 index f75d4d8ce..000000000 --- a/C++/mergeKLists.cpp +++ /dev/null @@ -1,48 +0,0 @@ -// Time Complexity: O(n * logk) -// Space Complexity: O(k) - -/** - * Definition for singly-linked list. - * struct ListNode { - * int val; - * ListNode *next; - * ListNode(int x) : val(x), next(NULL) {} - * }; - */ -class Solution { - public: - ListNode *mergeKLists(vector &lists) { - return mergeKLists(lists, 0, lists.size() - 1); - } - private: - ListNode *mergeKLists(vector &lists, int begin, int end) { - if(begin > end) - return NULL; - if(begin == end) - return lists[begin]; - return mergeTwoLists(mergeKLists(lists, begin, (begin + end) / 2), mergeKLists(lists, (begin + end) / 2 + 1, end)); - } - - ListNode *mergeTwoLists(ListNode *left, ListNode *right) { - ListNode dummy(INT_MIN); - ListNode *p = &dummy; - for(; left && right; p = p->next) { - if(left->val < right->val) { - p->next = left; - left = left->next; - } - else { - p->next = right; - right = right->next; - } - } - if(left) { - p->next = left; - } - else { - p->next = right; - } - - return dummy.next; - } -}; diff --git a/C++/middle-of-the-linked-list.cpp b/C++/middle-of-the-linked-list.cpp new file mode 100644 index 000000000..5fe3d3021 --- /dev/null +++ b/C++/middle-of-the-linked-list.cpp @@ -0,0 +1,22 @@ +// Time: O(n) +// Space: O(1) + +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode(int x) : val(x), next(NULL) {} + * }; + */ +class Solution { +public: + ListNode* middleNode(ListNode* head) { + auto slow = head, fast = head; + while (fast && fast->next) { + slow = slow->next; + fast = fast->next->next; + } + return slow; + } +}; diff --git a/C++/min-cost-climbing-stairs.cpp b/C++/min-cost-climbing-stairs.cpp new file mode 100644 index 000000000..531aba227 --- /dev/null +++ b/C++/min-cost-climbing-stairs.cpp @@ -0,0 +1,15 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + int minCostClimbingStairs(vector& cost) { + vector dp(3); + for (int i = cost.size() - 1; i >= 0; --i) { + dp[i % 3] = cost[i] + + min(dp[(i + 1) % 3], + dp[(i + 2) % 3]); + } + return min(dp[0], dp[1]); + } +}; diff --git a/C++/min-stack.cpp b/C++/min-stack.cpp new file mode 100644 index 000000000..3f59d21a3 --- /dev/null +++ b/C++/min-stack.cpp @@ -0,0 +1,77 @@ +// Time: O(n) +// Space: O(1) + +class MinStack { +public: + void push(int number) { + if (elements_.empty()) { + elements_.emplace(0); + stack_min_ = number; + } else { + elements_.emplace(static_cast(number) - stack_min_); + if (number < stack_min_) { + stack_min_ = number; // Update min. + } + } + } + + void pop() { + auto diff = elements_.top(); + elements_.pop(); + if (diff < 0) { + stack_min_ -= diff; // Restore previous min. + } + } + + int top() { + if (elements_.top() > 0) { + return stack_min_ + elements_.top(); + } else { + return stack_min_; + } + } + + int getMin() { + return stack_min_; + } + +private: + stack elements_; + int stack_min_; +}; + + +// Time: O(n) +// Space: O(n) +class MinStack2 { +public: + void push(int number) { + if (cached_min_with_count_.empty() || cached_min_with_count_.top().first > number) { + cached_min_with_count_.emplace(number, 1); + } else if (cached_min_with_count_.top().first == number) { + ++cached_min_with_count_.top().second; + } + elements_.emplace(number); + } + + void pop() { + if (cached_min_with_count_.top().first == elements_.top()) { + if (--cached_min_with_count_.top().second == 0) { + cached_min_with_count_.pop(); + } + } + elements_.pop(); + } + + int top() { + return elements_.top(); + } + + int getMin() { + return cached_min_with_count_.top().first; + } + +private: + stack elements_; + stack> cached_min_with_count_; +}; diff --git a/C++/minWindow.cpp b/C++/minWindow.cpp deleted file mode 100644 index 0f24c2a75..000000000 --- a/C++/minWindow.cpp +++ /dev/null @@ -1,41 +0,0 @@ -// Time Complexity: O(n), where n is string length -// Space Complexity: O(m), where m is alphabet size - -class Solution { - public: - string minWindow(string S, string T) { - if(S.empty() || S.length() < T.length()) return ""; - const int ASCII_MAX = 256; - - vector expCnt(ASCII_MAX, 0); - vector curCnt(ASCII_MAX, 0); - int cnt = 0; - int start = 0; - int min_width = INT_MAX; - int min_start = 0; - - for(auto const &c : T) ++expCnt[c]; - - for(int i = 0; i < S.length(); ++i) { - if(expCnt[S[i]] > 0) { - ++curCnt[S[i]]; - if(curCnt[S[i]] <= expCnt[S[i]]) // counting expected elements - ++cnt; - } - if(cnt == T.size()) { // if window meets the requirement - while(expCnt[S[start]] == 0 || curCnt[S[start]] > expCnt[S[start]]) { // adjust left bound of window - --curCnt[S[start]]; - ++start; - } - - if(min_width > i - start + 1) { // update minimum window - min_width = i - start + 1; - min_start = start; - } - } - } - - if(min_width == INT_MAX) return ""; - return S.substr(min_start, min_width); - } -}; diff --git a/C++/minesweeper.cpp b/C++/minesweeper.cpp new file mode 100644 index 000000000..88fbb15f4 --- /dev/null +++ b/C++/minesweeper.cpp @@ -0,0 +1,108 @@ +// Time: O(m * n) +// Space: O(m + n) + +class Solution { +public: + vector> updateBoard(vector>& board, vector& click) { + queue> q; + q.emplace(click); + while (!q.empty()) { + int row = q.front()[0], col = q.front()[1]; + q.pop(); + if (board[row][col] == 'M') { + board[row][col] = 'X'; + } else { + int count = 0; + for (int i = -1; i < 2; ++i) { + for (int j = -1; j < 2; ++j) { + if (i == 0 && j == 0) { + continue; + } + int r = row + i, c = col + j; + if (r < 0 || r >= board.size() || c < 0 || c < 0 || c >= board[r].size()) { + continue; + } + if (board[r][c] == 'M' || board[r][c] == 'X') { + ++count; + } + } + } + + if (count > 0) { + board[row][col] = count + '0'; + } else { + board[row][col] = 'B'; + for (int i = -1; i < 2; ++i) { + for (int j = -1; j < 2; ++j) { + if (i == 0 && j == 0) { + continue; + } + int r = row + i, c = col + j; + if (r < 0 || r >= board.size() || c < 0 || c < 0 || c >= board[r].size()) { + continue; + } + if (board[r][c] == 'E') { + vector next_click = {r, c}; + q.emplace(next_click); + board[r][c] = 'B'; + } + } + } + } + } + } + + return board; + } +}; + +// Time: O(m * n) +// Space: O(m * n) +class Solution2 { +public: + vector> updateBoard(vector>& board, vector& click) { + int row = click[0], col = click[1]; + if (board[row][col] == 'M') { + board[row][col] = 'X'; + } else { + int count = 0; + for (int i = -1; i < 2; ++i) { + for (int j = -1; j < 2; ++j) { + if (i == 0 && j == 0) { + continue; + } + int r = row + i, c = col + j; + if (r < 0 || r >= board.size() || c < 0 || c < 0 || c >= board[r].size()) { + continue; + } + if (board[r][c] == 'M' || board[r][c] == 'X') { + ++count; + } + } + } + + if (count > 0) { + board[row][col] = count + '0'; + } else { + board[row][col] = 'B'; + for (int i = -1; i < 2; ++i) { + for (int j = -1; j < 2; ++j) { + if (i == 0 && j == 0) { + continue; + } + int r = row + i, c = col + j; + if (r < 0 || r >= board.size() || c < 0 || c < 0 || c >= board[r].size()) { + continue; + } + if (board[r][c] == 'E') { + vector next_click = {r, c}; + updateBoard(board, next_click); + } + } + } + } + } + + return board; + } +}; diff --git a/C++/mini-parser.cpp b/C++/mini-parser.cpp new file mode 100644 index 000000000..7c7cd6229 --- /dev/null +++ b/C++/mini-parser.cpp @@ -0,0 +1,136 @@ +// Time: O(n) +// Space: O(h) + +/** + * // This is the interface that allows for creating nested lists. + * // You should not implement it, or speculate about its implementation + * class NestedInteger { + * public: + * // Constructor initializes an empty nested list. + * NestedInteger(); + * + * // Constructor initializes a single integer. + * NestedInteger(int value); + * + * // Return true if this NestedInteger holds a single integer, rather than a nested list. + * bool isInteger() const; + * + * // Return the single integer that this NestedInteger holds, if it holds a single integer + * // The result is undefined if this NestedInteger holds a nested list + * int getInteger() const; + * + * // Set this NestedInteger to hold a single integer. + * void setInteger(int value); + * + * // Set this NestedInteger to hold a nested list and adds a nested integer to it. + * void add(const NestedInteger &ni); + * + * // Return the nested list that this NestedInteger holds, if it holds a nested list + * // The result is undefined if this NestedInteger holds a single integer + * const vector &getList() const; + * }; + */ + +// Iterative solution. +class Solution { +public: + NestedInteger deserialize(string s) { + if (s.empty()) { + return NestedInteger(); + } + + if (s[0] != '[') { + return NestedInteger(stoi(s)); + } + + stack stk; + for (int i = 0, j = 0; j < s.length(); ++j) { + if (s[j] == '[') { + stk.emplace(NestedInteger()); + i = j + 1; + } else if (s[j] == ',' || s[j] == ']') { + if (isdigit(s[j - 1])) { + stk.top().add(NestedInteger(stoi(s.substr(i, j - i)))); + } + if (s[j] == ']' && stk.size() > 1) { + NestedInteger cur = stk.top(); + stk.pop(); + stk.top().add(cur); + } + i = j + 1; + } + } + return stk.top(); + } +}; + +// Time: O(n) +// Space: O(h) +// Recursive solution. +class Solution2 { +public: + NestedInteger deserialize(string s) { + if (s.empty()) { + return NestedInteger(); + } + int i = 0; + return deserializeHelper(s, &i); + } + +private: + NestedInteger deserializeHelper(const string& s, int *i) { + NestedInteger result; + if (s[*i] != '[') { + int j = *i; + while (j < s.length() && (s[j] == '-' || isdigit(s[j]))) { + ++j; + } + result.setInteger(stoi(s.substr(*i, j - *i + 1))); + *i = j; + } else { + ++(*i); + while (*i < s.length() && s[*i] != ']') { + result.add(deserializeHelper(s, i)); + if (*i < s.length() && s[*i] == ',') { + ++(*i); + } + } + ++(*i); + } + return result; + } +}; + +// Time: O(n) +// Space: O(n) +// Recursive solution. +class Solution3 { +public: + NestedInteger deserialize(string s) { + if (s.empty()) { + return NestedInteger(); + } + istringstream in(s); // copy string: extra O(n) space + return deserializeHelper(in); + } + +private: + NestedInteger deserializeHelper(istringstream &in) { + NestedInteger result; + int num = 0; + if (in >> num) { + result.setInteger(num); + } else { + in.clear(); + in.get(); + while (in.peek() != ']') { + result.add(deserializeHelper(in)); + if (in.peek() == ',') { + in.get(); + } + } + in.get(); + } + return result; + } +}; diff --git a/C++/minimize-max-distance-to-gas-station.cpp b/C++/minimize-max-distance-to-gas-station.cpp new file mode 100644 index 000000000..50998593a --- /dev/null +++ b/C++/minimize-max-distance-to-gas-station.cpp @@ -0,0 +1,28 @@ +// Time: O(nlogr) +// Space: O(1) + +class Solution { +public: + double minmaxGasDist(vector& stations, int K) { + double left = 0.0; + double right = 1e8; + while (right - left > 1e-6) { + const auto mid = left + (right - left) / 2.0; + if (possible(stations, K, mid)) { + right = mid; + } else { + left = mid; + } + } + return left; + } + +private: + bool possible(const vector& stations, int K, double guess) { + int sum = 0; + for (int i = 0; i + 1 < stations.size(); ++i) { + sum += int((stations[i + 1] - stations[i]) / guess); + } + return sum <= K; + } +}; diff --git a/C++/minimum-absolute-difference-in-bst.cpp b/C++/minimum-absolute-difference-in-bst.cpp new file mode 100644 index 000000000..038991bd2 --- /dev/null +++ b/C++/minimum-absolute-difference-in-bst.cpp @@ -0,0 +1,39 @@ +// Time: O(n) +// Space: O(h) + +/** + * 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: + int getMinimumDifference(TreeNode* root) { + int result = numeric_limits::max(); + TreeNode *prev = nullptr; + + inorderTraversal(root, &prev, &result); + + return result; + } + +private: + void inorderTraversal(TreeNode *root, TreeNode **prev, int *result) { + if (!root) { + return; + } + + inorderTraversal(root->left, prev, result); + + if (*prev) { + *result = min(*result, root->val - (*prev)->val); + } + *prev = root; + + inorderTraversal(root->right, prev, result); + } +}; diff --git a/C++/minimum-ascii-delete-sum-for-two-strings.cpp b/C++/minimum-ascii-delete-sum-for-two-strings.cpp new file mode 100644 index 000000000..6c68db3d8 --- /dev/null +++ b/C++/minimum-ascii-delete-sum-for-two-strings.cpp @@ -0,0 +1,55 @@ +// Time: O(m * n) +// Space: O(n) + +// DP with rolling window. +class Solution { +public: + int minimumDeleteSum(string s1, string s2) { + vector> dp(2, vector(s2.length() + 1)); + for (int j = 0; j < s2.length(); ++j) { + dp[0][j + 1] = dp[0][j] + s2[j]; + } + + for (int i = 0; i < s1.length(); ++i) { + dp[(i + 1) % 2][0] = dp[i % 2][0] + s1[i]; + for (int j = 0; j < s2.length(); ++j) { + if (s1[i] == s2[j]) { + dp[(i + 1) % 2][j + 1] = dp[i % 2][j]; + } else { + dp[(i + 1) % 2][j + 1] = min(dp[i % 2][j + 1] + s1[i], + dp[(i + 1) % 2][j] + s2[j]); + } + } + } + + return dp[s1.length() % 2][s2.length()]; + } +}; + +// Time: O(m * n) +// Space: O(m * n) +class Solution2 { +public: + int minimumDeleteSum(string s1, string s2) { + vector> dp(s1.length() + 1, vector(s2.length() + 1)); + for (int i = 0; i < s1.length(); ++i) { + dp[i + 1][0] = dp[i][0] + s1[i]; + } + for (int j = 0; j < s2.length(); ++j) { + dp[0][j + 1] = dp[0][j] + s2[j]; + } + + for (int i = 0; i < s1.length(); ++i) { + for (int j = 0; j < s2.length(); ++j) { + if (s1[i] == s2[j]) { + dp[i + 1][j + 1] = dp[i][j]; + } else { + dp[i + 1][j + 1] = min(dp[i][j + 1] + s1[i], + dp[i + 1][j] + s2[j]); + } + } + } + + return dp[s1.length()][s2.length()]; + } +}; diff --git a/C++/minimum-cost-to-hire-k-workers.cpp b/C++/minimum-cost-to-hire-k-workers.cpp new file mode 100644 index 000000000..82ab1eca4 --- /dev/null +++ b/C++/minimum-cost-to-hire-k-workers.cpp @@ -0,0 +1,28 @@ +// Time: O(nlogn) +// Space: O(n) + +class Solution { +public: + double mincostToHireWorkers(vector& quality, vector& wage, int K) { + vector> workers; + for (int i = 0; i < quality.size(); ++i) { + workers.emplace_back(static_cast(wage[i]) / quality[i], + quality[i]); + } + sort(workers.begin(), workers.end()); + auto result = numeric_limits::max(); + auto sum = 0.0; + priority_queue max_heap; + for (const auto& worker: workers) { + sum += worker.second; + max_heap.emplace(worker.second); + if (max_heap.size() > K) { + sum -= max_heap.top(), max_heap.pop(); + } + if (max_heap.size() == K) { + result = min(result, sum * worker.first); + } + } + return result; + } +}; diff --git a/C++/minimum-distance-between-bst-nodes.cpp b/C++/minimum-distance-between-bst-nodes.cpp new file mode 100644 index 000000000..c84359bd7 --- /dev/null +++ b/C++/minimum-distance-between-bst-nodes.cpp @@ -0,0 +1,34 @@ +// Time: O(n) +// Space: O(h) + +/** + * 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: + int minDiffInBST(TreeNode* root) { + int prev = numeric_limits::min(); + int result = numeric_limits::max(); + dfs(root, &prev, &result); + return result; + } + +private: + void dfs(TreeNode* node, int *prev, int *result) { + if (!node) { + return; + } + dfs(node->left, prev, result); + if (*prev != numeric_limits::min()) { + *result = min(*result, node->val - *prev); + } + *prev = node->val; + dfs(node->right, prev, result); + } +}; diff --git a/C++/minimum-factorization.cpp b/C++/minimum-factorization.cpp new file mode 100644 index 000000000..633c9b048 --- /dev/null +++ b/C++/minimum-factorization.cpp @@ -0,0 +1,21 @@ +// Time: O(loga) +// Space: O(1) + +class Solution { +public: + int smallestFactorization(int a) { + if (a < 2) { + return a; + } + long result = 0, mul = 1; + for (int i = 9; i >= 2; --i) { + while (a % i == 0) { + a /= i; + result = mul * i + result; + mul *= 10; + } + } + return a == 1 && result <= numeric_limits::max() ? static_cast(result) : 0; + } +}; + diff --git a/C++/minimum-genetic-mutation.cpp b/C++/minimum-genetic-mutation.cpp new file mode 100644 index 000000000..9aa1defc6 --- /dev/null +++ b/C++/minimum-genetic-mutation.cpp @@ -0,0 +1,40 @@ +// Time: O(n * b), n is the length of gene string, b is size of bank +// Space: O(b) + +class Solution { +public: + int minMutation(string start, string end, vector& bank) { + unordered_map lookup; + for (const auto& b : bank) { + lookup.emplace(b, false); + } + + queue> q; + q.emplace(start, 0); + while (!q.empty()) { + string cur; + int level; + tie(cur, level) = q.front(); q.pop(); + + if (cur == end) { + return level; + } + + for (int i = 0; i < cur.size(); ++i) { + auto cur_copy = cur; + for (const auto& c : {'A', 'T', 'C', 'G'}) { + if (cur_copy[i] == c) { + continue; + } + cur_copy[i] = c; + if (lookup.count(cur_copy) && lookup[cur_copy] == false) { + q.emplace(cur_copy, level + 1); + lookup[cur_copy] = true; + } + } + } + } + + return -1; + } +}; diff --git a/C++/minimum-height-trees.cpp b/C++/minimum-height-trees.cpp new file mode 100644 index 000000000..feba813fc --- /dev/null +++ b/C++/minimum-height-trees.cpp @@ -0,0 +1,50 @@ +// Time: O(n) +// Space: O(n) + +class Solution { +public: + vector findMinHeightTrees(int n, vector>& edges) { + if (n == 1) { + return {0}; + } + + unordered_map> neighbors; + for (const auto& e : edges) { + int u, v; + tie(u, v) = e; + neighbors[u].emplace(v); + neighbors[v].emplace(u); + } + + vector pre_level, cur_level; + unordered_set unvisited; + for (int i = 0; i < n; ++i) { + if (neighbors[i].size() == 1) { // A leaf. + pre_level.emplace_back(i); + } + unvisited.emplace(i); + } + + // A graph can have 2 MHTs at most. + // BFS from the leaves until the number + // of the unvisited nodes is less than 3. + while (unvisited.size() > 2) { + cur_level.clear(); + for (const auto& u : pre_level) { + unvisited.erase(u); + for (const auto& v : neighbors[u]) { + if (unvisited.count(v)) { + neighbors[v].erase(u); + if (neighbors[v].size() == 1) { + cur_level.emplace_back(v); + } + } + } + } + swap(pre_level, cur_level); + } + + vector res(unvisited.begin(), unvisited.end()); + return res; + } +}; diff --git a/C++/minimum-index-sum-of-two-lists.cpp b/C++/minimum-index-sum-of-two-lists.cpp new file mode 100644 index 000000000..4657e28fb --- /dev/null +++ b/C++/minimum-index-sum-of-two-lists.cpp @@ -0,0 +1,26 @@ +// Time: O((m + n) * l), m is the size of list1, n is the size of list2 +// Space: O(m * l), l is the average string length + +class Solution { +public: + vector findRestaurant(vector& list1, vector& list2) { + unordered_map lookup; + for (int i = 0; i < list1.size(); ++i) { + lookup[list1[i]] = i; + } + vector result; + int min_sum = numeric_limits::max(); + for (int j = 0; j < list2.size() && j <= min_sum; ++j) { + if (lookup.count(list2[j])) { + auto sum = j + lookup[list2[j]]; + if (sum < min_sum) { + result.clear(); + result.emplace_back(list2[j]); + min_sum = sum; + } else if (sum == min_sum) + result.emplace_back(list2[j]); + } + } + return result; + } +}; diff --git a/C++/minimum-moves-to-equal-array-elements-ii.cpp b/C++/minimum-moves-to-equal-array-elements-ii.cpp new file mode 100644 index 000000000..1597aa985 --- /dev/null +++ b/C++/minimum-moves-to-equal-array-elements-ii.cpp @@ -0,0 +1,17 @@ +// Time: O(n) on average +// Space: O(1) + +// Quick select solution. +class Solution { +public: + int minMoves2(vector& nums) { + auto it = nums.begin() + nums.size() / 2; + nth_element(nums.begin(), it, nums.end()); + const auto median = *it; + int result = 0; + for (const auto &i : nums) { + result += abs(i - median); + } + return result; + } +}; diff --git a/C++/minimum-moves-to-equal-array-elements.cpp b/C++/minimum-moves-to-equal-array-elements.cpp new file mode 100644 index 000000000..330d91dbd --- /dev/null +++ b/C++/minimum-moves-to-equal-array-elements.cpp @@ -0,0 +1,10 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + int minMoves(vector& nums) { + return accumulate(nums.cbegin(), nums.cend(), 0) - + nums.size() * *min_element(nums.cbegin(), nums.cend()); + } +}; diff --git a/C++/minimum-number-of-arrows-to-burst-balloons.cpp b/C++/minimum-number-of-arrows-to-burst-balloons.cpp new file mode 100644 index 000000000..2bc27dbc0 --- /dev/null +++ b/C++/minimum-number-of-arrows-to-burst-balloons.cpp @@ -0,0 +1,26 @@ +// Time: O(nlogn) +// Space: O(1) + +class Solution { +public: + int findMinArrowShots(vector>& points) { + if (points.empty()) { + return 0; + } + + sort(points.begin(), points.end()); + + int result = 0; + for (int i = 0; i < points.size(); ++i) { + int j = i + 1; + int right_bound = points[i].second; + while (j < points.size() && points[j].first <= right_bound) { + right_bound = min(right_bound, points[j].second); + ++j; + } + ++result; + i = j - 1; + } + return result; + } +}; diff --git a/C++/minimum-number-of-refueling-stops.cpp b/C++/minimum-number-of-refueling-stops.cpp new file mode 100644 index 000000000..94e4b2e5e --- /dev/null +++ b/C++/minimum-number-of-refueling-stops.cpp @@ -0,0 +1,25 @@ +// Time: O(nlogn) +// Space: O(n) + +class Solution { +public: + int minRefuelStops(int target, int startFuel, vector>& stations) { + priority_queue max_heap; + stations.push_back(vector{target, numeric_limits::min()}); + + int result = 0, prev = 0; + for (const auto& station : stations) { + startFuel -= station[0] - prev; + while (!max_heap.empty() && startFuel < 0) { + startFuel += max_heap.top(); max_heap.pop(); + ++result; + } + if (startFuel < 0) { + return -1; + } + max_heap.emplace(station[1]); + prev = station[0]; + } + return result; + } +}; diff --git a/C++/minimum-size-subarray-sum.cpp b/C++/minimum-size-subarray-sum.cpp new file mode 100644 index 000000000..cb3a91994 --- /dev/null +++ b/C++/minimum-size-subarray-sum.cpp @@ -0,0 +1,46 @@ +// Time: O(n) +// Space: O(1) + +// Sliding window solution. +class Solution { +public: + int minSubArrayLen(int s, vector& nums) { + int start = -1, sum = 0, min_size = numeric_limits::max(); + for (int i = 0; i < nums.size(); ++i) { + sum += nums[i]; + while (sum >= s) { + min_size = min(min_size, i - start); + sum -= nums[++start]; + } + } + if (min_size == numeric_limits::max()) { + return 0; + } + return min_size; + } +}; + +// Time: O(nlogn) +// Space: O(n) +// Binary search solution. +class Solution2 { +public: + int minSubArrayLen(int s, vector& nums) { + int min_size = numeric_limits::max(); + vector sum_from_start(nums.size() + 1); + partial_sum(nums.cbegin(), nums.cend(), sum_from_start.begin() + 1); + for (int i = 0; i < nums.size(); ++i) { + const auto& end_it = lower_bound(sum_from_start.cbegin() + i, + sum_from_start.cend(), + sum_from_start[i] + s); + if (end_it != sum_from_start.cend()) { + int end = static_cast(end_it - sum_from_start.cbegin()); + min_size = min(min_size, end - i); + } + } + if (min_size == numeric_limits::max()) { + return 0; + } + return min_size; + } +}; diff --git a/C++/minimum-swaps-to-make-sequences-increasing.cpp b/C++/minimum-swaps-to-make-sequences-increasing.cpp new file mode 100644 index 000000000..73f7bc7d3 --- /dev/null +++ b/C++/minimum-swaps-to-make-sequences-increasing.cpp @@ -0,0 +1,21 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + int minSwap(vector& A, vector& B) { + vector dp_no_swap(2), dp_swap(2, 1); + for (int i = 1; i < A.size(); ++i) { + dp_no_swap[i % 2] = dp_swap[i % 2] = numeric_limits::max(); + if (A[i - 1] < A[i] && B[i -1] < B[i]) { + dp_no_swap[i % 2] = min(dp_no_swap[i % 2], dp_no_swap[(i - 1) % 2]); + dp_swap[i % 2] = min(dp_swap[i % 2], dp_swap[(i - 1) % 2] + 1); + } + if (A[i - 1] < B[i] && B[i - 1] < A[i]) { + dp_no_swap[i % 2] = min(dp_no_swap[i % 2], dp_swap[(i - 1) % 2]); + dp_swap[i % 2] = min(dp_swap[i % 2], dp_no_swap[(i - 1) % 2] + 1); + } + } + return min(dp_no_swap[(A.size() - 1) % 2], dp_swap[(A.size() - 1) % 2]); + } +}; diff --git a/C++/minimum-time-difference.cpp b/C++/minimum-time-difference.cpp new file mode 100644 index 000000000..145cf6dce --- /dev/null +++ b/C++/minimum-time-difference.cpp @@ -0,0 +1,19 @@ +// Time: O(nlogn) +// Space: O(n) + +class Solution { +public: + int findMinDifference(vector& timePoints) { + static const int N = 60 * 24; + vector minutes; + for (const auto& t : timePoints) { + minutes.emplace_back(stoi(t.substr(0, 2)) * 60 + stoi(t.substr(3))); + } + sort(minutes.begin(), minutes.end()); + int result = numeric_limits::max(); + for (int i = 0; i < timePoints.size(); ++i) { + result = min(result, (N + minutes[(i + 1) % timePoints.size()] - minutes[i]) % N); + } + return result; + } +}; diff --git a/C++/minimum-unique-word-abbreviation.cpp b/C++/minimum-unique-word-abbreviation.cpp new file mode 100644 index 000000000..3d15a89b3 --- /dev/null +++ b/C++/minimum-unique-word-abbreviation.cpp @@ -0,0 +1,75 @@ +// Time: O(2^n) +// Space: O(n) + +class Solution { +public: + string minAbbreviation(string target, vector& dictionary) { + vector diffs; + dictionary_to_diffs(target, dictionary, &diffs); + + if (diffs.empty()) { + return to_string(target.length()); + } + + int bits = (1 << target.length()) - 1; + for (int i = 0; i < (1 << target.length()); ++i) { + if (all_of(diffs.begin(), diffs.end(), [&i](int d) { return d & i; } )) { + if (bits_len(target, i) > bits_len(target, bits)) { + bits = i; + } + } + } + + return bits_to_abbr(target, bits); + } + +private: + void dictionary_to_diffs(const string& target, const vector& dictionary, + vector *diffs) { + + for (const auto& word : dictionary) { + if (word.length() != target.length()) { + continue; + } + + int bits = 0; + for (int i = 0; i < word.length(); ++i) { + if (target[i] != word[i]) { + bits |= 1 << i; + } + } + diffs->emplace_back(bits); + } + } + + int bits_len(const string& target, int bits) { + int sum = 0; + + for (int i = 0; i < target.length() - 1; ++i) { + if (((bits >> i) & 3) == 0) { + ++sum; + } + } + + return sum; + } + + string bits_to_abbr(const string& target, int bits) { + string abbr; + + int pre = 0; + for (int i = 0, prev = 0; i < target.length(); ++i, bits >>= 1) { + if (bits & 1) { + if (i - pre > 0) { + abbr += to_string(i - pre); + } + pre = i + 1; + abbr.push_back(target[i]); + } else if (i == target.length() - 1) { + abbr += to_string(i - pre + 1); + } + } + + return abbr; + } +}; diff --git a/C++/minimum-window-subsequence.cpp b/C++/minimum-window-subsequence.cpp new file mode 100644 index 000000000..984aae5f4 --- /dev/null +++ b/C++/minimum-window-subsequence.cpp @@ -0,0 +1,36 @@ +// Time: O(s * t) +// Space: O(s) + +class Solution { +public: + string minWindow(string S, string T) { + vector> dp(2, vector(S.length(), -1)); + for (int j = 0; j < S.length(); ++j) { + if (S[j] == T[0]) { + dp[0][j] = j; + } + } + + for (int i = 1; i < T.length(); ++i) { + int prev = -1; + dp[i % 2] = vector(S.length(), -1); + for (int j = 0; j < S.length(); ++j) { + if (prev != -1 && S[j] == T[i]) { + dp[i % 2][j] = prev; + } + if (dp[(i - 1) % 2][j] != -1) { + prev = dp[(i - 1) % 2][j]; + } + } + } + + int start = 0, end = S.length(); + for (int j = 0; j < S.length(); ++j) { + int i = dp[(T.length() - 1) % 2][j]; + if (i >= 0 && j - i < end - start) { + tie(start, end) = make_pair(i, j); + } + } + return end < S.length() ? S.substr(start, end - start + 1) : ""; + } +}; diff --git a/C++/minimum-window-substring.cpp b/C++/minimum-window-substring.cpp new file mode 100644 index 000000000..cf593f4b4 --- /dev/null +++ b/C++/minimum-window-substring.cpp @@ -0,0 +1,51 @@ +// Time: O(n) +// Space: O(k) + +class Solution { +public: + string minWindow(string s, string t) { + if (s.empty() || s.length() < t.length()) { + return ""; + } + + const int ASCII_MAX = 256; + vector exp_cnt(ASCII_MAX, 0); + vector cur_cnt(ASCII_MAX, 0); + + int cnt = 0; + int start = 0; + int min_start = 0; + int min_width = numeric_limits::max(); + + for (const auto& c : t) { + ++exp_cnt[c]; + } + + for (int i = 0; i < s.length(); ++i) { + if (exp_cnt[s[i]] > 0) { + ++cur_cnt[s[i]]; + if (cur_cnt[s[i]] <= exp_cnt[s[i]]) { // Counting expected elements. + ++cnt; + } + } + if (cnt == t.size()) { // If window meets the requirement. + while (exp_cnt[s[start]] == 0 || // Adjust left bound of window. + cur_cnt[s[start]] > exp_cnt[s[start]]) { + --cur_cnt[s[start]]; + ++start; + } + + if (min_width > i - start + 1) { // Update minimum window. + min_width = i - start + 1; + min_start = start; + } + } + } + + if (min_width == numeric_limits::max()) { + return ""; + } + + return s.substr(min_start, min_width); + } +}; diff --git a/C++/mirror-reflection.cpp b/C++/mirror-reflection.cpp new file mode 100644 index 000000000..ad3165ae7 --- /dev/null +++ b/C++/mirror-reflection.cpp @@ -0,0 +1,38 @@ +// Time: O(1) +// Space: O(1) + +class Solution { +public: + int mirrorReflection(int p, int q) { + // explanation commented in the following solution + return (p & -p) > (q & -q) ? 2 : (p & -p) < (q & -q) ? 0 : 1; + } +}; + + +// Time: O(log(max(p, q))) = O(1) due to 32-bit integer +// Space: O(1) +class Solution2 { +public: + int mirrorReflection(int p, int q) { + const auto lcm = p * q / gcd(p, q); + // let a = lcm / p, b = lcm / q + if (lcm / p % 2 == 1) { + if (lcm / q % 2 == 1) { + return 1; // a is odd, b is odd <=> (p & -p) == (q & -q) + } + return 2; // a is odd, b is even <=> (p & -p) > (q & -q) + } + return 0; // a is even, b is odd <=> (p & -p) < (q & -q) + } + +private: + int gcd(int a, int b) { + while (b != 0) { + int tmp = b; + b = a % b; + a = tmp; + } + return a; + } +}; diff --git a/C++/missing-number.cpp b/C++/missing-number.cpp new file mode 100644 index 000000000..c6acf3901 --- /dev/null +++ b/C++/missing-number.cpp @@ -0,0 +1,25 @@ +// Time: O(n) +// Space: O(1) + +class Solution { + public: + int missingNumber(vector& nums) { + int num = 0; + for (int i = 0; i < nums.size(); ++i) { + num ^= nums[i] ^ (i + 1); + } + return num; + } +}; + +// Time: O(n) +// Space: O(n) +class Solution2 { + public: + int missingNumber(vector& nums) { + vector expected(nums.size()); + iota(expected.begin(), expected.end(), 1); // Costs extra space O(n) + return accumulate(nums.cbegin(), nums.cend(), 0, bit_xor()) ^ + accumulate(expected.cbegin(), expected.cend(), 0, bit_xor()); + } +}; diff --git a/C++/missing-ranges.cpp b/C++/missing-ranges.cpp new file mode 100644 index 000000000..e097d37b6 --- /dev/null +++ b/C++/missing-ranges.cpp @@ -0,0 +1,28 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + vector findMissingRanges(vector& nums, int lower, int upper) { + vector ranges; + for (int i = 0, pre = lower - 1, cur = 0; i <= nums.size(); ++i, pre = cur) { + if (i == nums.size()) { + cur = upper + 1; + } else { + cur = nums[i]; + } + if (cur - pre >= 2) { + ranges.emplace_back(getRange(pre + 1, cur - 1)); + } + } + return ranges; + } + + string getRange(const int lower, const int upper) { + if (lower == upper) { + return to_string(lower); + } else { + return to_string(lower) + "->" + to_string(upper); + } + } +}; diff --git a/C++/monotone-increasing-digits.cpp b/C++/monotone-increasing-digits.cpp new file mode 100644 index 000000000..b9cf59927 --- /dev/null +++ b/C++/monotone-increasing-digits.cpp @@ -0,0 +1,20 @@ +// Time: O(logn) = O(1) +// Space: O(logn) = O(1) + +class Solution { +public: + int monotoneIncreasingDigits(int N) { + string s = to_string(N); + int leftmost_inverted_idx = s.length(); + for (int i = s.length() - 1; i > 0; --i) { + if (s[i - 1] > s[i]) { + leftmost_inverted_idx = i; + --s[i - 1]; + } + } + for (int i = leftmost_inverted_idx; i < s.length(); ++i) { + s[i] = '9'; + } + return stoi(s); + } +}; diff --git a/C++/most-common-word.cpp b/C++/most-common-word.cpp new file mode 100644 index 000000000..041e9943f --- /dev/null +++ b/C++/most-common-word.cpp @@ -0,0 +1,30 @@ +// Time: O(m + n), m is the size of banned, n is the size of paragraph +// Space: O(m + n) + +class Solution { +public: + string mostCommonWord(string paragraph, vector& banned) { + unordered_set lookup(banned.cbegin(), banned.cend()); + unordered_map counts; + string word; + for (const auto& c : paragraph) { + if (isalpha(c)) { + word.push_back(tolower(c)); + } else if (!word.empty()) { + ++counts[word]; + word.clear(); + } + } + if (!word.empty()) { + ++counts[word]; + } + string result; + for (const auto& kvp : counts) { + if ((result.empty() || kvp.second > counts[result]) && + !lookup.count(kvp.first)) { + result = kvp.first; + } + } + return result; + } +}; diff --git a/C++/most-frequent-subtree-sum.cpp b/C++/most-frequent-subtree-sum.cpp new file mode 100644 index 000000000..cb3ff6f6c --- /dev/null +++ b/C++/most-frequent-subtree-sum.cpp @@ -0,0 +1,41 @@ +// Time: O(n) +// Space: O(n) + +/** + * 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: + vector findFrequentTreeSum(TreeNode* root) { + unordered_map counts; + int max_count = 0; + countSubtreeSumsHelper(root, &counts, &max_count); + + vector result; + for (const auto& kvp : counts){ + if (kvp.second == max_count) { + result.emplace_back(kvp.first); + } + } + return result; + } + +private: + int countSubtreeSumsHelper(TreeNode *root, unordered_map *counts, int *max_count) { + if (!root) { + return 0; + } + auto sum = root->val + + countSubtreeSumsHelper(root->left, counts, max_count) + + countSubtreeSumsHelper(root->right, counts, max_count); + ++(*counts)[sum]; + (*max_count) = max((*max_count), (*counts)[sum]); + return sum; + } +}; diff --git a/C++/most-profit-assigning-work.cpp b/C++/most-profit-assigning-work.cpp new file mode 100644 index 000000000..6e042201e --- /dev/null +++ b/C++/most-profit-assigning-work.cpp @@ -0,0 +1,24 @@ +// Time: O(mlogm + nlogn), m is the number of workers, +// , n is the number of jobs +// Space: O(n) + +class Solution { +public: + int maxProfitAssignment(vector& difficulty, vector& profit, vector& worker) { + vector> jobs; + for (int i = 0; i < profit.size(); ++i) { + jobs.emplace_back(difficulty[i], profit[i]); + } + sort(jobs.begin(), jobs.end()); + sort(worker.begin(), worker.end()); + + int result = 0, i = 0, max_profit = 0; + for (const auto& ability: worker) { + while (i < profit.size() && jobs[i].first <= ability) { + max_profit = max(max_profit, jobs[i++].second); + } + result += max_profit; + } + return result; + } +}; diff --git a/C++/move-zeroes.cpp b/C++/move-zeroes.cpp new file mode 100644 index 000000000..8f984a02e --- /dev/null +++ b/C++/move-zeroes.cpp @@ -0,0 +1,27 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + void moveZeroes(vector& nums) { + int pos = 0; + for (auto& num : nums) { + if (num) { + swap(nums[pos++], num); + } + } + } +}; + +class Solution2 { +public: + void moveZeroes(vector& nums) { + int pos = 0; + for (const auto& num : nums) { + if (num) { + nums[pos++] = num; + } + } + fill(next(nums.begin(), pos), nums.end(), 0); + } +}; diff --git a/C++/moving-average-from-data-stream.cpp b/C++/moving-average-from-data-stream.cpp new file mode 100644 index 000000000..e4ac8d76e --- /dev/null +++ b/C++/moving-average-from-data-stream.cpp @@ -0,0 +1,31 @@ +// Time: O(1) +// Space: O(w) + +class MovingAverage { +public: + /** Initialize your data structure here. */ + MovingAverage(int size) : size_(size), sum_(0) { + } + + double next(int val) { + if (q_.size() == size_) { + sum_ -= q_.front(); + q_.pop(); + } + q_.emplace(val); + sum_ += val; + return 1.0 * sum_ / q_.size(); + } + +private: + int size_; + int sum_; + queue q_; +}; + +/** + * Your MovingAverage object will be instantiated and called as such: + * MovingAverage obj = new MovingAverage(size); + * double param_1 = obj.next(val); + */ + diff --git a/C++/multiply-strings.cpp b/C++/multiply-strings.cpp new file mode 100644 index 000000000..a661246f4 --- /dev/null +++ b/C++/multiply-strings.cpp @@ -0,0 +1,89 @@ +// Time: O(m * n) +// Space: O(m + n) + +class Solution { +public: + string multiply(string num1, string num2) { + const auto char_to_int = [](const char c) { return c - '0'; }; + const auto int_to_char = [](const int i) { return i + '0'; }; + + vector n1; + transform(num1.rbegin(), num1.rend(), back_inserter(n1), char_to_int); + vector n2; + transform(num2.rbegin(), num2.rend(), back_inserter(n2), char_to_int); + + vector tmp(n1.size() + n2.size()); + for(int i = 0; i < n1.size(); ++i) { + for(int j = 0; j < n2.size(); ++j) { + tmp[i + j] += n1[i] * n2[j]; + tmp[i + j + 1] += tmp[i + j] / 10; + tmp[i + j] %= 10; + } + } + + string res; + transform(find_if(tmp.rbegin(), prev(tmp.rend()), + [](const int i) { return i != 0; }), + tmp.rend(), back_inserter(res), int_to_char); + return res; + } +}; + +// Time: O(m * n) +// Space: O(m + n) +// Define a new BigInt class solution. +class Solution2 { +public: + string multiply(string num1, string num2) { + return BigInt(num1) * BigInt(num2); + } + + class BigInt { + public: + BigInt(const string& s) { + transform(s.rbegin(), s.rend(), back_inserter(n_), + [](const char c) { return c - '0'; }); + } + + operator string() { + string s; + transform(find_if(n_.rbegin(), prev(n_.rend()), + [](const int i) { return i != 0; }), + n_.rend(), back_inserter(s), + [](const int i) { return i + '0'; }); + return s; + } + + BigInt operator*(const BigInt &rhs) const { + BigInt res(n_.size() + rhs.size(), 0); + for(auto i = 0; i < n_.size(); ++i) { + for(auto j = 0; j < rhs.size(); ++j) { + res[i + j] += n_[i] * rhs[j]; + res[i + j + 1] += res[i + j] / 10; + res[i + j] %= 10; + } + } + return res; + } + + private: + vector n_; + + BigInt(int num, int val): n_(num, val) { + } + + // Getter. + int operator[] (int i) const { + return n_[i]; + } + + // Setter. + int & operator[] (int i) { + return n_[i]; + } + + size_t size() const { + return n_.size(); + } + }; +}; diff --git a/C++/multiply.cpp b/C++/multiply.cpp deleted file mode 100644 index 6989e837b..000000000 --- a/C++/multiply.cpp +++ /dev/null @@ -1,57 +0,0 @@ -// Time Complexity: O(n) -// Space Complexity: O(1) - -class BigInt { - public: - BigInt(string s) { - transform(s.rbegin(), s.rend(), back_inserter(n), - [](const char c) { return c - '0';}); - } - - operator string() { - string s; - transform(find_if(this->n.rbegin(), prev(this->n.rend()), - [](const int i) { return i != 0; }), this->n.rend(), back_inserter(s), - [](const int i) { return i + '0'; }); - - return s; - } - - BigInt operator*(const BigInt &rhs) const { - BigInt z(n.size() + rhs.size() + 1, 0); - for(auto i = 0; i < n.size(); ++i) { - for(auto j = 0; j < rhs.size(); ++j) { - z[i + j] += n[i] * rhs[j]; - z[i + j + 1] += z[i + j] / 10; - z[i + j] %= 10; - } - } - return z; - } - private: - vector n; - - BigInt(int num, int val): n(num, val) { - } - - // getter - int operator[] (int i) const { - return this->n[i]; - } - - // setter - int & operator[] (int i) { - return this->n[i]; - } - - int size() const { - return this->n.size(); - } -}; - -class Solution { - public: - string multiply(string num1, string num2) { - return BigInt(num1) * BigInt(num2); - } -}; diff --git a/C++/my-calendar-i.cpp b/C++/my-calendar-i.cpp new file mode 100644 index 000000000..28d1b911a --- /dev/null +++ b/C++/my-calendar-i.cpp @@ -0,0 +1,32 @@ +// Time: O(nlogn) +// Space: O(n) + +class MyCalendar { +public: + MyCalendar() { + + } + + bool book(int s, int e) { + auto next = books_.lower_bound(s); + if (next != books_.end() && next->first < e) { + return false; + } + if (next != books_.begin() && s < (--next)->second) { + return false; + } + books_[s] = e; + return true; + } + +private: + map books_; + +}; + +/** + * Your MyCalendar object will be instantiated and called as such: + * MyCalendar obj = new MyCalendar(); + * bool param_1 = obj.book(start,end); + */ + diff --git a/C++/my-calendar-ii.cpp b/C++/my-calendar-ii.cpp new file mode 100644 index 000000000..60e26aa42 --- /dev/null +++ b/C++/my-calendar-ii.cpp @@ -0,0 +1,34 @@ +// Time: O(n^2) +// Space: O(n) + +class MyCalendarTwo { +public: + MyCalendarTwo() { + + } + + bool book(int start, int end) { + for (const auto& p : overlaps_) { + if (start < p.second && end > p.first) { + return false; + } + } + for (const auto& p : calendar_) { + if (start < p.second && end > p.first) { + overlaps_.emplace_back(max(start, p.first), min(end, p.second)); + } + } + calendar_.emplace_back(start, end); + return true; + } + +private: + vector> overlaps_; + vector> calendar_; +}; + +/** + * Your MyCalendarTwo object will be instantiated and called as such: + * MyCalendarTwo obj = new MyCalendarTwo(); + * bool param_1 = obj.book(start,end); + */ diff --git a/C++/my-calendar-iii.cpp b/C++/my-calendar-iii.cpp new file mode 100644 index 000000000..895b389dd --- /dev/null +++ b/C++/my-calendar-iii.cpp @@ -0,0 +1,30 @@ +// Time: O(n^2) +// Space: O(n) + +class MyCalendarThree { +public: + MyCalendarThree() { + + } + + int book(int start, int end) { + ++books_[start]; + --books_[end]; + int result = 0; + int cnt = 0; + for (const auto &book : books_) { + cnt += book.second; + result = max(result, cnt); + } + return result; + } + +private: + map books_; +}; + +/** + * Your MyCalendarThree object will be instantiated and called as such: + * MyCalendarThree obj = new MyCalendarThree(); + * int param_1 = obj.book(start,end); + */ diff --git a/C++/n-ary-tree-level-order-traversal.cpp b/C++/n-ary-tree-level-order-traversal.cpp new file mode 100644 index 000000000..222aa6b7a --- /dev/null +++ b/C++/n-ary-tree-level-order-traversal.cpp @@ -0,0 +1,43 @@ +// Time: O(n) +// Space: O(w) + +/* +// Definition for a Node. +class Node { +public: + int val = NULL; + vector children; + + Node() {} + + Node(int _val, vector _children) { + val = _val; + children = _children; + } +}; +*/ +class Solution { +public: + vector> levelOrder(Node* root) { + if (!root) { + return {}; + } + vector> result; + vector q{root}; + while (!q.empty()) { + vector curr; + vector next_q; + for (const auto& node : q) { + for (const auto& child : node->children) { + if (child) { + next_q.emplace_back(child); + } + } + curr.emplace_back(node->val); + } + result.emplace_back(move(curr)); + q = move(next_q); + } + return result; + } +}; diff --git a/C++/n-ary-tree-postorder-traversal.cpp b/C++/n-ary-tree-postorder-traversal.cpp new file mode 100644 index 000000000..31981e6b6 --- /dev/null +++ b/C++/n-ary-tree-postorder-traversal.cpp @@ -0,0 +1,39 @@ +// Time: O(n) +// Space: O(h) + +/* +// Definition for a Node. +class Node { +public: + int val; + vector children; + + Node() {} + + Node(int _val, vector _children) { + val = _val; + children = _children; + } +}; +*/ +class Solution { +public: + vector postorder(Node* root) { + if (!root) { + return {}; + } + vector result; + vector stack{root}; + while (!stack.empty()) { + auto node = stack.back(); stack.pop_back(); + result.emplace_back(node->val); + for (const auto& child : node->children) { + if (child) { + stack.emplace_back(child); + } + } + } + reverse(result.begin(), result.end()); + return result; + } +}; diff --git a/C++/n-ary-tree-preorder-traversal.cpp b/C++/n-ary-tree-preorder-traversal.cpp new file mode 100644 index 000000000..923a62455 --- /dev/null +++ b/C++/n-ary-tree-preorder-traversal.cpp @@ -0,0 +1,40 @@ +// Time: O(n) +// Space: O(h) + +/* +// Definition for a Node. +class Node { +public: + int val; + vector children; + + Node() {} + + Node(int _val, vector _children) { + val = _val; + children = _children; + } +}; +*/ +class Solution { +public: + vector preorder(Node* root) { + if (!root) { + return {}; + } + vector result; + vector stack{root}; + while (!stack.empty()) { + auto node = stack.back(); stack.pop_back(); + result.emplace_back(node->val); + for (auto rit = node->children.rbegin(); + rit != node->children.rend(); + ++rit) { + if (*rit) { + stack.emplace_back(*rit); + } + } + } + return result; + } +}; diff --git a/C++/nested-list-weight-sum-ii.cpp b/C++/nested-list-weight-sum-ii.cpp new file mode 100644 index 000000000..61be50f94 --- /dev/null +++ b/C++/nested-list-weight-sum-ii.cpp @@ -0,0 +1,49 @@ +// Time: O(n) +// Space: O(h) + +/** + * // This is the interface that allows for creating nested lists. + * // You should not implement it, or speculate about its implementation + * class NestedInteger { + * public: + * // Return true if this NestedInteger holds a single integer, rather than a nested list. + * bool isInteger() const; + * + * // Return the single integer that this NestedInteger holds, if it holds a single integer + * // The result is undefined if this NestedInteger holds a nested list + * int getInteger() const; + * + * // Return the nested list that this NestedInteger holds, if it holds a nested list + * // The result is undefined if this NestedInteger holds a single integer + * const vector &getList() const; + * }; + */ +class Solution { +public: + int depthSumInverse(vector& nestedList) { + vector result; + for (const auto& list : nestedList) { + depthSumInverseHelper(list, 0, &result); + } + + int sum = 0; + for (int i = result.size() - 1; i >= 0; --i) { + sum += result[i] * (result.size() - i); + } + return sum; + } + +private: + void depthSumInverseHelper(const NestedInteger &list, int depth, vector *result) { + if (result->size() < depth + 1) { + result->emplace_back(0); + } + if (list.isInteger()) { + (*result)[depth] += list.getInteger(); + } else { + for (const auto& l : list.getList()) { + depthSumInverseHelper(l, depth + 1, result); + } + } + } +}; diff --git a/C++/nested-list-weight-sum.cpp b/C++/nested-list-weight-sum.cpp new file mode 100644 index 000000000..0a58943f3 --- /dev/null +++ b/C++/nested-list-weight-sum.cpp @@ -0,0 +1,39 @@ +// Time: O(n) +// Space: O(h) + +/** + * // This is the interface that allows for creating nested lists. + * // You should not implement it, or speculate about its implementation + * class NestedInteger { + * public: + * // Return true if this NestedInteger holds a single integer, rather than a nested list. + * bool isInteger() const; + * + * // Return the single integer that this NestedInteger holds, if it holds a single integer + * // The result is undefined if this NestedInteger holds a nested list + * int getInteger() const; + * + * // Return the nested list that this NestedInteger holds, if it holds a nested list + * // The result is undefined if this NestedInteger holds a single integer + * const vector &getList() const; + * }; + */ +class Solution { +public: + int depthSum(vector& nestedList) { + return depthSumHelper(nestedList, 1); + } + +private: + int depthSumHelper(const vector& nestedList, int depth) { + int sum = 0; + for (const auto& list : nestedList) { + if (list.isInteger()) { + sum += list.getInteger() * depth; + } else { + sum += depthSumHelper(list.getList(), depth + 1); + } + } + return sum; + } +}; diff --git a/C++/network-delay-time.cpp b/C++/network-delay-time.cpp new file mode 100644 index 000000000..7496cd87e --- /dev/null +++ b/C++/network-delay-time.cpp @@ -0,0 +1,42 @@ +// Time: O((|E| + |V|) * log|V|) = O(|E| * log|V|) +// Space: O(|E| + |V|) = O(|E|) + +// Dijkstra's algorithm +class Solution { +public: + int networkDelayTime(vector>& times, int N, int K) { + using P = pair; + vector> adj(N); + for (const auto& time : times) { + int u, v, w; + tie(u, v, w) = make_tuple(time[0] - 1, time[1] - 1, time[2]); + adj[u].emplace_back(v, w); + } + + int result = 0; + unordered_set lookup; + unordered_map best; + priority_queue, greater

> min_heap; + min_heap.emplace(0, K - 1); + while (!min_heap.empty() && lookup.size() != N) { + int u; + tie(result, u) = min_heap.top(); min_heap.pop(); + lookup.emplace(u); + if (best.count(u) && + best[u] < result) { + continue; + } + for (const auto& kvp : adj[u]) { + int v, w; + tie(v, w) = kvp; + if (lookup.count(v)) continue; + if (!best.count(v) || + result + w < best[v]) { + best[v] = result + w; + min_heap.emplace(result + w, v); + } + } + } + return lookup.size() == N ? result : -1; + } +}; diff --git a/C++/new-21-game.cpp b/C++/new-21-game.cpp new file mode 100644 index 000000000..4743fde6a --- /dev/null +++ b/C++/new-21-game.cpp @@ -0,0 +1,26 @@ +// Time: O(n) +// Space: O(n) + +class Solution { +public: + double new21Game(int N, int K, int W) { + if (K == 0 || N >= K + W) { + return 1.0f; + } + vector dp(N + 1); + dp[0] = 1.0f; + double W_sum = 1.0f, result = 0.0f; + for (int i = 1; i <= N; ++i) { + dp[i] = W_sum / W; + if (i < K) { + W_sum += dp[i]; + } else { + result += dp[i]; + } + if (i - W >= 0) { + W_sum -= dp[i - W]; + } + } + return result; + } +}; diff --git a/C++/next-closest-time.cpp b/C++/next-closest-time.cpp new file mode 100644 index 000000000..c5a9922ae --- /dev/null +++ b/C++/next-closest-time.cpp @@ -0,0 +1,23 @@ +// Time: O(1) +// Space: O(1) + +class Solution { +public: + string nextClosestTime(string time) { + static const vector mins = { 600, 60, 10, 1 }; + auto npos = time.find(':'); + auto curr = stoi(time.substr(0, npos)) * 60 + + stoi(time.substr(npos + 1)); + string result = "0000"; + for (int i = 1, d = 0; i <= 1440 && d < 4; ++i) { + int m = (curr + i) % 1440; + for (d = 0; d < 4; ++d) { + result[d] = '0' + m / mins[d]; m %= mins[d]; + if (time.find(result[d]) == string::npos) { + break; + } + } + } + return result.substr(0, 2) + ':' + result.substr(2, 2); + } +}; diff --git a/C++/next-greater-element-i.cpp b/C++/next-greater-element-i.cpp new file mode 100644 index 000000000..28bdc2623 --- /dev/null +++ b/C++/next-greater-element-i.cpp @@ -0,0 +1,27 @@ +// Time: O(m + n) +// Space: O(m + n) + +class Solution { +public: + vector nextGreaterElement(vector& findNums, vector& nums) { + stack stk; + unordered_map lookup; + for (const auto& num : nums) { + while (!stk.empty() && num > stk.top()) { + lookup[stk.top()] = num; + stk.pop(); + } + stk.emplace(num); + } + while (!stk.empty()) { + lookup[stk.top()] = -1; + stk.pop(); + } + + vector result; + for (const auto& num : findNums) { + result.emplace_back(lookup[num]); + } + return result; + } +}; diff --git a/C++/next-greater-element-ii.cpp b/C++/next-greater-element-ii.cpp new file mode 100644 index 000000000..04c6440bf --- /dev/null +++ b/C++/next-greater-element-ii.cpp @@ -0,0 +1,18 @@ +// Time: O(n) +// Space: O(n) + +class Solution { +public: + vector nextGreaterElements(vector& nums) { + vector result(nums.size()); + stack stk; + for (int i = 2 * nums.size() - 1; i >= 0; --i) { + while (!stk.empty() && stk.top() <= nums[i % nums.size()]) { + stk.pop(); + } + result[i % nums.size()] = stk.empty() ? -1 : stk.top(); + stk.emplace(nums[i % nums.size()]); + } + return result; + } +}; diff --git a/C++/next-greater-element-iii.cpp b/C++/next-greater-element-iii.cpp new file mode 100644 index 000000000..2f086500c --- /dev/null +++ b/C++/next-greater-element-iii.cpp @@ -0,0 +1,39 @@ +// Time: O(logn) = O(1) +// Space: O(logn) = O(1) + +class Solution { +public: + int nextGreaterElement(int n) { + auto digits = to_string(n); + nextPermutation(begin(digits), end(digits)); // self-implemented next_permutattion() + auto result = stoll(digits); + return (result > numeric_limits::max() || result <= n) ? -1 : result; + } + +private: + template + bool nextPermutation(BidiIt begin, BidiIt end) { + const auto rbegin = reverse_iterator(end); + const auto rend = reverse_iterator(begin); + + // Find the first element (pivot) which is less than its successor. + auto pivot = next(rbegin); + while (pivot != rend && *pivot >= *prev(pivot)) { + ++pivot; + } + + bool is_greater = true; + if (pivot != rend) { + // Find the number which is greater than pivot, and swap it with pivot + auto change = find_if(rbegin, pivot, bind1st(less(), *pivot)); + swap(*change, *pivot); + } else { + is_greater = false; + } + + // Make the sequence after pivot non-descending + reverse(rbegin, pivot); + + return is_greater; + } +}; diff --git a/C++/next-permutation.cpp b/C++/next-permutation.cpp new file mode 100644 index 000000000..a6f8a9e4b --- /dev/null +++ b/C++/next-permutation.cpp @@ -0,0 +1,43 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + void nextPermutation(vector &num) { + nextPermutation(num.begin(), num.end()); + } + +private: + template + bool nextPermutation(BidiIt begin, BidiIt end) { + const auto rbegin = reverse_iterator(end); + const auto rend = reverse_iterator(begin); + + // Find the first element (pivot) which is less than its successor. + auto pivot = next(rbegin); + while (pivot != rend && *pivot >= *prev(pivot)) { + ++pivot; + } + + bool is_greater = true; + if (pivot != rend) { + // Find the number which is greater than pivot, and swap it with pivot + auto change = find_if(rbegin, pivot, bind1st(less(), *pivot)); + swap(*change, *pivot); + } else { + is_greater = false; + } + + // Make the sequence after pivot non-descending + reverse(rbegin, pivot); + + return is_greater; + } +}; + +class Solution2 { +public: + void nextPermutation(vector &num) { + next_permutation(num.begin(), num.end()); + } +}; diff --git a/C++/nextPermutation.cpp b/C++/nextPermutation.cpp deleted file mode 100644 index 371f8b07c..000000000 --- a/C++/nextPermutation.cpp +++ /dev/null @@ -1,37 +0,0 @@ -// Time Complexity: O(n) -// Space Complexity: O(1) - -class Solution { - public: - void nextPermutation(vector &num) { - nextPermutation(begin(num), end(num)); - } - - private: - template - bool nextPermutation(BidiIt begin, BidiIt end) { - const auto rbegin = reverse_iterator(end); - const auto rend = reverse_iterator(begin); - - // find the firt element (pivot) which is less than its successor - auto pivot = next(rbegin); - while(pivot != rend && *pivot >= *prev(pivot)) { - ++pivot; - } - - // no next permutation, just reverse the whole sequence - if(pivot == rend) { - reverse(rbegin, rend); - return false; - } - - // find the number which is greater than pivot, and swap it with pivot - auto change = find_if(rbegin, pivot, bind1st(less(), *pivot)); - swap(*change, *pivot); - - // make the sequence after pivot non-descending - reverse(rbegin, pivot); - - return true; // return next permutation - } -}; diff --git a/C++/nim-game.cpp b/C++/nim-game.cpp new file mode 100644 index 000000000..f0b3470bb --- /dev/null +++ b/C++/nim-game.cpp @@ -0,0 +1,9 @@ +// Time: O(1) +// Soace: O(1) + +class Solution { +public: + bool canWinNim(int n) { + return n % 4 != 0; + } +}; diff --git a/C++/non-decreasing-array.cpp b/C++/non-decreasing-array.cpp new file mode 100644 index 000000000..87e288d8e --- /dev/null +++ b/C++/non-decreasing-array.cpp @@ -0,0 +1,24 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + bool checkPossibility(vector& nums) { + int cnt = 0; + for (int i = 1, prev = nums[0]; i < nums.size(); ++i) { + if (prev > nums[i]) { + if (cnt++) { + return false; + } + if (i - 2 < 0 || nums[i - 2] <= nums[i]) { + prev = nums[i]; // nums[i - 1] = nums[i], prev = nums[i] +// } else { +// prev = nums[i - 1]; // nums[i] = nums[i - 1], prev = nums[i] + } + } else { + prev = nums[i]; + } + } + return true; + } +}; diff --git a/C++/non-negative-integers-without-consecutive-ones.cpp b/C++/non-negative-integers-without-consecutive-ones.cpp new file mode 100644 index 000000000..02fbe7862 --- /dev/null +++ b/C++/non-negative-integers-without-consecutive-ones.cpp @@ -0,0 +1,28 @@ +// Time: O(1) +// Space: O(1) + +class Solution { +public: + int findIntegers(int num) { + vector dp(32); + dp[0] = 1; + dp[1] = 2; + for (int i = 2; i < dp.size(); ++i) { + dp[i] = dp[i - 1] + dp[i - 2]; + } + int result = 0, prev_bit = 0; + for (int i = 30; i >= 0; --i) { + if ((num & (1 << i)) != 0) { + result += dp[i]; + if (prev_bit == 1) { + --result; + break; + } + prev_bit = 1; + } else { + prev_bit = 0; + } + } + return result + 1; + } +}; diff --git a/C++/non-overlapping-intervals.cpp b/C++/non-overlapping-intervals.cpp new file mode 100644 index 000000000..54728e436 --- /dev/null +++ b/C++/non-overlapping-intervals.cpp @@ -0,0 +1,32 @@ +// Time: O(nlogn) +// Space: O(1) + +/** + * Definition for an interval. + * struct Interval { + * int start; + * int end; + * Interval() : start(0), end(0) {} + * Interval(int s, int e) : start(s), end(e) {} + * }; + */ +class Solution { +public: + int eraseOverlapIntervals(vector& intervals) { + sort(intervals.begin(), intervals.end(), + [](const Interval& a, const Interval& b) { return a.start < b.start; }); + + int result = 0, prev = 0; + for (int i = 1; i < intervals.size(); ++i) { + if (intervals[i].start < intervals[prev].end) { + if (intervals[i].end < intervals[prev].end) { + prev = i; + } + ++result; + } else { + prev = i; + } + } + return result; + } +}; diff --git a/C++/nth-digit.cpp b/C++/nth-digit.cpp new file mode 100644 index 000000000..afe69e929 --- /dev/null +++ b/C++/nth-digit.cpp @@ -0,0 +1,20 @@ +// Time: O(logn) +// Space: O(1) + +class Solution { +public: + int findNthDigit(int n) { + int digit_len = 1; + while (n > digit_len * 9 * pow(10, digit_len - 1)) { + n -= digit_len * 9 * pow(10, digit_len - 1); + ++digit_len; + } + + const int num = pow(10, digit_len - 1) + (n - 1) / digit_len; + + int nth_digit = num / pow(10, (digit_len - 1) - (n - 1) % digit_len); + nth_digit %= 10; + + return nth_digit; + } +}; diff --git a/C++/nth-magical-number.cpp b/C++/nth-magical-number.cpp new file mode 100644 index 000000000..8e9ec5475 --- /dev/null +++ b/C++/nth-magical-number.cpp @@ -0,0 +1,36 @@ +// Time: O(logn) +// Space: O(1) + +class Solution { +public: + int nthMagicalNumber(int N, int A, int B) { + static const int M = 1000000007; + const uint64_t a = A, b = B; + const auto lcm = a * b / gcd(a, b); + + auto left = min(a, b), right = max(a, b) * N; + while (left <= right) { + const auto mid = left + (right - left) / 2; + if (check(A, B, N, lcm, mid)) { + right = mid - 1; + } else { + left = mid + 1; + } + } + return left % M; + } + +private: + bool check(uint64_t a, uint64_t b, uint64_t N, uint64_t lcm, uint64_t target) { + return target / a + target / b - target / lcm >= N; + } + + uint64_t gcd(uint64_t a, uint64_t b) { + while (b != 0) { + int tmp = b; + b = a % b; + a = tmp; + } + return a; + } +}; diff --git a/C++/numDecodings.cpp b/C++/numDecodings.cpp deleted file mode 100644 index 1f77e5d0a..000000000 --- a/C++/numDecodings.cpp +++ /dev/null @@ -1,25 +0,0 @@ -// Time Complexity: O(n) -// Space Complexity: O(1) - -class Solution { - public: - int numDecodings(string s) { - if(s.empty()) return 0; - - int prev = 0; // f[n - 2] - int cur = 1; // f[n - 1] - - for(int i = 1; i <= s.length(); ++i) { - if(s[i - 1] == '0') - cur = 0; // f[n - 1] = 0 - if(i < 2 || !(s[i - 2] == '1' || (s[i - 2] == '2' && s[i - 1] <= '6'))) - prev = 0; // f[n - 2] = 0; - - int tmp = cur; - cur += prev; // f[n] = f[n - 1] + f[n - 2] - prev = tmp; - } - - return cur; - } -}; diff --git a/C++/number-complement.cpp b/C++/number-complement.cpp new file mode 100644 index 000000000..d07a6b8e1 --- /dev/null +++ b/C++/number-complement.cpp @@ -0,0 +1,13 @@ +// Time: O(1) +// Space: O(1) + +class Solution { +public: + int findComplement(int num) { + unsigned int i = 1; + while (i <= num) { + i <<= 1; + } + return (i - 1) ^ num; + } +}; diff --git a/C++/number-of-1-bits.cpp b/C++/number-of-1-bits.cpp new file mode 100644 index 000000000..38c66af18 --- /dev/null +++ b/C++/number-of-1-bits.cpp @@ -0,0 +1,13 @@ +// Time: O(logn) = O(32) +// Space: O(1) + +class Solution { +public: + int hammingWeight(uint32_t n) { + int count = 0; + for (; n; n &= n - 1) { + ++count; + } + return count; + } +}; diff --git a/C++/number-of-atoms.cpp b/C++/number-of-atoms.cpp new file mode 100644 index 000000000..64aa73f1f --- /dev/null +++ b/C++/number-of-atoms.cpp @@ -0,0 +1,40 @@ +// Time: O(n) +// Space: O(n) + +class Solution { +public: + string countOfAtoms(string formula) { + stack> stk; + stk.emplace(); + int submatches[] = { 1, 2, 3, 4, 5 }; + const auto e = regex("([A-Z][a-z]*)(\\d*)|(\\()|(\\))(\\d*)"); + for (regex_token_iterator it(formula.begin(), formula.end(), e, submatches), end; + it != end;) { + const auto& name = (it++)->str(); + const auto& m1 = (it++)->str(); + const auto& left_open = (it++)->str(); + const auto& right_open = (it++)->str(); + const auto& m2 = (it++)->str(); + if (!name.empty()) { + stk.top()[name] += stoi(!m1.empty() ? m1 : "1"); + } + if (!left_open.empty()) { + stk.emplace(); + } + if (!right_open.empty()) { + const auto top = move(stk.top()); stk.pop(); + for (const auto& kvp: top) { + stk.top()[kvp.first] += kvp.second * stoi(!m2.empty() ? m2 : "1"); + } + } + } + string result; + for (const auto& kvp : stk.top()) { + result += kvp.first; + if (kvp.second > 1) { + result += to_string(kvp.second); + } + } + return result; + } +}; diff --git a/C++/number-of-boomerangs.cpp b/C++/number-of-boomerangs.cpp new file mode 100644 index 000000000..e5b813cc8 --- /dev/null +++ b/C++/number-of-boomerangs.cpp @@ -0,0 +1,29 @@ +// Time: O(n^2) +// Space: O(n) + +class Solution { +public: + int numberOfBoomerangs(vector>& points) { + int result = 0; + + for (int i = 0; i < points.size(); ++i) { + unordered_map group; + for (int j = 0; j < points.size(); ++j) { + if (j == i) { + continue; + } + const auto dx = points[i].first - points[j].first; + const auto dy = points[i].second - points[j].second; + ++group[dx * dx + dy * dy]; + } + + for (const auto& p : group) { + if (p.second > 1) { + result += p.second * (p.second - 1); + } + } + } + + return result; + } +}; diff --git a/C++/number-of-connected-components-in-an-undirected-graph.cpp b/C++/number-of-connected-components-in-an-undirected-graph.cpp new file mode 100644 index 000000000..73bbdf533 --- /dev/null +++ b/C++/number-of-connected-components-in-an-undirected-graph.cpp @@ -0,0 +1,44 @@ +// Time: O(nlog*n) ~= O(n), n is the length of the positions +// Space: O(n) + +class Solution { +public: + int countComponents(int n, vector>& edges) { + UnionFind union_find(n); + for (const auto& e : edges) { + union_find.union_set(e.first, e.second); + } + return union_find.length(); + } + +private: + class UnionFind { + public: + UnionFind(const int n) : set_(n), count_(n) { + iota(set_.begin(), set_.end(), 0); + } + + int find_set(const int x) { + if (set_[x] != x) { + set_[x] = find_set(set_[x]); // Path compression. + } + return set_[x]; + } + + void union_set(const int x, const int y) { + int x_root = find_set(x), y_root = find_set(y); + if (x_root != y_root) { + set_[min(x_root, y_root)] = max(x_root, y_root); + --count_; + } + } + + int length() const { + return count_; + } + + private: + vector set_; + int count_; + }; +}; diff --git a/C++/number-of-corner-rectangles.cpp b/C++/number-of-corner-rectangles.cpp new file mode 100644 index 000000000..7d2b7a2a5 --- /dev/null +++ b/C++/number-of-corner-rectangles.cpp @@ -0,0 +1,32 @@ +// Time: O(m^2 * n), m is the number of rows with 1s, n is the number of cols with 1s +// Space: O(m * n) + +class Solution { +public: + int countCornerRectangles(vector>& grid) { + vector> rows; + for (int i = 0; i < grid.size(); ++i) { + vector row; + for (int j = 0; j < grid[i].size(); ++j) { + if (grid[i][j]) { + row.emplace_back(j); + } + } + if (!row.empty()) { + rows.emplace_back(move(row)); + } + } + int result = 0; + for (int i = 0; i < rows.size(); ++i) { + unordered_set lookup(rows[i].begin(), rows[i].end()); + for (int j = 0; j < i; ++j) { + int count = 0; + for (const auto& c : rows[j]) { + count += lookup.count(c); + } + result += count * (count - 1) / 2; + } + } + return result; + } +}; diff --git a/C++/number-of-digit-one.cpp b/C++/number-of-digit-one.cpp new file mode 100644 index 000000000..3b6f3f35d --- /dev/null +++ b/C++/number-of-digit-one.cpp @@ -0,0 +1,34 @@ +// Time: O(logn) = O(1) +// Space: O(1) + +class Solution { +public: + int countDigitOne(int n) { + const int k = 1; + int cnt = 0, multiplier = 1, left_part = n; + + while (left_part > 0) { + // split number into: left_part, curr, right_part + int curr = left_part % 10; + int right_part = n % multiplier; + + // count of (c000 ~ oooc000) = (ooo + (k < curr)? 1 : 0) * 1000 + cnt += (left_part / 10 + (k < curr)) * multiplier; + + // if k == 0, oooc000 = (ooo - 1) * 1000 + if (k == 0 && multiplier > 1) { + cnt -= multiplier; + } + + // count of (oook000 ~ oookxxx): count += xxx + 1 + if (curr == k) { + cnt += right_part + 1; + } + + left_part /= 10; + multiplier *= 10; + } + + return cnt; + } +}; diff --git a/C++/number-of-distinct-islands-ii.cpp b/C++/number-of-distinct-islands-ii.cpp new file mode 100644 index 000000000..7f09919ec --- /dev/null +++ b/C++/number-of-distinct-islands-ii.cpp @@ -0,0 +1,73 @@ +// Time: O((m * n) * log(m * n)) +// Space: O(m * n) + +class Solution { +public: + int numDistinctIslands2(vector>& grid) { + unordered_set>, VectorHash> islands; + for (int i = 0; i < grid.size(); ++i) { + for (int j = 0; j < grid[i].size(); ++j) { + if (grid[i][j] == 1) { + vector> island; + if (dfs(i, j, &grid, &island)) { + islands.emplace(normalize(island)); + } + } + } + } + return islands.size(); + } + +private: + struct VectorHash { + size_t operator()(const std::vector>& v) const { + size_t seed = 0; + for (const auto& i : v) { + seed ^= std::hash{}(i.first) + 0x9e3779b9 + (seed<<6) + (seed>>2); + seed ^= std::hash{}(i.second) + 0x9e3779b9 + (seed<<6) + (seed>>2); + } + return seed; + } + }; + + bool dfs(const int i, const int j, + vector> *grid, vector> *island) { + + static const vector> directions{{1, 0}, {-1, 0}, + {0, 1}, {0, -1}}; + + if (i < 0 || i >= grid->size() || + j < 0 || j >= (*grid)[0].size() || + (*grid)[i][j] <= 0) { + return false; + } + (*grid)[i][j] *= -1; + island->emplace_back(i, j); + for (const auto& direction : directions) { + dfs(i + direction.first, j + direction.second, grid, island); + } + return true; + } + + vector> normalize(const vector>& island) { + vector>> shapes(8); + for (const auto& p : island) { + int x, y; + tie(x, y) = p; + vector> rotations_and_reflections{{ x, y}, { x, -y}, {-x, y}, {-x, -y}, + { y, x}, { y, -x}, {-y, x}, {-y, -x}}; + for (int i = 0; i < rotations_and_reflections.size(); ++i) { + shapes[i].emplace_back(rotations_and_reflections[i]); + } + } + for (auto& shape : shapes) { + sort(shape.begin(), shape.end()); // Time: O(ilogi), i is the size of the island, the max would be (m * n) + const auto origin = shape.front(); + for (auto& p : shape) { + p = {p.first - origin.first, + p.second - origin.second}; + } + } + return *min_element(shapes.begin(), shapes.end()); + } +}; diff --git a/C++/number-of-distinct-islands.cpp b/C++/number-of-distinct-islands.cpp new file mode 100644 index 000000000..09f50532f --- /dev/null +++ b/C++/number-of-distinct-islands.cpp @@ -0,0 +1,39 @@ +// Time: O(m * n) +// Space: O(m * n) + +class Solution { +public: + int numDistinctIslands(vector>& grid) { + unordered_set islands; + for (int i = 0; i < grid.size(); ++i) { + for (int j = 0; j < grid[0].size(); ++j) { + string island; + if (dfs(i, j, &grid, &island)) { + islands.emplace(island); + } + } + } + return islands.size(); + } + +private: + bool dfs(const int i, const int j, + vector> *grid, string *island) { + + static const unordered_map> + directions = { {'l', {-1, 0} }, {'r', { 1, 0} }, + {'u', { 0, 1} }, {'d', { 0, -1} }}; + + if (i < 0 || i >= grid->size() || + j < 0 || j >= (*grid)[0].size() || + (*grid)[i][j] <= 0) { + return false; + } + (*grid)[i][j] *= -1; + for (const auto& kvp : directions) { + island->push_back(kvp.first); + dfs(i + kvp.second.first, j + kvp.second.second, grid, island); + } + return true; + } +}; diff --git a/C++/number-of-islands-ii.cpp b/C++/number-of-islands-ii.cpp new file mode 100644 index 000000000..cc234784d --- /dev/null +++ b/C++/number-of-islands-ii.cpp @@ -0,0 +1,119 @@ +// Time: O(klog*k) ~= O(k), k is the length of the positions +// Space: O(k) + +// Using unordered_map. +class Solution { +public: + vector numIslands2(int m, int n, vector>& positions) { + vector numbers; + int number = 0; + const vector> directions{{0, -1}, {0, 1}, + {-1, 0}, {1, 0}}; + unordered_map set; + for (const auto& position : positions) { + const auto& node = make_pair(position.first, position.second); + set[node_id(node, n)] = node_id(node, n); + ++number; + + for (const auto& d : directions) { + const auto& neighbor = make_pair(position.first + d.first, + position.second + d.second); + if (neighbor.first >= 0 && neighbor.first < m && + neighbor.second >= 0 && neighbor.second < n && + set.find(node_id(neighbor, n)) != set.end()) { + if (find_set(node_id(node, n), &set) != + find_set(node_id(neighbor, n), &set)) { + // Merge different islands, amortised time: O(log*k) ~= O(1) + union_set(&set, node_id(node, n), node_id(neighbor, n)); + --number; + } + } + } + numbers.emplace_back(number); + } + + return numbers; + } + + int node_id(const pair& node, const int n) { + return node.first * n + node.second; + } + + int find_set(int x, unordered_map *set) { + if ((*set)[x] != x) { + (*set)[x] = find_set((*set)[x], set); // path compression. + } + return (*set)[x]; + } + + void union_set(unordered_map *set, const int x, const int y) { + int x_root = find_set(x, set), y_root = find_set(y, set); + (*set)[min(x_root, y_root)] = max(x_root, y_root); + } +}; + + +// Time: O(klog*k) ~= O(k), k is the length of the positions +// Space: O(m * n) +// Using vector. +class Solution2 { +public: + /** + * @param n an integer + * @param m an integer + * @param operators an array of point + * @return an integer array + */ + vector numIslands2(int m, int n, vector>& positions) { + vector numbers; + int number = 0; + const vector> directions{{0, -1}, {0, 1}, + {-1, 0}, {1, 0}}; + vector set(m * n, -1); + for (const auto& position : positions) { + const auto& node = make_pair(position.first, position.second); + set[node_id(node, n)] = node_id(node, n); + ++number; + + for (const auto& d : directions) { + const auto& neighbor = make_pair(position.first + d.first, + position.second + d.second); + if (neighbor.first >= 0 && neighbor.first < m && + neighbor.second >= 0 && neighbor.second < n && + set[node_id(neighbor, n)] != -1) { + if (find_set(node_id(node, n), &set) != + find_set(node_id(neighbor, n), &set)) { + // Merge different islands, amortised time: O(log*k) ~= O(1) + union_set(&set, node_id(node, n), node_id(neighbor, n)); + --number; + } + } + } + numbers.emplace_back(number); + } + + return numbers; + } + + int node_id(const pair& node, const int m) { + return node.first * m + node.second; + } + + int find_set(int x, vector *set) { + int parent = x; + while ((*set)[parent] != parent) { + parent = (*set)[parent]; + } + while ((*set)[x] != x) { + int tmp = (*set)[x]; + (*set)[x] = parent; + x = tmp; + } + return parent; + } + + void union_set(vector *set, const int x, const int y) { + int x_root = find_set(x, set), y_root = find_set(y, set); + (*set)[min(x_root, y_root)] = max(x_root, y_root); + } +}; diff --git a/C++/number-of-lines-to-write-string.cpp b/C++/number-of-lines-to-write-string.cpp new file mode 100644 index 000000000..8bbb14e08 --- /dev/null +++ b/C++/number-of-lines-to-write-string.cpp @@ -0,0 +1,19 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + vector numberOfLines(vector& widths, string S) { + vector result(2); + result.front() = 1; + for (const auto& c : S) { + const auto& w = widths[c - 'a']; + result.back() += w; + if (result.back() > 100) { + ++result.front(); + result.back() = w; + } + } + return result; + } +}; diff --git a/C++/number-of-longest-increasing-subsequence.cpp b/C++/number-of-longest-increasing-subsequence.cpp new file mode 100644 index 000000000..5c9588a42 --- /dev/null +++ b/C++/number-of-longest-increasing-subsequence.cpp @@ -0,0 +1,28 @@ +// Time: O(n^2) +// Space: O(n) + +class Solution { +public: + int findNumberOfLIS(vector& nums) { + auto result = 0, max_len = 0; + vector> dp(nums.size(), {1, 1}); // {length, number} pair + for (int i = 0; i < nums.size(); ++i) { + for (int j = 0; j < i; ++j) { + if (nums[i] > nums[j]) { + if (dp[i].first == dp[j].first + 1) { + dp[i].second += dp[j].second; + } else if (dp[i].first < dp[j].first + 1) { + dp[i] = {dp[j].first + 1, dp[j].second}; + } + } + } + if (max_len == dp[i].first) { + result += dp[i].second; + } else if (max_len < dp[i].first) { + max_len = dp[i].first; + result = dp[i].second; + } + } + return result; + } +}; diff --git a/C++/number-of-matching-subsequences.cpp b/C++/number-of-matching-subsequences.cpp new file mode 100644 index 000000000..b1965ef9b --- /dev/null +++ b/C++/number-of-matching-subsequences.cpp @@ -0,0 +1,22 @@ +// Time: O(n + w), n is the size of S, w is the size of words +// Space: O(1) + +class Solution { +public: + int numMatchingSubseq(string S, vector& words) { + unordered_map>> waiting; + for (int i = 0; i < words.size(); ++i) { + waiting[words[i][0]].emplace_back(i, 1); + } + for (const auto& c : S) { + auto advance = move(waiting[c]); + waiting.erase(c); + for (const auto& kvp : advance) { + int i = kvp.first, j = kvp.second; + int next = (j != words[i].length()) ? words[i][j] : 0; + waiting[next].emplace_back(i, j + 1); + } + } + return waiting[0].size(); + } +}; diff --git a/C++/number-of-segments-in-a-string.cpp b/C++/number-of-segments-in-a-string.cpp new file mode 100644 index 000000000..73b078893 --- /dev/null +++ b/C++/number-of-segments-in-a-string.cpp @@ -0,0 +1,15 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + int countSegments(string s) { + int result = static_cast(!s.empty() && s.back() != ' '); + for (int i = 1; i < s.size(); ++i) { + if (s[i] == ' ' && s[i - 1] != ' ') { + ++result; + } + } + return result; + } +}; diff --git a/C++/number-of-subarrays-with-bounded-maximum.cpp b/C++/number-of-subarrays-with-bounded-maximum.cpp new file mode 100644 index 000000000..203e69560 --- /dev/null +++ b/C++/number-of-subarrays-with-bounded-maximum.cpp @@ -0,0 +1,19 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + int numSubarrayBoundedMax(vector& A, int L, int R) { + return count(A, R) - count(A, L - 1); + } + +private: + int count(const vector& A, int bound) const { + int result = 0, curr = 0; + for (const auto& i : A) { + curr = (i <= bound) ? curr + 1 : 0; + result += curr; + } + return result; + } +}; diff --git a/C++/odd-even-linked-list.cpp b/C++/odd-even-linked-list.cpp new file mode 100644 index 000000000..1897ec415 --- /dev/null +++ b/C++/odd-even-linked-list.cpp @@ -0,0 +1,28 @@ +// Time: O(n) +// Space: O(1) + +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode(int x) : val(x), next(NULL) {} + * }; + */ +class Solution { +public: + ListNode* oddEvenList(ListNode* head) { + if (head) { + for (auto odd_tail = head, curr = head->next; + curr && curr->next; + curr = curr->next) { + ListNode *even_head = odd_tail->next; + odd_tail->next = curr->next; + odd_tail = odd_tail->next; + curr->next = odd_tail->next; + odd_tail->next = even_head; + } + } + return head; + } +}; diff --git a/C++/one-edit-distance.cpp b/C++/one-edit-distance.cpp new file mode 100644 index 000000000..586fc8200 --- /dev/null +++ b/C++/one-edit-distance.cpp @@ -0,0 +1,28 @@ +// Time: O(m + n) +// Space: O(1) + +class Solution { +public: + bool isOneEditDistance(string s, string t) { + const int m = s.length(), n = t.length(); + if (m > n) { + return isOneEditDistance(t, s); + } + if (n - m > 1) { + return false; + } + + int i = 0, shift = n - m; + while (i < m && s[i] == t[i]) { + ++i; + } + if (shift == 0) { + ++i; + } + while (i < m && s[i] == t[i + shift]) { + ++i; + } + + return i == m; + } +}; diff --git a/C++/ones-and-zeroes.cpp b/C++/ones-and-zeroes.cpp new file mode 100644 index 000000000..5abc12520 --- /dev/null +++ b/C++/ones-and-zeroes.cpp @@ -0,0 +1,26 @@ +// Time: O(s * m * n), s is the size of the array. +// Space: O(m * n) + +class Solution { +public: + int findMaxForm(vector& strs, int m, int n) { + vector> dp(m + 1, vector(n + 1)); + for (const auto &str : strs) { + int zero_count = 0, one_count = 0; + for (const auto& c : str) { + if (c == '0') { + ++zero_count; + } else if (c == '1') { + ++one_count; + } + } + + for (int i = m; i - zero_count >= 0; --i) { + for (int j = n; j - one_count >= 0; --j) { + dp[i][j] = max(dp[i][j], dp[i - zero_count][j - one_count] + 1); + } + } + } + return dp[m][n]; + } +}; diff --git a/C++/open-the-lock.cpp b/C++/open-the-lock.cpp new file mode 100644 index 000000000..85cda74fe --- /dev/null +++ b/C++/open-the-lock.cpp @@ -0,0 +1,40 @@ +// Time: O(k * n^k + d), n is the number of alphabets, +// k is the length of target, +// d is the size of deadends +// Space: O(k * n^k + d) + +class Solution { +public: + int openLock(vector& deadends, string target) { + unordered_set dead(deadends.begin(), deadends.end()); + vector q{"0000"}; + unordered_set lookup{"0000"}; + int depth = 0; + while (!q.empty()) { + vector next_q; + for (const auto& node : q) { + if (node == target) { + return depth; + } + if (dead.count(node)) { + continue; + } + for (int i = 0; i < 4; ++i) { + auto n = node[i] - '0'; + for (const auto& d : {-1, 1}) { + auto nn = (n + d + 10) % 10; + auto neighbor = node; + neighbor[i] = '0' + nn; + if (!lookup.count(neighbor)) { + lookup.emplace(neighbor); + next_q.emplace_back(neighbor); + } + } + } + } + swap(q, next_q); + ++depth; + } + return -1; + } +}; diff --git a/C++/optimal-account-balancing.cpp b/C++/optimal-account-balancing.cpp new file mode 100644 index 000000000..ec33dedef --- /dev/null +++ b/C++/optimal-account-balancing.cpp @@ -0,0 +1,48 @@ +// Time: O(n * 2^n), n is the size of the debt. +// Space: O(n * 2^n) + +class Solution { +public: + int minTransfers(vector>& transactions) { + unordered_map account; + for (const auto& transaction : transactions) { + account[transaction[0]] += transaction[2]; + account[transaction[1]] -= transaction[2]; + } + + vector debt; + for (const auto& kvp : account) { + if (kvp.second) { + debt.emplace_back(kvp.second); + } + } + if (debt.empty()) { + return 0; + } + + const auto n = 1 << debt.size(); + vector dp(n, numeric_limits::max()), subset; + for (int i = 1; i < n; ++i) { + int net_debt = 0, number = 0; + for (int j = 0; j < debt.size(); ++j) { + if (i & 1 << j) { + net_debt += debt[j]; + ++number; + } + } + if (net_debt == 0) { + dp[i] = number - 1; + for (const auto& s : subset) { + if ((i & s) == s) { + if (dp[s] != numeric_limits::max() && + dp[i - s] != numeric_limits::max()) { + dp[i] = min(dp[i], dp[s] + dp[i - s]); + } + } + } + subset.emplace_back(i); + } + } + return dp.back(); + } +}; diff --git a/C++/optimal-division.cpp b/C++/optimal-division.cpp new file mode 100644 index 000000000..899ab0fa3 --- /dev/null +++ b/C++/optimal-division.cpp @@ -0,0 +1,20 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + string optimalDivision(vector& nums) { + if (nums.size() == 1) { + return to_string(nums[0]); + } + if (nums.size() == 2) { + return to_string(nums[0]) + "/" + to_string(nums[1]); + } + string result = to_string(nums[0]) + "/(" + to_string(nums[1]); + for (int i = 2; i < nums.size(); ++i) { + result += "/" + to_string(nums[i]); + } + result.push_back(')'); + return result; + } +}; diff --git a/C++/out-of-boundary-paths.cpp b/C++/out-of-boundary-paths.cpp new file mode 100644 index 000000000..a0c829858 --- /dev/null +++ b/C++/out-of-boundary-paths.cpp @@ -0,0 +1,22 @@ +// Time: O(N * m * n) +// Space: O(m * n) + +class Solution { +public: + int findPaths(int m, int n, int N, int x, int y) { + const auto M = 1000000000 + 7; + vector>> dp(2, vector>(m, vector(n))); + int result = 0; + for (int moves = 0; moves < N; ++moves) { + for (int i = 0; i < m; ++i) { + for (int j = 0; j < n; ++j) { + dp[(moves + 1) % 2][i][j] = (((i == 0 ? 1 : dp[moves % 2][i - 1][j]) + + (i == m - 1 ? 1 : dp[moves % 2][i + 1][j])) % M + + ((j == 0 ? 1 : dp[moves % 2][i][j - 1]) + + (j == n - 1 ? 1 : dp[moves % 2][i][j + 1])) % M) % M; + } + } + } + return dp[N % 2][x][y]; + } +}; diff --git a/C++/output-contest-matches.cpp b/C++/output-contest-matches.cpp new file mode 100644 index 000000000..b97d47a3e --- /dev/null +++ b/C++/output-contest-matches.cpp @@ -0,0 +1,20 @@ +// Time: O(n) +// Space: O(n) + +class Solution { +public: + string findContestMatch(int n) { + vector matches(n); + for (int i = 0; i < n; ++i) { + matches[i] = to_string(i + 1); + } + while (matches.size() / 2) { + vector next_matches; + for (int i = 0; i < matches.size() / 2; ++i) { + next_matches.emplace_back("(" + matches[i] + "," + matches[matches.size() - 1 - i] + ")"); + } + swap(matches, next_matches); + } + return matches[0]; + } +}; diff --git a/C++/pacific-atlantic-water-flow.cpp b/C++/pacific-atlantic-water-flow.cpp new file mode 100644 index 000000000..1128de3e5 --- /dev/null +++ b/C++/pacific-atlantic-water-flow.cpp @@ -0,0 +1,50 @@ +// Time: O(m * n) +// Space: O(m * n) + +class Solution { +public: + + vector> pacificAtlantic(vector>& matrix) { + if (matrix.empty()) { + return {}; + } + + vector> res; + const auto m = matrix.size(), n = matrix[0].size(); + vector> visited(m, vector(n)); + + for (int i = 0; i < m; ++i) { + pacificAtlanticHelper(matrix, i, 0, numeric_limits::min(), PACIFIC, &visited, &res); + pacificAtlanticHelper(matrix, i, n - 1, numeric_limits::min(), ATLANTIC, &visited, &res); + } + for (int j = 0; j < n; ++j) { + pacificAtlanticHelper(matrix, 0, j, numeric_limits::min(), PACIFIC, &visited, &res); + pacificAtlanticHelper(matrix, m - 1, j, numeric_limits::min(), ATLANTIC, &visited, &res); + } + + return res; + } + +private: + void pacificAtlanticHelper(const vector>& matrix, int x, int y, int prev_height, int prev_val, + vector> *visited, vector> *res) { + + if (x < 0 || x >= matrix.size() || + y < 0 || y >= matrix[0].size() || + matrix[x][y] < prev_height || ((*visited)[x][y] | prev_val) == (*visited)[x][y]) { + return; + } + + (*visited)[x][y] |= prev_val; + if ((*visited)[x][y] == (PACIFIC | ATLANTIC)) { + res->emplace_back(x, y); + } + + for (const auto& dir : directions) { + pacificAtlanticHelper(matrix, x + dir.first, y + dir.second, matrix[x][y], (*visited)[x][y], visited, res); + } + } + + enum ocean { PACIFIC = 1, ATLANTIC = 2 }; + const vector> directions{ {0, -1}, {0, 1}, {-1, 0}, {1, 0} }; +}; diff --git a/C++/paint-fence.cpp b/C++/paint-fence.cpp new file mode 100644 index 000000000..6dd44afb8 --- /dev/null +++ b/C++/paint-fence.cpp @@ -0,0 +1,42 @@ +// Time: O(n) +// Space: O(1) + +// DP with rolling window. +class Solution { +public: + int numWays(int n, int k) { + if (n == 0) { + return 0; + } else if (n == 1) { + return k; + } + vector ways(3, 0); + ways[0] = k; + ways[1] = (k - 1) * ways[0] + k; + for (int i = 2; i < n; ++i) { + ways[i % 3] = (k - 1) * (ways[(i - 1) % 3] + ways[(i - 2) % 3]); + } + return ways[(n - 1) % 3]; + } +}; + +// Time: O(n) +// Space: O(n) +// DP solution. +class Solution2 { +public: + int numWays(int n, int k) { + if (n == 0) { + return 0; + } else if (n == 1) { + return k; + } + vector ways(n, 0); + ways[0] = k; + ways[1] = (k - 1) * ways[0] + k; + for (int i = 2; i < n; ++i) { + ways[i] = (k - 1) * (ways[i - 1] + ways[i - 2]); + } + return ways[n - 1]; + } +}; diff --git a/C++/paint-house-ii.cpp b/C++/paint-house-ii.cpp new file mode 100644 index 000000000..7a4fc3851 --- /dev/null +++ b/C++/paint-house-ii.cpp @@ -0,0 +1,58 @@ +// Time: O(n * k) +// Space: O(k) + +class Solution { +public: + int minCostII(vector>& costs) { + if (costs.empty()) { + return 0; + } + + vector> min_cost(2, costs[0]); + + const int n = costs.size(); + const int k = costs[0].size(); + for (int i = 1; i < n; ++i) { + int smallest = numeric_limits::max(), second_smallest = numeric_limits::max(); + for (int j = 0; j < k; ++j) { + if (min_cost[(i - 1) % 2][j] < smallest) { + second_smallest = smallest; + smallest = min_cost[(i - 1) % 2][j]; + } else if (min_cost[(i - 1) % 2][j] < second_smallest) { + second_smallest = min_cost[(i - 1) % 2][j]; + } + } + for (int j = 0; j < k; ++j) { + const int min_j = (min_cost[(i - 1) % 2][j] != smallest) ? smallest : second_smallest; + min_cost[i % 2][j] = costs[i][j] + min_j; + } + } + + return *min_element(min_cost[(n - 1) % 2].cbegin(), min_cost[(n - 1) % 2].cend()); + } +}; + +// Time: O(n * k) +// Space: O(k) +class Solution2{ +public: + int minCostII(vector>& costs) { + if (costs.empty()) { + return 0; + } + auto combine = [](const vector& tmp, const vector& house) { + const int smallest = *min_element(tmp.cbegin(), tmp.cend()); + const int i = distance(tmp.begin(), find(tmp.cbegin(), tmp.cend(), smallest)); + vector tmp2(tmp); + tmp2.erase(tmp2.begin() + i); + const int second_smallest = *min_element(tmp2.cbegin(), tmp2.cend()); + vector min_cost(tmp.size(), smallest); + min_cost[i] = second_smallest; + transform(min_cost.cbegin(), min_cost.cend(), house.cbegin(), + min_cost.begin(), std::plus()); + return min_cost; + }; + vector min_cost = accumulate(costs.cbegin(), costs.cend(), vector(costs[0].size(), 0), combine); + return *min_element(min_cost.cbegin(), min_cost.cend()); + } +}; diff --git a/C++/paint-house.cpp b/C++/paint-house.cpp new file mode 100644 index 000000000..d93d65527 --- /dev/null +++ b/C++/paint-house.cpp @@ -0,0 +1,46 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + int minCost(vector>& costs) { + if (costs.empty()) { + return 0; + } + + vector> min_cost(2, costs[0]); + + const int n = costs.size(); + for (int i = 1; i < n; ++i) { + min_cost[i % 2][0] = costs[i][0] + + min(min_cost[(i - 1) % 2][1], min_cost[(i - 1) % 2][2]); + min_cost[i % 2][1] = costs[i][1] + + min(min_cost[(i - 1) % 2][0], min_cost[(i - 1) % 2][2]); + min_cost[i % 2][2] = costs[i][2] + + min(min_cost[(i - 1) % 2][0], min_cost[(i - 1) % 2][1]); + } + + return min(min_cost[(n - 1) % 2][0], + min(min_cost[(n - 1) % 2][1], min_cost[(n - 1) % 2][2])); + } +}; + +// Time: O(n) +// Space: O(n) +class Solution2 { +public: + int minCost(vector>& costs) { + if (costs.empty()) { + return 0; + } + + const int n = costs.size(); + for (int i = 1; i < n; ++i) { + costs[i][0] += min(costs[i - 1][1], costs[i - 1][2]); + costs[i][1] += min(costs[i - 1][0], costs[i - 1][2]); + costs[i][2] += min(costs[i - 1][0], costs[i - 1][1]); + } + + return min(costs[n - 1][0], min(costs[n - 1][1], costs[n - 1][2])); + } +}; diff --git a/C++/palindrome-linked-list.cpp b/C++/palindrome-linked-list.cpp new file mode 100644 index 000000000..af5feef28 --- /dev/null +++ b/C++/palindrome-linked-list.cpp @@ -0,0 +1,43 @@ +// Time: O(n) +// Space: O(1) + +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode(int x) : val(x), next(NULL) {} + * }; + */ +class Solution { +public: + bool isPalindrome(ListNode* head) { + // Reverse the first half list. + ListNode *reverse = nullptr, *fast = head; + while (fast && fast->next) { + fast = fast->next->next; + const auto head_next = head->next; + head->next = reverse; + reverse = head; + head = head_next; + } + + // If the number of the nodes is odd, + // set the head of the tail list to the next of the median node. + ListNode *tail = fast ? head->next : head; + + // Compare the reversed first half list with the second half list. + // And restore the reversed first half list. + bool is_palindrome = true; + while (reverse) { + is_palindrome = is_palindrome && reverse->val == tail->val; + const auto reverse_next = reverse->next; + reverse->next = head; + head = reverse; + reverse = reverse_next; + tail = tail->next; + } + + return is_palindrome; + } +}; diff --git a/C++/palindrome-number.cpp b/C++/palindrome-number.cpp new file mode 100644 index 000000000..6c32423e2 --- /dev/null +++ b/C++/palindrome-number.cpp @@ -0,0 +1,44 @@ +// Time: O(logx) = O(1) +// Space: O(1) + +class Solution { +public: + bool isPalindrome(int x) { + if (x < 0) { + return false; + } + int temp = x; + int reversed = 0; + while (temp != 0) { + reversed = reversed * 10 + temp % 10; + temp = temp / 10; + } + return reversed == x; + } +}; + +// Time: O(logx) = O(1) +// Space: O(1) +class Solution2 { +public: + bool isPalindrome(int x) { + if(x < 0) { + return false; + } + + int divisor = 1; + while (x / divisor >= 10) { + divisor *= 10; + } + + for (; x > 0; x = (x % divisor) / 10, divisor /= 100) { + int left = x / divisor; + int right = x % 10; + if (left != right) { + return false; + } + } + + return true; + } +}; diff --git a/C++/palindrome-pairs.cpp b/C++/palindrome-pairs.cpp new file mode 100644 index 000000000..8b108b478 --- /dev/null +++ b/C++/palindrome-pairs.cpp @@ -0,0 +1,181 @@ +// Time: O(n * k^2), n is the number of the words, k is the max length of the words. +// Space: O(n * k) + +class Solution { +public: + vector> palindromePairs(vector& words) { + vector> res; + unordered_map lookup; + for (int i = 0; i < words.size(); ++i) { + lookup[words[i]] = i; + } + + for (int i = 0; i < words.size(); ++i) { + for (int j = 0; j <= words[i].length(); ++j) { + if (is_palindrome(words[i], j, words[i].length() - 1)) { + string suffix = words[i].substr(0, j); + reverse(suffix.begin(), suffix.end()); + if (lookup.find(suffix) != lookup.end() && i != lookup[suffix]) { + res.push_back({i, lookup[suffix]}); + } + } + if (j > 0 && is_palindrome(words[i], 0, j - 1)) { + string prefix = words[i].substr(j); + reverse(prefix.begin(), prefix.end()); + if (lookup.find(prefix) != lookup.end() && lookup[prefix] != i) { + res.push_back({lookup[prefix], i}); + } + } + } + } + return res; + } + +private: + bool is_palindrome(string& s, int start, int end) { + while (start < end) { + if (s[start++] != s[end--]) { + return false; + } + } + return true; + } +}; + +// Time: O(n * k^2), n is the number of the words, k is the max length of the words. +// Space: O(n * k^2) +// Manacher solution. +class Solution2 { +public: + vector> palindromePairs(vector& words) { + unordered_multimap prefix, suffix; + for (int i = 0; i < words.size(); ++i) { // O(n) + vector P; + manacher(words[i], &P); + for (int j = 0; j < P.size(); ++j) { // O(k) + if (j - P[j] == 1) { + prefix.emplace(words[i].substr((j + P[j]) / 2), i); // O(k) + } + if (j + P[j] == P.size() - 2) { + suffix.emplace(words[i].substr(0, (j - P[j]) / 2), i); + } + } + } + + vector> res; + for (int i = 0; i < words.size(); ++i) { // O(n) + string reversed_word(words[i].rbegin(), words[i].rend()); // O(k) + auto its = prefix.equal_range(reversed_word); + for (auto it = its.first; it != its.second; ++it) { + if (it->second != i) { + res.push_back({i, it->second}); + } + } + its = suffix.equal_range(reversed_word); + for (auto it = its.first; it != its.second; ++it) { + if (words[i].size() != words[it->second].size()) { + res.push_back({it->second, i}); + } + } + } + return res; + } + + void manacher(const string& s, vector *P) { + string T = preProcess(s); + const int n = T.length(); + P->resize(n); + int C = 0, R = 0; + for (int i = 1; i < n - 1; ++i) { + int i_mirror = 2 * C - i; + (*P)[i] = (R > i) ? min(R - i, (*P)[i_mirror]) : 0; + while (T[i + 1 + (*P)[i]] == T[i - 1 - (*P)[i]]) { + ++(*P)[i]; + } + if (i + (*P)[i] > R) { + C = i; + R = i + (*P)[i]; + } + } + } + + string preProcess(const string& s) { + if (s.empty()) { + return "^$"; + } + string ret = "^"; + for (int i = 0; i < s.length(); ++i) { + ret += "#" + s.substr(i, 1); + } + ret += "#$"; + return ret; + } +}; + +// Time: O(n * k^2), n is the number of the words, k is the max length of the words. +// Space: O(n * k) +// Trie solution. +class Solution_MLE { +public: + vector> palindromePairs(vector& words) { + vector> res; + TrieNode trie; + for (int i = 0; i < words.size(); ++i) { + trie.insert(words[i], i); + } + for (int i = 0; i < words.size(); ++i) { + trie.find(words[i], i, &res); + } + return res; + } + +private: + struct TrieNode { + int word_idx = -1; + unordered_map leaves; + + void insert(const string& s, int i) { + auto* p = this; + for (const auto& c : s) { + if (p->leaves.find(c) == p->leaves.cend()) { + p->leaves[c] = new TrieNode; + } + p = p->leaves[c]; + } + p->word_idx = i; + } + + void find(const string& s, int idx, vector> *res) { + auto* p = this; + for (int i = s.length() - 1; i >= 0; --i) { // O(k) + if (p->leaves.find(s[i]) != p->leaves.cend()) { + p = p->leaves[s[i]]; + if (p->word_idx != -1 && p->word_idx != idx && + is_palindrome(s, i - 1)) { // O(k) + res->push_back({p->word_idx, idx}); + } + } else { + break; + } + } + } + + bool is_palindrome(const string& s, int j) { + int i = 0; + while (i <= j) { + if (s[i++] != s[j--]) { + return false; + } + } + return true; + } + + ~TrieNode() { + for (auto& kv : leaves) { + if (kv.second) { + delete kv.second; + } + } + } + }; +}; diff --git a/C++/palindrome-permutation-ii.cpp b/C++/palindrome-permutation-ii.cpp new file mode 100644 index 000000000..806824177 --- /dev/null +++ b/C++/palindrome-permutation-ii.cpp @@ -0,0 +1,96 @@ +// Time: O(n * n!) +// Space: O(n) + +class Solution { +public: + vector generatePalindromes(string s) { + if (s.empty()) { + return {}; + } + + unordered_map cnt; + for (const auto& c : s) { + ++cnt[c]; + } + + string mid, chars; + for (const auto& kvp : cnt) { + if (kvp.second % 2) { + if (mid.empty()) { + mid.push_back(kvp.first); + } else { // The count of the middle char is at most one. + return {}; + } + } + chars.append(kvp.second / 2, kvp.first); + } + return permuteUnique(mid, chars); + } + + vector permuteUnique(const string& mid, string& chars) { + vector result; + sort(chars.begin(), chars.end()); + do { + string reverse_chars(chars.crbegin(), chars.crend()); + result.emplace_back(chars + mid + reverse_chars); + } while (next_permutation(chars.begin(), chars.end())); + return result; + } +}; + +class Solution2 { +public: + vector generatePalindromes(string s) { + if (s.empty()) { + return {}; + } + + unordered_map cnt; + for (const auto& c : s) { + ++cnt[c]; + } + + string mid, chars; + for (const auto& kvp : cnt) { + if (kvp.second % 2) { + if (mid.empty()) { + mid.append(1, kvp.first); + } else { // The count of the middle char is at most one. + return {}; + } + } + chars.append(kvp.second / 2, kvp.first); + } + + return permuteUnique(mid, chars); + } + + vector permuteUnique(const string& mid, string& s) { + vector result; + vector used(s.length(), false); + string ans; + + sort(s.begin(), s.end()); + permuteUniqueRecu(mid, s, &used, &ans, &result); + return result; + } + + void permuteUniqueRecu(const string& mid, const string& s, vector *used, + string *ans, vector *result) { + if (ans->length() == s.length()) { + string reverse_ans(ans->crbegin(), ans->crend()); + result->emplace_back(*ans + mid + reverse_ans); + return; + } + + for (int i = 0; i < s.length(); ++i) { + if (!(*used)[i] && !(i != 0 && s[i - 1] == s[i] && (*used)[i - 1])) { + (*used)[i] = true; + ans->push_back(s[i]); + permuteUniqueRecu(mid, s, used, ans, result); + ans->pop_back(); + (*used)[i] = false; + } + } + } +}; diff --git a/C++/palindrome-permutation.cpp b/C++/palindrome-permutation.cpp new file mode 100644 index 000000000..f68d5cba3 --- /dev/null +++ b/C++/palindrome-permutation.cpp @@ -0,0 +1,13 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + bool canPermutePalindrome(string s) { + bitset<256> bits; + for (const auto& c : s) { + bits.flip(c); + } + return bits.count() < 2; + } +}; diff --git a/C++/palindromic-substrings.cpp b/C++/palindromic-substrings.cpp new file mode 100644 index 000000000..f3012a70f --- /dev/null +++ b/C++/palindromic-substrings.cpp @@ -0,0 +1,46 @@ +// Time: O(n) +// Space: O(n) + +class Solution { +public: + int countSubstrings(string s) { + auto result = 0; + for (const auto& max_len : manacher(s)) { + result += (max_len + 1) / 2; + } + return result; + } + +private: + vector manacher(const string& s) { + string T = preProcess(s); + const int n = T.length(); + vector P(n); + int C = 0, R = 0; + for (int i = 1; i < n - 1; ++i) { + int i_mirror = 2 * C - i; + P[i] = (R > i) ? min(R - i, P[i_mirror]) : 0; + while (T[i + 1 + P[i]] == T[i - 1 - P[i]]) { + ++P[i]; + } + if (i + P[i] > R) { + C = i; + R = i + P[i]; + } + } + return P; + } + + string preProcess(const string& s) { + if (s.empty()) { + return "^$"; + } + string ret = "^"; + for (int i = 0; i < s.length(); ++i) { + ret += "#" + s.substr(i, 1); + } + ret += "#$"; + return ret; + } +}; + diff --git a/C++/parse-lisp-expression.cpp b/C++/parse-lisp-expression.cpp new file mode 100644 index 000000000..9fdb3b225 --- /dev/null +++ b/C++/parse-lisp-expression.cpp @@ -0,0 +1,50 @@ +// Time: O(n^2) +// Space: O(n^2) + +class Solution { +public: + int evaluate(string expression) { + vector tokens{""}; + unordered_map lookup; + vector, unordered_map>> stk; + for (const auto& c : expression) { + if (c == '(') { + if (tokens[0] == "let") { + evaluate(tokens, &lookup); + } + stk.emplace_back(move(tokens), lookup); + tokens = {""}; + } else if (c == ' ') { + tokens.emplace_back(); + } else if (c == ')') { + const auto& val = evaluate(tokens, &lookup); + tie(tokens, lookup) = move(stk.back()); + stk.pop_back(); + tokens.back() += val; + } else { + tokens.back().push_back(c); + } + } + return stoi(tokens[0]); + } + +private: + string evaluate(const vector& tokens, unordered_map* lookup) { + static const unordered_set operators{"add", "mult"}; + if (operators.count(tokens[0])) { + const auto& a = stoi(getval(*lookup, tokens[1])); + const auto& b = stoi(getval(*lookup, tokens[2])); + return to_string(tokens[0] == "add" ? a + b : a * b); + } + for (int i = 1; i < tokens.size() - 1; i += 2) { + if (!tokens[i + 1].empty()) { + (*lookup)[tokens[i]] = getval(*lookup, tokens[i + 1]); + } + } + return getval(*lookup, tokens.back()); + } + + string getval(const unordered_map& lookup, const string& x) { + return lookup.count(x) ? lookup.at(x) : x; + } +}; diff --git a/C++/partition-equal-subset-sum.cpp b/C++/partition-equal-subset-sum.cpp new file mode 100644 index 000000000..1176d41b5 --- /dev/null +++ b/C++/partition-equal-subset-sum.cpp @@ -0,0 +1,23 @@ +// Time: O(n * s), s is the sum of nums. +// Space: O(s) + +class Solution { +public: + bool canPartition(vector& nums) { + const auto sum = accumulate(nums.cbegin(), nums.cend(), 0); + if (sum % 2) { + return false; + } + + vector dp(sum / 2 + 1); + dp[0] = true; + for (const auto& num : nums) { + for (int i = dp.size() - 1; i >= 0; --i) { + if (num <= i) { + dp[i] = dp[i] || dp[i - num]; + } + } + } + return dp.back(); + } +}; diff --git a/C++/partition-labels.cpp b/C++/partition-labels.cpp new file mode 100644 index 000000000..5989631ed --- /dev/null +++ b/C++/partition-labels.cpp @@ -0,0 +1,22 @@ +// Time: O(n) +// Space: O(n) + +class Solution { +public: + vector partitionLabels(string S) { + unordered_map lookup; + for (int i = 0; i < S.length(); ++i) { + lookup[S[i]] = i; + } + int first = 0, last = 0; + vector result; + for (int i = 0; i < S.length(); ++i) { + last = max(last, lookup[S[i]]); + if (i == last) { + result.emplace_back(i - first + 1); + first = i + 1; + } + } + return result; + } +}; diff --git a/C++/partition-list.cpp b/C++/partition-list.cpp new file mode 100644 index 000000000..b3a38a912 --- /dev/null +++ b/C++/partition-list.cpp @@ -0,0 +1,35 @@ +// Time: O(n) +// Space: O(1) + +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode(int x) : val(x), next(NULL) {} + * }; + */ +class Solution { +public: + ListNode *partition(ListNode *head, int x) { + ListNode dummy_smaller{0}; + ListNode dummy_larger{0}; + auto smaller = &dummy_smaller; + auto larger = &dummy_larger; + + while (head) { + if (head->val < x) { + smaller->next = head; + smaller = smaller->next; + } else { + larger->next = head; + larger = larger->next; + } + head = head->next; + } + smaller->next = dummy_larger.next; + larger->next = nullptr; + + return dummy_smaller.next; + } +}; diff --git a/C++/partition-to-k-equal-sum-subsets.cpp b/C++/partition-to-k-equal-sum-subsets.cpp new file mode 100644 index 000000000..cc1dab60c --- /dev/null +++ b/C++/partition-to-k-equal-sum-subsets.cpp @@ -0,0 +1,72 @@ +// Time: O(n*2^n) +// Space: O(2^n) + +// Memoization solution. +class Solution { +public: + bool canPartitionKSubsets(vector& nums, int k) { + auto sum = accumulate(nums.begin(), nums.end(), 0); + if (sum % k != 0 || *max_element(nums.begin(), nums.end()) > sum / k) { + return false; + } + unordered_map lookup; + lookup[(1 << nums.size()) - 1] = true; + return dfs(nums, sum / k, 0, sum, &lookup); + } + +private: + bool dfs(const vector& nums, const int target, + const int used, const int todo, + unordered_map *lookup) { + if (!lookup->count(used)) { + const auto targ = (todo - 1) % target + 1; + for (int i = 0; i < nums.size(); ++i) { + if (((used >> i) & 1) == 0 && nums[i] <= targ) { + if (dfs(nums, target, used | (1 << i), todo - nums[i], lookup)) { + (*lookup)[used] = true; + break; + } + } + } + } + return (*lookup)[used]; + } +}; + +// Time: O(k^(n-k) * k!) +// Space: O(n) +// DFS solution with pruning. +class Solution2 { +public: + bool canPartitionKSubsets(vector& nums, int k) { + auto sum = accumulate(nums.begin(), nums.end(), 0); + if (sum % k != 0 || *max_element(nums.begin(), nums.end()) > sum / k) { + return false; + } + sort(nums.begin(), nums.end(), greater()); // speedup dfs + vector subset_sums(k); + return dfs(nums, sum / k, 0, &subset_sums); + } + +private: + bool dfs(const vector &nums, const int target, + const int i, vector *subset_sums) { + if (i == nums.size()) { + return true; + } + for (auto& subset_sum : *subset_sums) { + if (subset_sum + nums[i] > target) { + continue; + } + subset_sum += nums[i]; + if (dfs(nums, target, i + 1, subset_sums)) { + return true; + } + subset_sum -= nums[i]; + if (subset_sum == 0) { // pruning + break; + } + } + return false; + } +}; diff --git a/C++/partition.cpp b/C++/partition.cpp deleted file mode 100644 index 70e9b8da0..000000000 --- a/C++/partition.cpp +++ /dev/null @@ -1,35 +0,0 @@ -// Time Complexity: O(n) -// Space Complexity: O(1) - -/** - * Definition for singly-linked list. - * struct ListNode { - * int val; - * ListNode *next; - * ListNode(int x) : val(x), next(NULL) {} - * }; - */ -class Solution { - public: - ListNode *partition(ListNode *head, int x) { - ListNode left_dummy(-1); - ListNode right_dummy(-1); - auto left_cur = &left_dummy; - auto right_cur = &right_dummy; - - for(auto cur = head; cur; cur = cur->next) { - if(cur->val < x) { - left_cur->next = cur; - left_cur = cur; - } - else { - right_cur->next = cur; - right_cur = cur; - } - } - - left_cur->next = right_dummy.next; - right_cur->next = nullptr; - return left_dummy.next; - } -}; diff --git a/C++/pascals-triangle-ii.cpp b/C++/pascals-triangle-ii.cpp new file mode 100644 index 000000000..5c11c9346 --- /dev/null +++ b/C++/pascals-triangle-ii.cpp @@ -0,0 +1,18 @@ +// Time: O(n^2) +// Space: O(1) + +class Solution { +public: + vector getRow(int rowIndex) { + vector result(rowIndex + 1); + for (int i = 0; i < result.size(); ++i) { + int prev_result = result[0] = 1; + for (int j = 1; j <= i; ++j) { + const int tmp = result[j]; + result[j] += prev_result; + prev_result = tmp; + } + } + return result; + } +}; diff --git a/C++/pascals-triangle.cpp b/C++/pascals-triangle.cpp new file mode 100644 index 000000000..95a723551 --- /dev/null +++ b/C++/pascals-triangle.cpp @@ -0,0 +1,21 @@ +// Time: O(n^2) +// Space: O(1) + +class Solution { +public: + vector> generate(int numRows) { + vector> result; + for (int i = 0; i < numRows; ++i) { + result.push_back({}); + for (int j = 0; j <= i; ++j) { + if (j == 0 || j == i) { + result[i].emplace_back(1); + } else { + result[i].emplace_back(result[i - 1][j - 1] + + result[i - 1][j]); + } + } + } + return result; + } +}; diff --git a/C++/patching-array.cpp b/C++/patching-array.cpp new file mode 100644 index 000000000..81e054547 --- /dev/null +++ b/C++/patching-array.cpp @@ -0,0 +1,18 @@ +// Time: O(s + logn), s is the number of elements in the array +// Space: O(1) + +class Solution { +public: + int minPatches(vector& nums, int n) { + int patch = 0; + for (uint64_t miss = 1, i = 0; miss <= n;) { + if (i < nums.size() && nums[i] <= miss) { + miss += nums[i++]; + } else { + miss += miss; // miss may overflow, thus prefer to use uint64_t. + ++patch; + } + } + return patch; + } +}; diff --git a/C++/path-sum-iii.cpp b/C++/path-sum-iii.cpp new file mode 100644 index 000000000..7723668dc --- /dev/null +++ b/C++/path-sum-iii.cpp @@ -0,0 +1,58 @@ +// Time: O(n) +// Space: O(h) + +/** + * 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: + int pathSum(TreeNode* root, int sum) { + unordered_map lookup; + lookup[0] = 1; + return pathSumHelper(root, 0, sum, &lookup); + } + +private: + int pathSumHelper(TreeNode* root, int curr, int sum, unordered_map *lookup) { + if (!root) { + return 0; + } + curr += root->val; + int result = lookup->count(curr - sum) ? (*lookup)[curr - sum] : 0; + ++(*lookup)[curr]; + result += pathSumHelper(root->left, curr, sum, lookup) + pathSumHelper(root->right, curr, sum, lookup); + --(*lookup)[curr]; + if ((*lookup)[curr] == 0) { + lookup->erase(curr); + } + return result; + } +}; + + +// Time: O(n^2) +// Space: O(h) +class Solution2 { +public: + int pathSum(TreeNode* root, int sum) { + if (!root) { + return 0; + } + return pathSumHelper(root, 0, sum) + pathSum(root->left, sum) + pathSum(root->right, sum); + } + +private: + int pathSumHelper(TreeNode* root, int prev, int sum) { + if (!root) { + return 0; + } + int curr = prev + root->val; + return (curr == sum) + pathSumHelper(root->left, curr, sum) + pathSumHelper(root->right, curr, sum); + } +}; diff --git a/C++/path-sum-iv.cpp b/C++/path-sum-iv.cpp new file mode 100644 index 000000000..6d6bbec0f --- /dev/null +++ b/C++/path-sum-iv.cpp @@ -0,0 +1,41 @@ +// Time: O(n) +// Space: O(p), p is the number of paths + +class Solution { +public: + int pathSum(vector& nums) { + if (nums.empty()) { + return 0; + } + int result = 0; + queue q; + node dummy(10); + auto parent_ptr = &dummy; + for (const auto& num : nums) { + node child(num); + while (!parent_ptr->isParent(child)) { + result += parent_ptr->leaf ? parent_ptr->val : 0; + parent_ptr = &q.front(); + q.pop(); + } + parent_ptr->leaf = false; + child.val += parent_ptr->val; + q.emplace(move(child)); + } + while (!q.empty()) { + result += q.front().val; + q.pop(); + } + return result; + } + +private: + struct node { + int level, i, val; + bool leaf; + node(int n) : level(n / 100 - 1), i((n % 100) / 10 - 1), val(n % 10), leaf(true) {}; + inline bool isParent(const node& other) { + return level == other.level - 1 && i == other.i / 2; + }; + }; +}; diff --git a/C++/peak-index-in-a-mountain-array.cpp b/C++/peak-index-in-a-mountain-array.cpp new file mode 100644 index 000000000..2425f8459 --- /dev/null +++ b/C++/peak-index-in-a-mountain-array.cpp @@ -0,0 +1,18 @@ +// Time: O(logn) +// Space: O(1) + +class Solution { +public: + int peakIndexInMountainArray(vector& A) { + int left = 0, right = A.size(); + while (left < right) { + const auto mid = left + (right - left) / 2; + if (A[mid] > A[mid + 1]) { + right = mid; + } else { + left = mid + 1; + } + } + return left; + } +}; diff --git a/C++/peeking-iterator.cpp b/C++/peeking-iterator.cpp new file mode 100644 index 000000000..fa5e500be --- /dev/null +++ b/C++/peeking-iterator.cpp @@ -0,0 +1,54 @@ +// Time: O(1) per peek(), next(), hasNext() +// Space: O(1) + +// Below is the interface for Iterator, which is already defined for you. +// **DO NOT** modify the interface for Iterator. +class Iterator { + struct Data; + Data* data; +public: + Iterator(const vector& nums); + Iterator(const Iterator& iter); + virtual ~Iterator(); + // Returns the next element in the iteration. + int next(); + // Returns true if the iteration has more elements. + bool hasNext() const; +}; + + +class PeekingIterator : public Iterator { +public: + PeekingIterator(const vector& nums) : Iterator(nums), has_next_(Iterator::hasNext()) { + // Initialize any member here. + // **DO NOT** save a copy of nums and manipulate it directly. + // You should only use the Iterator interface methods. + } + + // Returns the next element in the iteration without advancing the iterator. + int peek() { + if (!has_peeked_) { + has_peeked_ = true; + val_ = Iterator::next(); + } + return val_; + } + + // hasNext() and next() should behave the same as in the Iterator interface. + // Override them if needed. + int next() { + val_ = peek(); + has_peeked_ = false; + has_next_ = Iterator::hasNext(); + return val_; + } + + bool hasNext() const { + return has_next_; + } + +private: + int val_; + bool has_next_; + bool has_peeked_ = false; +}; diff --git a/C++/perfect-number.cpp b/C++/perfect-number.cpp new file mode 100644 index 000000000..c3516c4ca --- /dev/null +++ b/C++/perfect-number.cpp @@ -0,0 +1,21 @@ +// Time: O(sqrt(n)) +// Space: O(1) + +class Solution { +public: + bool checkPerfectNumber(int num) { + if (num <= 0) { + return false; + } + int sum = 0; + for (int i = 1; i * i <= num; ++i) { + if (num % i == 0) { + sum += i; + if (i * i != num) { + sum += num / i; + } + } + } + return sum - num == num; + } +}; diff --git a/C++/perfect-rectangle.cpp b/C++/perfect-rectangle.cpp new file mode 100644 index 000000000..96d72b5df --- /dev/null +++ b/C++/perfect-rectangle.cpp @@ -0,0 +1,49 @@ +// Time: O(n) +// Space: O(n) + +class Solution { +public: + bool isRectangleCover(vector>& rectangles) { + enum Location {L = 0, B = 1, R = 2, T = 3}; + int left = numeric_limits::max(), bottom = numeric_limits::max(), + right = numeric_limits::min(), top = numeric_limits::min(); + for (const auto& rect : rectangles) { + left = min(left, rect[L]); + bottom = min(bottom, rect[B]); + right = max(right, rect[R]); + top = max(top, rect[T]); + } + + using P = pair, int>; + enum Corner {LB = 1, RB = 2, LT = 4, RT = 8}; + unordered_map> corner_count; + vector

corners{{{L, B}, LB}, {{R, B}, RB}, {{L, T}, LT}, {{R, T}, RT}}; + for (const auto& rect : rectangles) { + for (const auto& corner : corners) { + const auto x = rect[corner.first.first]; + const auto y = rect[corner.first.second]; + if (corner_count[x][y] & corner.second) { + return false; + } + corner_count[x][y] |= corner.second; + } + } + + bitset<16> is_valid; + is_valid[LB | RB] = is_valid[LB | LT] = is_valid[RB | RT] = is_valid[LT | RT] = is_valid[LB | RB | LT | RT] = true; + for (auto itx = corner_count.cbegin(); itx != corner_count.cend(); ++itx) { + const auto x = itx->first; + for (auto ity = itx->second.cbegin(); ity != itx->second.cend(); ++ity) { + const auto y = ity->first; + const auto mask = ity->second; + if ((left < x && x < right) || (bottom < y && y < top)) { + if (!is_valid[mask]) { + return false; + } + } + } + } + + return true; + } +}; diff --git a/C++/perfect-squares.cpp b/C++/perfect-squares.cpp new file mode 100644 index 000000000..15188df34 --- /dev/null +++ b/C++/perfect-squares.cpp @@ -0,0 +1,33 @@ +// Time: O(n * sqrt(n)) +// Space: O(n) + +class Solution { +public: + int numSquares(int n) { + static vector num{0}; + while (num.size() <= n) { + int squares = numeric_limits::max(); + for (int i = 1; i * i <= num.size(); ++i) { + squares = min(squares, num[num.size() - i * i] + 1); + } + num.emplace_back(squares); + } + return num[n]; + } +}; + +// Time: O(n * sqrt(n)) +// Space: O(n) +class Solution2 { +public: + int numSquares(int n) { + vector num(n + 1, numeric_limits::max()); + num[0] = 0; + for (int i = 0; i <= n; ++i) { + for (int j = i - 1, k = 1; j >= 0; ++k, j = i - k * k) { + num[i] = min(num[i], num[j] + 1); + } + } + return num[n]; + } +}; diff --git a/C++/permutation-in-string.cpp b/C++/permutation-in-string.cpp new file mode 100644 index 000000000..daaf56889 --- /dev/null +++ b/C++/permutation-in-string.cpp @@ -0,0 +1,25 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + bool checkInclusion(string s1, string s2) { + vector counts(256); + for (const auto& c : s1) { + ++counts[c]; + } + for (int i = 0, l = s1.length(); i < s2.length(); ++i) { + if (counts[s2[i]]-- > 0) { + --l; + } + if (l == 0) { + return true; + } + int start = i + 1 - s1.length(); + if (start >= 0 && ++counts[s2[start]] > 0) { + ++l; + } + } + return false; + } +}; diff --git a/C++/permutation-sequence.cpp b/C++/permutation-sequence.cpp new file mode 100644 index 000000000..c3a4e3c98 --- /dev/null +++ b/C++/permutation-sequence.cpp @@ -0,0 +1,33 @@ +// Time: O(n^2) +// Space: O(n) + +class Solution { +public: + string getPermutation(int n, int k) { + vector nums; + int total = 1; + for (int i = 1; i <= n; ++i) { + nums.emplace_back(i); + total *= i; + } + + // Cantor Ordering: + // Construct the k-th permutation with a list of n numbers + // Idea: group all permutations according to their first number (so n groups, each of + // (n - 1)! numbers), find the group where the k-th permutation belongs, remove the common + // first number from the list and append it to the resulting string, and iteratively + // construct the (((k - 1) % (n - 1)!) + 1)-th permutation with the remaining n-1 numbers + int group = total; + stringstream permutation; + while (n > 0) { + group /= n; + int idx = (k - 1) / group; + permutation << nums[idx]; + nums.erase(nums.begin() + idx); + k = (k - 1) % group + 1; + --n; + } + + return permutation.str(); + } +}; diff --git a/C++/plus-one-linked-list.cpp b/C++/plus-one-linked-list.cpp new file mode 100644 index 000000000..f7e097520 --- /dev/null +++ b/C++/plus-one-linked-list.cpp @@ -0,0 +1,88 @@ +// Time: O(n) +// Space: O(1) + +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode(int x) : val(x), next(NULL) {} + * }; + */ + // Two pointers solution. +class Solution { +public: + ListNode* plusOne(ListNode* head) { + if (!head) { + return nullptr; + } + + auto dummy = new ListNode{0}; + dummy->next = head; + + auto left = dummy, right = head; + while (right->next) { + if (right->val != 9) { + left = right; + } + right = right->next; + } + + if (right->val != 9) { + ++right->val; + } else { + ++left->val; + right = left->next; + while (right) { + right->val = 0; + right = right->next; + } + } + + if (dummy->val == 0) { + head = dummy->next; + delete dummy; + return head; + } + + return dummy; + } +}; + +// Time: O(n) +// Space: O(1) +class Solution2 { +public: + ListNode* plusOne(ListNode* head) { + auto rev_head = reverseList(head); + + auto curr = rev_head; + int carry = 1; + while (curr && carry) { + curr->val += carry; + carry = curr->val / 10; + curr->val %= 10; + if (carry && !curr->next) { + curr->next = new ListNode(0); + } + curr = curr->next; + } + + return reverseList(rev_head); + } + +private: + ListNode* reverseList(ListNode* head) { + ListNode dummy{0}; + auto curr = head; + + while (curr) { + auto tmp = curr->next; + curr->next = dummy.next; + dummy.next = curr; + curr = tmp; + } + + return dummy.next; + } +}; diff --git a/C++/plus-one.cpp b/C++/plus-one.cpp new file mode 100644 index 000000000..c66fb4d2b --- /dev/null +++ b/C++/plus-one.cpp @@ -0,0 +1,40 @@ +// Time: O(n) +// Space: O(1) + +// in-place solution +class Solution { +public: + vector plusOne(vector& digits) { + for (int i = digits.size() - 1; i >= 0; --i) { + if (digits[i] == 9) { + digits[i] = 0; + } else { + ++digits[i]; + return digits; + } + } + digits[0] = 1; + digits.emplace_back(0); + return digits; + } +}; + +// Time: O(n) +// Space: O(n) +class Solution2 { +public: + vector plusOne(vector& digits) { + vector result(digits.rbegin(), digits.rend()); + int carry = 1; + for (auto& num : result) { + num += carry; + carry = num / 10; + num %= 10; + } + if (carry == 1) { + result.emplace_back(carry); + } + reverse(result.begin(), result.end()); + return result; + } +}; diff --git a/C++/plusOne.cpp b/C++/plusOne.cpp deleted file mode 100644 index d158b2a77..000000000 --- a/C++/plusOne.cpp +++ /dev/null @@ -1,21 +0,0 @@ -// Time Complexity: O(n) -// Space Complexity: O(1) - -class Solution { - public: - vector plusOne(vector &digits) { - int c = 1; - - for(auto it = digits.rbegin(); it != digits.rend(); ++it) { - *it += c; - c = *it / 10; - *it %= 10; - } - - if(c > 0) { - digits.insert(digits.begin(), 1); - } - - return digits; - } -}; diff --git a/C++/poor-pigs.cpp b/C++/poor-pigs.cpp new file mode 100644 index 000000000..bee9ffa4b --- /dev/null +++ b/C++/poor-pigs.cpp @@ -0,0 +1,9 @@ +// Time: O(1) +// Space: O(1) + +class Solution { +public: + int poorPigs(int buckets, int minutesToDie, int minutesToTest) { + return ceil(log(buckets) / log(minutesToTest / minutesToDie + 1)); + } +}; diff --git a/C++/positions-of-large-groups.cpp b/C++/positions-of-large-groups.cpp new file mode 100644 index 000000000..0395ccdab --- /dev/null +++ b/C++/positions-of-large-groups.cpp @@ -0,0 +1,18 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + vector> largeGroupPositions(string S) { + vector> result; + for (int i = 0, j = 0; j < S.length(); ++j) { + if (j == S.length() - 1 || S[j] != S[j + 1]) { + if (j - i + 1 >= 3) { + result.emplace_back(vector{i, j}); + } + i = j + 1; + } + } + return result; + } +}; diff --git a/C++/pour-water.cpp b/C++/pour-water.cpp new file mode 100644 index 000000000..3d24b2d9c --- /dev/null +++ b/C++/pour-water.cpp @@ -0,0 +1,26 @@ +// Time: O(v * n) +// Space: O(1) + +class Solution { +public: + vector pourWater(vector& heights, int V, int K) { + for (; V > 0; --V) { + int best = K; + for (const auto& d : {-1, 1}) { + int i = K; + while (0 <= i + d && i + d < heights.size() && + heights[i + d] <= heights[i]) { + if (heights[i + d] < heights[i]) { + best = i + d; + } + i += d; + } + if (best != K) { + break; + } + } + ++heights[best]; + } + return heights; + } +}; diff --git a/C++/pow.cpp b/C++/pow.cpp deleted file mode 100644 index 4877d7443..000000000 --- a/C++/pow.cpp +++ /dev/null @@ -1,24 +0,0 @@ -// Time Complexity: O(logn) -// Space Complexity: O(logn) - -class Solution { - public: - double pow(double x, int n) { - if(n < 0) - return 1.0 / power(x, -n); // be careful: -1 * -2147483648 is still -2147483648 - else - return power(x, n); - } - - private: - double power(double x, int n) { - if(n == 0) - return 1; - double v = power(x, n / 2); - - if(n % 2 != 0) - return v * v * x; - else - return v * v; - } -}; diff --git a/C++/power-of-four.cpp b/C++/power-of-four.cpp new file mode 100644 index 000000000..0bcca2d46 --- /dev/null +++ b/C++/power-of-four.cpp @@ -0,0 +1,22 @@ +// Time: O(1) +// Space: O(1) + +class Solution { +public: + bool isPowerOfFour(int num) { + return num > 0 && (num & (num - 1)) == 0 && + ((num & 0b01010101010101010101010101010101) == num); + } +}; + +// Time: O(1) +// Space: O(1) +class Solution2 { +public: + bool isPowerOfFour(int num) { + while (num && !(num & 0b11)) { + num >>= 2; + } + return (num == 1); + } +}; diff --git a/C++/power-of-three.cpp b/C++/power-of-three.cpp new file mode 100644 index 000000000..73f196331 --- /dev/null +++ b/C++/power-of-three.cpp @@ -0,0 +1,12 @@ +// Time: O(1) +// Space: O(1) + +class Solution { +public: + static const int max_log3 = log(numeric_limits::max()) / log(3); + static const int max_pow3 = pow(3, max_log3); + + bool isPowerOfThree(int n) { + return n > 0 && max_pow3 % n == 0; + } +}; diff --git a/C++/power-of-two.cpp b/C++/power-of-two.cpp new file mode 100644 index 000000000..d0261cad9 --- /dev/null +++ b/C++/power-of-two.cpp @@ -0,0 +1,16 @@ +// Time: O(1) +// Space: O(1) + +class Solution { +public: + bool isPowerOfTwo(int n) { + return n > 0 && (n & (n - 1)) == 0; + } +}; + +class Solution2 { +public: + bool isPowerOfTwo(int n) { + return n > 0 && (n & ~-n) == 0; + } +}; diff --git a/C++/powx-n.cpp b/C++/powx-n.cpp new file mode 100644 index 000000000..652600ee8 --- /dev/null +++ b/C++/powx-n.cpp @@ -0,0 +1,40 @@ +// Time: O(logn) = O(1) +// Space: O(1) + +// Iterative solution. +class Solution { +public: + double myPow(double x, int n) { + double result = 1; + long long abs_n = abs(static_cast(n)); + while (abs_n > 0) { + if (abs_n & 1) { + result *= x; + } + abs_n >>= 1; + x *= x; + } + return n < 0 ? 1 / result : result; + } +}; + +// Time: O(logn) = O(1) +// Space: O(logn) = O(1) +// Recursive solution. +class Solution2 { +public: + double myPow(double x, int n) { + if (n < 0 && n != -n) { + return 1.0 / myPow(x, -n); + } + if (n == 0) { + return 1; + } + double v = myPow(x, n / 2); + if (n % 2 == 0) { + return v * v; + } else { + return v * v * x; + } + } +}; diff --git a/C++/predict-the-winner.cpp b/C++/predict-the-winner.cpp new file mode 100644 index 000000000..4cda5afd5 --- /dev/null +++ b/C++/predict-the-winner.cpp @@ -0,0 +1,21 @@ +// Time: O(n^2) +// Space: O(n) + +class Solution { +public: + bool PredictTheWinner(vector& nums) { + if (nums.size() % 2 == 0 || nums.size() == 1) { + return true; + } + + vector dp(nums.size()); + for (int i = nums.size() - 1; i >= 0; --i) { + dp[i] = nums[i]; + for (int j = i + 1; j < nums.size(); ++j) { + dp[j] = max(nums[i] - dp[j], nums[j] - dp[j - 1]); + } + } + + return dp.back() >= 0; + } +}; diff --git a/C++/prefix-and-suffix-search.cpp b/C++/prefix-and-suffix-search.cpp new file mode 100644 index 000000000..644b4378d --- /dev/null +++ b/C++/prefix-and-suffix-search.cpp @@ -0,0 +1,139 @@ +// Time: ctor: O(w * l^2), w is the number of words, l is the word length on average +// search: O(p + s) , p is the length of the prefix, s is the length of the suffix, +// Space: O(t), t is the number of trie nodes + +class WordFilter { +public: + WordFilter(vector words) { + for (int i = 0; i < words.size(); ++i) { + for (int j = 0; j <= words[i].length(); ++j) { + trie_.insert(words[i].substr(j) + SEPARATOR + words[i], i); + } + } + } + + int f(string prefix, string suffix) { + return trie_.find(suffix + SEPARATOR + prefix); + } + +private: + struct TrieNode { + int weight; + vector leaves; + + TrieNode() : weight(0), leaves(27) {} + + void insert(const string& s, const int weight) { + auto* p = this; + p->weight = weight; + for (const auto& c : s) { + if (!p->leaves[c - 'a']) { + p->leaves[c - 'a'] = new TrieNode; + } + p = p->leaves[c - 'a']; + p->weight = weight; + } + } + + int find(const string& s) const { + auto* p = this; + for (const auto& c : s) { + if (!p->leaves[c - 'a']) { + return -1; + } + p = p->leaves[c - 'a']; + } + return p->weight; + } + + ~TrieNode() { + for (auto& node : leaves) { + if (node) { + delete node; + } + } + } + }; + const string SEPARATOR = "{"; // ascii code of 'z' + 1 + TrieNode trie_; +}; + +// Time: ctor: O(w * l), w is the number of words, l is the word length on average +// search: O(p + s + max(m, n)), p is the length of the prefix, s is the length of the suffix, +// m is the number of the prefix match, n is the number of the suffix match +// Space: O(w * l) +class WordFilter2 { +public: + WordFilter(vector words) { + for (int i = words.size() - 1; i >= 0; --i) { + auto word = words[i]; + prefix_trie_.insert(word, i); + reverse(word.begin(), word.end()); + suffix_trie_.insert(word, i); + } + } + + int f(string prefix, string suffix) { + const auto& prefix_match = prefix_trie_.find(prefix); + reverse(suffix.begin(), suffix.end()); + const auto& suffix_match = suffix_trie_.find(suffix); + int i = 0, j = 0; + while (i != prefix_match.size() && j != suffix_match.size()) { + if (prefix_match[i] == suffix_match[j]) { + return prefix_match[i]; + } else if (prefix_match[i] > suffix_match[j]) { + ++i; + } else { + ++j; + } + } + return -1; + } + +private: + struct TrieNode { + vector words; // index of words + vector leaves; + + TrieNode() : leaves(26) {} + + void insert(const string& s, const int i) { + auto* p = this; + p->words.emplace_back(i); + for (const auto& c : s) { + if (!p->leaves[c - 'a']) { + p->leaves[c - 'a'] = new TrieNode; + } + p = p->leaves[c - 'a']; + p->words.emplace_back(i); + } + } + + const vector& find(const string& s) const { + auto* p = this; + for (const auto& c : s) { + if (!p->leaves[c - 'a']) { + static const vector empty; + return empty; + } + p = p->leaves[c - 'a']; + } + return p->words; + } + + ~TrieNode() { + for (auto& node : leaves) { + if (node) { + delete node; + } + } + } + }; + TrieNode prefix_trie_, suffix_trie_; +}; + +/** + * Your WordFilter object will be instantiated and called as such: + * WordFilter obj = new WordFilter(words); + * int param_1 = obj.f(prefix,suffix); + */ diff --git a/C++/preimage-size-of-factorial-zeroes-function.cpp b/C++/preimage-size-of-factorial-zeroes-function.cpp new file mode 100644 index 000000000..3f02ab71f --- /dev/null +++ b/C++/preimage-size-of-factorial-zeroes-function.cpp @@ -0,0 +1,28 @@ +// Time: O((logn)^2) +// Space: O(1) + +class Solution { +public: + int preimageSizeFZF(int K) { + const int p = 5; + int left = 0, right = p * K; + while (left <= right) { + const int mid = left + (right - left) / 2; + if (countOfFactorialPrimes(mid, p) >= K) { + right = mid - 1; + } else { + left = mid + 1; + } + } + return countOfFactorialPrimes(left, p) == K ? p : 0; + } + +private: + int countOfFactorialPrimes(int n, int p) { + int cnt = 0; + for (; n > 0; n /= p) { + cnt += n / p; + } + return cnt; + } +}; diff --git a/C++/prime-number-of-set-bits-in-binary-representation.cpp b/C++/prime-number-of-set-bits-in-binary-representation.cpp new file mode 100644 index 000000000..539d9d281 --- /dev/null +++ b/C++/prime-number-of-set-bits-in-binary-representation.cpp @@ -0,0 +1,23 @@ +// Time: O(log(R - L)) = O(1) +// Space: O(1) + +class Solution { +public: + int countPrimeSetBits(int L, int R) { + static const unordered_set primes{2, 3, 5, 7, 11, 13, 17, 19}; + int result = 0; + for (int i = L; i <= R; ++i) { + result += primes.count(bitCount(i)); + } + return result; + } + +private: + int bitCount(uint32_t n) { + int count = 0; + for (; n; n &= n - 1) { + ++count; + } + return count; + } +}; diff --git a/C++/prime-palindrome.cpp b/C++/prime-palindrome.cpp new file mode 100644 index 000000000..c869a3942 --- /dev/null +++ b/C++/prime-palindrome.cpp @@ -0,0 +1,33 @@ +// Time: O(n^(3/2)) +// Space: O(logn) + +class Solution { +public: + int primePalindrome(int N) { + if (8 <= N && N <= 11) { + // any palindrome with even digits is multiple of 11 + return 11; + } + for (int i = to_string(N).length() / 2; i < 100000; ++i) { + const string s = to_string(i), rev_s(s.rbegin(), s.rend()); + int j = stoi(s + rev_s.substr(1)); + if (j >= N && isPrime(j)) { + return j; + } + } + return -1; + } + +private: + bool isPrime(int num) { + if (num < 2 || num % 2 == 0) { + return num == 2; + } + for (int i = 3; i * i <= num; i += 2) { + if (num % i == 0) { + return false; + } + } + return true; + } +}; diff --git a/C++/print-binary-tree.cpp b/C++/print-binary-tree.cpp new file mode 100644 index 000000000..12f1ac47d --- /dev/null +++ b/C++/print-binary-tree.cpp @@ -0,0 +1,46 @@ +// Time: O(h * 2^h) +// Space: O(h * 2^h) + +/** + * 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: + vector> printTree(TreeNode* root) { + auto h = getHeight(root), w = getWidth(root); + vector> result(h, vector(w, "")); + preorderTraversal(root, 0, 0, w - 1, &result); + return result; + } + +private: + int getHeight(TreeNode* root) { + if (!root) { + return 0; + } + return max(getHeight(root->left), getHeight(root->right)) + 1; + } + + int getWidth(TreeNode* root) { + if (!root) { + return 0; + } + return 2 * max(getWidth(root->left), getWidth(root->right)) + 1; + } + + void preorderTraversal(TreeNode *root, int level, int left, int right, vector> *result) { + if (!root) { + return; + } + auto mid = left + (right - left) / 2; + (*result)[level][mid] = to_string(root->val); + preorderTraversal(root->left, level + 1, left, mid - 1, result); + preorderTraversal(root->right, level + 1, mid + 1, right, result); + } +}; diff --git a/C++/product-of-array-except-self.cpp b/C++/product-of-array-except-self.cpp new file mode 100644 index 000000000..0d516b10e --- /dev/null +++ b/C++/product-of-array-except-self.cpp @@ -0,0 +1,26 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + vector productExceptSelf(vector& nums) { + if (nums.empty()) { + return {}; + } + + vector left_product(nums.size()); + + left_product[0] = 1; + for (int i = 1; i < nums.size(); ++i) { + left_product[i] = left_product[i - 1] * nums[i - 1]; + } + + int right_product = 1; + for (int i = static_cast(nums.size()) - 2; i >= 0; --i) { + right_product *= nums[i + 1]; + left_product[i] = left_product[i] * right_product; + } + + return left_product; + } +}; diff --git a/C++/profitable-schemes.cpp b/C++/profitable-schemes.cpp new file mode 100644 index 000000000..146e78531 --- /dev/null +++ b/C++/profitable-schemes.cpp @@ -0,0 +1,22 @@ +// Time: O(n * p * g) +// Space: O(p * g) + +class Solution { +public: + int profitableSchemes(int G, int P, vector& group, vector& profit) { + static const int M = 1000000007; + vector> dp(P + 1, vector(G + 1)); + dp[0][0] = 1; + int result = 0; + for (int k = 0; k < group.size(); ++k) { + int g = group[k], p = profit[k]; + for (int i = P; i >= 0; --i) + for (int j = G - g; j >= 0; --j) + dp[min(i + p, P)][j + g] = (dp[min(i + p, P)][j + g] + dp[i][j]) % M; + } + for (const auto& p : dp[P]) { + result = (result + p) % M; + } + return result; + } +}; diff --git a/C++/push-dominoes.cpp b/C++/push-dominoes.cpp new file mode 100644 index 000000000..33779062d --- /dev/null +++ b/C++/push-dominoes.cpp @@ -0,0 +1,38 @@ +// Time: O(n) +// Space: O(n) + +class Solution { +public: + string pushDominoes(string dominoes) { + vector force(dominoes.length()); + + int f = 0; + for (int i = 0; i < dominoes.length(); ++i) { + if (dominoes[i] == 'R') { + f = dominoes.length(); + } else if (dominoes[i] == 'L') { + f = 0; + } else { + f = max(f - 1, 0); + } + force[i] += f; + } + + f = 0; + for (int i = dominoes.length() - 1; i >= 0; --i) { + if (dominoes[i] == 'L') { + f = dominoes.length(); + } else if (dominoes[i] == 'R') { + f = 0; + } else { + f = max(f - 1, 0); + } + force[i] -= f; + } + string result; + for (const auto& f : force) { + result.push_back((f == 0) ? '.' : ((f > 0) ? 'R' : 'L')); + } + return result; + } +}; diff --git a/C++/pyramid-transition-matrix.cpp b/C++/pyramid-transition-matrix.cpp new file mode 100644 index 000000000..75d7825df --- /dev/null +++ b/C++/pyramid-transition-matrix.cpp @@ -0,0 +1,49 @@ +// Time: O((a^(b+1)-a)/(a-1)) = O(a^b) , a is the size of allowed, +// b is the length of bottom +// Space: O((a^(b+1)-a)/(a-1)) = O(a^b) + +// dfs solution +class Solution { +public: + bool pyramidTransition(string bottom, vector& allowed) { + vector>> edges(7, vector>(7)); + unordered_set lookup; + for (const auto& s: allowed) { + edges[s[0] - 'A'][s[1] - 'A'].emplace_back(s[2] - 'A'); + } + return pyramidTransitionHelper(bottom, edges, &lookup); + } + +private: + bool pyramidTransitionHelper(const string& bottom, const vector>>& edges, + unordered_set *lookup) { + if (bottom.size() == 1) { + return true; + } + for (int i = 0; i < bottom.size() - 1; ++i) { + if (edges[bottom[i] - 'A'][bottom[i + 1] - 'A'].empty()) { + return false; + } + } + if (lookup->count(bottom)) { + return false; + } + lookup->emplace(bottom); + string new_bottom(bottom.size() - 1, 'A'); + return dfs(bottom, edges, &new_bottom, 0, lookup); + } + + bool dfs(const string& bottom, const vector>>& edges, string *new_bottom, int idx, + unordered_set *lookup) { + if (idx == bottom.size() - 1) { + return pyramidTransitionHelper(*new_bottom, edges, lookup); + } + for (const auto& i : edges[bottom[idx] - 'A'][bottom[idx + 1] - 'A']) { + (*new_bottom)[idx] = i + 'A'; + if (dfs(bottom, edges, new_bottom, idx + 1, lookup)) { + return true; + } + } + return false; + } +}; diff --git a/C++/quad-tree-intersection.cpp b/C++/quad-tree-intersection.cpp new file mode 100644 index 000000000..c3684e0f2 --- /dev/null +++ b/C++/quad-tree-intersection.cpp @@ -0,0 +1,48 @@ +// Time: O(n) +// Space: O(h) + +/* +// Definition for a QuadTree node. +class Node { +public: + bool val; + bool isLeaf; + Node* topLeft; + Node* topRight; + Node* bottomLeft; + Node* bottomRight; + + Node() {} + + Node(bool _val, bool _isLeaf, Node* _topLeft, Node* _topRight, Node* _bottomLeft, Node* _bottomRight) { + val = _val; + isLeaf = _isLeaf; + topLeft = _topLeft; + topRight = _topRight; + bottomLeft = _bottomLeft; + bottomRight = _bottomRight; + } +}; +*/ +class Solution { +public: + Node* intersect(Node* quadTree1, Node* quadTree2) { + if (quadTree1->isLeaf) { + return quadTree1->val ? quadTree1 : quadTree2; + } else if (quadTree2->isLeaf) { + return quadTree2->val ? quadTree2 : quadTree1; + } + auto topLeftNode = intersect(quadTree1->topLeft, quadTree2->topLeft); + auto topRightNode = intersect(quadTree1->topRight, quadTree2->topRight); + auto bottomLeftNode = intersect(quadTree1->bottomLeft, quadTree2->bottomLeft); + auto bottomRightNode = intersect(quadTree1->bottomRight, quadTree2->bottomRight); + if (topLeftNode->isLeaf && topRightNode->isLeaf && + bottomLeftNode->isLeaf && bottomRightNode->isLeaf && + topLeftNode->val == topRightNode->val && + topRightNode->val == bottomLeftNode->val && + bottomLeftNode->val == bottomRightNode->val) { + return new Node(topLeftNode->val, true, nullptr, nullptr, nullptr, nullptr); + } + return new Node(true, false, topLeftNode, topRightNode, bottomLeftNode, bottomRightNode); + } +}; diff --git a/C++/queue-reconstruction-by-height.cpp b/C++/queue-reconstruction-by-height.cpp new file mode 100644 index 000000000..0f5bf98cf --- /dev/null +++ b/C++/queue-reconstruction-by-height.cpp @@ -0,0 +1,57 @@ +// Time: O(n * sqrt(n)) +// Space: O(n) + +class Solution { +public: + vector> reconstructQueue(vector>& people) { + sort(people.begin(), people.end(), + [](const pair& a, pair& b) { + return b.first == a.first ? a.second < b.second : b.first < a.first; + }); + + vector>> blocks(1); + for (const auto& p : people) { + auto index = p.second; + int i = 0; + for (; i < blocks.size(); ++i) { + if (index <= blocks[i].size()) { + break; + } + index -= blocks[i].size(); + } + blocks[i].emplace(blocks[i].begin() + index, p); + + if (blocks[i].size() * blocks[i].size() > people.size()) { + blocks.emplace(blocks.begin() + i + 1, + blocks[i].begin() + blocks[i].size() / 2, + blocks[i].end()); + blocks[i].erase(blocks[i].begin() + blocks[i].size() / 2, blocks[i].end()); + } + } + + vector> result; + for (const auto& block : blocks) { + for (const auto& p : block) { + result.emplace_back(p); + } + } + return result; + } +}; + +// Time: O(n^2) +// Space: O(n) +class Solution2 { +public: + vector> reconstructQueue(vector>& people) { + sort(people.begin(), people.end(), + [](const pair& a, pair& b) { + return b.first == a.first ? a.second < b.second : b.first < a.first; + }); + vector> result; + for (const auto& p : people) { + result.emplace(result.begin() + p.second, p); + } + return result; + } +}; diff --git a/C++/rabbits-in-forest.cpp b/C++/rabbits-in-forest.cpp new file mode 100644 index 000000000..5e0740939 --- /dev/null +++ b/C++/rabbits-in-forest.cpp @@ -0,0 +1,17 @@ +// Time: O(n) +// Space: O(n) + +class Solution { +public: + int numRabbits(vector& answers) { + unordered_map lookup; + for (const auto& answer : answers) { + ++lookup[answer]; + } + int result = 0; + for (const auto& kvp : lookup) { + result += ((kvp.first + 1) + kvp.second - 1) / (kvp.first + 1) * (kvp.first + 1); + } + return result; + } +}; diff --git a/C++/race-car.cpp b/C++/race-car.cpp new file mode 100644 index 000000000..54fab1a51 --- /dev/null +++ b/C++/race-car.cpp @@ -0,0 +1,35 @@ +// Time : O(nlogn), n is the value of the target +// Space: O(n) + +class Solution { +public: + int racecar(int target) { + vector dp(target + 1); + for (int i = 1; i <= target; ++i) { + int k = bitLength(i); + if (i == (1 << k) - 1) { + dp[i] = k; + continue; + } + dp[i] = dp[(1 << k) - 1 - i] + k + 1; + for (int j = 0; j < k; ++j) { + dp[i] = min(dp[i], dp[i - (1 << (k - 1)) + (1 << j)] + k + j + 1); + } + } + return dp.back(); + } + +private: + uint32_t bitLength(uint32_t n) { + uint32_t left = 1, right = 32; + while (left <= right) { + auto mid = left + (right - left) / 2; + if ((1 << mid) > n) { + right = mid - 1; + } else { + left = mid + 1; + } + } + return left; + } +}; diff --git a/C++/random-flip-matrix.cpp b/C++/random-flip-matrix.cpp new file mode 100644 index 000000000..f4c06efbb --- /dev/null +++ b/C++/random-flip-matrix.cpp @@ -0,0 +1,47 @@ +// Time: ctor: O(1) +// flip: O(1) +// reset: O(min(f, r * c)) +// Space: O(min(f, r * c)) + +class Solution { +public: + Solution(int n_rows, int n_cols) : + n_rows_(n_rows), + n_cols_(n_cols), + n_(n_rows * n_cols), + gen_{(random_device())()} { + + } + + vector flip() { + uniform_int_distribution uni(0, --n_); + const auto target = uni(gen_); + int x = get(target, target); + lookup_[target] = get(n_, n_); + return {x / n_cols_, x % n_cols_}; + } + + void reset() { + lookup_.clear(); + n_ = n_rows_ * n_cols_; + } + +private: + int get(int key, int default_value) { + return lookup_.count(key) ? lookup_[key] : default_value; + } + + int n_rows_; + int n_cols_; + int n_; + unordered_map lookup_; + default_random_engine gen_; +}; + +/** + * Your Solution object will be instantiated and called as such: + * Solution obj = new Solution(n_rows, n_cols); + * vector param_1 = obj.flip(); + * obj.reset(); + */ + diff --git a/C++/random-pick-index.cpp b/C++/random-pick-index.cpp new file mode 100644 index 000000000..27e16a51c --- /dev/null +++ b/C++/random-pick-index.cpp @@ -0,0 +1,32 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + Solution(vector nums) : nums_(nums) { + + } + + int pick(int target) { + auto reservoir = -1; + auto n = 0; + for (int i = 0; i < nums_.size(); ++i) { + if (nums_[i] != target) { + continue; + } + if (rand() % ++n == 0) { + reservoir = i; + } + } + return reservoir; + } + +private: + const vector nums_; +}; + +/** + * Your Solution object will be instantiated and called as such: + * Solution obj = new Solution(nums); + * int param_1 = obj.pick(target); + */ diff --git a/C++/random-pick-with-blacklist.cpp b/C++/random-pick-with-blacklist.cpp new file mode 100644 index 000000000..5bc64a9c6 --- /dev/null +++ b/C++/random-pick-with-blacklist.cpp @@ -0,0 +1,69 @@ +// Time: ctor: O(b) +// pick: O(1) +// Space: O(b) + +class Solution { +public: + Solution(int N, vector blacklist) : + n_(N - blacklist.size()) { + unordered_set whitelist; + for (int i = n_; i < N; ++i) { + whitelist.emplace(i); + } + for (const auto& black : blacklist) { + whitelist.erase(black); + } + auto white = whitelist.cbegin(); + for (const auto& black : blacklist) { + if (black < n_) { + lookup_[black] = *(white++); + } + } + } + + int pick() { + int index = rand() % n_; + return lookup_.count(index) ? lookup_[index] : index; + } + +private: + int n_; + unordered_map lookup_; +}; + +// Time: ctor: O(blogb) +// pick: O(logb) +// Space: O(b) +class Solution2 { +public: + Solution(int N, vector blacklist) : + n_(N - blacklist.size()), + blacklist_(blacklist) { + + sort(blacklist_.begin(), blacklist_.end()); + } + + int pick() { + int index = rand() % n_; + int left = 0, right = blacklist_.size() - 1; + while (left <= right) { + auto mid = left + (right - left) / 2; + if (index + mid < blacklist_[mid]) { + right = mid - 1; + } else { + left = mid + 1; + } + } + return index + left; + } + +private: + int n_; + vector blacklist_; +}; + +/** + * Your Solution object will be instantiated and called as such: + * Solution obj = new Solution(N, blacklist); + * int param_1 = obj.pick(); + */ diff --git a/C++/random-pick-with-weight.cpp b/C++/random-pick-with-weight.cpp new file mode 100644 index 000000000..7715ae725 --- /dev/null +++ b/C++/random-pick-with-weight.cpp @@ -0,0 +1,31 @@ +// Time: ctor: O(n) +// pickIndex: O(logn) +// Space: O(n) + +class Solution { +public: + Solution(vector w) : + prefix_sum_(w.size()), + uni_((random_device())()) { + + partial_sum(w.cbegin(), w.cend(), prefix_sum_.begin(), plus()); + uni_ = uniform_int_distribution{0, prefix_sum_.back() - 1}; + } + + int pickIndex() { + const auto target = uni_(gen_); + return distance(prefix_sum_.cbegin(), + upper_bound(prefix_sum_.cbegin(), prefix_sum_.cend(), target)); + } + +private: + vector prefix_sum_; + default_random_engine gen_; + uniform_int_distribution uni_; +}; + +/** + * Your Solution object will be instantiated and called as such: + * Solution obj = new Solution(w); + * int param_1 = obj.pickIndex(); + */ diff --git a/C++/random-point-in-non-overlapping-rectangles.cpp b/C++/random-point-in-non-overlapping-rectangles.cpp new file mode 100644 index 000000000..b43511b30 --- /dev/null +++ b/C++/random-point-in-non-overlapping-rectangles.cpp @@ -0,0 +1,48 @@ +// Time: ctor: O(n) +// pick: O(logn) +// Space: O(n) + +class Solution { +public: + Solution(vector> rects) : + rects_(move(rects)), + gen_{(random_device())()} { + + for (const auto& rect : rects_) { + const auto width = rect[2] - rect[0] + 1; + const auto height = rect[3] - rect[1] + 1; + if (prefix_sum_.empty()) { + prefix_sum_.emplace_back(width * height); + } else { + prefix_sum_.emplace_back(prefix_sum_.back() + width * height); + } + } + uni_ = uniform_int_distribution{0, prefix_sum_.back() - 1}; + } + + vector pick() { + const auto target = uni_(gen_); + const auto left = distance(prefix_sum_.cbegin(), + upper_bound(prefix_sum_.cbegin(), + prefix_sum_.cend(), + target)); + const auto& rect = rects_[left]; + const auto width = rect[2] - rect[0] + 1; + const auto height = rect[3] - rect[1] + 1; + const auto base = prefix_sum_[left] - width * height; + return {rect[0] + (target - base) % width, rect[1] + (target - base) / width}; + } + +private: + const vector> rects_; + vector prefix_sum_; + default_random_engine gen_; + uniform_int_distribution uni_; +}; + +/** + * Your Solution object will be instantiated and called as such: + * Solution obj = new Solution(rects); + * vector param_1 = obj.pick(); + */ + diff --git a/C++/range-addition-ii.cpp b/C++/range-addition-ii.cpp new file mode 100644 index 000000000..e9218c9d4 --- /dev/null +++ b/C++/range-addition-ii.cpp @@ -0,0 +1,13 @@ +// Time: O(p), p is the number of ops +// Space: O(1) + +class Solution { +public: + int maxCount(int m, int n, vector>& ops) { + for (const auto& op : ops) { + m = min(m, op[0]); + n = min(n, op[1]); + } + return m * n; + } +}; diff --git a/C++/range-addition.cpp b/C++/range-addition.cpp new file mode 100644 index 000000000..0664e622d --- /dev/null +++ b/C++/range-addition.cpp @@ -0,0 +1,22 @@ +// Time: O(k + n) +// Space: O(1) + +class Solution { +public: + vector getModifiedArray(int length, vector>& updates) { + vector result(length, 0); + + for (const auto& update: updates) { + result[update[0]] += update[2]; + if (update[1] + 1 < length) { + result[update[1] + 1] -= update[2]; + } + } + + for (int i = 1; i < length; ++i) { + result[i] += result[i - 1]; + } + + return result; + } +}; diff --git a/C++/range-module.cpp b/C++/range-module.cpp new file mode 100644 index 000000000..53e2c5402 --- /dev/null +++ b/C++/range-module.cpp @@ -0,0 +1,84 @@ +// Time: addRange: O(n) +// removeRange: O(n) +// queryRange: O(logn) +// Space: O(n) + +class RangeModule { +public: + RangeModule() { + + } + + // Time: O(n) + void addRange(int left, int right) { + vector> tmp; + int i = 0; + for (const auto& interval: intervals_) { + if (right < interval.first ) { + tmp.emplace_back(left, right); + break; + } else if (interval.second < left) { + tmp.emplace_back(interval); + } else { + left = min(left, interval.first); + right = max(right, interval.second); + } + ++i; + } + if (i == intervals_.size()) { + tmp.emplace_back(left, right); + } + while (i < intervals_.size()) { + tmp.emplace_back(intervals_[i++]); + } + swap(intervals_, tmp); + } + + // Time: O(logn) + bool queryRange(int left, int right) { + const auto it = lower_bound(intervals_.begin(), intervals_.end(), make_pair(left, right), + [](const pair& lhs, + const pair& rhs) { + return less{}(lhs.second, rhs.first); + }); + return it != intervals_.end() && it->first <= left && it->second >= right; + } + + // Time: O(logn) + bool queryRange2(int left, int right) { + auto it = lower_bound(intervals_.begin(), intervals_.end(), make_pair(left, numeric_limits::max())); + if (it != intervals_.begin()) { + it = prev(it); + } + return it != intervals_.end() && it->first <= left && it->second >= right; + } + + // Time: O(n) + void removeRange(int left, int right) { + vector> tmp; + for (const auto& interval : intervals_) { + if (interval.second <= left || interval.first >= right) { + tmp.emplace_back(interval); + } else { + if (interval.first < left) { + tmp.emplace_back(interval.first, left); + } + if (right < interval.second) { + tmp.emplace_back(right, interval.second); + } + } + } + swap(intervals_, tmp); + } + +private: + vector> intervals_; +}; + +/** + * Your RangeModule object will be instantiated and called as such: + * RangeModule obj = new RangeModule(); + * obj.addRange(left,right); + * bool param_2 = obj.queryRange(left,right); + * obj.removeRange(left,right); + */ diff --git a/C++/range-sum-query-2d-immutable.cpp b/C++/range-sum-query-2d-immutable.cpp new file mode 100644 index 000000000..b38183c27 --- /dev/null +++ b/C++/range-sum-query-2d-immutable.cpp @@ -0,0 +1,41 @@ +// Time: ctor: O(m * n), +// lookup: O(1) +// Space: O(m * n) + +class NumMatrix { +public: + NumMatrix(vector> &matrix) { + if (matrix.empty()) { + return; + } + + const auto m = matrix.size(), n = matrix[0].size(); + for (int i = 0; i <= m; ++i) { + sums_.emplace_back(n + 1, 0); + } + for (int i = 1; i <= m; ++i) { + for (int j = 0; j <= n; ++j) { + sums_[i][j] = sums_[i][j - 1] + matrix[i - 1][j - 1]; + } + } + for (int j = 0; j <= n; ++j) { + for (int i = 1; i <= m; ++i) { + sums_[i][j] += sums_[i - 1][j]; + } + } + } + + int sumRegion(int row1, int col1, int row2, int col2) { + return sums_[row2 + 1][col2 + 1] - sums_[row2 + 1][col1] - + sums_[row1][col2 + 1] + sums_[row1][col1]; + } + +private: + vector> sums_; +}; + + +// Your NumMatrix object will be instantiated and called as such: +// NumMatrix numMatrix(matrix); +// numMatrix.sumRegion(0, 1, 2, 3); +// numMatrix.sumRegion(1, 2, 3, 4); diff --git a/C++/range-sum-query-2d-mutable.cpp b/C++/range-sum-query-2d-mutable.cpp new file mode 100644 index 000000000..391094098 --- /dev/null +++ b/C++/range-sum-query-2d-mutable.cpp @@ -0,0 +1,199 @@ +// Time: ctor: O(m * n), +// update: O(logm + logn), +// query: O(logm + logn) +// Space: O(m * n) + +// Segment Tree solution. +class NumMatrix { +public: + NumMatrix(vector> &matrix) : matrix_(matrix) { + if (!matrix.empty() && !matrix[0].empty()) { + const int m = matrix.size(); + const int n = matrix[0].size(); + root_ = buildHelper(matrix, + make_pair(0, 0), + make_pair(m - 1, n - 1)); + } + } + + void update(int row, int col, int val) { + if (matrix_[row][col] != val) { + matrix_[row][col] = val; + updateHelper(root_, make_pair(row, col), val); + } + } + + int sumRegion(int row1, int col1, int row2, int col2) { + return sumRangeHelper(root_, make_pair(row1, col1), make_pair(row2, col2)); + } + +private: + vector>& matrix_; + + class SegmentTreeNode { + public: + pair start, end; + int sum; + vector neighbor; + SegmentTreeNode(const pair& i, const pair& j, int s) : + start(i), end(j), sum(s) { + } + }; + + SegmentTreeNode *root_; + + // Build segment tree. + SegmentTreeNode *buildHelper(const vector>& matrix, + const pair& start, + const pair& end) { + if (start.first > end.first || start.second > end.second) { + return nullptr; + } + + // The root's start and end is given by build method. + SegmentTreeNode *root = new SegmentTreeNode(start, end, 0); + + // If start equals to end, there will be no children for this node. + if (start == end) { + root->sum = matrix[start.first][start.second]; + return root; + } + + int mid_x = (start.first + end.first) / 2; + int mid_y = (start.second + end.second) / 2; + root->neighbor.emplace_back(buildHelper(matrix, start, make_pair(mid_x, mid_y))); + root->neighbor.emplace_back(buildHelper(matrix, make_pair(start.first, mid_y + 1), make_pair(mid_x, end.second))); + root->neighbor.emplace_back(buildHelper(matrix, make_pair(mid_x + 1, start.second), make_pair(end.first, mid_y))); + root->neighbor.emplace_back(buildHelper(matrix, make_pair(mid_x + 1, mid_y + 1), end)); + for (auto& node : root->neighbor) { + if (node) { + root->sum += node->sum; + } + } + return root; + } + + void updateHelper(SegmentTreeNode *root, const pair& i, int val) { + // Out of range. + if (root == nullptr || + (root->start.first > i.first || root->start.second > i.second) || + (root->end.first < i.first || root->end.second < i.second)) { + return; + } + + // Change the node's value with [i] to the new given value. + if ((root->start.first == i.first && root->start.second == i.second) && + (root->end.first == i.first && root->end.second == i.second)) { + root->sum = val; + return; + } + for (auto& node : root->neighbor) { + updateHelper(node, i, val); + } + + root->sum = 0; + for (auto& node : root->neighbor) { + if (node) { + root->sum += node->sum; + } + } + } + + int sumRangeHelper(SegmentTreeNode *root, const pair& start, const pair& end) { + // Out of range. + if (root == nullptr || + (root->start.first > end.first || root->start.second > end.second) || + (root->end.first < start.first || root->end.second < start.second)) { + return 0; + } + + // Current segment is totally within range [start, end] + if ((root->start.first >= start.first && root->start.second >= start.second) && + (root->end.first <= end.first && root->end.second <= end.second)) { + return root->sum; + } + int sum = 0; + for (auto& node : root->neighbor) { + if (node) { + sum += sumRangeHelper(node, start, end); + } + } + return sum; + } +}; + +// Time: ctor: O(m * n) +// update: O(logm * logn) +// query: O(logm * logn) +// Space: O(m * n) +// Binary Indexed Tree (BIT) solution. +class NumMatrix2 { +public: + NumMatrix(vector> &matrix) : matrix_(matrix) { + if (matrix_.empty()) { + return; + } + bit_ = vector>(matrix_.size() + 1, + vector(matrix_[0].size() + 1)); + for (int i = 1; i < bit_.size(); ++i) { + for (int j = 1; j < bit_[0].size(); ++j) { + bit_[i][j] = matrix[i - 1][j - 1] + bit_[i - 1][j] + + bit_[i][j - 1] - bit_[i - 1][j - 1]; + } + } + for (int i = bit_.size() - 1; i >= 1; --i) { + for (int j = bit_[0].size() - 1; j >= 1; --j) { + int last_i = i - lower_bit(i), last_j = j - lower_bit(j); + bit_[i][j] = bit_[i][j] - bit_[i][last_j] - + bit_[last_i][j] + bit_[last_i][last_j]; + } + } + } + + void update(int row, int col, int val) { + if (val - matrix_[row][col]) { + add(row, col, val - matrix_[row][col]); + matrix_[row][col] = val; + } + } + + int sumRegion(int row1, int col1, int row2, int col2) { + return sum(row2, col2) - sum(row2, col1 - 1) - + sum(row1 - 1, col2) + sum(row1 - 1, col1 - 1); + } + +private: + vector> &matrix_; + vector> bit_; + + int sum(int row, int col) { + ++row, ++col; + int sum = 0; + for (int i = row; i > 0; i -= lower_bit(i)) { + for (int j = col; j > 0; j -= lower_bit(j)) { + sum += bit_[i][j]; + } + } + return sum; + } + + void add(int row, int col, int val) { + ++row, ++col; + for (int i = row; i <= matrix_.size(); i += lower_bit(i)) { + for (int j = col; j <= matrix_[0].size(); j += lower_bit(j)) { + bit_[i][j] += val; + } + } + } + + inline int lower_bit(int i) { + return i & -i; + } +}; + + +// Your NumMatrix object will be instantiated and called as such: +// NumMatrix numMatrix(matrix); +// numMatrix.sumRegion(0, 1, 2, 3); +// numMatrix.update(1, 1, 10); +// numMatrix.sumRegion(1, 2, 3, 4); diff --git a/C++/range-sum-query-immutable.cpp b/C++/range-sum-query-immutable.cpp new file mode 100644 index 000000000..0a8c7e7b9 --- /dev/null +++ b/C++/range-sum-query-immutable.cpp @@ -0,0 +1,26 @@ +// Time: ctor: O(n), +// lookup: O(1) +// Space: O(n) + +class NumArray { +public: + NumArray(vector &nums) { + accu.emplace_back(0); + for (const auto& num : nums) { + accu.emplace_back(accu.back() + num); + } + } + + int sumRange(int i, int j) { + return accu[j + 1] - accu[i]; + } + +private: + vector accu; +}; + + +// Your NumArray object will be instantiated and called as such: +// NumArray numArray(nums); +// numArray.sumRange(0, 1); +// numArray.sumRange(1, 2); diff --git a/C++/range-sum-query-mutable.cpp b/C++/range-sum-query-mutable.cpp new file mode 100644 index 000000000..9119904ba --- /dev/null +++ b/C++/range-sum-query-mutable.cpp @@ -0,0 +1,161 @@ +// Time: ctor: O(n), +// update: O(logn), +// query: O(logn) +// Space: O(n) + +// Binary Indexed Tree (BIT) solution. +class NumArray { +public: + NumArray(vector &nums) : nums_(nums) { + bit_ = vector(nums_.size() + 1); + for (int i = 1; i < bit_.size(); ++i) { + bit_[i] = nums[i - 1] + bit_[i - 1]; + } + for (int i = bit_.size() - 1; i >= 1; --i) { + int last_i = i - lower_bit(i); + bit_[i] -= bit_[last_i]; + } + } + + void update(int i, int val) { + if (val - nums_[i]) { + add(i, val - nums_[i]); + nums_[i] = val; + } + } + + int sumRange(int i, int j) { + return sum(j) - sum(i - 1); + } + +private: + vector &nums_; + vector bit_; + + int sum(int i) { + ++i; + int sum = 0; + for (; i > 0; i -= lower_bit(i)) { + sum += bit_[i]; + } + return sum; + } + + void add(int i, int val) { + ++i; + for (; i <= nums_.size(); i += lower_bit(i)) { + bit_[i] += val; + } + } + + inline int lower_bit(int i) { + return i & -i; + } +}; + +// Time: ctor: O(n), +// update: O(logn), +// query: O(logn) +// Space: O(n) +// Segment Tree solution. +class NumArray2 { +public: + NumArray(vector &nums) : nums_(nums) { + root_ = buildHelper(nums, 0, nums.size() - 1); + } + + void update(int i, int val) { + if (nums_[i] != val) { + nums_[i] = val; + updateHelper(root_, i, val); + } + } + + int sumRange(int i, int j) { + return sumRangeHelper(root_, i, j); + } + +private: + vector& nums_; + + class SegmentTreeNode { + public: + int start, end; + int sum; + SegmentTreeNode *left, *right; + SegmentTreeNode(int i, int j, int s) : + start(i), end(j), sum(s), + left(nullptr), right(nullptr) { + } + }; + + SegmentTreeNode *root_; + + // Build segment tree. + SegmentTreeNode *buildHelper(const vector& nums, int start, int end) { + if (start > end) { + return nullptr; + } + + // The root's start and end is given by build method. + SegmentTreeNode *root = new SegmentTreeNode(start, end, 0); + + // If start equals to end, there will be no children for this node. + if (start == end) { + root->sum = nums[start]; + return root; + } + + // Left child: start=numsleft, end=(numsleft + numsright) / 2. + root->left = buildHelper(nums, start, (start + end) / 2); + + // Right child: start=(numsleft + numsright) / 2 + 1, end=numsright. + root->right = buildHelper(nums, (start + end) / 2 + 1, end); + + // Update sum. + root->sum = (root->left != nullptr ? root->left->sum : 0) + + (root->right != nullptr ? root->right->sum : 0); + return root; + } + + void updateHelper(SegmentTreeNode *root, int i, int val) { + // Out of range. + if (root == nullptr || root->start > i || root->end < i) { + return; + } + + // Change the node's value with [i] to the new given value. + if (root->start == i && root->end == i) { + root->sum = val; + return; + } + + updateHelper(root->left, i, val); + updateHelper(root->right, i, val); + + // Update sum. + root->sum = (root->left != nullptr ? root->left->sum : 0) + + (root->right != nullptr ? root->right->sum : 0); + } + + int sumRangeHelper(SegmentTreeNode *root, int start, int end) { + // Out of range. + if (root == nullptr || root->start > end || root->end < start) { + return 0; + } + + // Current segment is totally within range [start, end] + if (root->start >= start && root->end <= end) { + return root->sum; + } + + return sumRangeHelper(root->left, start, end) + + sumRangeHelper(root->right, start, end); + } +}; + +// Your NumArray object will be instantiated and called as such: +// NumArray numArray(nums); +// numArray.sumRange(0, 1); +// numArray.update(1, 10); +// numArray.sumRange(1, 2); diff --git a/C++/ransom-note.cpp b/C++/ransom-note.cpp new file mode 100644 index 000000000..550b4a3ae --- /dev/null +++ b/C++/ransom-note.cpp @@ -0,0 +1,22 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + bool canConstruct(string ransomNote, string magazine) { + vector counts(26); + int letters = 0; + for (const auto& c : ransomNote) { + if (counts[c - 'a']++ == 0) { + ++letters; + } + } + for (const auto& c : magazine) { + if (--counts[c - 'a'] == 0 && --letters == 0) { + // Break as soon as possible if letters have been enough. + break; + } + } + return letters == 0; + } +}; diff --git a/C++/reach-a-number.cpp b/C++/reach-a-number.cpp new file mode 100644 index 000000000..62fc14ba2 --- /dev/null +++ b/C++/reach-a-number.cpp @@ -0,0 +1,28 @@ +// Time: O(logn) +// Space: O(1) + +class Solution { +public: + int reachNumber(int target) { + target = abs(target); + auto k = static_cast(ceil((-1 + sqrt(1 + 8.0 * target)) / 2)); + target -= k * (k + 1) / 2; + return (target % 2 == 0) ? k : k + 1 + k % 2; + } +}; + + +// Time: O(sqrt(n)) +// Space: O(1) +class Solution2 { +public: + int reachNumber(int target) { + target = abs(target); + int k = 0; + while (target > 0) { + ++k; + target -= k; + } + return (target % 2 == 0) ? k : k + 1 + k % 2; + } +}; diff --git a/C++/reaching-points.cpp b/C++/reaching-points.cpp new file mode 100644 index 000000000..914752f4c --- /dev/null +++ b/C++/reaching-points.cpp @@ -0,0 +1,20 @@ +// Time: O(log(max(m, n))) +// Space: O(1) + +class Solution { +public: + bool reachingPoints(int sx, int sy, int tx, int ty) { + while (tx >= sx && ty >= sy) { + if (tx < ty) { + swap(sx, sy); + swap(tx, ty); + } + if (ty > sy) { + tx %= ty; + } else { + return (tx - sx) % ty == 0; + } + } + return false; + } +}; diff --git a/C++/read-n-characters-given-read4-ii-call-multiple-times.cpp b/C++/read-n-characters-given-read4-ii-call-multiple-times.cpp new file mode 100644 index 000000000..fb53207c8 --- /dev/null +++ b/C++/read-n-characters-given-read4-ii-call-multiple-times.cpp @@ -0,0 +1,31 @@ +// Time: O(n) +// Space: O(1) + +// Forward declaration of the read4 API. +int read4(char *buf); + +class Solution { +public: + /** + * @param buf Destination buffer + * @param n Maximum number of characters to read + * @return The number of characters read + */ + int read(char *buf, int n) { + int i = 0; + while (i < n) { + if (i4_ < n4_) { // Any characters in buf4. + buf[i++] = buf4_[i4_++]; + } else if (n4_ = read4(buf4_)) { // Read more characters. + i4_ = 0; + } else { // Buffer has been empty. + break; + } + } + return i; + } + +private: + char buf4_[4]; + int i4_ = 0, n4_ = 0; +}; diff --git a/C++/read-n-characters-given-read4.cpp b/C++/read-n-characters-given-read4.cpp new file mode 100644 index 000000000..b6d6a93ad --- /dev/null +++ b/C++/read-n-characters-given-read4.cpp @@ -0,0 +1,28 @@ +// Time: O(n) +// Space: O(1) + +int read4(char *buf); + +class Solution { +public: + /** + * @param buf Destination buffer + * @param n Maximum number of characters to read + * @return The number of characters read + */ + int read(char *buf, int n) { + int read_bytes = 0; + char buffer[4]; + for (int i = 0; i <= n / 4; ++i) { + if (int size = read4(buffer)) { + size = min(size, n - read_bytes); + for (int j = 0; j < size; ++j) { + buf[read_bytes++] = buffer[j]; + } + } else { + break; + } + } + return read_bytes; + } +}; diff --git a/C++/rearrange-string-k-distance-apart.cpp b/C++/rearrange-string-k-distance-apart.cpp new file mode 100644 index 000000000..c19221a65 --- /dev/null +++ b/C++/rearrange-string-k-distance-apart.cpp @@ -0,0 +1,81 @@ +// Time: O(n) +// Space: O(n) + +class Solution { +public: + string rearrangeString(string str, int k) { + int cnts [26] = {0}; + for (int i = 0; i < str.length(); ++i) { + ++cnts[str[i] - 'a']; + } + + vector> sorted_cnts; + for (int i = 0; i < 26; ++i) { + sorted_cnts.emplace_back(cnts[i], i + 'a'); + } + sort(sorted_cnts.begin(), sorted_cnts.end(), greater>()); + + const auto max_cnt = sorted_cnts[0].first; + string blocks[max_cnt]; + int i = 0; + for (const auto& cnt : sorted_cnts) { + for (int j = 0; j < cnt.first; ++j) { + blocks[i].push_back(cnt.second); + i = (i + 1) % max(cnt.first, max_cnt - 1); + } + } + + string result; + for (int i = 0; i < max_cnt - 1; ++i) { + if (blocks[i].length() < k) { + return ""; + } else { + result += blocks[i]; + } + } + result += blocks[max_cnt - 1]; + return result; + } +}; + +// Time: O(nlogc), c is the count of unique characters. +// Space: O(c) +class Solution2 { +public: + string rearrangeString(string str, int k) { + if (k == 0) { + return str; + } + + unordered_map cnts; + for (const auto& c : str) { + ++cnts[c]; + } + + priority_queue> heap; + for (const auto& kvp : cnts) { + heap.emplace(kvp.second, kvp.first); + } + + string result; + while (!heap.empty()) { + vector> used_cnt_chars; + int cnt = min(k, static_cast(str.length() - result.length())); + for (int i = 0; i < cnt; ++i) { + if (heap.empty()) { + return ""; + } + auto cnt_char = heap.top(); + heap.pop(); + result.push_back(cnt_char.second); + if (--cnt_char.first > 0) { + used_cnt_chars.emplace_back(move(cnt_char)); + } + } + for (auto& cnt_char: used_cnt_chars) { + heap.emplace(move(cnt_char)); + } + } + return result; + } +}; diff --git a/C++/reconstruct-itinerary.cpp b/C++/reconstruct-itinerary.cpp new file mode 100644 index 000000000..d53398a59 --- /dev/null +++ b/C++/reconstruct-itinerary.cpp @@ -0,0 +1,40 @@ +// Time: O(t! / (n1! * n2! * ... nk!)), t is the total number of tickets, +// ni is the number of the ticket which from is city i, +// k is the total number of cities. +// Space: O(t) + +class Solution { +public: + vector findItinerary(vector> tickets) { + unordered_map> graph; + for (const auto& ticket : tickets) { + ++graph[ticket.first][ticket.second]; + } + const string from{"JFK"}; + vector ans{from}; + routeHelper(from, tickets.size(), &graph, &ans); + return ans; + } + +private: + bool routeHelper(const string& from, const int ticket_cnt, + unordered_map> *graph, vector *ans) { + + if (ticket_cnt == 0) { + return true; + } + + for (auto& to : (*graph)[from]) { + if (to.second) { + --to.second; + ans->emplace_back(to.first); + if (routeHelper(to.first, ticket_cnt - 1, graph, ans)) { + return true; + } + ans->pop_back(); + ++to.second; + } + } + return false; + } +}; diff --git a/C++/reconstruct-original-digits-from-english.cpp b/C++/reconstruct-original-digits-from-english.cpp new file mode 100644 index 000000000..3b8f64548 --- /dev/null +++ b/C++/reconstruct-original-digits-from-english.cpp @@ -0,0 +1,39 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + string originalDigits(string s) { + const vector numbers{"zero", "one", "two", "three", + "four", "five", "six", "seven", + "eight", "nine"}; + vector> cnts(numbers.size(), vector(26)); + for (int i = 0; i < numbers.size(); ++i) { + for (const auto& c : numbers[i]) { + ++cnts[i][c - 'a']; + } + } + + // The order for greedy method. + vector order{0, 2, 4, 6, 8, 1, 3, 5, 7, 9}; + + // The unique char in the order. + vector unique_chars{'z', 'o', 'w', 't', 'u', 'f', 'x', 's', 'g', 'n'}; + vector cnt(26); + for (const auto& c : s) { + ++cnt[c - 'a']; + } + + string result; + for (const auto& i : order) { + while (cnt[unique_chars[i] - 'a'] > 0) { + for (int j = 0; j < cnt.size(); ++j) { + cnt[j] -= cnts[i][j]; + } + result.push_back(i + '0'); + } + } + sort(result.begin(), result.end()); + return result; + } +}; diff --git a/C++/recover-binary-search-tree.cpp b/C++/recover-binary-search-tree.cpp new file mode 100644 index 000000000..b0b9105f4 --- /dev/null +++ b/C++/recover-binary-search-tree.cpp @@ -0,0 +1,59 @@ +// Time: O(n) +// Space: O(1) + +/** + * Definition for binary tree + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode(int x) : val(x), left(NULL), right(NULL) {} + * }; + */ +class Solution { +public: + void recoverTree(TreeNode *root) { + MorrisTraversal(root); + } + +private: + void MorrisTraversal(TreeNode *root) { + if (!root) { + return; + } + pair broken; + TreeNode *prev = nullptr; + TreeNode *cur = root; + while (cur) { + if (!cur->left) { + detect(prev, cur, &broken); + prev = cur; + cur = cur->right; + } else { + TreeNode *node = cur->left; + while (node->right && node->right != cur) { + node = node->right; + } + if (!node->right) { + node->right = cur; + cur = cur->left; + } else { + detect(prev, cur, &broken); + prev = cur; + node->right = nullptr; + cur = cur->right; + } + } + } + swap(broken.first->val, broken.second->val); + } + + void detect(TreeNode *prev, TreeNode *cur, pair *broken) { + if (prev && prev->val > cur->val) { + if (!broken->first) { // Find the first broken node. + broken->first = prev; + } + broken->second = cur; // Find the last broken node. + } + } +}; diff --git a/C++/recoverTree.cpp b/C++/recoverTree.cpp deleted file mode 100644 index a260f5329..000000000 --- a/C++/recoverTree.cpp +++ /dev/null @@ -1,76 +0,0 @@ -// Time Complexity: O(n) -// Space Complexity: O(1) - -/** - * Definition for binary tree - * struct TreeNode { - * int val; - * TreeNode *left; - * TreeNode *right; - * TreeNode(int x) : val(x), left(NULL), right(NULL) {} - * }; - */ -class Solution { - public: - void recoverTree(TreeNode *root) { - MorrisTraversal(root); - } - - private: - /* Function to traverse binary tree without recursion and - without stack */ - void MorrisTraversal(TreeNode *root) - { - if(root == NULL) - return; - - pair broken; - TreeNode *cur = root; - TreeNode *pre = NULL; - - while(cur != NULL) - { - if(cur->left == NULL) - { - detect(broken, pre, cur); - pre = cur; - cur = cur->right; - } - else - { - /* Find the inorder predecessor of current */ - auto node = cur->left; - while(node->right != NULL && node->right != cur) - node = node->right; - - /* Make current as right child of its inorder predecessor */ - if(node->right == NULL) - { - node->right = cur; - cur = cur->left; - } - - /* Revert the changes made in if part to restore the original - tree i.e., fix the right child of predecssor */ - else - { - detect(broken, pre, cur); - node->right = NULL; - pre = cur; - cur = cur->right; - } - } - } - - swap(broken.first->val, broken.second->val); // swap the fist and the last broken node - } - - void detect(pair &broken, TreeNode *pre, TreeNode *cur) { - if(pre && pre->val > cur->val) { - if(!broken.first) { // find the first broken node - broken.first = pre; - } - broken.second = cur; // find the last broken node - } - } -}; diff --git a/C++/rectangle-area-ii.cpp b/C++/rectangle-area-ii.cpp new file mode 100644 index 000000000..7ff59dcff --- /dev/null +++ b/C++/rectangle-area-ii.cpp @@ -0,0 +1,96 @@ +// Time: O(nlogn) +// Space: O(n) + +class Solution { +public: + enum { OPEN = 1, CLOSE = -1}; + + int rectangleArea(vector>& rectangles) { + vector> events; + set Xvals; + for (const auto& rec: rectangles) { + events.emplace_back(vector{rec[1], OPEN, rec[0], rec[2]}); + events.emplace_back(vector{rec[3], CLOSE, rec[0], rec[2]}); + Xvals.emplace(rec[0]); + Xvals.emplace(rec[2]); + } + sort(events.begin(), events.end()); + vector X(Xvals.cbegin(), Xvals.cend()); + unordered_map Xi; + for (int i = 0; i < X.size(); ++i) { + Xi[X[i]] = i; + } + + auto st = new SegmentTreeNode(0, X.size() - 1, X); + int64_t result = 0; + int64_t cur_x_sum = 0; + int cur_y = events[0][0]; + for (const auto& event: events) { + int y = event[0], type = event[1], x1 = event[2], x2 = event[3]; + result += cur_x_sum * (y - cur_y); + cur_x_sum = st->update(Xi[x1], Xi[x2], type); + cur_y = y; + } + return result % static_cast(1e9 + 7); + } + + class SegmentTreeNode { + public: + SegmentTreeNode(int start, int end, const vector& X) : + start_(start), + end_(end), + X_(X), + left_(nullptr), + right_(nullptr), + count_(0), + total_(0) { + } + + int mid() const { + return start_ + (end_ - start_) / 2; + } + + SegmentTreeNode *left() { + if (left_ == nullptr) { + left_ = new SegmentTreeNode(start_, mid(), X_); + } + return left_; + } + + SegmentTreeNode *right() { + if (right_ == nullptr) { + right_ = new SegmentTreeNode(mid(), end_, X_); + } + return right_; + } + + int64_t total() const { + return total_; + } + + int64_t update(int i, int j, int val) { + if (i >= j) { + return 0; + } + if (start_ == i && end_ == j) { + count_ += val; + } else { + left()->update(i, min(mid(), j), val); + right()->update(max(mid(), i), j, val); + } + if (count_ > 0) { + total_ = X_[end_] - X_[start_]; + } else { + total_ = left()->total() + right()->total(); + } + return total_; + } + + private: + int start_, end_; + const vector& X_; + SegmentTreeNode *left_, *right_; + int count_; + int64_t total_; + }; +}; diff --git a/C++/rectangle-area.cpp b/C++/rectangle-area.cpp new file mode 100644 index 000000000..5f30c95ea --- /dev/null +++ b/C++/rectangle-area.cpp @@ -0,0 +1,12 @@ +// Time: O(1) +// Space: O(1) + +class Solution { +public: + int computeArea(int A, int B, int C, int D, int E, int F, int G, int H) { + return (D - B) * (C - A) + + (G - E) * (H - F) - + max(0, (min(C, G) - max(A, E))) * + max(0, (min(D, H) - max(B, F))); + } +}; diff --git a/C++/rectangle-overlap.cpp b/C++/rectangle-overlap.cpp new file mode 100644 index 000000000..e1b15f366 --- /dev/null +++ b/C++/rectangle-overlap.cpp @@ -0,0 +1,15 @@ +// Time: O(1) +// Space: O(1) + +class Solution { +public: + bool isRectangleOverlap(vector& rec1, vector& rec2) { + return (intersect(rec1[0], rec1[2], rec2[0], rec2[2]) && + intersect(rec1[1], rec1[3], rec2[1], rec2[3])); + } + +private: + bool intersect(int p_left, int p_right, int q_left, int q_right) { + return max(p_left, q_left) < min(p_right, q_right); + } +}; diff --git a/C++/redundant-connection-ii.cpp b/C++/redundant-connection-ii.cpp new file mode 100644 index 000000000..ef039fcaa --- /dev/null +++ b/C++/redundant-connection-ii.cpp @@ -0,0 +1,48 @@ +// Time: O(nlog*n) ~= O(n), n is the length of the positions +// Space: O(n) + +class Solution { +public: + vector findRedundantDirectedConnection(vector>& edges) { + UnionFind union_find(edges.size() + 1); + for (const auto& edge : edges) { + if (!union_find.union_set(edge[0], edge[1])) { + return edge; + } + } + return {}; + } + +private: + class UnionFind { + public: + UnionFind(const int n) : set_(n), count_(n) { + iota(set_.begin(), set_.end(), 0); + } + + int find_set(const int x) { + if (set_[x] != x) { + set_[x] = find_set(set_[x]); // Path compression. + } + return set_[x]; + } + + bool union_set(const int x, const int y) { + int x_root = find_set(x), y_root = find_set(y); + if (x_root == y_root || y != y_root) { + return false; + } + set_[y_root] = x_root; + --count_; + return true; + } + + int length() const { + return count_; + } + + private: + vector set_; + int count_; + }; +}; diff --git a/C++/redundant-connection.cpp b/C++/redundant-connection.cpp new file mode 100644 index 000000000..76f921a91 --- /dev/null +++ b/C++/redundant-connection.cpp @@ -0,0 +1,48 @@ +// Time: O(nlog*n) ~= O(n), n is the length of the positions +// Space: O(n) + +class Solution { +public: + vector findRedundantConnection(vector>& edges) { + UnionFind union_find(edges.size() + 1); + for (const auto& edge : edges) { + if (!union_find.union_set(edge[0], edge[1])) { + return edge; + } + } + return {}; + } + +private: + class UnionFind { + public: + UnionFind(const int n) : set_(n), count_(n) { + iota(set_.begin(), set_.end(), 0); + } + + int find_set(const int x) { + if (set_[x] != x) { + set_[x] = find_set(set_[x]); // Path compression. + } + return set_[x]; + } + + bool union_set(const int x, const int y) { + int x_root = find_set(x), y_root = find_set(y); + if (x_root == y_root) { + return false; + } + set_[min(x_root, y_root)] = max(x_root, y_root); + --count_; + return true; + } + + int length() const { + return count_; + } + + private: + vector set_; + int count_; + }; +}; diff --git a/C++/relative-ranks.cpp b/C++/relative-ranks.cpp new file mode 100644 index 000000000..835aaf44b --- /dev/null +++ b/C++/relative-ranks.cpp @@ -0,0 +1,21 @@ +// Time: O(nlogn) +// Space: O(n) + +class Solution { +public: + vector findRelativeRanks(vector& nums) { + vector indices(nums.size()); + iota(indices.begin(), indices.end(), 0); + sort(indices.begin(), indices.end(), + [&nums](int a, int b) { + return nums[a] > nums[b]; + }); + + const vector ranks = {"Gold Medal", "Silver Medal", "Bronze Medal"}; + vector result(nums.size()); + for (int i = 0; i < nums.size(); ++i) { + result[indices[i]] = i > 2 ? to_string(i + 1) : ranks[i]; + } + return result; + } +}; diff --git a/C++/remove-9.cpp b/C++/remove-9.cpp new file mode 100644 index 000000000..48b926441 --- /dev/null +++ b/C++/remove-9.cpp @@ -0,0 +1,15 @@ +// Time: O(logn) +// Space: O(1) + +class Solution { +public: + int newInteger(int n) { + int result = 0, base = 1; + while (n > 0) { + result += (n % 9) * base; + n /= 9; + base *= 10; + } + return result; + } +}; diff --git a/C++/remove-boxes.cpp b/C++/remove-boxes.cpp new file mode 100644 index 000000000..b23789130 --- /dev/null +++ b/C++/remove-boxes.cpp @@ -0,0 +1,34 @@ +// Time: O(n^3) ~ O(n^4) +// Space: O(n^3) + +class Solution { +public: + int removeBoxes(vector& boxes) { + int lookup[100][100][100] = {0}; + return removeBoxesHelper(boxes, 0, boxes.size() - 1, 0, lookup); + } + +private: + int removeBoxesHelper(const vector& boxes, int l, int r, int k, int lookup[100][100][100]) { + if (l > r) { + return 0; + } + if (lookup[l][r][k]) { + return lookup[l][r][k]; + } + + auto& result = lookup[l][r][k]; + while (l < r && boxes[l + 1] == boxes[l]) { + ++l, ++k; + } + result = removeBoxesHelper(boxes, l + 1, r, 0, lookup) + (k + 1) * (k + 1); + for (int i = l + 1; i <= r; ++i) { + if (boxes[i] == boxes[l]) { + result = max(result, + removeBoxesHelper(boxes, l + 1, i - 1, 0, lookup) + + removeBoxesHelper(boxes, i, r, k + 1, lookup)); + } + } + return result; + } +}; diff --git a/C++/remove-comments.cpp b/C++/remove-comments.cpp new file mode 100644 index 000000000..ab781fa0e --- /dev/null +++ b/C++/remove-comments.cpp @@ -0,0 +1,30 @@ +// Time: O(n), n is the length of the source +// Space: O(k), k is the max length of a line + +class Solution { +public: + vector removeComments(vector& source) { + bool in_block = false; + vector result; + string newline; + for (const auto& line : source) { + for (int i = 0; i < line.length(); ++i) { + if (!in_block && i + 1 < line.length() && line.substr(i, 2) == "/*") { + in_block = true; + ++i; + } else if (in_block && i + 1 < line.length() && line.substr(i, 2) == "*/") { + in_block = false; + ++i; + } else if (!in_block && i + 1 < line.length() && line.substr(i, 2) == "//") { + break; + } else if (!in_block) { + newline.push_back(line[i]); + } + } + if (!in_block && !newline.empty()) { + result.emplace_back(move(newline)); + } + } + return result; + } +}; diff --git a/C++/remove-duplicate-letters.cpp b/C++/remove-duplicate-letters.cpp new file mode 100644 index 000000000..9034bf7f7 --- /dev/null +++ b/C++/remove-duplicate-letters.cpp @@ -0,0 +1,57 @@ +// Time: O(n) +// Space: O(k), k is size of the alphabet + +// vector solution, need to know size of the alphabet in advance (4ms) +class Solution { +public: + string removeDuplicateLetters(string s) { + const int k = 26; + vector remaining(k); + for (const auto& c : s) { + ++remaining[c - 'a']; + } + + vector in_stack(k); + string stk; + for (const auto& c : s) { + if (!in_stack[c - 'a']) { + while (!stk.empty() && stk.back() > c && remaining[stk.back() - 'a']) { + in_stack[stk.back() - 'a'] = false; + stk.pop_back(); + } + stk.push_back(c); + in_stack[c - 'a'] = true; + } + --remaining[c - 'a']; + } + return stk; + } +}; + +// Time: O(n) +// Space: O(k), k is size of the alphabet +// hash solution, no need to know size of the alphabet in advance (16ms) +class Solution2 { +public: + string removeDuplicateLetters(string s) { + unordered_map remaining; + for (const auto& c : s) { + ++remaining[c]; + } + + unordered_set in_stack; + string stk; + for (const auto& c : s) { + if (!in_stack.count(c)) { + while (!stk.empty() && stk.back() > c && remaining[stk.back()]) { + in_stack.erase(stk.back()); + stk.pop_back(); + } + stk.push_back(c); + in_stack.emplace(c); + } + --remaining[c]; + } + return stk; + } +}; diff --git a/C++/remove-duplicates-from-sorted-array-ii.cpp b/C++/remove-duplicates-from-sorted-array-ii.cpp new file mode 100644 index 000000000..764fdfa74 --- /dev/null +++ b/C++/remove-duplicates-from-sorted-array-ii.cpp @@ -0,0 +1,27 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + int removeDuplicates(vector& nums) { + if (nums.empty()) { + return 0; + } + + const int k = 2; // At most k duplicated. + + int left = 0; + int right = 1; + + while (right < nums.size()) { + if (nums[left] != nums[right] || + (left - k + 1 < 0 || nums[left] != nums[left - k + 1])) { + ++left; + nums[left] = nums[right]; + } + ++right; + } + + return left + 1; + } +}; diff --git a/C++/remove-duplicates-from-sorted-array.cpp b/C++/remove-duplicates-from-sorted-array.cpp new file mode 100644 index 000000000..0d8a71832 --- /dev/null +++ b/C++/remove-duplicates-from-sorted-array.cpp @@ -0,0 +1,15 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + int removeDuplicates(vector& nums) { + int last = -1; + for (const auto& num : nums) { + if (last == -1 || nums[last] != num) { + nums[++last] = num; + } + } + return last + 1; + } +}; diff --git a/C++/remove-duplicates-from-sorted-list-ii.cpp b/C++/remove-duplicates-from-sorted-list-ii.cpp new file mode 100644 index 000000000..5136aa0a4 --- /dev/null +++ b/C++/remove-duplicates-from-sorted-list-ii.cpp @@ -0,0 +1,32 @@ +// Time: O(n) +// Space: O(1) + +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode(int x) : val(x), next(NULL) {} + * }; + */ +class Solution { +public: + ListNode* deleteDuplicates(ListNode* head) { + ListNode dummy{0}; + auto prev = &dummy; + while (head) { + if (head->next && head->next->val == head->val) { + auto val = head->val; + while (head && head->val == val) { + head = head->next; + } + prev->next = head; + } else { + prev->next = head; + prev = head; + head = head->next; + } + } + return dummy.next; + } +}; diff --git a/C++/remove-duplicates-from-sorted-list.cpp b/C++/remove-duplicates-from-sorted-list.cpp new file mode 100644 index 000000000..0a0795c91 --- /dev/null +++ b/C++/remove-duplicates-from-sorted-list.cpp @@ -0,0 +1,26 @@ +// Time: O(n) +// Space: O(1) + +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode(int x) : val(x), next(NULL) {} + * }; + */ +class Solution { +public: + ListNode* deleteDuplicates(ListNode* head) { + auto iter = head; + while (iter) { + auto runner = iter->next; + while (runner && runner->val == iter->val) { + runner = runner->next; + } + iter->next = runner; + iter = runner; + } + return head; + } +}; diff --git a/C++/remove-element.cpp b/C++/remove-element.cpp new file mode 100644 index 000000000..522d6af4b --- /dev/null +++ b/C++/remove-element.cpp @@ -0,0 +1,17 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + int removeElement(vector& nums, int val) { + int left = 0, right = nums.size(); + while (left < right) { + if (nums[left] != val) { + ++left; + } else { + swap(nums[left], nums[--right]); + } + } + return right; + } +}; diff --git a/C++/remove-invalid-parentheses.cpp b/C++/remove-invalid-parentheses.cpp new file mode 100644 index 000000000..31242b26c --- /dev/null +++ b/C++/remove-invalid-parentheses.cpp @@ -0,0 +1,248 @@ +// Time: O(C(n, c)), try out all possible substrings with the minimum c deletion. +// Space: O(c), the depth is at most c, and it costs n at each depth + +// DFS solution with removed array. (4ms) +class Solution { +public: + vector removeInvalidParentheses(string s) { + int left_removed = 0, right_removed = 0; + findMinRemove(s, &left_removed, &right_removed); + + vector res; + vector removed; + removeInvalidParenthesesHelper(s, 0, left_removed, right_removed, &removed, &res); + return res; + } + +private: + void findMinRemove(const string& s, int *left_removed, int *right_removed) { + // Calculate the minimum left and right parantheses to remove. + for (const auto& c : s) { + if (c == '(') { + ++(*left_removed); + } else if (c == ')') { + if (!(*left_removed)) { + ++(*right_removed); + } else { + --(*left_removed); + } + } + } + } + + void removeInvalidParenthesesHelper(const string& s, int start, + int left_removed, int right_removed, + vector *removed, vector *res) { + + if (left_removed == 0 && right_removed == 0) { + string tmp; + for (int i = 0, j = 0; i < s.length(); ++i) { + if (j < removed->size() && i == (*removed)[j]) { + ++j; + } else { + tmp.push_back(s[i]); + } + } + if (isValid(tmp)) { + res->emplace_back(tmp); + } + return; + } + + for (int i = start; i < s.length(); ++i) { + if (right_removed == 0 && left_removed > 0 && s[i] == '(') { + if (i == start || s[i] != s[i - 1]) { // Skip duplicated. + removed->emplace_back(i); + removeInvalidParenthesesHelper(s, i + 1, left_removed - 1, right_removed, + removed, res); + removed->pop_back(); + } + } else if (right_removed > 0 && s[i] == ')') { + if (i == start || s[i] != s[i - 1]) { // Skip duplicated. + removed->emplace_back(i); + removeInvalidParenthesesHelper(s, i + 1, left_removed, right_removed - 1, + removed, res); + removed->pop_back(); + } + } + + } + } + + // Check whether s is valid or not. + bool isValid(string s) { + int sum = 0; + for (const auto &c : s) { + if (c == '(') { + ++sum; + } else if (c == ')') { + --sum; + } + if (sum < 0) { + return false; + } + } + return sum == 0; + } +}; + +// Time: O(C(n, c)), try out all possible substrings with the minimum c deletion. +// Space: O(c), the depth is at most c, and it costs n at each depth +// DFS solution with removed hash. (8ms) +class Solution2 { +public: + vector removeInvalidParentheses(string s) { + int left_removed = 0, right_removed = 0; + findMinRemove(s, &left_removed, &right_removed); + + vector res; + unordered_set removed; + removeInvalidParenthesesHelper(s, 0, left_removed, right_removed, &removed, &res); + return res; + } + +private: + void findMinRemove(const string& s, int *left_removed, int *right_removed) { + // Calculate the minimum left and right parantheses to remove. + for (const auto& c : s) { + if (c == '(') { + ++(*left_removed); + } else if (c == ')') { + if (!(*left_removed)) { + ++(*right_removed); + } else { + --(*left_removed); + } + } + } + } + + void removeInvalidParenthesesHelper(const string& s, int start, + int left_removed, int right_removed, + unordered_set *removed, vector *res) { + + if (left_removed == 0 && right_removed == 0) { + string tmp; + for (int i = 0; i < s.length(); ++i) { + if (!removed->count(i)) { + tmp.push_back(s[i]); + } + } + if (isValid(tmp)) { + res->emplace_back(tmp); + } + return; + } + + for (int i = start; i < s.length(); ++i) { + if (right_removed == 0 && left_removed > 0 && s[i] == '(') { + if (i == start || s[i] != s[i - 1]) { // Skip duplicated. + removed->emplace(i); + removeInvalidParenthesesHelper(s, i + 1, left_removed - 1, right_removed, + removed, res); + removed->erase(i); + } + } else if (right_removed > 0 && s[i] == ')') { + if (i == start || s[i] != s[i - 1]) { // Skip duplicated. + removed->emplace(i); + removeInvalidParenthesesHelper(s, i + 1, left_removed, right_removed - 1, + removed, res); + removed->erase(i); + } + } + + } + } + + // Check whether s is valid or not. + bool isValid(string s) { + int sum = 0; + for (const auto &c : s) { + if (c == '(') { + ++sum; + } else if (c == ')') { + --sum; + } + if (sum < 0) { + return false; + } + } + return sum == 0; + } +}; + + +// Time: O(n * C(n, c)), try out all possible substrings with the minimum c deletion. +// Space: O(n * c), the depth is at most c, and it costs n at each depth +// DFS solution. (4ms) +class Solution3 { +public: + vector removeInvalidParentheses(string s) { + int left_removed = 0, right_removed = 0; + findMinRemove(s, &left_removed, &right_removed); + + vector res; + removeInvalidParenthesesHelper(s, 0, left_removed, right_removed, &res); + return res; + } + + void findMinRemove(const string& s, int *left_removed, int *right_removed) { + // Calculate the minimum left and right parantheses to remove. + for (const auto& c : s) { + if (c == '(') { + ++(*left_removed); + } else if (c == ')') { + if (!(*left_removed)) { + ++(*right_removed); + } else { + --(*left_removed); + } + } + } + } + +private: + void removeInvalidParenthesesHelper(const string& s, int start, + int left_removed, int right_removed, vector *res) { + + if (left_removed == 0 && right_removed == 0) { + if (isValid(s)) { + res->emplace_back(s); + } + return; + } + + for (int i = start; i < s.length(); ++i) { + if (right_removed == 0 && left_removed > 0 && s[i] == '(') { + if (i == start || s[i] != s[i - 1]) { // Skip duplicated. + string tmp = s; + tmp.erase(i, 1); + removeInvalidParenthesesHelper(tmp, i, left_removed - 1, right_removed, res); + } + } else if (right_removed > 0 && s[i] == ')') { + if (i == start || s[i] != s[i - 1]) { // Skip duplicated. + string tmp = s; + tmp.erase(i, 1); + removeInvalidParenthesesHelper(tmp, i, left_removed, right_removed - 1, res); + } + } + + } + } + + // Check whether s is valid or not. + bool isValid(string s) { + int sum = 0; + for (const auto &c : s) { + if (c == '(') { + ++sum; + } else if (c == ')') { + --sum; + } + if (sum < 0) { + return false; + } + } + return sum == 0; + } +}; diff --git a/C++/remove-k-digits.cpp b/C++/remove-k-digits.cpp new file mode 100644 index 000000000..bc25f33c2 --- /dev/null +++ b/C++/remove-k-digits.cpp @@ -0,0 +1,23 @@ +// Time: O(n) +// Space: O(n) + +class Solution { +public: + string removeKdigits(string num, int k) { + // If a digit is greater than next one, delete it. + string s; + for (const auto c : num) { + while (k > 0 && !s.empty() && s.back() > c) { + s.pop_back(); + --k; + } + s.push_back(c); + } + + // If all digits are increasingly sorted, delete last. + s.resize(s.length() - k); + + // Strip all leading '0' + return s.empty() || s == "0" ? "0" : s.substr(s.find_first_not_of('0')); + } +}; diff --git a/C++/remove-linked-list-elements.cpp b/C++/remove-linked-list-elements.cpp new file mode 100644 index 000000000..925e964eb --- /dev/null +++ b/C++/remove-linked-list-elements.cpp @@ -0,0 +1,30 @@ +// Time: O(n) +// Space: O(1) + +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode(int x) : val(x), next(NULL) {} + * }; + */ +class Solution { +public: + ListNode* removeElements(ListNode* head, int val) { + ListNode dummy{0}; + dummy.next = head; + auto *prev = &dummy, *cur = dummy.next; + + while (cur) { + if (cur->val == val) { + prev->next = cur->next; + delete cur; + } else { + prev = cur; + } + cur = cur->next; + } + return dummy.next; + } +}; diff --git a/C++/remove-nth-node-from-end-of-list.cpp b/C++/remove-nth-node-from-end-of-list.cpp new file mode 100644 index 000000000..81568c340 --- /dev/null +++ b/C++/remove-nth-node-from-end-of-list.cpp @@ -0,0 +1,38 @@ +// Time: O(n) +// Space: O(1) + +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode(int x) : val(x), next(NULL) {} + * }; + */ +class Solution { +public: + ListNode *removeNthFromEnd(ListNode *head, int n) { + ListNode dummy{0}; + dummy.next = head; + auto slow = &dummy; + auto fast = &dummy; + + // fast is n-step ahead. + while (n > 0) { + fast = fast->next; + --n; + } + + // When fast reaches the end, slow must be nth to last node. + while (fast->next != nullptr) { + slow = slow->next; + fast = fast->next; + } + + auto node_to_delete = slow->next; + slow->next = slow->next->next; + delete node_to_delete; + + return dummy.next; + } +}; diff --git a/C++/removeDuplicates.cpp b/C++/removeDuplicates.cpp deleted file mode 100644 index f059dfe4c..000000000 --- a/C++/removeDuplicates.cpp +++ /dev/null @@ -1,19 +0,0 @@ -// Time Complexity: O(n) -// Space Complexity: O(1) - -class Solution { - public: - int removeDuplicates(int A[], int n) { - const int occur = 2; - if(n <= occur) return n; - - int cnt = occur; - - for(int i = occur; i < n; ++i) { - if(A[i] != A[cnt - occur]) - A[cnt++] = A[i]; - } - - return cnt; - } -}; diff --git a/C++/removeNthFromEnd.cpp b/C++/removeNthFromEnd.cpp deleted file mode 100644 index f2f71848d..000000000 --- a/C++/removeNthFromEnd.cpp +++ /dev/null @@ -1,39 +0,0 @@ -// Time Complexity: O(n) -// Space Complexity: O(1) - -/** - * Definition for singly-linked list. - * struct ListNode { - * int val; - * ListNode *next; - * ListNode(int x) : val(x), next(NULL) {} - * }; - */ -class Solution { - public: - ListNode *removeNthFromEnd(ListNode *head, int n) { - ListNode *slow = head, *fast = head, *pre = NULL; - - while(n > 0) { - fast = fast->next; - --n; - } - - while(fast) { - pre = slow; - slow = slow->next; - fast = fast->next; - } - - if(!pre && !slow->next) - return NULL; - - if(!pre && slow->next) - return slow->next; - - pre->next = slow->next; - delete slow; - - return head; - } -}; diff --git a/C++/reorder-list.cpp b/C++/reorder-list.cpp new file mode 100644 index 000000000..8dc4f303f --- /dev/null +++ b/C++/reorder-list.cpp @@ -0,0 +1,70 @@ +// Time: O(n) +// Space: O(1) + +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode(int x) : val(x), next(NULL) {} + * }; + */ +class Solution { + public: + void reorderList(ListNode *head) { + if (!head) { + return; + } + + auto slow = head, fast = head; + + while (fast->next && fast->next->next) { + slow = slow->next; + fast = fast->next->next; + } + + // Split into two lists. + auto tmp = slow->next; + slow->next = nullptr; + slow = tmp; + + merge(head, reverse(slow)); + } + +private: + ListNode *reverse(ListNode *head) { + ListNode dummy{0}; + + while (head) { + auto tmp = head->next; + head->next = dummy.next; + dummy.next = head; + head = tmp; + } + + return dummy.next; + } + + ListNode *merge(ListNode *list1, ListNode *list2) { + ListNode dummy{0}; + auto ptr = &dummy; + + while (list1 && list2) { + auto tmp = list1->next; + + ptr->next = list1; + ptr = ptr->next; + ptr->next = list2; + ptr = ptr->next; + + list1 = tmp; + list2 = list2->next; + } + + if (list1) { + ptr->next = list1; + } + + return dummy.next; + } +}; diff --git a/C++/reorderList.cpp b/C++/reorderList.cpp deleted file mode 100644 index 7feadc471..000000000 --- a/C++/reorderList.cpp +++ /dev/null @@ -1,69 +0,0 @@ -// Time Complexity: O(n) -// Space Complexity: O(1) - -/** - * Definition for singly-linked list. - * struct ListNode { - * int val; - * ListNode *next; - * ListNode(int x) : val(x), next(NULL) {} - * }; - */ -class Solution { - public: - void reorderList(ListNode *head) { - if(!head) return; - - ListNode *slow = head; - ListNode *fast = head; - - while(fast->next && fast->next->next) { - slow = slow->next; - fast = fast->next->next; - } - - // split into two lists - ListNode *tmp = slow->next; - slow->next = nullptr; - slow = tmp; - - merge(head, reverse(slow)); - } - - private: - ListNode *reverse(ListNode *head) { - ListNode dummy(INT_MIN); - - while(head) { - ListNode *tmp = head->next; - - head->next = dummy.next; - dummy.next = head; - - head = tmp; - } - - return dummy.next; - } - - ListNode *merge(ListNode *list1, ListNode *list2) { - ListNode dummy(INT_MIN); - ListNode *ptr = &dummy; - - while(list1 && list2) { - ListNode *tmp = list1->next; // backup list1 next - - ptr->next = list1; - ptr = ptr->next; - ptr->next = list2; // list1 next is overwritten - ptr = ptr->next; - - list1 = tmp; - list2 = list2->next; - } - - if(list1) ptr->next = list1; // append remaining list1 - - return dummy.next; - } -}; diff --git a/C++/reordered-power-of-2.cpp b/C++/reordered-power-of-2.cpp new file mode 100644 index 000000000..26bef5bb9 --- /dev/null +++ b/C++/reordered-power-of-2.cpp @@ -0,0 +1,24 @@ +// Time: O((logn)^2) = O(1) due to n is a 32-bit number +// Space: O(logn) = O(1) + +class Solution { +public: + bool reorderedPowerOf2(int N) { + vector count = counter(N); + for (int i = 0; i < 31; ++i) { + if (count == counter(1 << i)) { + return true; + } + } + return false; + } + +private: + vector counter(int N) { + vector result(10, 0); + for (; N; N /= 10) { + ++result[N % 10]; + } + return result; + } +}; diff --git a/C++/reorganize-string.cpp b/C++/reorganize-string.cpp new file mode 100644 index 000000000..d835640dc --- /dev/null +++ b/C++/reorganize-string.cpp @@ -0,0 +1,41 @@ +// Time: O(nloga) = O(n), a is the size of alphabet +// Space: O(a) = O(1) + +class Solution { +public: + string reorganizeString(string S) { + unordered_map counts; + for (const auto& c : S) { + ++counts[c]; + } + if (any_of(counts.cbegin(), counts.cend(), + [&](const pair& kvp) { + return kvp.second > (S.length() + 1) / 2; + })) { + return ""; + } + + string result; + priority_queue> max_heap; + for (const auto& kvp : counts) { + max_heap.emplace(kvp.second, kvp.first); + } + while (max_heap.size() > 1) { + char c1, c2; + int count1, count2; + tie(count1, c1) = max_heap.top(); max_heap.pop(); + tie(count2, c2) = max_heap.top(); max_heap.pop(); + if (result.empty() || c1 != result.back()) { + result.push_back(c1); + result.push_back(c2); + if (count1 - 1 > 0) { + max_heap.emplace(count1 - 1, c1); + } + if (count2 - 1 > 0) { + max_heap.emplace(count2 - 1, c2); + } + } + } + return max_heap.empty() ? result : result + max_heap.top().second; + } +}; diff --git a/C++/repeated-string-match.cpp b/C++/repeated-string-match.cpp new file mode 100644 index 000000000..3f4b8ef73 --- /dev/null +++ b/C++/repeated-string-match.cpp @@ -0,0 +1,66 @@ +// Time: O(n + m) +// Space: O(1) + +// Rabin-Karp Algorithm (rolling hash) +class Solution { +public: + int repeatedStringMatch(string A, string B) { + static const uint64_t M = 1000000007; + static const uint64_t p = 113; + static const uint64_t p_inv = pow(p, M - 2, M); + + const auto q = (B.length() + A.length() - 1) / A.length(); + + uint64_t b_hash = 0, power = 1; + for (int i = 0; i < B.length(); ++i) { + b_hash += power * B[i]; + b_hash %= M; + power = (power * p) % M; + } + + uint64_t a_hash = 0; power = 1; + for (int i = 0; i < B.length(); ++i) { + a_hash += power * A[i % A.length()]; + a_hash %= M; + power = (power * p) % M; + } + if (a_hash == b_hash && check(0, A, B)) { + return q; + } + + power = (power * p_inv) % M; + for (int i = B.length(); i < (q + 1) * A.length(); ++i) { + a_hash -= A[(i - B.length()) % A.length()]; + a_hash *= p_inv; + a_hash += power * A[i % A.length()]; + a_hash %= M; + if (a_hash == b_hash && check(i - B.length() + 1, A, B)) { + return i < q * A.length() ? q : q + 1; + } + } + return -1; + } + +private: + bool check(int index, const string& A, const string& B) { + for (int i = 0; i < B.length(); ++i) { + if (A[(i + index) % A.length()] != B[i]) { + return false; + } + } + return true; + } + + uint64_t pow(uint64_t a,uint64_t b, uint64_t m) { + a %= m; + uint64_t result = 1; + while (b) { + if (b & 1) { + result = (result * a) % m; + } + a = (a * a) % m; + b >>= 1; + } + return result; + } +}; diff --git a/C++/repeated-substring-pattern.cpp b/C++/repeated-substring-pattern.cpp new file mode 100644 index 000000000..afd5e990e --- /dev/null +++ b/C++/repeated-substring-pattern.cpp @@ -0,0 +1,28 @@ +// Time: O(n) +// Space: O(n) + +// KMP solution. +class Solution { +public: + bool repeatedSubstringPattern(string str) { + vector prefix = getPrefix(str); + return prefix.back() != -1 && + (prefix.back() + 1) % (str.length() - prefix.back() - 1) == 0; + } + +private: + vector getPrefix(const string& pattern) { + vector prefix(pattern.length(), -1); + int j = -1; + for (int i = 1; i < pattern.length(); ++i) { + while (j > -1 && pattern[j + 1] != pattern[i]) { + j = prefix[j]; + } + if (pattern[j + 1] == pattern[i]) { + ++j; + } + prefix[i] = j; + } + return prefix; + } +}; diff --git a/C++/replace-words.cpp b/C++/replace-words.cpp new file mode 100644 index 000000000..33633eae6 --- /dev/null +++ b/C++/replace-words.cpp @@ -0,0 +1,50 @@ +// Time: O(n) +// Space: O(t), t is the number of nodes in trie + +class Solution { +public: + string replaceWords(vector& dict, string sentence) { + TrieNode trie; + string result; + for (const auto& s : dict) { + trie.Insert(s); + } + auto curr = ≜ + for (const auto& c : sentence) { + if (c == ' ' || !curr || !curr->isString) { + result += c; + } + if (c == ' ') { + curr = ≜ + } else if (curr && !curr->isString) { + curr = curr->leaves[c]; + } + } + return result; + } + +private: + struct TrieNode { + bool isString = false; + unordered_map leaves; + + void Insert(const string& s) { + auto* p = this; + for (const auto& c : s) { + if (p->leaves.find(c) == p->leaves.cend()) { + p->leaves[c] = new TrieNode; + } + p = p->leaves[c]; + } + p->isString = true; + } + + ~TrieNode() { + for (auto& kv : leaves) { + if (kv.second) { + delete kv.second; + } + } + } + }; +}; diff --git a/C++/reshape-the-matrix.cpp b/C++/reshape-the-matrix.cpp new file mode 100644 index 000000000..11ac63605 --- /dev/null +++ b/C++/reshape-the-matrix.cpp @@ -0,0 +1,22 @@ +// Time: O(m * n) +// Space: O(m * n) + +class Solution { +public: + vector> matrixReshape(vector>& nums, int r, int c) { + if (nums.empty() || + r * c != nums.size() * nums[0].size()) { + return nums; + } + + vector> result(r, vector(c)); + int count = 0; + for (int i = 0; i < nums.size(); ++i) { + for (int j = 0; j < nums[0].size(); ++j) { + result[count / c][count % c] = nums[i][j]; + ++count; + } + } + return result; + } +}; diff --git a/C++/reverse-bits.cpp b/C++/reverse-bits.cpp new file mode 100644 index 000000000..6bedc3410 --- /dev/null +++ b/C++/reverse-bits.cpp @@ -0,0 +1,16 @@ +// Time: O(logn) = O(32) +// Space: O(1) + +class Solution { +public: + uint32_t reverseBits(uint32_t n) { + uint32_t result = 0; + int count = 32; + while (count--) { + result <<= 1; + result |= n & 1; + n >>= 1; + } + return result; + } +}; diff --git a/C++/reverse-integer.cpp b/C++/reverse-integer.cpp new file mode 100644 index 000000000..5e00c210e --- /dev/null +++ b/C++/reverse-integer.cpp @@ -0,0 +1,20 @@ +// Time: O(logn) = O(1) +// Space: O(1) + +class Solution { +public: + int reverse(int x) { + int result = 0; + while (x) { + auto prev = result; + result *= 10; + result += x % 10; + if (result / 10 != prev) { + result = 0; + break; + } + x /= 10; + } + return result; + } +}; diff --git a/C++/reverse-linked-list-ii.cpp b/C++/reverse-linked-list-ii.cpp new file mode 100644 index 000000000..a3152d3d3 --- /dev/null +++ b/C++/reverse-linked-list-ii.cpp @@ -0,0 +1,38 @@ +// Time: O(n) +// Space: O(1) + +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode(int x) : val(x), next(NULL) {} + * }; + */ +class Solution { +public: + ListNode* reverseBetween(ListNode* head, int m, int n) { + ListNode dummy{0}; + dummy.next = head; + + auto *prev = &dummy; + + for (int i = 0; i < m - 1; ++i) { + prev = prev->next; + } + + auto *head2 = prev; + + prev = prev->next; + auto *cur = prev->next; + + for (int i = m; i < n; ++i) { + prev->next = cur->next; // Remove cur from the list. + cur->next = head2->next; // Add cur to the head. + head2->next = cur; // Add cur to the head. + cur = prev->next; // Get next cur. + } + + return dummy.next; + } +}; diff --git a/C++/reverse-linked-list.cpp b/C++/reverse-linked-list.cpp new file mode 100644 index 000000000..06ed186a7 --- /dev/null +++ b/C++/reverse-linked-list.cpp @@ -0,0 +1,26 @@ +// Time: O(n) +// Space: O(1) + +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode(int x) : val(x), next(NULL) {} + * }; + */ +class Solution { +public: + ListNode* reverseList(ListNode* head) { + auto dummy = ListNode{0}; + + while (head) { + auto tmp = head->next; + head->next = dummy.next; + dummy.next = head; + head = tmp; + } + + return dummy.next; + } +}; diff --git a/C++/reverse-nodes-in-k-group.cpp b/C++/reverse-nodes-in-k-group.cpp new file mode 100644 index 000000000..5c667d419 --- /dev/null +++ b/C++/reverse-nodes-in-k-group.cpp @@ -0,0 +1,45 @@ +// Time: O(n) +// Space: O(1) + +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode(int x) : val(x), next(NULL) {} + * }; + */ +class Solution { +public: + ListNode* reverseKGroup(ListNode* head, int k) { + ListNode dummy{0}; + dummy.next = head; + auto curr = head, curr_dummy = &dummy; + int len = 0; + + while (curr) { + auto next_curr = curr->next; + len = (len + 1) % k; + + if (len == 0) { + auto next_dummy = curr_dummy->next; + reverse(&curr_dummy, curr->next); + curr_dummy = next_dummy; + } + curr = next_curr; + } + return dummy.next; + } + + void reverse(ListNode **begin, const ListNode *end) { + ListNode *first = (*begin)->next; + ListNode *curr = first->next; + + while (curr != end) { + first->next = curr->next; + curr->next = (*begin)->next; + (*begin)->next = curr; + curr = first->next; + } + } +}; diff --git a/C++/reverse-pairs.cpp b/C++/reverse-pairs.cpp new file mode 100644 index 000000000..4dcc941d3 --- /dev/null +++ b/C++/reverse-pairs.cpp @@ -0,0 +1,26 @@ +// Time: O(nlogn) +// Space: O(logn) + +class Solution { +public: + int reversePairs(vector& nums) { + return countAndMergeSort(nums.begin(), nums.end()); + } + +private: + int countAndMergeSort(vector::iterator begin, vector::iterator end) { + if (end - begin <= 1) { + return 0; + } + auto mid = begin + (end - begin) / 2; + int count = countAndMergeSort(begin, mid) + countAndMergeSort(mid, end); + for (auto i = begin, j = mid; i != mid; ++i) { + while (j != end && *i > 2L * *j) { + ++j; + } + count += j - mid; + } + inplace_merge(begin, mid, end); + return count; + } +}; diff --git a/C++/reverse-string-ii.cpp b/C++/reverse-string-ii.cpp new file mode 100644 index 000000000..590c24fd1 --- /dev/null +++ b/C++/reverse-string-ii.cpp @@ -0,0 +1,15 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + string reverseStr(string s, int k) { + for (int left = 0; left < s.size(); left += 2 * k) { + for (int i = left, j = min(left + k - 1, static_cast(s.size()) - 1); + i < j; ++i, --j) { + swap(s[i], s[j]); + } + } + return s; + } +}; diff --git a/C++/reverse-string.cpp b/C++/reverse-string.cpp new file mode 100644 index 000000000..c9fc329bc --- /dev/null +++ b/C++/reverse-string.cpp @@ -0,0 +1,22 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + string reverseString(string s) { + for (int i = 0, j = s.length() - 1; i < j; ++i, --j) { + swap(s[i], s[j]); + } + return s; + } +}; + +// Time: O(n) +// Space: O(1) +class Solution2 { +public: + string reverseString(string s) { + reverse(s.begin(), s.end()); + return s; + } +}; diff --git a/C++/reverse-vowels-of-a-string.cpp b/C++/reverse-vowels-of-a-string.cpp new file mode 100644 index 000000000..f919e4fdd --- /dev/null +++ b/C++/reverse-vowels-of-a-string.cpp @@ -0,0 +1,24 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + string reverseVowels(string s) { + for (int i = 0, j = s.length() - 1; i < j;) { + if (!is_vowel(tolower(s[i]))) { + ++i; + } else if (!is_vowel(tolower(s[j]))) { + --j; + } else { + swap(s[i++], s[j--]); + } + } + return s; + } + +private: + const string vowels_ = "aeiou"; + bool is_vowel(char a){ + return vowels_.find(a) != string::npos; + } +}; diff --git a/C++/reverse-words-in-a-string-ii.cpp b/C++/reverse-words-in-a-string-ii.cpp new file mode 100644 index 000000000..88babb196 --- /dev/null +++ b/C++/reverse-words-in-a-string-ii.cpp @@ -0,0 +1,15 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + void reverseWords(string &s) { + reverse(s.begin(), s.end()); + for (int i = 0, j = 0; j <= s.length(); ++j) { + if (j == s.length() || s[j] == ' ') { + reverse(s.begin() + i, s.begin() + j); + i = j + 1; + } + } + } +}; diff --git a/C++/reverse-words-in-a-string-iii.cpp b/C++/reverse-words-in-a-string-iii.cpp new file mode 100644 index 000000000..8edc11bbc --- /dev/null +++ b/C++/reverse-words-in-a-string-iii.cpp @@ -0,0 +1,15 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + string reverseWords(string s) { + for (int i = 0, j = 0; j <= s.length(); ++j) { + if (j == s.length() || s[j] == ' ') { + reverse(s.begin() + i, s.begin() + j); + i = j + 1; + } + } + return s; + } +}; diff --git a/C++/reverse-words-in-a-string.cpp b/C++/reverse-words-in-a-string.cpp new file mode 100644 index 000000000..5621252ec --- /dev/null +++ b/C++/reverse-words-in-a-string.cpp @@ -0,0 +1,25 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + void reverseWords(string &s) { + // Reverse the whole string first. + reverse(s.begin(), s.end()); + + size_t begin = 0, end = 0, len = 0; + while ((begin = s.find_first_not_of(" ", end)) != string::npos) { + if ((end = s.find(" ", begin)) == string::npos) { + end = s.length(); + } + // Reverse each word in the string. + reverse(s.begin() + begin, s.begin() + end); + + // Shift the word to avoid extra space. + move(s.begin() + begin, s.begin() + end, s.begin() + len); + len += end - begin; + s[len++] = ' '; + } + s.resize(len ? len - 1 : 0); + } +}; diff --git a/C++/reverseBetween.cpp b/C++/reverseBetween.cpp deleted file mode 100644 index 0936b787e..000000000 --- a/C++/reverseBetween.cpp +++ /dev/null @@ -1,38 +0,0 @@ -// Time Complexity: O(n) -// Space Complexity: O(1) - -/** - * Definition for singly-linked list. - * struct ListNode { - * int val; - * ListNode *next; - * ListNode(int x) : val(x), next(NULL) {} - * }; - */ -class Solution { - public: - ListNode *reverseBetween(ListNode *head, int m, int n) { - ListNode dummy(-1); - dummy.next = head; - - ListNode *prev = &dummy; - - for(int i = 0; i < m - 1; ++i) { - prev = prev->next; - } - - ListNode *const head2 = prev; - - prev = prev->next; - ListNode *cur = prev->next; - - for(int i = m; i < n; ++i) { - prev->next = cur->next; // remove cur from the list - cur->next = head2->next; // add cur to the head - head2->next = cur; // add cur to the head - cur = prev->next; // get next cur - } - - return dummy.next; - } -}; diff --git a/C++/reverseKGroup.cpp b/C++/reverseKGroup.cpp deleted file mode 100644 index a384ed97a..000000000 --- a/C++/reverseKGroup.cpp +++ /dev/null @@ -1,47 +0,0 @@ -// Time Complexity: O(n) -// Space Complexity: O(1) - -/** - * Definition for singly-linked list. - * struct ListNode { - * int val; - * ListNode *next; - * ListNode(int x) : val(x), next(NULL) {} - * }; - */ -class Solution { - public: - ListNode *reverseKGroup(ListNode *head, int k) { - ListNode dummy(INT_MIN); - dummy.next = head; - - ListNode *cur = head; - ListNode *cur_dummy = &dummy; - int len = 0; - - while(cur) { - ListNode *next = cur->next; - len = (len + 1) % k; - if(len == 0) { - ListNode *next_dummy = cur_dummy->next; - reverseKGroup(cur_dummy, cur->next); - cur_dummy = next_dummy; - } - cur = next; - } - - return dummy.next; - } - - void reverseKGroup(ListNode *pre, ListNode *end) { - ListNode *first = pre->next; - ListNode *cur = first->next; - while(cur != end) { - ListNode *next = cur->next; - first->next = cur->next; // connect first node to the one next to current node - cur->next = pre->next; // remove current node from list and add the current node to the head - pre->next = cur; // connect previous node to the current node - cur = next; // set next node as current node - } - } -}; diff --git a/C++/reverseWords.cpp b/C++/reverseWords.cpp deleted file mode 100644 index c481c96b0..000000000 --- a/C++/reverseWords.cpp +++ /dev/null @@ -1,22 +0,0 @@ -// Complexity: -// O(n) time -// O(n) space - -class Solution { -public: - void reverseWords(string &s) - { - string rs; - for (int i = s.length()-1; i >= 0; ) - { - while (i >= 0 && s[i] == ' ') i--; - if (i < 0) break; - if (!rs.empty()) rs.push_back(' '); - string t; - while (i >= 0 && s[i] != ' ') t.push_back(s[i--]); - reverse(t.begin(), t.end()); - rs.append(t); - } - s = rs; - } -}; \ No newline at end of file diff --git a/C++/robot-room-cleaner.cpp b/C++/robot-room-cleaner.cpp new file mode 100644 index 000000000..b8a029231 --- /dev/null +++ b/C++/robot-room-cleaner.cpp @@ -0,0 +1,67 @@ +// Time: O(n), n is the number of cells +// Space: O(n) + +/** + * // This is the robot's control interface. + * // You should not implement it, or speculate about its implementation + * class Robot { + * public: + * // Returns true if the cell in front is open and robot moves into the cell. + * // Returns false if the cell in front is blocked and robot stays in the current cell. + * bool move(); + * + * // Robot will stay in the same cell after calling turnLeft/turnRight. + * // Each turn will be 90 degrees. + * void turnLeft(); + * void turnRight(); + * + * // Clean the current cell. + * void clean(); + * }; + */ +class Solution { +public: + template + struct PairHash { + size_t operator()(const pair& p) const { + size_t seed = 0; + seed ^= std::hash{}(p.first) + 0x9e3779b9 + (seed<<6) + (seed>>2); + seed ^= std::hash{}(p.second) + 0x9e3779b9 + (seed<<6) + (seed>>2); + return seed; + } + }; + + void cleanRoom(Robot& robot) { + unordered_set, PairHash> lookup; + dfs({0, 0}, robot, 0, &lookup); + } + +private: + void dfs(const pair& pos, Robot& robot, int dir, + unordered_set, PairHash> *lookup) { + if (lookup->count(pos)) { + return; + } + lookup->emplace(pos); + + robot.clean(); + static const vector> directions{{0, 1}, {1, 0}, {0, -1}, {-1, 0}}; + for (int i = 0; i < directions.size(); ++i, dir = (dir + 1) % directions.size()) { + if (robot.move()) { + dfs({pos.first + directions[dir].first, + pos.second + directions[dir].second}, + robot, dir, lookup); + goBack(robot); + } + robot.turnRight(); + } + } + + void goBack(Robot& robot) { + robot.turnLeft(); + robot.turnLeft(); + robot.move(); + robot.turnRight(); + robot.turnRight(); + } +}; diff --git a/C++/roman-to-integer.cpp b/C++/roman-to-integer.cpp new file mode 100644 index 000000000..13553fdea --- /dev/null +++ b/C++/roman-to-integer.cpp @@ -0,0 +1,20 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + int romanToInt(string s) { + unordered_map numeral_map = {{'I', 1}, {'V', 5}, {'X', 10}, + {'L', 50}, {'C', 100}, {'D', 500}, + {'M', 1000}}; + int decimal = 0; + for (int i = 0; i < s.length(); ++i) { + if (i > 0 && numeral_map[s[i]] > numeral_map[s[i - 1]]) { + decimal += numeral_map[s[i]] - 2 * numeral_map[s[i - 1]]; + } else { + decimal += numeral_map[s[i]]; + } + } + return decimal; + } +}; diff --git a/C++/rotate-array.cpp b/C++/rotate-array.cpp new file mode 100644 index 000000000..ad98c3b4a --- /dev/null +++ b/C++/rotate-array.cpp @@ -0,0 +1,14 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + void rotate(vector& nums, int k) { + if (!nums.empty()) { + k %= nums.size(); + reverse(nums.begin(), nums.begin() + nums.size() - k); + reverse(nums.begin() + nums.size() - k, nums.end()); + reverse(nums.begin(), nums.end()); + } + } +}; diff --git a/C++/rotate-function.cpp b/C++/rotate-function.cpp new file mode 100644 index 000000000..fb15b424d --- /dev/null +++ b/C++/rotate-function.cpp @@ -0,0 +1,20 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + int maxRotateFunction(vector& A) { + int sum = accumulate(A.begin(), A.end(), 0); + int fi = 0; + for (int i = 0; i < A.size(); ++i) { + fi += i * A[i]; + } + + int result = fi; + for (int i = 1; i <= A.size(); ++i) { + fi += sum - A.size() * A[A.size() - i]; + result = max(result, fi); + } + return result; + } +}; diff --git a/C++/rotate-image.cpp b/C++/rotate-image.cpp new file mode 100644 index 000000000..d3ca7cc1a --- /dev/null +++ b/C++/rotate-image.cpp @@ -0,0 +1,37 @@ +// Time: O(n^2) +// Space: O(1) + +class Solution { +public: + void rotate(vector>& matrix) { + const int n = matrix.size(); + for (int i = 0; i < n / 2; ++i) { + for (int j = i; j < n - 1 - i; ++j) { + const auto tmp = matrix[i][j]; + matrix[i][j] = matrix[n - 1 - j][i]; + matrix[n - 1- j][i] = matrix[n - 1 - i][n - 1 - j]; + matrix[n - 1 - i][n - 1 - j] = matrix[j][n - 1 - i]; + matrix[j][n - 1 - i] = tmp; + } + } + } +}; + +class Solution2 { +public: + void rotate(vector>& matrix) { + const int n = matrix.size(); + // Anti-diagonal mirror. + for (int i = 0; i < n; ++i) { + for (int j = 0; j < n - i; ++j) { + swap(matrix[i][j], matrix[n - 1 - j][n - 1 - i]); + } + } + // Horizontal mirror. + for (int i = 0; i < n / 2; ++i) { + for (int j = 0; j < n; ++j) { + swap(matrix[i][j], matrix[n - 1 - i][j]); + } + } + } +}; diff --git a/C++/rotate-list.cpp b/C++/rotate-list.cpp new file mode 100644 index 000000000..ba6f59d07 --- /dev/null +++ b/C++/rotate-list.cpp @@ -0,0 +1,44 @@ +// Time: O(n) +// Space: O(1) + +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode(int x) : val(x), next(NULL) {} + * }; + */ +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode(int x) : val(x), next(NULL) {} + * }; + */ +class Solution { +public: + ListNode* rotateRight(ListNode* head, int k) { + if (head == nullptr || head->next == nullptr) { + return head; + } + + int n = 1; + auto curr = head; + for (; curr->next; curr = curr->next) { + ++n; + } + curr->next = head; + + auto tail = curr; + k = n - k % n; + curr = head; + for (int i = 0; i < k; curr = curr->next, ++i) { + tail = curr; + } + + tail->next = nullptr; + return curr; + } +}; diff --git a/C++/rotate-string.cpp b/C++/rotate-string.cpp new file mode 100644 index 000000000..0ae259746 --- /dev/null +++ b/C++/rotate-string.cpp @@ -0,0 +1,130 @@ +// Time: O(n) +// Space: O(1) + +// Rabin-Karp Algorithm (rolling hash) +class Solution { +public: + bool rotateString(string A, string B) { + if (A.length() != B.length()) { + return false; + } + static const uint64_t M = 1000000007; + static const uint64_t p = 113; + static const uint64_t p_inv = pow(p, M - 2, M); + + uint64_t b_hash = 0, power = 1; + for (int i = 0; i < B.length(); ++i) { + b_hash += power * B[i]; + b_hash %= M; + power = (power * p) % M; + } + + uint64_t a_hash = 0; power = 1; + for (int i = 0; i < B.length(); ++i) { + a_hash += power * A[i % A.length()]; + a_hash %= M; + power = (power * p) % M; + } + if (a_hash == b_hash && check(0, A, B)) { + return true; + } + + power = (power * p_inv) % M; + for (int i = B.length(); i < 2 * A.length(); ++i) { + a_hash -= A[(i - B.length()) % A.length()]; + a_hash *= p_inv; + a_hash += power * A[i % A.length()]; + a_hash %= M; + if (a_hash == b_hash && check(i - B.length() + 1, A, B)) { + return true; + } + } + return false; + } + +private: + bool check(int index, const string& A, const string& B) { + for (int i = 0; i < B.length(); ++i) { + if (A[(i + index) % A.length()] != B[i]) { + return false; + } + } + return true; + } + + uint64_t pow(uint64_t a,uint64_t b, uint64_t m) { + a %= m; + uint64_t result = 1; + while (b) { + if (b & 1) { + result = (result * a) % m; + } + a = (a * a) % m; + b >>= 1; + } + return result; + } +}; + +// Time: O(n) +// Space: O(n) +// KMP algorithm +class Solution2 { +public: + bool rotateString(string A, string B) { + if (A.length() != B.length()) { + return false; + } + return strStr(A + A, B) != -1; + } + +private: + int strStr(string haystack, string needle) { + if (needle.empty()) { + return 0; + } + + return KMP(haystack, needle); + } + + int KMP(const string& text, const string& pattern) { + const vector prefix = getPrefix(pattern); + int j = -1; + for (int i = 0; i < text.length(); ++i) { + while (j > -1 && pattern[j + 1] != text[i]) { + j = prefix[j]; + } + if (pattern[j + 1] == text[i]) { + ++j; + } + if (j == pattern.length() - 1) { + return i - j; + } + } + return -1; + } + + vector getPrefix(const string& pattern) { + vector prefix(pattern.length(), -1); + int j = -1; + for (int i = 1; i < pattern.length(); ++i) { + while (j > -1 && pattern[j + 1] != pattern[i]) { + j = prefix[j]; + } + if (pattern[j + 1] == pattern[i]) { + ++j; + } + prefix[i] = j; + } + return prefix; + } +}; + +// Time: O(n^2) +// Space: O(n) +class Solution3 { +public: + bool rotateString(string A, string B) { + return A.size() == B.size() && (A + A).find(B) != string::npos; + } +}; diff --git a/C++/rotate.cpp b/C++/rotate.cpp deleted file mode 100644 index 04d9b392c..000000000 --- a/C++/rotate.cpp +++ /dev/null @@ -1,18 +0,0 @@ -// Time Complexity: O(n^2) -// Space Complexity: O(1) - -class Solution { - public: - void rotate(vector > &matrix) { - int n = matrix.size(); - for(int i = 0; i < n / 2; i++) { - for(int j = i; j < n - 1 - i; j++) { - int tmp = matrix[i][j]; - matrix[i][j] = matrix[n-1-j][i]; - matrix[n-1-j][i] = matrix[n-1-i][n-1-j]; - matrix[n-1-i][n-1-j]= matrix[j][n-1-i]; - matrix[j][n-1-i] = tmp; - } - } - } -}; diff --git a/C++/rotateRight.cpp b/C++/rotateRight.cpp deleted file mode 100644 index 9e5ffbd42..000000000 --- a/C++/rotateRight.cpp +++ /dev/null @@ -1,35 +0,0 @@ -// Time Complexity: O(n) -// Space Complexity: O(1) - -/** - * Definition for singly-linked list. - * struct ListNode { - * int val; - * ListNode *next; - * ListNode(int x) : val(x), next(NULL) {} - * }; - */ -class Solution { - public: - ListNode *rotateRight(ListNode *head, int k) { - ListNode dummy(INT_MIN); - dummy.next = head; - ListNode *p = &dummy; - for(int i = 0; p && i < k; ++i) { - p = p->next; - if(!p) - p = dummy.next; - } - - if(!p || !p->next) - return dummy.next; - - ListNode *cur = &dummy; - for(; p->next; cur = cur->next, p = p->next); // find new head - p->next = dummy.next; // connect tail to the head - dummy.next = cur->next; // update new head - cur->next = NULL; // update new tail - - return dummy.next; - } -}; diff --git a/C++/rotated-digits.cpp b/C++/rotated-digits.cpp new file mode 100644 index 000000000..a13ecf5bb --- /dev/null +++ b/C++/rotated-digits.cpp @@ -0,0 +1,116 @@ +// Time: O(logn) +// Space: O(logn) + +// memoization (top-down dp) +class Solution { +private: + template + struct TupleHash { + size_t operator()(const tuple& p) const { + size_t seed = 0; + A a; B b; C c; + tie(a, b, c) = p; + seed ^= std::hash{}(a) + 0x9e3779b9 + (seed<<6) + (seed>>2); + seed ^= std::hash{}(b) + 0x9e3779b9 + (seed<<6) + (seed>>2); + seed ^= std::hash{}(c) + 0x9e3779b9 + (seed<<6) + (seed>>2); + return seed; + } + }; + +public: + int rotatedDigits(int N) { + vector A; + for (; N; N /= 10) { + A.emplace_back(N % 10); + } + reverse(A.begin(), A.end()); + unordered_map, int, TupleHash> lookup; + return dp(A, 0, true, false, &lookup); + } + +private: + int dp(const vector& A, int i, bool is_prefix_equal, bool is_good, + unordered_map, int, TupleHash> *lookup) { + if (i == A.size()) { + return static_cast(is_good); + } + if (!lookup->count(make_tuple(i, is_prefix_equal, is_good))) { + const auto& ceil = is_prefix_equal ? A[i] + 1 : 10; + int result = 0; + for (int d = 0; d < ceil; ++d) { + if (invalid.count(d)) continue; + result += dp(A, i + 1, + is_prefix_equal && d == A[i], + is_good || diff.count(d), + lookup); + } + (*lookup)[make_tuple(i, is_prefix_equal, is_good)] = result; + } + return (*lookup)[make_tuple(i, is_prefix_equal, is_good)]; + } + + const unordered_set invalid = {3, 4, 7}; + const unordered_set diff = {2, 5, 6, 9}; +}; + +// Time: O(n) +// Space: O(n) +class Solution2 { +public: + int rotatedDigits(int N) { + enum State {INVALID, SAME, DIFF}; + const vector same = {0, 1, 8}; + const vector diff = {2, 5, 6, 9}; + vector dp(N + 1); + dp[0] = SAME; + for (int i = 0; 10 * i <= N; ++i) { + if (dp[i] != INVALID) { + for (const auto& j : same) { + if (i * 10 + j <= N) { + dp[i * 10 + j] = max(SAME, dp[i]); + } + } + for (const auto& j : diff) { + if (i * 10 + j <= N) { + dp[i * 10 + j] = DIFF; + } + } + } + } + return count(dp.cbegin(), dp.cend(), DIFF); + } +}; + +// Time: O(nlogn) = O(n), because O(logn) = O(32) by this input +// Space: O(logn) = O(1) +class Solution3 { +public: + int rotatedDigits(int N) { + const unordered_set invalid = {'3', '4', '7'}; + const unordered_set diff = {'2', '5', '6', '9'}; + int result = 0; + for (int i = 0; i <= N; ++i){ + string s(to_string(i)); + unordered_set lookup(s.begin(),s.end()); + if (intersect(invalid, lookup)) { + continue; + } + if (intersect(diff, lookup)) { + ++result; + } + } + return result; + } + +private: + template + bool intersect(const unordered_set& a, const unordered_set& b) { + if (a.size() > b.size()) { + return intersect(b, a); + } + return any_of(a.cbegin(), a.cend(), + [&b](const T& e) { + return b.count(e); + }); + } +}; diff --git a/C++/russian-doll-envelopes.cpp b/C++/russian-doll-envelopes.cpp new file mode 100644 index 000000000..f1c3aa3f9 --- /dev/null +++ b/C++/russian-doll-envelopes.cpp @@ -0,0 +1,28 @@ +// Time: O(nlogn + nlogk) = O(nlogn), k is the length of the result. +// Space: O(1) + +class Solution { +public: + int maxEnvelopes(vector>& envelopes) { + vector result; + + sort(envelopes.begin(), envelopes.end(), // O(nlogn) + [](const pair& a, const pair& b) { + if (a.first == b.first) { + return a.second > b.second; + } + return a.first < b.first; + }); + for (const auto& envelope : envelopes) { + const auto target = envelope.second; + auto it = lower_bound(result.begin(), result.end(), target); // O(logk) + if (it == result.end()) { + result.emplace_back(target); + } else { + *it = target; + } + } + + return result.size(); + } +}; diff --git a/C++/same-tree.cpp b/C++/same-tree.cpp new file mode 100644 index 000000000..6fa9f9dad --- /dev/null +++ b/C++/same-tree.cpp @@ -0,0 +1,23 @@ +// Time: O(n) +// Space: O(h), h is height of binary 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: + bool isSameTree(TreeNode* p, TreeNode* q) { + if (!p && !q) { + return true; + } + return p && q && p->val == q->val && + isSameTree(p->left, q->left) && + isSameTree(p->right, q->right); + } +}; diff --git a/C++/score-after-flipping-matrix.cpp b/C++/score-after-flipping-matrix.cpp new file mode 100644 index 000000000..dc2ce59ac --- /dev/null +++ b/C++/score-after-flipping-matrix.cpp @@ -0,0 +1,19 @@ +// Time: O(r * c) +// Space: O(1) + +class Solution { +public: + int matrixScore(vector>& A) { + int R = A.size(); + int C = A[0].size(); + int result = 0; + for (int c = 0; c < C; ++c) { + int col = 0; + for (int r = 0; r < R; ++r) { + col += A[r][c] ^ A[r][0]; + } + result += max(col, R - col) * (1 << (C - 1 - c)); + } + return result; + } +}; diff --git a/C++/score-of-parentheses.cpp b/C++/score-of-parentheses.cpp new file mode 100644 index 000000000..4cf6c8a9e --- /dev/null +++ b/C++/score-of-parentheses.cpp @@ -0,0 +1,38 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + int scoreOfParentheses(string S) { + int result = 0, depth = 0; + for (int i = 0; i < S.length(); ++i) { + if (S[i] == '(') { + ++depth; + } else { + --depth; + if (S[i - 1] == '(') { + result += 1 << depth; + } + } + } + return result; + } +}; + +// Time: O(n) +// Space: O(h) +class Solution2 { +public: + int scoreOfParentheses(string S) { + vector stack(1, 0); + for (const auto& c : S) { + if (c == '(') { + stack.emplace_back(0); + } else { + const auto last = stack.back(); stack.pop_back(); + stack.back() += max(1, 2 * last); + } + } + return stack.front(); + } +}; diff --git a/C++/search-a-2d-matrix-ii.cpp b/C++/search-a-2d-matrix-ii.cpp new file mode 100644 index 000000000..e2af261ce --- /dev/null +++ b/C++/search-a-2d-matrix-ii.cpp @@ -0,0 +1,30 @@ +// Time: O(m + n) +// Space: O(1) + +class Solution { +public: + bool searchMatrix(vector>& matrix, int target) { + const int m = matrix.size(); + if (m == 0) { + return false; + } + const int n = matrix[0].size(); + if (n == 0) { + return false; + } + int count = 0; + + int i = 0, j = n - 1; + while (i < m && j >= 0) { + if (matrix[i][j] == target) { + return true; + } else if (matrix[i][j] > target) { + --j; + } else { + ++i; + } + } + + return false; + } +}; diff --git a/C++/search-a-2d-matrix.cpp b/C++/search-a-2d-matrix.cpp new file mode 100644 index 000000000..a6fcfa723 --- /dev/null +++ b/C++/search-a-2d-matrix.cpp @@ -0,0 +1,34 @@ +// Time: O(logm + logn) +// Space: O(1) + +class Solution { +public: + bool searchMatrix(vector>& matrix, int target) { + if (matrix.empty()) { + return false; + } + + // Treat matrix as 1D array. + const int m = matrix.size(); + const int n = matrix[0].size(); + int left = 0; + int right = m * n - 1; + + // Find min of left s.t. matrix[left / n][left % n] >= target + while (left <= right) { + int mid = left + (right - left) / 2; + if (matrix[mid / n][mid % n] >= target) { + right = mid - 1; + } else { + left = mid + 1; + } + } + + // Check if matrix[left / n][left % n] equals to target. + if (left != m * n && matrix[left / n][left % n] == target) { + return true; + } + + return false; + } +}; diff --git a/C++/search-for-a-range.cpp b/C++/search-for-a-range.cpp new file mode 100644 index 000000000..cd775bc3f --- /dev/null +++ b/C++/search-for-a-range.cpp @@ -0,0 +1,59 @@ +// Time: O(logn) +// Space: O(1) + +class Solution { +public: + vector searchRange(vector& nums, int target) { + const auto start = lower_bound(nums.cbegin(), nums.cend(), target); + const auto end = upper_bound(nums.cbegin(), nums.cend(), target); + if (start != nums.cend() && *start == target) { + return {start - nums.cbegin(), end - nums.cbegin() - 1}; + } + return {-1, -1}; + } +}; + +class Solution2 { +public: + vector searchRange(vector &nums, int target) { + const int begin = lower_bound(nums, target); + const int end = upper_bound(nums, target); + + if (begin < nums.size() && nums[begin] == target) { + return {begin, end - 1}; + } + + return {-1, -1}; + } + +private: + int lower_bound(vector &nums, int target) { + int left = 0; + int right = nums.size(); + // Find min left s.t. A[left] >= target. + while (left < right) { + const auto mid = left + (right - left) / 2; + if (nums[mid] >= target) { + right = mid; + } else { + left = mid + 1; + } + } + return left; + } + + int upper_bound(vector &nums, int target) { + int left = 0; + int right = nums.size(); + // Find min left s.t. A[left] > target. + while (left < right) { + const auto mid = left + (right - left) / 2; + if (nums[mid] > target) { + right = mid; + } else { + left = mid + 1; + } + } + return left; + } +}; diff --git a/C++/search-in-a-binary-search-tree.cpp b/C++/search-in-a-binary-search-tree.cpp new file mode 100644 index 000000000..e4fa5b4bc --- /dev/null +++ b/C++/search-in-a-binary-search-tree.cpp @@ -0,0 +1,25 @@ +// Time: O(h) +// Space: O(1) + +/** + * 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* searchBST(TreeNode* root, int val) { + while (root && val != root->val) { + if (val < root->val) { + root = root->left; + } else { + root = root->right; + } + } + return root; + } +}; diff --git a/C++/search-in-a-sorted-array-of-unknown-size.cpp b/C++/search-in-a-sorted-array-of-unknown-size.cpp new file mode 100644 index 000000000..d30a7f7c5 --- /dev/null +++ b/C++/search-in-a-sorted-array-of-unknown-size.cpp @@ -0,0 +1,24 @@ +// Time: O(logn) +// Space: O(1) + +// Forward declaration of ArrayReader class. +class ArrayReader; + +class Solution { +public: + int search(const ArrayReader& reader, int target) { + int left = 0, right = 19999; + while (left <= right) { + auto mid = left + (right-left) / 2; + auto response = reader.get(mid); + if (response > target) { + right = mid - 1; + } else if (response < target) { + left = mid + 1; + } else { + return mid; + } + } + return -1; + } +}; diff --git a/C++/search-in-rotated-sorted-array-ii.cpp b/C++/search-in-rotated-sorted-array-ii.cpp new file mode 100644 index 000000000..8b03c92b5 --- /dev/null +++ b/C++/search-in-rotated-sorted-array-ii.cpp @@ -0,0 +1,48 @@ +// Time: O(logn) +// Space: O(1) + +class Solution { +public: + bool search(vector &nums, int target) { + int left = 0, right = nums.size() - 1; + + while (left <= right) { + int mid = left + (right - left) / 2; + if (nums[mid] == target) { + return true; + } else if (nums[mid] == nums[left]) { + ++left; + } else if ((nums[mid] > nums[left] && nums[left] <= target && target < nums[mid]) || + (nums[mid] < nums[left] && !(nums[mid] < target && target <= nums[right]))) { + right = mid - 1; + } else { + left = mid + 1; + } + } + + return false; + } +}; + +class Solution2 { +public: + bool search(vector &nums, int target) { + int left = 0, right = nums.size(); + + while (left < right) { + int mid = left + (right - left) / 2; + if (nums[mid] == target) { + return true; + } else if (nums[mid] == nums[left]) { + ++left; + } else if ((nums[left] <= nums[mid] && nums[left] <= target && target < nums[mid]) || + (nums[left] > nums[mid] && !(nums[mid] < target && target <= nums[right - 1]))) { + right = mid; + } else { + left = mid + 1; + } + } + + return false; + } +}; diff --git a/C++/search-in-rotated-sorted-array.cpp b/C++/search-in-rotated-sorted-array.cpp new file mode 100644 index 000000000..25578bc87 --- /dev/null +++ b/C++/search-in-rotated-sorted-array.cpp @@ -0,0 +1,44 @@ +// Time: O(logn) +// Space: O(1) + +class Solution { +public: + int search(vector& nums, int target) { + int left = 0, right = nums.size() - 1; + + while (left <= right) { + int mid = left + (right - left) / 2; + if (nums[mid] == target) { + return mid; + } else if ((nums[mid] >= nums[left] && nums[left] <= target && target < nums[mid]) || + (nums[mid] < nums[left] && !(nums[mid] < target && target <= nums[right]))) { + right = mid - 1; + } else { + left = mid + 1; + } + } + + return -1; + } +}; + +class Solution2 { +public: + int search(vector& nums, int target) { + int left = 0, right = nums.size(); + + while (left < right) { + int mid = left + (right - left) / 2; + if (nums[mid] == target) { + return mid; + } else if ((nums[left] <= nums[mid] && nums[left] <= target && target < nums[mid]) || + (nums[left] > nums[mid] && !(nums[mid] < target && target <= nums[right - 1]))) { + right = mid; + } else { + left = mid + 1; + } + } + + return -1; + } +}; diff --git a/C++/search-insert-position.cpp b/C++/search-insert-position.cpp new file mode 100644 index 000000000..b348cd62e --- /dev/null +++ b/C++/search-insert-position.cpp @@ -0,0 +1,21 @@ +// Time: O(logn) +// Space: O(1) + +class Solution { +public: + int searchInsert(vector& nums, int target) { + int left = 0; + int right = nums.size() - 1; + + while (left <= right) { + const auto mid = left + (right -left) / 2; + if (nums[mid] >= target) { + right = mid - 1; + } else { + left = mid + 1; + } + } + + return left; + } +}; diff --git a/C++/search.cpp b/C++/search.cpp deleted file mode 100644 index 60f5babe4..000000000 --- a/C++/search.cpp +++ /dev/null @@ -1,29 +0,0 @@ -// Time Complexity: O(logn) -// O(n) if duplicates are allowed -// Space Complexity: O(1) - -class Solution { - public: - bool search(int A[], int n, int target) { - for(int start = 0, end = n; start < end; ) { - const int mid = (start + end) / 2; - if(A[mid] == target) - return true; - if(A[start] < A[mid]) { - if(A[start] <= target && target < A[mid]) - end = mid; - else - start = mid + 1; - } - else if(A[start] > A[mid]) { - if(A[mid] < target && target <= A[end - 1]) - start = mid + 1; - else - end = mid; - } - else - ++start; - } - return false; - } -}; diff --git a/C++/searchMatrix.cpp b/C++/searchMatrix.cpp deleted file mode 100644 index 6bc9a9a07..000000000 --- a/C++/searchMatrix.cpp +++ /dev/null @@ -1,27 +0,0 @@ -// Time Complexity: O(m+n) (Akra-Bazzi theorem) -// Space Complexity: O(log(mn)) - -class Solution { - public: - bool partitionAndSearch(vector > &matrix, int target, int i, int j, int m, int n) { - if(m < 1 || n < 1) - return false; - int start, end; - for(start = 0, end = min(m, n); start < end;) { - int tmp = (start+end)/2; - if(target < matrix[i+tmp][j+tmp]) - end = tmp; - else if (target > matrix[i+tmp][j+tmp]) - start = tmp+1; - else - return true; - } - if(start < 1) - return false; - return partitionAndSearch(matrix, target, i, j+start, m, n - start) - || partitionAndSearch(matrix, target, i+start, j, m - start, n); - } - bool searchMatrix(vector > &matrix, int target) { - return partitionAndSearch(matrix, target, 0, 0, matrix.size(), matrix[0].size()); - } -}; diff --git a/C++/searchRange.cpp b/C++/searchRange.cpp deleted file mode 100644 index abcf490e0..000000000 --- a/C++/searchRange.cpp +++ /dev/null @@ -1,42 +0,0 @@ -// Time Complexity: O(logn) -// Space Complexity: O(1) - -class Solution { - public: - vector searchRange(int A[], int n, int target) { - int begin = lower_bound(A, n, target); - int end = upper_bound(A, n, target); - - if(begin < n && A[begin] == target) - return {begin, end - 1}; - - return {-1, -1}; - } - - private: - int lower_bound(int A[], int n, int target) { - int begin = 0; - int end = n; - while(begin < end) { - int mid = (begin + end) / 2; - if(A[mid] < target) - begin = mid + 1; - else - end = mid; - } - return begin; - } - - int upper_bound(int A[], int n, int target) { - int begin = 0; - int end = n; - while(begin < end) { - int mid = (begin + end) / 2; - if(A[mid] <= target) - begin = mid + 1; - else - end = mid; - } - return begin; - } -}; diff --git a/C++/second-minimum-node-in-a-binary-tree.cpp b/C++/second-minimum-node-in-a-binary-tree.cpp new file mode 100644 index 000000000..19134d7dd --- /dev/null +++ b/C++/second-minimum-node-in-a-binary-tree.cpp @@ -0,0 +1,36 @@ +// Time: O(n) +// Space: O(h) + +/** + * 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: + int findSecondMinimumValue(TreeNode* root) { + set bst; + findSecondMinimumValueHelper(root, &bst); + if (bst.size() < 2) { + return -1; + } + return *bst.rbegin(); + } + +private: + void findSecondMinimumValueHelper(TreeNode* root, set *bst) { + if (!root) { + return; + } + bst->emplace(root->val); + if (bst->size() > 2) { + bst->erase(prev(bst->end())); + } + findSecondMinimumValueHelper(root->left, bst); + findSecondMinimumValueHelper(root->right, bst); + } +}; diff --git a/C++/self-crossing.cpp b/C++/self-crossing.cpp new file mode 100644 index 000000000..2db45b704 --- /dev/null +++ b/C++/self-crossing.cpp @@ -0,0 +1,38 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + bool isSelfCrossing(vector& x) { + if (x.size() >= 5 && x[3] == x[1] && x[4] + x[0] >= x[2]) { + // Crossing in a loop: + // 2 + // 3 ┌────┐ + // └─══>┘1 + // 4 0 (overlapped) + return true; + } + + for (int i = 3; i < x.size(); ++i) { + if (x[i] >= x[i - 2] && x[i - 3] >= x[i - 1]) { + // Case 1: + // i-2 + // i-1┌─┐ + // └─┼─>i + // i-3 + return true; + } else if (i >= 5 && x[i - 4] <= x[i - 2] && x[i] + x[i - 4] >= x[i - 2] && + x[i - 1] <= x[i - 3] && x[i - 1] + x[i - 5] >= x[i - 3]) { + // Case 2: + // i-4 + // ┌──┐ + // │i<┼─┐ + // i-3│ i-5│i-1 + // └────┘ + // i-2 + return true; + } + } + return false; + } +}; diff --git a/C++/self-dividing-numbers.cpp b/C++/self-dividing-numbers.cpp new file mode 100644 index 000000000..d4a1565c5 --- /dev/null +++ b/C++/self-dividing-numbers.cpp @@ -0,0 +1,25 @@ +// Time: O(nlogr) = O(n) +// Space: O(logr) = O(1) + +class Solution { +public: + vector selfDividingNumbers(int left, int right) { + vector result; + for (int i = left; i <= right; ++i) { + if (isDividingNumber(i)) { + result.emplace_back(i); + } + } + return result; + } + +private: + bool isDividingNumber(int num) { + for (int n = num; n > 0; n /= 10) { + if (!(n % 10) || num % (n % 10)) { + return false; + } + } + return true; + } +}; diff --git a/C++/sentence-screen-fitting.cpp b/C++/sentence-screen-fitting.cpp new file mode 100644 index 000000000..e63cf0072 --- /dev/null +++ b/C++/sentence-screen-fitting.cpp @@ -0,0 +1,35 @@ +// Time: O(r + n * c) +// Space: O(n) + +class Solution { +public: + int wordsTyping(vector& sentence, int rows, int cols) { + vector wc(sentence.size()); + for (int i = 0; i < sentence.size(); ++i) { + wc[i] = wordsFit(sentence, i, cols); + } + + int words = 0, start = 0; + for (int i = 0; i < rows; ++i) { + words += wc[start]; + start = (start + wc[start]) % sentence.size(); + } + return words / sentence.size(); + } + +private: + int wordsFit(const vector& sentence, int start, int cols) { + if (sentence[start].length() > cols) { + return 0; + } + + int sum = sentence[start].length(), count = 1; + for (int i = (start + 1) % sentence.size(); + sum + 1 + sentence[i].length() <= cols; + i = (i + 1) % sentence.size()) { + sum += 1 + sentence[i].length(); + ++count; + } + return count; + } +}; diff --git a/C++/sentence-similarity-ii.cpp b/C++/sentence-similarity-ii.cpp new file mode 100644 index 000000000..e556e2e69 --- /dev/null +++ b/C++/sentence-similarity-ii.cpp @@ -0,0 +1,58 @@ +// Time: O(n + p) +// Space: O(p) + +class Solution { +public: + bool areSentencesSimilarTwo(vector& words1, vector& words2, vector> pairs) { + if (words1.size() != words2.size()) { + return false; + } + unordered_map lookup; + UnionFind union_find(2 * pairs.size()); + for (const auto& pair : pairs) { + if (!lookup.count(pair.first)) { + lookup.emplace(pair.first, lookup.size()); + } + if (!lookup.count(pair.second)) { + lookup.emplace(pair.second, lookup.size()); + } + union_find.union_set(lookup[pair.first], lookup[pair.second]); + } + for (int i = 0; i < words1.size(); ++i) { + if (words1[i] != words2[i] && + (!lookup.count(words1[i]) || !lookup.count(words2[i]) || + union_find.find_set(lookup[words1[i]]) != + union_find.find_set(lookup[words2[i]]))) { + return false; + } + } + return true; + } + +private: + class UnionFind { + public: + UnionFind(const int n) : set_(n) { + iota(set_.begin(), set_.end(), 0); + } + + int find_set(const int x) { + if (set_[x] != x) { + set_[x] = find_set(set_[x]); // Path compression. + } + return set_[x]; + } + + bool union_set(const int x, const int y) { + int x_root = find_set(x), y_root = find_set(y); + if (x_root == y_root) { + return false; + } + set_[min(x_root, y_root)] = max(x_root, y_root); + return true; + } + + private: + vector set_; + }; +}; diff --git a/C++/sentence-similarity.cpp b/C++/sentence-similarity.cpp new file mode 100644 index 000000000..076568ee3 --- /dev/null +++ b/C++/sentence-similarity.cpp @@ -0,0 +1,34 @@ +// Time: O(n + p) +// Space: O(p) + +class Solution { +public: + bool areSentencesSimilar(vector& words1, vector& words2, vector> pairs) { + if (words1.size() != words2.size()) { + return false; + } + unordered_set, PairHash> lookup; + for (const auto& pair : pairs) { + lookup.emplace(pair.first, pair.second); + lookup.emplace(pair.second, pair.first); + } + for (int i = 0; i < words1.size(); ++i) { + if (words1[i] != words2[i] && + !lookup.count(make_pair(words1[i], words2[i]))) { + return false; + } + } + return true; + } + +private: + template + struct PairHash { + size_t operator()(const pair& p) const { + size_t seed = 0; + seed ^= std::hash{}(p.first) + 0x9e3779b9 + (seed<<6) + (seed>>2); + seed ^= std::hash{}(p.second) + 0x9e3779b9 + (seed<<6) + (seed>>2); + return seed; + } + }; +}; diff --git a/C++/sequence-reconstruction.cpp b/C++/sequence-reconstruction.cpp new file mode 100644 index 000000000..2778589d1 --- /dev/null +++ b/C++/sequence-reconstruction.cpp @@ -0,0 +1,36 @@ +// Time: O(n * s), n is the size of org, s is the size of seqs +// Space: O(n) + +class Solution { +public: + bool sequenceReconstruction(vector& org, vector>& seqs) { + if (seqs.empty()) { + return false; + } + vector pos(org.size() + 1); + for (int i = 0; i < org.size(); ++i) { + pos[org[i]] = i; + } + + vector is_matched(org.size() + 1); + int cnt_to_match = org.size() - 1; + for (const auto& seq : seqs) { + for (int i = 0; i < seq.size(); ++i) { + if (seq[i] <= 0 || seq[i] > org.size()) { + return false; + } + if (i == 0) { + continue; + } + if (pos[seq[i - 1]] >= pos[seq[i]]) { + return false; + } + if (is_matched[seq[i - 1]] == false && pos[seq[i - 1]] + 1 == pos[seq[i]]) { + is_matched[seq[i - 1]] = true; + --cnt_to_match; + } + } + } + return cnt_to_match == 0; + } +}; diff --git a/C++/serialize-and-deserialize-binary-tree.cpp b/C++/serialize-and-deserialize-binary-tree.cpp new file mode 100644 index 000000000..baeb0076e --- /dev/null +++ b/C++/serialize-and-deserialize-binary-tree.cpp @@ -0,0 +1,119 @@ +// Time: O(n) +// Space: O(h) + +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode(int x) : val(x), left(NULL), right(NULL) {} + * }; + */ +class Codec { +public: + + // Encodes a tree to a single string. + string serialize(TreeNode* root) { + string output; + serializeHelper(root, &output); + return output; + } + + // Decodes your encoded data to tree. + TreeNode* deserialize(string data) { + TreeNode *root = nullptr; + int start = 0; + return deserializeHelper(data, &start); + } + +private: + bool getNumber(const string &data, int *start, int *num) { + int sign = 1; + if (data[*start] == '#') { + *start += 2; // Skip "# ". + return false; + } else if (data[*start] == '-') { + sign = -1; + ++(*start); + } + + for (*num = 0; isdigit(data[*start]); ++(*start)) { + *num = *num * 10 + data[*start] - '0'; + } + *num *= sign; + ++(*start); // Skip " ". + + return true; + } + + void serializeHelper(const TreeNode *root, string *prev) { + if (!root) { + prev->append("# "); + } else { + prev->append(to_string(root->val).append(" ")); + serializeHelper(root->left, prev); + serializeHelper(root->right, prev); + } + } + + TreeNode *deserializeHelper(const string& data, int *start) { + int num; + if (!getNumber(data, start, &num)) { + return nullptr; + } else { + TreeNode *root = new TreeNode(num); + root->left = deserializeHelper(data, start); + root->right = deserializeHelper(data, start); + return root; + } + } +}; + + +// Time: O(n) +// Space: O(n) +class Codec2 { +public: + + // Encodes a tree to a single string. + string serialize(TreeNode* root) { + ostringstream out; + serializeHelper(root, out); + return out.str(); + } + + // Decodes your encoded data to tree. + TreeNode* deserialize(string data) { + istringstream in(data); // Space: O(n) + return deserializeHelper(in); + } + +private: + void serializeHelper(const TreeNode *root, ostringstream& out) { + if (!root) { + out << "# "; + } else { + out << root->val << " "; + serializeHelper(root->left, out); + serializeHelper(root->right, out); + } + } + + TreeNode *deserializeHelper(istringstream& in) { + string val; + in >> val; + if (val == "#") { + return nullptr; + } else { + TreeNode* root = new TreeNode(stoi(val)); + root->left = deserializeHelper(in); + root->right = deserializeHelper(in); + return root; + } + } +}; + +// Your Codec object will be instantiated and called as such: +// Codec codec; +// codec.deserialize(codec.serialize(root)); diff --git a/C++/serialize-and-deserialize-bst.cpp b/C++/serialize-and-deserialize-bst.cpp new file mode 100644 index 000000000..8ac849122 --- /dev/null +++ b/C++/serialize-and-deserialize-bst.cpp @@ -0,0 +1,59 @@ +// Time: O(n) +// Space: O(h) + +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode(int x) : val(x), left(NULL), right(NULL) {} + * }; + */ +class Codec { +public: + + // Encodes a tree to a single string. + string serialize(TreeNode* root) { + string data; + serializeHelper(root, &data); + return data; + } + + // Decodes your encoded data to tree. + TreeNode* deserialize(string data) { + int i = 0; + return deserializeHelper(numeric_limits::min(), numeric_limits::max(), data, &i); + } + + +private: + void serializeHelper(TreeNode *node, string *data) { + if (node) { + *data += to_string(node->val) + " "; + serializeHelper(node->left, data); + serializeHelper(node->right, data); + } + } + + TreeNode* deserializeHelper(int minVal, int maxVal, const string& data, int *i) { + if (*i == data.length()) { + return nullptr; + } + int j = data.find(' ', *i); + auto val = stoi(data.substr(*i, j - *i)); + if (minVal < val && val < maxVal) { + auto node = new TreeNode(val); + *i = j + 1; + node->left = deserializeHelper(minVal, val, data, i); + node->right = deserializeHelper(val, maxVal, data, i); + return node; + } else { + return nullptr; + } + } +}; + +// Your Codec object will be instantiated and called as such: +// Codec codec; +// codec.deserialize(codec.serialize(root)); diff --git a/C++/serialize-and-deserialize-n-ary-tree.cpp b/C++/serialize-and-deserialize-n-ary-tree.cpp new file mode 100644 index 000000000..76bd46986 --- /dev/null +++ b/C++/serialize-and-deserialize-n-ary-tree.cpp @@ -0,0 +1,137 @@ +// Time: O(n) +// Space: O(h) + +/* +// Definition for a Node. +class Node { +public: + int val = NULL; + vector children; + + Node() {} + + Node(int _val, vector _children) { + val = _val; + children = _children; + } +}; +*/ +class Codec { +public: + + // Encodes a tree to a single string. + string serialize(Node* root) { + ostringstream out; + serializeHelper(root, out); + return out.str(); + } + + // Decodes your encoded data to tree. + Node* deserialize(string data) { + if (data.empty()) { + return nullptr; + } + int start = 0; + return deserializeHelper(data, &start); + } + +private: + void serializeHelper(const Node *root, ostringstream& out) { + if (!root) { + return; + } + + out << root->val << " "; + for (const auto& child : root->children) { + serializeHelper(child, out); + } + out << "# "; + } + + Node *deserializeHelper(const string& data, int *start) { + int num; + if (!getNumber(data, start, &num)) { + return nullptr; + } + auto root = new Node(num); + for (auto child = deserializeHelper(data, start); + child != nullptr; + child = deserializeHelper(data, start)) { + root->children.emplace_back(child); + } + return root; + } + + bool getNumber(const string& data, int *start, int *num) { + int sign = 1; + if (data[*start] == '#') { + *start += 2; // Skip "# ". + return false; + } else if (data[*start] == '-') { + sign = -1; + ++(*start); + } + + for (*num = 0; isdigit(data[*start]); ++(*start)) { + *num = *num * 10 + data[*start] - '0'; + } + *num *= sign; + ++(*start); // Skip " ". + + return true; + } +}; + +// Time: O(n) +// Space: O(n) +class Codec2 { +public: + + // Encodes a tree to a single string. + string serialize(Node* root) { + ostringstream out; + serializeHelper(root, out); + return out.str(); + } + + // Decodes your encoded data to tree. + Node* deserialize(string data) { + if (data.empty()) { + return nullptr; + } + istringstream in(data); // Space: O(n) + return deserializeHelper(in); + } + +private: + void serializeHelper(const Node *root, ostringstream& out) { + if (!root) { + return; + } + + out << root->val << " "; + for (const auto& child : root->children) { + serializeHelper(child, out); + } + out << "# "; + } + + Node *deserializeHelper(istringstream& in) { + string val; + in >> val; + if (val == "#") { + return nullptr; + } + auto root = new Node(stoi(val)); + for (auto child = deserializeHelper(in); + child != nullptr; + child = deserializeHelper(in)) { + root->children.emplace_back(child); + } + return root; + } +}; + +// Your Codec object will be instantiated and called as such: +// Codec codec; +// codec.deserialize(codec.serialize(root)); diff --git a/C++/set-intersection-size-at-least-two.cpp b/C++/set-intersection-size-at-least-two.cpp new file mode 100644 index 000000000..834948726 --- /dev/null +++ b/C++/set-intersection-size-at-least-two.cpp @@ -0,0 +1,28 @@ +// Time: O(nlogn) +// Space: O(n) + +// greedy solution +class Solution { +public: + int intersectionSizeTwo(vector>& intervals) { + sort(intervals.begin(), intervals.end(), + [](const vector& a, const vector& b) { + return (a[0] != b[0]) ? (a[0] < b[0]) : (b[1] < a[1]); + }); + vector cnts(intervals.size(), 2); + int result = 0; + while (!intervals.empty()) { + auto start = intervals.back()[0]; intervals.pop_back(); + auto cnt = cnts.back(); cnts.pop_back(); + for (int s = start; s < start + cnt; ++s) { + for (int i = 0; i < intervals.size(); ++i) { + if (cnts[i] && s <= intervals[i][1]) { + --cnts[i]; + } + } + } + result += cnt; + } + return result; + } +}; diff --git a/C++/set-matrix-zeroes.cpp b/C++/set-matrix-zeroes.cpp new file mode 100644 index 000000000..b251ce394 --- /dev/null +++ b/C++/set-matrix-zeroes.cpp @@ -0,0 +1,50 @@ +// Time: O(m * n) +// Space: O(1) + +class Solution { +public: + void setZeroes(vector>& matrix) { + if (matrix.empty()) { + return; + } + + bool has_zero = false; + int zero_i = -1, zero_j = -1; + + for (int i = 0; i < matrix.size(); ++i) { + for (int j = 0; j < matrix[0].size(); ++j) { + if (matrix[i][j] == 0) { + if (!has_zero) { + zero_i = i; + zero_j = j; + has_zero = true; + } + matrix[zero_i][j] = 0; + matrix[i][zero_j] = 0; + } + } + } + + if (has_zero) { + for (int i = 0; i < matrix.size(); ++i) { + if (i == zero_i) { + continue; + } + for (int j = 0; j < matrix[0].size(); ++j) { + if (j == zero_j) { + continue; + } + if (matrix[zero_i][j] == 0 || matrix[i][zero_j] == 0) { + matrix[i][j] = 0; + } + } + } + for (int i = 0; i < matrix.size(); ++i) { + matrix[i][zero_j] = 0; + } + for (int j = 0; j < matrix[0].size(); ++j) { + matrix[zero_i][j] = 0; + } + } + } +}; diff --git a/C++/set-mismatch.cpp b/C++/set-mismatch.cpp new file mode 100644 index 000000000..f5a28b1e6 --- /dev/null +++ b/C++/set-mismatch.cpp @@ -0,0 +1,47 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + vector findErrorNums(vector& nums) { + int x_xor_y = 0; + for (int i = 0; i < nums.size(); ++i) { + x_xor_y ^= nums[i] ^ (i + 1); + } + int bit = x_xor_y & ~(x_xor_y - 1); + vector result(2); + for (int i = 0; i < nums.size(); ++i) { + result[static_cast(nums[i] & bit)] ^= nums[i]; + result[static_cast((i + 1) & bit)] ^= i + 1; + } + if (find(nums.begin(), nums.end(), result[0]) == nums.end()) { + swap(result[0], result[1]); + } + return result; + } +}; + + +// Time: O(n) +// Space: O(1) +class Solution2 { +public: + vector findErrorNums(vector& nums) { + vector result(2); + for (const auto& i : nums) { + if (nums[abs(i) - 1] < 0) { // twice + result[0] = abs(i); + } else { + nums[abs(i) - 1] *= -1; + } + } + for (int i = 0; i < nums.size(); ++i) { + if (nums[i] > 0) { // missing + result[1] = i + 1; + } else { + nums[i] *= -1; + } + } + return result; + } +}; diff --git a/C++/shifting-letters.cpp b/C++/shifting-letters.cpp new file mode 100644 index 000000000..4c23b114a --- /dev/null +++ b/C++/shifting-letters.cpp @@ -0,0 +1,15 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + string shiftingLetters(string S, vector& shifts) { + string result; + auto times = accumulate(shifts.cbegin(), shifts.cend(), 0L) % 26; + for (int i = 0; i < S.length(); ++i) { + result.push_back('a' + (S[i] - 'a' + times) % 26); + times = ((times - shifts[i]) % 26 + 26) % 26; + } + return result; + } +}; diff --git a/C++/shopping-offers.cpp b/C++/shopping-offers.cpp new file mode 100644 index 000000000..09b8ec402 --- /dev/null +++ b/C++/shopping-offers.cpp @@ -0,0 +1,31 @@ +// Time: O(n * 2^n) +// Space: O(n) + +class Solution { +public: + int shoppingOffers(vector& price, vector>& special, vector& needs) { + return shoppingOffersHelper(price, special, needs, 0); + } + +private: + int shoppingOffersHelper(const vector& price, const vector>& special, vector& needs, int i) { + if (i == special.size()) { + return inner_product(price.begin(), price.end(), needs.begin(), 0); + } + + int result = shoppingOffersHelper(price, special, needs, i + 1); + + for (int j = 0; j < needs.size(); ++j) { + needs[j] -= special[i][j]; + } + if (all_of(needs.begin(), needs.end(), [](int i) { return i >= 0; })) { + result = min(result, special[i].back() + shoppingOffersHelper(price, special, needs, i)); + } + for (int j = 0; j < needs.size(); ++j) { + needs[j] += special[i][j]; + } + + return result; + } +}; + diff --git a/C++/short-encoding-of-words.cpp b/C++/short-encoding-of-words.cpp new file mode 100644 index 000000000..c1adde4d5 --- /dev/null +++ b/C++/short-encoding-of-words.cpp @@ -0,0 +1,45 @@ +// Time: O(n), n is the total sum of the lengths of words +// Space: O(t), t is the number of nodes in trie + +class Solution { +public: + int minimumLengthEncoding(vector& words) { + unordered_set unique_words(words.cbegin(), words.cend()); + vector leaves; + TrieNode trie; + for (auto word : unique_words) { + reverse(word.begin(), word.end()); + leaves.emplace_back(trie.Insert(word)); + } + int result = 0; + int i = 0; + for (const auto& word: unique_words) { + if (leaves[i++]->leaves.empty()) { + result += word.length() + 1; + } + } + return result; + } + +private: + struct TrieNode { + unordered_map leaves; + + TrieNode *Insert(const string& s) { + auto* p = this; + for (const auto& c : s) { + if (!p->leaves[c - 'a']) { + p->leaves[c - 'a'] = new TrieNode; + } + p = p->leaves[c - 'a']; + } + return p; + } + + ~TrieNode() { + for (auto& node : leaves) { + delete node.second; + } + } + }; +}; diff --git a/C++/shortest-completing-word.cpp b/C++/shortest-completing-word.cpp new file mode 100644 index 000000000..0ece33588 --- /dev/null +++ b/C++/shortest-completing-word.cpp @@ -0,0 +1,38 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + string shortestCompletingWord(string licensePlate, vector& words) { + string result; + const auto& counter = counts(licensePlate); + for (const auto& word : words) { + if ((result.empty() || word.length() < result.length()) && + contains(counter, word)) { + result = word; + } + } + return result; + } + +private: + bool contains(const vector& counter1, const string& w2) const { + const auto& counter2 = counts(w2); + for (int i = 0; i < counter2.size(); ++i) { + if (counter1[i] > counter2[i]) { + return false; + } + } + return true; + } + + vector counts(const string& s) const { + vector count(26); + for (const auto& c : s) { + if (isalpha(c)) { + ++count[tolower(c) - 'a']; + } + } + return count; + } +}; diff --git a/C++/shortest-distance-from-all-buildings.cpp b/C++/shortest-distance-from-all-buildings.cpp new file mode 100644 index 000000000..218721bfa --- /dev/null +++ b/C++/shortest-distance-from-all-buildings.cpp @@ -0,0 +1,59 @@ +// Time: O(k * m * n), k is the number of the buildings +// Space: O(m * n) + +class Solution { +public: + int shortestDistance(vector>& grid) { + int m = grid.size(), n = grid[0].size(), cnt = 0; + vector> dists(m, vector(n)), cnts(m, vector(n)); + for (int i = 0; i < m; ++i) { + for (int j = 0; j < n; ++j) { + if (grid[i][j] == 1) { + ++cnt; + BFS(grid, i, j, &dists, &cnts); + } + } + } + + int shortest = numeric_limits::max(); + for (int i = 0; i < m; ++i) { + for (int j = 0; j < n; ++j) { + if (dists[i][j] < shortest && cnts[i][j] == cnt) { + shortest = dists[i][j]; + } + } + } + + return shortest != numeric_limits::max() ? shortest : -1; + } + + void BFS(const vector>& grid, int x, int y, + vector> *dists, vector> *cnts) { + int dist = 0, m = grid.size(), n = grid[0].size(); + vector> visited(m, vector(n)); + + vector> pre_level{{x, y}}, cur_level; + visited[x][y] = true; + while (!pre_level.empty()) { + ++dist; + cur_level.clear(); + for (const auto& p : pre_level) { + int i, j; + tie(i, j) = p; + const vector> directions{{0, -1}, {0, 1}, + {-1, 0}, {1, 0}}; + for (const auto& d : directions) { + const int I = i + d.first, J = j + d.second; + if (0 <= I && I < m && 0 <= J && J < n && + grid[I][J] == 0 && !visited[I][J]) { + (*dists)[I][J] += dist; + ++(*cnts)[I][J]; + cur_level.push_back({I, J}); + visited[I][J] = true; + } + } + } + swap(pre_level, cur_level); + } + } +}; diff --git a/C++/shortest-distance-to-a-character.cpp b/C++/shortest-distance-to-a-character.cpp new file mode 100644 index 000000000..872ae0daf --- /dev/null +++ b/C++/shortest-distance-to-a-character.cpp @@ -0,0 +1,23 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + vector shortestToChar(string S, char C) { + vector result(S.length(), S.length()); + int prev = -S.length(); + for (int i = 0; i < S.length(); ++i) { + if (S[i] == C) { + prev = i; + } + result[i] = min(result[i], abs(i - prev)); + } + for (int i = S.length() - 1; i >= 0; --i) { + if (S[i] == C) { + prev = i; + } + result[i] = min(result[i], abs(i - prev)); + } + return result; + } +}; diff --git a/C++/shortest-palindrome.cpp b/C++/shortest-palindrome.cpp new file mode 100644 index 000000000..094d6df5e --- /dev/null +++ b/C++/shortest-palindrome.cpp @@ -0,0 +1,104 @@ +// Time: O(n) +// Space: O(n) + +// KMP Algorithm +class Solution { +public: + string shortestPalindrome(string s) { + if (s.empty()) { + return s; + } + string rev_s(s.crbegin(), s.crend()); + // Assume s is (Palindrome)abc, + // A would be (Palindrome)abccba(Palindrome). + string A = s + rev_s; + auto prefix = getPrefix(A); + // The index prefix.back() of A would be: + // (Palindrome)abccba(Palindrome) + // ^ + // The index prefix.back() + 1 of s would be: + // (Palindrome)abc + // ^ + // Get non palindrome part of s. + int i = prefix.back(); + while (i >= s.length()) { + i = prefix[i]; + } + string non_palindrome = s.substr(i + 1); + reverse(non_palindrome.begin(), non_palindrome.end()); + return non_palindrome + s; // cba(Palindrome)abc. + } + +private: + vector getPrefix(const string& pattern) { + vector prefix(pattern.length(), -1); + int j = -1; + for (int i = 1; i < pattern.length(); ++i) { + while (j > -1 && pattern[j + 1] != pattern[i]) { + j = prefix[j]; + } + if (pattern[j + 1] == pattern[i]) { + ++j; + } + prefix[i] = j; + } + return prefix; + } +}; + +// Time: O(n) +// Space: O(n) +// Manacher's Algorithm +class Solution2 { +public: + string shortestPalindrome(string s) { + string T = preProcess(s); + int n = T.length(); + vector P(n); + int C = 0, R = 0; + for (int i = 1; i < n - 1; ++i) { + int i_mirror = 2 * C - i; // equals to i' = C - (i-C) + + P[i] = (R > i) ? min(R - i, P[i_mirror]) : 0; + + // Attempt to expand palindrome centered at i + while (T[i + 1 + P[i]] == T[i - 1 - P[i]]) { + ++P[i]; + } + + // If palindrome centered at i expand past R, + // adjust center based on expanded palindrome. + if (i + P[i] > R) { + C = i; + R = i + P[i]; + } + } + + // Find the max len of palindrome which starts with the first char of s. + int max_len = 0; + for (int i = 1; i < n - 1; ++i) { + if (i - P[i] == 1) { + max_len = P[i]; + } + } + + // Assume s is (Palindrome)abc. + string ans = s.substr(max_len); // abc. + reverse(ans.begin(), ans.end()); // cba. + ans.append(s); // cba(Palindrome)abc. + return ans; + } +private: + string preProcess(string s) { + int n = s.length(); + if (n == 0) { + return "^$"; + } + string ret = "^"; + for (int i = 0; i < n; ++i) { + ret += "#" + s.substr(i, 1); + } + ret += "#$"; + return ret; + } +}; diff --git a/C++/shortest-path-to-get-all-keys.cpp b/C++/shortest-path-to-get-all-keys.cpp new file mode 100644 index 000000000..8e256a0d1 --- /dev/null +++ b/C++/shortest-path-to-get-all-keys.cpp @@ -0,0 +1,105 @@ +// Time: O(k*r*c + |E|log|V|) = O(k*r*c + (k*|V|)*log|V|) +// = O(k*r*c + (k*(k*2^k))*log(k*2^k)) +// = O(k*r*c + (k*(k*2^k))*(logk + k*log2)) +// = O(k*r*c + (k*(k*2^k))*k) +// = O(k*r*c + k^3*2^k) +// Space: O(|V|) = O(k*2^k) + +class Solution { +public: + int shortestPathAllKeys(vector& grid) { + unordered_map> locations; + for (int r = 0; r < grid.size(); ++r) { + for (int c = 0; c < grid[0].size(); ++c) { + if (string(".#").find(grid[r][c]) == string::npos) { + locations[grid[r][c]] = make_pair(r, c); + } + } + } + unordered_map> dists; + for (const auto& kvp : locations) { + dists[kvp.first] = bfs(grid, kvp.first, locations); + } + + // Dijkstra's algorithm + using T = tuple; + priority_queue, greater> min_heap; + min_heap.emplace(0, '@', 0); + unordered_map> best; + best['@'][0] = 0; + int count = 0; + for (const auto& kvp : locations) { + if (islower(kvp.first)) { + ++count; + } + } + uint32_t target_state = (1 << count) - 1; + while (!min_heap.empty()) { + int cur_d, state; + char place; + tie(cur_d, place, state) = min_heap.top(); min_heap.pop(); + if (best.count(place) && + best[place].count(state) && + best[place][state] < cur_d) { + continue; + } + if (state == target_state) { + return cur_d; + } + for (const auto& kvp : dists[place]) { + int dest, d; + tie(dest, d) = kvp; + auto next_state = state; + if (islower(dest)) { + next_state |= (1 << (dest - 'a')); + } else if (isupper(dest)) { + if (!(state & (1 << (dest - 'A')))) { + continue; + } + } + if (!best.count(dest) || + !best[dest].count(next_state) || + cur_d + d < best[dest][next_state]) { + best[dest][next_state] = cur_d + d; + min_heap.emplace(cur_d + d, dest, next_state); + } + } + } + return -1; + } + +private: + unordered_map bfs(const vector&grid, + char source, + const unordered_map>& locations) { + static const vector> directions{{0, -1}, {0, 1}, + {-1, 0}, {1, 0}}; + int r, c; + tie(r, c) = locations.at(source); + vector> lookup(grid.size(), vector(grid[0].size())); + lookup[r][c] = true; + queue> q; + q.emplace(r, c, 0); + unordered_map dist; + while (!q.empty()) { + int r, c, d; + tie(r, c, d) = q.front(); q.pop(); + if (source != grid[r][c] && grid[r][c] != '.') { + dist[grid[r][c]] = d; + continue; + } + for (const auto& dir : directions) { + int cr = r + dir.first, cc = c + dir.second; + if (!((0 <= cr && cr < grid.size()) && + (0 <= cc && cc < grid[0].size()))) { + continue; + } + if (grid[cr][cc] != '#' && !lookup[cr][cc]) { + lookup[cr][cc] = true; + q.emplace(cr, cc, d + 1); + } + } + } + return dist ; + } +}; diff --git a/C++/shortest-path-visiting-all-nodes.cpp b/C++/shortest-path-visiting-all-nodes.cpp new file mode 100644 index 000000000..a99d8f8b9 --- /dev/null +++ b/C++/shortest-path-visiting-all-nodes.cpp @@ -0,0 +1,29 @@ +// Time: O(n * 2^n) +// Space: O(n * 2^n) + +class Solution { +public: + int shortestPathLength(vector>& graph) { + static const auto& INF = numeric_limits::max(); + vector> dp(1 << graph.size(), + vector(graph.size(), INF)); + queue> q; + for (int i = 0; i < graph.size(); ++i) { + dp[1 << i][i] = 0; + q.emplace(1 << i, i); + } + while (!q.empty()) { + int state, node; + tie(state, node) = q.front(); q.pop(); + auto steps = dp[state][node]; + for (const auto& nei : graph[node]) { + auto new_state = state | (1 << nei); + if (dp[new_state][nei] == INF) { + dp[new_state][nei] = steps + 1; + q.emplace(new_state, nei); + } + } + } + return *min_element(dp.back().cbegin(), dp.back().cend()); + } +}; diff --git a/C++/shortest-subarray-with-sum-at-least-k.cpp b/C++/shortest-subarray-with-sum-at-least-k.cpp new file mode 100644 index 000000000..5d8628e7e --- /dev/null +++ b/C++/shortest-subarray-with-sum-at-least-k.cpp @@ -0,0 +1,26 @@ +// Time: O(n) +// Space: O(n) + +class Solution { +public: + int shortestSubarray(vector& A, int K) { + vector accumulated_sum(A.size() + 1, 0); + partial_sum(A.cbegin(), A.cend(), next(accumulated_sum.begin()), plus()); + + int result = numeric_limits::max(); + deque mono_increasing_q; + for (int i = 0; i < accumulated_sum.size(); ++i) { + while (!mono_increasing_q.empty() && + accumulated_sum[i] <= accumulated_sum[mono_increasing_q.back()]) { + mono_increasing_q.pop_back(); + } + while (!mono_increasing_q.empty() && + accumulated_sum[i] - accumulated_sum[mono_increasing_q.front()] >= K) { + result = min(result, i - mono_increasing_q.front()); + mono_increasing_q.pop_front(); + } + mono_increasing_q.emplace_back(i); + } + return result != numeric_limits::max() ? result : -1; + } +}; diff --git a/C++/shortest-unsorted-continuous-subarray.cpp b/C++/shortest-unsorted-continuous-subarray.cpp new file mode 100644 index 000000000..fe45e1ac8 --- /dev/null +++ b/C++/shortest-unsorted-continuous-subarray.cpp @@ -0,0 +1,22 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + int findUnsortedSubarray(vector& nums) { + const auto n = nums.size(); + auto left = -1, right = -2; + auto max_from_left = nums[0], min_from_right = nums.back(); + for (int i = 1; i < n; ++i) { + max_from_left = max(max_from_left, nums[i]); + min_from_right = min(min_from_right, nums[n - 1 - i]); + if (nums[i] < max_from_left) { + right = i; + } + if (nums[n - 1 - i] > min_from_right) { + left = n - 1 - i; + } + } + return right - left + 1; + } +}; diff --git a/C++/shortest-word-distance-ii.cpp b/C++/shortest-word-distance-ii.cpp new file mode 100644 index 000000000..a402d7b15 --- /dev/null +++ b/C++/shortest-word-distance-ii.cpp @@ -0,0 +1,26 @@ +// Time: ctor: O(n), shortest: O(a + b), a, b is occurences of word1, word2 +// Space: O(n) + +class WordDistance { +public: + WordDistance(vector words) { + for (int i = 0; i < words.size(); ++i) { + wordIndex[words[i]].emplace_back(i); + } + } + + int shortest(string word1, string word2) { + const vector& indexes1 = wordIndex[word1]; + const vector& indexes2 = wordIndex[word2]; + + int i = 0, j = 0, dist = INT_MAX; + while (i < indexes1.size() && j < indexes2.size()) { + dist = min(dist, abs(indexes1[i] - indexes2[j])); + indexes1[i] < indexes2[j] ? ++i : ++j; + } + return dist; + } + +private: + unordered_map> wordIndex; +}; diff --git a/C++/shortest-word-distance-iii.cpp b/C++/shortest-word-distance-iii.cpp new file mode 100644 index 000000000..6043f572f --- /dev/null +++ b/C++/shortest-word-distance-iii.cpp @@ -0,0 +1,24 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + int shortestWordDistance(vector& words, string word1, string word2) { + int dist = INT_MAX; + bool is_same = (word1 == word2); + for (int i = 0, index1 = -1, index2 = -1; i < words.size(); ++i) { + if (words[i] == word1) { + if (is_same && index1 != -1) { + dist = min(dist, abs(index1 - i)); + } + index1 = i; + } else if (words[i] == word2) { + index2 = i; + } + if (index1 != -1 && index2 != -1) { + dist = min(dist, abs(index1 - index2)); + } + } + return dist; + } +}; diff --git a/C++/shortest-word-distance.cpp b/C++/shortest-word-distance.cpp new file mode 100644 index 000000000..cd7957e2d --- /dev/null +++ b/C++/shortest-word-distance.cpp @@ -0,0 +1,20 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + int shortestDistance(vector& words, string word1, string word2) { + int dist = INT_MAX; + for (int i = 0, index1 = -1, index2 = -1; i < words.size(); ++i) { + if (words[i] == word1) { + index1 = i; + } else if (words[i] == word2) { + index2 = i; + } + if (index1 != -1 && index2 != -1) { + dist = min(dist, abs(index1 - index2)); + } + } + return dist; + } +}; diff --git a/C++/shuffle-an-array.cpp b/C++/shuffle-an-array.cpp new file mode 100644 index 000000000..2d60b923f --- /dev/null +++ b/C++/shuffle-an-array.cpp @@ -0,0 +1,36 @@ +// Time: O(n) +// Space: O(n) + +class Solution { +public: + Solution(vector nums) : nums_(nums) { + + } + + /** Resets the array to its original configuration and return it. */ + vector reset() { + return nums_; + } + + /** Returns a random shuffling of the array. */ + vector shuffle() { + vector nums{nums_}; + default_random_engine seed((random_device())()); + for (int i = 0; i < nums.size(); ++i) { + swap(nums[i], nums[uniform_int_distribution{ + i, static_cast(nums.size()) - 1}(seed)]); + } + return nums; + } + +private: + const vector nums_; +}; + +/** + * Your Solution object will be instantiated and called as such: + * Solution obj = new Solution(nums); + * vector param_1 = obj.reset(); + * vector param_2 = obj.shuffle(); + */ + diff --git a/C++/similar-rgb-color.cpp b/C++/similar-rgb-color.cpp new file mode 100644 index 000000000..39a7c8809 --- /dev/null +++ b/C++/similar-rgb-color.cpp @@ -0,0 +1,24 @@ +// Time: O(1) +// Space: O(1) + +class Solution { +public: + string similarRGB(string color) { + string result = "#"; + result += round(color.substr(1, 2)); + result += round(color.substr(3, 2)); + result += round(color.substr(5, 2)); + return result; + } + +private: + string round(const string& color) { + std::stringstream in(color); + int decimal = 0; + in >> std::hex >> decimal; + decimal = decimal / 17 + (decimal % 17 > 8 ? 1 : 0); + std::stringstream out; + out << std::setfill('0') << std::setw(2) << std::hex << 17 * q; + return out.str(); + } +}; diff --git a/C++/similar-string-groups.cpp b/C++/similar-string-groups.cpp new file mode 100644 index 000000000..da15f3510 --- /dev/null +++ b/C++/similar-string-groups.cpp @@ -0,0 +1,151 @@ +// Time: O(n^2 * l), l is the average length of words +// Space: O(n) + +class Solution { +public: + int numSimilarGroups(vector& A) { + UnionFind union_find(A.size()); + for (int i = 0; i < A.size(); ++i) { + for (int j = 0; j < i; ++j) { + if (isSimilar(A[i], A[j])) { + union_find.union_set(i, j); + } + } + } + return union_find.size(); + } + +private: + bool isSimilar(const string& a, const string& b) { + int diff = 0; + for (int i = 0; i < a.length(); ++i) { + if (a[i] != b[i]) { + if (++diff > 2) { + return false; + } + } + } + return diff == 2; + } + + class UnionFind { + public: + UnionFind(const int n) : set_(n), size_(n) { + iota(set_.begin(), set_.end(), 0); + } + + int find_set(const int x) { + if (set_[x] != x) { + set_[x] = find_set(set_[x]); // Path compression. + } + return set_[x]; + } + + bool union_set(const int x, const int y) { + int x_root = find_set(x), y_root = find_set(y); + if (x_root == y_root) { + return false; + } + set_[min(x_root, y_root)] = max(x_root, y_root); + --size_; + return true; + } + + int size() const { + return size_; + } + + private: + vector set_; + int size_; + }; +}; + +// Time: O(n^2 * l) ~ O(n * l^4) +// Space: O(n) ~ O(n * l^3) +class Solution_MLE { +public: + int numSimilarGroups(vector& A) { + const int N = A.size(), L = A[0].length(); + UnionFind union_find(A.size()); + if (N < L*L) { + for (int i = 0; i < N; ++i) { + for (int j = 0; j < i; ++j) { + if (isSimilar(A[i], A[j])) { + union_find.union_set(i, j); + } + } + } + } else { + unordered_map> buckets; + unordered_set lookup; + for (int i = 0; i < A.size(); ++i) { + auto word = A[i]; + if (!lookup.count(word)) { + buckets[word].emplace_back(i); + lookup.emplace(word); + } + for (int j1 = 0; j1 < L; ++j1) { + for (int j2 = 0; j2 < j1; ++j2) { + swap(word[j1], word[j2]); + buckets[word].emplace_back(i); + swap(word[j1], word[j2]); + } + } + } + for (const auto& word : A) { + for (int i = 0; i < buckets[word].size(); ++i) { + for (int j = 0; j < i; ++j) { + union_find.union_set(buckets[word][i], buckets[word][j]); + } + } + } + } + return union_find.size(); + } + +private: + bool isSimilar(const string& a, const string& b) { + int diff = 0; + for (int i = 0; i < a.length(); ++i) { + if (a[i] != b[i]) { + if (++diff > 2) { + return false; + } + } + } + return diff == 2; + } + + class UnionFind { + public: + UnionFind(const int n) : set_(n), size_(n) { + iota(set_.begin(), set_.end(), 0); + } + + int find_set(const int x) { + if (set_[x] != x) { + set_[x] = find_set(set_[x]); // Path compression. + } + return set_[x]; + } + + bool union_set(const int x, const int y) { + int x_root = find_set(x), y_root = find_set(y); + if (x_root == y_root) { + return false; + } + set_[min(x_root, y_root)] = max(x_root, y_root); + --size_; + return true; + } + + int size() const { + return size_; + } + + private: + vector set_; + int size_; + }; +}; diff --git a/C++/simplify-path.cpp b/C++/simplify-path.cpp new file mode 100644 index 000000000..3528b86f0 --- /dev/null +++ b/C++/simplify-path.cpp @@ -0,0 +1,42 @@ +// Time: O(n) +// Space: O(n) + +class Solution { +public: + string simplifyPath(string path) { + vector names; + vector tokens(move(split(path, '/'))); + for (const auto& token : tokens) { + if (token == ".." && !names.empty()) { + names.pop_back(); + } else if (token != ".." && token != "." && !token.empty()) { + names.emplace_back(token); + } + } + return string("/").append(join(names, '/')); + } + +private: + // Split string by delimitor. + vector split(const string& s, const char delim) { + vector tokens; + stringstream ss(s); + string token; + while (getline(ss, token, delim)) { + tokens.emplace_back(token); + } + return tokens; + } + + // Join strings with delimitor. + string join(const vector& names, const char delim) { + ostringstream ss; + if (!names.empty()) { + const string delim_str(1, delim); + copy(names.cbegin(), prev(names.cend()), + ostream_iterator(ss, delim_str.c_str())); + ss << names.back(); + } + return ss.str(); + } +}; diff --git a/C++/simplifyPath.cpp b/C++/simplifyPath.cpp deleted file mode 100644 index 992aafdac..000000000 --- a/C++/simplifyPath.cpp +++ /dev/null @@ -1,34 +0,0 @@ -// Time Complexity: O(n) -// Space Complexity: O(n) - -class Solution { - public: - string simplifyPath(string path) { - vector dirs; - - for(auto i = path.cbegin(); i != path.cend();) { - ++i; // write here is to make sure i is not end - - auto j = find(i, path.cend(), '/'); - string dir = string(i, j); - - if(!dir.empty() && dir != ".") { - if(dir == "..") { - if(!dirs.empty()) dirs.pop_back(); - } - else - dirs.push_back(dir); - } - i = j; // i may be end - } - - if(dirs.empty()) return "/"; - - string ans; - for(auto dir : dirs) { - ans.append("/" + dir); - } - - return ans; - } -}; diff --git a/C++/single-element-in-a-sorted-array.cpp b/C++/single-element-in-a-sorted-array.cpp new file mode 100644 index 000000000..3f800b08e --- /dev/null +++ b/C++/single-element-in-a-sorted-array.cpp @@ -0,0 +1,21 @@ +// Time: O(logn) +// Space: O(1) + +class Solution { +public: + int singleNonDuplicate(vector& nums) { + int left = 0, right = nums.size() - 1; + while (left <= right) { + auto mid = left + (right - left) / 2; + if (!(mid % 2 == 0 && mid + 1 < nums.size() && + nums[mid] == nums[mid + 1]) && + !(mid % 2 == 1 && nums[mid] == nums[mid - 1])) { + right = mid - 1; + } else { + left = mid + 1; + } + } + return nums[left]; + } +}; + diff --git a/C++/single-number-ii.cpp b/C++/single-number-ii.cpp new file mode 100644 index 000000000..be693c446 --- /dev/null +++ b/C++/single-number-ii.cpp @@ -0,0 +1,17 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + int singleNumber(vector& nums) { + int one = 0, two = 0; + + for (const auto& i : nums) { + int new_one = (~i & one) | (i & ~one & ~two); + int new_two = (~i & two) | (i & one); + one = new_one, two = new_two; + } + + return one; + } +}; diff --git a/C++/single-number-iii.cpp b/C++/single-number-iii.cpp new file mode 100644 index 000000000..02893acac --- /dev/null +++ b/C++/single-number-iii.cpp @@ -0,0 +1,50 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + vector singleNumber(vector& nums) { + // Xor all the elements to get x ^ y. + const auto x_xor_y = accumulate(nums.cbegin(), nums.cend(), 0, bit_xor()); + + // Get the last bit where 1 occurs by "x & ~(x - 1)" + // Because -(x - 1) = ~(x - 1) + 1 <=> -x = ~(x - 1) + // So we can also get the last bit where 1 occurs by "x & -x" + const auto bit = x_xor_y & -x_xor_y; + + // Get the subset of A where the number has the bit. + // The subset only contains one of the two integers, call it x. + // Xor all the elements in the subset to get x. + vector result(2, 0); + for (const auto& i : nums) { + result[static_cast(i & bit)] ^= i; + } + return result; + } +}; + +class Solution2 { +public: + vector singleNumber(vector& nums) { + // Xor all the elements to get x ^ y. + int x_xor_y = 0; + for (const auto& i : nums) { + x_xor_y ^= i; + } + + // Get the last bit where 1 occurs. + const auto bit = x_xor_y & ~(x_xor_y - 1); + + // Get the subset of A where the number has the bit. + // The subset only contains one of the two integers, call it x. + // Xor all the elements in the subset to get x. + int x = 0; + for (const auto& i : nums) { + if (i & bit) { + x ^= i; + } + } + + return {x, x_xor_y ^ x}; + } +}; diff --git a/C++/single-number.cpp b/C++/single-number.cpp new file mode 100644 index 000000000..d42eed2cd --- /dev/null +++ b/C++/single-number.cpp @@ -0,0 +1,10 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + int singleNumber(vector& nums) { + return accumulate(nums.cbegin(), nums.cend(), + 0, std::bit_xor()); + } +}; diff --git a/C++/sliding-puzzle.cpp b/C++/sliding-puzzle.cpp new file mode 100644 index 000000000..e445c5ca8 --- /dev/null +++ b/C++/sliding-puzzle.cpp @@ -0,0 +1,192 @@ +// Time: O((m * n) * (m * n)!) +// Space: O((m * n) * (m * n)!) + +// A* Search Algorithm +class Solution { +public: + int slidingPuzzle(vector>& board) { + const auto& R = board.size(), &C = board[0].size(); + vector begin, end; + unordered_map> expected; + int zero_idx = 0; + for (int i = 0; i < R; ++i) { + for (int j = 0; j < C; ++j) { + auto val = (C * i + j + 1) % (R * C); + expected[val] = {i, j}; + if (board[i][j] == 0) { + zero_idx = begin.size(); + } + begin.emplace_back(board[i][j]); + end.emplace_back(val); + } + } + + int min_steps = heuristic_estimate(begin, R, C, expected); + unordered_set, Hash>> lookup; + vector>> closer{make_pair(zero_idx, begin)}, detour; + while (true) { + if (closer.empty()) { + if (detour.empty()) { + return -1; + } + min_steps += 2; + swap(closer, detour); + } + int zero; + vector board; + tie(zero, board) = closer.back(); closer.pop_back(); + if (board == end) { + return min_steps; + } + if (!lookup.count(board)) { + lookup.emplace(board); + int r = zero / C; + int c = zero % C; + static const vector> directions{{-1, 0}, {1, 0}, {0, -1}, {0, 1}}; + for (const auto& direction : directions) { + int i = r + direction.first; + int j = c + direction.second; + if (0 <= i && i < R && 0 <= j && j < C) { + auto new_zero = C * i + j; + auto new_board = board; + swap(new_board[zero], new_board[new_zero]); + int r2, c2; + tie(r2, c2) = expected[board[new_zero]]; + int r1 = zero / C; + int c1 = zero % C; + int r0 = new_zero / C; + int c0 = new_zero % C; + bool is_closer = dot({r1 - r0, c1 - c0}, {r2 - r0, c2 - c0}) > 0; + is_closer ? closer.emplace_back(new_zero, new_board) : detour.emplace_back(new_zero, new_board); + } + } + } + } + + return min_steps; + } + +private: + int heuristic_estimate(const vector& board, int R, int C, const unordered_map>& expected) { + int result = 0; + for (int i = 0; i < R; ++i) { + for (int j = 0; j < C; ++j) { + const auto& val = board[C * i + j]; + if (val == 0) { + continue; + } + int r, c; + tie(r, c) = expected.at(val); + result += abs(r - i) + abs(c - j); + } + } + return result; + } + + inline int dot(const pair& a, const pair& b) { + return a.first * b.first + a.second * b.second; + } + + template + struct Hash { + size_t operator()(const ContType& v) const { + size_t seed = 0; + for (const auto& i : v) { + seed ^= std::hash{}(i) + 0x9e3779b9 + (seed<<6) + (seed>>2); + } + return seed; + } + }; +}; + +// Time: O((m * n) * (m * n)! * log((m * n)!)) +// Space: O((m * n) * (m * n)!) +// A* Search Algorithm +class Solution2 { +public: + int slidingPuzzle(vector>& board) { + const auto& R = board.size(), &C = board[0].size(); + vector begin, end; + unordered_map> expected; + int zero_idx = 0; + for (int i = 0; i < R; ++i) { + for (int j = 0; j < C; ++j) { + auto val = (C * i + j + 1) % (R * C); + expected[val] = {i, j}; + if (board[i][j] == 0) { + zero_idx = begin.size(); + } + begin.emplace_back(board[i][j]); + end.emplace_back(val); + } + } + vector end_wrong(end); + swap(end_wrong[end_wrong.size() - 2], end_wrong[end_wrong.size() - 3]); + + using P = tuple>; + priority_queue, greater

> min_heap; + min_heap.emplace(make_tuple(0, 0, zero_idx, begin)); + unordered_map, int, Hash>> lookup; + lookup[begin] = 0; + while (!min_heap.empty()) { + int f, g, zero; + vector board; + tie(f, g, zero, board) = min_heap.top(); min_heap.pop(); + if (board == end) { + return g; + } + if (board == end_wrong) { + return -1; + } + if (f > lookup[board]) { + continue; + } + int r = zero / C; + int c = zero % C; + static const vector> directions{{-1, 0}, {1, 0}, {0, -1}, {0, 1}}; + for (const auto& direction : directions) { + int i = r + direction.first; + int j = c + direction.second; + if (0 <= i && i < R && 0 <= j && j < C) { + auto new_zero = C * i + j; + auto new_board = board; + swap(new_board[zero], new_board[new_zero]); + f = g + 1 + heuristic_estimate(new_board, R, C, expected); + if (!lookup.count(new_board) || f < lookup[new_board]) + lookup[new_board] = f; + min_heap.emplace(make_tuple(f, g + 1, new_zero, new_board)); + } + } + } + } + return -1; + } + +private: + int heuristic_estimate(const vector& board, int R, int C, const unordered_map>& expected) { + int result = 0; + for (int i = 0; i < R; ++i) { + for (int j = 0; j < C; ++j) { + const auto& val = board[C * i + j]; + if (val == 0) { + continue; + } + int r, c; + tie(r, c) = expected.at(val); + result += abs(r - i) + abs(c - j); + } + } + return result; + } + + template + struct Hash { + size_t operator()(const ContType& v) const { + size_t seed = 0; + for (const auto& i : v) { + seed ^= std::hash{}(i) + 0x9e3779b9 + (seed<<6) + (seed>>2); + } + return seed; + } + }; +}; diff --git a/C++/sliding-window-maximum.cpp b/C++/sliding-window-maximum.cpp new file mode 100644 index 000000000..5960d102d --- /dev/null +++ b/C++/sliding-window-maximum.cpp @@ -0,0 +1,25 @@ +// Time: O(n) +// Space: O(k) + +class Solution { +public: + vector maxSlidingWindow(vector& nums, int k) { + deque dq; + vector max_numbers; + + for (int i = 0; i < nums.size(); ++i) { + while (!dq.empty() && nums[i] >= nums[dq.back()]) { + dq.pop_back(); + } + dq.emplace_back(i); + if (i >= k && !dq.empty() && dq.front() == i - k) { + dq.pop_front(); + } + if (i >= k - 1) { + max_numbers.emplace_back(nums[dq.front()]); + } + } + + return max_numbers; + } +}; diff --git a/C++/sliding-window-median.cpp b/C++/sliding-window-median.cpp new file mode 100644 index 000000000..984721150 --- /dev/null +++ b/C++/sliding-window-median.cpp @@ -0,0 +1,42 @@ +// Time: O(nlogk) +// Space: O(k) + +class Solution { +public: +vector medianSlidingWindow(vector& nums, int k) { + multiset> min_bst; + multiset> max_bst; + + vector result; + for (int i = 0; i < nums.size(); ++i) { + if (i >= k) { + if (max_bst.find(nums[i - k]) != max_bst.cend()) { + max_bst.erase(max_bst.find(nums[i - k])); + } else { + min_bst.erase(min_bst.find(nums[i - k])); + } + } + + if (max_bst.empty() || nums[i] > *max_bst.cbegin()) { + min_bst.emplace(nums[i]); + if (min_bst.size() > max_bst.size() + 1) { + max_bst.emplace(*min_bst.cbegin()); + min_bst.erase(min_bst.cbegin()); + } + } else { + max_bst.emplace(nums[i]); + if (max_bst.size() > min_bst.size()) { + min_bst.emplace(*max_bst.cbegin()); + max_bst.erase(max_bst.cbegin()); + } + } + + if (i >= k - 1) { + result.emplace_back(min_bst.size() == max_bst.size() ? + *max_bst.cbegin() / 2.0 + *min_bst.cbegin() / 2.0 : *min_bst.cbegin()); + } + } + + return result; + } +}; diff --git a/C++/smallest-good-base.cpp b/C++/smallest-good-base.cpp new file mode 100644 index 000000000..e3a435ab1 --- /dev/null +++ b/C++/smallest-good-base.cpp @@ -0,0 +1,19 @@ +// Time: O((logn)^2) +// Space: O(1) + +class Solution { +public: + string smallestGoodBase(string n) { + unsigned long long num = stoll(n); + for (int l = log(num) / log(2); l >= 2; --l) { + unsigned long long b = pow(num, 1.0 / l), sum = 0, curr = 1; + for (int i = 0; i <= l; ++i, curr *= b) { + sum += curr; + } + if (sum == num) { + return to_string(b); + } + } + return to_string(num - 1); + } +}; diff --git a/C++/smallest-range.cpp b/C++/smallest-range.cpp new file mode 100644 index 000000000..f0a3bfdb2 --- /dev/null +++ b/C++/smallest-range.cpp @@ -0,0 +1,40 @@ +// Time: O(nlogk) +// Space: O(k) + +class Solution { +public: + vector smallestRange(vector>& nums) { + using VIT = vector::iterator; + + const auto comp = [](const pair& p1, const pair& p2) { + return *p1.first > *p2.first; + }; + + int left = numeric_limits::max(), right = numeric_limits::min(); + priority_queue, vector>, decltype(comp)> min_heap(comp); + for (auto &row : nums) { + left = min(left, row[0]); + right = max(right, row[0]); + min_heap.emplace(row.begin(), row.end()); + } + + vector result = {left, right}; + while (!min_heap.empty()) { + auto p = min_heap.top(); + min_heap.pop(); + ++p.first; + if (p.first == p.second) { + break; + } + min_heap.emplace(p); + + left = *min_heap.top().first; + right = max(right, *p.first); + if (right - left < result[1] - result[0]) { + result = {left, right}; + } + } + return result; + } +}; + diff --git a/C++/smallest-rectangle-enclosing-black-pixels.cpp b/C++/smallest-rectangle-enclosing-black-pixels.cpp new file mode 100644 index 000000000..9f96b1eee --- /dev/null +++ b/C++/smallest-rectangle-enclosing-black-pixels.cpp @@ -0,0 +1,128 @@ +// Time: O(nlogn) +// Space: O(1) + +// Using template. +class Solution { +public: + int minArea(vector>& image, int x, int y) { + using namespace std::placeholders; // for _1, _2, _3... + + const auto searchColumns = + [](const vector>& image, bool has_one, const int mid) { + return has_one == any_of(image.cbegin(), image.cend(), + [=](const vector& row) { return row[mid] == '1'; }); + }; + const auto searchRows = + [](const vector>& image, bool has_one, const int mid) { + return has_one == any_of(image[mid].cbegin(), image[mid].cend(), + [](const char& col) { return col == '1'; }); + }; + + const int left = binarySearch(0, y - 1, bind(searchColumns, image, true, _1)); + const int right = binarySearch(y + 1, image[0].size() - 1, bind(searchColumns, image, false, _1)); + const int top = binarySearch(0, x - 1, bind(searchRows, image, true, _1)); + const int bottom = binarySearch(x + 1, image.size() - 1, bind(searchRows, image, false, _1)); + + return (right - left) * (bottom - top); + } + +private: + template + int binarySearch(int left, int right, const T& find) { + while (left <= right) { + const int mid = left + (right - left) / 2; + if (find(mid)) { + right = mid - 1; + } else { + left = mid + 1; + } + } + return left; + } +}; + +// Using std::bind(). +class Solution2 { +public: + int minArea(vector>& image, int x, int y) { + using namespace std::placeholders; // for _1, _2, _3... + + const auto searchColumns = + [](const vector>& image, bool has_one, const int mid) { + return has_one == any_of(image.cbegin(), image.cend(), + [=](const vector& row) { return row[mid] == '1'; }); + }; + const auto searchRows = + [](const vector>& image, bool has_one, const int mid) { + return has_one == any_of(image[mid].cbegin(), image[mid].cend(), + [](const char& col) { return col == '1'; }); + }; + + function findLeft = bind(searchColumns, image, true, _1); + const int left = binarySearch(0, y - 1, findLeft); + + function findRight = bind(searchColumns, image, false, _1); + const int right = binarySearch(y + 1, image[0].size() - 1, findRight); + + function findTop = bind(searchRows, image, true, _1); + const int top = binarySearch(0, x - 1, findTop); + + function findBottom = bind(searchRows, image, false, _1); + const int bottom = binarySearch(x + 1, image.size() - 1, findBottom); + + return (right - left) * (bottom - top); + } + +private: + int binarySearch(int left, int right, function& find) { + while (left <= right) { + const int mid = left + (right - left) / 2; + if (find(mid)) { + right = mid - 1; + } else { + left = mid + 1; + } + } + return left; + } +}; + +// Using lambda. +class Solution3 { +public: + int minArea(vector>& image, int x, int y) { + const auto searchColumns = + [](const vector>& image, bool has_one, const int mid) { + return has_one == any_of(image.cbegin(), image.cend(), + [=](const vector& row) { return row[mid] == '1'; }); + }; + const auto searchRows = + [](const vector>& image, bool has_one, const int mid) { + return has_one == any_of(image[mid].cbegin(), image[mid].cend(), + [](const char& col) { return col == '1'; }); + }; + + const int left = binarySearch(0, y - 1, searchColumns, image, true); + const int right = binarySearch(y + 1, image[0].size() - 1, searchColumns, image, false); + const int top = binarySearch(0, x - 1, searchRows, image, true); + const int bottom = binarySearch(x + 1, image.size() - 1, searchRows, image, false); + + return (right - left) * (bottom - top); + } + +private: + int binarySearch(int left, int right, + const function>&, bool, const int)>& find, + const vector>& image, + bool has_one) { + while (left <= right) { + const int mid = left + (right - left) / 2; + if (find(image, has_one, mid)) { + right = mid - 1; + } else { + left = mid + 1; + } + } + return left; + } +}; diff --git a/C++/smallest-rotation-with-highest-score.cpp b/C++/smallest-rotation-with-highest-score.cpp new file mode 100644 index 000000000..089f143e5 --- /dev/null +++ b/C++/smallest-rotation-with-highest-score.cpp @@ -0,0 +1,17 @@ +// Time: O(n) +// Space: O(n) + +class Solution { +public: + int bestRotation(vector& A) { + const int N = A.size(); + vector change(N); + for (int i = 0; i < N; ++i) { + --change[(i - A[i] + 1 + N) % N]; + } + for (int i = 1; i < N; ++i) { + change[i] += change[i - 1] + 1; + } + return distance(change.begin(), max_element(change.begin(), change.begin() + N)); + } +}; diff --git a/C++/smallest-subtree-with-all-the-deepest-nodes.cpp b/C++/smallest-subtree-with-all-the-deepest-nodes.cpp new file mode 100644 index 000000000..b29a24187 --- /dev/null +++ b/C++/smallest-subtree-with-all-the-deepest-nodes.cpp @@ -0,0 +1,39 @@ +// Time: O(n) +// Space: O(h) + +/** + * 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* subtreeWithAllDeepest(TreeNode* root) { + return dfs(root).node; + } + +private: + struct Result { + TreeNode *node; + int depth; + }; + + Result dfs(TreeNode *node) { + if (!node) { + return {nullptr, 0}; + } + auto left = dfs(node->left); + auto right = dfs(node->right); + if (left.depth > right.depth) { + return {left.node, left.depth + 1}; + } + if (left.depth < right.depth) { + return {right.node, right.depth + 1}; + } + return {node, left.depth + 1}; + } +}; diff --git a/C++/solve-the-equation.cpp b/C++/solve-the-equation.cpp new file mode 100644 index 000000000..cacf34c27 --- /dev/null +++ b/C++/solve-the-equation.cpp @@ -0,0 +1,56 @@ +// Time: O(n) +// Space: O(n) + +class Solution { +public: + string solveEquation(string equation) { + auto a = 0, b = 0; + auto side = 1; + int submatches[] = { 1, 2, 3, 4 }; + auto e = regex("(=)|([-+]?)(\\d*)(x?)"); + for (regex_token_iterator it(equation.begin(), equation.end(), e, submatches), end; + it != end;) { + auto eq = (it++)->str(); + auto sign = (it++)->str(); + auto num = (it++)->str(); + auto isx = (it++)->str(); + if (!eq.empty()) { + side = -1; + } else if (!isx.empty()) { + a += side * stoi(sign + "1") * stoi(num.empty() ? "1" : num); + } else if (!num.empty()) { + b -= side * stoi(sign + num); + } + } + return a != 0 ? "x=" + to_string(b / a) : b != 0 ? "No solution" : "Infinite solutions"; + } +}; + +// Time: O(n) +// Space: O(n) +class Solution2 { +public: + string solveEquation(string equation) { + equation = regex_replace(equation, regex("(^|[+=-])x"), "$011x"); + + auto pos = equation.find('='); + auto l = coef(equation.substr(0, pos)); + auto r = coef(equation.substr(pos + 1)); + + auto a = l.first - r.first; + auto b = r.second - l.second; + + return a != 0 ? "x=" + to_string(b / a) : b != 0 ? "No solution" : "Infinite solutions"; + } + +private: + pair coef(string s) { + auto a = 0, b = 0; + auto e = regex("(^|[+-])\\d+x?"); + for (regex_token_iterator it(s.begin(), s.end(), e), end; + it != end; ++it) { + (it->str().back() == 'x' ? a : b) += stoi(*it); + } + return {a, b}; + } +}; diff --git a/C++/sort-characters-by-frequency.cpp b/C++/sort-characters-by-frequency.cpp new file mode 100644 index 000000000..c37dd4fe2 --- /dev/null +++ b/C++/sort-characters-by-frequency.cpp @@ -0,0 +1,26 @@ +// Time: O(n) +// Space: O(n) + +class Solution { +public: + string frequencySort(string s) { + unordered_map freq; + for (const auto& c : s) { + ++freq[c]; + } + + vector counts(s.size() + 1); + for (const auto& kvp : freq) { + counts[kvp.second].push_back(kvp.first); + } + + string result; + for (int count = counts.size() - 1; count >= 0; --count) { + for (const auto& c : counts[count]) { + result += string(count, c); + } + } + + return result; + } +}; diff --git a/C++/sort-colors.cpp b/C++/sort-colors.cpp new file mode 100644 index 000000000..9eb088944 --- /dev/null +++ b/C++/sort-colors.cpp @@ -0,0 +1,19 @@ +// Time: O(n) +// Space: O(1) + +// Tri-Partition solution. +class Solution { +public: + void sortColors(vector& nums) { + const int target = 1; + for (int i = 0, j = 0, n = nums.size() - 1; j <= n;) { + if (nums[j] < target) { + swap(nums[i++], nums[j++]); + } else if (nums[j] > target) { + swap(nums[j], nums[n--]); + } else { + ++j; + } + } + } +}; diff --git a/C++/sort-list.cpp b/C++/sort-list.cpp new file mode 100644 index 000000000..11320d427 --- /dev/null +++ b/C++/sort-list.cpp @@ -0,0 +1,52 @@ +// Time: O(nlogn) +// Space: O(logn) + +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode(int x) : val(x), next(NULL) {} + * }; + */ +class Solution { +public: + ListNode* sortList(ListNode* head) { + if (!head || !head->next) { + return head; + } + + auto slow = head, fast = head; + while (fast->next && fast->next->next) { + slow = slow->next; + fast = fast->next->next; + } + + // Split linked list. + fast = slow; + slow = slow->next; + fast->next = nullptr; + + return mergeTwoLists(sortList(head), sortList(slow)); + } + +private: + ListNode *mergeTwoLists(ListNode *l1, ListNode *l2) { + ListNode dummy{0}; + auto curr = &dummy; + + while (l1 && l2) { + if (l1->val <= l2->val) { + curr->next = l1; + l1 = l1->next; + } else { + curr->next = l2; + l2 = l2->next; + } + curr = curr->next; + } + curr->next = l1 ? l1 : l2; + + return dummy.next; + } +}; diff --git a/C++/sort-transformed-array.cpp b/C++/sort-transformed-array.cpp new file mode 100644 index 000000000..71f6e5541 --- /dev/null +++ b/C++/sort-transformed-array.cpp @@ -0,0 +1,31 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + vector sortTransformedArray(vector& nums, int a, int b, int c) { + const auto f = [](int x, int a, int b, int c) { + return a * x * x + b * x + c; + }; + + vector result; + if (nums.empty()) { + return result; + } + + int left = 0, right = nums.size() - 1; + int d = a > 0 ? -1 : 1; + while (left <= right) { + if (d * f(nums[left], a, b, c) < d * f(nums[right], a, b, c)) { + result.emplace_back(f(nums[left++], a, b, c)); + } else { + result.emplace_back(f(nums[right--], a, b, c)); + } + } + if (d == -1) { + reverse(result.begin(), result.end()); + } + + return result; + } +}; diff --git a/C++/sortList.cpp b/C++/sortList.cpp deleted file mode 100644 index da85f13f2..000000000 --- a/C++/sortList.cpp +++ /dev/null @@ -1,59 +0,0 @@ -// Time Complexity: O(nlogn) -// Space Complexity: O(1) - -/** - * Definition for singly-linked list. - * struct ListNode { - * int val; - * ListNode *next; - * ListNode(int x) : val(x), next(NULL) {} - * }; - */ -class Solution { - public: - ListNode *sortList(ListNode *head) { - if(!head || !head->next) return head; - - ListNode *slow = head; - ListNode *fast = head; - - while(fast->next && fast->next->next) { - slow = slow->next; - fast = fast->next->next; - } - - // split linked list - fast = slow; - slow = slow->next; - fast->next = nullptr; - - return mergeList(sortList(head), sortList(slow)); // merge sorted list - } - - private: - ListNode *mergeList(ListNode *list1, ListNode *list2) { - ListNode dummy(INT_MIN); - dummy.next = nullptr; - ListNode *head = &dummy; - - for(;list1 && list2; head = head->next) { - if(list1->val <= list2->val) { - head->next = list1; - list1 = list1->next; - } - else { - head->next = list2; - list2 = list2->next; - } - } - - if(list1) { - head->next = list1; - } - else if(list2) { - head->next = list2; - } - - return dummy.next; - } -}; diff --git a/C++/sortedListToBST.cpp b/C++/sortedListToBST.cpp deleted file mode 100644 index 6ea2911a0..000000000 --- a/C++/sortedListToBST.cpp +++ /dev/null @@ -1,46 +0,0 @@ -// Time Complexity: O(n) -// Space Complexity: O(logn) - -/** - * Definition for singly-linked list. - * struct ListNode { - * int val; - * ListNode *next; - * ListNode(int x) : val(x), next(NULL) {} - * }; - */ -/** - * Definition for binary tree - * struct TreeNode { - * int val; - * TreeNode *left; - * TreeNode *right; - * TreeNode(int x) : val(x), left(NULL), right(NULL) {} - * }; - */ -class Solution { - public: - TreeNode *sortedListToBST(ListNode *head) { - int len = 0; - - ListNode *p = head; - while(p) { - p = p->next; - ++len; - } - - return sortedListToBST(head, len); - } - - private: - TreeNode *sortedListToBST(ListNode *&head, int len) { - if(!len || !head) - return NULL; - TreeNode *left = sortedListToBST(head, len / 2); - TreeNode *parent = new TreeNode(head->val); - parent->left = left; - head = head->next; - parent->right = sortedListToBST(head, (len % 2 != 0)? len / 2: len / 2 - 1); - return parent; - } -}; diff --git a/C++/soup-servings.cpp b/C++/soup-servings.cpp new file mode 100644 index 000000000..cc9ce0b13 --- /dev/null +++ b/C++/soup-servings.cpp @@ -0,0 +1,46 @@ +// Time: O(1) +// Space: O(1) + +class Solution { +private: + template + struct PairHash { + size_t operator()(const pair& p) const { + size_t seed = 0; + seed ^= std::hash{}(p.first) + 0x9e3779b9 + (seed<<6) + (seed>>2); + seed ^= std::hash{}(p.second) + 0x9e3779b9 + (seed<<6) + (seed>>2); + return seed; + } + }; + +public: + double soupServings(int N) { + if (N > 4800) { + return 1.0; + } + unordered_map, double, PairHash> lookup; + N = (N + 24) / 25; + return dp(N, N, &lookup); + } + +private: + double dp(int a, int b, unordered_map, double, PairHash> *lookup) { + if (lookup->count(make_pair(a, b))) { + return (*lookup)[make_pair(a, b)]; + } + if (a <= 0 && b <= 0) { + return 0.5; + } + if (a <= 0) { + return 1.0; + } + if (b <= 0) { + return 0.0; + } + (*lookup)[make_pair(a, b)] = 0.25 * (dp(a - 4, b, lookup) + + dp(a - 3, b - 1, lookup) + + dp(a - 2, b - 2, lookup) + + dp(a - 1, b - 3, lookup)); + return (*lookup)[make_pair(a, b)]; + } +}; diff --git a/C++/sparse-matrix-multiplication.cpp b/C++/sparse-matrix-multiplication.cpp new file mode 100644 index 000000000..3910db4fe --- /dev/null +++ b/C++/sparse-matrix-multiplication.cpp @@ -0,0 +1,20 @@ +// Time: O(m * n * l), A is m x n matrix, B is n x l matrix +// Space: O(m * l) + +class Solution { +public: + vector> multiply(vector>& A, vector>& B) { + const int m = A.size(), n = A[0].size(), l = B[0].size(); + vector> res(m, vector(l)); + for (int i = 0; i < m; ++i) { + for (int k = 0; k < n; ++k) { + if (A[i][k]) { + for (int j = 0; j < l; ++j) { + res[i][j] += A[i][k] * B[k][j]; + } + } + } + } + return res; + } +}; diff --git a/C++/special-binary-string.cpp b/C++/special-binary-string.cpp new file mode 100644 index 000000000..7405effe4 --- /dev/null +++ b/C++/special-binary-string.cpp @@ -0,0 +1,22 @@ +// Time: f(n) = k * f(n/k) + n/k * klogk <= O(logn * nlogk) <= O(n^2) +// n is the length of S, k is the max number of special strings in each depth +// Space: O(n) + +class Solution { +public: + string makeLargestSpecial(string S) { + vector result; + int anchor = 0, count = 0; + for (int i = 0; i < S.length(); ++i) { + (S[i] == '1') ? ++count : --count; + if (count == 0) { + result.emplace_back("1"); + result.back() += makeLargestSpecial(S.substr(anchor + 1, i - anchor - 1)); + result.back() += "0"; + anchor = i + 1; + } + } + sort(result.begin(), result.end(), greater()); + return accumulate(result.begin(), result.end(), string()); + } +}; diff --git a/C++/spiral-matrix-ii.cpp b/C++/spiral-matrix-ii.cpp new file mode 100644 index 000000000..52d40d6ca --- /dev/null +++ b/C++/spiral-matrix-ii.cpp @@ -0,0 +1,81 @@ +// Time: O(n^2) +// Space: O(1) + +class Solution { +public: + /** + * @param n an integer + * @return a square matrix + */ + vector> generateMatrix(int n) { + vector> matrix(n, vector(n)); + + for (int num = 0, left = 0, right = n - 1, top = 0, bottom = n - 1; + left <= right && top <= bottom; + ++left, --right, ++top, --bottom) { + + for (int j = left; j <= right; ++j) { + matrix[top][j] = ++num; + } + for (int i = top + 1; i < bottom; ++i) { + matrix[i][right] = ++num; + } + for (int j = right; top < bottom && j >= left; --j) { + matrix[bottom][j] = ++num; + } + for (int i = bottom - 1; left < right && i >= top + 1; --i) { + matrix[i][left] = ++num; + } + } + + return matrix; + } +}; + +// Time: O(n^2) +// Space: O(1) +class Solution2 { +public: + vector > generateMatrix(int n) { + vector > matrix(n, vector(n)); + enum Action {RIGHT, DOWN, LEFT, UP}; + Action action = RIGHT; + for (int i = 0, j = 0, cnt = 0, total = n * n; cnt < total;) { + matrix[i][j] = ++cnt; + + switch (action) { + case RIGHT: + if (j + 1 < n && matrix[i][j + 1] == 0) { + ++j; + } else { + action = DOWN, ++i; + } + break; + case DOWN: + if (i + 1 < n && matrix[i + 1][j] == 0) { + ++i; + } else { + action = LEFT, --j; + } + break; + case LEFT: + if (j - 1 >= 0 && matrix[i][j - 1] == 0) { + --j; + } else { + action = UP, --i; + } + break; + case UP: + if (i - 1 >= 0 && matrix[i - 1][j] == 0) { + --i; + } else { + action = RIGHT, ++j; + } + break; + default: + break; + } + } + return matrix; + } +}; diff --git a/C++/spiral-matrix.cpp b/C++/spiral-matrix.cpp new file mode 100644 index 000000000..61d6fc9ba --- /dev/null +++ b/C++/spiral-matrix.cpp @@ -0,0 +1,89 @@ +// Time: O(m * n) +// Space: O(1) + +class Solution { +public: + vector spiralOrder(vector>& matrix) { + vector res; + if (matrix.empty()) { + return res; + } + + for (int left = 0, right = matrix[0].size() - 1, + top = 0, bottom = matrix.size() - 1; + left <= right && top <= bottom; + ++left, --right, ++top, --bottom) { + + for (int j = left; j <= right; ++j) { + res.emplace_back(matrix[top][j]); + } + for (int i = top + 1; i < bottom; ++i) { + res.emplace_back(matrix[i][right]); + } + for (int j = right; top < bottom && j >= left; --j) { + res.emplace_back(matrix[bottom][j]); + } + for (int i = bottom - 1; left < right && i > top; --i) { + res.emplace_back(matrix[i][left]); + } + } + + return res; + } +}; + +// Time: O(m * n) +// Space: O(1) +class Solution2 { +public: + vector spiralOrder(vector>& matrix) { + const int m = matrix.size(); + vector res; + if (m == 0) { + return res; + } + + const int n = matrix.front().size(); + enum Action {RIGHT, DOWN, LEFT, UP}; + Action action = RIGHT; + for (int i = 0, j = 0, begini = 0, beginj = 0, endi = m, + endj = n, cnt = 0, total = m * n; cnt < total; ++cnt) { + + res.emplace_back(matrix[i][j]); + + switch (action) { + case RIGHT: + if (j + 1 < endj) { + ++j; + } else { + action = DOWN, ++begini, ++i; + } + break; + case DOWN: + if (i + 1 < endi) { + ++i; + } else { + action = LEFT, --endj, --j; + } + break; + case LEFT: + if (j - 1 >= beginj) { + --j; + } else { + action = UP, --endi, --i; + } + break; + case UP: + if (i - 1 >= begini) { + --i; + } else { + action = RIGHT, ++beginj, ++j; + } + break; + default: + break; + } + } + return res; + } +}; diff --git a/C++/spiralOrder.cpp b/C++/spiralOrder.cpp deleted file mode 100644 index 21e2b96cd..000000000 --- a/C++/spiralOrder.cpp +++ /dev/null @@ -1,40 +0,0 @@ -// Time Complexity: O(n) -// Space Complexity: O(1) - -class Solution { - public: - vector spiralOrder(vector > &matrix) { - const int m = matrix.size(); - vector ans; - if(m == 0) return ans; - - const int n = matrix.front().size(); - enum Action {RIGHT, DOWN, LEFT, UP}; - Action action = RIGHT; - for(int i = 0, j = 0, begini = 0, beginj = 0, endi = m, endj = n, cnt = 0, total = m * n; cnt < total; ++cnt) { - ans.push_back(matrix[i][j]); - - switch(action) { - case RIGHT: - if(j + 1 < endj) ++j; - else action = DOWN, ++begini, ++i; - break; - case DOWN: - if(i + 1 < endi) ++i; - else action = LEFT, --endj, --j; - break; - case LEFT: - if(j - 1 >= beginj) --j; - else action = UP, --endi, --i; - break; - case UP: - if(i - 1 >= begini) --i; - else action = RIGHT, ++beginj, ++j; - break; - default: - break; - } - } - return ans; - } -}; diff --git a/C++/split-array-into-consecutive-subsequences.cpp b/C++/split-array-into-consecutive-subsequences.cpp new file mode 100644 index 000000000..67bfe6c79 --- /dev/null +++ b/C++/split-array-into-consecutive-subsequences.cpp @@ -0,0 +1,29 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + bool isPossible(vector& nums) { + auto pre = numeric_limits::min(), cur = 0; + auto cnt1 = 0, cnt2 = 0, cnt3 = 0; // count of each length ending at cur + for (int i = 0; i < nums.size(); pre = cur) { + auto cnt = 0; + for (cur = nums[i]; i < nums.size() && cur == nums[i]; ++cnt, ++i); + + if (cur != pre + 1) { + if (cnt1 != 0 || cnt2 != 0) { + return false; + } + tie(cnt1, cnt2, cnt3) = make_tuple(cnt, 0, 0); + } else { + if (cnt < cnt1 + cnt2) { + return false; + } + tie(cnt1, cnt2, cnt3) = make_tuple(max(0, cnt - (cnt1 + cnt2 + cnt3)), + cnt1, + cnt2 + min(cnt3, cnt - (cnt1 + cnt2))); + } + } + return cnt1 == 0 && cnt2 == 0; + } +}; diff --git a/C++/split-array-into-fibonacci-sequence.cpp b/C++/split-array-into-fibonacci-sequence.cpp new file mode 100644 index 000000000..5f4b41ee4 --- /dev/null +++ b/C++/split-array-into-fibonacci-sequence.cpp @@ -0,0 +1,52 @@ +// Time: O(n^3) +// Space: O(n) + +class Solution { +public: + vector splitIntoFibonacci(string S) { + for (int i = 0, a = 0; i + 2 < S.length(); ++i) { + a = 10 * a + S[i] - '0'; + for (int j = i + 1, b = 0; j + 1 < S.length(); ++j) { + b = 10 * b + S[j] - '0'; + vector fib = {a, b}; + int k = j + 1; + while (k < S.length()) { + if (fib[fib.size() - 2] > numeric_limits::max() - fib[fib.size() - 1]) { + break; + } + auto c = fib[fib.size() - 2] + fib[fib.size() - 1]; + auto l = startswith(S, k, c); + if (l == 0) { + break; + } + fib.emplace_back(c); + k += l; + } + if (k == S.length()) { + return fib; + } + if (b == 0) { + break; + } + } + if (a == 0) { + break; + } + } + return {}; + } + +private: + int startswith(const string& S, int k, int x) { + int y = 0; + for (int i = k; i < S.length(); ++i) { + y = 10 * y + S[i] - '0'; + if (y == x) { + return i - k + 1; + } else if (y > x) { + break; + } + } + return 0; + } +}; diff --git a/C++/split-array-largest-sum.cpp b/C++/split-array-largest-sum.cpp new file mode 100644 index 000000000..b39e527a5 --- /dev/null +++ b/C++/split-array-largest-sum.cpp @@ -0,0 +1,36 @@ +// Time: O(nlogs), s is the sum of nums +// Space: O(1) + +class Solution { +public: + int splitArray(vector& nums, int m) { + int left = 0, right = 0; + for (const auto& num : nums) { + left = max(left, num); + right += num; + } + + while (left <= right) { + const auto mid = left + (right - left) / 2; + if (canSplit(nums, m, mid)) { + right = mid - 1; + } else { + left = mid + 1; + } + } + return left; + } + +private: + bool canSplit(vector& nums, int m, int sum) { + int cnt = 1, curr_sum = 0; + for (const auto& num : nums) { + curr_sum += num; + if (curr_sum > sum) { + curr_sum = num; + ++cnt; + } + } + return cnt <= m; + } +}; diff --git a/C++/split-array-with-equal-sum.cpp b/C++/split-array-with-equal-sum.cpp new file mode 100644 index 000000000..5f4c336de --- /dev/null +++ b/C++/split-array-with-equal-sum.cpp @@ -0,0 +1,32 @@ +// Time: O(n^2) +// Space: O(n) + +class Solution { +public: + bool splitArray(vector& nums) { + if (nums.size() < 7) { + return false; + } + + vector sum(nums.size()); + sum[0] = nums[0]; + for (int i = 1; i < nums.size(); ++i) { + sum[i] = sum[i - 1] + nums[i]; + } + for (int j = 3; j < nums.size() - 3; ++j) { + unordered_set lookup; + for (int i = 1; i < j - 1; ++i) { + if (sum[i - 1] == sum[j - 1] - sum[i]) { + lookup.emplace(sum[i - 1]); + } + } + for (int k = j + 2; k < nums.size() - 1; ++k) { + if (sum[nums.size() - 1] - sum[k] == sum[k - 1] - sum[j] && + lookup.count(sum[k - 1] - sum[j])) { + return true; + } + } + } + return false; + } +}; diff --git a/C++/split-array-with-same-average.cpp b/C++/split-array-with-same-average.cpp new file mode 100644 index 000000000..2c32605e5 --- /dev/null +++ b/C++/split-array-with-same-average.cpp @@ -0,0 +1,40 @@ +// Time: O(n^4) +// Space: O(n^3) + +class Solution { +public: + bool splitArraySameAverage(vector& A) { + const int n = A.size(); + const int sum = accumulate(A.cbegin(), A.cend(), 0); + if (!possible(n, sum)) { + return false; + } + + vector> sums(n / 2 + 1); + sums[0].emplace(0); + for (const auto& num: A) { // O(n) times + for (int i = n / 2; i >= 1; --i) { // O(n) times + for (const auto& prev : sums[i - 1]) { // O(1) + O(2) + ... O(n/2) = O(n^2) times + sums[i].emplace(prev + num); + } + } + } + for (int i = 1; i <= n / 2; ++i) { + if (sum * i % n == 0 && + sums[i].count(sum * i / n)) { + return true; + } + } + return false; + } + +private: + bool possible(int n, int sum) { + for (int i = 1; i <= n / 2; ++i) { + if (sum * i % n == 0) { + return true; + } + } + return false; + } +}; diff --git a/C++/split-bst.cpp b/C++/split-bst.cpp new file mode 100644 index 000000000..8c88d6686 --- /dev/null +++ b/C++/split-bst.cpp @@ -0,0 +1,28 @@ +// Time: O(n) +// Space: O(h) + +/** + * 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: + vector splitBST(TreeNode* root, int V) { + if (!root) { + return {nullptr, nullptr}; + } else if (root->val <= V) { + const auto& result = splitBST(root->right, V); + root->right = result[0]; + return {root, result[1]}; + } else { + const auto& result = splitBST(root->left, V); + root->left = result[1]; + return {result[0], root}; + } + } +}; diff --git a/C++/split-concatenated-strings.cpp b/C++/split-concatenated-strings.cpp new file mode 100644 index 000000000..0c2578f43 --- /dev/null +++ b/C++/split-concatenated-strings.cpp @@ -0,0 +1,31 @@ +// Time: O(n^2) +// Space: O(n) + +class Solution { +public: + string splitLoopedString(vector& strs) { + string s; + for (const auto& str : strs) { + auto rev{str}; + reverse(rev.begin(), rev.end()); + s += max(str, rev); + } + string result{"a"}; + for (auto i = 0, st = 0; i < strs.size(); st += strs[i++].size()) { + auto rev{strs[i]}, body{s.substr(st + strs[i].length())}; + body += s.substr(0, st); + reverse(rev.begin(), rev.end()); + for (const auto& p : {strs[i], rev}) { + for (auto j = 0; j < strs[i].size(); ++j) { + if (p[j] >= result[0]) { + string tmp{p.substr(j)}; + tmp += body; + tmp += p.substr(0, j); + result = max(result, tmp); + } + } + } + } + return result; + } +}; diff --git a/C++/split-linked-list-in-parts.cpp b/C++/split-linked-list-in-parts.cpp new file mode 100644 index 000000000..2792a408a --- /dev/null +++ b/C++/split-linked-list-in-parts.cpp @@ -0,0 +1,41 @@ +// Time: O(n + k) +// Space: O(1) + +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode(int x) : val(x), next(NULL) {} + * }; + */ +class Solution { +public: + vector splitListToParts(ListNode* root, int k) { + auto n = 0; + auto curr = root; + while (curr) { + curr = curr->next; + n += 1; + } + auto width = n / k; + auto remainder = n % k; + + vector result; + curr = root; + for (int i = 0; i < k; ++i) { + auto head = curr; + for (int j = 0; curr && + j < width - 1 + static_cast(i < remainder); ++j) { + curr = curr->next; + } + if (curr) { + auto tail = curr; + curr = curr->next; + tail->next = nullptr; + } + result.emplace_back(head); + } + return result; + } +}; diff --git a/C++/sqrt.cpp b/C++/sqrt.cpp deleted file mode 100644 index df80bc622..000000000 --- a/C++/sqrt.cpp +++ /dev/null @@ -1,27 +0,0 @@ -// Time Complexity: O(n) -// Space Complexity: O(1) - -class Solution { - public: - int sqrt(int x) { - int left = 1; - int right = x; - int last_mid = 0; - - while(left <= right) { - int mid = left + (right - left) / 2; - - if(x / mid > mid) { - left = mid + 1; - last_mid = mid; - } - else if (x / mid < mid) { - right = mid - 1; - } - else - return mid; - } - - return last_mid; - } -}; diff --git a/C++/sqrtx.cpp b/C++/sqrtx.cpp new file mode 100644 index 000000000..2d4092aca --- /dev/null +++ b/C++/sqrtx.cpp @@ -0,0 +1,23 @@ +// Time: O(logn) +// Space: O(1) + +class Solution { +public: + int mySqrt(int x) { + if (x < 2) { + return x; + } + + int left = 1, right = x / 2; + while (left <= right) { + const auto mid = left + (right - left) / 2; + if (mid > x / mid) { + right = mid - 1; + } else { + left = mid + 1; + } + } + + return left - 1; + } +}; diff --git a/C++/squirrel-simulation.cpp b/C++/squirrel-simulation.cpp new file mode 100644 index 000000000..adf3aa525 --- /dev/null +++ b/C++/squirrel-simulation.cpp @@ -0,0 +1,20 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + int minDistance(int height, int width, vector& tree, vector& squirrel, vector>& nuts) { + int result = 0; + int d = numeric_limits::max(); + for (const auto& nut : nuts) { + result += (distance(nut, tree) * 2); + d = min(d, distance(nut, squirrel) - distance(nut, tree)); + } + return result + d; + } + +private: + int distance(const vector& a, const vector& b) { + return abs(a[0] - b[0]) + abs(a[1] - b[1]); + } +}; diff --git a/C++/stickers-to-spell-word.cpp b/C++/stickers-to-spell-word.cpp new file mode 100644 index 000000000..90a42e399 --- /dev/null +++ b/C++/stickers-to-spell-word.cpp @@ -0,0 +1,49 @@ +// Time: O(T * S^T) +// Space: O(T * S^T) + +class Solution { +public: + int minStickers(vector& stickers, string target) { + vector> sticker_counts(stickers.size(), vector(26)); + unordered_map dp; + for (int i = 0; i < stickers.size(); ++i) { + for (const auto& c : stickers[i]) { + ++sticker_counts[i][c - 'a']; + } + } + dp[""] = 0; + return minStickersHelper(sticker_counts, target, &dp); + } + +private: + int minStickersHelper(const vector>& sticker_counts, const string& target, + unordered_map *dp) { + if (dp->count(target)) { + return (*dp)[target]; + } + int result = numeric_limits::max(); + vector target_count(26); + for (const auto& c : target) { + ++target_count[c - 'a']; + } + for (const auto& sticker_count : sticker_counts) { + if (sticker_count[target[0] - 'a'] == 0) { + continue; + } + string new_target; + for (int i = 0; i < target_count.size(); ++i) { + if (target_count[i] - sticker_count[i] > 0) { + new_target += string(target_count[i] - sticker_count[i], 'a' + i); + } + } + if (new_target.length() != target.length()) { + int num = minStickersHelper(sticker_counts, new_target, dp); + if (num != -1) { + result = min(result, 1 + num); + } + } + } + (*dp)[target] = (result == numeric_limits::max()) ? -1 : result; + return (*dp)[target]; + } +}; \ No newline at end of file diff --git a/C++/stone-game.cpp b/C++/stone-game.cpp new file mode 100644 index 000000000..46cb7466f --- /dev/null +++ b/C++/stone-game.cpp @@ -0,0 +1,23 @@ +// Time: O(n^2) +// Space: O(n) + +// The solution is the same as https://leetcode.com/problems/predict-the-winner/description/ +class Solution { +public: + bool stoneGame(vector& piles) { + if (piles.size() % 2 == 0 || piles.size() == 1) { + return true; + } + + vector dp(piles.size()); + for (int i = piles.size() - 1; i >= 0; --i) { + dp[i] = piles[i]; + for (int j = i + 1; j < piles.size(); ++j) { + dp[j] = max(piles[i] - dp[j], piles[j] - dp[j - 1]); + } + } + + return dp.back() >= 0; + } +}; + diff --git a/C++/strStr.cpp b/C++/strStr.cpp deleted file mode 100644 index 6320e9f73..000000000 --- a/C++/strStr.cpp +++ /dev/null @@ -1,46 +0,0 @@ -// Time Complexity: O(m + n) -// Space Complexity: O(n) - -class Solution { - public: - char *strStr(char *haystack, char *needle) { - string target(haystack); - string pattern(needle); - - if(target.size() < pattern.size()) - return nullptr; - - if(pattern.size() == 0) - return haystack; - - int i = kmp(target, pattern); - - return (i != -1)? haystack + i : nullptr; - } - private: - int kmp(const string &target, const string &pattern) { - const auto m = target.size(); - const auto n = pattern.size(); - - vector failure(n, -1); - for(int i = 1, j = -1; i < n; ++i) { - while(j >= 0 && pattern[j + 1] != pattern[i]) - j = failure[j]; - if(pattern[j + 1] == pattern[i]) - ++j; - failure[i] = j; - } - - for(int i = 0, j = -1; i < m; ++i) { - while(j >= 0 && pattern[j + 1] != target[i]) - j = failure[j]; - if(pattern[j + 1] == target[i]) - ++j; - if(j == n - 1) { - return i - j; - } - } - - return -1; - } -}; diff --git a/C++/strange-printer.cpp b/C++/strange-printer.cpp new file mode 100644 index 000000000..89997ba20 --- /dev/null +++ b/C++/strange-printer.cpp @@ -0,0 +1,27 @@ +// Time: O(n^3) +// Space: O(n^2) + +class Solution { +public: + int strangePrinter(string s) { + vector> lookup(s.length(), vector(s.length())); + return dp(s, 0, s.length() - 1, &lookup); + } + +private: + int dp(const string& s, int i, int j, vector> *lookup) { + if (i > j) { + return 0; + } + if (!(*lookup)[i][j]) { + (*lookup)[i][j] = dp(s, i, j - 1, lookup) + 1; + for (int k = i; k < j; ++k) { + if (s[k] == s[j]) { + (*lookup)[i][j] = min((*lookup)[i][j], + dp(s, i, k, lookup) + dp(s, k + 1, j - 1, lookup)); + } + } + } + return (*lookup)[i][j]; + } +}; diff --git a/C++/string-compression.cpp b/C++/string-compression.cpp new file mode 100644 index 000000000..8021ebce8 --- /dev/null +++ b/C++/string-compression.cpp @@ -0,0 +1,25 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + int compress(vector& chars) { + int write = 0, anchor = 0; + for (int read = 0; read < chars.size(); ++read) { + if (read + 1 == chars.size() || chars[read + 1] != chars[read]) { + chars[write++] = chars[read]; + if (read - anchor > 0) { + auto n = read - anchor + 1, cnt = 0; + while (n > 0) { + chars[write++] = n % 10 + '0'; + n /= 10; + ++cnt; + } + reverse(chars.begin() + write - cnt, chars.begin() + write); + } + anchor = read + 1; + } + } + return write; + } +}; diff --git a/C++/string-to-integer-atoi.cpp b/C++/string-to-integer-atoi.cpp new file mode 100644 index 000000000..06a67c731 --- /dev/null +++ b/C++/string-to-integer-atoi.cpp @@ -0,0 +1,44 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + int myAtoi(string str) { + if (str.empty()) { + return 0; + } + + int ans = 0; + int sign = 1; + int i = 0; + + // Skip whitespace. + while (str[i] == ' ' || str[i] == '\t') { + ++i; + } + + if (i == str.length()) { + return 0; + } + + // Parse sign. + if (str[i] == '+') { + ++i; + } else if (str[i] == '-') { + sign = -1; + ++i; + } + + // Compute integer. + for (; i < str.length() && isdigit(str[i]); ++i) { + if (ans > (numeric_limits::max() - (str[i] - '0')) / 10) { + return sign > 0 ? numeric_limits::max() : numeric_limits::min(); + } + ans *= 10; + ans += str[i] - '0'; + } + + ans *= sign; + return ans; + } +}; diff --git a/C++/strobogrammatic-number-ii.cpp b/C++/strobogrammatic-number-ii.cpp new file mode 100644 index 000000000..4c5b610e0 --- /dev/null +++ b/C++/strobogrammatic-number-ii.cpp @@ -0,0 +1,32 @@ +// Time: O(n^2 * 5^(n/2)) +// Space: O(n) + +class Solution { +public: + vector findStrobogrammatic(int n) { + return findStrobogrammaticRecu(n, n); + } + + vector findStrobogrammaticRecu(const int n, int k) { + if (k == 0) { + return {""}; + } else if (k == 1) { + return {"0", "1", "8"}; + } + + vector result; + for (const auto& num : findStrobogrammaticRecu(n, k - 2)) { + for (const auto& kvp : lookup) { + if (n != k || kvp.first != "0") { + result.emplace_back(kvp.first + num + kvp.second); + } + } + } + return result; + } + +private: + const unordered_map lookup{{"0", "0"}, {"1", "1"}, + {"6", "9"}, {"8", "8"}, + {"9", "6"}}; +}; diff --git a/C++/strobogrammatic-number-iii.cpp b/C++/strobogrammatic-number-iii.cpp new file mode 100644 index 000000000..0510a5c00 --- /dev/null +++ b/C++/strobogrammatic-number-iii.cpp @@ -0,0 +1,96 @@ +// Time: O(5^(n/2)) +// Space: O(n) + +class Solution { +public: + int strobogrammaticInRange(string low, string high) { + int count = countStrobogrammaticUntil(high, false) - + countStrobogrammaticUntil(low, false) + + isStrobogrammatic(low); + return count >= 0 ? count : 0; + } + + int countStrobogrammaticUntil(string num, bool can_start_with_0) { + if (can_start_with_0 && cache.find(num) != cache.end()) { + return cache[num]; + } + + int count = 0; + if (num.length() == 1) { + for (const auto& c : {'0', '1', '8'}) { + if (num.front() >= c) { + ++count; + } + } + cache[num] = count; + return count; + } + + for (const auto& kvp : lookup) { + if (can_start_with_0 || kvp.first != '0') { + if (num.front() > kvp.first) { + if (num.length() == 2) { // num is like "21" + ++count; + } else { // num is like "201" + count += countStrobogrammaticUntil(string(num.length() - 2, '9'), true); + } + } else if (num.front() == kvp.first) { + if (num.length() == 2) { // num is like 12". + count += num.back() >= kvp.second; + } else { + if (num.back() >= kvp.second) { // num is like "102". + count += countStrobogrammaticUntil(getMid(num), true); + } else if (getMid(num) != string(num.length() - 2, '0')) { // num is like "110". + count += countStrobogrammaticUntil(getMid(num), true) - + isStrobogrammatic(getMid(num)); + } + } + } + } + } + + if (!can_start_with_0) { // Sum up each length. + for (int i = num.length() - 1; i > 0; --i) { + count += countStrobogrammaticByLength(i); + } + } else { + cache[num] = count; + } + + return count; + } + + string getMid(const string& num) { + return num.substr(1, num.length() - 2); + } + + int countStrobogrammaticByLength(int n) { + switch (n) { + case 1: + return 3; // "0", "1", "8" + case 2: + return 4; // "11", "69", "88", "96" + case 3: + return 4 * 3; // "101", "111", "181", ... + default: + return 5 * countStrobogrammaticByLength(n - 2); + } + } + + bool isStrobogrammatic(const string& num) { + const int n = num.size(); + for (int i = 0; i <= n - 1 - i; ++i) { + const auto it = lookup.find(num[n - 1 - i]); + if (it == lookup.end() || num[i] != it->second) { + return false; + } + } + return true; + } + +private: + const unordered_map lookup{{'0', '0'}, {'1', '1'}, + {'6', '9'}, {'8', '8'}, + {'9', '6'}}; + unordered_map cache; +}; diff --git a/C++/strobogrammatic-number.cpp b/C++/strobogrammatic-number.cpp new file mode 100644 index 000000000..6d62376b8 --- /dev/null +++ b/C++/strobogrammatic-number.cpp @@ -0,0 +1,21 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + bool isStrobogrammatic(string num) { + const int n = num.size(); + for (int i = 0; i <= n - 1 - i; ++i) { + const auto it = lookup.find(num[n - 1 - i]); + if (it == lookup.end() || num[i] != it->second) { + return false; + } + } + return true; + } + +private: + const unordered_map lookup{{'0', '0'}, {'1', '1'}, + {'6', '9'}, {'8', '8'}, + {'9', '6'}}; +}; diff --git a/C++/strong-password-checker.cpp b/C++/strong-password-checker.cpp new file mode 100644 index 000000000..fa8cf5854 --- /dev/null +++ b/C++/strong-password-checker.cpp @@ -0,0 +1,48 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + int strongPasswordChecker(string s) { + int missing_type_cnt = 3; + missing_type_cnt -= static_cast(any_of(s.begin(), s.end(), [](char c){ return isdigit(c); })); + missing_type_cnt -= static_cast(any_of(s.begin(), s.end(), [](char c){ return isupper(c); })); + missing_type_cnt -= static_cast(any_of(s.begin(), s.end(), [](char c){ return islower(c); })); + + int total_change_cnt = 0; + int one_change_cnt = 0, two_change_cnt = 0, three_change_cnt = 0; + for (int i = 2; i < s.length();) { + if (s[i] == s[i - 1] && s[i - 1] == s[i - 2]) { + int length = 2; + while (i < s.length() && s[i] == s[i - 1]) { + ++length; + ++i; + } + total_change_cnt += length / 3; + if (length % 3 == 0) { + ++one_change_cnt; + } else if (length % 3 == 1) { + ++two_change_cnt; + } else { + ++three_change_cnt; + } + } else { + ++i; + } + } + + if (s.length() < 6) { + return max(missing_type_cnt, 6 - static_cast(s.length())); + } else if (s.length() <= 20) { + return max(missing_type_cnt, total_change_cnt); + } + + int delete_cnt = s.length() - 20; + + total_change_cnt -= min(delete_cnt, one_change_cnt) / 1; + total_change_cnt -= min(max(delete_cnt - one_change_cnt, 0), two_change_cnt * 2) / 2; + total_change_cnt -= min(max(delete_cnt - one_change_cnt - 2 * two_change_cnt, 0), three_change_cnt * 3) / 3; + + return delete_cnt + max(missing_type_cnt, total_change_cnt); + } +}; diff --git a/C++/student-attendance-record-i.cpp b/C++/student-attendance-record-i.cpp new file mode 100644 index 000000000..bf7eb9aa2 --- /dev/null +++ b/C++/student-attendance-record-i.cpp @@ -0,0 +1,20 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + bool checkRecord(string s) { + int count_A = 0; + for (int i = 0; i < s.length(); ++i) { + if (s[i] == 'A') { + if (++count_A == 2) { + return false; + } + } + if (i + 2 < s.length() && s[i] == 'L' && s[i + 1] == 'L' && s[i + 2] == 'L') { + return false; + } + } + return true; + } +}; diff --git a/C++/student-attendance-record-ii.cpp b/C++/student-attendance-record-ii.cpp new file mode 100644 index 000000000..819c954da --- /dev/null +++ b/C++/student-attendance-record-ii.cpp @@ -0,0 +1,21 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + int checkRecord(int n) { + static const long long M = 1000000007; + long long a0l0 = 1, a0l1 = 0, a0l2 = 0, a1l0 = 0, a1l1 = 0, a1l2 = 0; + for (int i = 0; i <= n; ++i) { + auto new_a0l0 = (a0l0 + a0l1 + a0l2) % M; + a0l2 = a0l1; + a0l1 = a0l0; + a0l0 = new_a0l0; + auto new_a1l0 = (a0l0 + a1l0 + a1l1 + a1l2) % M; + a1l2 = a1l1; + a1l1 = a1l0; + a1l0 = new_a1l0; + } + return static_cast(a1l0); + } +}; diff --git a/C++/subarray-product-less-than-k.cpp b/C++/subarray-product-less-than-k.cpp new file mode 100644 index 000000000..6312bc526 --- /dev/null +++ b/C++/subarray-product-less-than-k.cpp @@ -0,0 +1,22 @@ +// Time: O(n) +// Space: O(1) + +// Sliding window solution. +class Solution { +public: + int numSubarrayProductLessThanK(vector& nums, int k) { + if (k <= 1) { + return 0; + } + int result = 0, start = 0, prod = 1; + for (int i = 0; i < nums.size(); ++i) { + prod *= nums[i]; + while (prod >= k) { + prod /= nums[start]; + ++start; + } + result += i - start + 1; + } + return result; + } +}; diff --git a/C++/subarray-sum-equals-k.cpp b/C++/subarray-sum-equals-k.cpp new file mode 100644 index 000000000..3b0e19401 --- /dev/null +++ b/C++/subarray-sum-equals-k.cpp @@ -0,0 +1,18 @@ +// Time: O(n) +// Space: O(n) + +class Solution { +public: + int subarraySum(vector& nums, int k) { + auto result{0}; + auto accumulated_sum{0}; + unordered_map lookup; + ++lookup[0]; + for (const auto& num : nums) { + accumulated_sum += num; + result += lookup[accumulated_sum - k]; + ++lookup[accumulated_sum]; + } + return result; + } +}; diff --git a/C++/subdomain-visit-count.cpp b/C++/subdomain-visit-count.cpp new file mode 100644 index 000000000..1ffb3bd03 --- /dev/null +++ b/C++/subdomain-visit-count.cpp @@ -0,0 +1,28 @@ +// Time: O(n), is the length of cpdomains (assuming the length of cpdomains[i] is fixed) +// Space: O(n) + +class Solution { +public: + vector subdomainVisits(vector& cpdomains) { + unordered_map counts; + for (const auto& cpdomain : cpdomains) { + int i = cpdomain.find(" "); + const int count = stoi(cpdomain.substr(0, i)); + const auto& domain = cpdomain.substr(i + 1); + for (int i = -1; i < static_cast(domain.length()); ++i) { + if (i != -1 && domain[i] != '.') { + continue; + } + counts[domain.substr(i + 1)] += count; + } + } + + vector result; + for (const auto& count : counts) { + result.emplace_back(to_string(count.second)); + result.back() += " "; + result.back() += count.first; + } + return result; + } +}; diff --git a/C++/subsets-ii.cpp b/C++/subsets-ii.cpp new file mode 100644 index 000000000..2a6b1cd3b --- /dev/null +++ b/C++/subsets-ii.cpp @@ -0,0 +1,23 @@ +// Time: O(n * 2^n) +// Space: O(1) + +class Solution { +public: + vector> subsetsWithDup(vector &nums) { + vector> result(1); + sort(nums.begin(), nums.end()); + size_t previous_size = 0; + for (size_t i = 0; i < nums.size(); ++i) { + const size_t size = result.size(); + for (size_t j = 0; j < size; ++j) { + // Only union non-duplicate element or new union set. + if (i == 0 || nums[i] != nums[i - 1] || j >= previous_size) { + result.emplace_back(result[j]); + result.back().emplace_back(nums[i]); + } + } + previous_size = size; + } + return result; + } +}; diff --git a/C++/subsets.cpp b/C++/subsets.cpp index 9b99a3809..9c0d9d0c3 100644 --- a/C++/subsets.cpp +++ b/C++/subsets.cpp @@ -1,25 +1,36 @@ -// Time Complexity: O(2^n) -// Space Complexity: O(1) +// Time: O(n * 2^n) +// Space: O(1) class Solution { - public: - vector > subsets(vector &S) { - const int size = S.size(); - const int setSize = 1 << size; - vector > ans; - vector v; - - sort(S.begin(), S.end()); - - for(int i = 0; i < setSize; ++i) { - for(int j = 0; j < size; j++) { - if(i & (1 << j)) - v.push_back(S[j]); - } - ans.push_back(v); - v.clear(); +public: + vector > subsets(vector &nums) { + vector> result(1); + sort(nums.begin(), nums.end()); + for (size_t i = 0; i < nums.size(); ++i) { + const size_t size = result.size(); + for (size_t j = 0; j < size; ++j) { + result.emplace_back(result[j]); + result.back().emplace_back(nums[i]); } + } + return result; + } +}; - return ans; +// Time: O(n * 2^n) +// Space: O(1) +class Solution2 { +public: + vector > subsets(vector &nums) { + vector> result(1); + sort(nums.begin(), nums.end()); + for (size_t i = 0; i < nums.size(); ++i) { + const size_t size = result.size(); + for (size_t j = 0; j < size; ++j) { + result.emplace_back(result[j]); + result.back().emplace_back(nums[i]); + } } + return result; + } }; diff --git a/C++/subsetsWithDup.cpp b/C++/subsetsWithDup.cpp deleted file mode 100644 index 36907d1a3..000000000 --- a/C++/subsetsWithDup.cpp +++ /dev/null @@ -1,23 +0,0 @@ -// Time Complexity: O(2^n) -// Space Complexity: O(1) - -class Solution { - public: - vector > subsetsWithDup(vector &S) { - sort(S.begin(), S.end()); - vector > result(1); - size_t previous_size = 0; - for (size_t i = 0; i < S.size(); ++i) { - const size_t size = result.size(); - for (size_t j = 0; j < size; ++j) { - // only union non-duplicate element or new union set - if (i == 0 || S[i] != S[i-1] || j >= previous_size) { - result.push_back(result[j]); - result.back().push_back(S[i]); - } - } - previous_size = size; - } - return result; - } -}; diff --git a/C++/substring-with-concatenation-of-all-words.cpp b/C++/substring-with-concatenation-of-all-words.cpp new file mode 100644 index 000000000..eff6743b7 --- /dev/null +++ b/C++/substring-with-concatenation-of-all-words.cpp @@ -0,0 +1,96 @@ +// Time: O((m + n) * k), m is the length of the string, +// n is the size of the dictionary, +// k is the length of each word +// Space: O(n * k) + +// Sliding window solution. +class Solution { +public: + vector findSubstring(string s, vector& words) { + vector result; + const int m = s.length(); + const int n = words.size(); + const int k = words.front().length(); + if (m < n * k) { + return result; + } + + unordered_map lookup; + for (const auto& word : words) { + ++lookup[word]; // Space: O(n * k) + } + for (int i = 0; i < k; ++i) { // Time: O(k) + int left = i, count = 0; + unordered_map tmp; + for (int j = i; j <= m - k; j += k) { // Time: O(m / k) + const auto& str = s.substr(j, k); // Time: O(k) + if (lookup.count(str)) { + ++tmp[str]; + if (tmp[str] <= lookup[str]) { + ++count; + } else { + while (tmp[str] > lookup[str]) { + const auto& str1 = s.substr(left, k); + --tmp[str1]; + if (tmp[str1] < lookup[str1]) { + --count; + } + left += k; + } + } + if (count == n) { + result.emplace_back(left); + --tmp[s.substr(left, k)]; + --count; + left += k; + } + } else { + tmp.clear(); + count = 0; + left = j + k; + } + } + } + return result; + } +}; + + +// Time: O((m - n * k) * n * k) ~ O(m * n * k), m is the length of the string, +// n is the size of the dictionary, +// k is the length of each word +// Space: O(n * k) +class Solution2 { +public: + vector findSubstring(string s, vector& words) { + const auto word_length = words.front().length(); + const auto cat_length = word_length * words.size(); + vector result; + + if (s.length() < cat_length) { + return result; + } + + unordered_map wordCount; + for (const auto & word : words) { + ++wordCount[word]; + } + + for (auto it = s.begin(); it != prev(s.end(), cat_length - 1); ++it) { + unordered_map unused(wordCount); + for (auto jt = it; jt != next(it, cat_length); jt += word_length) { + auto pos = unused.find(string(jt, next(jt, word_length))); + if (pos == unused.end()) { + break; + } + if (--pos->second == 0) { + unused.erase(pos); + } + } + if (unused.empty()) { + result.emplace_back(distance(s.begin(), it)); + } + } + return result; + } +}; diff --git a/C++/subtree-of-another-tree.cpp b/C++/subtree-of-another-tree.cpp new file mode 100644 index 000000000..c018489fd --- /dev/null +++ b/C++/subtree-of-another-tree.cpp @@ -0,0 +1,37 @@ +// Time: O(m * n), m is the number of nodes of s, n is the number of nodes of t +// Space: O(h), h is the height of s + +/** + * 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: + bool isSubtree(TreeNode* s, TreeNode* t) { + return preOrderTraverse(s, t); + } + +private: + bool preOrderTraverse(TreeNode *s, TreeNode *t) { + return s && (isSame(s, t) || + preOrderTraverse(s->left, t) || + preOrderTraverse(s->right, t)); + } + + bool isSame(TreeNode *x,TreeNode *y) { + if (!x && !y) { + return true; + } + if (!x || !y) { + return false; + } + return x->val == y->val && + isSame(x->left, y->left) && + isSame(x->right, y->right); + } +}; diff --git a/C++/sum-of-distances-in-tree.cpp b/C++/sum-of-distances-in-tree.cpp new file mode 100644 index 000000000..d88e32289 --- /dev/null +++ b/C++/sum-of-distances-in-tree.cpp @@ -0,0 +1,51 @@ +// Time: O(n) +// Space: O(n) + +class Solution { +public: + vector sumOfDistancesInTree(int N, vector>& edges) { + unordered_map> graph; + for (const auto& edge : edges) { + graph[edge[0]].emplace_back(edge[1]); + graph[edge[1]].emplace_back(edge[0]); + } + + vector count(N, 1); + vector result(N, 0); + + dfs(graph, 0, -1, &count, &result); + dfs2(graph, 0, -1, &count, &result); + return result; + } + +private: + void dfs(const unordered_map>& graph, + int node, int parent, + vector *count, vector *result) { + if (!graph.count(node)) { + return; + } + for (const auto& nei : graph.at(node)) { + if (nei != parent) { + dfs(graph, nei, node, count, result); + (*count)[node] += (*count)[nei]; + (*result)[node] += (*result)[nei] + (*count)[nei]; + } + } + } + + void dfs2(const unordered_map>& graph, + int node, int parent, + vector *count, vector *result) { + if (!graph.count(node)) { + return; + } + for (const auto& nei : graph.at(node)) { + if (nei != parent) { + (*result)[nei] = (*result)[node] - (*count)[nei] + + count->size() - (*count)[nei]; + dfs2(graph, nei, node, count, result); + } + } + } +}; diff --git a/C++/sum-of-left-leaves.cpp b/C++/sum-of-left-leaves.cpp new file mode 100644 index 000000000..21f4ac16a --- /dev/null +++ b/C++/sum-of-left-leaves.cpp @@ -0,0 +1,30 @@ +// Time: O(n) +// Space: O(h) + +/** + * 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: + int sumOfLeftLeaves(TreeNode* root) { + return sumOfLeftLeavesHelper(root, false); + } + +private: + int sumOfLeftLeavesHelper(TreeNode* root, bool is_left) { + if (!root) { + return 0; + } + if (!root->left && !root->right) { + return is_left ? root->val : 0; + } + return sumOfLeftLeavesHelper(root->left, true) + + sumOfLeftLeavesHelper(root->right, false); + } +}; diff --git a/C++/sum-of-square-numbers.cpp b/C++/sum-of-square-numbers.cpp new file mode 100644 index 000000000..bf24612ce --- /dev/null +++ b/C++/sum-of-square-numbers.cpp @@ -0,0 +1,16 @@ +// Time: O(sqrt(c) * logc) +// Space: O(1) + +class Solution { +public: + bool judgeSquareSum(int c) { + for (long long a = 0; a * a <= c; ++a) { + auto b = static_cast(sqrt(c - a * a)); + if (a * a + b * b == c) { + return true; + } + } + return false; + } +}; + diff --git a/C++/sum-of-two-integers.cpp b/C++/sum-of-two-integers.cpp new file mode 100644 index 000000000..50c66d19f --- /dev/null +++ b/C++/sum-of-two-integers.cpp @@ -0,0 +1,14 @@ +// Time: O(1) +// Space: O(1) + +class Solution { +public: + int getSum(int a, int b) { + while (b) { + int carry = a & b; + a ^= b; + b = carry << 1; + } + return a; + } +}; diff --git a/C++/summary-ranges.cpp b/C++/summary-ranges.cpp new file mode 100644 index 000000000..b1a012967 --- /dev/null +++ b/C++/summary-ranges.cpp @@ -0,0 +1,30 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + vector summaryRanges(vector& nums) { + vector ranges; + if (nums.empty()) { + return ranges; + } + + int start = nums[0], end = nums[0]; + for (int i = 1; i <= nums.size(); ++i) { + if (i < nums.size() && nums[i] == end + 1) { + end = nums[i]; + } else { + auto&& range = to_string(start); + if (start != end) { + range.append("->" + to_string(end)); + } + ranges.emplace_back(range); + if (i < nums.size()) { + start = end = nums[i]; + } + } + } + + return ranges; + } +}; diff --git a/C++/super-pow.cpp b/C++/super-pow.cpp new file mode 100644 index 000000000..fe3393d87 --- /dev/null +++ b/C++/super-pow.cpp @@ -0,0 +1,27 @@ +// Time: O(n), n is the size of b. +// Space: O(1) + +class Solution { +public: + int superPow(int a, vector& b) { + int result = 1; + for (const auto& digit : b) { + result = myPow(result, 10, 1337) * myPow(a, digit, 1337) % 1337; + } + return result; + } + +private: + int myPow(int a, int n, int b) { + int result = 1; + int x = a % b; + while (n) { + if (n & 1) { + result = result * x % b; + } + n >>= 1; + x = x * x % b; + } + return result % b; + } +}; diff --git a/C++/super-ugly-number.cpp b/C++/super-ugly-number.cpp new file mode 100644 index 000000000..67097c6fb --- /dev/null +++ b/C++/super-ugly-number.cpp @@ -0,0 +1,141 @@ +// Time: O(n * k) +// Space: O(n + k) + +// Heap solution. (308ms) +class Solution { +public: + int nthSuperUglyNumber(int n, vector& primes) { + priority_queue, vector>, greater>> heap; + vector uglies(n), idx(primes.size()), ugly_by_last_prime(n); + uglies[0] = 1; + + for (int i = 0; i < primes.size(); ++i) { + heap.emplace(primes[i], i); + } + for (int i = 1; i < n; ++i) { + int k; + tie(uglies[i], k) = heap.top(); + heap.pop(); + ugly_by_last_prime[i] = k; + while (ugly_by_last_prime[++idx[k]] > k); // average time: O(k) + heap.emplace(uglies[idx[k]] * primes[k], k); + } + return uglies[n - 1]; + } +}; + +// Time: O(n * k) +// Space: O(n + k) +// DP solution. (596ms) +class Solution2 { +public: + int nthSuperUglyNumber(int n, vector& primes) { + vector uglies(n), ugly_by_prime(primes), idx(primes.size()); + uglies[0] = 1; + + for (int i = 1; i < n; ++i) { + int min_val = *min_element(ugly_by_prime.begin(), ugly_by_prime.end()); + uglies[i] = min_val; + for (int k = 0; k < primes.size(); ++k) { + if (min_val == ugly_by_prime[k]) { + ugly_by_prime[k] = primes[k] * uglies[++idx[k]]; + } + } + } + + return uglies[n - 1]; + } +}; + +// Time: O(n * logk) ~ O(n * klogk) +// Space: O(k^2) +// Heap solution. (612ms) +class Solution3 { +public: + int nthSuperUglyNumber(int n, vector& primes) { + long long ugly_number = 0; + priority_queue, greater> heap; + + heap.emplace(1); + for (const auto& p: primes) { + heap.emplace(p); + } + for (int i = 0; i < n; ++i) { + ugly_number = heap.top(); + heap.pop(); + int j = 0; + for (; j < primes.size(); ++j) { + if (ugly_number % primes[j] == 0) { + for (int k = 0; k <= j; ++k) { + // worst time: O(klogk) + // worst space: O(k^2) + heap.emplace(ugly_number * primes[k]); + } + break; + } + } + } + + return ugly_number; + } +}; + +// Time: O(n * k) +// Space: O(n + k) +// Hash solution. (804ms) +class Solution4 { +public: + int nthSuperUglyNumber(int n, vector& primes) { + priority_queue, vector>, greater>> heap; + unordered_set ugly_set{1}; + vector uglies(n), idx(primes.size()); + uglies[0] = 1; + + for (int k = 0; k < primes.size(); ++k) { + heap.emplace(primes[k], k); + ugly_set.emplace(primes[k]); + } + + for (int i = 1; i < n; ++i) { + int k; + tie(uglies[i], k) = heap.top(); + heap.pop(); + while (ugly_set.count(primes[k] * uglies[idx[k]])) { + ++idx[k]; + } + heap.emplace(primes[k] * uglies[idx[k]], k); + ugly_set.emplace(primes[k] * uglies[idx[k]]); + } + + return uglies[n - 1]; + } +}; + +// Time: O(n * logk) ~ O(n * klogk) +// Space: O(n + k) +// Heap solution. (1184ms) +class Solution5 { +public: + int nthSuperUglyNumber(int n, vector& primes) { + priority_queue, vector>, greater>> heap; + vector uglies(n), idx(primes.size()); + uglies[0] = 1; + + for (int k = 0; k < primes.size(); ++k) { + heap.emplace(primes[k], k); + } + + for (int i = 1; i < n; ++i) { + int k; + tie(uglies[i], k) = heap.top(); + + while (heap.top().first == uglies[i]) { // worst time: O(klogk) + tie(uglies[i], k) = heap.top(); + heap.pop(); + heap.emplace(primes[k] * uglies[++idx[k]], k); + } + } + + return uglies[n - 1]; + } +}; diff --git a/C++/super-washing-machines.cpp b/C++/super-washing-machines.cpp new file mode 100644 index 000000000..e2f0ff82e --- /dev/null +++ b/C++/super-washing-machines.cpp @@ -0,0 +1,19 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + int findMinMoves(vector& machines) { + int sum = accumulate(machines.begin(), machines.end(), 0); + if (sum % machines.size() != 0) { + return -1; + } + + int result = 0, target = sum / machines.size(), curr = 0; + for (const auto& n : machines) { + curr += n - target; + result = max(result, max(n - target, abs(curr))); + } + return result; + } +}; diff --git a/C++/surrounded-regions.cpp b/C++/surrounded-regions.cpp new file mode 100644 index 000000000..6ac13171c --- /dev/null +++ b/C++/surrounded-regions.cpp @@ -0,0 +1,51 @@ +// Time: O(m * n) +// Space: O(m + n) + +class Solution { +public: + void solve(vector>& board) { + if (board.empty()) { + return; + } + + queue> q; + for (int i = 0; i < board.size(); ++i) { + q.emplace(i, 0); + q.emplace(i, board[0].size() - 1); + } + for (int j = 0; j < board[0].size(); ++j) { + q.emplace(0, j); + q.emplace(board.size() - 1, j); + } + + while (!q.empty()) { + int i, j; + tie(i, j) = q.front(); + q.pop(); + if (board[i][j] == 'O' || board[i][j] == 'V') { + board[i][j] = 'V'; + const vector> directions{{0, -1}, {0, 1}, + {-1, 0}, {1, 0}}; + for (const auto& d : directions) { + const int x = i + d.first, y = j + d.second; + if (0 <= x && x < board.size() && + 0 <= y && y < board[0].size() && + board[x][y] == 'O') { + board[x][y] = 'V'; + q.emplace(x, y); + } + } + } + } + + for (int i = 0; i < board.size(); ++i) { + for (int j = 0; j < board[0].size(); ++j) { + if (board[i][j] != 'V') { + board[i][j] = 'X'; + } else { + board[i][j] = 'O'; + } + } + } + } +}; diff --git a/C++/swap-adjacent-in-lr-string.cpp b/C++/swap-adjacent-in-lr-string.cpp new file mode 100644 index 000000000..8a5662203 --- /dev/null +++ b/C++/swap-adjacent-in-lr-string.cpp @@ -0,0 +1,23 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + bool canTransform(string start, string end) { + int N = start.length(); + for (int i = 0, j = 0; i < N && j < N; ++i, ++j) { + while (i < N && start[i] == 'X') ++i; + while (j < N && end[j] == 'X') ++j; + if ((i < N) != (j < N)) { + return false; + } else if (i < N && j < N) { + if (start[i] != end[j] || + (start[i] == 'L' && i < j) || + (start[i] == 'R' && i > j)) { + return false; + } + } + } + return true; + } +}; diff --git a/C++/swap-nodes-in-pairs.cpp b/C++/swap-nodes-in-pairs.cpp new file mode 100644 index 000000000..d9ae7ae75 --- /dev/null +++ b/C++/swap-nodes-in-pairs.cpp @@ -0,0 +1,29 @@ +// Time: O(n) +// Space: O(1) + +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode(int x) : val(x), next(NULL) {} + * }; + */ +class Solution { +public: + ListNode* swapPairs(ListNode* head) { + ListNode dummy{0}; + dummy.next = head; + auto curr = &dummy; + while (curr->next && curr->next->next) { + auto next_one = curr->next; + auto next_two = next_one->next; + auto next_three = next_two->next; + curr->next = next_two; + next_two->next = next_one; + next_one->next = next_three; + curr = next_one; + } + return dummy.next; + } +}; diff --git a/C++/swim-in-rising-water.cpp b/C++/swim-in-rising-water.cpp new file mode 100644 index 000000000..0d950b690 --- /dev/null +++ b/C++/swim-in-rising-water.cpp @@ -0,0 +1,59 @@ +// Time: O(n^2) +// Space: O(n^2) + +class Solution { +public: + int swimInWater(vector>& grid) { + const int n = grid.size(); + vector> positions(n * n); + for (int i = 0; i < n; ++i) { + for (int j = 0; j < n; ++j) { + positions[grid[i][j]] = {i, j}; + } + } + static const vector> directions{{-1, 0}, {1, 0}, {0, -1}, {0, 1}}; + UnionFind union_find(n * n); + for (int elevation = 0; elevation < positions.size(); ++elevation) { + int i, j; + tie(i, j) = positions[elevation]; + for (const auto& dir : directions) { + int x = i + dir.first; + int y = j + dir.second; + if (0 <= x && x < n && + 0 <= y && y < n && + grid[x][y] <= elevation) { + union_find.union_set(i * n + j, x * n + y); + if (union_find.find_set(0) == union_find.find_set(n * n - 1)) { + return elevation; + } + } + } + } + return n * n - 1; + } + +private: + class UnionFind { + public: + UnionFind(const int n) : set_(n) { + iota(set_.begin(), set_.end(), 0); + } + + int find_set(const int x) { + if (set_[x] != x) { + set_[x] = find_set(set_[x]); // Path compression. + } + return set_[x]; + } + + void union_set(const int x, const int y) { + int x_root = find_set(x), y_root = find_set(y); + if (x_root != y_root) { + set_[min(x_root, y_root)] = max(x_root, y_root); + } + } + + private: + vector set_; + }; +}; diff --git a/C++/symmetric-tree.cpp b/C++/symmetric-tree.cpp new file mode 100644 index 000000000..e3808f89d --- /dev/null +++ b/C++/symmetric-tree.cpp @@ -0,0 +1,70 @@ +// Time: O(n) +// Space: O(h), h is the height of the binary tree. + +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode(int x) : val(x), left(NULL), right(NULL) {} + * }; + */ + +// Iterative solution. +class Solution { +public: + bool isSymmetric(TreeNode* root) { + if (!root) { + return true; + } + // isSymmetricHelper(root->left, root->right) + stack nodes; + nodes.emplace(root->left); + nodes.emplace(root->right); + + while (!nodes.empty()) { + auto right = nodes.top(); + nodes.pop(); + auto left = nodes.top(); + nodes.pop(); + if (!left && !right) { + continue; + } + if (!left || !right || left->val != right->val) { + return false; + } + // isSymmetricHelper(left->right, right->left) + nodes.emplace(left->right); + nodes.emplace(right->left); + + // isSymmetricHelper(left->left, right->right) + nodes.emplace(left->left); + nodes.emplace(right->right); + } + return true; + } +}; + + +// Recursive solution. +class Solution2 { +public: + bool isSymmetric(TreeNode* root) { + if (!root) { + return true; + } + return isSymmetricHelper(root->left, root->right); + } + + bool isSymmetricHelper(TreeNode *left, TreeNode *right) { + if (!left && !right) { + return true; + } + if (!left || !right || left->val != right->val) { + return false; + } + return isSymmetricHelper(left->left, right->right) && + isSymmetricHelper(left->right, right->left); + } +}; diff --git a/C++/tag-validator.cpp b/C++/tag-validator.cpp new file mode 100644 index 000000000..25556dd6e --- /dev/null +++ b/C++/tag-validator.cpp @@ -0,0 +1,70 @@ +// Time: O(n) +// Space: O(n) + +class Solution { +public: + bool isValid(string code) { + auto i = 0; + return validTag(code, &i) && i == code.length(); + } + +private: + bool validTag(const string& s, int *i) { + auto j = *i; + auto tag = parseTagName(s, &j); + if (tag.empty()) { + return false; + } + parseContent(s, &j); + auto k = j + tag.size() + 2; + if (k >= s.size() || s.substr(j, k + 1 - j) != "") { + return false; + } + *i = k + 1; + return true; + } + + string parseTagName(const string& s, int *i) { + if (s[*i] != '<') { + return ""; + } + auto j = s.find('>', *i); + if (j == string::npos || j - 1 - *i < 1 || 9 < j - 1 - *i) { + return ""; + } + auto tag = s.substr(*i + 1, j - 1 - *i); + for (const auto& c : tag) { + if (c < 'A' || 'Z' < c) { + return ""; + } + } + *i = j + 1; + return tag; + } + + void parseContent(const string& s, int *i) { + while (*i < s.size()) { + if (!validText(s, i) && !validCData(s, i) && !validTag(s, i)) { + break; + } + } + } + + bool validText(const string& s, int *i) { + auto j = *i; + *i = s.find("<", *i); + return *i != j; + } + + bool validCData(const string& s, int *i) { + if (s.find("", *i); + if (j == string::npos) { + return false; + } + *i = j + 3; + return true; + } +}; diff --git a/C++/target-sum.cpp b/C++/target-sum.cpp new file mode 100644 index 000000000..a3a7f01d7 --- /dev/null +++ b/C++/target-sum.cpp @@ -0,0 +1,28 @@ +// Time: O(n * S) +// Space: O(S) + +class Solution { +public: + int findTargetSumWays(vector& nums, int S) { + // sum(P) - sum(N) = S + // <=> + // 2 * sum(P) = S + sum(nums) + int sum = accumulate(nums.begin(), nums.end(), 0); + if (sum < S || (S + sum) % 2) { + return 0; + } + return subsetSum(nums, (S + sum) / 2); + } + +private: + int subsetSum(vector& nums, int S) { + vector dp(S + 1); + dp[0] = 1; + for (const auto& n : nums) { + for (int i = S; i >= n; --i) { + dp[i] += dp[i - n]; + } + } + return dp.back(); + } +}; diff --git a/C++/task-scheduler.cpp b/C++/task-scheduler.cpp new file mode 100644 index 000000000..c37929643 --- /dev/null +++ b/C++/task-scheduler.cpp @@ -0,0 +1,22 @@ +// Time: O(n) +// Space: O(26) = O(1) + +class Solution { +public: + int leastInterval(vector& tasks, int n) { + unordered_map count; + int max_count = 0; + for (const auto& task : tasks) { + ++count[task]; + max_count = max(max_count, count[task]); + } + + auto result = (max_count - 1) * (n + 1); + for (const auto& kvp : count) { + if (kvp.second == max_count) { + ++result; + } + } + return max(result, static_cast(tasks.size())); + } +}; diff --git a/C++/teemo-attacking.cpp b/C++/teemo-attacking.cpp new file mode 100644 index 000000000..294abfb79 --- /dev/null +++ b/C++/teemo-attacking.cpp @@ -0,0 +1,13 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + int findPoisonedDuration(vector& timeSeries, int duration) { + int result = duration * timeSeries.size(); + for (int i = 1; i < timeSeries.size(); ++i){ + result -= max(0, duration - (timeSeries[i] - timeSeries[i - 1])); + } + return result; + } +}; diff --git a/C++/ternary-expression-parser.cpp b/C++/ternary-expression-parser.cpp new file mode 100644 index 000000000..d71e836dc --- /dev/null +++ b/C++/ternary-expression-parser.cpp @@ -0,0 +1,32 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + string parseTernary(string expression) { + if (expression.empty()) { + return ""; + } + + string stack; + for (int i = expression.length() - 1; i >= 0; --i) { + auto c = expression[i]; + if (!stack.empty() && stack.back() == '?') { + stack.pop_back(); // pop '?' + auto first = stack.back(); stack.pop_back(); + stack.pop_back(); // pop ':' + auto second = stack.back(); stack.pop_back(); + + if (c == 'T') { + stack.push_back(first); + } else { + stack.push_back(second); + } + } else { + stack.push_back(c); + } + } + + return string(1, stack.back()); + } +}; diff --git a/C++/text-justification.cpp b/C++/text-justification.cpp new file mode 100644 index 000000000..601be856e --- /dev/null +++ b/C++/text-justification.cpp @@ -0,0 +1,48 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + vector fullJustify(vector& words, int maxWidth) { + vector res; + const int n = words.size(); + int begin = 0, len = 0; + for (int i = 0; i < n; ++i) { + if (len + words[i].size() + (i - begin) > maxWidth) { + res.emplace_back(connect(words, maxWidth, begin, i, len, false)); + begin = i; + len = 0; + } + len += words[i].size(); + } + // Last line. + res.emplace_back(connect(words, maxWidth, begin, n, len, true)); + return res; + } + +private: + string connect(const vector& words, int maxWidth, + int begin, int end, int len, + bool is_last) { + string s; + int n = end - begin; + for (int i = 0; i < n; ++i) { + s += words[begin + i]; + addSpaces(i, n - 1, maxWidth - len, is_last, &s); + } + // For only one word in a line. + if (s.size() < maxWidth) { + s.append(maxWidth - s.size(), ' '); + } + return s; + } + + void addSpaces(int i, int spaceCnt, int maxWidth, bool is_last, string *s) { + if (i < spaceCnt) { + // For the last line of text, it should be left justified, + // and no extra space is inserted between words. + int spaces = is_last ? 1 : maxWidth / spaceCnt + (i < maxWidth % spaceCnt); + s->append(spaces, ' '); + } + } +}; diff --git a/C++/textJustification.cpp b/C++/textJustification.cpp deleted file mode 100644 index 8187d104f..000000000 --- a/C++/textJustification.cpp +++ /dev/null @@ -1,46 +0,0 @@ -// LeetCode, Text Justification -// Complexity: -// O(n) time -// O(1) space - -class Solution { -public: - vector fullJustify(vector &words, int L) { - vector result; - const int n = words.size(); - int begin = 0, len = 0; - for (int i = 0; i < n; ++i) { - if (len + words[i].size() + (i - begin) > L) { - result.push_back(connect(words, begin, i - 1, len, L, false)); - begin = i; - len = 0; - } - - len += words[i].size(); - } - // last line - result.push_back(connect(words, begin, n - 1, len, L, true)); - return result; - } - - string connect(vector &words, int begin, int end, - int len, int L, bool is_last) { - string s; - int n = end - begin + 1; - for (int i = 0; i < n; ++i) { - s += words[begin + i]; - addSpaces(s, i, n - 1, L - len, is_last); - } - // for only one word in a line - if (s.size() < L) s.append(L - s.size(), ' '); - return s; - } - - void addSpaces(string &s, int i, int n, int L, bool is_last) { - if (n < 1 || i > n - 1) return; - // for the last line of text, it should be left justified, - // and no extra space is inserted between words. - int spaces = is_last ? 1 : (L / n + (i < (L % n) ? 1 : 0)); - s.append(spaces, ' '); - } -}; \ No newline at end of file diff --git a/C++/the-maze-ii.cpp b/C++/the-maze-ii.cpp new file mode 100644 index 000000000..1256ced9b --- /dev/null +++ b/C++/the-maze-ii.cpp @@ -0,0 +1,58 @@ +// Time: O(max(r, c) * wlogw) +// Space: O(w) + +class Solution { +public: + int shortestDistance(vector>& maze, vector& start, vector& destination) { + static const vector> dirs = {{-1, 0}, {0, 1}, {0, -1}, {1, 0}}; + priority_queue, greater> heap; + unordered_set visited; + heap.emplace(0, start); + + while (!heap.empty()) { + int dist = 0; + vector node; + tie(dist, node) = heap.top(); + heap.pop(); + if (visited.count(hash(maze, node))) { + continue; + } + if (node[0] == destination[0] && + node[1] == destination[1]) { + return dist; + } + + visited.emplace(hash(maze, node)); + for (const auto& dir : dirs) { + int neighbor_dist = 0; + vector neighbor; + tie(neighbor_dist, neighbor) = findNeighbor(maze, node, dir); + heap.emplace(dist + neighbor_dist, neighbor); + } + } + + return -1; + } + +private: + using node = pair>; + + node findNeighbor(const vector>& maze, + const vector& node, const vector& dir) { + vector cur_node = node; + int dist = 0; + + while (0 <= cur_node[0] + dir[0] && cur_node[0] + dir[0] < maze.size() && + 0 <= cur_node[1] + dir[1] && cur_node[1] + dir[1] < maze[0].size() && + !maze[cur_node[0] + dir[0]][cur_node[1] + dir[1]]) { + cur_node[0] += dir[0]; + cur_node[1] += dir[1]; + ++dist; + } + return {dist, cur_node}; + } + + int hash(const vector>& maze, const vector& node) { + return node[0] * maze[0].size() + node[1]; + } +}; diff --git a/C++/the-maze-iii.cpp b/C++/the-maze-iii.cpp new file mode 100644 index 000000000..cf2739527 --- /dev/null +++ b/C++/the-maze-iii.cpp @@ -0,0 +1,75 @@ +// Time: O(max(r, c) * wlogw) +// Space: O(w^2) + +class Solution { +public: + string findShortestWay(vector>& maze, vector& ball, vector& hole) { + static const unordered_map> dirs = {{"u", {-1, 0}}, {"r", {0, 1}}, + {"l", {0, -1}}, {"d", {1, 0}}}; + priority_queue, greater> heap; + unordered_set visited; + heap.emplace(0, make_pair("", ball)); + + while (!heap.empty()) { + int dist = 0; + string path; + vector node; + tie(dist, lvalue(tie(path, node))) = heap.top(); + heap.pop(); + if (visited.count(hash(maze, node))) { + continue; + } + + if (node[0] == hole[0] && + node[1] == hole[1]) { + return path; + } + + visited.emplace(hash(maze, node)); + for (const auto& kvp : dirs) { + int neighbor_dist = 0; + string dir; + vector neighbor; + tie(neighbor_dist, lvalue(tie(dir, neighbor))) = findNeighbor(maze, hole, node, kvp); + heap.emplace(dist + neighbor_dist, make_pair(path + dir, neighbor)); + } + } + + return "impossible"; + } + +private: + using node = pair>>; + + node findNeighbor(const vector>& maze, const vector& hole, + const vector& node, const pair>& kvp) { + string dir; + vector vec; + tie(dir, vec) = kvp; + vector cur_node = node; + int dist = 0; + + while (0 <= cur_node[0] + vec[0] && cur_node[0] + vec[0] < maze.size() && + 0 <= cur_node[1] + vec[1] && cur_node[1] + vec[1] < maze[0].size() && + !maze[cur_node[0] + vec[0]][cur_node[1] + vec[1]]) { + + cur_node[0] += vec[0]; + cur_node[1] += vec[1]; + ++dist; + if (cur_node[0] == hole[0] && + cur_node[1] == hole[1]) { + break; + } + } + return {dist, {dir, cur_node}}; + } + + int hash(const vector>& maze, const vector& node) { + return node[0] * maze[0].size() + node[1]; + } + + template + constexpr T &lvalue(T &&v) { + return v; + } +}; diff --git a/C++/the-maze.cpp b/C++/the-maze.cpp new file mode 100644 index 000000000..6a80e40e6 --- /dev/null +++ b/C++/the-maze.cpp @@ -0,0 +1,58 @@ +// Time: O(max(r, c) * w) +// Space: O(w) + +class Solution { +public: + bool hasPath(vector>& maze, vector& start, vector& destination) { + static const vector> dirs = {{-1, 0}, {0, 1}, {0, -1}, {1, 0}}; + queue q; + unordered_set visited; + q.emplace(0, start); + + while (!q.empty()) { + int dist = 0; + vector node; + tie(dist, node) = q.front(); + q.pop(); + if (visited.count(hash(maze, node))) { + continue; + } + if (node[0] == destination[0] && + node[1] == destination[1]) { + return true; + } + + visited.emplace(hash(maze, node)); + for (const auto& dir : dirs) { + int neighbor_dist = 0; + vector neighbor; + tie(neighbor_dist, neighbor) = findNeighbor(maze, node, dir); + q.emplace(dist + neighbor_dist, neighbor); + } + } + + return false; + } + +private: + using node = pair>; + + node findNeighbor(const vector>& maze, + const vector& node, const vector& dir) { + vector cur_node = node; + int dist = 0; + + while (0 <= cur_node[0] + dir[0] && cur_node[0] + dir[0] < maze.size() && + 0 <= cur_node[1] + dir[1] && cur_node[1] + dir[1] < maze[0].size() && + !maze[cur_node[0] + dir[0]][cur_node[1] + dir[1]]) { + cur_node[0] += dir[0]; + cur_node[1] += dir[1]; + ++dist; + } + return {dist, cur_node}; + } + + int hash(const vector>& maze, const vector& node) { + return node[0] * maze[0].size() + node[1]; + } +}; diff --git a/C++/the-skyline-problem.cpp b/C++/the-skyline-problem.cpp new file mode 100644 index 000000000..626963c95 --- /dev/null +++ b/C++/the-skyline-problem.cpp @@ -0,0 +1,147 @@ +// Time: O(nlogn) +// Space: O(n) + +// BST solution. +class Solution { +public: + enum {start, end, height}; + + struct Endpoint { + int height; + bool isStart; + }; + + vector > getSkyline(vector >& buildings) { + map> point_to_height; // Ordered, no duplicates. + for (const auto& building : buildings) { + point_to_height[building[start]].emplace_back(Endpoint{building[height], true}); + point_to_height[building[end]].emplace_back(Endpoint{building[height], false}); + } + + vector> res; + map height_to_count; // BST. + int curr_max = 0; + // Enumerate each point in increasing order. + for (const auto& kvp : point_to_height) { + const auto& point = kvp.first; + const auto& heights = kvp.second; + + for (const auto& h : heights) { + if (h.isStart) { + ++height_to_count[h.height]; + } else { + --height_to_count[h.height]; + if (height_to_count[h.height] == 0) { + height_to_count.erase(h.height); + } + } + } + + if (height_to_count.empty() || + curr_max != height_to_count.crbegin()->first) { + curr_max = height_to_count.empty() ? + 0 : height_to_count.crbegin()->first; + res.emplace_back(point, curr_max); + } + } + return res; + } +}; + +// Time: O(nlogn) +// Space: O(n) +// Divide and conquer solution. +class Solution2 { +public: + enum {start, end, height}; + + vector> getSkyline(vector>& buildings) { + const auto intervals = ComputeSkylineInInterval(buildings, 0, buildings.size()); + + vector> res; + int last_end = -1; + for (const auto& interval : intervals) { + if (last_end != -1 && last_end < interval[start]) { + res.emplace_back(last_end, 0); + } + res.emplace_back(interval[start], interval[height]); + last_end = interval[end]; + } + if (last_end != -1) { + res.emplace_back(last_end, 0); + } + return res; + } + + // Divide and Conquer. + vector> ComputeSkylineInInterval(const vector>& buildings, + int left_endpoint, int right_endpoint) { + if (right_endpoint - left_endpoint <= 1) { // 0 or 1 skyline, just copy it. + return {buildings.cbegin() + left_endpoint, + buildings.cbegin() + right_endpoint}; + } + int mid = left_endpoint + ((right_endpoint - left_endpoint) / 2); + auto left_skyline = ComputeSkylineInInterval(buildings, left_endpoint, mid); + auto right_skyline = ComputeSkylineInInterval(buildings, mid, right_endpoint); + return MergeSkylines(left_skyline, right_skyline); + } + + // Merge Sort + vector> MergeSkylines(vector>& left_skyline, vector>& right_skyline) { + int i = 0, j = 0; + vector> merged; + + while (i < left_skyline.size() && j < right_skyline.size()) { + if (left_skyline[i][end] < right_skyline[j][start]) { + merged.emplace_back(move(left_skyline[i++])); + } else if (right_skyline[j][end] < left_skyline[i][start]) { + merged.emplace_back(move(right_skyline[j++])); + } else if (left_skyline[i][start] <= right_skyline[j][start]) { + MergeIntersectSkylines(merged, left_skyline[i], i, + right_skyline[j], j); + } else { // left_skyline[i][start] > right_skyline[j][start]. + MergeIntersectSkylines(merged, right_skyline[j], j, + left_skyline[i], i); + } + } + + // Insert the remaining skylines. + merged.insert(merged.end(), left_skyline.begin() + i, left_skyline.end()); + merged.insert(merged.end(), right_skyline.begin() + j, right_skyline.end()); + return merged; + } + + // a[start] <= b[start] + void MergeIntersectSkylines(vector>& merged, vector& a, int& a_idx, + vector& b, int& b_idx) { + if (a[end] <= b[end]) { + if (a[height] > b[height]) { // |aaa| + if (b[end] != a[end]) { // |abb|b + b[start] = a[end]; + merged.emplace_back(move(a)), ++a_idx; + } else { // aaa + ++b_idx; // abb + } + } else if (a[height] == b[height]) { // abb + b[start] = a[start], ++a_idx; // abb + } else { // a[height] < b[height]. + if (a[start] != b[start]) { // bb + merged.emplace_back(move(vector{a[start], b[start], a[height]})); // |a|bb + } + ++a_idx; + } + } else { // a[end] > b[end]. + if (a[height] >= b[height]) { // aaaa + ++b_idx; // abba + } else { + // |bb| + // |a||bb|a + if (a[start] != b[start]) { + merged.emplace_back(move(vector{a[start], b[start], a[height]})); + } + a[start] = b[end]; + merged.emplace_back(move(b)), ++b_idx; + } + } + } +}; diff --git a/C++/third-maximum-number.cpp b/C++/third-maximum-number.cpp new file mode 100644 index 000000000..baa9cfa23 --- /dev/null +++ b/C++/third-maximum-number.cpp @@ -0,0 +1,31 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + int thirdMax(vector& nums) { + int count = 0; + vector top(3, numeric_limits::min()); + + for (const auto& num : nums) { + if (num > top[0]) { + top[2] = top[1]; + top[1] = top[0]; + top[0] = num; + ++count; + } else if (num != top[0] && num > top[1]) { + top[2] = top[1]; + top[1] = num; + ++count; + } else if (num != top[0] && num != top[1] && num >= top[2]) { + top[2] = num; + ++count; + } + } + + if (count < 3) { + return top[0]; + } + return top[2]; + } +}; diff --git a/C++/threeSum.cpp b/C++/threeSum.cpp deleted file mode 100644 index 8cbea6c72..000000000 --- a/C++/threeSum.cpp +++ /dev/null @@ -1,47 +0,0 @@ -// Time Complexity: O(n^2) -// Space Complexity: O(1) - -class Solution { - public: - vector > threeSum(vector &num) { - vector > ans; - const int target = 0; - - if(num.size() < 3) - return ans; - - sort(num.begin(), num.end()); - auto last = num.end(); - for(auto a = num.begin(); a < prev(last, 2); ++a) { - if(a > num.begin() && *a == *(a - 1)) - continue; - auto b = next(a); - auto c = prev(last); - - while(b < c) { - if(b > next(a) && *b == *(b - 1)) { - ++b; - } - else if(c < prev(last) && *c == *(c + 1)) { - --c; - } - else { - const int sum = *a + *b + *c; - - if(sum < target) - ++b; - else if(sum > target) - --c; - else { - ans.push_back({ *a, *b, *c}); - ++b; - --c; - } - } - } - } - - return ans; - } -}; - diff --git a/C++/threeSumCloset.cpp b/C++/threeSumCloset.cpp deleted file mode 100644 index 67034d769..000000000 --- a/C++/threeSumCloset.cpp +++ /dev/null @@ -1,32 +0,0 @@ -// Time Complexity: O(n^2) -// Space Complexity: O(1) - -class Solution { - public: - int threeSumClosest(vector &num, int target) { - int ans = 0; - int gap = INT_MAX; - - sort(num.begin(), num.end()); - auto last = num.end(); - for(auto a = num.begin(); a != prev(last, 2); a++) { - auto b = next(a); - auto c = prev(last); - - while(b != c) { - const int sum = *a + *b + *c; - - if(gap > abs(target - sum)) { - gap = abs(target - sum); - ans = sum; - } - if(sum < target) - ++b; - else - --c; - } - } - - return ans; - } -}; diff --git a/C++/to-lower-case.cpp b/C++/to-lower-case.cpp new file mode 100644 index 000000000..73e99af33 --- /dev/null +++ b/C++/to-lower-case.cpp @@ -0,0 +1,17 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + string toLowerCase(string str) { + string result; + for (const auto c : str) { + if ('A' <= c && c <= 'Z') { + result.push_back('a' + c - 'A'); + } else { + result.push_back(c); + } + } + return result; + } +}; diff --git a/C++/toeplitz-matrix.cpp b/C++/toeplitz-matrix.cpp new file mode 100644 index 000000000..febae4902 --- /dev/null +++ b/C++/toeplitz-matrix.cpp @@ -0,0 +1,17 @@ +// Time: O(m * n) +// Space: O(1) + +class Solution { +public: + bool isToeplitzMatrix(vector>& matrix) { + for (int i = 0; i < matrix.size(); ++i) { + for (int j = 0; j < matrix[i].size(); ++j) { + if (i != 0 && j != 0 && + matrix[i - 1][j - 1] != matrix[i][j]) { + return false; + } + } + } + return true; + } +}; diff --git a/C++/top-k-frequent-elements.cpp b/C++/top-k-frequent-elements.cpp new file mode 100644 index 000000000..216a27f24 --- /dev/null +++ b/C++/top-k-frequent-elements.cpp @@ -0,0 +1,78 @@ +// Time: O(n) +// Space: O(n) + +// Bucket Sort Solution +class Solution { +public: + vector topKFrequent(vector& nums, int k) { + unordered_map counts; + for (const auto& i : nums) { + ++counts[i]; + } + vector> buckets(nums.size() + 1); + for (const auto& kvp : counts) { + buckets[kvp.second].emplace_back(kvp.first); + } + + vector result; + for (int i = buckets.size() - 1; i >= 0; --i) { + for (int j = 0; j < buckets[i].size(); ++j){ + result.emplace_back(buckets[i][j]); + if (result.size() == k) { + return result; + } + } + } + return result; + } +}; + +// Time: O(n) ~ O(n^2), O(n) on average. +// Space: O(n) +// Quick Select Solution +class Solution2 { +public: + vector topKFrequent(vector& nums, int k) { + unordered_map counts; + for (const auto& i : nums) { + ++counts[i]; + } + vector> p; + for (const auto& kvp : counts) { + p.emplace_back(-kvp.second, kvp.first); + } + nth_element(p.begin(), p.begin() + k - 1, p.end()); + vector result; + for (int i = 0; i < k; ++i) { + result.emplace_back(p[i].second); + } + return result; + } +}; + +// Time: O(nlogk) +// Space: O(n) +// Heap solution. +class Solution3 { +public: + vector topKFrequent(vector& nums, int k) { + unordered_map counts; + for (const auto& i : nums) { + ++counts[i]; + } + priority_queue> heap; + for (const auto& kvp : counts) { + heap.emplace(-kvp.second, kvp.first); + if (heap.size() == k + 1) { + heap.pop(); + } + } + vector result; + while (!heap.empty()) { + result.emplace_back(heap.top().second); + heap.pop(); + } + reverse(result.begin(), result.end()); + return result; + } +}; diff --git a/C++/top-k-frequent-words.cpp b/C++/top-k-frequent-words.cpp new file mode 100644 index 000000000..fe77afcb7 --- /dev/null +++ b/C++/top-k-frequent-words.cpp @@ -0,0 +1,86 @@ +// Time: O(n + klogk) on average +// Space: O(n) + +// Quick Select Solution +class Solution { +public: + vector topKFrequent(vector& words, int k) { + unordered_map counts; + for (const auto& word : words) { + ++counts[word]; + } + vector> p; + for (const auto& kvp : counts) { + p.emplace_back(-kvp.second, kvp.first); + } + nth_element(p.begin(), p.begin() + k - 1, p.end()); // O(n) time on average. + sort(p.begin(), p.begin() + k); // O(klogk) time. + vector result; + for (int i = 0; i < k; ++i) { + result.emplace_back(p[i].second); + } + return result; + } +}; + + +// Time: O(nlogk) +// Space: O(n) +// Heap Solution +class Solution2 { +public: + vector topKFrequent(vector& words, int k) { + unordered_map counts; + for (const auto& word : words) { + ++counts[word]; + } + priority_queue> heap; + for (const auto& kvp : counts) { + heap.emplace(-kvp.second, kvp.first); + if (heap.size() == k + 1) { + heap.pop(); + } + } + vector result; + while (!heap.empty()) { + result.emplace_back(heap.top().second); + heap.pop(); + } + reverse(result.begin(), result.end()); + return result; + } +}; + + +// Time: O(n + klogk) ~ O(n + nlogn) +// Space: O(n) +// Bucket Sort Solution +class Solution3 { +public: + vector topKFrequent(vector& words, int k) { + unordered_map counts; + for (const auto& word : words) { + ++counts[word]; + } + vector> buckets(words.size() + 1); + for (const auto& kvp : counts) { + buckets[kvp.second].emplace_back(kvp.first); + } + + vector> p; + for (int i = buckets.size() - 1; i >= 0; --i) { + for (const auto& word : buckets[i]) { + p.emplace_back(-i, word); + } + if (p.size() >= k) { + break; + } + } + sort(p.begin(), p.end()); + vector result; + for (int i = 0; i < k; ++i) { + result.emplace_back(p[i].second); + } + return result; + } +}; diff --git a/C++/total-hamming-distance.cpp b/C++/total-hamming-distance.cpp new file mode 100644 index 000000000..b9098e48e --- /dev/null +++ b/C++/total-hamming-distance.cpp @@ -0,0 +1,17 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + int totalHammingDistance(vector& nums) { + int result = 0; + for (int i = 0; i < 8 * sizeof(int); ++i) { + vector counts(2); + for (const auto& num : nums) { + ++counts[(num >> i) & 1]; + } + result += counts[0] * counts[1]; + } + return result; + } +}; diff --git a/C++/transform-to-chessboard.cpp b/C++/transform-to-chessboard.cpp new file mode 100644 index 000000000..181036c7e --- /dev/null +++ b/C++/transform-to-chessboard.cpp @@ -0,0 +1,78 @@ +// Time: O(n^2) +// Space: O(n) + +class Solution { +public: + int movesToChessboard(vector>& board) { + const int N = board.size(); + unordered_map, int, Hash>> row_lookup, col_lookup; + for (int i = 0; i < N; ++i) { + const auto& row = board[i]; + ++row_lookup[row]; + if (row_lookup.size() > 2) { + return -1; + } + } + for (int j = 0; j < N; ++j) { + vector col; + for (int i = 0; i < N; ++i) { + col.emplace_back(board[i][j]); + } + ++col_lookup[col]; + if (col_lookup.size() > 2) { + return -1; + } + } + + int row_count = move(N, row_lookup); + if (row_count < 0) { + return -1; + } + int col_count = move(N, col_lookup); + if (col_count < 0) { + return -1; + } + return row_count + col_count; + } + +private: + template + struct Hash { + size_t operator()(const ContType& v) const { + size_t seed = 0; + for (const auto& i : v) { + seed ^= std::hash{}(i) + 0x9e3779b9 + (seed<<6) + (seed>>2); + } + return seed; + } + }; + + int move(int N, const unordered_map, int, Hash>>& lookup) { + if (lookup.size() != 2 || + min(lookup.begin()->second, next(lookup.begin())->second) != N / 2 || + max(lookup.begin()->second, next(lookup.begin())->second) != (N + 1) / 2) { + return -1; + } + const auto& seq1 = lookup.begin()->first; + const auto& seq2 = next(lookup.begin())->first; + for (int i = 0; i < N; ++i) { + if (seq1[i] == seq2[i]) { + return -1; + } + } + + vector begins = (N % 2) ? vector{static_cast(std::count(seq1.begin(), seq1.end(), 1) * 2 > N)} : + vector{0, 1}; + int result = numeric_limits::max(); + for (const auto& begin : begins) { + int i = begin; + int sum = 0; + for (const auto& v : seq1) { + sum += static_cast((i % 2) != v); + ++i; + } + result = min(result, sum / 2); + } + return result; + } +}; diff --git a/C++/transpose-matrix.cpp b/C++/transpose-matrix.cpp new file mode 100644 index 000000000..c6b259607 --- /dev/null +++ b/C++/transpose-matrix.cpp @@ -0,0 +1,15 @@ +// Time: O(r * c) +// Space: O(1) + +class Solution { +public: + vector> transpose(vector>& A) { + vector> result(A[0].size(), vector(A.size())); + for (int r = 0; r < A.size(); ++r) { + for (int c = 0; c < A[0].size(); ++c) { + result[c][r] = A[r][c]; + } + } + return result; + } +}; diff --git a/C++/trap.cpp b/C++/trap.cpp deleted file mode 100644 index cb25e875e..000000000 --- a/C++/trap.cpp +++ /dev/null @@ -1,30 +0,0 @@ -// Time Complexity: O(n) -// Space Complexity: O(1) - -class Solution { - public: - int trap(int A[], int n) { - int max = 0; - for(int i = 0; i < n; ++i) { - if(A[i] > A[max]) - max = i; - } - - int water = 0; - for(int i = 0, top = 0; i < max; ++i) { - if(A[i] > top) - top = A[i]; - else - water += top - A[i]; - } - - for(int i = n - 1, top = 0; i > max; --i) { - if(A[i] > top) - top = A[i]; - else - water += top - A[i]; - } - - return water; - } -}; diff --git a/C++/trapping-rain-water-ii.cpp b/C++/trapping-rain-water-ii.cpp new file mode 100644 index 000000000..97c231218 --- /dev/null +++ b/C++/trapping-rain-water-ii.cpp @@ -0,0 +1,81 @@ +// Time: O(m * n * log(m + n)) ~ O(m * n * log(m * n)) +// Space: O(m * n) + +class Solution { +public: + int trapRainWater(vector>& heightMap) { + // Init m_, n_, is_visited_. + m_ = heightMap.size(); + if (!m_) { + return 0; + } + n_ = heightMap[0].size(); + if (!n_) { + return 0; + } + + is_visited_ = vector>(m_, vector(n_, false)); + + int trap = 0; + + // Put the cells on the border into min heap. + for (int i = 0; i < m_; ++i) { + heap_.emplace(Cell{i, 0, heightMap[i][0]}); + is_visited_[i][0] = true; + heap_.emplace(Cell{i, n_ - 1, heightMap[i][n_ - 1]}); + is_visited_[i][n_ - 1] = true; + } + for (int j = 0; j < n_; ++j) { + heap_.emplace(Cell{0, j, heightMap[0][j]}); + is_visited_[0][j] = true; + heap_.emplace(Cell{m_ - 1, j, heightMap[m_ - 1][j]}); + is_visited_[m_ - 1][j] = true; + } + const vector> directions{{0, -1}, {0, 1}, + {-1, 0}, {1, 0}}; + // BFS with priority queue (min heap) + while (!heap_.empty()) { + Cell c = heap_.top(); + heap_.pop(); + for (const auto& d : directions) { + trap += fill(heightMap, c.i + d.first, c.j + d.second, c.height); + } + } + + return trap; + } + +private: + int fill(const vector>& heightMap, int i, int j, int height) { + // Out of border. + if ( i < 0 || i >= m_ || j < 0 || j >= n_) { + return 0; + } + + // Fill unvisited cell. + if (!is_visited_[i][j]) { + heap_.emplace(Cell{i, j, max(height, heightMap[i][j])}); + is_visited_[i][j] = true; // Marked as visited. + return max(0, height - heightMap[i][j]); // Fill in the gap. + } + + return 0; + } + + struct Cell { + int i; + int j; + int height; + }; + + struct Compare { + bool operator()(const Cell& a, const Cell& b) { + return a.height > b.height; + } + }; + + int m_; + int n_; + vector> is_visited_; + priority_queue, Compare> heap_; // Use min heap to get the lowerest cell. +}; diff --git a/C++/trapping-rain-water.cpp b/C++/trapping-rain-water.cpp new file mode 100644 index 000000000..082772360 --- /dev/null +++ b/C++/trapping-rain-water.cpp @@ -0,0 +1,35 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + int trap(vector& height) { + if (height.empty()) { + return 0; + } + + int i = 0, j = height.size() - 1; + int left_height = height[0]; + int right_height = height[height.size() - 1]; + int trap = 0; + + while (i < j) { + if (left_height < right_height) { + ++i; + // Fill in the gap. + trap += max(0, left_height - height[i]); + // Update current max height from left. + left_height = max(left_height, height[i]); + } + else { + --j; + // Fill in the gap. + trap += max(0, right_height - height[j]); + // Update current max height from right. + right_height = max(right_height, height[j]); + } + } + + return trap; + } +}; diff --git a/C++/trim-a-binary-search-tree.cpp b/C++/trim-a-binary-search-tree.cpp new file mode 100644 index 000000000..d7ebff2bc --- /dev/null +++ b/C++/trim-a-binary-search-tree.cpp @@ -0,0 +1,29 @@ +// Time: O(n) +// Space: O(h) + +/** + * 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* trimBST(TreeNode* root, int L, int R) { + if (!root) { + return nullptr; + } + if (root->val < L) { + return trimBST(root->right, L, R); + } + if (root->val > R) { + return trimBST(root->left, L, R); + } + root->left = trimBST(root->left, L, R); + root->right = trimBST(root->right, L, R); + return root; + } +}; diff --git a/C++/two-sum-ii-input-array-is-sorted.cpp b/C++/two-sum-ii-input-array-is-sorted.cpp new file mode 100644 index 000000000..d7baf13f8 --- /dev/null +++ b/C++/two-sum-ii-input-array-is-sorted.cpp @@ -0,0 +1,22 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + vector twoSum(vector& numbers, int target) { + int left = 0, right = numbers.size() - 1; + + while (left != right) { + const auto sum = numbers[left] + numbers[right]; + if (sum > target) { + --right; + } else if (sum < target) { + ++left; + } else { + return {left + 1, right + 1}; + } + } + + return {0, 0}; + } +}; diff --git a/C++/two-sum-iii-data-structure-design.cpp b/C++/two-sum-iii-data-structure-design.cpp new file mode 100644 index 000000000..d664aea3c --- /dev/null +++ b/C++/two-sum-iii-data-structure-design.cpp @@ -0,0 +1,31 @@ +// Time: O(n) +// Space: O(n) + +class TwoSum { +public: + + // Add the number to an internal data structure. + void add(int number) { + ++lookup_[number]; + } + + // Find if there exists any pair of numbers which sum is equal to the value. + bool find(int value) { + for (const auto& kvp : lookup_) { + const auto num = value - kvp.first; + if (lookup_.count(num) && (num != kvp.first || kvp.second > 1)) { + return true; + } + } + return false; + } + +private: + unordered_map lookup_; +}; + + +// Your TwoSum object will be instantiated and called as such: +// TwoSum twoSum; +// twoSum.add(number); +// twoSum.find(value); diff --git a/C++/two-sum-iv-input-is-a-bst.cpp b/C++/two-sum-iv-input-is-a-bst.cpp new file mode 100644 index 000000000..3cbeeb4f4 --- /dev/null +++ b/C++/two-sum-iv-input-is-a-bst.cpp @@ -0,0 +1,67 @@ +// Time: O(n) +// Space: O(h) + +/** + * 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: + bool findTarget(TreeNode* root, int k) { + if (!root) { + return false; + } + BSTIterator left(root, true), right(root, false); + while (*left < *right) { + if (*left + *right == k) { + return true; + } else if (*left + *right < k) { + ++left; + } else { + ++right; + } + } + return false; + } + +private: + class BSTIterator { + public: + BSTIterator(TreeNode *root, bool forward) : + node_(root), + forward_(forward) { + ++(*this); + }; + + int operator*() { + return cur_; + } + + void operator++() { + while (node_ || !s_.empty()) { + if (node_) { + s_.emplace(node_); + node_ = forward_ ? node_->left : node_->right; + } else { + node_ = s_.top(); + s_.pop(); + cur_ = node_->val; + node_ = forward_ ? node_->right : node_->left; + break; + } + } + } + + private: + TreeNode* node_; + bool forward_; + stack s_; + int cur_; + }; +}; diff --git a/C++/two-sum.cpp b/C++/two-sum.cpp new file mode 100644 index 000000000..841d2e1a5 --- /dev/null +++ b/C++/two-sum.cpp @@ -0,0 +1,16 @@ +// Time: O(n) +// Space: O(n) + +class Solution { +public: + vector twoSum(vector& nums, int target) { + unordered_map lookup; + for (int i = 0; i < nums.size(); ++i) { + if (lookup.count(target - nums[i])) { + return {lookup[target - nums[i]], i}; + } + lookup[nums[i]] = i; + } + return {}; + } +}; diff --git a/C++/ugly-number-ii.cpp b/C++/ugly-number-ii.cpp new file mode 100644 index 000000000..00c79a9f5 --- /dev/null +++ b/C++/ugly-number-ii.cpp @@ -0,0 +1,85 @@ +// Time: O(n) +// Space: O(n) + +// DP solution. (12ms) +class Solution { +public: + int nthUglyNumber(int n) { + vector uglies(n); + uglies[0] = 1; + + int f2 = 2, f3 = 3, f5 = 5; + int idx2 = 0, idx3 = 0, idx5 = 0; + + for (int i = 1; i < n; ++i) { + int min_val = min(min(f2, f3), f5); + uglies[i] = min_val; + + if (min_val == f2) { + f2 = 2 * uglies[++idx2]; + } + if (min_val == f3) { + f3 = 3 * uglies[++idx3]; + } + if (min_val == f5) { + f5 = 5 * uglies[++idx5]; + } + } + + return uglies[n - 1]; + } +}; + +// Time: O(n) +// Space: O(1) +// Heap solution. (148ms) +class Solution2 { +public: + int nthUglyNumber(int n) { + long long ugly_number = 0; + priority_queue, greater> heap; + + heap.emplace(1); + for (int i = 0; i < n; ++i) { + ugly_number = heap.top(); + heap.pop(); + if (ugly_number % 2 == 0) { + heap.emplace(ugly_number * 2); + } else if (ugly_number % 3 == 0) { + heap.emplace(ugly_number * 2); + heap.emplace(ugly_number * 3); + } else { + heap.emplace(ugly_number * 2); + heap.emplace(ugly_number * 3); + heap.emplace(ugly_number * 5); + } + } + return ugly_number; + } +}; + +// BST solution. +class Solution3 { +public: + int nthUglyNumber(int n) { + long long ugly_number = 0; + set bst; + + bst.emplace(1); + for (int i = 0; i < n; ++i) { + ugly_number = *bst.cbegin(); + bst.erase(bst.cbegin()); + if (ugly_number % 2 == 0) { + bst.emplace(ugly_number * 2); + } else if (ugly_number % 3 == 0) { + bst.emplace(ugly_number * 2); + bst.emplace(ugly_number * 3); + } else { + bst.emplace(ugly_number * 2); + bst.emplace(ugly_number * 3); + bst.emplace(ugly_number * 5); + } + } + return ugly_number; + } +}; diff --git a/C++/ugly-number.cpp b/C++/ugly-number.cpp new file mode 100644 index 000000000..3f357bc5c --- /dev/null +++ b/C++/ugly-number.cpp @@ -0,0 +1,17 @@ +// Time: O(logn) = O(1) +// Space: O(1) + +class Solution { +public: + bool isUgly(int num) { + if (num == 0) { + return false; + } + for (const auto& i : {2, 3, 5}) { + while (num % i == 0) { + num /= i; + } + } + return num == 1; + } +}; diff --git a/C++/unique-binary-search-trees-ii.cpp b/C++/unique-binary-search-trees-ii.cpp new file mode 100644 index 000000000..a26e83c9f --- /dev/null +++ b/C++/unique-binary-search-trees-ii.cpp @@ -0,0 +1,57 @@ +// Time: O(n * 4^n / n^(3/2)) ~= Catalan numbers +// Space: O(n) + +/** + * 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: + vector generateTrees(int n) { + if (n == 0) { + return {}; + } + return generateTreesHelper(1, n); + } + +private: + vector generateTreesHelper(int start, int end) { + vector result; + if (start > end) { + result.emplace_back(nullptr); + return result; + } + + for (int i = start; i <= end; ++i) { + vector leftSubTrees = generateTreesHelper(start, i - 1); + vector rightSubTrees = generateTreesHelper(i + 1, end); + for (const auto& left : leftSubTrees) { + for (const auto& right : rightSubTrees) { + TreeNode *root = new TreeNode(i); + root->left = clone(left); + root->right = clone(right); + result.emplace_back(root); + } + } + + } + return result; + } + + TreeNode *clone(TreeNode *root) { + TreeNode *newRoot = nullptr; + + if (root) { + newRoot = new TreeNode(root->val); + newRoot->left = clone(root->left); + newRoot->right = clone(root->right); + } + + return newRoot; + } +}; diff --git a/C++/unique-letter-string.cpp b/C++/unique-letter-string.cpp new file mode 100644 index 000000000..155422ffb --- /dev/null +++ b/C++/unique-letter-string.cpp @@ -0,0 +1,23 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + int uniqueLetterString(string S) { + static const int M = 1e9 + 7; + int result = 0; + vector> index(26, vector(2, -1)); + for (int i = 0; i < S.length(); ++i) { + int c = S[i] - 'A'; + result = (result + (i - index[c][1]) * + (index[c][1] - index[c][0])) % M; + index[c][0] = index[c][1]; + index[c][1] = i; + } + for (int c = 0; c < 26; ++c) { + result = (result + (S.length() - index[c][1]) * + (index[c][1] - index[c][0])) % M; + } + return result; + } +}; diff --git a/C++/unique-morse-code-words.cpp b/C++/unique-morse-code-words.cpp new file mode 100644 index 000000000..26c72b140 --- /dev/null +++ b/C++/unique-morse-code-words.cpp @@ -0,0 +1,23 @@ +// Time: O(n), n is the sume of all word lengths +// Space: O(n) + +class Solution { +public: + int uniqueMorseRepresentations(vector& words) { + static const vector MORSE = + { ".-", "-...", "-.-.", "-..", ".", "..-.", "--.", + "....", "..", ".---", "-.-", ".-..", "--", "-.", + "---", ".--.", "--.-", ".-.", "...", "-", "..-", + "...-", ".--", "-..-", "-.--", "--.."}; + + unordered_set lookup; + for (const auto& word : words) { + string code; + for (const auto& c : word) { + code += MORSE[c - 'a']; + } + lookup.emplace(move(code)); + } + return lookup.size(); + } +}; diff --git a/C++/unique-substrings-in-wraparound-string.cpp b/C++/unique-substrings-in-wraparound-string.cpp new file mode 100644 index 000000000..dd4f847b7 --- /dev/null +++ b/C++/unique-substrings-in-wraparound-string.cpp @@ -0,0 +1,21 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + int findSubstringInWraproundString(string p) { + vector letters(26, 0); + int result = 0, len = 0; + for (int i = 0; i < p.length(); ++i) { + int curr = p[i] - 'a'; + if (i > 0 && p[i - 1] != (curr + 26 - 1) % 26 + 'a') { + len = 0; + } + if (++len > letters[curr]) { + result += len - letters[curr]; + letters[curr] = len; + } + } + return result; + } +}; diff --git a/C++/unique-word-abbreviation.cpp b/C++/unique-word-abbreviation.cpp new file mode 100644 index 000000000..e07743b9f --- /dev/null +++ b/C++/unique-word-abbreviation.cpp @@ -0,0 +1,35 @@ +// Time: ctor: O(n), n is number of words in the dictionary. +// lookup: O(1) +// Space: O(k), k is number of unique words. + +class ValidWordAbbr { +public: + ValidWordAbbr(vector &dictionary) { + for (string& word : dictionary) { + const string abbr = abbreviation(word); + lookup_[abbr].emplace(word); + } + } + + bool isUnique(string word) { + const string abbr = abbreviation(word); + return lookup_[abbr].empty() || + (lookup_[abbr].count(word) == lookup_[abbr].size()); + } + +private: + unordered_map> lookup_; + + string abbreviation(const string& word) { + if (word.length() <= 2) { + return word; + } + return word.front() + to_string(word.length()) + word.back(); + } +}; + + +// Your ValidWordAbbr object will be instantiated and called as such: +// ValidWordAbbr vwa(dictionary); +// vwa.isUnique("hello"); +// vwa.isUnique("anotherWord"); diff --git a/C++/utf-8-validation.cpp b/C++/utf-8-validation.cpp new file mode 100644 index 000000000..319f94569 --- /dev/null +++ b/C++/utf-8-validation.cpp @@ -0,0 +1,28 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + bool validUtf8(vector& data) { + int count = 0; + for (const auto& c : data) { + if (count == 0) { + if ((c >> 5) == 0b110) { + count = 1; + } else if ((c >> 4) == 0b1110) { + count = 2; + } else if ((c >> 3) == 0b11110) { + count = 3; + } else if ((c >> 7)) { + return false; + } + } else { + if ((c >> 6) != 0b10) { + return false; + } + --count; + } + } + return count == 0; + } +}; diff --git a/C++/valid-anagram.cpp b/C++/valid-anagram.cpp new file mode 100644 index 000000000..290375884 --- /dev/null +++ b/C++/valid-anagram.cpp @@ -0,0 +1,42 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + bool isAnagram(string s, string t) { + if (s.length() != t.length()) { + return false; + } + + unordered_map count; + + for (const auto& c: s) { + ++count[tolower(c)]; + } + + for (const auto& c: t) { + --count[tolower(c)]; + if (count[tolower(c)] < 0) { + return false; + } + } + + return true; + } +}; + +// Time: O(nlogn) +// Space: O(n) +class Solution2 { +public: + bool isAnagram(string s, string t) { + if (s.length() != t.length()) { + return false; + } + + sort(s.begin(), s.end()); + sort(t.begin(), t.end()); + + return s == t; + } +}; diff --git a/C++/valid-number.cpp b/C++/valid-number.cpp new file mode 100644 index 000000000..7e18513ac --- /dev/null +++ b/C++/valid-number.cpp @@ -0,0 +1,64 @@ +// Time: O(n) +// Space: O(1) + +// automata: http://images.cnitblog.com/i/627993/201405/012016243309923.png +class Solution { +public: + bool isNumber(string s) { + enum InputType { + INVALID, // 0 + SPACE, // 1 + SIGN, // 2 + DIGIT, // 3 + DOT, // 4 + EXPONENT, // 5 + NUM_INPUTS // 6 + }; + int transitionTable[][NUM_INPUTS] = { + -1, 0, 3, 1, 2, -1, // next states for state 0 + -1, 8, -1, 1, 4, 5, // next states for state 1 + -1, -1, -1, 4, -1, -1, // next states for state 2 + -1, -1, -1, 1, 2, -1, // next states for state 3 + -1, 8, -1, 4, -1, 5, // next states for state 4 + -1, -1, 6, 7, -1, -1, // next states for state 5 + -1, -1, -1, 7, -1, -1, // next states for state 6 + -1, 8, -1, 7, -1, -1, // next states for state 7 + -1, 8, -1, -1, -1, -1, // next states for state 8 + }; + + int state = 0; + for (const auto& c: s) { + InputType inputType = INVALID; + if (isspace(c)) { + inputType = SPACE; + } else if (c == '+' || c == '-') { + inputType = SIGN; + } else if (isdigit(c)) { + inputType = DIGIT; + } else if (c == '.') { + inputType = DOT; + } else if (c == 'e' || c == 'E') { + inputType = EXPONENT; + } + // Get next state from current state and input symbol + state = transitionTable[state][inputType]; + + // Invalid input + if (state == -1) { + return false; + } + } + // If the current state belongs to one of the accepting (final) states, + // then the number is valid + return state == 1 || state == 4 || state == 7 || state == 8; + } +}; + +#include +class Solution_TLE { +public: + bool isNumber(string s) { + regex e("^\\s*[\\+-]?((\\d+(\\.\\d*)?)|\\.\\d+)([eE][\\+-]?\\d+)?\\s*$"); + return regex_match(s, e); + } +}; diff --git a/C++/valid-palindrome-ii.cpp b/C++/valid-palindrome-ii.cpp new file mode 100644 index 000000000..1cd239064 --- /dev/null +++ b/C++/valid-palindrome-ii.cpp @@ -0,0 +1,27 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + bool validPalindrome(string s) { + int left = 0, right = s.length() - 1; + while (left < right) { + if (s[left] != s[right]) { + return validPalindrome(s, left, right - 1) || validPalindrome(s, left + 1, right); + } + ++left, --right; + } + return true; + } + +private: + bool validPalindrome(const string& s, int left, int right) { + while (left < right) { + if (s[left] != s[right]) { + return false; + } + ++left, --right; + } + return true; + } +}; diff --git a/C++/valid-palindrome.cpp b/C++/valid-palindrome.cpp new file mode 100644 index 000000000..3e81b0275 --- /dev/null +++ b/C++/valid-palindrome.cpp @@ -0,0 +1,44 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + bool isPalindrome(string s) { + int i = 0, j = s.length() - 1; + while (i < j) { + if (!isalnum(s[i])) { + ++i; + } else if (!isalnum(s[j])) { + --j; + } else if (tolower(s[i]) != tolower(s[j])) { + return false; + } else { + ++i, --j; + } + } + return true; + } +}; + +// Time: O(n) +// Space: O(1) +// Iterator solution. +class Solution2 { +public: + bool isPalindrome(string s) { + auto left = s.begin(); + auto right = prev(s.end()); + while (left < right) { + if (!isalnum(*left)) { + ++left; + } else if (!isalnum(*right)) { + --right; + } else if (tolower(*left) != tolower(*right)) { + return false; + } else { + ++left, --right; + } + } + return true; + } +}; diff --git a/C++/valid-parentheses.cpp b/C++/valid-parentheses.cpp new file mode 100644 index 000000000..aeb15cac0 --- /dev/null +++ b/C++/valid-parentheses.cpp @@ -0,0 +1,25 @@ +// Time: O(n) +// Space: O(n) + +class Solution { +public: + bool isValid(string s) { + const unordered_map symbol_pair = {{')', '('}, + {']', '['}, + {'}', '{'}}; + stack parentheses; + for (const auto& c: s) { + const auto& it = symbol_pair.find(c); + if (it != symbol_pair.cend()) { + if (parentheses.empty() || + parentheses.top() != it->second) { + return false; + } + parentheses.pop(); + } else { + parentheses.emplace(c); + } + } + return parentheses.empty(); + } +}; diff --git a/C++/valid-parenthesis-string.cpp b/C++/valid-parenthesis-string.cpp new file mode 100644 index 000000000..af21619c9 --- /dev/null +++ b/C++/valid-parenthesis-string.cpp @@ -0,0 +1,16 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + bool checkValidString(string s) { + int lower = 0, upper = 0; // keep lower bound and upper bound of '(' counts + for (const auto& c : s) { + lower += (c == '(') ? 1 : -1; + upper -= (c == ')') ? 1 : -1; + if (upper < 0) break; + lower = max(lower, 0); + } + return lower == 0; // range of '(' count is valid + } +}; diff --git a/C++/valid-perfect-square.cpp b/C++/valid-perfect-square.cpp new file mode 100644 index 000000000..91c301ee5 --- /dev/null +++ b/C++/valid-perfect-square.cpp @@ -0,0 +1,18 @@ +// Time: O(logn) +// Space: O(1) + +class Solution { +public: + bool isPerfectSquare(int num) { + int left = 1, right = num; + while (left <= right) { + const int mid = left + (right - left) / 2; + if (mid >= num / mid) { + right = mid - 1; + } else { + left = mid + 1; + } + } + return left == num / left && num % left == 0; + } +}; diff --git a/C++/valid-square.cpp b/C++/valid-square.cpp new file mode 100644 index 000000000..54d7d3d83 --- /dev/null +++ b/C++/valid-square.cpp @@ -0,0 +1,18 @@ +// Time: O(1) +// Space: O(1) + +class Solution { +public: + bool validSquare(vector& p1, vector& p2, vector& p3, vector& p4) { + unordered_set lookup({ dist(p1, p2), dist(p1, p3), + dist(p1, p4), dist(p2, p3), + dist(p2, p4), dist(p3, p4) }); + return !lookup.count(0) && lookup.size() == 2; + } + +private: + int dist(vector& p1, vector& p2) { + return (p1[0] - p2[0]) * (p1[0] - p2[0]) + + (p1[1] - p2[1]) * (p1[1] - p2[1]); + } +}; diff --git a/C++/valid-sudoku.cpp b/C++/valid-sudoku.cpp new file mode 100644 index 000000000..07a4c043e --- /dev/null +++ b/C++/valid-sudoku.cpp @@ -0,0 +1,107 @@ +// Time: O(9^2) +// Space: O(9) + +// Better performance solution. +class Solution { +public: + bool isValidSudoku(const vector>& board) { + // Check row constraints. + for (int i = 0; i < 9; ++i) { + if (anyDuplicate(board, i, i + 1, 0, 9)) { + return false; + } + } + + // Check column constraints. + for (int j = 0; j < board.size(); ++j) { + if (anyDuplicate(board, 0, 9, j, j + 1)) { + return false; + } + } + + // Check region constraints. + for (int i = 0; i < 9; i += 3) { + for (int j = 0; j < 9; j += 3) { + if (anyDuplicate(board, i, i + 3, j, j + 3)) { + return false; + } + } + } + return true; + } + +private: + // Return true if subarray board[start_row : end_row - 1][start_col : end_col - 1] + // contains any duplicates in [1 : num_elements]; otherwise return false. + bool anyDuplicate(const vector>& board, int start_row, int end_row, + int start_col, int end_col) { + bitset<9> is_present; + for (int i = start_row; i < end_row; ++i) { + for (int j = start_col; j < end_col; ++j) { + if (board[i][j] != '.') { + if (is_present[board[i][j] - '1']) { + return true; + } + is_present.flip(board[i][j] - '1'); + } + } + } + return false; + } +}; + + +// Time: O(9^2) +// Space: O(9) +// More generic solution. +class Solution2 { +public: + bool isValidSudoku(const vector>& board) { + // Check row constraints. + for (int i = 0; i < board.size(); ++i) { + if (anyDuplicate(board, i, i + 1, 0, board.size(), board.size())) { + return false; + } + } + + // Check column constraints. + for (int j = 0; j < board.size(); ++j) { + if (anyDuplicate(board, 0, board.size(), j, j + 1, board.size())) { + return false; + } + } + + // Check region constraints. + int region_size = sqrt(board.size()); + for (int i = 0; i < board.size(); i += region_size) { + for (int j = 0; j < board.size(); j += region_size) { + if (anyDuplicate(board, + i, i + region_size, + j, j + region_size, + board.size())) { + return false; + } + } + } + return true; + } + +private: + // Return true if subarray board[start_row : end_row - 1][start_col : end_col - 1] + // contains any duplicates in [1 : num_elements]; otherwise return false. + bool anyDuplicate(const vector>& board, int start_row, int end_row, + int start_col, int end_col, int num_elements) { + vector is_present(num_elements + 1, false); + for (int i = start_row; i < end_row; ++i) { + for (int j = start_col; j < end_col; ++j) { + if (board[i][j] != '.') { + if (is_present[board[i][j] - '0']) { + return true; + } + is_present[board[i][j] - '0'] = true; + } + } + } + return false; + } +}; diff --git a/C++/valid-tic-tac-toe-state.cpp b/C++/valid-tic-tac-toe-state.cpp new file mode 100644 index 000000000..cd61f04ad --- /dev/null +++ b/C++/valid-tic-tac-toe-state.cpp @@ -0,0 +1,54 @@ +// Time: O(1) +// Space: O(1) + +class Solution { +public: + bool validTicTacToe(vector& board) { + const auto FIRST = 'X', SECOND = 'O'; + const auto x_count = + accumulate(board.cbegin(), board.cend(), 0, + [&FIRST](int accu, const string& s) { + return accu + count(s.cbegin(), s.cend(), FIRST); + }); + const auto o_count = + accumulate(board.cbegin(), board.cend(), 0, + [&SECOND](int accu, const string& s) { + return accu + count(s.cbegin(), s.cend(), SECOND); + }); + if (o_count != x_count - 1 && o_count != x_count) { + return false; + } + if (isWin(board, FIRST) && o_count != x_count - 1) { + return false; + } + if (isWin(board, SECOND) && o_count != x_count) { + return false; + } + return true; + } + +private: + bool isWin(const vector& board, char player) { + for (int i = 0; i < board.size(); ++i) { + if (all_of(board[i].cbegin(), board[i].cend(), + [&player](const char& c) { + return c == player; + })) { + return true; + } + if (all_of(board.cbegin(), board.cend(), + [&i, &player](const string& row) { + return row[i] == player; + })) { + return true; + } + } + + return (player == board[1][1] && + board[1][1] == board[0][0] && + board[0][0] == board[2][2]) || + (player == board[1][1] && + board[1][1] == board[0][2] && + board[0][2] == board[2][0]); + } +}; diff --git a/C++/valid-triangle-number.cpp b/C++/valid-triangle-number.cpp new file mode 100644 index 000000000..fd270550f --- /dev/null +++ b/C++/valid-triangle-number.cpp @@ -0,0 +1,24 @@ +// Time: O(n^2) +// Space: O(1) + +class Solution { +public: + int triangleNumber(vector& nums) { + int result = 0; + sort(nums.begin(), nums.end()); + for (int i = 0; i + 2 < nums.size(); ++i) { + if (nums[i] == 0) { + continue; + } + auto k = i + 2; + for (int j = i + 1; j + 1 < nums.size(); ++j) { + while (k < nums.size() && nums[i] + nums[j] > nums[k]) { + ++k; + } + result += k - j - 1; + } + } + return result; + } +}; + diff --git a/C++/valid-word-abbreviation.cpp b/C++/valid-word-abbreviation.cpp new file mode 100644 index 000000000..a60bcf47d --- /dev/null +++ b/C++/valid-word-abbreviation.cpp @@ -0,0 +1,30 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + bool validWordAbbreviation(string word, string abbr) { + int i = 0, digit = 0; + for (const auto& c : abbr) { + if (isdigit(c)) { + if (digit == 0 && c == '0') { + return false; + } + digit *= 10; + digit += c - '0'; + } else { + if (digit) { + i += digit; + digit = 0; + } + if (i >= word.length() || word[i++] != c) { + return false; + } + } + } + if (digit) { + i += digit; + } + return i == word.length(); + } +}; diff --git a/C++/valid-word-square.cpp b/C++/valid-word-square.cpp new file mode 100644 index 000000000..300d53318 --- /dev/null +++ b/C++/valid-word-square.cpp @@ -0,0 +1,17 @@ +// Time: O(m * n) +// Space: O(1) + +class Solution { +public: + bool validWordSquare(vector& words) { + for (int i = 0; i < words.size(); ++i) { + for (int j = 0; j < words[i].size(); ++j) { + if (j >= words.size() || i >= words[j].size() || + words[j][i] != words[i][j]) { + return false; + } + } + } + return true; + } +}; diff --git a/C++/validate-binary-search-tree.cpp b/C++/validate-binary-search-tree.cpp new file mode 100644 index 000000000..4ac6bab20 --- /dev/null +++ b/C++/validate-binary-search-tree.cpp @@ -0,0 +1,78 @@ +// Time: O(n) +// Space: O(1) + +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode(int x) : val(x), left(NULL), right(NULL) {} + * }; + */ + +// Morris Traversal +class Solution { +public: + bool isValidBST(TreeNode* root) { + TreeNode *prev = nullptr; + TreeNode *curr = root; + while (curr) { + if (!curr->left) { + if (prev && prev->val >= curr->val) { + return false; + } + prev = curr; + curr = curr->right; + } else { + TreeNode *node = curr->left; + while (node->right && node->right != curr) { + node = node->right; + } + if (!node->right) { + node->right = curr; + curr = curr->left; + } else { + if (prev && prev->val >= curr->val) { + return false; + } + prev = curr; + node->right = nullptr; + curr = curr->right; + } + } + } + + return true; + } +}; + +// Time: O(n) +// Space: O(h) +class Solution2 { +public: + bool isValidBST(TreeNode* root) { + if (!root) { + return true; + } + + if (!isValidBST(root->left)) { + return false; + } + + if (last && last != root && last->val >= root->val) { + return false; + } + + last = root; + + if (!isValidBST(root->right)) { + return false; + } + + return true; + } + +private: + TreeNode *last = nullptr; +}; diff --git a/C++/validate-ip-address.cpp b/C++/validate-ip-address.cpp new file mode 100644 index 000000000..936451682 --- /dev/null +++ b/C++/validate-ip-address.cpp @@ -0,0 +1,62 @@ +// Time: O(1) +// Space: O(1) + +class Solution { +public: + string validIPAddress(string IP) { + stringstream ss(IP); + string block; + if (IP.substr(0, 4).find('.') != string::npos) { + for (int i = 0; i < 4; ++i) { + if (!getline(ss, block, '.') || !isValidIPv4Block(block)) { + return "Neither"; + } + } + if (ss.eof()) { + return "IPv4"; + } + } else if (IP.substr(0, 5).find(':') != string::npos) { + for (int i = 0; i < 8; ++i) { + if (!getline(ss, block, ':') || !isValidIPv6Block(block)) { + return "Neither"; + } + } + if (ss.eof()) { + return "IPv6"; + } + } + + return "Neither"; + } + +private: + bool isValidIPv4Block(const string& block) { + int num = 0; + if (block.size() > 0 && block.size() <= 3) { + for (int i = 0; i < block.size(); ++i) { + char c = block[i]; + if (!isalnum(c) || (i == 0 && c == '0' && block.size() > 1)) { + return false; + } else { + num *= 10; + num += c - '0'; + } + } + return num <= 255; + } + return false; + } + + bool isValidIPv6Block(const string& block) { + if (block.size() > 0 && block.size() <= 4) { + for (int i = 0; i < block.size(); ++i) { + char c = block[i]; + if (!isxdigit(c)) { + return false; + } + } + return true; + } + return false; + } +}; diff --git a/C++/verify-preorder-sequence-in-binary-search-tree.cpp b/C++/verify-preorder-sequence-in-binary-search-tree.cpp new file mode 100644 index 000000000..9e66bee98 --- /dev/null +++ b/C++/verify-preorder-sequence-in-binary-search-tree.cpp @@ -0,0 +1,43 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + bool verifyPreorder(vector& preorder) { + int low = INT_MIN, i = -1; + for (auto& p : preorder) { + if (p < low) { + return false; + } + while (i >= 0 && p > preorder[i]) { + low = preorder[i--]; + } + preorder[++i] = p; + } + return true; + } +}; + +// Time: O(n) +// Space: O(h) +class Solution2 { +public: + bool verifyPreorder(vector& preorder) { + int low = INT_MIN; + stack path; + for (auto& p : preorder) { + if (p < low) { + return false; + } + while (!path.empty() && p > path.top()) { + // Traverse to its right subtree now. + // Use the popped values as a lower bound because + // we shouldn't come across a smaller number anymore. + low = path.top(); + path.pop(); + } + path.emplace(p); + } + return true; + } +}; diff --git a/C++/verify-preorder-serialization-of-a-binary-tree.cpp b/C++/verify-preorder-serialization-of-a-binary-tree.cpp new file mode 100644 index 000000000..42c34086a --- /dev/null +++ b/C++/verify-preorder-serialization-of-a-binary-tree.cpp @@ -0,0 +1,51 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + bool isValidSerialization(string preorder) { + if (preorder.empty()) { + return false; + } + Tokenizer tokens(preorder); + int depth = 0; + int i = 0; + for (; i < tokens.size() && depth >= 0; ++i) { + if (tokens.get_next() == "#") { + --depth; + } else { + ++depth; + } + } + return i == tokens.size() && depth < 0; + } + + class Tokenizer { + public: + Tokenizer(const string& str) : str_(str), i_(0), cnt_(0) { + size_ = count(str_.cbegin(), str_.cend(), ',') + 1; + } + + string get_next() { + string next; + if (cnt_ < size_) { + size_t j = str_.find(",", i_); + next = str_.substr(i_, j - i_); + i_ = j + 1; + ++cnt_; + } + return next; + } + + size_t size() { + return size_; + } + + private: + const string& str_; + size_t size_; + size_t cnt_; + size_t i_; + }; + +}; diff --git a/C++/walking-robot-simulation.cpp b/C++/walking-robot-simulation.cpp new file mode 100644 index 000000000..874e240e2 --- /dev/null +++ b/C++/walking-robot-simulation.cpp @@ -0,0 +1,44 @@ +// Time: O(n + k) +// Space: O(k) + +class Solution { +private: + template + struct PairHash { + size_t operator()(const pair& p) const { + size_t seed = 0; + seed ^= std::hash{}(p.first) + 0x9e3779b9 + (seed<<6) + (seed>>2); + seed ^= std::hash{}(p.second) + 0x9e3779b9 + (seed<<6) + (seed>>2); + return seed; + } + }; + +public: + int robotSim(vector& commands, vector>& obstacles) { + static const vector> directions{{0, 1}, {1, 0}, + {0, -1}, {-1, 0}}; + unordered_set, PairHash> lookup; + for (const auto& obstacle: obstacles) { + lookup.emplace(obstacle[0], obstacle[1]); + } + int result = 0; + int x = 0, y = 0, i = 0; + for (const auto& cmd: commands) { + if (cmd == -2) { + i = (i - 1 + 4) % 4; + } else if (cmd == -1) { + i = (i + 1) % 4; + } else { + for (int k = 0; k < cmd; ++k) { + if (!lookup.count(make_pair(x + directions[i].first, + y + directions[i].second))) { + x += directions[i].first; + y += directions[i].second; + result = max(result, x * x + y * y); + } + } + } + } + return result; + } +}; diff --git a/C++/walls-and-gates.cpp b/C++/walls-and-gates.cpp new file mode 100644 index 000000000..9e66eca21 --- /dev/null +++ b/C++/walls-and-gates.cpp @@ -0,0 +1,34 @@ +// Time: O(m * n) +// Space: O(g) + +class Solution { +public: + void wallsAndGates(vector>& rooms) { + const int INF = numeric_limits::max(); + queue> q; + for (int i = 0; i < rooms.size(); ++i) { + for (int j = 0; j < rooms[0].size(); ++j) { + if (rooms[i][j] == 0) { + q.emplace(i, j); + } + } + } + while (!q.empty()) { + int i, j; + tie(i, j) = q.front(); + q.pop(); + for (const pair& d : + vector>{{i + 1, j}, {i - 1, j}, + {i, j + 1}, {i, j - 1}}) { + int I, J; + tie(I, J) = d; + if (I >= 0 && I < rooms.size() && + J >= 0 && J < rooms[0].size() && + rooms[I][J] == INF) { + rooms[I][J] = rooms[i][j] + 1; + q.emplace(I, J); + } + } + } + } +}; diff --git a/C++/water-and-jug-problem.cpp b/C++/water-and-jug-problem.cpp new file mode 100644 index 000000000..29402f4be --- /dev/null +++ b/C++/water-and-jug-problem.cpp @@ -0,0 +1,20 @@ +// Time: O(logn), n is the max of (x, y) +// Space: O(1) + +// Bézout's identity (also called Bézout's lemma) +class Solution { +public: + bool canMeasureWater(int x, int y, int z) { + return z == 0 || (z <= x + y && z % gcd(x, y) == 0); + } + +private: + int gcd(int a, int b) { + while (b != 0) { + int tmp = b; + b = a % b; + a = tmp; + } + return a; + } +}; diff --git a/C++/wiggle-sort-ii.cpp b/C++/wiggle-sort-ii.cpp new file mode 100644 index 000000000..76d6835db --- /dev/null +++ b/C++/wiggle-sort-ii.cpp @@ -0,0 +1,104 @@ +// Time: O(n) ~ O(n^2), O(n) on average. +// Space: O(1) + +// Tri Partition (aka Dutch National Flag Problem) with virtual index solution. (44ms) +class Solution { +public: + void wiggleSort(vector& nums) { + int mid = (nums.size() - 1) / 2; + nth_element(nums.begin(), nums.begin() + mid, nums.end()); // O(n) ~ O(n^2) time + reversedTriPartitionWithVI(nums, nums[mid]); // O(n) time, O(1) space + } + + void reversedTriPartitionWithVI(vector& nums, int val) { + const int N = nums.size() / 2 * 2 + 1; + #define Nums(i) nums[(1 + 2 * (i)) % N] + for (int i = 0, j = 0, n = nums.size() - 1; j <= n;) { + if (Nums(j) > val) { + swap(Nums(i++), Nums(j++)); + } else if (Nums(j) < val) { + swap(Nums(j), Nums(n--)); + } else { + ++j; + } + } + } +}; + +// Time: O(n) ~ O(n^2) +// Space: O(n) +// Tri Partition (aka Dutch National Flag Problem) solution. (64ms) +class Solution2 { +public: + void wiggleSort(vector& nums) { + int mid = (nums.size() - 1) / 2; + nth_element(nums.begin(), nums.begin() + mid, nums.end()); // O(n) ~ O(n^2) time + triPartition(nums, nums[mid]); // O(n) time, O(1) space + + vector res(nums.size()); // O(n) space + for (int i = 0, smallEnd = mid; i < nums.size(); i += 2, --smallEnd) { + res[i] = nums[smallEnd]; + } + for (int i = 1, largeEnd = nums.size() - 1; i < nums.size(); i += 2, --largeEnd) { + res[i] = nums[largeEnd]; + } + nums = res; + } + + void triPartition(vector& nums, int val) { + for (int i = 0, j = 0, n = nums.size() - 1; j <= n;) { + if (nums[j] < val) { + swap(nums[i++], nums[j++]); + } else if (nums[j] > val) { + swap(nums[j], nums[n--]); + } else { + ++j; + } + } + } +}; + +// Time: O(nlogn) +// Space: O(1) +// Sorting and Tri Partition (aka Dutch National Flag Problem) with virtual index solution. (64ms) +class Solution3 { +public: + void wiggleSort(vector& nums) { + int mid = (nums.size() - 1) / 2; + sort(nums.begin(), nums.end()); // O(nlogn) time + reversedTriPartitionWithVI(nums, nums[mid]); // O(n) time, O(1) space + } + + void reversedTriPartitionWithVI(vector& nums, int val) { + const int N = nums.size() / 2 * 2 + 1; + #define Nums(i) nums[(1 + 2 * (i)) % N] + for (int i = 0, j = 0, n = nums.size() - 1; j <= n;) { + if (Nums(j) > val) { + swap(Nums(i++), Nums(j++)); + } else if (Nums(j) < val) { + swap(Nums(j), Nums(n--)); + } else { + ++j; + } + } + } +}; + +// Time: O(nlogn) +// Space: O(n) +// Sorting and reorder solution. (64ms) +class Solution4 { +public: + void wiggleSort(vector& nums) { + int mid = (nums.size() - 1) / 2; + sort(nums.begin(), nums.end()); // O(nlogn) time + vector res(nums.size()); // O(n) space + for (int i = 0, smallEnd = mid; i < nums.size(); i += 2, --smallEnd) { + res[i] = nums[smallEnd]; + } + for (int i = 1, largeEnd = nums.size() - 1; i < nums.size(); i += 2, --largeEnd) { + res[i] = nums[largeEnd]; + } + nums = res; + } +}; diff --git a/C++/wiggle-sort.cpp b/C++/wiggle-sort.cpp new file mode 100644 index 000000000..7f550a748 --- /dev/null +++ b/C++/wiggle-sort.cpp @@ -0,0 +1,15 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + void wiggleSort(vector& nums) { + for (int i = 1; i < nums.size(); ++i) { + if (((i % 2) && nums[i] < nums[i - 1]) || + (!(i % 2) && nums[i] > nums[i - 1])) { + // Swap unordered elements. + swap(nums[i], nums[i - 1]); + } + } + } +}; diff --git a/C++/wiggle-subsequence.cpp b/C++/wiggle-subsequence.cpp new file mode 100644 index 000000000..83830a720 --- /dev/null +++ b/C++/wiggle-subsequence.cpp @@ -0,0 +1,25 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + int wiggleMaxLength(vector& nums) { + if (nums.size() < 2) { + return nums.size(); + } + + int length = 1, up = 0; + + for (int i = 1; i < nums.size(); ++i) { + if (nums[i - 1] < nums[i] && (up == 0 || up == 1)) { + ++length; + up = -1; + } else if (nums[i - 1] > nums[i] && (up == 0 || up == -1)) { + ++length; + up = 1; + } + } + + return length; + } +}; diff --git a/C++/word-abbreviation.cpp b/C++/word-abbreviation.cpp new file mode 100644 index 000000000..ef4e94bbf --- /dev/null +++ b/C++/word-abbreviation.cpp @@ -0,0 +1,52 @@ +// Time: O(n * l) ~ O(n^2 * l^2) +// Space: O(n * l) + +class Solution { +public: + vector wordsAbbreviation(vector& dict) { + unordered_map> abbr_to_word; + unordered_map word_to_abbr; + + for (const auto& word : dict) { + const auto prefix = word.substr(0, 1); + abbr_to_word[toAbbr(prefix, word)].emplace(word); + } + + for (const auto& kvp : abbr_to_word) { + if (kvp.second.size() > 1) { + for (const auto& word : kvp.second) { + for (int i = 2; i < word.length(); ++i) { + const auto prefix = word.substr(0, i); + if (isUnique(prefix, kvp.second)) { + word_to_abbr[word] = toAbbr(prefix, word); + break; + } + } + } + } else { + word_to_abbr[*kvp.second.begin()] = kvp.first; + } + } + + vector result; + for (const auto& word : dict) { + result.emplace_back(word_to_abbr[word]); + } + return result; + } + +private: + bool isUnique(const string& prefix, const unordered_set& words) { + return 1 == count_if(words.begin(), words.end(), + [&prefix](const string& word) { + return !word.compare(0, prefix.length(), prefix); + }); + } + + string toAbbr(const string& prefix, const string& word) { + string abbr = prefix; + abbr += to_string(word.length() - 1 - prefix.length()); + abbr += word.back(); + return abbr.length() < word.length() ? abbr : word; + } +}; diff --git a/C++/word-break-ii.cpp b/C++/word-break-ii.cpp new file mode 100644 index 000000000..3e2b92667 --- /dev/null +++ b/C++/word-break-ii.cpp @@ -0,0 +1,55 @@ +// Time: O(n * l^2 + n * r), l is the max length of the words, +// r is the number of the results. +// Space: O(n^2) + +class Solution { +public: + vector wordBreak(string s, unordered_set& wordDict) { + const int n = s.length(); + + size_t max_len = 0; + for (const auto& str: wordDict) { + max_len = max(max_len, str.length()); + } + + vector canBreak(n + 1, false); + vector> valid(n, vector(n, false)); + canBreak[0] = true; + for (int i = 1; i <= n; ++i) { + for (int l = 1; l <= max_len && i - l >= 0; ++l) { + if (canBreak[i - l] && wordDict.count(s.substr(i - l, l))) { + valid[i - l][i - 1] = true; + canBreak[i] = true; + } + } + } + + vector result, path; + if (canBreak[n]) { + wordBreakHelper(s, valid, 0, &path, &result); + } + return result; + } + + + void wordBreakHelper(const string& s, const vector>& valid, + int start, vector *path, vector *result) { + if (start == s.length()) { + string tmp; + for (const auto& str : *path) { + tmp += str; + tmp += " "; + } + tmp.pop_back(); + result->emplace_back(move(tmp)); + return; + } + for (int i = start; i < s.length(); ++i) { + if (valid[start][i]) { + path->emplace_back(s.substr(start, i + 1 - start)); + wordBreakHelper(s, valid, i + 1, path, result); + path->pop_back(); + } + } + } +}; diff --git a/C++/word-break.cpp b/C++/word-break.cpp new file mode 100644 index 000000000..c3305a139 --- /dev/null +++ b/C++/word-break.cpp @@ -0,0 +1,27 @@ +// Time: O(n * l^2), l is the max length of the words. +// Space: O(n) + +class Solution { +public: + bool wordBreak(string s, unordered_set& wordDict) { + const int n = s.length(); + + size_t max_len = 0; + for (const auto& str: wordDict) { + max_len = max(max_len, str.length()); + } + + vector canBreak(n + 1, false); + canBreak[0] = true; + for (int i = 1; i <= n; ++i) { + for (int l = 1; l <= max_len && i - l >= 0; ++l) { + if (canBreak[i - l] && wordDict.count(s.substr(i - l, l))) { + canBreak[i] = true; + break; + } + } + } + + return canBreak[n]; + } +}; diff --git a/C++/word-pattern-ii.cpp b/C++/word-pattern-ii.cpp new file mode 100644 index 000000000..bf679e586 --- /dev/null +++ b/C++/word-pattern-ii.cpp @@ -0,0 +1,43 @@ +// Time: O(n * C(n - 1, c - 1)), n is length of str, c is unique count of pattern, +// there are H(n - c, c - 1) = C(n - 1, c - 1) possible splits of string, +// and each one costs O(n) to check if it matches the word pattern. +// Space: O(n + c) + +class Solution { +public: + bool wordPatternMatch(string pattern, string str) { + unordered_map w2p; + unordered_map p2w; + return match(pattern, str, 0, 0, &w2p, &p2w); + } + + bool match(const string &pattern, const string &str, + const int i, const int j, + unordered_map* w2p, + unordered_map* p2w) { + + bool is_match = false; + if (i == pattern.length() && j == str.length()) { + is_match = true; + } else if (i < pattern.length() && j < str.length()) { + const char p = pattern[i]; + if (p2w->count(p)) { + const auto& w = (*p2w)[p]; + if (w == str.substr(j, w.length())) { // Match pattern. + is_match = match(pattern, str, i + 1, j + w.length(), w2p, p2w); + } // Else return false. + } else { + for (int k = j; k < str.length() && !is_match; ++k) { + const string w = str.substr(j, k - j + 1); + if (!w2p->count(w)) { + // Build mapping. Space: O(n + c) + (*w2p)[w] = p, (*p2w)[p] = w; + is_match = match(pattern, str, i + 1, k + 1, w2p, p2w); + w2p->erase(w), p2w->erase(p); + } // Else try longer word. + } + } + } + return is_match; + } +}; diff --git a/C++/word-pattern.cpp b/C++/word-pattern.cpp new file mode 100644 index 000000000..b4a88d415 --- /dev/null +++ b/C++/word-pattern.cpp @@ -0,0 +1,41 @@ +// Time: O(n) +// Space: O(c), c is unique count of pattern + +class Solution { +public: + bool wordPattern(string pattern, string str) { + // Count the words. + int cnt = str.empty() ? 0 : 1; + for (const auto& c : str) { + if (c == ' ') { + ++cnt; + } + } + if (pattern.size() != cnt) { + return false; + } + + unordered_map w2p; + unordered_map p2w; + int i = 0, j = 0; + for (const auto& p : pattern) { + // Get a word at a time without saving all the words. + j = str.find(" ", i); + if (j == string::npos) { + j = str.length(); + } + const string w = str.substr(i, j - i); + + if (!w2p.count(w) && !p2w.count(p)) { + // Build mapping. Space: O(c) + w2p[w] = p; + p2w[p] = w; + } else if (!w2p.count(w) || w2p[w] != p) { + // Contradict mapping. + return false; + } + i = j + 1; + } + return true; + } +}; diff --git a/C++/word-search-ii.cpp b/C++/word-search-ii.cpp new file mode 100644 index 000000000..731c4e73a --- /dev/null +++ b/C++/word-search-ii.cpp @@ -0,0 +1,102 @@ +// Time: O(m * n * h), h is height of trie +// Space: O(26^h) + +class Solution { +private: + struct TrieNode { + bool isString = false; + unordered_map leaves; + + bool Insert(const string& s) { + auto* p = this; + for (const auto& c : s) { + if (p->leaves.find(c) == p->leaves.cend()) { + p->leaves[c] = new TrieNode; + } + p = p->leaves[c]; + } + + // s already existed in this trie. + if (p->isString) { + return false; + } else { + p->isString = true; + return true; + } + } + + ~TrieNode() { + for (auto& kv : leaves) { + if (kv.second) { + delete kv.second; + } + } + } + }; + +public: + /** + * @param board: A list of lists of character + * @param words: A list of string + * @return: A list of string + */ + vector findWords(vector>& board, vector& words) { + unordered_set ret; + vector> visited(board.size(), vector(board[0].size(), false)); + string cur; + TrieNode trie; + for (const auto& word : words) { + trie.Insert(word); + } + + for (int i = 0; i < board.size(); ++i) { + for (int j = 0; j < board[0].size(); ++j) { + findWordsDFS(board, visited, &trie, i, j, cur, ret); + } + } + + return vector(ret.begin(), ret.end()); + } + + void findWordsDFS(vector> &grid, + vector> &visited, + TrieNode *trie, + int i, + int j, + string cur, + unordered_set &ret) { + // Invalid state. + if (!trie || i < 0 || i >= grid.size() || j < 0 || j >= grid[0].size()) { + return; + } + + // Not in trie or visited. + if (!trie->leaves[grid[i][j] ] || visited[i][j]) { + return; + } + + // Get next trie nodes. + TrieNode *nextNode = trie->leaves[grid[i][j]]; + + // Update current string. + cur.push_back(grid[i][j]); + + // Find the string, add to the answers. + if (nextNode->isString) { + ret.insert(cur); + } + + // Marked as visited. + visited[i][j] = true; + + // Try each direction. + const vector> directions{{0, -1}, {0, 1}, + {-1, 0}, {1, 0}}; + for (const auto& d : directions) { + findWordsDFS(grid, visited, nextNode, + i + d.first, j + d.second, cur, ret); + } + + visited[i][j] = false; + } +}; diff --git a/C++/word-squares.cpp b/C++/word-squares.cpp new file mode 100644 index 000000000..11d66497c --- /dev/null +++ b/C++/word-squares.cpp @@ -0,0 +1,62 @@ +// Time: O(n^2 * n!) +// Space: O(n^2) + +class Solution { +private: + struct TrieNode { + vector indices; + vector children; + TrieNode() : children(26, nullptr) {} + }; + + TrieNode *buildTrie(const vector& words) { + TrieNode *root = new TrieNode(); + for (int j = 0; j < words.size(); ++j) { + TrieNode* t = root; + for (int i = 0; i < words[j].size(); ++i) { + if (!t->children[words[j][i] - 'a']) { + t->children[words[j][i] - 'a'] = new TrieNode(); + } + t = t->children[words[j][i] - 'a']; + t->indices.push_back(j); + } + } + return root; + } + +public: + vector> wordSquares(vector& words) { + vector> result; + + TrieNode *trie = buildTrie(words); + vector curr; + for (const auto& s : words) { + curr.emplace_back(s); + wordSquaresHelper(words, trie, &curr, &result); + curr.pop_back(); + } + + return result; + } + +private: + void wordSquaresHelper(const vector& words, TrieNode *trie, vector *curr, + vector> *result) { + if (curr->size() >= words[0].length()) { + return result->emplace_back(*curr); + } + + TrieNode *node = trie; + for (int i = 0; i < curr->size(); ++i) { + if (!(node = node->children[(*curr)[i][curr->size()] - 'a'])) { + return; + } + } + + for (const auto& i : node->indices) { + curr->emplace_back(words[i]); + wordSquaresHelper(words, trie, curr, result); + curr->pop_back(); + } + } +}; diff --git a/C++/wordBreak.cpp b/C++/wordBreak.cpp deleted file mode 100644 index 4b4d6d9be..000000000 --- a/C++/wordBreak.cpp +++ /dev/null @@ -1,19 +0,0 @@ -// Time Complexity: O(n^2) -// Space Complexity: O(n) - -class Solution { - public: - bool wordBreak(string s, unordered_set &dict) { - vector f(s.size() + 1, false); - f[0] = true; // null string - for(int i = 1; i <= s.size(); ++i) { - for(int j = i - 1; j >= 0; --j) { - if(f[j] && dict.find(s.substr(j, i - j)) != dict.end()) { - f[i] = true; - break; - } - } - } - return f[s.size()]; - } -}; diff --git a/C++/zigzag-conversion.cpp b/C++/zigzag-conversion.cpp new file mode 100644 index 000000000..3cb96df9b --- /dev/null +++ b/C++/zigzag-conversion.cpp @@ -0,0 +1,23 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + string convert(string s, int numRows) { + if (numRows == 1) { + return s; + } + const int step = 2 * numRows - 2; + string zigzag; + for (int i = 0; i < numRows; ++i) { + for (int j = i; j < s.length(); j += step) { + zigzag.push_back(s[j]); + if (0 < i && i < numRows - 1 && + j + step - 2 * i < s.length()) { + zigzag.push_back(s[j + step - 2 * i]); + } + } + } + return zigzag; + } +}; diff --git a/C++/zigzag-iterator.cpp b/C++/zigzag-iterator.cpp new file mode 100644 index 000000000..c2a470699 --- /dev/null +++ b/C++/zigzag-iterator.cpp @@ -0,0 +1,37 @@ +// Time: O(n) +// Space: O(k) + +class ZigzagIterator { +public: + ZigzagIterator(vector& v1, vector& v2) { + if (!v1.empty()) { + q.emplace(v1.size(), v1.cbegin()); + } + if (!v2.empty()) { + q.emplace(v2.size(), v2.cbegin()); + } + } + + int next() { + const auto len = q.front().first; + const auto it = q.front().second; + q.pop(); + if (len > 1) { + q.emplace(len - 1, it + 1); + } + return *it; + } + + bool hasNext() { + return !q.empty(); + } + +private: + queue::const_iterator>> q; +}; + +/** + * Your ZigzagIterator object will be instantiated and called as such: + * ZigzagIterator i(v1, v2); + * while (i.hasNext()) cout << i.next(); + */ diff --git a/C++/zuma-game.cpp b/C++/zuma-game.cpp new file mode 100644 index 000000000..c038098ba --- /dev/null +++ b/C++/zuma-game.cpp @@ -0,0 +1,74 @@ +// Time: O(b * b! * h!) +// Space: O(b * b! * h!) + +class Solution { +public: + int findMinStep(string board, string hand) { + unordered_map> lookup; + sort(hand.begin(), hand.end()); + int result = findMinStepHelper(board, hand, &lookup); + return result > hand.size() ? -1 : result; + } + +private: + int findMinStepHelper(const string& board, const string& hand, + unordered_map> *lookup) { + if (board.empty()) { + return 0; + } + if (hand.empty()) { + return MAX_STEP; + } + if ((*lookup)[board][hand]) { + return (*lookup)[board][hand]; + } + + int result = MAX_STEP; + for (int i = 0; i < hand.size(); ++i) { + int j = 0; + while (j < board.size()) { + int k = board.find(hand[i], j); + if (k == string::npos) { + break; + } + if (k < board.size() - 1 && board[k] == board[k + 1]) { + string next_board = shrink(board.substr(0, k) + board.substr(k + 2)); + string next_hand = hand.substr(0, i) + hand.substr(i + 1); + result = min(result, findMinStepHelper(next_board, next_hand, lookup) + 1); + ++k; + } else if (i > 0 && hand[i] == hand[i - 1]) { + string next_board = shrink(board.substr(0, k) + board.substr(k + 1)); + string next_hand = hand.substr(0, i - 1) + hand.substr(i + 1); + result = min(result, findMinStepHelper(next_board, next_hand, lookup) + 2); + } + j = k + 1; + } + } + + return (*lookup)[board][hand] = result; + } + + string shrink(const string& s) { // Time: O(n), Space: O(n) + vector> stack; + for (int i = 0, start = 0; i <= s.size(); ++i) { + if (i == s.size() || s[i] != s[start]) { + if (!stack.empty() && stack.back().first == s[start]) { + stack.back().second += i - start; + if (stack.back().second >= 3) { + stack.pop_back(); + } + } else if (!s.empty() && i - start < 3) { + stack.emplace_back(s[start], i - start); + } + start = i; + } + } + string result; + for (const auto& p : stack) { + result += string(p.second, p.first); + } + return result; + } + + static const int MAX_STEP = 6; +}; diff --git a/Golang/add-two-numbers.go b/Golang/add-two-numbers.go new file mode 100644 index 000000000..a3f033323 --- /dev/null +++ b/Golang/add-two-numbers.go @@ -0,0 +1,35 @@ +// Time: O(n) +// Space: O(1) + +/** + * Definition for singly-linked list. + * type ListNode struct { + * Val int + * Next *ListNode + * } + */ +func addTwoNumbers(l1 *ListNode, l2 *ListNode) *ListNode { + dummy := &ListNode{} + current, carry := dummy, 0 + + for l1 != nil || l2 != nil { + val := carry + if l1 != nil { + val += l1.Val + l1 = l1.Next + } + if l2 != nil { + val += l2.Val + l2 = l2.Next + } + carry, val = val / 10, val % 10 + current.Next = &ListNode{Val: val} + current = current.Next + } + + if carry == 1 { + current.Next = &ListNode{Val: 1} + } + + return dummy.Next +} diff --git a/Golang/longest-substring-without-repeating-characters.go b/Golang/longest-substring-without-repeating-characters.go new file mode 100644 index 000000000..11dd57570 --- /dev/null +++ b/Golang/longest-substring-without-repeating-characters.go @@ -0,0 +1,34 @@ +package leetcode + +// Given a string, find the length of the longest substring without repeating characters. +// +// Examples: +// Given "abcabcbb", the answer is "abc", which the length is 3. +// Given "bbbbb", the answer is "b", with the length of 1. +// Given "pwwkew", the answer is "wke", with the length of 3. +// Note that the answer must be a substring, "pwke" is a subsequence and not a substring. +// +func lengthOfLongestSubstring(s string) int { + hashmap := map[byte]int{} + max := 0 + for i := range s { + _, ok := hashmap[s[i]] + if !ok { + hashmap[s[i]] = i + if len(hashmap) > max { + max = len(hashmap) + } + } else { + // remove repeated + oldI := hashmap[s[i]] + hashmap[s[i]] = i + + for key, value := range hashmap { + if value < oldI { + delete(hashmap, key) + } + } + } + } + return max +} diff --git a/Golang/two-sum.go b/Golang/two-sum.go new file mode 100644 index 000000000..0cf7df1b2 --- /dev/null +++ b/Golang/two-sum.go @@ -0,0 +1,13 @@ +// Time: O(n) +// Space: O(n) + +func twoSum(nums []int, target int) []int { + lookup := make(map[int]int) + for i, num := range nums { + if j, ok := lookup[target-num]; ok { + return []int{j, i} + } + lookup[nums[i]] = i + } + return nil +} diff --git a/Golang/two-sum_test.go b/Golang/two-sum_test.go new file mode 100644 index 000000000..e6e6c414b --- /dev/null +++ b/Golang/two-sum_test.go @@ -0,0 +1,13 @@ +package leetcode + +import "testing" + +func Test_two_sum(t *testing.T) { + + if result := TwoSum([]int{1, 3, 5}, 4); result[0] != 0 && result[1] != 1 { + t.Errorf("Error") + } + if result := TwoSum([]int{3, 1, 5}, 6); result[0] != 1 && result[1] != 2 { + t.Errorf("Error") + } +} diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 000000000..2288c82f8 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016 https://github.com/kamyu104/LeetCode + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/MySQL/average-salary-departments-vs-company.sql b/MySQL/average-salary-departments-vs-company.sql new file mode 100644 index 000000000..7106e85d3 --- /dev/null +++ b/MySQL/average-salary-departments-vs-company.sql @@ -0,0 +1,23 @@ +# Time: O(nlogn) +# Space: O(n) + +SELECT department_salary.pay_month, department_id, +CASE + WHEN department_avg < company_avg THEN 'lower' + WHEN department_avg > company_avg THEN 'higher' + ELSE 'same' +END AS comparison +FROM +( + SELECT department_id, AVG(amount) AS department_avg, date_format(pay_date, '%Y-%m') AS pay_month + FROM salary JOIN employee ON salary.employee_id = employee.employee_id + GROUP BY department_id, pay_month +) AS department_salary +JOIN +( + SELECT AVG(amount) AS company_avg, date_format(pay_date, '%Y-%m') AS pay_month + FROM salary + GROUP BY date_format(pay_date, '%Y-%m') +) AS company_salary +ON department_salary.pay_month = company_salary.pay_month +; diff --git a/MySQL/big-countries.sql b/MySQL/big-countries.sql new file mode 100644 index 000000000..5bc687b3b --- /dev/null +++ b/MySQL/big-countries.sql @@ -0,0 +1,27 @@ +# Time: O(n) +# Space: O(1) + +# There is a table World +# +-----------------+------------+------------+--------------+---------------+ +# | name | continent | area | population | gdp | +# +-----------------+------------+------------+--------------+---------------+ +# | Afghanistan | Asia | 652230 | 25500100 | 20343000 | +# | Albania | Europe | 28748 | 2831741 | 12960000 | +# | Algeria | Africa | 2381741 | 37100000 | 188681000 | +# | Andorra | Europe | 468 | 78115 | 3712000 | +# | Angola | Africa | 1246700 | 20609294 | 100990000 | +# +-----------------+------------+------------+--------------+---------------+ +# +# A country is big if it has an area of bigger than 3 million square km or a population of more than 25 million. +# Write a SQL solution to output big countries' name, population and area. +# For example, according to the above table, we should output: +# +--------------+-------------+--------------+ +# | name | population | area | +# +--------------+-------------+--------------+ +# | Afghanistan | 25500100 | 652230 | +# | Algeria | 37100000 | 2381741 | +# +--------------+-------------+--------------+ + +SELECT name, population, area +FROM World +WHERE area > 3000000 OR population > 25000000; diff --git a/MySQL/biggest-single-number.sql b/MySQL/biggest-single-number.sql new file mode 100644 index 000000000..2a195a016 --- /dev/null +++ b/MySQL/biggest-single-number.sql @@ -0,0 +1,19 @@ +# Time: O(n) +# Space: O(n) + +SELECT +IFNULL( + (SELECT + MAX(num) + FROM + (SELECT + num + FROM + number + GROUP BY num + HAVING COUNT(num) = 1 + ORDER BY NULL) AS t + ) + , NULL +) AS num +; diff --git a/MySQL/classes-more-than-5-students.sql b/MySQL/classes-more-than-5-students.sql new file mode 100644 index 000000000..b225427dc --- /dev/null +++ b/MySQL/classes-more-than-5-students.sql @@ -0,0 +1,35 @@ +# Time: O(n) +# Space: O(n) + +# There is a table courses with columns: student and class +# Please list out all classes which have more than or equal to 5 students. +# For example, the table: +# +---------+------------+ +# | student | class | +# +---------+------------+ +# | A | Math | +# | B | English | +# | C | Math | +# | D | Biology | +# | E | Math | +# | F | Computer | +# | G | Math | +# | H | Math | +# | I | Math | +# +---------+------------+ +# +# Should output: +# +---------+ +# | class | +# +---------+ +# | Math | +# +---------+ +# +# Note: +# The students should not be counted duplicate in each course. + +SELECT class +FROM courses +GROUP BY class +HAVING COUNT(DISTINCT student) >= 5 +ORDER BY NULL; diff --git a/MySQL/consecutive-available-seats.sql b/MySQL/consecutive-available-seats.sql new file mode 100644 index 000000000..79025770a --- /dev/null +++ b/MySQL/consecutive-available-seats.sql @@ -0,0 +1,9 @@ +# Time: O(nlogn) +# Space: O(n) + +SELECT DISTINCT c1.seat_id +FROM cinema c1 JOIN cinema c2 + ON ((c1.seat_id = c2.seat_id - 1) OR (c1.seat_id = c2.seat_id + 1)) + AND c1.free = true AND c2.free = true +ORDER BY c1.seat_id +; diff --git a/MySQL/consecutive-numbers.sql b/MySQL/consecutive-numbers.sql index 47026f66f..7d4cc2e27 100644 --- a/MySQL/consecutive-numbers.sql +++ b/MySQL/consecutive-numbers.sql @@ -17,8 +17,9 @@ # For example, given the above Logs table, 1 is the only number that appears consecutively for at least three times. # +# Solution 1 # Write your MySQL query statement below -SELECT DISTINCT(Num) AS ConsecutiveNums +SELECT DISTINCT(Num) AS ConsecutiveNums FROM ( SELECT Num, @@ -27,3 +28,8 @@ FROM ( FROM Logs y, (SELECT @counter:=1, @prev:=NULL) vars ) sq WHERE how_many_cnt_in_a_row >= 3 + +# Solution 2 +SELECT DISTINCT l1.Num as ConsecutiveNums +FROM Logs l1, Logs l2, Logs l3 +WHERE l1.Id + 1 = l2.Id AND l2.Id + 1 = l3.Id AND l1.Num = l2.Num AND l2.Num = l3.Num diff --git "a/MySQL/count-student-number-in-departments\b.sql" "b/MySQL/count-student-number-in-departments\b.sql" new file mode 100644 index 000000000..335c6430d --- /dev/null +++ "b/MySQL/count-student-number-in-departments\b.sql" @@ -0,0 +1,12 @@ +# Time: O(s+dlogd) +# Space: O(d+s) + +SELECT + dept_name, COUNT(student_id) AS student_number +FROM + department + LEFT JOIN + student ON department.dept_id = student.dept_id +GROUP BY department.dept_name +ORDER BY student_number DESC , department.dept_name +; diff --git a/MySQL/customer-placing-the-largest-number-of-orders.sql b/MySQL/customer-placing-the-largest-number-of-orders.sql new file mode 100644 index 000000000..164017d90 --- /dev/null +++ b/MySQL/customer-placing-the-largest-number-of-orders.sql @@ -0,0 +1,19 @@ +# Time: O(n) +# Space: O(n) + +SELECT customer_number +FROM orders +GROUP BY customer_number +HAVING COUNT(order_number) = + (SELECT MAX(*) + FROM + (SELECT COUNT(*) AS cnt + FROM + orders + GROUP BY customer_number + ORDER BY NULL + ) AS cnt_tbl + ) +ORDER BY NULL +LIMIT 1 +; diff --git a/MySQL/customers-who-never-order.sql b/MySQL/customers-who-never-order.sql new file mode 100644 index 000000000..c63d41aaa --- /dev/null +++ b/MySQL/customers-who-never-order.sql @@ -0,0 +1,42 @@ +# Time: O(n^2) +# Space: O(1) +# +# Suppose that a website contains two tables, the Customers table and the Orders table. Write a SQL query to find all customers who never order anything. +# +# Table: Customers. +# +# +----+-------+ +# | Id | Name | +# +----+-------+ +# | 1 | Joe | +# | 2 | Henry | +# | 3 | Sam | +# | 4 | Max | +# +----+-------+ +# Table: Orders. +# +# +----+------------+ +# | Id | CustomerId | +# +----+------------+ +# | 1 | 3 | +# | 2 | 1 | +# +----+------------+ +# Using the above tables as example, return the following: +# +# +-----------+ +# | Customers | +# +-----------+ +# | Henry | +# | Max | +# +-----------+ +# + +# Time: O(n^2) +# Space: O(1) +# Write your MySQL query statement below +SELECT Name AS Customers FROM Customers WHERE Id NOT IN (SELECT CustomerId FROM Orders) + +# Time: O(n^2) +# Space: O(n) +# Write your MySQL query statement below +SELECT Customers.Name AS Customers FROM (Customers LEFT JOIN Orders ON Customers.Id = Orders.CustomerId) WHERE Orders.CustomerId IS NULL diff --git a/MySQL/delete-duplicate-emails.sql b/MySQL/delete-duplicate-emails.sql new file mode 100644 index 000000000..b098426ca --- /dev/null +++ b/MySQL/delete-duplicate-emails.sql @@ -0,0 +1,28 @@ +# Time: O(n^2) +# Space: O(n) +# +# Write a SQL query to delete all duplicate email entries in a table named Person, +# keeping only unique emails based on its smallest Id. +# +# +----+------------------+ +# | Id | Email | +# +----+------------------+ +# | 1 | john@example.com | +# | 2 | bob@example.com | +# | 3 | john@example.com | +# +----+------------------+ +# Id is the primary key column for this table. +# For example, after running your query, the above Person table should have the following rows: +# +# +----+------------------+ +# | Id | Email | +# +----+------------------+ +# | 1 | john@example.com | +# | 2 | bob@example.com | +# +----+------------------+ +# + +# Write your MySQL query statement below +DELETE p1 +FROM Person p1, Person p2 +WHERE p1.Email = p2.Email AND p1.Id > p2.Id diff --git a/MySQL/department-highest-salary.sql b/MySQL/department-highest-salary.sql new file mode 100644 index 000000000..69c88f734 --- /dev/null +++ b/MySQL/department-highest-salary.sql @@ -0,0 +1,42 @@ +# Time: O(n^2) +# Space: O(n) +# +# The Employee table holds all employees. Every employee has an Id, a salary, and there is also a column for the department Id. +# +# +----+-------+--------+--------------+ +# | Id | Name | Salary | DepartmentId | +# +----+-------+--------+--------------+ +# | 1 | Joe | 70000 | 1 | +# | 2 | Henry | 80000 | 2 | +# | 3 | Sam | 60000 | 2 | +# | 4 | Max | 90000 | 1 | +# +----+-------+--------+--------------+ +# The Department table holds all departments of the company. +# +# +----+----------+ +# | Id | Name | +# +----+----------+ +# | 1 | IT | +# | 2 | Sales | +# +----+----------+ +# Write a SQL query to find employees who have the highest salary in each of the departments. For the above tables, Max has the highest salary in the IT department and Henry has the highest salary in the Sales department. +# +# +------------+----------+--------+ +# | Department | Employee | Salary | +# +------------+----------+--------+ +# | IT | Max | 90000 | +# | Sales | Henry | 80000 | +# +------------+----------+--------+ +# +# Write your MySQL query statement below +SELECT d.Department AS Department, e.Name AS Employee, d.Salary AS Salary +FROM (SELECT Department.Id AS DepartmentId, Department.Name AS Department, emp.Salary AS Salary + FROM Department JOIN (SELECT DepartmentId, MAX(Salary) AS Salary FROM Employee GROUP BY DepartmentId) emp + ON Department.Id = emp.DepartmentId) d + JOIN Employee e + ON e.DepartmentId = d.DepartmentId and e.Salary = d.Salary + +# Write your MySQL query statement below +SELECT Department.Name AS Department, Employee.Name AS Employee, Employee.Salary AS Salary +FROM Department JOIN Employee ON Employee.DepartmentId = Department.Id +WHERE Employee.Salary IN (SELECT MAX(e.Salary) FROM Employee e WHERE e.DepartmentId = Employee.DepartmentId) diff --git a/MySQL/department-top-three-salaries.sql b/MySQL/department-top-three-salaries.sql new file mode 100644 index 000000000..337c3ff81 --- /dev/null +++ b/MySQL/department-top-three-salaries.sql @@ -0,0 +1,41 @@ +# Time: O(n^2) +# Space: O(n) +# +# The Employee table holds all employees. Every employee has an Id, and there is also a column for the department Id. +# +# +----+-------+--------+--------------+ +# | Id | Name | Salary | DepartmentId | +# +----+-------+--------+--------------+ +# | 1 | Joe | 70000 | 1 | +# | 2 | Henry | 80000 | 2 | +# | 3 | Sam | 60000 | 2 | +# | 4 | Max | 90000 | 1 | +# | 5 | Janet | 69000 | 1 | +# | 6 | Randy | 85000 | 1 | +# +----+-------+--------+--------------+ +# The Department table holds all departments of the company. +# +# +----+----------+ +# | Id | Name | +# +----+----------+ +# | 1 | IT | +# | 2 | Sales | +# +----+----------+ +# Write a SQL query to find employees who earn the top three salaries in each of the department. For the above tables, your SQL query should return the following rows. +# +# +------------+----------+--------+ +# | Department | Employee | Salary | +# +------------+----------+--------+ +# | IT | Max | 90000 | +# | IT | Randy | 85000 | +# | IT | Joe | 70000 | +# | Sales | Henry | 80000 | +# | Sales | Sam | 60000 | +# +------------+----------+--------+ + +# Write your MySQL query statement below +SELECT D.Name AS Department, E.Name AS Employee, E.Salary AS Salary +FROM Employee E INNER JOIN Department D ON E.DepartmentId = D.Id +WHERE (SELECT COUNT(DISTINCT(Salary)) FROM Employee + WHERE DepartmentId = E.DepartmentId AND Salary > E.Salary) < 3 +ORDER by E.DepartmentId, E.Salary DESC; diff --git a/MySQL/duplicate-emails.sql b/MySQL/duplicate-emails.sql new file mode 100644 index 000000000..836ad0490 --- /dev/null +++ b/MySQL/duplicate-emails.sql @@ -0,0 +1,24 @@ +# Time: O(n^2) +# Space: O(n) +# +# Write a SQL query to find all duplicate emails in a table named Person. +# +# +----+---------+ +# | Id | Email | +# +----+---------+ +# | 1 | a@b.com | +# | 2 | c@d.com | +# | 3 | a@b.com | +# +----+---------+ +# For example, your query should return the following for the above table: +# +# +---------+ +# | Email | +# +---------+ +# | a@b.com | +# +---------+ +# Note: All emails are in lowercase. +# + +# Write your MySQL query statement below +SELECT Email FROM Person GROUP BY Email HAVING COUNT(*) > 1 diff --git a/MySQL/employee-bonus.sql b/MySQL/employee-bonus.sql new file mode 100644 index 000000000..706ef70d9 --- /dev/null +++ b/MySQL/employee-bonus.sql @@ -0,0 +1,14 @@ +# Time: O(n) if hash join, O(nlogn) if merge join +# Space: O(n) + +# https://www.quora.com/What-is-time-complexity-of-Join-algorithm-in-Database + +SELECT + Employee.name, Bonus.bonus +FROM + Employee + LEFT JOIN + Bonus ON Employee.empid = Bonus.empid +WHERE + Bonus.bonus < 1000 OR Bonus.bonus IS NULL +; diff --git a/MySQL/employees-earning-more-than-their-managers.sql b/MySQL/employees-earning-more-than-their-managers.sql new file mode 100644 index 000000000..eaafe2a00 --- /dev/null +++ b/MySQL/employees-earning-more-than-their-managers.sql @@ -0,0 +1,37 @@ +# Time: O(n^2) +# Space: O(1) +# +# The Employee table holds all employees including their managers. Every employee has an Id, and there is also a column for the manager Id. +# +# +----+-------+--------+-----------+ +# | Id | Name | Salary | ManagerId | +# +----+-------+--------+-----------+ +# | 1 | Joe | 70000 | 3 | +# | 2 | Henry | 80000 | 4 | +# | 3 | Sam | 60000 | NULL | +# | 4 | Max | 90000 | NULL | +# +----+-------+--------+-----------+ +# Given the Employee table, write a SQL query that finds out employees who earn more than their managers. For the above table, Joe is the only employee who earns more than his manager. +# +# +----------+ +# | Employee | +# +----------+ +# | Joe | +# +----------+ +# + +# Time: O(n^2) +# Space: O(n) +# Write your MySQL query statement below +SELECT e.Name AS Employee FROM Employee e LEFT JOIN Employee b + ON e.ManagerId=b.Id + WHERE e.Salary > b.Salary + +# Time: O(n^2) +# Space: O(1) +# Write your MySQL query statement below +SELECT Name AS Employee + FROM Employee e + WHERE e.ManagerId IS NOT NULL AND e.Salary > (SELECT Salary + FROM Employee + WHERE e.ManagerId = Id) diff --git a/MySQL/exchange-seats.sql b/MySQL/exchange-seats.sql new file mode 100644 index 000000000..1a80d7664 --- /dev/null +++ b/MySQL/exchange-seats.sql @@ -0,0 +1,40 @@ +# Time: O(nlogn) +# Space: O(n) + +# Mary is a teacher in a middle school and she has a table seat storing +# students' names and their corresponding seat ids. +# +# The column id is continuous increment. +# Mary wants to change seats for the adjacent students. +# Can you write a SQL query to output the result for Mary? +# +---------+---------+ +# | id | student | +# +---------+---------+ +# | 1 | Abbot | +# | 2 | Doris | +# | 3 | Emerson | +# | 4 | Green | +# | 5 | Jeames | +# +---------+---------+ +# +# For the sample input, the output is: +# +---------+---------+ +# | id | student | +# +---------+---------+ +# | 1 | Doris | +# | 2 | Abbot | +# | 3 | Green | +# | 4 | Emerson | +# | 5 | Jeames | +# +---------+---------+ +# +# Note: +# If the number of students is odd, there is no need to change the last one's seat. + +SELECT + s1.id, COALESCE(s2.student, s1.student) AS student +FROM + seat s1 + LEFT JOIN + seat s2 ON ((s1.id + 1) ^ 1) - 1 = s2.id +ORDER BY s1.id; diff --git a/MySQL/find-cumulative-salary-of-an-employee.sql b/MySQL/find-cumulative-salary-of-an-employee.sql new file mode 100644 index 000000000..be723db8a --- /dev/null +++ b/MySQL/find-cumulative-salary-of-an-employee.sql @@ -0,0 +1,22 @@ +# Time: O(n^2) +# Space: O(n) + +SELECT EA.Id, + EA.Month, + SUM(EB.salary) AS Salary +FROM (SELECT E1.* + FROM employee E1 + LEFT JOIN (SELECT id, + MAX(month) AS month + FROM employee + GROUP BY id) E2 + ON E1.id = E2.id + WHERE E1.month < E2.month) EA + LEFT JOIN employee EB + ON EA.id = EB.id +WHERE EA.month - 2 <= EB.month + AND EB.month <= EA.month +GROUP BY EA.id, + EA.month +ORDER BY EA.id, + month DESC diff --git a/MySQL/find-customer-referee.sql b/MySQL/find-customer-referee.sql new file mode 100644 index 000000000..4364ed01d --- /dev/null +++ b/MySQL/find-customer-referee.sql @@ -0,0 +1,6 @@ +# Time: O(n) +# Space: O(1) + +SELECT name +FROM customer +WHERE referee_id is NULL OR referee_id != 2; diff --git a/MySQL/find-median-given-frequency-of-numbers.sql b/MySQL/find-median-given-frequency-of-numbers.sql new file mode 100644 index 000000000..6007b846b --- /dev/null +++ b/MySQL/find-median-given-frequency-of-numbers.sql @@ -0,0 +1,15 @@ +# Time: O(nlogn) +# Space: O(n) + +SELECT AVG(n.Number) AS median +FROM Numbers n LEFT JOIN +( +SELECT Number, @prev := @count AS prevNumber, (@count := @count + Frequency) AS countNumber +FROM Numbers, +(SELECT @count := 0, @prev := 0, @total := (SELECT SUM(Frequency) FROM Numbers)) temp ORDER BY Number +) n2 +ON n.Number = n2.Number +WHERE +(prevNumber < floor((@total+1)/2) AND countNumber >= floor((@total+1)/2)) +OR +(prevNumber < floor((@total+2)/2) AND countNumber >= floor((@total+2)/2)) diff --git a/MySQL/friend-requests-i-overall-acceptance-rate.sql b/MySQL/friend-requests-i-overall-acceptance-rate.sql new file mode 100644 index 000000000..5ca34216d --- /dev/null +++ b/MySQL/friend-requests-i-overall-acceptance-rate.sql @@ -0,0 +1,11 @@ +# Time: O(rlogr + aloga) +# Space: O(r + a) + +SELECT +ROUND( + IFNULL( + (SELECT COUNT(*) FROM (SELECT DISTINCT requester_id, accepter_id FROM request_accepted) AS r) + / + (SELECT COUNT(*) FROM (SELECT DISTINCT sender_id, send_to_id FROM friend_request) AS a), + 0) +, 2) AS accept_rate; diff --git a/MySQL/friend-requests-ii-who-has-the-most-friends.sql b/MySQL/friend-requests-ii-who-has-the-most-friends.sql new file mode 100644 index 000000000..baff9f17c --- /dev/null +++ b/MySQL/friend-requests-ii-who-has-the-most-friends.sql @@ -0,0 +1,14 @@ +# Time: O(nlogn) +# Space: O(n) + +SELECT ids as id, COUNT(*) AS num + FROM + ( + SELECT requester_id AS ids FROM request_accepted + UNION ALL + SELECT accepter_id FROM request_accepted + ) AS tmp +GROUP BY ids +ORDER BY num DESC +LIMIT 1 +; diff --git a/MySQL/get-highest-answer-rate-question.sql b/MySQL/get-highest-answer-rate-question.sql new file mode 100644 index 000000000..e39d30a99 --- /dev/null +++ b/MySQL/get-highest-answer-rate-question.sql @@ -0,0 +1,10 @@ +# Time: O(nlogn) +# Space: O(n) + +SELECT + question_id AS 'survey_log' +FROM + survey_log +GROUP BY question_id +ORDER BY COUNT(answer_id) / COUNT(IF(action = 'show', 1, 0)) DESC +LIMIT 1; diff --git a/MySQL/human-traffic-of-stadium.sql b/MySQL/human-traffic-of-stadium.sql new file mode 100644 index 000000000..fcf908393 --- /dev/null +++ b/MySQL/human-traffic-of-stadium.sql @@ -0,0 +1,16 @@ +# Time: O(n^3) +# Space: O(n^3) + +SELECT DISTINCT s1.* +FROM stadium AS s1, stadium AS s2, stadium AS s3 +WHERE s1.people >= 100 AND s2.people >= 100 AND s3.people >= 100 +AND +( + (s2.id = s1.id + 1 AND s3.id = s2.id + 1 AND s3.id = s1.id + 2) -- s1, s2, s3 + OR + (s1.id = s2.id + 1 AND s3.id = s1.id + 1 AND s3.id = s2.id + 2) -- s2, s1, s3 + OR + (s3.id = s2.id + 1 AND s1.id = s3.id + 1 AND s1.id = s2.id + 2) -- s2, s3, s1 +) +ORDER BY s1.id +; diff --git a/MySQL/investments-in-2016.sql b/MySQL/investments-in-2016.sql new file mode 100644 index 000000000..6d9e1ad1e --- /dev/null +++ b/MySQL/investments-in-2016.sql @@ -0,0 +1,29 @@ +# Time: O(n^2) +# Space: O(n) + +SELECT + SUM(insurance.TIV_2016) AS TIV_2016 +FROM + insurance +WHERE + insurance.TIV_2015 IN + ( + SELECT + TIV_2015 + FROM + insurance + GROUP BY TIV_2015 + HAVING COUNT(*) > 1 + ORDER BY NULL + ) + AND CONCAT(LAT, LON) IN + ( + SELECT + CONCAT(LAT, LON) + FROM + insurance + GROUP BY LAT , LON + HAVING COUNT(*) = 1 + ORDER BY NULL + ) +; diff --git a/MySQL/managers-with-at-least-5-direct-reports.sql b/MySQL/managers-with-at-least-5-direct-reports.sql new file mode 100644 index 000000000..f2fc77644 --- /dev/null +++ b/MySQL/managers-with-at-least-5-direct-reports.sql @@ -0,0 +1,16 @@ +# Time: O(n) +# Space: O(n) + +SELECT + Name +FROM + Employee AS t1 INNER JOIN + (SELECT + ManagerId + FROM + Employee + GROUP BY ManagerId + HAVING COUNT(ManagerId) >= 5 + ORDER BY NULL) AS t2 + ON t1.Id = t2.ManagerId +; diff --git a/MySQL/median-employee-salary.sql b/MySQL/median-employee-salary.sql new file mode 100644 index 000000000..29bd42dbf --- /dev/null +++ b/MySQL/median-employee-salary.sql @@ -0,0 +1,14 @@ +# Time: O(nlogn) +# Space: O(n) + +SELECT Id, Company, Salary FROM +( +SELECT e.Id, e.Salary, e.Company, IF (@Prev = e.Company , @Rank := @Rank + 1, @Rank := 1) AS Rank, @Prev := e.Company +FROM Employee e , (SELECT @Rank := 0, @prev := 0) AS Temp ORDER BY e.Company, e.Salary, e.Id +) Ranking +INNER JOIN +( +SELECT COUNT(*) AS TotalCount, Company AS Name FROM Employee e2 GROUP BY e2.Company +) CompanyCount +ON CompanyCount.Name = Ranking.Company +WHERE Rank = floor((TotalCount+1)/2) OR Rank = floor((TotalCount+2)/2) diff --git a/MySQL/not-boring-movies.sql b/MySQL/not-boring-movies.sql new file mode 100644 index 000000000..765a9dbc8 --- /dev/null +++ b/MySQL/not-boring-movies.sql @@ -0,0 +1,31 @@ +# Time: O(nlogn) +# Space: O(1) + +# X city opened a new cinema, many people would like to go to this cinema. +# The cinema also gives out a poster indicating the movies’ ratings and descriptions. +# +# Please write a SQL query to output movies with an odd numbered ID and +# a description that is not 'boring'. Order the result by rating. +# +# For example, table cinema: +# +---------+-----------+--------------+-----------+ +# | id | movie | description | rating | +# +---------+-----------+--------------+-----------+ +# | 1 | War | great 3D | 8.9 | +# | 2 | Science | fiction | 8.5 | +# | 3 | irish | boring | 6.2 | +# | 4 | Ice song | Fantacy | 8.6 | +# | 5 | House card| Interesting| 9.1 | +# +---------+-----------+--------------+-----------+ +# +# For the example above, the output should be: +# +---------+-----------+--------------+-----------+ +# | id | movie | description | rating | +# +---------+-----------+--------------+-----------+ +# | 5 | House card| Interesting| 9.1 | +# | 1 | War | great 3D | 8.9 | +# +---------+-----------+--------------+-----------+ + +SELECT * FROM cinema +WHERE id % 2 != 0 and description != 'boring' +ORDER BY rating DESC; diff --git a/MySQL/rising-temperature.sql b/MySQL/rising-temperature.sql new file mode 100644 index 000000000..91c25d228 --- /dev/null +++ b/MySQL/rising-temperature.sql @@ -0,0 +1,28 @@ +# Time: O(n^2) +# Space: O(n) +# +# Given a Weather table, write a SQL query to find all dates' +# Ids with higher temperature compared to its previous (yesterday's) dates. +# +# +---------+------------+------------------+ +# | Id(INT) | Date(DATE) | Temperature(INT) | +# +---------+------------+------------------+ +# | 1 | 2015-01-01 | 10 | +# | 2 | 2015-01-02 | 25 | +# | 3 | 2015-01-03 | 20 | +# | 4 | 2015-01-04 | 30 | +# +---------+------------+------------------+ +# For example, return the following Ids for the above Weather table: +# +----+ +# | Id | +# +----+ +# | 2 | +# | 4 | +# +----+ +# + +# Write your MySQL query statement below +SELECT wt1.Id +FROM Weather wt1, Weather wt2 +WHERE wt1.Temperature > wt2.Temperature AND + TO_DAYS(wt1.DATE)-TO_DAYS(wt2.DATE)=1; diff --git a/MySQL/sales-person.sql b/MySQL/sales-person.sql new file mode 100644 index 000000000..9a008d568 --- /dev/null +++ b/MySQL/sales-person.sql @@ -0,0 +1,17 @@ +# Time: O(s * o) +# Space: O(s + o) + +SELECT + s.name +FROM + salesperson AS s +WHERE + s.sales_id NOT IN (SELECT + o.sales_id + FROM + orders AS o + LEFT JOIN + company AS c ON o.com_id = c.com_id + WHERE + c.name = 'RED') +; diff --git a/MySQL/second-degree-follower.sql b/MySQL/second-degree-follower.sql new file mode 100644 index 000000000..3fa3462c2 --- /dev/null +++ b/MySQL/second-degree-follower.sql @@ -0,0 +1,7 @@ +# Time: O(nlogn) +# Space: O(n) + +SELECT f1.follower, COUNT(DISTINCT f2.follower) AS num +FROM follow f1 +JOIN follow f2 ON f1.follower = f2.followee +GROUP BY f1.follower diff --git a/MySQL/second-highest-salary.sql b/MySQL/second-highest-salary.sql index 0a1a3e1c7..272ee4713 100644 --- a/MySQL/second-highest-salary.sql +++ b/MySQL/second-highest-salary.sql @@ -1,7 +1,7 @@ # Time: O(n) # Space: O(1) # -# Write a SQL query to get the second highest salary from the Employee table. +# Write a SQL query to get the second highest salary from the Employee table. Alias Salary as SecondHighestSalary. # # +----+--------+ # | Id | Salary | @@ -13,5 +13,6 @@ # For example, given the above Employee table, the second highest salary is 200. If there is no second highest salary, then the query should return null. # # Write your MySQL query statement below -SELECT MAX(Salary) FROM Employee -WHERE Salary NOT IN (SELECT MAX(Salary) FROM Employee) +SELECT (SELECT MAX(Salary) FROM Employee WHERE Salary NOT IN (SELECT MAX(Salary) FROM Employee)) SecondHighestSalary; +# or +SELECT (SELECT Salary FROM Employee GROUP BY Salary ORDER BY Salary DESC LIMIT 1,1) SecondHighestSalary; diff --git a/MySQL/shortest-distance-in-a-line.sql b/MySQL/shortest-distance-in-a-line.sql new file mode 100644 index 000000000..41b702618 --- /dev/null +++ b/MySQL/shortest-distance-in-a-line.sql @@ -0,0 +1,30 @@ +# Time: O(nlogn) +# Space: O(n) +SET @prev := -100000000; +SELECT MIN(diff) AS shortest +FROM (SELECT (x - @prev) AS diff, @prev := x + FROM (SELECT * + FROM point + ORDER BY x) AS t1 + ) AS t2 +; + +# Time: O(nlogn) +# Space: O(n) +SELECT MIN(P1.x - P2.x) AS shortest +FROM (SELECT @id1:=0, @id2:=0) AS t, + (SELECT @id1:=@id1+1 AS id, x FROM point ORDER BY x) AS P1 + JOIN + (SELECT @id2:=@id2+1 AS id, x FROM point ORDER BY x) AS P2 + ON P1.id = P2.id + 1 +WHERE P1.id > 1; + +# Time: O(n^2) +# Space: O(n) +SELECT + MIN(p2.x - p1.x) AS shortest +FROM + point p1 + JOIN + point p2 ON p1.x < p2.x +; diff --git a/MySQL/shortest-distance-in-a-plane.sql b/MySQL/shortest-distance-in-a-plane.sql new file mode 100644 index 000000000..374363906 --- /dev/null +++ b/MySQL/shortest-distance-in-a-plane.sql @@ -0,0 +1,10 @@ +# Time: O(n^2) +# Space: O(n^2) + +SELECT + ROUND(SQRT(MIN((POW(p1.x - p2.x, 2) + POW(p1.y - p2.y, 2)))),2) AS shortest +FROM + point_2d p1 + JOIN + point_2d p2 ON (p1.x < p2.x) OR (p1.x = p2.x AND p1.y < p2.y) +; diff --git a/MySQL/students-report-by-geography.sql b/MySQL/students-report-by-geography.sql new file mode 100644 index 000000000..30186537d --- /dev/null +++ b/MySQL/students-report-by-geography.sql @@ -0,0 +1,31 @@ +# Time: O(nlogn) +# Space: O(n) + +SELECT + America, Asia, Europe +FROM + (SELECT @as:=0, @am:=0, @eu:=0) t, + (SELECT + @am:=@am + 1 AS amid, name AS America + FROM + student + WHERE + continent = 'America' + ORDER BY America) AS t1 + LEFT JOIN + (SELECT + @as:=@as + 1 AS asid, name AS Asia + FROM + student + WHERE + continent = 'Asia' + ORDER BY Asia) AS t2 ON amid = asid + LEFT JOIN + (SELECT + @eu:=@eu + 1 AS euid, name AS Europe + FROM + student + WHERE + continent = 'Europe' + ORDER BY Europe) AS t3 ON amid = euid +; diff --git a/MySQL/swap-salary.sql b/MySQL/swap-salary.sql new file mode 100644 index 000000000..0d468a90d --- /dev/null +++ b/MySQL/swap-salary.sql @@ -0,0 +1,23 @@ +# Time: O(n) +# Space: O(1) + +# Given a table salary, such as the one below, that has m=male and f=female values. +# Swap all f and m values (i.e., change all f values to m and vice versa) with a single update query and no intermediate temp table. +# +# For example: +# | id | name | sex | salary | +# |----|------|-----|--------| +# | 1 | A | m | 2500 | +# | 2 | B | f | 1500 | +# | 3 | C | m | 5500 | +# | 4 | D | f | 500 | +# +# After running your query, the above salary table should have the following rows: +# | id | name | sex | salary | +# |----|------|-----|--------| +# | 1 | A | f | 2500 | +# | 2 | B | m | 1500 | +# | 3 | C | f | 5500 | +# | 4 | D | m | 500 | + +UPDATE salary SET sex = IF(sex = 'm', 'f', 'm') diff --git a/MySQL/tree-node.sql b/MySQL/tree-node.sql new file mode 100644 index 000000000..a432b325d --- /dev/null +++ b/MySQL/tree-node.sql @@ -0,0 +1,11 @@ +# Time: O(n^2) +# Space: O(n) + +SELECT + atree.id, + IF(ISNULL(atree.p_id), + 'Root', + IF(atree.id IN (SELECT p_id FROM tree), 'Inner','Leaf')) AS Type +FROM + tree AS atree +ORDER BY atree.id diff --git a/MySQL/triangle-judgement.sql b/MySQL/triangle-judgement.sql new file mode 100644 index 000000000..996d7dbc7 --- /dev/null +++ b/MySQL/triangle-judgement.sql @@ -0,0 +1,6 @@ +# Time: O(n) +# Space: O(1) + +SELECT *, + IF (x+y>z AND x+z>y AND y+z>x, "Yes","No") AS triangle +FROM triangle; diff --git a/MySQL/trips-and-users.sql b/MySQL/trips-and-users.sql new file mode 100644 index 000000000..751ad9b5c --- /dev/null +++ b/MySQL/trips-and-users.sql @@ -0,0 +1,56 @@ +# Time: O((t * u) + tlogt) +# Space: O(t) +# +# The Trips table holds all taxi trips. Each trip has a unique Id, while Client_Id and Driver_Id +# are both foreign keys to the Users_Id at the Users table. Status is an ENUM type of +# (‘completed’, ‘cancelled_by_driver’, ‘cancelled_by_client’). +# +# +----+-----------+-----------+---------+--------------------+----------+ +# | Id | Client_Id | Driver_Id | City_Id | Status |Request_at| +# +----+-----------+-----------+---------+--------------------+----------+ +# | 1 | 1 | 10 | 1 | completed |2013-10-01| +# | 2 | 2 | 11 | 1 | cancelled_by_driver|2013-10-01| +# | 3 | 3 | 12 | 6 | completed |2013-10-01| +# | 4 | 4 | 13 | 6 | cancelled_by_client|2013-10-01| +# | 5 | 1 | 10 | 1 | completed |2013-10-02| +# | 6 | 2 | 11 | 6 | completed |2013-10-02| +# | 7 | 3 | 12 | 6 | completed |2013-10-02| +# | 8 | 2 | 12 | 12 | completed |2013-10-03| +# | 9 | 3 | 10 | 12 | completed |2013-10-03| +# | 10 | 4 | 13 | 12 | cancelled_by_driver|2013-10-03| +# +----+-----------+-----------+---------+--------------------+----------+ +# The Users table holds all users. Each user has an unique Users_Id, and Role is an ENUM type of +# (‘client’, ‘driver’, ‘partner’). +# +# +----------+--------+--------+ +# | Users_Id | Banned | Role | +# +----------+--------+--------+ +# | 1 | No | client | +# | 2 | Yes | client | +# | 3 | No | client | +# | 4 | No | client | +# | 10 | No | driver | +# | 11 | No | driver | +# | 12 | No | driver | +# | 13 | No | driver | +# +----------+--------+--------+ +# Write a SQL query to find the cancellation rate of requests made by unbanned clients between +# Oct 1, 2013 and Oct 3, 2013. For the above tables, your SQL query should return the following +# rows with the cancellation rate being rounded to two decimal places. +# +# +------------+-------------------+ +# | Day | Cancellation Rate | +# +------------+-------------------+ +# | 2013-10-01 | 0.33 | +# | 2013-10-02 | 0.00 | +# | 2013-10-03 | 0.50 | +# +------------+-------------------+ +# +select +t.Request_at Day, +round(sum(case when t.Status = 'completed' then 0 else 1 end) / count(*), 2) Rate +from Trips t +inner join Users u +on t.Client_Id = u.Users_Id and u.Banned = 'No' +where t.Request_at between '2013-10-01' and '2013-10-03' +group by t.Request_at diff --git a/MySQL/winning-candidate.sql b/MySQL/winning-candidate.sql new file mode 100644 index 000000000..ec491535d --- /dev/null +++ b/MySQL/winning-candidate.sql @@ -0,0 +1,18 @@ +# Time: O(nlogn) +# Space: O(n) + +SELECT + name AS Name +FROM + Candidate + JOIN + (SELECT + Candidateid + FROM + Vote + GROUP BY Candidateid + ORDER BY COUNT(*) DESC + LIMIT 1) AS Winner +WHERE + Candidate.id = Winner.Candidateid +; diff --git a/Python/01-matrix.py b/Python/01-matrix.py new file mode 100644 index 000000000..479a22a42 --- /dev/null +++ b/Python/01-matrix.py @@ -0,0 +1,100 @@ +# Time: O(m * n) +# Space: O(m * n) + +# Given a matrix consists of 0 and 1, find the distance of the nearest 0 for each cell. +# The distance between two adjacent cells is 1. +# +# Example 1: +# +# Input: +# 0 0 0 +# 0 1 0 +# 0 0 0 +# +# Output: +# 0 0 0 +# 0 1 0 +# 0 0 0 +# +# Example 2: +# +# Input: +# 0 0 0 +# 0 1 0 +# 1 1 1 +# +# Output: +# 0 0 0 +# 0 1 0 +# 1 2 1 +# +# Note: +# The number of elements of the given matrix will not exceed 10,000. +# There are at least one 0 in the given matrix. +# The cells are adjacent in only four directions: up, down, left and right. + +import collections + +try: + xrange # Python 2 +except NameError: + xrange = range # Python 3 + + +class Solution(object): + def updateMatrix(self, matrix): + """ + :type matrix: List[List[int]] + :rtype: List[List[int]] + """ + queue = collections.deque([]) + for i in xrange(len(matrix)): + for j in xrange(len(matrix[0])): + if matrix[i][j] == 0: + queue.append((i, j)) + else: + matrix[i][j] = float("inf") + + dirs = [(-1, 0), (1, 0), (0, -1), (0, 1)] + while queue: + cell = queue.popleft() + for dir in dirs: + i, j = cell[0]+dir[0], cell[1]+dir[1] + if not (0 <= i < len(matrix)) or not (0 <= j < len(matrix[0])) or \ + matrix[i][j] <= matrix[cell[0]][cell[1]]+1: + continue + queue.append((i, j)) + matrix[i][j] = matrix[cell[0]][cell[1]]+1 + + return matrix + + +# Time: O(m * n) +# Space: O(m * n) +# dp solution +class Solution2(object): + def updateMatrix(self, matrix): + """ + :type matrix: List[List[int]] + :rtype: List[List[int]] + """ + dp = [[float("inf")]*len(matrix[0]) for _ in xrange(len(matrix))] + for i in xrange(len(matrix)): + for j in xrange(len(matrix[i])): + if matrix[i][j] == 0: + dp[i][j] = 0 + else: + if i > 0: + dp[i][j] = min(dp[i][j], dp[i-1][j]+1) + if j > 0: + dp[i][j] = min(dp[i][j], dp[i][j-1]+1) + for i in reversed(xrange(len(matrix))): + for j in reversed(xrange(len(matrix[i]))): + if matrix[i][j] == 0: + dp[i][j] = 0 + else: + if i < len(matrix)-1: + dp[i][j] = min(dp[i][j], dp[i+1][j]+1) + if j < len(matrix[i])-1: + dp[i][j] = min(dp[i][j], dp[i][j+1]+1) + return dp diff --git a/Python/1-bit-and-2-bit-characters.py b/Python/1-bit-and-2-bit-characters.py new file mode 100644 index 000000000..4aa5410d5 --- /dev/null +++ b/Python/1-bit-and-2-bit-characters.py @@ -0,0 +1,49 @@ +# Time: O(n) +# Space: O(1) + +# We have two special characters. The first character can be represented +# by one bit 0. +# The second character can be represented by two bits (10 or 11). +# +# Now given a string represented by several bits. +# Return whether the last character must +# be a one-bit character or not. The given string will always end with a zero. +# +# Example 1: +# Input: +# bits = [1, 0, 0] +# Output: True +# Explanation: +# The only way to decode it is two-bit character and one-bit character. +# So the last character is one-bit character. +# +# Example 2: +# Input: +# bits = [1, 1, 1, 0] +# Output: False +# Explanation: +# The only way to decode it is two-bit character and two-bit character. +# So the last character is NOT one-bit character. +# Note: +# +# 1 <= len(bits) <= 1000. +# bits[i] is always 0 or 1. + +try: + xrange # Python 2 +except NameError: + xrange = range # Python 3 + + +class Solution(object): + def isOneBitCharacter(self, bits): + """ + :type bits: List[int] + :rtype: bool + """ + parity = 0 + for i in reversed(xrange(len(bits)-1)): + if bits[i] == 0: + break + parity ^= bits[i] + return parity == 0 diff --git a/Python/132-pattern.py b/Python/132-pattern.py new file mode 100644 index 000000000..15e0aecbc --- /dev/null +++ b/Python/132-pattern.py @@ -0,0 +1,51 @@ +# Time: O(n) +# Space: O(n) + +# Given a sequence of n integers a1, a2, ..., an, +# a 132 pattern is a subsequence ai, aj, ak such that i < j < k and +# ai < ak < aj. Design an algorithm that takes a list of n numbers as +# input and checks whether there is a 132 pattern in the list. +# +# Note: n will be less than 15,000. +# +# Example 1: +# Input: [1, 2, 3, 4] +# +# Output: False +# +# Explanation: There is no 132 pattern in the sequence. +# Example 2: +# Input: [3, 1, 4, 2] +# +# Output: True +# +# Explanation: There is a 132 pattern in the sequence: [1, 4, 2]. +# Example 3: +# Input: [-1, 3, 2, 0] +# +# Output: True +# +# Explanation: There are three 132 patterns in the sequence: [-1, 3, 2], [-1, 3, 0] and [-1, 2, 0]. + +try: + xrange # Python 2 +except NameError: + xrange = range # Python 3 + + +class Solution(object): + def find132pattern(self, nums): + """ + :type nums: List[int] + :rtype: bool + """ + ak = float("-inf") + st = [] + for i in reversed(xrange(len(nums))): + if nums[i] < ak: + return True + else: + while st and nums[i] > st[-1]: + ak = st.pop() + st.append(nums[i]) + return False diff --git a/Python/2-keys-keyboard.py b/Python/2-keys-keyboard.py new file mode 100644 index 000000000..220adbe6a --- /dev/null +++ b/Python/2-keys-keyboard.py @@ -0,0 +1,43 @@ +# Time: O(sqrt(n)) +# Space: O(1) + +# Initially on a notepad only one character 'A' is present. +# You can perform two operations on this notepad for each step: +# +# Copy All: You can copy all the characters present on the notepad +# (partial copy is not allowed). +# Paste: You can paste the characters which are copied last time. +# Given a number n. +# You have to get exactly n 'A' on the notepad by performing the minimum +# number of steps permitted. +# Output the minimum number of steps to get n 'A'. +# +# Example 1: +# Input: 3 +# Output: 3 +# Explanation: +# Intitally, we have one character 'A'. +# In step 1, we use Copy All operation. +# In step 2, we use Paste operation to get 'AA'. +# In step 3, we use Paste operation to get 'AAA'. +# Note: +# The n will be in the range [1, 1000]. + + +class Solution(object): + def minSteps(self, n): + """ + :type n: int + :rtype: int + """ + result = 0 + p = 2 + # the answer is the sum of prime factors + while p**2 <= n: + while n % p == 0: + result += p + n //= p + p += 1 + if n > 1: + result += n + return result diff --git a/Python/24-game.py b/Python/24-game.py new file mode 100644 index 000000000..995ada2f7 --- /dev/null +++ b/Python/24-game.py @@ -0,0 +1,86 @@ +# Time: O(n^3 * 4^n) = O(1), n = 4 +# Space: O(n^2) = O(1) + +# You have 4 cards each containing a number from 1 to 9. +# You need to judge whether they could operated through *, /, +, -, (, ) to get the value of 24. +# +# Example 1: +# Input: [4, 1, 8, 7] +# Output: True +# Explanation: (8-4) * (7-1) = 24 +# Example 2: +# Input: [1, 2, 1, 2] +# Output: False +# Note: +# The division operator / represents real division, not integer division. +# For example, 4 / (1 - 2/3) = 12. +# Every operation done is between two numbers. In particular, +# we cannot use - as a unary operator. +# For example, with [1, 1, 1, 1] as input, +# the expression -1 - 1 - 1 - 1 is not allowed. +# You cannot concatenate numbers together. For example, +# if the input is [1, 2, 1, 2], we cannot write this as 12 + 12. + +from operator import add, sub, mul, truediv +from fractions import Fraction + +try: + xrange # Python 2 +except NameError: + xrange = range # Python 3 + + +class Solution(object): + def judgePoint24(self, nums): + """ + :type nums: List[int] + :rtype: bool + """ + if len(nums) == 1: + return abs(nums[0]-24) < 1e-6 + ops = [add, sub, mul, truediv] + for i in xrange(len(nums)): + for j in xrange(len(nums)): + if i == j: + continue + next_nums = [nums[k] for k in xrange(len(nums)) if i != k != j] + for op in ops: + if ((op is add or op is mul) and j > i) or \ + (op == truediv and nums[j] == 0): + continue + next_nums.append(op(nums[i], nums[j])) + if self.judgePoint24(next_nums): + return True + next_nums.pop() + return False + + +# Time: O(n^3 * 4^n) = O(1), n = 4 +# Space: O(n^2) = O(1) +class Solution2(object): + def judgePoint24(self, nums): + """ + :type nums: List[int] + :rtype: bool + """ + def dfs(nums): + if len(nums) == 1: + return nums[0] == 24 + ops = [add, sub, mul, truediv] + for i in xrange(len(nums)): + for j in xrange(len(nums)): + if i == j: + continue + next_nums = [nums[k] for k in xrange(len(nums)) + if i != k != j] + for op in ops: + if ((op is add or op is mul) and j > i) or \ + (op == truediv and nums[j] == 0): + continue + next_nums.append(op(nums[i], nums[j])) + if dfs(next_nums): + return True + next_nums.pop() + return False + + return dfs(map(Fraction, nums)) diff --git a/Python/3sum-closest.py b/Python/3sum-closest.py index 0681ae221..a1ece7507 100644 --- a/Python/3sum-closest.py +++ b/Python/3sum-closest.py @@ -1,38 +1,38 @@ # Time: O(n^2) # Space: O(1) -# -# Given an array S of n integers, -# find three integers in S such that the sum is closest to a given number, target. -# Return the sum of the three integers. + +# Given an array S of n integers, +# find three integers in S such that the sum is closest to a given number, +# target. +# Return the sum of the three integers. # You may assume that each input would have exactly one solution. # # For example, given array S = {-1 2 1 -4}, and target = 1. # # The sum that is closest to the target is 2. (-1 + 2 + 1 = 2). -# -class Solution: - # @return an integer + +class Solution(object): def threeSumClosest(self, nums, target): + """ + :type nums: List[int] + :type target: int + :rtype: int + """ nums, result, min_diff, i = sorted(nums), float("inf"), float("inf"), 0 while i < len(nums) - 2: - j, k = i + 1, len(nums) - 1 - while j < k: - diff = nums[i] + nums[j] + nums[k] - target - if abs(diff) < min_diff: - min_diff = abs(diff) - result = nums[i] + nums[j] + nums[k] - if diff < 0: - j += 1 - elif diff > 0: - k -= 1 - else: - return target + if i == 0 or nums[i] != nums[i - 1]: + j, k = i + 1, len(nums) - 1 + while j < k: + diff = nums[i] + nums[j] + nums[k] - target + if abs(diff) < min_diff: + min_diff = abs(diff) + result = nums[i] + nums[j] + nums[k] + if diff < 0: + j += 1 + elif diff > 0: + k -= 1 + else: + return target i += 1 - while i < len(nums) - 2 and nums[i] == nums[i - 1]: - i += 1 return result - -if __name__ == '__main__': - result = Solution().threeSumClosest([-1, 2, 1, -4], 1) - print result diff --git a/Python/3sum-smaller.py b/Python/3sum-smaller.py new file mode 100644 index 000000000..010ce9788 --- /dev/null +++ b/Python/3sum-smaller.py @@ -0,0 +1,24 @@ +# Time: O(n^2) +# Space: O(1) + + +class Solution: + # @param {integer[]} nums + # @param {integer} target + # @return {integer} + def threeSumSmaller(self, nums, target): + nums.sort() + n = len(nums) + + count, k = 0, 2 + while k < n: + i, j = 0, k - 1 + while i < j: # Two Pointers, linear time. + if nums[i] + nums[j] + nums[k] >= target: + j -= 1 + else: + count += j - i + i += 1 + k += 1 + + return count diff --git a/Python/3sum.py b/Python/3sum.py index 2ad5ea356..7015e4208 100644 --- a/Python/3sum.py +++ b/Python/3sum.py @@ -1,6 +1,6 @@ # Time: O(n^2) # Space: O(1) -# + # Given an array S of n integers, # are there elements a, b, c in S such that a + b + c = 0? # Find all unique triplets in the array which gives the sum of zero. @@ -13,31 +13,52 @@ # A solution set is: # (-1, 0, 1) # (-1, -1, 2) -# -class Solution: - # @return a list of lists of length 3, [[val1,val2,val3]] +import collections + + +class Solution(object): def threeSum(self, nums): + """ + :type nums: List[int] + :rtype: List[List[int]] + """ nums, result, i = sorted(nums), [], 0 while i < len(nums) - 2: - j, k = i + 1, len(nums) - 1 - while j < k: - if nums[i] + nums[j] + nums[k] < 0: - j += 1 - elif nums[i] + nums[j] + nums[k] > 0: - k -= 1 - else: - result.append([nums[i], nums[j], nums[k]]) - j, k = j + 1, k - 1 - while j < k and nums[j] == nums[j - 1]: + if i == 0 or nums[i] != nums[i - 1]: + j, k = i + 1, len(nums) - 1 + while j < k: + if nums[i] + nums[j] + nums[k] < 0: j += 1 - while j < k and nums[k] == nums[k + 1]: + elif nums[i] + nums[j] + nums[k] > 0: k -= 1 + else: + result.append([nums[i], nums[j], nums[k]]) + j, k = j + 1, k - 1 + while j < k and nums[j] == nums[j - 1]: + j += 1 + while j < k and nums[k] == nums[k + 1]: + k -= 1 i += 1 - while i < len(nums) - 2 and nums[i] == nums[i - 1]: - i += 1 return result -if __name__ == '__main__': - result = Solution().threeSum([-1, 0, 1, 2, -1, -4]) - print result \ No newline at end of file + def threeSum2(self, nums): + """ + :type nums: List[int] + :rtype: List[List[int]] + """ + d = collections.Counter(nums) + nums_2 = [x[0] for x in d.items() if x[1] > 1] + nums_new = sorted([x[0] for x in d.items()]) + rtn = [[0, 0, 0]] if d[0] >= 3 else [] + for i, j in enumerate(nums_new): + if j <= 0: + numss2 = nums_new[i + 1:] + for x, y in enumerate(numss2): + if 0 - j - y in [j, y] and 0 - j - y in nums_2: + if sorted([j, y, 0 - j - y]) not in rtn: + rtn.append(sorted([j, y, 0 - j - y])) + if 0 - j - y not in [j, y] and 0 - j - y in nums_new: + if sorted([j, y, 0 - j - y]) not in rtn: + rtn.append(sorted([j, y, 0 - j - y])) + return rtn diff --git a/Python/4-keys-keyboard.py b/Python/4-keys-keyboard.py new file mode 100644 index 000000000..45edbdf56 --- /dev/null +++ b/Python/4-keys-keyboard.py @@ -0,0 +1,43 @@ +# Time: O(1) +# Space: O(1) + +try: + xrange # Python 2 +except NameError: + xrange = range # Python 3 + + +class Solution(object): + def maxA(self, N): + """ + :type N: int + :rtype: int + """ + if N < 7: + return N + if N == 10: + return 20 # the following rule doesn't hold when N = 10 + + n = N // 5 + 1 # n3 + n4 increases one every 5 keys + # (1) n = n3 + n4 + # (2) N + 1 = 4 * n3 + 5 * n4 + # 5 x (1) - (2) => 5*n - N - 1 = n3 + n3 = 5*n - N - 1 + n4 = n - n3 + return 3**n3 * 4**n4 + + +# Time: O(n) +# Space: O(1) +class Solution2(object): + def maxA(self, N): + """ + :type N: int + :rtype: int + """ + if N < 7: + return N + dp = range(N+1) + for i in xrange(7, N+1): + dp[i % 6] = max(dp[(i-4) % 6]*3, dp[(i-5) % 6]*4) + return dp[N % 6] diff --git a/Python/4sum-ii.py b/Python/4sum-ii.py new file mode 100644 index 000000000..7ca13bab2 --- /dev/null +++ b/Python/4sum-ii.py @@ -0,0 +1,42 @@ +# Time: O(n^2) +# Space: O(n^2) + +# Given four lists A, B, C, D of integer values, +# compute how many tuples (i, j, k, l) there are +# such that A[i] + B[j] + C[k] + D[l] is zero. +# +# To make problem a bit easier, all A, B, C, D have same length of N +# where 0 <= N <= 500. +# All integers are in the range of -228 to 228 - 1 and the result +# is guaranteed to be at most 231 - 1. +# +# Example: +# +# Input: +# A = [ 1, 2] +# B = [-2,-1] +# C = [-1, 2] +# D = [ 0, 2] +# +# Output: +# 2 +# +# Explanation: +# The two tuples are: +# 1. (0, 0, 0, 1) -> A[0] + B[0] + C[0] + D[1] = 1 + (-2) + (-1) + 2 = 0 +# 2. (1, 1, 0, 0) -> A[1] + B[1] + C[0] + D[0] = 2 + (-1) + (-1) + 0 = 0 + +import collections + + +class Solution(object): + def fourSumCount(self, A, B, C, D): + """ + :type A: List[int] + :type B: List[int] + :type C: List[int] + :type D: List[int] + :rtype: int + """ + A_B_sum = collections.Counter(a+b for a in A for b in B) + return sum(A_B_sum[-c-d] for c in C for d in D) diff --git a/Python/4sum.py b/Python/4sum.py index c049c577c..36c5aca2d 100644 --- a/Python/4sum.py +++ b/Python/4sum.py @@ -1,12 +1,13 @@ -# Time: O(n^2) ~ O(n^4) -# Space: O(n^2) -# -# Given an array S of n integers, +# Time: O(n^3) +# Space: O(1) + +# Given an array S of n integers, # are there elements a, b, c, and d in S such that a + b + c + d = target? # Find all unique quadruplets in the array which gives the sum of target. # # Note: -# Elements in a quadruplet (a,b,c,d) must be in non-descending order. (ie, a <= b <= c <= d) +# Elements in a quadruplet (a,b,c,d) must be in non-descending order. +# (ie, a <= b <= c <= d) # The solution set must not contain duplicate quadruplets. # For example, given array S = {1 0 -1 0 -2 2}, and target = 0. # @@ -16,27 +17,104 @@ # (-2, 0, 0, 2) # -class Solution: - # @return a list of lists of length 4, [[val1,val2,val3,val4]] +import collections + +try: + xrange # Python 2 +except NameError: + xrange = range # Python 3 + + +# Two pointer solution. (1356ms) +class Solution(object): + def fourSum(self, nums, target): + """ + :type nums: List[int] + :type target: int + :rtype: List[List[int]] + """ + nums.sort() + res = [] + for i in xrange(len(nums) - 3): + if i and nums[i] == nums[i - 1]: + continue + for j in xrange(i + 1, len(nums) - 2): + if j != i + 1 and nums[j] == nums[j - 1]: + continue + sum = target - nums[i] - nums[j] + left, right = j + 1, len(nums) - 1 + while left < right: + if nums[left] + nums[right] == sum: + res.append([nums[i], nums[j], nums[left], nums[right]]) + right -= 1 + left += 1 + while left < right and nums[left] == nums[left - 1]: + left += 1 + while left < right and nums[right] == nums[right + 1]: + right -= 1 + elif nums[left] + nums[right] > sum: + right -= 1 + else: + left += 1 + return res + + +# Time: O(n^2 * p) +# Space: O(n^2 * p) +# Hash solution. (224ms) +class Solution2(object): + def fourSum(self, nums, target): + """ + :type nums: List[int] + :type target: int + :rtype: List[List[int]] + """ + nums, result, lookup = sorted(nums), [], collections.defaultdict(list) + for i in xrange(0, len(nums) - 1): + for j in xrange(i + 1, len(nums)): + is_duplicated = False + for [x, y] in lookup[nums[i] + nums[j]]: + if nums[x] == nums[i]: + is_duplicated = True + break + if not is_duplicated: + lookup[nums[i] + nums[j]].append([i, j]) + ans = {} + for c in xrange(2, len(nums)): + for d in xrange(c+1, len(nums)): + if target - nums[c] - nums[d] in lookup: + for [a, b] in lookup[target - nums[c] - nums[d]]: + if b < c: + quad = [nums[a], nums[b], nums[c], nums[d]] + quad_hash = " ".join(str(quad)) + if quad_hash not in ans: + ans[quad_hash] = True + result.append(quad) + return result + + +# Time: O(n^2 * p) ~ O(n^4) +# Space: O(n^2) +class Solution3(object): def fourSum(self, nums, target): - nums, result, lookup = sorted(nums), [], {} + """ + :type nums: List[int] + :type target: int + :rtype: List[List[int]] + """ + nums, result, lookup = sorted(nums), [], collections.defaultdict(list) for i in xrange(0, len(nums) - 1): - for j in xrange(i + 1, len(nums)): - if nums[i] + nums[j] not in lookup: - lookup[nums[i] + nums[j]] = [] + for j in xrange(i + 1, len(nums)): lookup[nums[i] + nums[j]].append([i, j]) for i in lookup.keys(): if target - i in lookup: for x in lookup[i]: - for y in lookup[target -i]: + for y in lookup[target - i]: [a, b], [c, d] = x, y - if a is not c and a is not d and b is not c and b is not d: + if a is not c and a is not d and \ + b is not c and b is not d: quad = sorted([nums[a], nums[b], nums[c], nums[d]]) if quad not in result: result.append(quad) return sorted(result) - -if __name__ == '__main__': - result = Solution().fourSum([1, 0, -1, 0, -2, 2], 0) - print result diff --git a/Python/accounts-merge.py b/Python/accounts-merge.py new file mode 100644 index 000000000..dca820b6a --- /dev/null +++ b/Python/accounts-merge.py @@ -0,0 +1,101 @@ +# Time: O(nlogn), n is the number of total emails, +# and the max length ofemail is 320, p.s. {64}@{255} +# Space: O(n) + +# Given a list accounts, each element accounts[i] is a list of strings, +# where the first element accounts[i][0] is a name, +# and the rest of the elements are emails representing emails of the account. +# +# Now, we would like to merge these accounts. +# Two accounts definitely belong to the same person if there is some email +# that is common to both accounts. +# Note that even if two accounts have the same name, +# they may belong to different people as people could have the same name. +# A person can have any number of accounts initially, but all of their +# accounts definitely have the same name. +# +# After merging the accounts, return the accounts in the following format: +# the first element of each account is the name, and the rest of the elements +# are emails in sorted order. +# The accounts themselves can be returned in any order. +# +# Example 1: +# Input: +# accounts = [["John", "johnsmith@mail.com", "john00@mail.com"], +# ["John", "johnnybravo@mail.com"], +# ["John", "johnsmith@mail.com", "john_newyork@mail.com"], +# ["Mary", "mary@mail.com"]] +# Output: [["John", 'john00@mail.com', 'john_newyork@mail.com', +# 'johnsmith@mail.com'], +# ["John", "johnnybravo@mail.com"], ["Mary", "mary@mail.com"]] +# +# Explanation: +# The first and third John's are the same person as they have the common +# email "johnsmith@mail.com". +# The second John and Mary are different people as none of their email +# addresses are used by other accounts. +# We could return these lists in any order, +# for example the answer [['Mary', 'mary@mail.com'], +# ['John', 'johnnybravo@mail.com'], +# ['John', 'john00@mail.com', 'john_newyork@mail.com', +# 'johnsmith@mail.com']] +# would still be accepted. +# +# Note: +# +# The length of accounts will be in the range [1, 1000]. +# The length of accounts[i] will be in the range [1, 10]. +# The length of accounts[i][j] will be in the range [1, 30]. + +import collections + +try: + xrange # Python 2 +except NameError: + xrange = range # Python 3 + + +class UnionFind(object): + def __init__(self): + self.set = [] + + def get_id(self): + self.set.append(len(self.set)) + return len(self.set)-1 + + def find_set(self, x): + if self.set[x] != x: + self.set[x] = self.find_set(self.set[x]) # path compression. + return self.set[x] + + def union_set(self, x, y): + x_root, y_root = map(self.find_set, (x, y)) + if x_root != y_root: + self.set[min(x_root, y_root)] = max(x_root, y_root) + + +class Solution(object): + def accountsMerge(self, accounts): + """ + :type accounts: List[List[str]] + :rtype: List[List[str]] + """ + union_find = UnionFind() + email_to_name = {} + email_to_id = {} + for account in accounts: + name = account[0] + for i in xrange(1, len(account)): + if account[i] not in email_to_id: + email_to_name[account[i]] = name + email_to_id[account[i]] = union_find.get_id() + union_find.union_set(email_to_id[account[1]], + email_to_id[account[i]]) + + result = collections.defaultdict(list) + for email in email_to_name.keys(): + result[union_find.find_set(email_to_id[email])].append(email) + for emails in result.values(): + emails.sort() + return [[email_to_name[emails[0]]] + emails + for emails in result.values()] diff --git a/Python/add-and-search-word-data-structure-design.py b/Python/add-and-search-word-data-structure-design.py new file mode 100644 index 000000000..aa179b3fd --- /dev/null +++ b/Python/add-and-search-word-data-structure-design.py @@ -0,0 +1,70 @@ +# Time: O(min(n, h)), per operation +# Space: O(min(n, h)) +# +# Design a data structure that supports the following two operations: +# +# void addWord(word) +# bool search(word) +# search(word) can search a literal word or a regular expression string +# containing only letters a-z or .. +# A . means it can represent any one letter. +# +# For example: +# +# addWord("bad") +# addWord("dad") +# addWord("mad") +# search("pad") -> false +# search("bad") -> true +# search(".ad") -> true +# search("b..") -> true +# Note: +# You may assume that all words are consist of lowercase letters a-z. +# + + +class TrieNode: + # Initialize your data structure here. + def __init__(self): + self.is_string = False + self.leaves = {} + + +class WordDictionary: + def __init__(self): + self.root = TrieNode() + + # @param {string} word + # @return {void} + # Adds a word into the data structure. + def addWord(self, word): + curr = self.root + for c in word: + if c not in curr.leaves: + curr.leaves[c] = TrieNode() + curr = curr.leaves[c] + curr.is_string = True + + # @param {string} word + # @return {boolean} + # Returns if the word is in the data structure. A word could + # contain the dot character '.' to represent any one letter. + def search(self, word): + return self.searchHelper(word, 0, self.root) + + def searchHelper(self, word, start, curr): + if start == len(word): + return curr.is_string + if word[start] in curr.leaves: + return self.searchHelper(word, start+1, curr.leaves[word[start]]) + elif word[start] == '.': + for c in curr.leaves: + if self.searchHelper(word, start+1, curr.leaves[c]): + return True + + return False + +# Your WordDictionary object will be instantiated and called as such: +# wordDictionary = WordDictionary() +# wordDictionary.addWord("word") +# wordDictionary.search("pattern") diff --git a/Python/add-binary.py b/Python/add-binary.py index 054570ade..fd43dfc73 100644 --- a/Python/add-binary.py +++ b/Python/add-binary.py @@ -9,24 +9,26 @@ # Return "100". # +try: + xrange # Python 2 +except NameError: + xrange = range # Python 3 + + class Solution: # @param a, a string # @param b, a string # @return a string def addBinary(self, a, b): - result, carry, val, len_a, len_b, i = "", 0, 0, len(a), len(b), 0 - for i in xrange(max(len_a, len_b)): + result, carry, val = "", 0, 0 + for i in xrange(max(len(a), len(b))): val = carry - if i < len_a: - sum += int(a[-(i + 1)]) - if i < len_b: - sum += int(b[-(i + 1)]) + if i < len(a): + val += int(a[-(i + 1)]) + if i < len(b): + val += int(b[-(i + 1)]) carry, val = val / 2, val % 2 - result = "{0}{1}".format(val, result) - if carry == 1: - result = "1" + result - return result - -if __name__ == '__main__': - result = Solution().addBinary('11', '1') - print result + result += str(val) + if carry: + result += str(carry) + return result[::-1] diff --git a/Python/add-bold-tag-in-string.py b/Python/add-bold-tag-in-string.py new file mode 100644 index 000000000..c8c9ac812 --- /dev/null +++ b/Python/add-bold-tag-in-string.py @@ -0,0 +1,73 @@ +# Time: O(n * d * l), l is the average string length +# Space: O(n) + +import collections +import functools + +try: + xrange # Python 2 +except NameError: + xrange = range # Python 3 + + +# 59ms +class Solution(object): + def addBoldTag(self, s, dict): + """ + :type s: str + :type dict: List[str] + :rtype: str + """ + lookup = [0] * len(s) + for d in dict: + pos = s.find(d) + while pos != -1: + lookup[pos:pos+len(d)] = [1] * len(d) + pos = s.find(d, pos + 1) + + result = [] + for i in xrange(len(s)): + if lookup[i] and (i == 0 or not lookup[i-1]): + result.append("") + result.append(s[i]) + if lookup[i] and (i == len(s)-1 or not lookup[i+1]): + result.append("") + return "".join(result) + + +# Time: O(n * l), l is the average string length +# Space: O(t) , t is the size of trie +# trie solution, 439ms +class Solution2(object): + def addBoldTag(self, s, words): + """ + :type s: str + :type words: List[str] + :rtype: str + """ + _trie = lambda: collections.defaultdict(_trie) + trie = _trie() + for i, word in enumerate(words): + functools.reduce(dict.__getitem__, word, trie).setdefault("_end") + + lookup = [False] * len(s) + for i in xrange(len(s)): + curr = trie + k = -1 + for j in xrange(i, len(s)): + if s[j] not in curr: + break + curr = curr[s[j]] + if "_end" in curr: + k = j + for j in xrange(i, k+1): + lookup[j] = True + + result = [] + for i in xrange(len(s)): + if lookup[i] and (i == 0 or not lookup[i-1]): + result.append("") + result.append(s[i]) + if lookup[i] and (i == len(s)-1 or not lookup[i+1]): + result.append("") + return "".join(result) diff --git a/Python/add-digits.py b/Python/add-digits.py new file mode 100644 index 000000000..a5c158645 --- /dev/null +++ b/Python/add-digits.py @@ -0,0 +1,28 @@ +# Time: O(1) +# Space: O(1) +# +# Given a non-negative integer num, repeatedly add +# all its digits until the result has only one digit. +# +# For example: +# +# Given num = 38, the process is like: 3 + 8 = 11, +# 1 + 1 = 2. Since 2 has only one digit, return it. +# +# Follow up: +# Could you do it without any loop/recursion in O(1) +# runtime? +# +# Hint: +# +# A naive implementation of the above process is trivial. +# Could you come up with other methods? + + +class Solution(object): + """ + :type num: int + :rtype: int + """ + def addDigits(self, num): + return (num - 1) % 9 + 1 if num > 0 else 0 diff --git a/Python/add-one-row-to-tree.py b/Python/add-one-row-to-tree.py new file mode 100644 index 000000000..e4622bcf8 --- /dev/null +++ b/Python/add-one-row-to-tree.py @@ -0,0 +1,94 @@ +# Time: O(n) +# Space: O(h) + +# Given the root of a binary tree, then value v and depth d, +# you need to add a row of nodes with value v at the given depth d. +# The root node is at depth 1. +# +# The adding rule is: given a positive integer depth d, +# for each NOT null tree nodes N in depth d-1, create two tree nodes +# with value v as N's left subtree root and right subtree root. +# And N's original left subtree should be the left subtree of +# the new left subtree root, +# its original right subtree should be the right subtree of +# the new right subtree root. +# If depth d is 1 that means there is no depth d-1 at all, +# then create a tree node with value v as the new root of +# the whole original tree, +# and the original tree is the new root's left subtree. +# +# Example 1: +# Input: +# A binary tree as following: +# 4 +# / \ +# 2 6 +# / \ / +# 3 1 5 +# +# v = 1 +# +# d = 2 +# +# Output: +# 4 +# / \ +# 1 1 +# / \ +# 2 6 +# / \ / +# 3 1 5 +# +# Example 2: +# Input: +# A binary tree as following: +# 4 +# / +# 2 +# / \ +# 3 1 +# +# v = 1 +# +# d = 3 +# +# Output: +# 4 +# / +# 2 +# / \ +# 1 1 +# / \ +# 3 1 +# Note: +# 1. The given d is in range [1, maximum depth of the given tree + 1]. +# 2. The given binary tree has at least one tree node. + + +# Definition for a binary tree node. +class TreeNode(object): + def __init__(self, x): + self.val = x + self.left = None + self.right = None + + +class Solution(object): + def addOneRow(self, root, v, d): + """ + :type root: TreeNode + :type v: int + :type d: int + :rtype: TreeNode + """ + if d in (0, 1): + node = TreeNode(v) + if d == 1: + node.left = root + else: + node.right = root + return node + if root and d >= 2: + root.left = self.addOneRow(root.left, v, d-1 if d > 2 else 1) + root.right = self.addOneRow(root.right, v, d-1 if d > 2 else 0) + return root diff --git a/Python/add-strings.py b/Python/add-strings.py new file mode 100644 index 000000000..7cf47e0b9 --- /dev/null +++ b/Python/add-strings.py @@ -0,0 +1,58 @@ +# Time: O(n) +# Space: O(1) + +# Given two non-negative numbers num1 and num2 represented as string, +# return the sum of num1 and num2. +# +# Note: +# +# The length of both num1 and num2 is < 5100. +# Both num1 and num2 contains only digits 0-9. +# Both num1 and num2 does not contain any leading zero. +# You must not use any built-in BigInteger library or +# convert the inputs to integer directly. + + +class Solution(object): + def addStrings(self, num1, num2): + """ + :type num1: str + :type num2: str + :rtype: str + """ + result = [] + i, j, carry = len(num1) - 1, len(num2) - 1, 0 + + while i >= 0 or j >= 0 or carry: + if i >= 0: + carry += ord(num1[i]) - ord('0') + i -= 1 + if j >= 0: + carry += ord(num2[j]) - ord('0') + j -= 1 + result.append(str(carry % 10)) + carry /= 10 + result.reverse() + + return "".join(result) + + def addStrings2(self, num1, num2): + """ + :type num1: str + :type num2: str + :rtype: str + """ + length = max(len(num1), len(num2)) + num1 = num1.zfill(length)[::-1] + num2 = num2.zfill(length)[::-1] + res, plus = '', 0 + for index, num in enumerate(num1): + tmp = str(int(num) + int(num2[index]) + plus) + res += tmp[-1] + if int(tmp) > 9: + plus = 1 + else: + plus = 0 + if plus: + res += '1' + return res[::-1] diff --git a/Python/add-two-numbers-ii.py b/Python/add-two-numbers-ii.py new file mode 100644 index 000000000..7111e061b --- /dev/null +++ b/Python/add-two-numbers-ii.py @@ -0,0 +1,61 @@ +# Time: O(m + n) +# Space: O(m + n) + +# You are given two linked lists representing two non-negative numbers. +# The most significant digit comes first and each of their nodes contain +# a single digit. +# Add the two numbers and return it as a linked list. +# +# You may assume the two numbers do not contain any leading zero, +# except the number 0 itself. +# +# Follow up: +# What if you cannot modify the input lists? In other words, +# reversing the lists is not allowed. +# +# Example: +# +# Input: (7 -> 2 -> 4 -> 3) + (5 -> 6 -> 4) +# Output: 7 -> 8 -> 0 -> 7 + + +# Definition for singly-linked list. +class ListNode(object): + def __init__(self, x): + self.val = x + self.next = None + + +class Solution(object): + def addTwoNumbers(self, l1, l2): + """ + :type l1: ListNode + :type l2: ListNode + :rtype: ListNode + """ + stk1, stk2 = [], [] + while l1: + stk1.append(l1.val) + l1 = l1.next + while l2: + stk2.append(l2.val) + l2 = l2.next + + prev, head = None, None + sum = 0 + while stk1 or stk2: + sum /= 10 + if stk1: + sum += stk1.pop() + if stk2: + sum += stk2.pop() + + head = ListNode(sum % 10) + head.next = prev + prev = head + + if sum >= 10: + head = ListNode(sum / 10) + head.next = prev + + return head diff --git a/Python/add-two-numbers.py b/Python/add-two-numbers.py index 158d85fd1..60cd35f1f 100644 --- a/Python/add-two-numbers.py +++ b/Python/add-two-numbers.py @@ -1,34 +1,42 @@ # Time: O(n) # Space: O(1) # -# You are given two linked lists representing two non-negative numbers. -# The digits are stored in reverse order and each of their nodes contain a single digit. +# You are given two linked lists representing two non-negative numbers. +# The digits are stored in reverse order and each of their nodes contain +# a single digit. # Add the two numbers and return it as a linked list. # # Input: (2 -> 4 -> 3) + (5 -> 6 -> 4) # Output: 7 -> 0 -> 8 # + + # Definition for singly-linked list. class ListNode: def __init__(self, x): self.val = x self.next = None -class Solution: - # @return a ListNode + +class Solution(object): def addTwoNumbers(self, l1, l2): + """ + :type l1: ListNode + :type l2: ListNode + :rtype: ListNode + """ dummy = ListNode(0) current, carry = dummy, 0 - while l1 is not None or l2 is not None: + while l1 or l2: val = carry - if l1 is not None: + if l1: val += l1.val l1 = l1.next - if l2 is not None: + if l2: val += l2.val l2 = l2.next - carry, val = val / 10, val % 10 + carry, val = divmod(val, 10) current.next = ListNode(val) current = current.next @@ -36,10 +44,3 @@ def addTwoNumbers(self, l1, l2): current.next = ListNode(1) return dummy.next - -if __name__ == '__main__': - a, a.next, a.next.next = ListNode(2), ListNode(4), ListNode(3) - b, b.next, b.next.next = ListNode(5), ListNode(6), ListNode(4) - result = Solution().addTwoNumbers(a, b) - print "{0} -> {1} -> {2}".format(result.val, result.next.val, result.next.next.val) - \ No newline at end of file diff --git a/Python/additive-number.py b/Python/additive-number.py new file mode 100644 index 000000000..2a60156ad --- /dev/null +++ b/Python/additive-number.py @@ -0,0 +1,72 @@ +# Time: O(n^3) +# Space: O(n) +# +# Additive number is a positive integer whose digits can form additive +# sequence. +# +# A valid additive sequence should contain at least three numbers. +# Except for the first two numbers, each subsequent number in the +# sequence +# must be the sum of the preceding two. +# +# For example: +# "112358" is an additive number because the digits can form an additive +# sequence: +# 1, 1, 2, 3, 5, 8. +# +# 1 + 1 = 2, 1 + 2 = 3, 2 + 3 = 5, 3 + 5 = 8 +# "199100199" is also an additive number, the additive sequence is: +# 1, 99, 100, 199. +# +# 1 + 99 = 100, 99 + 100 = 199 +# Note: Numbers in the additive sequence cannot have leading zeros, +# so sequence 1, 2, 03 or 1, 02, 3 is invalid. +# +# Given a string represents an integer, write a function to determine +# if it's an additive number. +# +# Follow up: +# How would you handle overflow for very large input integers? +# + +try: + xrange # Python 2 +except NameError: + xrange = range # Python 3 + + +class Solution(object): + def isAdditiveNumber(self, num): + """ + :type num: str + :rtype: bool + """ + def add(a, b): + res, carry, val = "", 0, 0 + for i in xrange(max(len(a), len(b))): + val = carry + if i < len(a): + val += int(a[-(i + 1)]) + if i < len(b): + val += int(b[-(i + 1)]) + carry, val = val / 10, val % 10 + res += str(val) + if carry: + res += str(carry) + return res[::-1] + + for i in xrange(1, len(num)): + for j in xrange(i + 1, len(num)): + s1, s2 = num[0:i], num[i:j] + if (len(s1) > 1 and s1[0] == '0') or \ + (len(s2) > 1 and s2[0] == '0'): + continue + + expected = add(s1, s2) + cur = s1 + s2 + expected + while len(cur) < len(num): + s1, s2, expected = s2, expected, add(s2, expected) + cur += expected + if cur == num: + return True + return False diff --git a/Python/advantage-shuffle.py b/Python/advantage-shuffle.py new file mode 100644 index 000000000..3935a4675 --- /dev/null +++ b/Python/advantage-shuffle.py @@ -0,0 +1,43 @@ +# Time: O(nlogn) +# Space: O(n) + +# Given two arrays A and B of equal size, the advantage of A with respect to B +# is the number of indices i for which A[i] > B[i]. +# +# Return any permutation of A that maximizes its advantage with respect to B. +# +# Example 1: +# +# Input: A = [2,7,11,15], B = [1,10,4,11] +# Output: [2,11,7,15] +# Example 2: +# +# Input: A = [12,24,8,32], B = [13,25,32,11] +# Output: [24,32,8,12] +# +# Note: +# - 1 <= A.length = B.length <= 10000 +# - 0 <= A[i] <= 10^9 +# - 0 <= B[i] <= 10^9 + +class Solution(object): + def advantageCount(self, A, B): + """ + :type A: List[int] + :type B: List[int] + :rtype: List[int] + """ + sortedA = sorted(A) + sortedB = sorted(B) + + candidates = {b: [] for b in B} + others = [] + j = 0 + for a in sortedA: + if a > sortedB[j]: + candidates[sortedB[j]].append(a) + j += 1 + else: + others.append(a) + return [candidates[b].pop() if candidates[b] else others.pop() + for b in B] diff --git a/Python/alien-dictionary.py b/Python/alien-dictionary.py new file mode 100644 index 000000000..04bd2bddc --- /dev/null +++ b/Python/alien-dictionary.py @@ -0,0 +1,116 @@ +# Time: O(n) +# Space: O(|V|+|E|) = O(26 + 26^2) = O(1) + +import collections + +try: + xrange # Python 2 +except NameError: + xrange = range # Python 3 + + +# BFS solution. +class Solution(object): + def alienOrder(self, words): + """ + :type words: List[str] + :rtype: str + """ + result, in_degree, out_degree = [], {}, {} + zero_in_degree_queue = collections.deque() + nodes = set() + for word in words: + for c in word: + nodes.add(c) + + for i in xrange(1, len(words)): + if (len(words[i-1]) > len(words[i]) and + words[i-1][:len(words[i])] == words[i]): + return "" + self.findEdges(words[i - 1], words[i], in_degree, out_degree) + + for node in nodes: + if node not in in_degree: + zero_in_degree_queue.append(node) + + while zero_in_degree_queue: + precedence = zero_in_degree_queue.popleft() + result.append(precedence) + + if precedence in out_degree: + for c in out_degree[precedence]: + in_degree[c].discard(precedence) + if not in_degree[c]: + zero_in_degree_queue.append(c) + + del out_degree[precedence] + + if out_degree: + return "" + + return "".join(result) + + # Construct the graph. + def findEdges(self, word1, word2, in_degree, out_degree): + str_len = min(len(word1), len(word2)) + for i in xrange(str_len): + if word1[i] != word2[i]: + if word2[i] not in in_degree: + in_degree[word2[i]] = set() + if word1[i] not in out_degree: + out_degree[word1[i]] = set() + in_degree[word2[i]].add(word1[i]) + out_degree[word1[i]].add(word2[i]) + break + + +# DFS solution. +class Solution2(object): + def alienOrder(self, words): + """ + :type words: List[str] + :rtype: str + """ + # Find ancestors of each node by DFS. + nodes, ancestors = set(), {} + for i in xrange(len(words)): + for c in words[i]: + nodes.add(c) + for node in nodes: + ancestors[node] = [] + for i in xrange(1, len(words)): + if (len(words[i-1]) > len(words[i]) and + words[i-1][:len(words[i])] == words[i]): + return "" + self.findEdges(words[i - 1], words[i], ancestors) + + # Output topological order by DFS. + result = [] + visited = {} + for node in nodes: + if self.topSortDFS(node, node, ancestors, visited, result): + return "" + + return "".join(result) + + # Construct the graph. + def findEdges(self, word1, word2, ancestors): + min_len = min(len(word1), len(word2)) + for i in xrange(min_len): + if word1[i] != word2[i]: + ancestors[word2[i]].append(word1[i]) + break + + # Topological sort, return whether there is a cycle. + def topSortDFS(self, root, node, ancestors, visited, result): + if node not in visited: + visited[node] = root + for ancestor in ancestors[node]: + if self.topSortDFS(root, ancestor, ancestors, visited, result): + return True + result.append(node) + elif visited[node] == root: + # Visited from the same root in the DFS path. + # So it is cyclic. + return True + return False diff --git a/Python/all-nodes-distance-k-in-binary-tree.py b/Python/all-nodes-distance-k-in-binary-tree.py new file mode 100644 index 000000000..7ca349ecd --- /dev/null +++ b/Python/all-nodes-distance-k-in-binary-tree.py @@ -0,0 +1,69 @@ +# Time: O(n) +# Space: O(n) + +# We are given a binary tree (with root node root), a target node, +# and an integer value `K`. +# +# Return a list of the values of all nodes that have a distance K +# from the target node. The answer can be returned in any order. +# +# Example 1: +# +# Input: root = [3,5,1,6,2,0,8,null,null,7,4], target = 5, K = 2 +# Output: [7,4,1] +# Explanation: +# The nodes that are a distance 2 from the target node (with value 5) +# have values 7, 4, and 1. +# +# Note that the inputs "root" and "target" are actually TreeNodes. +# The descriptions of the inputs above are +# just serializations of these objects. +# +# Note: +# - The given tree is non-empty. +# - Each node in the tree has unique values 0 <= node.val <= 500. +# - The target node is a node in the tree. +# - 0 <= K <= 1000. +# +# Definition for a binary tree node. +# class TreeNode(object): +# def __init__(self, x): +# self.val = x +# self.left = None +# self.right = None + +import collections + +try: + xrange # Python 2 +except NameError: + xrange = range # Python 3 + + +class Solution(object): + def distanceK(self, root, target, K): + """ + :type root: TreeNode + :type target: TreeNode + :type K: int + :rtype: List[int] + """ + def dfs(parent, child, neighbors): + if not child: + return + if parent: + neighbors[parent.val].append(child.val) + neighbors[child.val].append(parent.val) + dfs(child, child.left, neighbors) + dfs(child, child.right, neighbors) + + neighbors = collections.defaultdict(list) + dfs(None, root, neighbors) + bfs = [target.val] + lookup = set(bfs) + for _ in xrange(K): + bfs = [nei for node in bfs + for nei in neighbors[node] + if nei not in lookup] + lookup |= set(bfs) + return bfs diff --git a/Python/all-oone-data-structure.py b/Python/all-oone-data-structure.py new file mode 100644 index 000000000..81589f3e9 --- /dev/null +++ b/Python/all-oone-data-structure.py @@ -0,0 +1,125 @@ +# Time: O(1), per operation +# Space: O(k) + +# Implement a data structure supporting the following operations: +# +# Inc(Key) - Inserts a new key with value 1. Or increments an existing +# key by 1. +# Key is guaranteed to be a non-empty string. +# Dec(Key) - If Key's value is 1, remove it from the data structure. +# Otherwise decrements an existing key by 1. If the key does not exist, +# this function does nothing. Key is guaranteed to be a non-empty string. +# GetMaxKey() - Returns one of the keys with maximal value. If no element +# exists, return an empty string "". +# GetMinKey() - Returns one of the keys with minimal value. If no element +# exists, return an empty string "". +# Challenge: Perform all these in O(1) time complexity. + + +class Node(object): + """ + double linked list node + """ + def __init__(self, value, keys): + self.value = value + self.keys = keys + self.prev = None + self.next = None + + +class LinkedList(object): + def __init__(self): + self.head, self.tail = Node(0, set()), Node(0, set()) + self.head.next, self.tail.prev = self.tail, self.head + + def insert(self, pos, node): + node.prev, node.next = pos.prev, pos + pos.prev.next, pos.prev = node, node + return node + + def erase(self, node): + node.prev.next, node.next.prev = node.next, node.prev + del node + + def empty(self): + return self.head.next is self.tail + + def begin(self): + return self.head.next + + def end(self): + return self.tail + + def front(self): + return self.head.next + + def back(self): + return self.tail.prev + + +class AllOne(object): + + def __init__(self): + """ + Initialize your data structure here. + """ + self.bucket_of_key = {} + self.buckets = LinkedList() + + def inc(self, key): + """ + Inserts a new key with value 1. Or increments an existing key by 1. + :type key: str + :rtype: void + """ + if key not in self.bucket_of_key: + self.bucket_of_key[key] = self.buckets.insert(self.buckets.begin(), Node(0, set([key]))) + + bucket, next_bucket = self.bucket_of_key[key], self.bucket_of_key[key].next + if next_bucket is self.buckets.end() or next_bucket.value > bucket.value+1: + next_bucket = self.buckets.insert(next_bucket, Node(bucket.value+1, set())) + next_bucket.keys.add(key) + self.bucket_of_key[key] = next_bucket + + bucket.keys.remove(key) + if not bucket.keys: + self.buckets.erase(bucket) + + def dec(self, key): + """ + Decrements an existing key by 1. If Key's value is 1, remove it from the data structure. + :type key: str + :rtype: void + """ + if key not in self.bucket_of_key: + return + + bucket, prev_bucket = self.bucket_of_key[key], self.bucket_of_key[key].prev + self.bucket_of_key.pop(key, None) + if bucket.value > 1: + if bucket is self.buckets.begin() or prev_bucket.value < bucket.value-1: + prev_bucket = self.buckets.insert(bucket, Node(bucket.value-1, set())) + prev_bucket.keys.add(key) + self.bucket_of_key[key] = prev_bucket + + bucket.keys.remove(key) + if not bucket.keys: + self.buckets.erase(bucket) + + def getMaxKey(self): + """ + Returns one of the keys with maximal value. + :rtype: str + """ + if self.buckets.empty(): + return "" + return iter(self.buckets.back().keys).next() + + def getMinKey(self): + """ + Returns one of the keys with Minimal value. + :rtype: str + """ + if self.buckets.empty(): + return "" + return iter(self.buckets.front().keys).next() diff --git a/Python/all-paths-from-source-to-target.py b/Python/all-paths-from-source-to-target.py new file mode 100644 index 000000000..17ba7f8ea --- /dev/null +++ b/Python/all-paths-from-source-to-target.py @@ -0,0 +1,46 @@ +# Time: O(p + r * n), p is the count of all the possible paths in graph, +# r is the count of the result. +# Space: O(n) + +# Given a directed, acyclic graph of N nodes. +# Find all possible paths from node 0 to node N-1, and return them +# in any order. +# +# The graph is given as follows: the nodes are 0, 1, ..., +# graph.length - 1. +# graph[i] is a list of all nodes j for which the edge (i, j) exists. +# +# Example: +# Input: [[1,2], [3], [3], []] +# Output: [[0,1,3],[0,2,3]] +# Explanation: The graph looks like this: +# 0--->1 +# | | +# v v +# 2--->3 +# There are two paths: 0 -> 1 -> 3 and 0 -> 2 -> 3. +# +# Note: +# - The number of nodes in the graph will be in the range [2, 15]. +# - You can print different paths in any order, but you should keep +# the order of nodes inside one path. + + +class Solution(object): + def allPathsSourceTarget(self, graph): + """ + :type graph: List[List[int]] + :rtype: List[List[int]] + """ + def dfs(graph, curr, path, result): + if curr == len(graph)-1: + result.append(path[:]) + return + for node in graph[curr]: + path.append(node) + dfs(graph, node, path, result) + path.pop() + + result = [] + dfs(graph, 0, [0], result) + return result diff --git a/Python/ambiguous-coordinates.py b/Python/ambiguous-coordinates.py new file mode 100644 index 000000000..525bd9326 --- /dev/null +++ b/Python/ambiguous-coordinates.py @@ -0,0 +1,69 @@ +# Time: O(n^4) +# Space: O(n) + +# We had some 2-dimensional coordinates, like "(1, 3)" or "(2, 0.5)". +# Then, we removed all commas, decimal points, and spaces, +# and ended up with the string S. +# Return a list of strings representing all possibilities for +# what our original coordinates could have been. +# +# Our original representation never had extraneous zeroes, +# so we never started with numbers like +# "00", "0.0", "0.00", "1.0", "001", "00.01", +# or any other number that can be represented with less digits. +# Also, a decimal point within a number never occurs without +# at least one digit occuring before it, +# so we never started with numbers like ".1". +# +# The final answer list can be returned in any order. +# Also note that all coordinates in the final answer +# have exactly one space between them +# (occurring after the comma.) +# +# Example 1: +# Input: "(123)" +# Output: ["(1, 23)", "(12, 3)", "(1.2, 3)", "(1, 2.3)"] +# Example 2: +# Input: "(00011)" +# Output: ["(0.001, 1)", "(0, 0.011)"] +# Explanation: +# 0.0, 00, 0001 or 00.01 are not allowed. +# Example 3: +# Input: "(0123)" +# Output: ["(0, 123)", "(0, 12.3)", "(0, 1.23)", "(0.1, 23)", "(0.1, 2.3)", "(0.12, 3)"] +# Example 4: +# Input: "(100)" +# Output: [(10, 0)] +# Explanation: +# 1.0 is not allowed. +# +# Note: +# - 4 <= S.length <= 12. +# - S[0] = "(", S[S.length - 1] = ")", and the other elements in S are digits. + +import itertools + +try: + xrange # Python 2 +except NameError: + xrange = range # Python 3 + + +class Solution(object): + def ambiguousCoordinates(self, S): + """ + :type S: str + :rtype: List[str] + """ + def make(S, i, n): + for d in xrange(1, n+1): + left = S[i:i+d] + right = S[i+d:i+n] + if ((not left.startswith('0') or left == '0') + and (not right.endswith('0'))): + yield "".join([left, '.' if right else '', right]) + + return ["({}, {})".format(*cand) + for i in xrange(1, len(S)-2) + for cand in itertools.product(make(S, 1, i), + make(S, i+1, len(S)-2-i))] diff --git a/Python/anagrams.py b/Python/anagrams.py deleted file mode 100644 index 7930db78c..000000000 --- a/Python/anagrams.py +++ /dev/null @@ -1,27 +0,0 @@ -# Time: O(n) -# Space: O(n) -# -# Given an array of strings, return all groups of strings that are anagrams. -# -# Note: All inputs will be in lower-case. -# - -class Solution: - # @param strs, a list of strings - # @return a list of strings - def anagrams(self, strs): - anagrams_map, result = {}, [] - for s in strs: - sorted_str = ("").join(sorted(s)) - if sorted_str in anagrams_map: - anagrams_map[sorted_str].append(s) - else: - anagrams_map[sorted_str] = [s] - for anagram in anagrams_map.values(): - if len(anagram) > 1: - result += anagram - return result - -if __name__ == "__main__": - result = Solution().anagrams(["cat", "dog", "act", "mac"]) - print result \ No newline at end of file diff --git a/Python/android-unlock-patterns.py b/Python/android-unlock-patterns.py new file mode 100644 index 000000000..e4293d456 --- /dev/null +++ b/Python/android-unlock-patterns.py @@ -0,0 +1,188 @@ +# Time: O(9^2 * 2^9) +# Space: O(9 * 2^9) + +try: + xrange # Python 2 +except NameError: + xrange = range # Python 3 + + +# DP solution. +class Solution(object): + def numberOfPatterns(self, m, n): + """ + :type m: int + :type n: int + :rtype: int + """ + def merge(used, i): + return used | (1 << i) + + def number_of_keys(i): + number = 0 + while i > 0: + i &= i - 1 + number += 1 + return number + + def contain(used, i): + return bool(used & (1 << i)) + + def convert(i, j): + return 3 * i + j + + # dp[i][j]: i is the set of the numbers in binary representation, + # dp[i][j] is the number of ways ending with the number j. + dp = [[0] * 9 for _ in xrange(1 << 9)] + for i in xrange(9): + dp[merge(0, i)][i] = 1 + + res = 0 + for used in xrange(len(dp)): + number = number_of_keys(used) + if number > n: + continue + + for i in xrange(9): + if not contain(used, i): + continue + + if m <= number <= n: + res += dp[used][i] + + x1, y1 = divmod(i, 3) + for j in xrange(9): + if contain(used, j): + continue + + x2, y2 = divmod(j, 3) + if ((x1 == x2 and abs(y1 - y2) == 2) or + (y1 == y2 and abs(x1 - x2) == 2) or + (abs(x1 - x2) == 2 and abs(y1 - y2) == 2)) and \ + not contain(used, + convert((x1 + x2) // 2, (y1 + y2) // 2)): + continue + + dp[merge(used, j)][j] += dp[used][i] + + return res + + +# Time: O(9^2 * 2^9) +# Space: O(9 * 2^9) +# DP solution. +class Solution2(object): + def numberOfPatterns(self, m, n): + """ + :type m: int + :type n: int + :rtype: int + """ + def merge(used, i): + return used | (1 << i) + + def number_of_keys(i): + number = 0 + while i > 0: + i &= i - 1 + number += 1 + return number + + def exclude(used, i): + return used & ~(1 << i) + + def contain(used, i): + return bool(used & (1 << i)) + + def convert(i, j): + return 3 * i + j + + # dp[i][j]: i is the set of the numbers in binary representation, + # d[i][j] is the number of ways ending with the number j. + dp = [[0] * 9 for _ in xrange(1 << 9)] + for i in xrange(9): + dp[merge(0, i)][i] = 1 + + res = 0 + for used in xrange(len(dp)): + number = number_of_keys(used) + if number > n: + continue + + for i in xrange(9): + if not contain(used, i): + continue + + x1, y1 = divmod(i, 3) + for j in xrange(9): + if i == j or not contain(used, j): + continue + + x2, y2 = divmod(j, 3) + if ((x1 == x2 and abs(y1 - y2) == 2) or + (y1 == y2 and abs(x1 - x2) == 2) or + (abs(x1 - x2) == 2 and abs(y1 - y2) == 2)) and \ + not contain(used, + convert((x1 + x2) // 2, (y1 + y2) // 2)): + continue + + dp[used][i] += dp[exclude(used, i)][j] + + if m <= number <= n: + res += dp[used][i] + + return res + + +# Time: O(9!) +# Space: O(9) +# Backtracking solution. (TLE) +class Solution_TLE(object): + def numberOfPatterns(self, m, n): + """ + :type m: int + :type n: int + :rtype: int + """ + def merge(used, i): + return used | (1 << i) + + def contain(used, i): + return bool(used & (1 << i)) + + def convert(i, j): + return 3 * i + j + + def numberOfPatternsHelper(m, n, level, used, i): + number = 0 + if level > n: + return number + + if m <= level <= n: + number += 1 + + x1, y1 = divmod(i, 3) + for j in xrange(9): + if contain(used, j): + continue + + x2, y2 = divmod(j, 3) + if ((x1 == x2 and abs(y1 - y2) == 2) or + (y1 == y2 and abs(x1 - x2) == 2) or + (abs(x1 - x2) == 2 and abs(y1 - y2) == 2)) and \ + not contain(used, + convert((x1 + x2) // 2, (y1 + y2) // 2)): + continue + + number += numberOfPatternsHelper(m, n, level + 1, merge(used, j), j) + + return number + + number = 0 + # 1, 3, 7, 9 + number += 4 * numberOfPatternsHelper(m, n, 1, merge(0, 0), 0) + # 2, 4, 6, 8 + number += 4 * numberOfPatternsHelper(m, n, 1, merge(0, 1), 1) + # 5 + number += numberOfPatternsHelper(m, n, 1, merge(0, 4), 4) + return number diff --git a/Python/arithmetic-slices-ii-subsequence.py b/Python/arithmetic-slices-ii-subsequence.py new file mode 100644 index 000000000..10379bd60 --- /dev/null +++ b/Python/arithmetic-slices-ii-subsequence.py @@ -0,0 +1,73 @@ +# Time: O(n^2) +# Space: O(n * d) + +# A sequence of numbers is called arithmetic if it consists of +# at least three elements +# and if the difference between any two consecutive elements is the same. +# +# For example, these are arithmetic sequences: +# +# 1, 3, 5, 7, 9 +# 7, 7, 7, 7 +# 3, -1, -5, -9 +# The following sequence is not arithmetic. +# +# 1, 1, 2, 5, 7 +# +# A zero-indexed array A consisting of N numbers is given. +# A subsequence slice of that array is any sequence of integers +# (P0, P1, ..., Pk) +# such that 0 ≤ P0 < P1 < ... < Pk < N. +# +# A subsequence slice (P0, P1, ..., Pk) of array A is called arithmetic +# if the sequence A[P0], A[P1], ..., A[Pk-1], A[Pk] is arithmetic. +# In particular, this means that k >= 2. +# +# The function should return the number of arithmetic subsequence +# slices in the array A. +# +# The input contains N integers. Every integer is in the range of +# -2^31 and 2^31-1 and 0 <= N <= 1000. +# The output is guaranteed to be less than 2^31-1. +# +# +# Example: +# +# Input: [2, 4, 6, 8, 10] +# +# Output: 7 +# +# Explanation: +# All arithmetic subsequence slices are: +# [2,4,6] +# [4,6,8] +# [6,8,10] +# [2,4,6,8] +# [4,6,8,10] +# [2,4,6,8,10] +# [2,6,10] + +import collections + +try: + xrange # Python 2 +except NameError: + xrange = range # Python 3 + + +class Solution(object): + def numberOfArithmeticSlices(self, A): + """ + :type A: List[int] + :rtype: int + """ + result = 0 + dp = [collections.defaultdict(int) for i in xrange(len(A))] + for i in xrange(1, len(A)): + for j in xrange(i): + diff = A[i]-A[j] + dp[i][diff] += 1 + if diff in dp[j]: + dp[i][diff] += dp[j][diff] + result += dp[j][diff] + return result diff --git a/Python/arithmetic-slices.py b/Python/arithmetic-slices.py new file mode 100644 index 000000000..bbbfe9fc8 --- /dev/null +++ b/Python/arithmetic-slices.py @@ -0,0 +1,45 @@ +# Time: O(n) +# Space: O(1) + +# A sequence of number is called arithmetic if it consists of at least three elements +# and if the difference between any two consecutive elements is the same. +# +# For example, these are arithmetic sequence: +# +# 1, 3, 5, 7, 9 +# 7, 7, 7, 7 +# 3, -1, -5, -9 +# The following sequence is not arithmetic. +# +# 1, 1, 2, 5, 7 +# +# A zero-indexed array A consisting of N numbers is given. A slice of that array is any pair +# of integers (P, Q) such that 0 <= P < Q < N. +# +# A slice (P, Q) of array A is called arithmetic if the sequence: +# A[P], A[p + 1], ..., A[Q - 1], A[Q] is arithmetic. In particular, this means that P + 1 < Q. +# +# The function should return the number of arithmetic slices in the array A. +# +# Example: +# +# A = [1, 2, 3, 4] +# +# return: 3, for 3 arithmetic slices in A: [1, 2, 3], [2, 3, 4] and [1, 2, 3, 4] itself. + + +class Solution(object): + def numberOfArithmeticSlices(self, A): + """ + :type A: List[int] + :rtype: int + """ + res, i = 0, 0 + while i+2 < len(A): + start = i + while i+2 < len(A) and A[i+2] + A[i] == 2*A[i+1]: + res += i - start + 1 + i += 1 + i += 1 + + return res diff --git a/Python/arranging-coins.py b/Python/arranging-coins.py new file mode 100644 index 000000000..16655bf91 --- /dev/null +++ b/Python/arranging-coins.py @@ -0,0 +1,62 @@ +# Time: O(logn) +# Space: O(1) + +# You have a total of n coins that you want to form in a staircase shape, +# where every k-th row must have exactly k coins. +# +# Given n, find the total number of full staircase rows that can be +# formed. +# +# n is a non-negative integer and fits within the range of a 32-bit +# signed integer. +# +# Example 1: +# +# n = 5 +# +# The coins can form the following rows: +# ¤ +# ¤ ¤ +# ¤ ¤ +# +# Because the 3rd row is incomplete, we return 2. +# Example 2: +# +# n = 8 +# +# The coins can form the following rows: +# ¤ +# ¤ ¤ +# ¤ ¤ ¤ +# ¤ ¤ +# +# Because the 4th row is incomplete, we return 3. + +import math + + +class Solution(object): + def arrangeCoins(self, n): + """ + :type n: int + :rtype: int + """ + return int((math.sqrt(8*n+1)-1) / 2) # sqrt is O(logn) time. + + +# Time: O(logn) +# Space: O(1) +class Solution2(object): + def arrangeCoins(self, n): + """ + :type n: int + :rtype: int + """ + left, right = 1, n + while left <= right: + mid = left + (right - left) / 2 + if 2 * n < mid * (mid+1): + right = mid - 1 + else: + left = mid + 1 + return left - 1 diff --git a/Python/array-nesting.py b/Python/array-nesting.py new file mode 100644 index 000000000..c29bbf5cd --- /dev/null +++ b/Python/array-nesting.py @@ -0,0 +1,46 @@ +# Time: O(n) +# Space: O(1) + +# A zero-indexed array A consisting of N different integers is given. +# The array contains all integers in the range [0, N - 1]. +# +# Sets S[K] for 0 <= K < N are defined as follows: +# +# S[K] = { A[K], A[A[K]], A[A[A[K]]], ... }. +# +# Sets S[K] are finite for each K and should NOT contain duplicates. +# +# Write a function that given an array A consisting of N integers, +# return the size of the largest set S[K] for this array. +# +# Example 1: +# Input: A = [5,4,0,3,1,6,2] +# Output: 4 +# Explanation: +# A[0] = 5, A[1] = 4, A[2] = 0, A[3] = 3, A[4] = 1, A[5] = 6, A[6] = 2. +# +# One of the longest S[K]: +# S[0] = {A[0], A[5], A[6], A[2]} = {5, 6, 2, 0} +# Note: +# N is an integer within the range [1, 20,000]. +# The elements of A are all distinct. +# Each element of array A is an integer within the range [0, N-1]. + + +class Solution(object): + def arrayNesting(self, nums): + """ + :type nums: List[int] + :rtype: int + """ + result = 0 + for num in nums: + if num is not None: + start, count = num, 0 + while nums[start] is not None: + temp = start + start = nums[start] + nums[temp] = None + count += 1 + result = max(result, count) + return result diff --git a/Python/array-partition-i.py b/Python/array-partition-i.py new file mode 100644 index 000000000..a1ce5c2a3 --- /dev/null +++ b/Python/array-partition-i.py @@ -0,0 +1,63 @@ +# Time: O(r), r is the range size of the integers +# Space: O(r) + +# Given an array of 2n integers, your task is to group these integers into n pairs of integer, +# say (a1, b1), (a2, b2), ..., (an, bn) which makes sum of min(ai, bi) for all i from 1 to n as large as possible. +# +# Example 1: +# Input: [1,4,3,2] +# +# Output: 4 +# Explanation: n is 2, and the maximum sum of pairs is 4. +# Note: +# n is a positive integer, which is in the range of [1, 10000]. +# All the integers in the array will be in the range of [-10000, 10000]. + +try: + xrange # Python 2 +except NameError: + xrange = range # Python 3 + + +class Solution(object): + def arrayPairSum(self, nums): + """ + :type nums: List[int] + :rtype: int + """ + LEFT, RIGHT = -10000, 10000 + lookup = [0] * (RIGHT-LEFT+1) + for num in nums: + lookup[num-LEFT] += 1 + r, result = 0, 0 + for i in xrange(LEFT, RIGHT+1): + result += (lookup[i-LEFT] + 1 - r) / 2 * i + r = (lookup[i-LEFT] + r) % 2 + return result + + +# Time: O(nlogn) +# Space: O(1) +class Solution2(object): + def arrayPairSum(self, nums): + """ + :type nums: List[int] + :rtype: int + """ + nums.sort() + result = 0 + for i in xrange(0, len(nums), 2): + result += nums[i] + return result + + +# Time: O(nlogn) +# Space: O(n) +class Solution3(object): + def arrayPairSum(self, nums): + """ + :type nums: List[int] + :rtype: int + """ + nums = sorted(nums) + return sum([nums[i] for i in range(0, len(nums), 2)]) diff --git a/Python/assign-cookies.py b/Python/assign-cookies.py new file mode 100644 index 000000000..5fc6aa07f --- /dev/null +++ b/Python/assign-cookies.py @@ -0,0 +1,64 @@ +# Time: O(nlogn) +# Space: O(1) + +# Assume you are an awesome parent and want to give your children some +# cookies. +# But, you should give each child at most one cookie. +# Each child i has a greed factor gi, +# which is the minimum size of a cookie that the child will +# be content with; +# and each cookie j has a size sj. If sj >= gi, we can assign +# the cookie j to the child i, +# and the child i will be content. +# Your goal is to maximize the number of your content children and +# output the maximum number. +# +# Note: +# You may assume the greed factor is always positive. +# You cannot assign more than one cookie to one child. +# +# Example 1: +# Input: [1,2,3], [1,1] +# +# Output: 1 +# +# Explanation: You have 3 children and 2 cookies. +# The greed factors of 3 children are 1, 2, 3. +# And even though you have 2 cookies, since their size is both 1, +# you could only make the child whose greed factor is 1 content. +# You need to output 1. +# Example 2: +# Input: [1,2], [1,2,3] +# +# Output: 2 +# +# Explanation: You have 2 children and 3 cookies. +# The greed factors of 2 children are 1, 2. +# You have 3 cookies and their sizes are big enough to gratify +# all of the children, +# You need to output 2. + +try: + xrange # Python 2 +except NameError: + xrange = range # Python 3 + + +class Solution(object): + def findContentChildren(self, g, s): + """ + :type g: List[int] + :type s: List[int] + :rtype: int + """ + g.sort() + s.sort() + + result, i = 0, 0 + for j in xrange(len(s)): + if i == len(g): + break + if s[j] >= g[i]: + result += 1 + i += 1 + return result diff --git a/Python/asteroid-collision.py b/Python/asteroid-collision.py new file mode 100644 index 000000000..670376b9d --- /dev/null +++ b/Python/asteroid-collision.py @@ -0,0 +1,71 @@ +# Time: O(n) +# Space: O(n) + +# We are given an array asteroids of integers representing asteroids +# in a row. +# +# For each asteroid, the absolute value represents its size, +# and the sign represents its direction (positive meaning right, +# negative meaning left). +# Each asteroid moves at the same speed. +# +# Find out the state of the asteroids after all collisions. +# If two asteroids meet, the smaller one will explode. +# If both are the same size, both will explode. +# Two asteroids moving in the same direction will never meet. +# +# Example 1: +# Input: +# asteroids = [5, 10, -5] +# Output: [5, 10] +# Explanation: +# The 10 and -5 collide resulting in 10. The 5 and 10 never collide. +# Example 2: +# Input: +# asteroids = [8, -8] +# Output: [] +# Explanation: +# The 8 and -8 collide exploding each other. +# Example 3: +# Input: +# asteroids = [10, 2, -5] +# Output: [10] +# Explanation: +# The 2 and -5 collide resulting in -5. The 10 and -5 collide resulting in 10. +# Example 4: +# Input: +# asteroids = [-2, -1, 1, 2] +# Output: [-2, -1, 1, 2] +# Explanation: +# The -2 and -1 are moving left, while the 1 and 2 are moving right. +# Asteroids moving the same direction never meet, +# so no asteroids will meet each other. +# +# Note: +# - The length of asteroids will be at most 10000. +# - Each asteroid will be a non-zero integer in the range [-1000, 1000]. + +try: + xrange # Python 2 +except NameError: + xrange = range # Python 3 + + +class Solution(object): + def asteroidCollision(self, asteroids): + """ + :type asteroids: List[int] + :rtype: List[int] + """ + result = [] + for asteroid in asteroids: + while result and asteroid < 0 < result[-1]: + if result[-1] < -asteroid: + result.pop() + continue + elif result[-1] == -asteroid: + result.pop() + break + else: + result.append(asteroid) + return result diff --git a/Python/average-of-levels-in-binary-tree.py b/Python/average-of-levels-in-binary-tree.py new file mode 100644 index 000000000..0834e2901 --- /dev/null +++ b/Python/average-of-levels-in-binary-tree.py @@ -0,0 +1,56 @@ +# Time: O(n) +# Space: O(h) + +# Given a non-empty binary tree, +# return the average value of the nodes on each level in the form of +# an array. +# +# Example 1: +# Input: +# 3 +# / \ +# 9 20 +# / \ +# 15 7 +# Output: [3, 14.5, 11] +# Explanation: +# The average value of nodes on level 0 is 3, +# on level 1 is 14.5, and on level 2 is 11. Hence return [3, 14.5, 11]. +# +# Note: +# The range of node's value is in the range of 32-bit signed integer. + +# Definition for a binary tree node. +# class TreeNode(object): +# def __init__(self, x): +# self.val = x +# self.left = None +# self.right = None + +try: + xrange # Python 2 +except NameError: + xrange = range # Python 3 + + +class Solution(object): + def averageOfLevels(self, root): + """ + :type root: TreeNode + :rtype: List[float] + """ + result = [] + q = [root] + while q: + total, count = 0, 0 + next_q = [] + for n in q: + total += n.val + count += 1 + if n.left: + next_q.append(n.left) + if n.right: + next_q.append(n.right) + q = next_q + result.append(float(total) / count) + return result diff --git a/Python/backspace-string-compare.py b/Python/backspace-string-compare.py new file mode 100644 index 000000000..4a3e4a603 --- /dev/null +++ b/Python/backspace-string-compare.py @@ -0,0 +1,59 @@ +# Time: O(m + n) +# Space: O(1) + +# Given two strings S and T, return if they are equal +# when both are typed into empty text editors. # means a backspace character. +# +# Example 1: +# +# Input: S = "ab#c", T = "ad#c" +# Output: true +# Explanation: Both S and T become "ac". +# Example 2: +# +# Input: S = "ab##", T = "c#d#" +# Output: true +# Explanation: Both S and T become "". +# Example 3: +# +# Input: S = "a##c", T = "#a#c" +# Output: true +# Explanation: Both S and T become "c". +# Example 4: +# +# Input: S = "a#c", T = "b" +# Output: false +# Explanation: S becomes "c" while T becomes "b". +# +# Note: +# - 1 <= S.length <= 200 +# - 1 <= T.length <= 200 +# - S and T only contain lowercase letters and '#' characters. + +import itertools + +try: + xrange # Python 2 +except NameError: + xrange = range # Python 3 + + +class Solution(object): + def backspaceCompare(self, S, T): + """ + :type S: str + :type T: str + :rtype: bool + """ + def findNextChar(S): + skip = 0 + for i in reversed(xrange(len(S))): + if S[i] == '#': + skip += 1 + elif skip: + skip -= 1 + else: + yield S[i] + + return all(x == y for x, y in + itertools.izip_longest(findNextChar(S), findNextChar(T))) diff --git a/Python/balanced-binary-tree.py b/Python/balanced-binary-tree.py index 532657698..c65c301ba 100644 --- a/Python/balanced-binary-tree.py +++ b/Python/balanced-binary-tree.py @@ -1,39 +1,34 @@ # Time: O(n) -# Space: O(logn) +# Space: O(h), h is height of binary tree # # Given a binary tree, determine if it is height-balanced. # -# For this problem, a height-balanced binary tree is defined as a binary tree -# in which the depth of the two subtrees of every node never differ by more than 1. +# For this problem, a height-balanced binary tree is defined as a binary +# tree +# in which the depth of the two subtrees of every node never differ by more +# than 1. # + # Definition for a binary tree node -class TreeNode: +class TreeNode(object): def __init__(self, x): self.val = x self.left = None self.right = None -class Solution: + +class Solution(object): # @param root, a tree node # @return a boolean def isBalanced(self, root): - return (self.getHeight(root) >= 0) - - def getHeight(self, root): - if root is None: - return 0 - left_height, right_height = self.getHeight(root.left), self.getHeight(root.right) - if left_height < 0 or right_height < 0 or abs(left_height - right_height) > 1: - return -1 - return max(left_height, right_height) + 1 - -if __name__ == "__main__": - root = TreeNode(0) - root.left = TreeNode(1) - result = Solution().isBalanced(root) - print result - - root.left.left = TreeNode(2) - result = Solution().isBalanced(root) - print result + def getHeight(root): + if root is None: + return 0 + left_height, right_height = \ + getHeight(root.left), getHeight(root.right) + if left_height < 0 or right_height < 0 or \ + abs(left_height - right_height) > 1: + return -1 + return max(left_height, right_height) + 1 + return (getHeight(root) >= 0) diff --git a/Python/base-7.py b/Python/base-7.py new file mode 100644 index 000000000..5fbc956a7 --- /dev/null +++ b/Python/base-7.py @@ -0,0 +1,36 @@ +# Time: O(1) +# Space: O(1) + +# Given an integer, return its base 7 string representation. +# +# Example 1: +# Input: 100 +# Output: "202" +# Example 2: +# Input: -7 +# Output: "-10" +# Note: The input will be in range of [-1e7, 1e7]. + + +class Solution(object): + def convertToBase7(self, num): + if num < 0: + return '-' + self.convertToBase7(-num) + result = '' + while num: + result = str(num % 7) + result + num //= 7 + return result if result else '0' + + +class Solution2(object): + def convertToBase7(self, num): + """ + :type num: int + :rtype: str + """ + if num < 0: + return '-' + self.convertToBase7(-num) + if num < 7: + return str(num) + return self.convertToBase7(num // 7) + str(num % 7) diff --git a/Python/baseball-game.py b/Python/baseball-game.py new file mode 100644 index 000000000..309e1a45f --- /dev/null +++ b/Python/baseball-game.py @@ -0,0 +1,71 @@ +# Time: O(n) +# Space: O(n) + +# You're now a baseball game point recorder. +# Given a list of strings, each string can be one of the 4 following types: +# +# 1. Integer (one round's score): Directly represents the number of points +# you get in this round. +# 2. "+" (one round's score): Represents that the points you get in +# this round are the sum of the last two valid +# round's points. +# 3. "D" (one round's score): Represents that the points you get in this round +# are the doubled data of the last valid round's +# points. +# 4. "C" (an operation, which isn't a round's score): Represents the last +# valid round's points you get were invalid and +# should be removed. +# +# Each round's operation is permanent and could have an impact on the round +# before and the round after. +# You need to return the sum of the points you could get in all the rounds. +# +# Example 1: +# +# Input: ["5","2","C","D","+"] +# Output: 30 +# Explanation: +# Round 1: You could get 5 points. The sum is: 5. +# Round 2: You could get 2 points. The sum is: 7. +# Operation 1: The round 2's data was invalid. The sum is: 5. +# Round 3: You could get 10 points (the round 2's data has been removed). +# The sum is: 15. +# Round 4: You could get 5 + 10 = 15 points. The sum is: 30. +# +# Example 2: +# +# Input: ["5","-2","4","C","D","9","+","+"] +# Output: 27 +# Explanation: +# Round 1: You could get 5 points. The sum is: 5. +# Round 2: You could get -2 points. The sum is: 3. +# Round 3: You could get 4 points. The sum is: 7. +# Operation 1: The round 3's data is invalid. The sum is: 3. +# Round 4: You could get -4 points (the round 3's data has been removed). +# The sum is: -1. +# Round 5: You could get 9 points. The sum is: 8. +# Round 6: You could get -4 + 9 = 5 points. The sum is 13. +# Round 7: You could get 9 + 5 = 14 points. The sum is 27. +# +# Note: +# The size of the input list will be between 1 and 1000. +# Every integer represented in the list will be between -30000 and 30000. + + +class Solution(object): + def calPoints(self, ops): + """ + :type ops: List[str] + :rtype: int + """ + history = [] + for op in ops: + if op == '+': + history.append(history[-1] + history[-2]) + elif op == 'D': + history.append(history[-1] * 2) + elif op == 'C': + history.pop() + else: + history.append(int(op)) + return sum(history) diff --git a/Python/basic-calculator-ii.py b/Python/basic-calculator-ii.py new file mode 100644 index 000000000..57dca8ff5 --- /dev/null +++ b/Python/basic-calculator-ii.py @@ -0,0 +1,64 @@ +# Time: O(n) +# Space: O(n) +# +# Implement a basic calculator to evaluate a simple expression string. +# +# The expression string contains only non-negative integers, +, -, *, / +# operators and empty spaces . The integer division should truncate toward +# zero. +# +# You may assume that the given expression is always valid. +# +# Some examples: +# "3+2*2" = 7 +# " 3/2 " = 1 +# " 3+5 / 2 " = 5 +# Note: Do not use the eval built-in library function. +# + +try: + xrange # Python 2 +except NameError: + xrange = range # Python 3 + + +class Solution: + # @param {string} s + # @return {integer} + def calculate(self, s): + operands, operators = [], [] + operand = "" + for i in reversed(xrange(len(s))): + if s[i].isdigit(): + operand += s[i] + if i == 0 or not s[i-1].isdigit(): + operands.append(int(operand[::-1])) + operand = "" + elif s[i] == ')' or s[i] == '*' or s[i] == '/': + operators.append(s[i]) + elif s[i] == '+' or s[i] == '-': + while operators and \ + (operators[-1] == '*' or operators[-1] == '/'): + self.compute(operands, operators) + operators.append(s[i]) + elif s[i] == '(': + while operators[-1] != ')': + self.compute(operands, operators) + operators.pop() + + while operators: + self.compute(operands, operators) + + return operands[-1] + + def compute(self, operands, operators): + left, right = operands.pop(), operands.pop() + op = operators.pop() + if op == '+': + operands.append(left + right) + elif op == '-': + operands.append(left - right) + elif op == '*': + operands.append(left * right) + elif op == '/': + operands.append(left / right) diff --git a/Python/basic-calculator-iii.py b/Python/basic-calculator-iii.py new file mode 100644 index 000000000..43c67b25a --- /dev/null +++ b/Python/basic-calculator-iii.py @@ -0,0 +1,72 @@ +# Time: O(n) +# Space: O(n) + +# Implement a basic calculator to evaluate a simple expression string. +# +# The expression string may contain open ( and closing parentheses ), +# the plus + or minus sign -, non-negative integers and empty spaces . +# +# The expression string contains only non-negative integers, +, -, *, / +# operators , +# open ( and closing parentheses ) and empty spaces . +# The integer division should truncate toward zero. +# +# You may assume that the given expression is always valid. +# +# Some examples: +# +# "1 + 1" = 2 +# " 6-4 / 2 " = 4 +# "2*(5+5*2)/3+(6/2+8)" = 21 +# "(2+6* 3+5- (3*14/7+2)*5)+3"=-12 +# +# Note: Do not use the eval built-in library function. + +try: + xrange # Python 2 +except NameError: + xrange = range # Python 3 + + +class Solution(object): + def calculate(self, s): + """ + :type s: str + :rtype: int + """ + operands, operators = [], [] + operand = "" + for i in reversed(xrange(len(s))): + if s[i].isdigit(): + operand += s[i] + if i == 0 or not s[i-1].isdigit(): + operands.append(int(operand[::-1])) + operand = "" + elif s[i] == ')' or s[i] == '*' or s[i] == '/': + operators.append(s[i]) + elif s[i] == '+' or s[i] == '-': + while operators and \ + (operators[-1] == '*' or operators[-1] == '/'): + self.compute(operands, operators) + operators.append(s[i]) + elif s[i] == '(': + while operators[-1] != ')': + self.compute(operands, operators) + operators.pop() + + while operators: + self.compute(operands, operators) + + return operands[-1] + + def compute(self, operands, operators): + left, right = operands.pop(), operands.pop() + op = operators.pop() + if op == '+': + operands.append(left + right) + elif op == '-': + operands.append(left - right) + elif op == '*': + operands.append(left * right) + elif op == '/': + operands.append(left / right) diff --git a/Python/basic-calculator-iv.py b/Python/basic-calculator-iv.py new file mode 100644 index 000000000..b8c1ac620 --- /dev/null +++ b/Python/basic-calculator-iv.py @@ -0,0 +1,188 @@ +# Time: +: O(d * t), t is the number of terms, +# d is the average degree of terms +# -: O(d * t) +# *: O(d * t^2) +# eval: O(d * t) +# to_list: O(d * tlogt) +# Space: O(e + d * t), e is the number of evalvars + +# Given an expression such as expression = "e + 8 - a + 5" and +# an evaluation map such as {"e": 1} +# (given in terms of evalvars = ["e"] and evalints = [1]), +# return a list of tokens representing the simplified expression, +# such as ["-1*a","14"] +# - An expression alternates chunks and symbols, +# with a space separating each chunk and symbol. +# - A chunk is either an expression in parentheses, a variable, +# or a non-negative integer. +# - A variable is a string of lowercase letters (not including digits.) +# Note that variables can be multiple letters, and note that variables never +# have a leading coefficient or unary operator like "2x" or "-x". +# +# Expressions are evaluated in the usual order: +# brackets first, then multiplication, then addition and subtraction. +# For example, expression = "1 + 2 * 3" has an answer of ["7"]. +# +# The format of the output is as follows: +# - For each term of free variables with non-zero coefficient, +# we write the free variables within a term in sorted order +# lexicographically. +# For example, we would never write a term like "b*a*c", only "a*b*c". +# - Terms have degree equal to the number of free variables being multiplied, +# counting multiplicity. (For example, "a*a*b*c" has degree 4.) +# We write the largest degree terms of our answer first, +# breaking ties by lexicographic order ignoring the leading coefficient of +# the term. +# - The leading coefficient of the term is placed directly to the left with an +# asterisk separating it from the variables (if they exist.) +# A leading coefficient of 1 is still printed. +# - An example of a well formatted answer is +# ["-2*a*a*a", "3*a*a*b", "3*b*b", "4*a", "5*c", "-6"] +# - Terms (including constant terms) with coefficient 0 are not included. +# For example, an expression of "0" has an output of []. +# +# Examples: +# +# Input: expression = "e + 8 - a + 5", evalvars = ["e"], evalints = [1] +# Output: ["-1*a","14"] +# +# Input: expression = "e - 8 + temperature - pressure", +# evalvars = ["e", "temperature"], evalints = [1, 12] +# Output: ["-1*pressure","5"] +# +# Input: expression = "(e + 8) * (e - 8)", evalvars = [], evalints = [] +# Output: ["1*e*e","-64"] +# +# Input: expression = "7 - 7", evalvars = [], evalints = [] +# Output: [] +# +# Input: expression = "a * b * c + b * a * c * 4", evalvars = [], evalints = [] +# Output: ["5*a*b*c"] +# +# Input: expression = +# "((a - b) * (b - c) + (c - a)) * ((a - b) + (b - c) * (c - a))", +# evalvars = [], evalints = [] +# Output: +# ["-1*a*a*b*b","2*a*a*b*c","-1*a*a*c*c","1*a*b*b*b","-1*a*b*b*c","-1*a*b*c*c", +# "1*a*c*c*c","-1*b*b*b*c","2*b*b*c*c","-1*b*c*c*c","2*a*a*b","-2*a*a*c","-2*a*b*b", +# "2*a*c*c","1*b*b*b","-1*b*b*c","1*b*c*c","-1*c*c*c","-1*a*a","1*a*b","1*a*c","-1*b*c"] +# +# Note: +# - expression will have length in range [1, 1000]. +# - evalvars, evalints will have equal lengths in range [0, 1000]. + +import collections +import itertools + +try: + xrange # Python 2 +except NameError: + xrange = range # Python 3 + + +class Poly(collections.Counter): + def __init__(self, expr=None): + if expr is None: + return + if expr.isdigit(): + self.update({(): int(expr)}) + else: + self[(expr,)] += 1 + + def __add__(self, other): + self.update(other) + return self + + def __sub__(self, other): + self.update({k: -v for k, v in other.items()}) + return self + + def __mul__(self, other): + def merge(k1, k2): + result = [] + i, j = 0, 0 + while i != len(k1) or j != len(k2): + if j == len(k2): + result.append(k1[i]) + i += 1 + elif i == len(k1): + result.append(k2[j]) + j += 1 + elif k1[i] < k2[j]: + result.append(k1[i]) + i += 1 + else: + result.append(k2[j]) + j += 1 + return result + + result = Poly() + for k1, v1 in self.items(): + for k2, v2 in other.items(): + result.update({tuple(merge(k1, k2)): v1*v2}) + return result + + def eval(self, lookup): + result = Poly() + for polies, c in self.items(): + key = [] + for var in polies: + if var in lookup: + c *= lookup[var] + else: + key.append(var) + result[tuple(key)] += c + return result + + def to_list(self): + return ["*".join((str(v),) + k) + for k, v in sorted(self.items(), + key=lambda x: (-len(x[0]), x[0])) + if v] + + +class Solution(object): + def basicCalculatorIV(self, expression, evalvars, evalints): + """ + :type expression: str + :type evalvars: List[str] + :type evalints: List[int] + :rtype: List[str] + """ + def compute(operands, operators): + left, right = operands.pop(), operands.pop() + op = operators.pop() + if op == '+': + operands.append(left + right) + elif op == '-': + operands.append(left - right) + elif op == '*': + operands.append(left * right) + + def parse(s): + if not s: + return Poly() + operands, operators = [], [] + operand = "" + for i in reversed(xrange(len(s))): + if s[i].isalnum(): + operand += s[i] + if i == 0 or not s[i-1].isalnum(): + operands.append(Poly(operand[::-1])) + operand = "" + elif s[i] == ')' or s[i] == '*': + operators.append(s[i]) + elif s[i] == '+' or s[i] == '-': + while operators and operators[-1] == '*': + compute(operands, operators) + operators.append(s[i]) + elif s[i] == '(': + while operators[-1] != ')': + compute(operands, operators) + operators.pop() + while operators: + compute(operands, operators) + return operands[-1] + + lookup = dict(itertools.izip(evalvars, evalints)) + return parse(expression).eval(lookup).to_list() diff --git a/Python/basic-calculator.py b/Python/basic-calculator.py new file mode 100644 index 000000000..21cb994d9 --- /dev/null +++ b/Python/basic-calculator.py @@ -0,0 +1,53 @@ +# Time: O(n) +# Space: O(n) +# +# Implement a basic calculator to evaluate a simple expression string. +# +# The expression string may contain open ( and closing parentheses ), +# the plus + or minus sign -, non-negative integers and empty spaces . +# +# You may assume that the given expression is always valid. +# +# Some examples: +# "1 + 1" = 2 +# " 2-1 + 2 " = 3 +# "(1+(4+5+2)-3)+(6+8)" = 23 +# + +try: + xrange # Python 2 +except NameError: + xrange = range # Python 3 + + +class Solution: + # @param {string} s + # @return {integer} + def calculate(self, s): + operands, operators = [], [] + operand = "" + for i in reversed(xrange(len(s))): + if s[i].isdigit(): + operand += s[i] + if i == 0 or not s[i-1].isdigit(): + operands.append(int(operand[::-1])) + operand = "" + elif s[i] == ')' or s[i] == '+' or s[i] == '-': + operators.append(s[i]) + elif s[i] == '(': + while operators[-1] != ')': + self.compute(operands, operators) + operators.pop() + + while operators: + self.compute(operands, operators) + + return operands[-1] + + def compute(self, operands, operators): + left, right = operands.pop(), operands.pop() + op = operators.pop() + if op == '+': + operands.append(left + right) + elif op == '-': + operands.append(left - right) diff --git a/Python/battleships-in-a-board.py b/Python/battleships-in-a-board.py new file mode 100644 index 000000000..23ccf5273 --- /dev/null +++ b/Python/battleships-in-a-board.py @@ -0,0 +1,51 @@ +# Time: O(m * n) +# Space: O(1) + +# Given an 2D board, count how many different battleships are in it. +# The battleships are represented with 'X's, empty slots are represented with +# '.'s. +# You may assume the following rules: +# +# You receive a valid board, made of only battleships or empty slots. +# Battleships can only be placed horizontally or vertically. In other words, +# they can only be made of the shape 1xN (1 row, N columns) or Nx1 +# (N rows, 1 column), +# where N can be of any size. +# At least one horizontal or vertical cell separates between two battleships - +# there are no adjacent battleships. +# +# Example: +# X..X +# ...X +# ...X +# In the above board there are 2 battleships. +# Invalid Example: +# ...X +# XXXX +# ...X +# This is not a valid board - as battleships will always have a cell +# separating between them. +# Your algorithm should not modify the value of the board. + +try: + xrange # Python 2 +except NameError: + xrange = range # Python 3 + + +class Solution(object): + def countBattleships(self, board): + """ + :type board: List[List[str]] + :rtype: int + """ + if not board or not board[0]: + return 0 + + cnt = 0 + for i in xrange(len(board)): + for j in xrange(len(board[0])): + cnt += int(board[i][j] == 'X' and + (i == 0 or board[i - 1][j] != 'X') and + (j == 0 or board[i][j - 1] != 'X')) + return cnt diff --git a/Python/beautiful-arrangement-ii.py b/Python/beautiful-arrangement-ii.py new file mode 100644 index 000000000..8fe260d8e --- /dev/null +++ b/Python/beautiful-arrangement-ii.py @@ -0,0 +1,48 @@ +# Time: O(n) +# Space: O(1) + +# Given two integers n and k, +# you need to construct a list which contains n different positive integers +# ranging from 1 to n and obeys the following requirement: +# Suppose this list is [a1, a2, a3, ... , an], +# then the list [|a1 - a2|, |a2 - a3|, |a3 - a4|, ... , |an-1 - an|] has +# exactly k distinct integers. +# +# If there are multiple answers, print any of them. +# +# Example 1: +# Input: n = 3, k = 1 +# Output: [1, 2, 3] +# Explanation: The [1, 2, 3] has three different positive integers ranging +# from 1 to 3, and the [1, 1] has exactly 1 distinct integer: 1. +# +# Example 2: +# Input: n = 3, k = 2 +# Output: [1, 3, 2] +# Explanation: The [1, 3, 2] has three different positive integers ranging +# from 1 to 3, +# and the [2, 1] has exactly 2 distinct integers: 1 and 2. +# +# Note: +# The n and k are in the range 1 <= k < n <= 10^4. + + +class Solution(object): + def constructArray(self, n, k): + """ + :type n: int + :type k: int + :rtype: List[int] + """ + result = [] + left, right = 1, n + while left <= right: + if k % 2: + result.append(left) + left += 1 + else: + result.append(right) + right -= 1 + if k > 1: + k -= 1 + return result diff --git a/Python/beautiful-arrangement.py b/Python/beautiful-arrangement.py new file mode 100644 index 000000000..8110e4b80 --- /dev/null +++ b/Python/beautiful-arrangement.py @@ -0,0 +1,56 @@ +# Time: O(n!) +# Space: O(n) + +# Suppose you have N integers from 1 to N. +# We define a beautiful arrangement as an array that is constructed by +# these N numbers successfully +# if one of the following is true for +# the ith position (1 <= i <= N) in this array: +# +# The number at the ith position is divisible by i. +# i is divisible by the number at the ith position. +# Now given N, how many beautiful arrangements can you construct? +# +# Example 1: +# Input: 2 +# Output: 2 +# Explanation: +# +# The first beautiful arrangement is [1, 2]: +# +# Number at the 1st position (i=1) is 1, and 1 is divisible by i (i=1). +# +# Number at the 2nd position (i=2) is 2, and 2 is divisible by i (i=2). +# +# The second beautiful arrangement is [2, 1]: +# +# Number at the 1st position (i=1) is 2, and 2 is divisible by i (i=1). +# +# Number at the 2nd position (i=2) is 1, and i (i=2) is divisible by 1. +# Note: +# N is a positive integer and will not exceed 15. + +try: + xrange # Python 2 +except NameError: + xrange = range # Python 3 + + +class Solution(object): + def countArrangement(self, N): + """ + :type N: int + :rtype: int + """ + def countArrangementHelper(n, arr): + if n <= 0: + return 1 + count = 0 + for i in xrange(n): + if arr[i] % n == 0 or n % arr[i] == 0: + arr[i], arr[n-1] = arr[n-1], arr[i] + count += countArrangementHelper(n - 1, arr) + arr[i], arr[n-1] = arr[n-1], arr[i] + return count + + return countArrangementHelper(N, range(1, N+1)) diff --git a/Python/best-meeting-point.py b/Python/best-meeting-point.py new file mode 100644 index 000000000..0a3333eaa --- /dev/null +++ b/Python/best-meeting-point.py @@ -0,0 +1,50 @@ +# Time: O(m * n) +# Space: O(m + n) + +from random import randint + +try: + xrange # Python 2 +except NameError: + xrange = range # Python 3 + + +class Solution(object): + def minTotalDistance(self, grid): + """ + :type grid: List[List[int]] + :rtype: int + """ + x = [i for i, row in enumerate(grid) for v in row if v == 1] + y = [j for row in grid for j, v in enumerate(row) if v == 1] + mid_x = self.findKthLargest(x, len(x) / 2 + 1) + mid_y = self.findKthLargest(y, len(y) / 2 + 1) + + return sum([abs(mid_x-i) + abs(mid_y-j) + for i, row in enumerate(grid) + for j, v in enumerate(row) if v == 1]) + + def findKthLargest(self, nums, k): + left, right = 0, len(nums) - 1 + while left <= right: + pivot_idx = randint(left, right) + new_pivot_idx = self.PartitionAroundPivot(left, right, + pivot_idx, nums) + if new_pivot_idx == k - 1: + return nums[new_pivot_idx] + elif new_pivot_idx > k - 1: + right = new_pivot_idx - 1 + else: # new_pivot_idx < k - 1. + left = new_pivot_idx + 1 + + def PartitionAroundPivot(self, left, right, pivot_idx, nums): + pivot_value = nums[pivot_idx] + new_pivot_idx = left + nums[pivot_idx], nums[right] = nums[right], nums[pivot_idx] + for i in xrange(left, right): + if nums[i] > pivot_value: + nums[i], nums[new_pivot_idx] = nums[new_pivot_idx], nums[i] + new_pivot_idx += 1 + + nums[right], nums[new_pivot_idx] = nums[new_pivot_idx], nums[right] + return new_pivot_idx diff --git a/Python/best-time-to-buy-and-sell-stock-ii.py b/Python/best-time-to-buy-and-sell-stock-ii.py index 5cbdc9a23..fcd3ee4d1 100644 --- a/Python/best-time-to-buy-and-sell-stock-ii.py +++ b/Python/best-time-to-buy-and-sell-stock-ii.py @@ -1,15 +1,20 @@ # Time: O(n) # Space: O(1) # -# Say you have an array for which the ith element is +# Say you have an array for which the ith element is # the price of a given stock on day i. # -# Design an algorithm to find the maximum profit. -# You may complete as many transactions as you like -# (ie, buy one and sell one share of the stock multiple times). -# However, you may not engage in multiple transactions at the same time +# Design an algorithm to find the maximum profit. +# You may complete as many transactions as you like +# (ie, buy one and sell one share of the stock multiple times). +# However, you may not engage in multiple transactions at the same time # (ie, you must sell the stock before you buy again). -# + +try: + xrange # Python 2 +except NameError: + xrange = range # Python 3 + class Solution: # @param prices, a list of integer @@ -17,10 +22,9 @@ class Solution: def maxProfit(self, prices): profit = 0 for i in xrange(len(prices) - 1): - profit += max(0, prices[i + 1] - prices[i]) + profit += max(0, prices[i + 1] - prices[i]) return profit -if __name__ == "__main__": - result = Solution().maxProfit([3, 2, 1, 4, 2, 5, 6]) - print result - + def maxProfit2(self, prices): + return sum(map(lambda x: max(prices[x + 1] - prices[x], 0), + xrange(len(prices[:-1])))) diff --git a/Python/best-time-to-buy-and-sell-stock-iii.py b/Python/best-time-to-buy-and-sell-stock-iii.py index 7b08da290..d7e002f73 100644 --- a/Python/best-time-to-buy-and-sell-stock-iii.py +++ b/Python/best-time-to-buy-and-sell-stock-iii.py @@ -1,39 +1,81 @@ # Time: O(n) # Space: O(1) # -# Say you have an array for which the ith element +# Say you have an array for which the ith element # is the price of a given stock on day i. # -# Design an algorithm to find the maximum profit. +# Design an algorithm to find the maximum profit. # You may complete at most two transactions. # # Note: -# You may not engage in multiple transactions at the same time +# You may not engage in multiple transactions at the same time # (ie, you must sell the stock before you buy again). # -class Solution: +try: + xrange # Python 2 +except NameError: + xrange = range # Python 3 + + +class Solution(object): + # @param prices, a list of integer + # @return an integer + def maxProfit(self, prices): + hold1, hold2 = float("-inf"), float("-inf") + release1, release2 = 0, 0 + for i in prices: + release2 = max(release2, hold2 + i) + hold2 = max(hold2, release1 - i) + release1 = max(release1, hold1 + i) + hold1 = max(hold1, -i) + return release2 + + +# Time: O(k * n) +# Space: O(k) +class Solution2(object): # @param prices, a list of integer # @return an integer def maxProfit(self, prices): - min_price, max_profit_from_left, max_profits_from_left = float("inf"), 0, [] + return self.maxAtMostKPairsProfit(prices, 2) + + def maxAtMostKPairsProfit(self, prices, k): + max_buy = [float("-inf") for _ in xrange(k + 1)] + max_sell = [0 for _ in xrange(k + 1)] + + for i in xrange(len(prices)): + for j in xrange(1, min(k, i/2+1) + 1): + max_buy[j] = max(max_buy[j], max_sell[j-1] - prices[i]) + max_sell[j] = max(max_sell[j], max_buy[j] + prices[i]) + + return max_sell[k] + + +# Time: O(n) +# Space: O(n) +class Solution3(object): + # @param prices, a list of integer + # @return an integer + def maxProfit(self, prices): + min_price, max_profit_from_left, max_profits_from_left = \ + float("inf"), 0, [] for price in prices: min_price = min(min_price, price) max_profit_from_left = max(max_profit_from_left, price - min_price) max_profits_from_left.append(max_profit_from_left) - + max_price, max_profit_from_right, max_profits_from_right = 0, 0, [] for i in reversed(range(len(prices))): max_price = max(max_price, prices[i]) - max_profit_from_right = max(max_profit_from_right, max_price - prices[i]) + max_profit_from_right = max(max_profit_from_right, + max_price - prices[i]) max_profits_from_right.insert(0, max_profit_from_right) - + max_profit = 0 for i in range(len(prices)): - max_profit = max(max_profit, max_profits_from_left[i] + max_profits_from_right[i]) - + max_profit = max(max_profit, + max_profits_from_left[i] + + max_profits_from_right[i]) + return max_profit - -if __name__ == "__main__": - result = Solution().maxProfit([3, 2, 1, 4, 2, 5, 6]) - print result diff --git a/Python/best-time-to-buy-and-sell-stock-iv.py b/Python/best-time-to-buy-and-sell-stock-iv.py new file mode 100644 index 000000000..e69119480 --- /dev/null +++ b/Python/best-time-to-buy-and-sell-stock-iv.py @@ -0,0 +1,44 @@ +# Time: O(k * n) +# Space: O(k) +# +# Say you have an array for which the ith element is +# the price of a given stock on day i. +# +# Design an algorithm to find the maximum profit. +# You may complete at most k transactions. +# +# Note: +# You may not engage in multiple transactions at the same time +# (ie, you must sell the stock before you buy again). +# + +try: + xrange # Python 2 +except NameError: + xrange = range # Python 3 + + +class Solution(object): + # @return an integer as the maximum profit + def maxProfit(self, k, prices): + if k >= len(prices) / 2: + return self.maxAtMostNPairsProfit(prices) + + return self.maxAtMostKPairsProfit(prices, k) + + def maxAtMostNPairsProfit(self, prices): + profit = 0 + for i in xrange(len(prices) - 1): + profit += max(0, prices[i + 1] - prices[i]) + return profit + + def maxAtMostKPairsProfit(self, prices, k): + max_buy = [float("-inf") for _ in xrange(k + 1)] + max_sell = [0 for _ in xrange(k + 1)] + + for i in xrange(len(prices)): + for j in xrange(1, min(k, i/2+1) + 1): + max_buy[j] = max(max_buy[j], max_sell[j-1] - prices[i]) + max_sell[j] = max(max_sell[j], max_buy[j] + prices[i]) + + return max_sell[k] diff --git a/Python/best-time-to-buy-and-sell-stock-with-cooldown.py b/Python/best-time-to-buy-and-sell-stock-with-cooldown.py new file mode 100644 index 000000000..23ecb097e --- /dev/null +++ b/Python/best-time-to-buy-and-sell-stock-with-cooldown.py @@ -0,0 +1,47 @@ +# Time: O(n) +# Space: O(1) + +# Say you have an array for which the ith element is the price of +# a given stock on day i. +# +# Design an algorithm to find the maximum profit. You may complete as +# many transactions as you like (ie, buy one and sell one share of the +# stock multiple times) with the following restrictions: +# +# You may not engage in multiple transactions at the same time +# (ie, you must sell the stock before you buy again). +# After you sell your stock, you cannot buy stock on next day. +# (ie, cooldown 1 day) +# Example: +# +# prices = [1, 2, 3, 0, 2] +# maxProfit = 3 +# transactions = [buy, sell, cooldown, buy, sell] +# + +try: + xrange # Python 2 +except NameError: + xrange = range # Python 3 + + +class Solution(object): + def maxProfit(self, prices): + """ + :type prices: List[int] + :rtype: int + """ + if not prices: + return 0 + buy, sell, coolDown = [0] * 2, [0] * 2, [0] * 2 + buy[0] = -prices[0] + for i in xrange(1, len(prices)): + # Bought before or buy today. + buy[i % 2] = max(buy[(i - 1) % 2], + coolDown[(i - 1) % 2] - prices[i]) + # Sell today. + sell[i % 2] = buy[(i - 1) % 2] + prices[i] + # Sold before yesterday or sold yesterday. + coolDown[i % 2] = max(coolDown[(i - 1) % 2], sell[(i - 1) % 2]) + return max(coolDown[(len(prices) - 1) % 2], + sell[(len(prices) - 1) % 2]) diff --git a/Python/best-time-to-buy-and-sell-stock-with-transaction-fee.py b/Python/best-time-to-buy-and-sell-stock-with-transaction-fee.py new file mode 100644 index 000000000..e97c4038f --- /dev/null +++ b/Python/best-time-to-buy-and-sell-stock-with-transaction-fee.py @@ -0,0 +1,47 @@ +# Time: O(n) +# Space: O(1) + +# Your are given an array of integers prices, +# for which the i-th element is the price of a given stock on day i; +# and a non-negative integer fee representing a transaction fee. +# +# You may complete as many transactions as you like, +# but you need to pay the transaction fee for each transaction. +# You may not buy more than 1 share of a stock at a time +# (ie. you must sell the stock share before you buy again.) +# +# Return the maximum profit you can make. +# +# Example 1: +# Input: prices = [1, 3, 2, 8, 4, 9], fee = 2 +# Output: 8 +# Explanation: The maximum profit can be achieved by: +# Buying at prices[0] = 1 +# Selling at prices[3] = 8 +# Buying at prices[4] = 4 +# Selling at prices[5] = 9 +# The total profit is ((8 - 1) - 2) + ((9 - 4) - 2) = 8. +# +# Note: +# - 0 < prices.length <= 50000. +# - 0 < prices[i] < 50000. +# - 0 <= fee < 50000. + +try: + xrange # Python 2 +except NameError: + xrange = range # Python 3 + + +class Solution(object): + def maxProfit(self, prices, fee): + """ + :type prices: List[int] + :type fee: int + :rtype: int + """ + cash, hold = 0, -prices[0] + for i in xrange(1, len(prices)): + cash = max(cash, hold+prices[i]-fee) + hold = max(hold, cash-prices[i]) + return cash diff --git a/Python/best-time-to-buy-and-sell-stock.py b/Python/best-time-to-buy-and-sell-stock.py index 2c86c4a74..4d94fa9ae 100644 --- a/Python/best-time-to-buy-and-sell-stock.py +++ b/Python/best-time-to-buy-and-sell-stock.py @@ -1,25 +1,21 @@ # Time: O(n) # Space: O(1) # -# Say you have an array for which the ith element +# Say you have an array for which the ith element # is the price of a given stock on day i. # -# If you were only permitted to complete at most one transaction -# (ie, buy one and sell one share of the stock), +# If you were only permitted to complete at most one transaction +# (ie, buy one and sell one share of the stock), # design an algorithm to find the maximum profit. # -class Solution: + +class Solution(object): # @param prices, a list of integer # @return an integer def maxProfit(self, prices): max_profit, min_price = 0, float("inf") for price in prices: min_price = min(min_price, price) - max_profit = max(max_profit, price - min_price) + max_profit = max(max_profit, price - min_price) return max_profit - -if __name__ == "__main__": - result = Solution().maxProfit([3, 2, 1, 4, 2, 5, 6]) - print result - diff --git a/Python/binary-gap.py b/Python/binary-gap.py new file mode 100644 index 000000000..3e07b0043 --- /dev/null +++ b/Python/binary-gap.py @@ -0,0 +1,57 @@ +# Time: O(logn) = O(1) due to n is a 32-bit number +# Space: O(1) + +# Given a positive integer N, find and return the longest distance +# between two consecutive 1's in the binary representation of N. +# +# If there aren't two consecutive 1's, return 0. +# +# Example 1: +# +# Input: 22 +# Output: 2 +# Explanation: +# 22 in binary is 0b10110. +# In the binary representation of 22, there are three ones, +# and two consecutive pairs of 1's. +# The first consecutive pair of 1's have distance 2. +# The second consecutive pair of 1's have distance 1. +# The answer is the largest of these two distances, which is 2. +# Example 2: +# +# Input: 5 +# Output: 2 +# Explanation: +# 5 in binary is 0b101. +# Example 3: +# +# Input: 6 +# Output: 1 +# Explanation: +# 6 in binary is 0b110. +# Example 4: +# +# Input: 8 +# Output: 0 +# Explanation: +# 8 in binary is 0b1000. +# There aren't any consecutive pairs of 1's +# in the binary representation of 8, so we return 0. +# +# Note: +# - 1 <= N <= 10^9 + +class Solution(object): + def binaryGap(self, N): + """ + :type N: int + :rtype: int + """ + result = 0 + last = None + for i in xrange(32): + if (N >> i) & 1: + if last is not None: + result = max(result, i-last) + last = i + return result diff --git a/Python/binary-number-with-alternating-bits.py b/Python/binary-number-with-alternating-bits.py new file mode 100644 index 000000000..cf9d838ed --- /dev/null +++ b/Python/binary-number-with-alternating-bits.py @@ -0,0 +1,43 @@ +# Time: O(1) +# Space: O(1) + +# Given a positive integer, check whether it has alternating bits: +# namely, if two adjacent bits will always have different values. +# +# Example 1: +# Input: 5 +# Output: True +# Explanation: +# The binary representation of 5 is: 101 +# +# Example 2: +# Input: 7 +# Output: False +# Explanation: +# The binary representation of 7 is: 111. +# +# Example 3: +# Input: 11 +# Output: False +# Explanation: +# The binary representation of 11 is: 1011. +# +# Example 4: +# Input: 10 +# Output: True +# Explanation: +# The binary representation of 10 is: 1010. + + +class Solution(object): + def hasAlternatingBits(self, n): + """ + :type n: int + :rtype: bool + """ + n, curr = divmod(n, 2) + while n > 0: + if curr == n % 2: + return False + n, curr = divmod(n, 2) + return True diff --git a/Python/binary-search-tree-iterator.py b/Python/binary-search-tree-iterator.py index c198f87ec..525ec44fe 100644 --- a/Python/binary-search-tree-iterator.py +++ b/Python/binary-search-tree-iterator.py @@ -1,23 +1,25 @@ # Time: O(1) -# Space: O(logn) -# +# Space: O(h), h is height of binary tree +# # Implement an iterator over a binary search tree (BST). # Your iterator will be initialized with the root node of a BST. -# +# # Calling next() will return the next smallest number in the BST. -# +# # Note: next() and hasNext() should run in average O(1) time # and uses O(h) memory, where h is the height of the tree. # + # Definition for a binary tree node -class TreeNode: +class TreeNode(object): def __init__(self, x): self.val = x self.left = None self.right = None -class BSTIterator: + +class BSTIterator(object): # @param root, a binary search tree's root node def __init__(self, root): self.stack = [] @@ -32,19 +34,9 @@ def next(self): while self.cur: self.stack.append(self.cur) self.cur = self.cur.left - + self.cur = self.stack.pop() node = self.cur self.cur = self.cur.right - + return node.val - -if __name__ == "__main__": - root = TreeNode(2) - root.left = TreeNode(1) - - # Your BSTIterator will be called like this: - i, v = BSTIterator(root), [] - while i.hasNext(): v.append(i.next()) - - print v \ No newline at end of file diff --git a/Python/binary-search.py b/Python/binary-search.py new file mode 100644 index 000000000..315b30ec7 --- /dev/null +++ b/Python/binary-search.py @@ -0,0 +1,42 @@ +# Time: O(logn) +# Space: O(1) + +# Given a sorted (in ascending order) integer array nums of n elements +# and a target value, write a function to search target in nums. +# If target exists, then return its index, otherwise return -1. +# +# Example 1: +# +# Input: nums = [-1,0,3,5,9,12], target = 9 +# Output: 4 +# Explanation: 9 exists in nums and its index is 4 +# +# Example 2: +# +# Input: nums = [-1,0,3,5,9,12], target = 2 +# Output: -1 +# Explanation: 2 does not exist in nums so return -1 +# +# Note: +# - You may assume that all elements in nums are unique. +# - n will be in the range [1, 10000]. +# - The value of each element in nums will be in the range [-9999, 9999]. + + +class Solution(object): + def search(self, nums, target): + """ + :type nums: List[int] + :type target: int + :rtype: int + """ + left, right = 0, len(nums)-1 + while left <= right: + mid = left + (right-left)//2 + if nums[mid] > target: + right = mid-1 + elif nums[mid] < target: + left = mid+1 + else: + return mid + return -1 diff --git a/Python/binary-tree-inorder-traversal.py b/Python/binary-tree-inorder-traversal.py index 5972f569b..05f925fb8 100644 --- a/Python/binary-tree-inorder-traversal.py +++ b/Python/binary-tree-inorder-traversal.py @@ -15,66 +15,61 @@ # Note: Recursive solution is trivial, could you do it iteratively? # + # Definition for a binary tree node -class TreeNode: +class TreeNode(object): def __init__(self, x): self.val = x self.left = None self.right = None + # Morris Traversal Solution -class Solution: - # @param root, a tree node - # @return a list of integers +class Solution(object): def inorderTraversal(self, root): - result, prev, cur = [], None, root - while cur: - if cur.left is None: - result.append(cur.val) - prev = cur - cur = cur.right + """ + :type root: TreeNode + :rtype: List[int] + """ + result, curr = [], root + while curr: + if curr.left is None: + result.append(curr.val) + curr = curr.right else: - node = cur.left - while node.right and node.right != cur: + node = curr.left + while node.right and node.right != curr: node = node.right - + if node.right is None: - node.right = cur - cur = cur.left + node.right = curr + curr = curr.left else: - result.append(cur.val) + result.append(curr.val) node.right = None - prev = cur - cur = cur.right - + curr = curr.right + return result + # Time: O(n) -# Space: O(n) -# Stack Solution -class Solution2: - # @param root, a tree node - # @return a list of integers +# Space: O(h) +# Stack Solution +class Solution2(object): def inorderTraversal(self, root): - result, stack, current, last_traversed = [], [], root, None - while stack or current: - if current: - stack.append(current) - current = current.left + """ + :type root: TreeNode + :rtype: List[int] + """ + result, stack = [], [(root, False)] + while stack: + root, is_visited = stack.pop() + if root is None: + continue + if is_visited: + result.append(root.val) else: - parent = stack[-1] - if parent.right in (None, last_traversed): - if parent.right is None: - result.append(parent.val) - last_traversed= stack.pop() - else: - result.append(parent.val) - current = parent.right + stack.append((root.right, False)) + stack.append((root, True)) + stack.append((root.left, False)) return result - -if __name__ == "__main__": - root = TreeNode(1) - root.right = TreeNode(2) - root.right.left = TreeNode(3) - result = Solution().inorderTraversal(root) - print result \ No newline at end of file diff --git a/Python/binary-tree-level-order-traversal-ii.py b/Python/binary-tree-level-order-traversal-ii.py index 07e9995bc..4989e83f8 100644 --- a/Python/binary-tree-level-order-traversal-ii.py +++ b/Python/binary-tree-level-order-traversal-ii.py @@ -1,9 +1,10 @@ # Time: O(n) # Space: O(n) -# -# Given a binary tree, return the bottom-up level order traversal of its nodes' values. + +# Given a binary tree, return the bottom-up level order traversal +# of its nodes' values. # (ie, from left to right, level by level from leaf to root). -# +# # For example: # Given binary tree {3,9,20,#,#,15,7}, # 3 @@ -17,20 +18,25 @@ # [9,20], # [3] # ] -# + + # Definition for a binary tree node -class TreeNode: +class TreeNode(object): def __init__(self, x): self.val = x self.left = None self.right = None -class Solution: - # @param root, a tree node - # @return a list of lists of integers + +class Solution(object): def levelOrderBottom(self, root): + """ + :type root: TreeNode + :rtype: List[List[int]] + """ if root is None: return [] + result, current = [], [root] while current: next_level, vals = [], [] @@ -41,14 +47,6 @@ def levelOrderBottom(self, root): if node.right: next_level.append(node.right) current = next_level - result.insert(0, vals) - return result + result.append(vals) -if __name__ == "__main__": - root = TreeNode(3) - root.left = TreeNode(9) - root.right = TreeNode(20) - root.right.left = TreeNode(15) - root.right.right = TreeNode(7) - result = Solution().levelOrderBottom(root) - print result \ No newline at end of file + return result[::-1] diff --git a/Python/binary-tree-level-order-traversal.py b/Python/binary-tree-level-order-traversal.py index f1b8c9c47..218219e60 100644 --- a/Python/binary-tree-level-order-traversal.py +++ b/Python/binary-tree-level-order-traversal.py @@ -1,7 +1,7 @@ # Time: O(n) # Space: O(n) # -# Given a binary tree, return the level order traversal of its nodes' values. +# Given a binary tree, return the level order traversal of its nodes' values. # (ie, from left to right, level by level). # # For example: @@ -18,14 +18,17 @@ # [15,7] # ] # + + # Definition for a binary tree node -class TreeNode: +class TreeNode(object): def __init__(self, x): self.val = x self.left = None self.right = None -class Solution: + +class Solution(object): # @param root, a tree node # @return a list of lists of integers def levelOrder(self, root): @@ -43,12 +46,3 @@ def levelOrder(self, root): current = next_level result.append(vals) return result - -if __name__ == "__main__": - root = TreeNode(3) - root.left = TreeNode(9) - root.right = TreeNode(20) - root.right.left = TreeNode(15) - root.right.right = TreeNode(7) - result = Solution().levelOrder(root) - print result \ No newline at end of file diff --git a/Python/binary-tree-longest-consecutive-sequence-ii.py b/Python/binary-tree-longest-consecutive-sequence-ii.py new file mode 100644 index 000000000..d1c9b3018 --- /dev/null +++ b/Python/binary-tree-longest-consecutive-sequence-ii.py @@ -0,0 +1,39 @@ +# Time: O(n) +# Space: O(h) + +# Definition for a binary tree node. +# class TreeNode(object): +# def __init__(self, x): +# self.val = x +# self.left = None +# self.right = None + + +class Solution(object): + def longestConsecutive(self, root): + """ + :type root: TreeNode + :rtype: int + """ + def longestConsecutiveHelper(root): + if not root: + return 0, 0 + left_len = longestConsecutiveHelper(root.left) + right_len = longestConsecutiveHelper(root.right) + cur_inc_len, cur_dec_len = 1, 1 + if root.left: + if root.left.val == root.val + 1: + cur_inc_len = max(cur_inc_len, left_len[0] + 1) + elif root.left.val == root.val - 1: + cur_dec_len = max(cur_dec_len, left_len[1] + 1) + if root.right: + if root.right.val == root.val + 1: + cur_inc_len = max(cur_inc_len, right_len[0] + 1) + elif root.right.val == root.val - 1: + cur_dec_len = max(cur_dec_len, right_len[1] + 1) + self.max_len = max(self.max_len, cur_dec_len + cur_inc_len - 1) + return cur_inc_len, cur_dec_len + + self.max_len = 0 + longestConsecutiveHelper(root) + return self.max_len diff --git a/Python/binary-tree-longest-consecutive-sequence.py b/Python/binary-tree-longest-consecutive-sequence.py new file mode 100644 index 000000000..aa75421c2 --- /dev/null +++ b/Python/binary-tree-longest-consecutive-sequence.py @@ -0,0 +1,38 @@ +# Time: O(n) +# Space: O(h) + +# Definition for a binary tree node. +# class TreeNode(object): +# def __init__(self, x): +# self.val = x +# self.left = None +# self.right = None + + +class Solution(object): + def longestConsecutive(self, root): + """ + :type root: TreeNode + :rtype: int + """ + self.max_len = 0 + + def longestConsecutiveHelper(root): + if not root: + return 0 + + left_len = longestConsecutiveHelper(root.left) + right_len = longestConsecutiveHelper(root.right) + + cur_len = 1 + if root.left and root.left.val == root.val + 1: + cur_len = max(cur_len, left_len + 1) + if root.right and root.right.val == root.val + 1: + cur_len = max(cur_len, right_len + 1) + + self.max_len = max(self.max_len, cur_len, left_len, right_len) + + return cur_len + + longestConsecutiveHelper(root) + return self.max_len diff --git a/Python/binary-tree-maximum-path-sum.py b/Python/binary-tree-maximum-path-sum.py index 2c2705aa1..0c3089e4e 100644 --- a/Python/binary-tree-maximum-path-sum.py +++ b/Python/binary-tree-maximum-path-sum.py @@ -1,33 +1,37 @@ # Time: O(n) -# Space: O(logn) +# Space: O(h), h is height of binary tree # # Given a binary tree, find the maximum path sum. -# +# # The path may start and end at any node in the tree. -# +# # For example: # Given the below binary tree, -# +# # 1 # / \ # 2 3 # Return 6. # + + # Definition for a binary tree node -class TreeNode: +class TreeNode(object): def __init__(self, x): self.val = x self.left = None self.right = None -class Solution: + +class Solution(object): maxSum = float("-inf") + # @param root, a tree node # @return an integer def maxPathSum(self, root): self.maxPathSumRecu(root) return self.maxSum - + def maxPathSumRecu(self, root): if root is None: return 0 @@ -35,10 +39,3 @@ def maxPathSumRecu(self, root): right = max(0, self.maxPathSumRecu(root.right)) self.maxSum = max(self.maxSum, root.val + left + right) return root.val + max(left, right) - -if __name__ == "__main__": - root = TreeNode(1) - root.left = TreeNode(2) - root.right = TreeNode(3) - result = Solution().maxPathSum(root) - print result diff --git a/Python/binary-tree-paths.py b/Python/binary-tree-paths.py new file mode 100644 index 000000000..b32775adc --- /dev/null +++ b/Python/binary-tree-paths.py @@ -0,0 +1,52 @@ +# Time: O(n * h) +# Space: O(h) +# +# Given a binary tree, return all root-to-leaf paths. +# +# For example, given the following binary tree: +# +# 1 +# / \ +# 2 3 +# \ +# 5 +# All root-to-leaf paths are: +# +# ["1->2->5", "1->3"] +# +# +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, x): +# self.val = x +# self.left = None +# self.right = None + + +class Solution(object): + # @param {TreeNode} root + # @return {string[]} + def binaryTreePaths(self, root): + result, path = [], [] + self.binaryTreePathsRecu(root, path, result) + return result + + def binaryTreePathsRecu(self, node, path, result): + if node is None: + return + + if node.left is node.right is None: + ans = "" + for n in path: + ans += str(n.val) + "->" + result.append(ans + str(node.val)) + + if node.left: + path.append(node) + self.binaryTreePathsRecu(node.left, path, result) + path.pop() + + if node.right: + path.append(node) + self.binaryTreePathsRecu(node.right, path, result) + path.pop() diff --git a/Python/binary-tree-postorder-traversal.py b/Python/binary-tree-postorder-traversal.py index aa93e11fc..6984e8692 100644 --- a/Python/binary-tree-postorder-traversal.py +++ b/Python/binary-tree-postorder-traversal.py @@ -15,18 +15,22 @@ # Note: Recursive solution is trivial, could you do it iteratively? # + # Definition for a binary tree node -class TreeNode: +class TreeNode(object): def __init__(self, x): self.val = x self.left = None self.right = None + # Morris Traversal Solution -class Solution: - # @param root, a tree node - # @return a list of integers +class Solution(object): def postorderTraversal(self, root): + """ + :type root: TreeNode + :rtype: List[int] + """ dummy = TreeNode(0) dummy.left = root result, cur = [], dummy @@ -37,7 +41,7 @@ def postorderTraversal(self, root): node = cur.left while node.right and node.right != cur: node = node.right - + if node.right is None: node.right = cur cur = cur.left @@ -45,9 +49,9 @@ def postorderTraversal(self, root): result += self.traceBack(cur.left, node) node.right = None cur = cur.right - + return result - + def traceBack(self, frm, to): result, cur = [], frm while cur is not to: @@ -57,30 +61,25 @@ def traceBack(self, frm, to): result.reverse() return result + # Time: O(n) -# Space: O(n) -# Stack Solution -class Solution2: - # @param root, a tree node - # @return a list of integers +# Space: O(h) +# Stack Solution +class Solution2(object): def postorderTraversal(self, root): - result, stack, current, last_traversed = [], [], root, None - while stack or current: - if current: - stack.append(current) - current = current.left + """ + :type root: TreeNode + :rtype: List[int] + """ + result, stack = [], [(root, False)] + while stack: + root, is_visited = stack.pop() + if root is None: + continue + if is_visited: + result.append(root.val) else: - parent = stack[-1] - if parent.right in (None, last_traversed): - result.append(parent.val) - last_traversed = stack.pop() - else: - current = parent.right + stack.append((root, True)) + stack.append((root.right, False)) + stack.append((root.left, False)) return result - -if __name__ == "__main__": - root = TreeNode(1) - root.right = TreeNode(2) - root.right.left = TreeNode(3) - result = Solution().postorderTraversal(root) - print result \ No newline at end of file diff --git a/Python/binary-tree-preorder-traversal.py b/Python/binary-tree-preorder-traversal.py index 2cf998dcd..ed1edbfe1 100644 --- a/Python/binary-tree-preorder-traversal.py +++ b/Python/binary-tree-preorder-traversal.py @@ -15,64 +15,61 @@ # Note: Recursive solution is trivial, could you do it iteratively? # + # Definition for a binary tree node -class TreeNode: +class TreeNode(object): def __init__(self, x): self.val = x self.left = None self.right = None + # Morris Traversal Solution -class Solution: - # @param root, a tree node - # @return a list of integers +class Solution(object): def preorderTraversal(self, root): - result, prev, cur = [], None, root - while cur: - if cur.left is None: - result.append(cur.val) - prev = cur - cur = cur.right + """ + :type root: TreeNode + :rtype: List[int] + """ + result, curr = [], root + while curr: + if curr.left is None: + result.append(curr.val) + curr = curr.right else: - node = cur.left - while node.right and node.right != cur: + node = curr.left + while node.right and node.right != curr: node = node.right - + if node.right is None: - result.append(cur.val) - node.right = cur - prev =cur - cur = cur.left + result.append(curr.val) + node.right = curr + curr = curr.left else: node.right = None - cur = cur.right - + curr = curr.right + return result + # Time: O(n) -# Space: O(n) -# Stack Solution -class Solution2: - # @param root, a tree node - # @return a list of integers +# Space: O(h) +# Stack Solution +class Solution2(object): def preorderTraversal(self, root): - result, stack, current, last_traversed = [], [], root, None - while stack or current: - if current: - result.append(current.val) - stack.append(current) - current = current.left + """ + :type root: TreeNode + :rtype: List[int] + """ + result, stack = [], [(root, False)] + while stack: + root, is_visited = stack.pop() + if root is None: + continue + if is_visited: + result.append(root.val) else: - parent = stack[-1] - if parent.right in (None, last_traversed): - last_traversed = stack.pop() - else: - current = parent.right + stack.append((root.right, False)) + stack.append((root.left, False)) + stack.append((root, True)) return result - -if __name__ == "__main__": - root = TreeNode(1) - root.right = TreeNode(2) - root.right.left = TreeNode(3) - result = Solution().preorderTraversal(root) - print result \ No newline at end of file diff --git a/Python/binary-tree-pruning.py b/Python/binary-tree-pruning.py new file mode 100644 index 000000000..4d32ade90 --- /dev/null +++ b/Python/binary-tree-pruning.py @@ -0,0 +1,53 @@ +# Time: O(n) +# Space: O(h) + +# We are given the head node root of a binary tree, +# where additionally every node's value is either a 0 or a 1. +# +# Return the same tree where every subtree (of the given tree) +# not containing a 1 has been removed. +# +# (Recall that the subtree of a node X is X, +# plus every node that is a descendant of X.) +# +# Example 1: +# Input: [1,null,0,0,1] +# Output: [1,null,0,null,1] +# +# Explanation: +# Only the red nodes satisfy the property "every subtree not containing a 1". +# The diagram on the right represents the answer. +# +# Example 2: +# Input: [1,0,1,0,0,0,1] +# Output: [1,null,1,null,1] +# +# Example 3: +# Input: [1,1,0,1,1,0,1,0] +# Output: [1,1,0,1,1,null,1] +# +# Note: +# - The binary tree will have at most 100 nodes. +# - The value of each node will only be 0 or 1. + +# Definition for a binary tree node. +# class TreeNode(object): +# def __init__(self, x): +# self.val = x +# self.left = None +# self.right = None + + +class Solution(object): + def pruneTree(self, root): + """ + :type root: TreeNode + :rtype: TreeNode + """ + if not root: + return None + root.left = self.pruneTree(root.left) + root.right = self.pruneTree(root.right) + if not root.left and not root.right and root.val == 0: + return None + return root diff --git a/Python/binary-tree-right-side-view.py b/Python/binary-tree-right-side-view.py new file mode 100644 index 000000000..24e427261 --- /dev/null +++ b/Python/binary-tree-right-side-view.py @@ -0,0 +1,67 @@ +# Time: O(n) +# Space: O(h) +# +# Given a binary tree, imagine yourself standing on the right side of it, +# return the values of the nodes you can see ordered from top to bottom. +# +# For example: +# Given the following binary tree, +# 1 <--- +# / \ +# 2 3 <--- +# \ \ +# 5 4 <--- +# You should return [1, 3, 4]. +# + + +# Definition for a binary tree node +class TreeNode(object): + def __init__(self, x): + self.val = x + self.left = None + self.right = None + + +class Solution(object): + # @param root, a tree node + # @return a list of integers + def rightSideView(self, root): + result = [] + self.rightSideViewDFS(root, 1, result) + return result + + def rightSideViewDFS(self, node, depth, result): + if not node: + return + + if depth > len(result): + result.append(node.val) + + self.rightSideViewDFS(node.right, depth+1, result) + self.rightSideViewDFS(node.left, depth+1, result) + + +# BFS solution +# Time: O(n) +# Space: O(n) +class Solution2(object): + # @param root, a tree node + # @return a list of integers + def rightSideView(self, root): + if root is None: + return [] + + result, current = [], [root] + while current: + next_level = [] + for i, node in enumerate(current): + if node.left: + next_level.append(node.left) + if node.right: + next_level.append(node.right) + if i == len(current) - 1: + result.append(node.val) + current = next_level + + return result diff --git a/Python/binary-tree-tilt.py b/Python/binary-tree-tilt.py new file mode 100644 index 000000000..4b7de9125 --- /dev/null +++ b/Python/binary-tree-tilt.py @@ -0,0 +1,50 @@ +# Time: O(n) +# Space: O(n) + +# Given a binary tree, return the tilt of the whole tree. +# +# The tilt of a tree node is defined as the absolute difference +# between the sum of all left subtree node values and +# the sum of all right subtree node values. Null node has tilt 0. +# +# The tilt of the whole tree is defined as the sum of all nodes' tilt. +# +# Example: +# Input: +# 1 +# / \ +# 2 3 +# Output: 1 +# Explanation: +# Tilt of node 2 : 0 +# Tilt of node 3 : 0 +# Tilt of node 1 : |2-3| = 1 +# Tilt of binary tree : 0 + 0 + 1 = 1 +# Note: +# +# The sum of node values in any subtree won't exceed +# the range of 32-bit integer. + + +# Definition for a binary tree node. +# class TreeNode(object): +# def __init__(self, x): +# self.val = x +# self.left = None +# self.right = None + +class Solution(object): + def findTilt(self, root): + """ + :type root: TreeNode + :rtype: int + """ + def postOrderTraverse(root, tilt): + if not root: + return 0, tilt + left, tilt = postOrderTraverse(root.left, tilt) + right, tilt = postOrderTraverse(root.right, tilt) + tilt += abs(left-right) + return left+right+root.val, tilt + + return postOrderTraverse(root, 0)[1] diff --git a/Python/binary-tree-upside-down.py b/Python/binary-tree-upside-down.py index 51315ff94..9f58a714c 100644 --- a/Python/binary-tree-upside-down.py +++ b/Python/binary-tree-upside-down.py @@ -1,42 +1,46 @@ # Time: O(n) # Space: O(1) # -# Given a binary tree where all the right nodes are either leaf nodes with a sibling -# (a left node that shares the same parent node) or empty, flip it upside down and -# turn it into a tree where the original right nodes turned into left leaf nodes. +# Given a binary tree where all the right nodes are +# either leaf nodes with a sibling +# (a left node that shares the same parent node) or empty, +# flip it upside down and turn it into a tree +# where the original right nodes turned into left leaf nodes. # Return the new root. -# +# # For example: # Given a binary tree {1,2,3,4,5}, -# +# # 1 # / \ # 2 3 # / \ # 4 5 -# +# # return the root of the binary tree [4,5,2,#,#,3,1]. -# +# # 4 # / \ # 5 2 # / \ -# 3 1 +# 3 1 # + # Definition for a binary tree node -class TreeNode: +class TreeNode(object): def __init__(self, x): self.val = x self.left = None self.right = None -class Solution: + +class Solution(object): # @param root, a tree node # @return root of the upside down tree def upsideDownBinaryTree(self, root): p, parent, parent_right = root, None, None - + while p: left = p.left p.left = parent_right @@ -44,26 +48,27 @@ def upsideDownBinaryTree(self, root): p.right = parent parent = p p = left - + return parent + # Time: O(n) # Space: O(n) -class Solution2: +class Solution2(object): # @param root, a tree node # @return root of the upside down tree def upsideDownBinaryTree(self, root): return self.upsideDownBinaryTreeRecu(root, None) - + def upsideDownBinaryTreeRecu(self, p, parent): if p is None: return parent - + root = self.upsideDownBinaryTreeRecu(p.left, p) if parent: p.left = parent.right else: p.left = None p.right = parent - - return root \ No newline at end of file + + return root diff --git a/Python/binary-tree-vertical-order-traversal.py b/Python/binary-tree-vertical-order-traversal.py new file mode 100644 index 000000000..b57e04b99 --- /dev/null +++ b/Python/binary-tree-vertical-order-traversal.py @@ -0,0 +1,33 @@ +# Time: O(n) +# Space: O(n) + +# Definition for a binary tree node. +# class TreeNode(object): +# def __init__(self, x): +# self.val = x +# self.left = None +# self.right = None + +import collections + +try: + xrange # Python 2 +except NameError: + xrange = range # Python 3 + + +# BFS + hash solution. +class Solution(object): + def verticalOrder(self, root): + """ + :type root: TreeNode + :rtype: List[List[int]] + """ + cols = collections.defaultdict(list) + queue = [(root, 0)] + for node, i in queue: + if node: + cols[i].append(node.val) + queue += (node.left, i - 1), (node.right, i + 1) + return [cols[i] for i in xrange(min(cols.keys()), + max(cols.keys()) + 1)] if cols else [] diff --git a/Python/binary-tree-zigzag-level-order-traversal.py b/Python/binary-tree-zigzag-level-order-traversal.py index 7a6261e05..2b2261a33 100644 --- a/Python/binary-tree-zigzag-level-order-traversal.py +++ b/Python/binary-tree-zigzag-level-order-traversal.py @@ -1,8 +1,10 @@ # Time: O(n) # Space: O(n) # -# Given a binary tree, return the zigzag level order traversal of its nodes' values. (ie, from left to right, then right to left for the next level and alternate between). -# +# Given a binary tree, return the zigzag level order traversal of +# its nodes' values. (ie, from left to right, then right to left +# for the next level and alternate between). +# # For example: # Given binary tree {3,9,20,#,#,15,7}, # 3 @@ -17,14 +19,17 @@ # [15,7] # ] # + + # Definition for a binary tree node -class TreeNode: +class TreeNode(object): def __init__(self, x): self.val = x self.left = None self.right = None -class Solution: + +class Solution(object): # @param root, a tree node # @return a list of lists of integers def zigzagLevelOrder(self, root): @@ -46,12 +51,3 @@ def zigzagLevelOrder(self, root): level += 1 current = next_level return result - -if __name__ == "__main__": - root = TreeNode(3) - root.left = TreeNode(9) - root.right = TreeNode(20) - root.right.left = TreeNode(15) - root.right.right = TreeNode(7) - result = Solution().zigzagLevelOrder(root) - print result \ No newline at end of file diff --git a/Python/binary-trees-with-factors.py b/Python/binary-trees-with-factors.py new file mode 100644 index 000000000..1afe4a291 --- /dev/null +++ b/Python/binary-trees-with-factors.py @@ -0,0 +1,48 @@ +# Time: O(n^2) +# Space: O(n) + +# Given an array of unique integers, each integer is strictly greater than 1. +# We make a binary tree using these integers and each number may be used for +# any number of times. +# Each non-leaf node's value should be equal to the product of the values of +# it's children. +# How many binary trees can we make? Return the answer modulo 10 ** 9 + 7. +# +# Example 1: +# +# Input: A = [2, 4] +# Output: 3 +# Explanation: We can make these trees: [2], [4], [4, 2, 2] +# Example 2: +# +# Input: A = [2, 4, 5, 10] +# Output: 7 +# Explanation: We can make these trees: +# [2], [4], [5], [10], [4, 2, 2], [10, 2, 5], [10, 5, 2]. +# +# Note: +# - 1 <= A.length <= 1000. +# - 2 <= A[i] <= 10 ^ 9. + +try: + xrange # Python 2 +except NameError: + xrange = range # Python 3 + + +class Solution(object): + def numFactoredBinaryTrees(self, A): + """ + :type A: List[int] + :rtype: int + """ + M = 10**9 + 7 + A.sort() + dp = {} + for i in xrange(len(A)): + dp[A[i]] = 1 + for j in xrange(i): + if A[i] % A[j] == 0 and A[i] // A[j] in dp: + dp[A[i]] += dp[A[j]] * dp[A[i] // A[j]] + dp[A[i]] %= M + return sum(dp.values()) % M diff --git a/Python/binary-watch.py b/Python/binary-watch.py new file mode 100644 index 000000000..fa988a86e --- /dev/null +++ b/Python/binary-watch.py @@ -0,0 +1,55 @@ +# Time: O(1) +# Space: O(1) + +# A binary watch has 4 LEDs on the top which represent the hours (0-11), +# and the 6 LEDs on the bottom represent the minutes (0-59). +# +# Each LED represents a zero or one, with the least significant bit +# on the right. +# +# For example, the above binary watch reads "3:25". +# +# Given a non-negative integer n which represents the number of LEDs +# that are currently on, +# return all possible times the watch could represent. +# +# Example: +# +# Input: n = 1 +# Return: ["1:00", "2:00", "4:00", "8:00", "0:01", +# "0:02", "0:04", "0:08", "0:16", "0:32"] +# Note: +# The order of output does not matter. +# The hour must not contain a leading zero, for example "01:00" is not valid, +# it should be "1:00". + +try: + xrange # Python 2 +except NameError: + xrange = range # Python 3 + + +class Solution(object): + def readBinaryWatch(self, num): + """ + :type num: int + :rtype: List[str] + """ + def bit_count(bits): + count = 0 + while bits: + bits &= bits-1 + count += 1 + return count + + return ['%d:%02d' % (h, m) for h in xrange(12) for m in xrange(60) + if bit_count(h) + bit_count(m) == num] + + def readBinaryWatch2(self, num): + """ + :type num: int + :rtype: List[str] + """ + return ['{0}:{1}'.format(str(h), str(m).zfill(2)) + for h in range(12) for m in range(60) + if (bin(h) + bin(m)).count('1') == num] diff --git a/Python/bitwise-and-of-numbers-range.py b/Python/bitwise-and-of-numbers-range.py new file mode 100644 index 000000000..418018793 --- /dev/null +++ b/Python/bitwise-and-of-numbers-range.py @@ -0,0 +1,30 @@ +# Time: O(1) +# Space: O(1) +# +# Given a range [m, n] where 0 <= m <= n <= 2147483647, +# return the bitwise AND of all numbers in this range, inclusive. +# +# For example, given the range [5, 7], you should return 4. +# + + +class Solution(object): + # @param m, an integer + # @param n, an integer + # @return an integer + def rangeBitwiseAnd(self, m, n): + while m < n: + n &= n - 1 + return n + + +class Solution2(object): + # @param m, an integer + # @param n, an integer + # @return an integer + def rangeBitwiseAnd(self, m, n): + i, diff = 0, n-m + while diff: + diff >>= 1 + i += 1 + return n & m >> i << i diff --git a/Python/bold-words-in-string.py b/Python/bold-words-in-string.py new file mode 100644 index 000000000..7e08e8a84 --- /dev/null +++ b/Python/bold-words-in-string.py @@ -0,0 +1,71 @@ +# Time: O(n * l), n is the length of S, l is the average length of words +# Space: O(t) , t is the size of trie + +import collections +import functools + +try: + xrange # Python 2 +except NameError: + xrange = range # Python 3 + + +class Solution(object): + def boldWords(self, words, S): + """ + :type words: List[str] + :type S: str + :rtype: str + """ + _trie = lambda: collections.defaultdict(_trie) + trie = _trie() + for i, word in enumerate(words): + functools.reduce(dict.__getitem__, word, trie).setdefault("_end") + + lookup = [False] * len(S) + for i in xrange(len(S)): + curr = trie + k = -1 + for j in xrange(i, len(S)): + if S[j] not in curr: + break + curr = curr[S[j]] + if "_end" in curr: + k = j + for j in xrange(i, k+1): + lookup[j] = True + + result = [] + for i in xrange(len(S)): + if lookup[i] and (i == 0 or not lookup[i-1]): + result.append("") + result.append(S[i]) + if lookup[i] and (i == len(S)-1 or not lookup[i+1]): + result.append("") + return "".join(result) + + +# Time: O(n * d * l), l is the average length of words +# Space: O(n) +class Solution2(object): + def boldWords(self, words, S): + """ + :type words: List[str] + :type S: str + :rtype: str + """ + lookup = [0] * len(S) + for d in words: + pos = S.find(d) + while pos != -1: + lookup[pos:pos+len(d)] = [1] * len(d) + pos = S.find(d, pos+1) + + result = [] + for i in xrange(len(S)): + if lookup[i] and (i == 0 or not lookup[i-1]): + result.append("") + result.append(S[i]) + if lookup[i] and (i == len(S)-1 or not lookup[i+1]): + result.append("") + return "".join(result) diff --git a/Python/bomb-enemy.py b/Python/bomb-enemy.py new file mode 100644 index 000000000..576085c87 --- /dev/null +++ b/Python/bomb-enemy.py @@ -0,0 +1,46 @@ +# Time: O(m * n) +# Space: O(m * n) + +try: + xrange # Python 2 +except NameError: + xrange = range # Python 3 + + +class Solution(object): + def maxKilledEnemies(self, grid): + """ + :type grid: List[List[str]] + :rtype: int + """ + result = 0 + if not grid or not grid[0]: + return result + + down = [[0 for _ in xrange(len(grid[0]))] for _ in xrange(len(grid))] + right = [[0 for _ in xrange(len(grid[0]))] for _ in xrange(len(grid))] + for i in reversed(xrange(len(grid))): + for j in reversed(xrange(len(grid[0]))): + if grid[i][j] != 'W': + if i + 1 < len(grid): + down[i][j] = down[i + 1][j] + if j + 1 < len(grid[0]): + right[i][j] = right[i][j + 1] + if grid[i][j] == 'E': + down[i][j] += 1 + right[i][j] += 1 + + up = [0 for _ in xrange(len(grid[0]))] + for i in xrange(len(grid)): + left = 0 + for j in xrange(len(grid[0])): + if grid[i][j] == 'W': + up[j], left = 0, 0 + elif grid[i][j] == 'E': + up[j] += 1 + left += 1 + else: + result = max(result, + left + up[j] + right[i][j] + down[i][j]) + + return result diff --git a/Python/boundary-of-binary-tree.py b/Python/boundary-of-binary-tree.py new file mode 100644 index 000000000..348e2c2c6 --- /dev/null +++ b/Python/boundary-of-binary-tree.py @@ -0,0 +1,53 @@ +# Time: O(n) +# Space: O(h) + +# Definition for a binary tree node. +# class TreeNode(object): +# def __init__(self, x): +# self.val = x +# self.left = None +# self.right = None + + +class Solution(object): + def boundaryOfBinaryTree(self, root): + """ + :type root: TreeNode + :rtype: List[int] + """ + def leftBoundary(root, nodes): + if not root or (not root.left and not root.right): + return + nodes.append(root.val) + if not root.left: + leftBoundary(root.right, nodes) + else: + leftBoundary(root.left, nodes) + + def rightBoundary(root, nodes): + if not root or (not root.left and not root.right): + return + if not root.right: + rightBoundary(root.left, nodes) + else: + rightBoundary(root.right, nodes) + nodes.append(root.val) + + def leaves(root, nodes): + if not root: + return + if not root.left and not root.right: + nodes.append(root.val) + return + leaves(root.left, nodes) + leaves(root.right, nodes) + + if not root: + return [] + + nodes = [root.val] + leftBoundary(root.left, nodes) + leaves(root.left, nodes) + leaves(root.right, nodes) + rightBoundary(root.right, nodes) + return nodes diff --git a/Python/brick-wall.py b/Python/brick-wall.py new file mode 100644 index 000000000..7124e7469 --- /dev/null +++ b/Python/brick-wall.py @@ -0,0 +1,61 @@ +# Time: O(n), n is the total number of the bricks +# Space: O(m), m is the total number different widths + +# There is a brick wall in front of you. The wall is rectangular +# and has several rows of bricks. +# The bricks have the same height but different width. +# You want to draw a vertical line from +# the top to the bottom and cross the least bricks. +# +# The brick wall is represented by a list of rows. +# Each row is a list of integers representing the +# width of each brick in this row from left to right. +# +# If your line go through the edge of a brick, +# then the brick is not considered as crossed. +# You need to find out how to draw the line to cross the least bricks and +# return the number of crossed bricks. +# +# You cannot draw a line just along one of the two vertical edges of the wall, +# in which case the line will obviously cross no bricks. +# +# Example: +# Input: +# [[1,2,2,1], +# [3,1,2], +# [1,3,2], +# [2,4], +# [3,1,2], +# [1,3,1,1]] +# Output: 2 +# +# Note: +# The width sum of bricks in different rows are the same and +# won't exceed INT_MAX. +# The number of bricks in each row is in range [1,10,000]. +# The height of wall is in range [1,10,000]. +# Total number of bricks of the wall won't exceed 20,000. + +import collections + +try: + xrange # Python 2 +except NameError: + xrange = range # Python 3 + + +class Solution(object): + def leastBricks(self, wall): + """ + :type wall: List[List[int]] + :rtype: int + """ + widths = collections.defaultdict(int) + result = len(wall) + for row in wall: + width = 0 + for i in xrange(len(row)-1): + width += row[i] + widths[width] += 1 + result = min(result, len(wall) - widths[width]) + return result diff --git a/Python/bricks-falling-when-hit.py b/Python/bricks-falling-when-hit.py new file mode 100644 index 000000000..2d568ee0c --- /dev/null +++ b/Python/bricks-falling-when-hit.py @@ -0,0 +1,112 @@ +# Time: O(r * c) +# Space: O(r * c) + +# We have a grid of 1s and 0s; the 1s in a cell represent bricks. +# A brick will not drop if and +# only if it is directly connected to the top of the grid, +# or at least one of its (4-way) adjacent bricks will not drop. +# +# We will do some erasures sequentially. +# Each time we want to do the erasure at the location (i, j), +# the brick (if it exists) on that location will disappear, +# and then some other bricks may drop because of that erasure. +# +# Return an array representing the number of bricks that +# will drop after each erasure in sequence. +# +# Example 1: +# Input: +# grid = [[1,0,0,0],[1,1,1,0]] +# hits = [[1,0]] +# Output: [2] +# Explanation: +# If we erase the brick at (1, 0), the brick at (1, 1) and (1, 2) will drop. +# So we should return 2. +# +# Example 2: +# Input: +# grid = [[1,0,0,0],[1,1,0,0]] +# hits = [[1,1],[1,0]] +# Output: [0,0] +# Explanation: +# When we erase the brick at (1, 0), the brick at (1, 1) +# has already disappeared due to the last move. +# So each erasure will cause no bricks dropping. +# Note that the erased brick (1, 0) will not be counted as a dropped brick. +# +# Note: +# - The number of rows and columns in the grid will be in the range [1, 200]. +# - The number of erasures will not exceed the area of the grid. +# - It is guaranteed that each erasure will be different from +# any other erasure, and located inside the grid. +# - An erasure may refer to a location with no brick - +# if it does, no bricks drop. + + +class UnionFind(object): + def __init__(self, n): + self.set = range(n+1) + self.size = [1]*(n+1) + self.size[-1] = 0 + + def find_set(self, x): + if self.set[x] != x: + self.set[x] = self.find_set(self.set[x]) # path compression. + return self.set[x] + + def union_set(self, x, y): + x_root, y_root = map(self.find_set, (x, y)) + if x_root == y_root: + return False + self.set[min(x_root, y_root)] = max(x_root, y_root) + self.size[max(x_root, y_root)] += self.size[min(x_root, y_root)] + return True + + def top(self): + return self.size[self.find_set(len(self.size)-1)] + + +class Solution(object): + def hitBricks(self, grid, hits): + """ + :type grid: List[List[int]] + :type hits: List[List[int]] + :rtype: List[int] + """ + def index(C, r, c): + return r*C+c + + directions = [(0, -1), (0, 1), (-1, 0), (1, 0)] + R, C = len(grid), len(grid[0]) + + hit_grid = [row[:] for row in grid] + for i, j in hits: + hit_grid[i][j] = 0 + + union_find = UnionFind(R*C) + for r, row in enumerate(hit_grid): + for c, val in enumerate(row): + if not val: + continue + if r == 0: + union_find.union_set(index(C, r, c), R*C) + if r and hit_grid[r-1][c]: + union_find.union_set(index(C, r, c), index(C, r-1, c)) + if c and hit_grid[r][c-1]: + union_find.union_set(index(C, r, c), index(C, r, c-1)) + + result = [] + for r, c in reversed(hits): + prev_roof = union_find.top() + if grid[r][c] == 0: + result.append(0) + continue + for d in directions: + nr, nc = (r+d[0], c+d[1]) + if 0 <= nr < R and 0 <= nc < C and hit_grid[nr][nc]: + union_find.union_set(index(C, r, c), index(C, nr, nc)) + if r == 0: + union_find.union_set(index(C, r, c), R*C) + hit_grid[r][c] = 1 + result.append(max(0, union_find.top()-prev_roof-1)) + return result[::-1] diff --git a/Python/buddy-strings.py b/Python/buddy-strings.py new file mode 100644 index 000000000..f4810a478 --- /dev/null +++ b/Python/buddy-strings.py @@ -0,0 +1,53 @@ +# Time: O(n) +# Space: O(1) + +# Given two strings A and B of lowercase letters, +# return true if and only if we can swap two letters in A +# so that the result equals B. +# +# Example 1: +# +# Input: A = "ab", B = "ba" +# Output: true +# Example 2: +# +# Input: A = "ab", B = "ab" +# Output: false +# Example 3: +# +# Input: A = "aa", B = "aa" +# Output: true +# Example 4: +# +# Input: A = "aaaaaaabc", B = "aaaaaaacb" +# Output: true +# Example 5: +# +# Input: A = "", B = "aa" +# Output: false +# +# Note: +# - 0 <= A.length <= 20000 +# - 0 <= B.length <= 20000 +# - A and B consist only of lowercase letters. + +import itertools + + +class Solution(object): + def buddyStrings(self, A, B): + """ + :type A: str + :type B: str + :rtype: bool + """ + if len(A) != len(B): + return False + diff = [] + for a, b in itertools.izip(A, B): + if a != b: + diff.append((a, b)) + if len(diff) > 2: + return False + return (not diff and len(set(A)) < len(A)) or \ + (len(diff) == 2 and diff[0] == diff[1][::-1]) diff --git a/Python/bulb-switcher-ii.py b/Python/bulb-switcher-ii.py new file mode 100644 index 000000000..e41844686 --- /dev/null +++ b/Python/bulb-switcher-ii.py @@ -0,0 +1,50 @@ +# Time: O(1) +# Space: O(1) + +# There is a room with n lights which are turned on initially and +# 4 buttons on the wall. +# After performing exactly m unknown operations towards buttons, +# you need to return how many different kinds of status of +# the n lights could be. +# +# Suppose n lights are labeled as number [1, 2, 3 ..., n], +# function of these 4 buttons are given below: +# 1. Flip all the lights. +# 3. Flip lights with even numbers. +# 3. Flip lights with odd numbers. +# 4. Flip lights with (3k + 1) numbers, k = 0, 1, 2, ... +# +# Example 1: +# Input: n = 1, m = 1. +# Output: 2 +# Explanation: Status can be: [on], [off] +# Example 2: +# Input: n = 2, m = 1. +# Output: 3 +# Explanation: Status can be: [on, off], [off, on], [off, off] +# Example 3: +# Input: n = 3, m = 1. +# Output: 4 +# Explanation: Status can be: [off, on, off], [on, off, on], +# [off, off, off], [off, on, on]. +# Note: n and m both fit in range [0, 1000]. + + +class Solution(object): + def flipLights(self, n, m): + """ + :type n: int + :type m: int + :rtype: int + """ + if m == 0: + return 1 + if n == 1: + return 2 + if m == 1 and n == 2: + return 3 + if m == 1 or n == 2: + return 4 + if m == 2: + return 7 + return 8 diff --git a/Python/bulb-switcher.py b/Python/bulb-switcher.py new file mode 100644 index 000000000..f2dd883dd --- /dev/null +++ b/Python/bulb-switcher.py @@ -0,0 +1,35 @@ +# Time: O(1) +# Space: O(1) + +# There are n bulbs that are initially off. +# You first turn on all the bulbs. Then, +# you turn off every second bulb. On the +# third round, you toggle every third bulb +# (turning on if it's off or turning off if +# it's on). For the nth round, you only +# toggle the last bulb. Find how many bulbs +# are on after n rounds. +# +# Example: +# +# Given n = 3. +# +# At first, the three bulbs are [off, off, off]. +# After first round, the three bulbs are [on, on, on]. +# After second round, the three bulbs are [on, off, on]. +# After third round, the three bulbs are [on, off, off]. +# +# So you should return 1, because there is +# only one bulb is on. + +import math + + +class Solution(object): + def bulbSwitch(self, n): + """ + type n: int + rtype: int + """ + # The number of full squares. + return int(math.sqrt(n)) diff --git a/Python/bulls-and-cows.py b/Python/bulls-and-cows.py new file mode 100644 index 000000000..b9c0b5cc6 --- /dev/null +++ b/Python/bulls-and-cows.py @@ -0,0 +1,77 @@ +# Time: O(n) +# Space: O(10) = O(1) + +# You are playing the following Bulls and Cows game with your friend: +# You write a 4-digit secret number and ask your friend to guess it, +# each time your friend guesses a number, you give a hint, the hint +# tells your friend how many digits are in the correct positions +# (called "bulls") and how many digits are in the wrong positions +# (called "cows"), your friend will use those hints to find out the +# secret number. +# +# For example: +# +# Secret number: 1807 +# Friend's guess: 7810 +# Hint: 1 bull and 3 cows. (The bull is 8, the cows are 0, 1 and 7.) +# According to Wikipedia: "Bulls and Cows (also known as Cows and Bulls +# or Pigs and Bulls or Bulls and Cleots) is an old code-breaking mind or +# paper and pencil game for two or more players, predating the similar +# commercially marketed board game Mastermind. The numerical version of +# the game is usually played with 4 digits, but can also be played with +# 3 or any other number of digits." +# +# Write a function to return a hint according to the secret number and +# friend's guess, use A to indicate the bulls and B to indicate the cows, +# in the above example, your function should return 1A3B. +# +# You may assume that the secret number and your friend's guess only contain +# digits, and their lengths are always equal. +# + +import operator + + +# One pass solution. +from collections import defaultdict, Counter +from itertools import izip, imap + + +class Solution(object): + def getHint(self, secret, guess): + """ + :type secret: str + :type guess: str + :rtype: str + """ + A, B = 0, 0 + s_lookup, g_lookup = defaultdict(int), defaultdict(int) + for s, g in izip(secret, guess): + if s == g: + A += 1 + else: + if s_lookup[g]: + s_lookup[g] -= 1 + B += 1 + else: + g_lookup[g] += 1 + if g_lookup[s]: + g_lookup[s] -= 1 + B += 1 + else: + s_lookup[s] += 1 + + return "%dA%dB" % (A, B) + + +# Two pass solution. +class Solution2(object): + def getHint(self, secret, guess): + """ + :type secret: str + :type guess: str + :rtype: str + """ + A = sum(imap(operator.eq, secret, guess)) + B = sum((Counter(secret) & Counter(guess)).values()) - A + return "%dA%dB" % (A, B) diff --git a/Python/burst-balloons.py b/Python/burst-balloons.py new file mode 100644 index 000000000..ef84fff11 --- /dev/null +++ b/Python/burst-balloons.py @@ -0,0 +1,58 @@ +# Time: O(n^3) +# Space: O(n^2) + +# Given n balloons, indexed from 0 to n-1. +# Each balloon is painted with a number on it +# represented by array nums. +# You are asked to burst all the balloons. +# If the you burst balloon i you will get +# nums[left] * nums[i] * nums[right] coins. +# Here left and right are adjacent indices of i. +# After the burst, the left and right then +# becomes adjacent. +# +# Find the maximum coins you can collect by +# bursting the balloons wisely. +# +# Note: +# (1) You may imagine nums[-1] = nums[n] = 1. +# They are not real therefore you can not burst them. +# (2) 0 <= n <= 500, 0 <= nums[i] <= 100 +# +# Example: +# +# Given [3, 1, 5, 8] +# +# Return 167 +# +# nums = [3,1,5,8] --> [3,5,8] --> [3,8] --> [8] --> [] +# coins = 3*1*5 + 3*5*8 + 1*3*8 + 1*8*1 = 167 +# + +try: + xrange # Python 2 +except NameError: + xrange = range # Python 3 + + +class Solution(object): + def maxCoins(self, nums): + """ + :type nums: List[int] + :rtype: int + """ + coins = [1] + [i for i in nums if i > 0] + [1] + n = len(coins) + max_coins = [[0 for _ in xrange(n)] for _ in xrange(n)] + + for k in xrange(2, n): + for left in xrange(n - k): + right = left + k + for i in xrange(left + 1, right): + max_coins[left][right] = \ + max(max_coins[left][right], + coins[left] * coins[i] * coins[right] + + max_coins[left][i] + + max_coins[i][right]) + + return max_coins[0][-1] diff --git a/Python/bus-routes.py b/Python/bus-routes.py new file mode 100644 index 000000000..ddcda2572 --- /dev/null +++ b/Python/bus-routes.py @@ -0,0 +1,67 @@ +# Time: O(|V| + |E|) +# Space: O(|V| + |E|) + +# We have a list of bus routes. Each routes[i] is a bus route that +# the i-th bus repeats forever. For example if routes[0] = [1, 5, 7], +# this means that the first bus (0-th indexed) travels in the sequence +# 1->5->7->1->5->7->1->... forever. +# +# We start at bus stop S (initially not on a bus), and we want to go to bus +# stop T. +# Travelling by buses only, what is the least number of buses we must take to +# reach our destination? +# Return -1 if it is not possible. +# +# Example: +# Input: +# routes = [[1, 2, 7], [3, 6, 7]] +# S = 1 +# T = 6 +# Output: 2 +# Explanation: +# The best strategy is take the first bus to the bus stop 7, +# then take the second bus to the bus stop 6. +# +# Note: +# - 1 <= routes.length <= 500. +# - 1 <= routes[i].length <= 500. +# - 0 <= routes[i][j] < 10 ^ 6. + +import collections + + +class Solution(object): + def numBusesToDestination(self, routes, S, T): + """ + :type routes: List[List[int]] + :type S: int + :type T: int + :rtype: int + """ + if S == T: + return 0 + + to_route = collections.defaultdict(set) + for i, route in enumerate(routes): + for stop in route: + to_route[stop].add(i) + + result = 1 + q = [S] + lookup = set([S]) + while q: + next_q = [] + for stop in q: + for i in to_route[stop]: + for next_stop in routes[i]: + if next_stop in lookup: + continue + if next_stop == T: + return result + next_q.append(next_stop) + to_route[next_stop].remove(i) + lookup.add(next_stop) + q = next_q + result += 1 + + return -1 diff --git a/Python/can-i-win.py b/Python/can-i-win.py new file mode 100644 index 000000000..8e482cf30 --- /dev/null +++ b/Python/can-i-win.py @@ -0,0 +1,60 @@ +# Time: O(n!) +# Space: O(n) + +# In the "100 game," two players take turns adding, to a running total, any integer from 1..10. +# The player who first causes the running total to reach or exceed 100 wins. +# +# What if we change the game so that players cannot re-use integers? +# +# For example, two players might take turns drawing from a common pool of numbers of 1..15 +# without replacement until they reach a total >= 100. +# +# Given an integer maxChoosableInteger and another integer desiredTotal, +# determine if the first player to move can force a win, assuming both players play optimally. +# +# You can always assume that maxChoosableInteger will not be larger than 20 and +# desiredTotal will not be larger than 300. +# +# Example +# +# Input: +# maxChoosableInteger = 10 +# desiredTotal = 11 +# +# Output: +# false +# +# Explanation: +# No matter which integer the first player choose, the first player will lose. +# The first player can choose an integer from 1 up to 10. +# If the first player choose 1, the second player can only choose integers from 2 up to 10. +# The second player will win by choosing 10 and get a total = 11, which is >= desiredTotal. +# Same with other integers chosen by the first player, the second player will always win. + +# Memoization solution. +class Solution(object): + def canIWin(self, maxChoosableInteger, desiredTotal): + """ + :type maxChoosableInteger: int + :type desiredTotal: int + :rtype: bool + """ + def canIWinHelper(maxChoosableInteger, desiredTotal, visited, lookup): + if visited in lookup: + return lookup[visited] + + mask = 1 + for i in xrange(maxChoosableInteger): + if visited & mask == 0: + if i + 1 >= desiredTotal or \ + not canIWinHelper(maxChoosableInteger, desiredTotal - (i + 1), visited | mask, lookup): + lookup[visited] = True + return True + mask <<= 1 + lookup[visited] = False + return False + + if (1 + maxChoosableInteger) * (maxChoosableInteger / 2) < desiredTotal: + return False + + return canIWinHelper(maxChoosableInteger, desiredTotal, 0, {}) diff --git a/Python/can-place-flowers.py b/Python/can-place-flowers.py new file mode 100644 index 000000000..343a031c2 --- /dev/null +++ b/Python/can-place-flowers.py @@ -0,0 +1,37 @@ +# Time: O(n) +# Space: O(1) + +# Suppose you have a long flowerbed in which some of the plots are planted and some are not. +# However, flowers cannot be planted in adjacent plots - they would compete for water +# and both would die. +# +# Given a flowerbed (represented as an array containing 0 and 1, +# where 0 means empty and 1 means not empty), and a number n, +# return if n new flowers can be planted in it without violating the no-adjacent-flowers rule. +# +# Example 1: +# Input: flowerbed = [1,0,0,0,1], n = 1 +# Output: True +# Example 2: +# Input: flowerbed = [1,0,0,0,1], n = 2 +# Output: False +# Note: +# The input array won't violate no-adjacent-flowers rule. +# The input array size is in the range of [1, 20000]. +# n is a non-negative integer which won't exceed the input array size. + +class Solution(object): + def canPlaceFlowers(self, flowerbed, n): + """ + :type flowerbed: List[int] + :type n: int + :rtype: bool + """ + for i in xrange(len(flowerbed)): + if flowerbed[i] == 0 and (i == 0 or flowerbed[i-1] == 0) and \ + (i == len(flowerbed)-1 or flowerbed[i+1] == 0): + flowerbed[i] = 1 + n -= 1 + if n <= 0: + return True + return False diff --git a/Python/candy-crush.py b/Python/candy-crush.py new file mode 100644 index 000000000..591398830 --- /dev/null +++ b/Python/candy-crush.py @@ -0,0 +1,71 @@ +# Time: O((R * C)^2) +# Space: O(1) + +# This question is about implementing a basic elimination algorithm for Candy Crush. +# +# Given a 2D integer array board representing the grid of candy, +# different positive integers board[i][j] represent different types of candies. +# A value of board[i][j] = 0 represents that the cell at position (i, j) is empty. +# The given board represents the state of the game following the player's move. +# Now, you need to restore the board to a stable state by crushing candies according to the following rules: +# +# If three or more candies of the same type are adjacent vertically or horizontally, +# "crush" them all at the same time - these positions become empty. +# +# After crushing all candies simultaneously, +# if an empty space on the board has candies on top of itself, +# then these candies will drop until they hit a candy or bottom at the same time. +# (No new candies will drop outside the top boundary.) +# +# After the above steps, there may exist more candies that can be crushed. +# If so, you need to repeat the above steps. +# If there does not exist more candies that can be crushed (ie. the board is stable), +# then return the current board. +# You need to perform the above rules until the board becomes stable, then return the current board. +# +# Example 1: +# Input: +# board = +# [[110,5,112,113,114],[210,211,5,213,214],[310,311,3,313,314],[410,411,412,5,414],[5,1,512,3,3],[610,4,1,613,614],[710,1,2,713,714],[810,1,2,1,1],[1,1,2,2,2],[4,1,4,4,1014]] +# Output: +# [[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[110,0,0,0,114],[210,0,0,0,214],[310,0,0,113,314],[410,0,0,213,414],[610,211,112,313,614],[710,311,412,613,714],[810,411,512,713,1014]] + +# Note: +# The length of board will be in the range [3, 50]. +# The length of board[i] will be in the range [3, 50]. +# Each board[i][j] will initially start as an integer in the range [1, 2000]. + +class Solution(object): + def candyCrush(self, board): + """ + :type board: List[List[int]] + :rtype: List[List[int]] + """ + R, C = len(board), len(board[0]) + changed = True + + while changed: + changed = False + + for r in xrange(R): + for c in xrange(C-2): + if abs(board[r][c]) == abs(board[r][c+1]) == abs(board[r][c+2]) != 0: + board[r][c] = board[r][c+1] = board[r][c+2] = -abs(board[r][c]) + changed = True + + for r in xrange(R-2): + for c in xrange(C): + if abs(board[r][c]) == abs(board[r+1][c]) == abs(board[r+2][c]) != 0: + board[r][c] = board[r+1][c] = board[r+2][c] = -abs(board[r][c]) + changed = True + + for c in xrange(C): + i = R-1 + for r in reversed(xrange(R)): + if board[r][c] > 0: + board[i][c] = board[r][c] + i -= 1 + for r in reversed(xrange(i+1)): + board[r][c] = 0 + + return board diff --git a/Python/candy.py b/Python/candy.py index daf2f8153..c2c0c2e08 100644 --- a/Python/candy.py +++ b/Python/candy.py @@ -1,16 +1,18 @@ +from __future__ import print_function # Time: O(n) # Space: O(n) # # There are N children standing in a line. Each child is assigned a rating value. -# +# # You are giving candies to these children subjected to the following requirements: -# +# # Each child must have at least one candy. # Children with a higher rating get more candies than their neighbors. # What is the minimum candies you must give? # import operator +from functools import reduce class Solution: # @param ratings, a list of integer @@ -20,14 +22,14 @@ def candy(self, ratings): for i in xrange(1, len(ratings)): if ratings[i] > ratings[i - 1]: candies[i] = candies[i - 1] + 1 - + for i in reversed(xrange(1, len(ratings))): if ratings[i - 1] > ratings[i] and candies[i - 1] <= candies[i]: candies[i - 1] = candies[i] + 1 - + return reduce(operator.add, candies) - + if __name__ == "__main__": result = Solution().candy([1, 2, 3, 2, 3, 5, 2, 5]) - print result + print(result) diff --git a/Python/car-fleet.py b/Python/car-fleet.py new file mode 100644 index 000000000..85d5619c5 --- /dev/null +++ b/Python/car-fleet.py @@ -0,0 +1,59 @@ +# Time: O(nlogn) +# Space: O(n) + +# N cars are going to the same destination along a one lane road. +# The destination is target miles away. +# +# Each car i has a constant speed speed[i] (in miles per hour), +# and initial position position[i] miles towards the target along the road. +# +# A car can never pass another car ahead of it, +# but it can catch up to it, and drive bumper to bumper at the same speed. +# +# The distance between these two cars is ignored - they are assumed to +# have the same position. +# +# A car fleet is some non-empty set of cars driving at the same position +# and same speed. +# Note that a single car is also a car fleet. +# +# If a car catches up to a car fleet right at the destination point, +# it will still be considered as one car fleet. +# +# How many car fleets will arrive at the destination? +# +# Example 1: +# +# Input: target = 12, position = [10,8,0,5,3], speed = [2,4,1,1,3] +# Output: 3 +# Explanation: +# The cars starting at 10 and 8 become a fleet, meeting each other at 12. +# The car starting at 0 doesn't catch up to any other car, so it is a fleet +# by itself. +# The cars starting at 5 and 3 become a fleet, meeting each other at 6. +# Note that no other cars meet these fleets before the destination, +# so the answer is 3. +# +# Note: +# - 0 <= N <= 10 ^ 4 +# - 0 < target <= 10 ^ 6 +# - 0 < speed[i] <= 10 ^ 6 +# - 0 <= position[i] < target +# - All initial positions are different. + + +class Solution(object): + def carFleet(self, target, position, speed): + """ + :type target: int + :type position: List[int] + :type speed: List[int] + :rtype: int + """ + times = [float(target-p)/s for p, s in sorted(zip(position, speed))] + result, curr = 0, 0 + for t in reversed(times): + if t > curr: + result += 1 + curr = t + return result diff --git a/Python/card-flipping-game.py b/Python/card-flipping-game.py new file mode 100644 index 000000000..e39e2633b --- /dev/null +++ b/Python/card-flipping-game.py @@ -0,0 +1,49 @@ +# Time: O(n) +# Space: O(n) + +# On a table are N cards, with a positive integer printed on the front +# and back of each card (possibly different). +# +# We flip any number of cards, and after we choose one card. +# +# If the number X on the back of the chosen card is not on the front of +# any card, then this number X is good. +# +# What is the smallest number that is good? If no number is good, output 0. +# +# Here, fronts[i] and backs[i] represent the number on the front and back of +# card i. +# +# A flip swaps the front and back numbers, so the value on the front is +# now on the back and vice versa. +# +# Example: +# +# Input: fronts = [1,2,4,4,7], backs = [1,3,4,1,3] +# Output: 2 +# Explanation: If we flip the second card, the fronts are [1,3,4,4,7] and +# the backs are [1,2,4,1,3]. +# We choose the second card, which has number 2 on the back, +# and it isn't on the front of any card, so 2 is good. +# +# Note: +# - 1 <= fronts.length == backs.length <= 1000. +# - 1 <= fronts[i] <= 2000. +# - 1 <= backs[i] <= 2000. + +import itertools + + +class Solution(object): + def flipgame(self, fronts, backs): + """ + :type fronts: List[int] + :type backs: List[int] + :rtype: int + """ + same = {n for i, n in enumerate(fronts) if n == backs[i]} + result = float("inf") + for n in itertools.chain(fronts, backs): + if n not in same: + result = min(result, n) + return result if result < float("inf") else 0 diff --git a/Python/chalkboard-xor-game.py b/Python/chalkboard-xor-game.py new file mode 100644 index 000000000..b6c5781f2 --- /dev/null +++ b/Python/chalkboard-xor-game.py @@ -0,0 +1,43 @@ +# Time: O(n) +# Space: O(1) + +# We are given non-negative integers nums[i] which are written on a chalkboard. +# Alice and Bob take turns erasing exactly one number from the chalkboard, +# with Alice starting first. If erasing a number causes the bitwise XOR of +# all the elements of the chalkboard to become 0, then that player loses. +# (Also, we'll say the bitwise XOR of one element is that element itself, +# and the bitwise XOR of no elements is 0.) +# +# Also, if any player starts their turn with the bitwise XOR of all the elements +# of the chalkboard equal to 0, then that player wins. +# +# Return True if and only if Alice wins the game, assuming both players play optimally. +# +# Example: +# Input: nums = [1, 1, 2] +# Output: false +# Explanation: +# Alice has two choices: erase 1 or erase 2. +# If she erases 1, the nums array becomes [1, 2]. The bitwise XOR of +# all the elements of the chalkboard is 1 XOR 2 = 3. +# Now Bob can remove any element he wants, because Alice will be the one +# to erase the last element and she will lose. +# If Alice erases 2 first, now nums becomes [1, 1]. The bitwise XOR of +# all the elements of the chalkboard is 1 XOR 1 = 0. Alice will lose. +# +# Notes: +# - 1 <= N <= 1000. +# - 0 <= nums[i] <= 2^16. + +from operator import xor +from functools import reduce + + +class Solution(object): + def xorGame(self, nums): + """ + :type nums: List[int] + :rtype: bool + """ + return reduce(xor, nums) == 0 or \ + len(nums) % 2 == 0 diff --git a/Python/champagne-tower.py b/Python/champagne-tower.py new file mode 100644 index 000000000..ae3bac153 --- /dev/null +++ b/Python/champagne-tower.py @@ -0,0 +1,55 @@ +# Time: O(n^2) = O(1), since n is at most 99 +# Space: O(n) = O(1) + +# We stack glasses in a pyramid, where the first row has 1 glass, +# the second row has 2 glasses, and so on until the 100th row. +# Each glass holds one cup (250ml) of champagne. +# +# Then, some champagne is poured in the first glass at the top. +# When the top most glass is full, any excess liquid poured will fall +# equally to the glass immediately to the left and right of it. +# When those glasses become full, any excess champagne will fall +# equally to the left and right of those glasses, and so on. +# (A glass at the bottom row has it's excess champagne fall on the floor.) +# +# For example, after one cup of champagne is poured, the top most glass is full. +# After two cups of champagne are poured, the two glasses on the second row are half full. +# After three cups of champagne are poured, those two cups become full - +# there are 3 full glasses total now. After four cups of champagne are poured, +# the third row has the middle glass half full, and the two outside glasses are a quarter full, as pictured below. +# +# Now after pouring some non-negative integer cups of champagne, +# return how full the j-th glass in the i-th row is (both i and j are 0 indexed.) +# +# Example 1: +# Input: poured = 1, query_glass = 1, query_row = 1 +# Output: 0.0 +# Explanation: We poured 1 cup of champange to the top glass of the tower +# (which is indexed as (0, 0)). There will be no excess liquid so all the glasses under the top glass will remain empty. +# +# Example 2: +# Input: poured = 2, query_glass = 1, query_row = 1 +# Output: 0.5 +# Explanation: We poured 2 cups of champange to the top glass of the tower +# (which is indexed as (0, 0)). There is one cup of excess liquid. +# The glass indexed as (1, 0) and the glass indexed as (1, 1) will share +# the excess liquid equally, and each will get half cup of champange. +# +# Note: +# - poured will be in the range of [0, 10 ^ 9]. +# - query_glass and query_row will be in the range of [0, 99]. + +class Solution(object): + def champagneTower(self, poured, query_row, query_glass): + """ + :type poured: int + :type query_row: int + :type query_glass: int + :rtype: float + """ + result = [poured] + [0] * query_row + for i in xrange(1, query_row+1): + for j in reversed(xrange(i+1)): + result[j] = max(result[j]-1, 0)/2.0 + \ + max(result[j-1]-1, 0)/2.0 + return min(result[query_glass], 1) diff --git a/Python/cheapest-flights-within-k-stops.py b/Python/cheapest-flights-within-k-stops.py new file mode 100644 index 000000000..02acd7898 --- /dev/null +++ b/Python/cheapest-flights-within-k-stops.py @@ -0,0 +1,64 @@ +# Time: O((|E| + |V|) * log|V|) = O(|E| * log|V|), +# if we can further to use Fibonacci heap, it would be O(|E| + |V| * log|V|) +# Space: O(|E| + |V|) = O(|E|) + +# There are n cities connected by m flights. Each fight starts from city u and arrives at v with a price w. +# +# Now given all the cities and fights, together with starting city src and the destination dst, +# your task is to find the cheapest price from src to dst with up to k stops. +# If there is no such route, output -1. +# +# Example 1: +# Input: +# n = 3, edges = [[0,1,100],[1,2,100],[0,2,500]] +# src = 0, dst = 2, k = 1 +# Output: 200 +# Explanation: +# The cheapest price from city 0 to city 2 with at most 1 stop costs 200, as marked red in the picture. +# +# Example 2: +# Input: +# n = 3, edges = [[0,1,100],[1,2,100],[0,2,500]] +# src = 0, dst = 2, k = 0 +# Output: 500 +# +# Explanation: +# The cheapest price from city 0 to city 2 with at most 0 stop costs 500, as marked blue in the picture. +# Note: +# - The number of nodes n will be in range [1, 100], with nodes labeled from 0 to n - 1. +# - The size of flights will be in range [0, n * (n - 1) / 2]. +# - The format of each flight will be (src, dst, price). +# - The price of each flight will be in the range [1, 10000]. +# - k is in the range of [0, n - 1]. +# - There will not be any duplicated flights or self cycles. + +import collections +import heapq + + +class Solution(object): + def findCheapestPrice(self, n, flights, src, dst, K): + """ + :type n: int + :type flights: List[List[int]] + :type src: int + :type dst: int + :type K: int + :rtype: int + """ + adj = collections.defaultdict(list) + for u, v, w in flights: + adj[u].append((v, w)) + best = collections.defaultdict(lambda: collections.defaultdict(lambda: float("inf"))) + min_heap = [(0, src, K+1)] + while min_heap: + result, u, k = heapq.heappop(min_heap) + if k < 0 or best[u][k] < result: + continue + if u == dst: + return result + for v, w in adj[u]: + if result+w < best[v][k-1]: + best[v][k-1] = result+w + heapq.heappush(min_heap, (result+w, v, k-1)) + return -1 diff --git a/Python/cherry-pickup.py b/Python/cherry-pickup.py new file mode 100644 index 000000000..f5815a5ab --- /dev/null +++ b/Python/cherry-pickup.py @@ -0,0 +1,63 @@ +# Time: O(n^3) +# Space: O(n^2) + +# In a N x N grid representing a field of cherries, each cell is one of three possible integers. +# +# 0 means the cell is empty, so you can pass through; +# 1 means the cell contains a cherry, that you can pick up and pass through; +# -1 means the cell contains a thorn that blocks your way. +# Your task is to collect maximum number of cherries possible by following the rules below: +# +# Starting at the position (0, 0) and reaching (N-1, N-1) by moving right +# or down through valid path cells (cells with value 0 or 1); +# +# After reaching (N-1, N-1), returning to (0, 0) by moving left or up through valid path cells; +# When passing through a path cell containing a cherry, you pick it up and the cell becomes an empty cell (0); +# If there is no valid path between (0, 0) and (N-1, N-1), then no cherries can be collected. +# Example 1: +# Input: grid = +# [[0, 1, -1], +# [1, 0, -1], +# [1, 1, 1]] +# Output: 5 +# Explanation: +# The player started at (0, 0) and went down, down, right right to reach (2, 2). +# 4 cherries were picked up during this single trip, and the matrix becomes [[0,1,-1],[0,0,-1],[0,0,0]]. +# Then, the player went left, up, up, left to return home, picking up one more cherry. +# The total number of cherries picked up is 5, and this is the maximum possible. +# +# Note: +# - grid is an N by N 2D array, with 1 <= N <= 50. +# - Each grid[i][j] is an integer in the set {-1, 0, 1}. +# - It is guaranteed that grid[0][0] and grid[N-1][N-1] are not -1. + +class Solution(object): + def cherryPickup(self, grid): + """ + :type grid: List[List[int]] + :rtype: int + """ + # dp holds the max # of cherries two k-length paths can pickup. + # The two k-length paths arrive at (i, k - i) and (j, k - j), + # respectively. + n = len(grid) + dp = [[-1 for _ in xrange(n)] for _ in xrange(n)] + dp[0][0] = grid[0][0] + max_len = 2 * (n-1) + directions = [(0, 0), (-1, 0), (0, -1), (-1, -1)] + for k in xrange(1, max_len+1): + for i in reversed(xrange(max(0, k-n+1), min(k+1, n))): # 0 <= i < n, 0 <= k-i < n + for j in reversed(xrange(i, min(k+1, n))): # i <= j < n, 0 <= k-j < n + if grid[i][k-i] == -1 or grid[j][k-j] == -1: + dp[i][j] = -1 + continue + cnt = grid[i][k-i] + if i != j: + cnt += grid[j][k-j] + max_cnt = -1 + for direction in directions: + ii, jj = i+direction[0], j+direction[1] + if ii >= 0 and jj >= 0 and dp[ii][jj] >= 0: + max_cnt = max(max_cnt, dp[ii][jj]+cnt) + dp[i][j] = max_cnt + return max(dp[n-1][n-1], 0) diff --git a/Python/circular-array-loop.py b/Python/circular-array-loop.py new file mode 100644 index 000000000..32c7f87c7 --- /dev/null +++ b/Python/circular-array-loop.py @@ -0,0 +1,51 @@ +# Time: O(n) +# Space: O(1) + +# You are given an array of positive and negative integers. +# If a number n at an index is positive, then move forward n steps. +# Conversely, if it's negative (-n), move backward n steps. +# Assume the first element of the array is forward next to the last element, +# and the last element is backward next to the first element. +# Determine if there is a loop in this array. +# A loop starts and ends at a particular index with more than 1 element along the loop. +# The loop must be "forward" or "backward'. +# +# Example 1: Given the array [2, -1, 1, 2, 2], there is a loop, from index 0 -> 2 -> 3 -> 0. +# +# Example 2: Given the array [-1, 2], there is no loop. +# +# Note: The given array is guaranteed to contain no element "0". +# +# Can you do it in O(n) time complexity and O(1) space complexity? + +class Solution(object): + def circularArrayLoop(self, nums): + """ + :type nums: List[int] + :rtype: bool + """ + def next_index(nums, i): + return (i + nums[i]) % len(nums) + + for i in xrange(len(nums)): + if nums[i] == 0: + continue + + slow, fast = i, i + while nums[next_index(nums, slow)] * nums[i] > 0 and \ + nums[next_index(nums, fast)] * nums[i] > 0 and \ + nums[next_index(nums, next_index(nums, fast))] * nums[i] > 0: + slow = next_index(nums, slow) + fast = next_index(nums, next_index(nums, fast)) + if slow == fast: + if slow == next_index(nums, slow): + break + return True + + slow, val = i, nums[i] + while nums[slow] * val > 0: + tmp = next_index(nums, slow) + nums[slow] = 0 + slow = tmp + + return False diff --git a/Python/climbing-stairs.py b/Python/climbing-stairs.py index 1c8df918d..5ef865236 100644 --- a/Python/climbing-stairs.py +++ b/Python/climbing-stairs.py @@ -1,21 +1,33 @@ +from __future__ import print_function # Time: O(n) # Space: O(1) # # You are climbing a stair case. It takes n steps to reach to the top. -# -# Each time you can either climb 1 or 2 steps. -# In how many distinct ways can you climb to the top? # +# Each time you can either climb 1 or 2 steps. +# In how many distinct ways can you climb to the top? + class Solution: - # @param n, an integer - # @return an integer + """ + :type n: int + :rtype: int + """ def climbStairs(self, n): prev, current = 0, 1 for i in xrange(n): - prev, current = current, prev + current, + prev, current = current, prev + current, return current + # Time: O(2^n) + # Space: O(n) + def climbStairs1(self, n): + if n == 1: + return 1 + if n == 2: + return 2 + return self.climbStairs(n - 1) + self.climbStairs(n - 2) + if __name__ == "__main__": result = Solution().climbStairs(2) - print result + print(result) diff --git a/Python/clone-graph.py b/Python/clone-graph.py index 87ecec311..a4a0b0338 100644 --- a/Python/clone-graph.py +++ b/Python/clone-graph.py @@ -2,21 +2,21 @@ # Space: O(n) # # Clone an undirected graph. Each node in the graph contains a label and a list of its neighbors. -# -# +# +# # OJ's undirected graph serialization: # Nodes are labeled uniquely. -# +# # We use # as a separator for each node, and , as a separator for node label and each neighbor of the node. # As an example, consider the serialized graph {0,1,2#1,2#2,2}. -# +# # The graph has a total of three nodes, and therefore contains three parts as separated by #. -# +# # First node is labeled as 0. Connect node 0 to both nodes 1 and 2. # Second node is labeled as 1. Connect node 1 to node 2. # Third node is labeled as 2. Connect node 2 to node 2 (itself), thus forming a self-cycle. # Visually, the graph looks like the following: -# +# # 1 # / \ # / \ @@ -38,7 +38,7 @@ def cloneGraph(self, node): return None cloned_node = UndirectedGraphNode(node.label) cloned, queue = {node:cloned_node}, [node] - + while queue: current = queue.pop() for neighbor in current.neighbors: diff --git a/Python/closest-binary-search-tree-value-ii.py b/Python/closest-binary-search-tree-value-ii.py new file mode 100644 index 000000000..3abd38a54 --- /dev/null +++ b/Python/closest-binary-search-tree-value-ii.py @@ -0,0 +1,124 @@ +# Time: O(h + k) +# Space: O(h) + +# Definition for a binary tree node. +# class TreeNode(object): +# def __init__(self, x): +# self.val = x +# self.left = None +# self.right = None + +class Solution(object): + def closestKValues(self, root, target, k): + """ + :type root: TreeNode + :type target: float + :type k: int + :rtype: List[int] + """ + # Helper to make a stack to the next node. + def nextNode(stack, child1, child2): + if stack: + if child2(stack): + stack.append(child2(stack)) + while child1(stack): + stack.append(child1(stack)) + else: + child = stack.pop() + while stack and child is child2(stack): + child = stack.pop() + + # The forward or backward iterator. + backward = lambda stack: stack[-1].left + forward = lambda stack: stack[-1].right + + # Build the stack to the closest node. + stack = [] + while root: + stack.append(root) + root = root.left if target < root.val else root.right + dist = lambda node: abs(node.val - target) + forward_stack = stack[:stack.index(min(stack, key=dist))+1] + + # Get the stack to the next smaller node. + backward_stack = list(forward_stack) + nextNode(backward_stack, backward, forward) + + # Get the closest k values by advancing the iterators of the stacks. + result = [] + for _ in xrange(k): + if forward_stack and \ + (not backward_stack or dist(forward_stack[-1]) < dist(backward_stack[-1])): + result.append(forward_stack[-1].val) + nextNode(forward_stack, forward, backward) + elif backward_stack and \ + (not forward_stack or dist(backward_stack[-1]) <= dist(forward_stack[-1])): + result.append(backward_stack[-1].val) + nextNode(backward_stack, backward, forward) + return result + + +class Solution2(object): + def closestKValues(self, root, target, k): + """ + :type root: TreeNode + :type target: float + :type k: int + :rtype: List[int] + """ + # Helper class to make a stack to the next node. + class BSTIterator: + # @param root, a binary search tree's root node + def __init__(self, stack, child1, child2): + self.stack = list(stack) + self.cur = self.stack.pop() + self.child1 = child1 + self.child2 = child2 + + # @return an integer, the next node + def next(self): + node = None + if self.cur and self.child1(self.cur): + self.stack.append(self.cur) + node = self.child1(self.cur) + while self.child2(node): + self.stack.append(node) + node = self.child2(node) + elif self.stack: + prev = self.cur + node = self.stack.pop() + while node: + if self.child2(node) is prev: + break + else: + prev = node + node = self.stack.pop() if self.stack else None + self.cur = node + return node + + # Build the stack to the closet node. + stack = [] + while root: + stack.append(root) + root = root.left if target < root.val else root.right + dist = lambda node: abs(node.val - target) if node else float("inf") + stack = stack[:stack.index(min(stack, key=dist))+1] + + # The forward or backward iterator. + backward = lambda node: node.left + forward = lambda node: node.right + smaller_it, larger_it = BSTIterator(stack, backward, forward), BSTIterator(stack, forward, backward) + smaller_node, larger_node = smaller_it.next(), larger_it.next() + + # Get the closest k values by advancing the iterators of the stacks. + result = [stack[-1].val] + for _ in xrange(k - 1): + if dist(smaller_node) < dist(larger_node): + result.append(smaller_node.val) + smaller_node = smaller_it.next() + else: + result.append(larger_node.val) + larger_node = larger_it.next() + return result + + diff --git a/Python/closest-binary-search-tree-value.py b/Python/closest-binary-search-tree-value.py new file mode 100644 index 000000000..15d5a9cfc --- /dev/null +++ b/Python/closest-binary-search-tree-value.py @@ -0,0 +1,30 @@ +# Time: O(h) +# Space: O(1) + +# Definition for a binary tree node. +# class TreeNode(object): +# def __init__(self, x): +# self.val = x +# self.left = None +# self.right = None + +class Solution(object): + def closestValue(self, root, target): + """ + :type root: TreeNode + :type target: float + :rtype: int + """ + gap = float("inf") + closest = float("inf") + while root: + if abs(root.val - target) < gap: + gap = abs(root.val - target) + closest = root + if target == root.val: + break + elif target < root.val: + root = root.left + else: + root = root.right + return closest.val diff --git a/Python/closest-leaf-in-a-binary-tree.py b/Python/closest-leaf-in-a-binary-tree.py new file mode 100644 index 000000000..1b5c789d7 --- /dev/null +++ b/Python/closest-leaf-in-a-binary-tree.py @@ -0,0 +1,100 @@ +# Time: O(n) +# Space: O(n) + +# Given a binary tree where every node has a unique value, and a target key k, +# find the value of the closest leaf node to target k in the tree. +# +# Here, closest to a leaf means the least number of edges travelled on the binary tree to reach +# any leaf of the tree. Also, a node is called a leaf if it has no children. +# +# In the following examples, the input tree is represented in flattened form row by row. +# The actual root tree given will be a TreeNode object. +# +# Example 1: +# +# Input: +# root = [1, 3, 2], k = 1 +# Diagram of binary tree: +# 1 +# / \ +# 3 2 +# +# Output: 2 (or 3) +# +# Explanation: Either 2 or 3 is the closest leaf node to the target of 1. +# Example 2: +# +# Input: +# root = [1], k = 1 +# Output: 1 +# +# Explanation: The closest leaf node is the root node itself. +# Example 3: +# +# Input: +# root = [1,2,3,4,null,null,null,5,null,6], k = 2 +# Diagram of binary tree: +# 1 +# / \ +# 2 3 +# / +# 4 +# / +# 5 +# / +# 6 +# +# Output: 3 +# Explanation: The leaf node with value 3 (and not the leaf node with value 6) is closest to the node with value 2. +# Note: +# - root represents a binary tree with at least 1 node and at most 1000 nodes. +# - Every node has a unique node.val in range [1, 1000]. +# - There exists some node in the given binary tree for which node.val == k. + +# Definition for a binary tree node. +# class TreeNode(object): +# def __init__(self, x): +# self.val = x +# self.left = None +# self.right = None + +import collections + + +class Solution(object): + def findClosestLeaf(self, root, k): + """ + :type root: TreeNode + :type k: int + :rtype: int + """ + def traverse(node, neighbors, leaves): + if not node: + return + if not node.left and not node.right: + leaves.add(node.val) + return + if node.left: + neighbors[node.val].append(node.left.val) + neighbors[node.left.val].append(node.val) + traverse(node.left, neighbors, leaves) + if node.right: + neighbors[node.val].append(node.right.val) + neighbors[node.right.val].append(node.val) + traverse(node.right, neighbors, leaves) + + neighbors, leaves = collections.defaultdict(list), set() + traverse(root, neighbors, leaves) + q, lookup = [k], set([k]) + while q: + next_q = [] + for u in q: + if u in leaves: + return u + for v in neighbors[u]: + if v in lookup: + continue + lookup.add(v) + next_q.append(v) + q = next_q + return 0 diff --git a/Python/coin-change-2.py b/Python/coin-change-2.py new file mode 100644 index 000000000..c85c9d196 --- /dev/null +++ b/Python/coin-change-2.py @@ -0,0 +1,45 @@ +# Time: O(n * m) +# Space: O(m) + +# You are given coins of different denominations and a total amount of money. +# Write a function to compute the number of combinations that make up that amount. +# You may assume that you have infinite number of each kind of coin. +# +# Note: You can assume that +# +# 0 <= amount <= 5000 +# 1 <= coin <= 5000 +# the number of coins is less than 500 +# the answer is guaranteed to fit into signed 32-bit integer +# Example 1: +# +# Input: amount = 5, coins = [1, 2, 5] +# Output: 4 +# Explanation: there are four ways to make up the amount: +# 5=5 +# 5=2+2+1 +# 5=2+1+1+1 +# 5=1+1+1+1+1 +# Example 2: +# +# Input: amount = 3, coins = [2] +# Output: 0 +# Explanation: the amount of 3 cannot be made up just with coins of 2. +# Example 3: +# +# Input: amount = 10, coins = [10] +# Output: 1 + +class Solution(object): + def change(self, amount, coins): + """ + :type amount: int + :type coins: List[int] + :rtype: int + """ + dp = [0] * (amount+1) + dp[0] = 1; + for coin in coins: + for i in xrange(coin, amount+1): + dp[i] += dp[i-coin] + return dp[amount] diff --git a/Python/coin-change.py b/Python/coin-change.py new file mode 100644 index 000000000..093d4429b --- /dev/null +++ b/Python/coin-change.py @@ -0,0 +1,38 @@ +# Time: O(n * k), n is the number of coins, k is the amount of money +# Space: O(k) +# +# You are given coins of different denominations and +# a total amount of money amount. Write a function to +# compute the fewest number of coins that you need to +# make up that amount. If that amount of money cannot +# be made up by any combination of the coins, return -1. +# +# Example 1: +# coins = [1, 2, 5], amount = 11 +# return 3 (11 = 5 + 5 + 1) +# +# Example 2: +# coins = [2], amount = 3 +# return -1. +# +# Note: +# You may assume that you have an infinite number of each kind of coin. + +# DP solution. (1680ms) +class Solution(object): + def coinChange(self, coins, amount): + """ + :type coins: List[int] + :type amount: int + :rtype: int + """ + INF = 0x7fffffff # Using float("inf") would be slower. + amounts = [INF] * (amount + 1) + amounts[0] = 0 + for i in xrange(amount + 1): + if amounts[i] != INF: + for coin in coins: + if i + coin <= amount: + amounts[i + coin] = min(amounts[i + coin], amounts[i] + 1) + return amounts[amount] if amounts[amount] != INF else -1 + diff --git a/Python/coin-path.py b/Python/coin-path.py new file mode 100644 index 000000000..6d43de5c4 --- /dev/null +++ b/Python/coin-path.py @@ -0,0 +1,30 @@ +# Time: O(n * B) +# Space: O(n) + +class Solution(object): + def cheapestJump(self, A, B): + """ + :type A: List[int] + :type B: int + :rtype: List[int] + """ + result = [] + if not A or A[-1] == -1: + return result + n = len(A) + dp, next_pos = [float("inf")] * n, [-1] * n + dp[n-1] = A[n-1] + for i in reversed(xrange(n-1)): + if A[i] == -1: + continue + for j in xrange(i+1, min(i+B+1,n)): + if A[i] + dp[j] < dp[i]: + dp[i] = A[i] + dp[j] + next_pos[i] = j + if dp[0] == float("inf"): + return result + k = 0 + while k != -1: + result.append(k+1) + k = next_pos[k] + return result diff --git a/Python/combination-sum-ii.py b/Python/combination-sum-ii.py index 592f55544..fabfdf352 100644 --- a/Python/combination-sum-ii.py +++ b/Python/combination-sum-ii.py @@ -1,20 +1,21 @@ -# Time: O(n! / m!(n-m)!) -# Space: O(m) -# -# Given a collection of candidate numbers (C) and a target number (T), +from __future__ import print_function +# Time: O(k * C(n, k)) +# Space: O(k) +# +# Given a collection of candidate numbers (C) and a target number (T), # find all unique combinations in C where the candidate numbers sums to T. -# +# # Each number in C may only be used once in the combination. -# +# # Note: # All numbers (including target) will be positive integers. # Elements in a combination (a1, a2, ... , ak) must be in non-descending order. (ie, a1 <= a2 <= ... <= ak). # The solution set must not contain duplicate combinations. -# For example, given candidate set 10,1,2,7,6,1,5 and target 8, -# A solution set is: -# [1, 7] -# [1, 2, 5] -# [2, 6] +# For example, given candidate set 10,1,2,7,6,1,5 and target 8, +# A solution set is: +# [1, 7] +# [1, 2, 5] +# [2, 6] # [1, 1, 6] # @@ -26,18 +27,20 @@ def combinationSum2(self, candidates, target): result = [] self.combinationSumRecu(sorted(candidates), result, 0, [], target) return result - + def combinationSumRecu(self, candidates, result, start, intermediate, target): if target == 0: - result.append(intermediate) + result.append(list(intermediate)) prev = 0 while start < len(candidates) and candidates[start] <= target: if prev != candidates[start]: - self.combinationSumRecu(candidates, result, start + 1, intermediate + [candidates[start]], target - candidates[start]) + intermediate.append(candidates[start]) + self.combinationSumRecu(candidates, result, start + 1, intermediate, target - candidates[start]) + intermediate.pop() prev = candidates[start] start += 1 if __name__ == "__main__": candidates, target = [10, 1, 2, 7, 6, 1, 5], 8 - result = Solution().combinationSum2(candidates, target) - print result + result = Solution().combinationSum2(candidates, target) + print(result) diff --git a/Python/combination-sum-iii.py b/Python/combination-sum-iii.py new file mode 100644 index 000000000..f5accf25a --- /dev/null +++ b/Python/combination-sum-iii.py @@ -0,0 +1,45 @@ +# Time: O(k * C(n, k)) +# Space: O(k) +# +# Find all possible combinations of k numbers that add up to a number n, +# given that only numbers from 1 to 9 can be used and each combination should be a unique set of numbers. +# +# Ensure that numbers within the set are sorted in ascending order. +# +# +# Example 1: +# +# Input: k = 3, n = 7 +# +# Output: +# +# [[1,2,4]] +# +# Example 2: +# +# Input: k = 3, n = 9 +# +# Output: +# +# [[1,2,6], [1,3,5], [2,3,4]] +# + +class Solution: + # @param {integer} k + # @param {integer} n + # @return {integer[][]} + def combinationSum3(self, k, n): + result = [] + self.combinationSumRecu(result, [], 1, k, n) + return result + + def combinationSumRecu(self, result, intermediate, start, k, target): + if k == 0 and target == 0: + result.append(list(intermediate)) + elif k < 0: + return + while start < 10 and start * k + k * (k - 1) / 2 <= target: + intermediate.append(start) + self.combinationSumRecu(result, intermediate, start + 1, k - 1, target - start) + intermediate.pop() + start += 1 diff --git a/Python/combination-sum-iv.py b/Python/combination-sum-iv.py new file mode 100644 index 000000000..1b24689b1 --- /dev/null +++ b/Python/combination-sum-iv.py @@ -0,0 +1,47 @@ +# Time: O(nlon + n * t), t is the value of target. +# Space: O(t) + +# Given an integer array with all positive numbers and no duplicates, +# find the number of possible combinations that add up to a positive integer target. +# +# Example: +# +# nums = [1, 2, 3] +# target = 4 +# +# The possible combination ways are: +# (1, 1, 1, 1) +# (1, 1, 2) +# (1, 2, 1) +# (1, 3) +# (2, 1, 1) +# (2, 2) +# (3, 1) +# +# Note that different sequences are counted as different combinations. +# +# Therefore the output is 7. +# Follow up: +# What if negative numbers are allowed in the given array? +# How does it change the problem? +# What limitation we need to add to the question to allow negative numbers? + +class Solution(object): + def combinationSum4(self, nums, target): + """ + :type nums: List[int] + :type target: int + :rtype: int + """ + dp = [0] * (target+1) + dp[0] = 1 + nums.sort() + + for i in xrange(1, target+1): + for j in xrange(len(nums)): + if nums[j] <= i: + dp[i] += dp[i - nums[j]] + else: + break + + return dp[target] diff --git a/Python/combination-sum.py b/Python/combination-sum.py index 6d5edd50b..fdb9a1c74 100644 --- a/Python/combination-sum.py +++ b/Python/combination-sum.py @@ -1,19 +1,20 @@ -# Time: O(n^m) -# Space: O(m) +from __future__ import print_function +# Time: O(k * n^k) +# Space: O(k) # -# Given a set of candidate numbers (C) and a target number (T), +# Given a set of candidate numbers (C) and a target number (T), # find all unique combinations in C where the candidate numbers sums to T. -# +# # The same repeated number may be chosen from C unlimited number of times. -# +# # Note: # All numbers (including target) will be positive integers. # Elements in a combination (a1, a2, ... , ak) must be in non-descending order. (ie, a1 <= a2 <= ... <= ak). # The solution set must not contain duplicate combinations. -# For example, given candidate set 2,3,6,7 and target 7, -# A solution set is: -# [7] -# [2, 2, 3] +# For example, given candidate set 2,3,6,7 and target 7, +# A solution set is: +# [7] +# [2, 2, 3] # class Solution: @@ -24,15 +25,17 @@ def combinationSum(self, candidates, target): result = [] self.combinationSumRecu(sorted(candidates), result, 0, [], target) return result - + def combinationSumRecu(self, candidates, result, start, intermediate, target): if target == 0: - result.append(intermediate) + result.append(list(intermediate)) while start < len(candidates) and candidates[start] <= target: - self.combinationSumRecu(candidates, result, start, intermediate + [candidates[start]], target - candidates[start]) + intermediate.append(candidates[start]) + self.combinationSumRecu(candidates, result, start, intermediate, target - candidates[start]) + intermediate.pop() start += 1 if __name__ == "__main__": candidates, target = [2, 3, 6, 7], 7 - result = Solution().combinationSum(candidates, target) - print result + result = Solution().combinationSum(candidates, target) + print(result) diff --git a/Python/combinations.py b/Python/combinations.py index 1f206daad..9513ec413 100644 --- a/Python/combinations.py +++ b/Python/combinations.py @@ -1,11 +1,12 @@ -# Time: O(n!) -# Space: O(n) -# +from __future__ import print_function +# Time: O(k * C(n, k)) +# Space: O(k) + # Given two integers n and k, return all possible combinations of k numbers out of 1 ... n. -# +# # For example, # If n = 4 and k = 2, a solution is: -# +# # [ # [2,4], # [3,4], @@ -14,23 +15,51 @@ # [1,3], # [1,4], # ] -# -class Solution: - # @return a list of lists of integers +class Solution(object): def combine(self, n, k): + """ + :type n: int + :type k: int + :rtype: List[List[int]] + """ + result, combination = [], [] + i = 1 + while True: + if len(combination) == k: + result.append(combination[:]) + if len(combination) == k or \ + len(combination)+(n-i+1) < k: + if not combination: + break + i = combination.pop()+1 + else: + combination.append(i) + i += 1 + return result + + +class Solution2(object): + def combine(self, n, k): + """ + :type n: int + :type k: int + :rtype: List[List[int]] + """ + def combineDFS(n, start, intermediate, k, result): + if k == 0: + result.append(intermediate[:]) + return + for i in xrange(start, n): + intermediate.append(i+1) + combineDFS(n, i+1, intermediate, k-1, result) + intermediate.pop() + result = [] - self.combineRecu(n, result, 0, [], k) + combineDFS(n, 0, [], k, result) return result - - def combineRecu(self, n, result, start, intermediate, k): - if k == 0: - result.append(intermediate[:]) - for i in xrange(start, n): - intermediate.append(i + 1) - self.combineRecu(n, result, i + 1, intermediate, k - 1) - intermediate.pop() + if __name__ == "__main__": result = Solution().combine(4, 2) - print result + print(result) diff --git a/Python/compare-version-numbers.py b/Python/compare-version-numbers.py index a59d88c29..2cc7ade76 100644 --- a/Python/compare-version-numbers.py +++ b/Python/compare-version-numbers.py @@ -1,32 +1,68 @@ +from __future__ import print_function # Time: O(n) # Space: O(1) -# + # Compare two version numbers version1 and version1. -# If version1 > version2 return 1, if version1 < version2 return -1, otherwise return 0. -# -# You may assume that the version strings are non-empty and contain only digits and the . character. -# The . character does not represent a decimal point and is used to separate number sequences. -# For instance, 2.5 is not "two and a half" or "half way to version three", it is the fifth second-level revision of the second first-level revision. -# +# If version1 > version2 return 1, if version1 < version2 +# return -1, otherwise return 0. +# +# You may assume that the version strings are non-empty and +# contain only digits and the . character. +# The . character does not represent a decimal point and +# is used to separate number sequences. +# For instance, 2.5 is not "two and a half" or "half way to +# version three", it is the fifth second-level revision of +# the second first-level revision. +# # Here is an example of version numbers ordering: -# +# # 0.1 < 1.1 < 1.2 < 13.37 # +import itertools + + +class Solution(object): + def compareVersion(self, version1, version2): + """ + :type version1: str + :type version2: str + :rtype: int + """ + n1, n2 = len(version1), len(version2) + i, j = 0, 0 + while i < n1 or j < n2: + v1, v2 = 0, 0 + while i < n1 and version1[i] != '.': + v1 = v1 * 10 + int(version1[i]) + i += 1 + while j < n2 and version2[j] != '.': + v2 = v2 * 10 + int(version2[j]) + j += 1 + if v1 != v2: + return 1 if v1 > v2 else -1 + i += 1 + j += 1 + + return 0 # Time: O(n) -# Space: O(n), this could be enhanced to O(1) by better but trivial string parsing -class Solution: - # @param a, a string - # @param b, a string - # @return a boolean +# Space: O(n) + + +class Solution2(object): def compareVersion(self, version1, version2): + """ + :type version1: str + :type version2: str + :rtype: int + """ v1, v2 = version1.split("."), version2.split(".") - + if len(v1) > len(v2): v2 += ['0' for _ in xrange(len(v1) - len(v2))] elif len(v1) < len(v2): v1 += ['0' for _ in xrange(len(v2) - len(v1))] - + i = 0 while i < len(v1): if int(v1[i]) > int(v2[i]): @@ -35,10 +71,35 @@ def compareVersion(self, version1, version2): return -1 else: i += 1 - + return 0 + def compareVersion2(self, version1, version2): + """ + :type version1: str + :type version2: str + :rtype: int + """ + v1 = [int(x) for x in version1.split('.')] + v2 = [int(x) for x in version2.split('.')] + while len(v1) != len(v2): + if len(v1) > len(v2): + v2.append(0) + else: + v1.append(0) + return cmp(v1, v2) + + def compareVersion3(self, version1, version2): + splits = (map(int, v.split('.')) for v in (version1, version2)) + return cmp(*zip(*itertools.izip_longest(*splits, fillvalue=0))) + + def compareVersion4(self, version1, version2): + main1, _, rest1 = ('0' + version1).partition('.') + main2, _, rest2 = ('0' + version2).partition('.') + return cmp(int(main1), int(main2)) or len(rest1 + rest2) and self.compareVersion4(rest1, rest2) + + if __name__ == "__main__": - print Solution().compareVersion("21.0", "121.1.0") - print Solution().compareVersion("01", "1") - print Solution().compareVersion("1", "1.0") \ No newline at end of file + print(Solution().compareVersion("21.0", "121.1.0")) + print(Solution().compareVersion("01", "1")) + print(Solution().compareVersion("1", "1.0")) diff --git a/Python/complex-number-multiplication.py b/Python/complex-number-multiplication.py new file mode 100644 index 000000000..63fc97772 --- /dev/null +++ b/Python/complex-number-multiplication.py @@ -0,0 +1,32 @@ +# Time: O(1) +# Space: O(1) + +# Given two strings representing two complex numbers. +# +# You need to return a string representing their multiplication. Note i2 = -1 according to the definition. +# +# Example 1: +# Input: "1+1i", "1+1i" +# Output: "0+2i" +# Explanation: (1 + i) * (1 + i) = 1 + i2 + 2 * i = 2i, and you need convert it to the form of 0+2i. +# Example 2: +# Input: "1+-1i", "1+-1i" +# Output: "0+-2i" +# Explanation: (1 - i) * (1 - i) = 1 + i2 - 2 * i = -2i, and you need convert it to the form of 0+-2i. +# Note: +# +# The input strings will not have extra blank. +# The input strings will be given in the form of a+bi, +# where the integer a and b will both belong to the range of [-100, 100]. +# And the output should be also in this form. + +class Solution(object): + def complexNumberMultiply(self, a, b): + """ + :type a: str + :type b: str + :rtype: str + """ + ra, ia = map(int, a[:-1].split('+')) + rb, ib = map(int, b[:-1].split('+')) + return '%d+%di' % (ra * rb - ia * ib, ra * ib + ia * rb) diff --git a/Python/concatenated-words.py b/Python/concatenated-words.py new file mode 100644 index 000000000..6fceb631c --- /dev/null +++ b/Python/concatenated-words.py @@ -0,0 +1,47 @@ +# Time: O(n * l^2) +# Space: O(n * l) + +# Given a list of words, please write a program that returns +# all concatenated words in the given list of words. +# +# A concatenated word is defined as a string that is comprised entirely of +# at least two shorter words in the given array. +# +# Example: +# Input: ["cat","cats","catsdogcats","dog","dogcatsdog","hippopotamuses","rat","ratcatdogcat"] +# +# Output: ["catsdogcats","dogcatsdog","ratcatdogcat"] +# +# Explanation: "catsdogcats" can be concatenated by "cats", "dog" and "cats"; +# "dogcatsdog" can be concatenated by "dog", "cats" and "dog"; +# "ratcatdogcat" can be concatenated by "rat", "cat", "dog" and "cat". +# Note: +# The number of elements of the given array will not exceed 10,000 +# The length sum of elements in the given array will not exceed 600,000. +# All the input string will only include lower case letters. +# The returned elements order does not matter. + +class Solution(object): + def findAllConcatenatedWordsInADict(self, words): + """ + :type words: List[str] + :rtype: List[str] + """ + lookup = set(words) + result = [] + for word in words: + dp = [False] * (len(word)+1) + dp[0] = True + for i in xrange(len(word)): + if not dp[i]: + continue + + for j in xrange(i+1, len(word)+1): + if j - i < len(word) and word[i:j] in lookup: + dp[j] = True + + if dp[len(word)]: + result.append(word) + break + + return result diff --git a/Python/consecutive-numbers-sum.py b/Python/consecutive-numbers-sum.py new file mode 100644 index 000000000..40cf7e04f --- /dev/null +++ b/Python/consecutive-numbers-sum.py @@ -0,0 +1,53 @@ +# Time: O(sqrt(n)) +# Space: O(1) + +# Given a positive integer N, +# how many ways can we write it as a sum of +# consecutive positive integers? +# +# Example 1: +# +# Input: 5 +# Output: 2 +# Explanation: 5 = 5 = 2 + 3 +# Example 2: +# +# Input: 9 +# Output: 3 +# Explanation: 9 = 9 = 4 + 5 = 2 + 3 + 4 +# Example 3: +# +# Input: 15 +# Output: 4 +# Explanation: 15 = 15 = 8 + 7 = 4 + 5 + 6 = 1 + 2 + 3 + 4 + 5 +# Note: 1 <= N <= 10 ^ 9. + + +class Solution(object): + def consecutiveNumbersSum(self, N): + """ + :type N: int + :rtype: int + """ + # x + x+1 + x+2 + ... + x+l-1 = N = 2^k * M, where M is odd + # => l*x + (l-1)*l/2 = 2^k * M + # => x = (2^k * M -(l-1)*l/2)/l= 2^k * M/l - (l-1)/2 is integer + # => l could be 2 or any odd factor of M (excluding M) + # s.t. x = 2^k * M/l - (l-1)/2 is integer, and also unique + # => the answer is the number of all odd factors of M + # if prime factorization of N is 2^k * p1^a * p2^b * .. + # => answer is the number of all odd factors = (a+1) * (b+1) * ... + result = 1 + while N % 2 == 0: + N /= 2 + i = 3 + while i*i <= N: + count = 0 + while N % i == 0: + N /= i + count += 1 + result *= count+1 + i += 2 + if N > 1: + result *= 2 + return result diff --git a/Python/construct-binary-tree-from-inorder-and-postorder-traversal.py b/Python/construct-binary-tree-from-inorder-and-postorder-traversal.py index 991009a93..c09e28245 100644 --- a/Python/construct-binary-tree-from-inorder-and-postorder-traversal.py +++ b/Python/construct-binary-tree-from-inorder-and-postorder-traversal.py @@ -1,8 +1,9 @@ +from __future__ import print_function # Time: O(n) # Space: O(n) # # Given inorder and postorder traversal of a tree, construct the binary tree. -# +# # Note: # You may assume that duplicates do not exist in the tree. # @@ -23,7 +24,7 @@ def buildTree(self, inorder, postorder): for i, num in enumerate(inorder): lookup[num] = i return self.buildTreeRecu(lookup, postorder, inorder, len(postorder), 0, len(inorder)) - + def buildTreeRecu(self, lookup, postorder, inorder, post_end, in_start, in_end): if in_start == in_end: return None @@ -37,6 +38,6 @@ def buildTreeRecu(self, lookup, postorder, inorder, post_end, in_start, in_end): inorder = [2, 1, 3] postorder = [2, 3, 1] result = Solution().buildTree(inorder, postorder) - print result.val - print result.left.val - print result.right.val \ No newline at end of file + print(result.val) + print(result.left.val) + print(result.right.val) \ No newline at end of file diff --git a/Python/construct-binary-tree-from-preorder-and-inorder-traversal.py b/Python/construct-binary-tree-from-preorder-and-inorder-traversal.py index e3196454d..3da09b572 100644 --- a/Python/construct-binary-tree-from-preorder-and-inorder-traversal.py +++ b/Python/construct-binary-tree-from-preorder-and-inorder-traversal.py @@ -1,8 +1,9 @@ +from __future__ import print_function # Time: O(n) # Space: O(n) # # Given preorder and inorder traversal of a tree, construct the binary tree. -# +# # Note: # You may assume that duplicates do not exist in the tree. # @@ -23,7 +24,7 @@ def buildTree(self, preorder, inorder): for i, num in enumerate(inorder): lookup[num] = i return self.buildTreeRecu(lookup, preorder, inorder, 0, 0, len(inorder)) - + def buildTreeRecu(self, lookup, preorder, inorder, pre_start, in_start, in_end): if in_start == in_end: return None @@ -37,6 +38,6 @@ def buildTreeRecu(self, lookup, preorder, inorder, pre_start, in_start, in_end): preorder = [1, 2, 3] inorder = [2, 1, 3] result = Solution().buildTree(preorder, inorder) - print result.val - print result.left.val - print result.right.val + print(result.val) + print(result.left.val) + print(result.right.val) diff --git a/Python/construct-binary-tree-from-string.py b/Python/construct-binary-tree-from-string.py new file mode 100644 index 000000000..d8d085036 --- /dev/null +++ b/Python/construct-binary-tree-from-string.py @@ -0,0 +1,32 @@ +# Time: O(n) +# Space: O(h) + +# Definition for a binary tree node. +class TreeNode(object): + def __init__(self, x): + self.val = x + self.left = None + self.right = None + +class Solution(object): + def str2tree(self, s): + """ + :type s: str + :rtype: TreeNode + """ + def str2treeHelper(s, i): + start = i + if s[i] == '-': i += 1 + while i < len(s) and s[i].isdigit(): i += 1 + node = TreeNode(int(s[start:i])) + if i < len(s) and s[i] == '(': + i += 1 + node.left, i = str2treeHelper(s, i) + i += 1 + if i < len(s) and s[i] == '(': + i += 1 + node.right, i = str2treeHelper(s, i) + i += 1 + return node, i + + return str2treeHelper(s, 0)[0] if s else None diff --git a/Python/construct-quad-tree.py b/Python/construct-quad-tree.py new file mode 100644 index 000000000..39e4204a8 --- /dev/null +++ b/Python/construct-quad-tree.py @@ -0,0 +1,62 @@ +# Time: O(n) +# Space: O(h) + +# We want to use quad trees to store an N x N boolean grid. +# Each cell in the grid can only be true or false. +# The root node represents the whole grid. For each node, +# it will be subdivided into four children nodes until the values +# in the region it represents are all the same. +# +# Each node has another two boolean attributes : isLeaf and val. isLeaf is true +# if and only if the node is a leaf node. +# The val attribute for a leaf node contains the value of the region it represents. +# +# Your task is to use a quad tree to represent a given grid. +# The following example may help you understand the problem better: +# +# Given the 8 x 8 grid below, we want to construct the corresponding quad tree: +# It can be divided according to the definition above: +# +# The corresponding quad tree should be as following, +# where each node is represented as a (isLeaf, val) pair. +# +# For the non-leaf nodes, val can be arbitrary, so it is represented as *. +# +# Note: +# - N is less than 1000 and guaranteened to be a power of 2. +# - If you want to know more about the quad tree, you can refer to its wiki. + +# Definition for a QuadTree node. +class Node(object): + def __init__(self, val, isLeaf, topLeft, topRight, bottomLeft, bottomRight): + self.val = val + self.isLeaf = isLeaf + self.topLeft = topLeft + self.topRight = topRight + self.bottomLeft = bottomLeft + self.bottomRight = bottomRight + + +class Solution(object): + def construct(self, grid): + """ + :type grid: List[List[int]] + :rtype: Node + """ + def dfs(grid, x, y, l): + if l == 1: + return Node(grid[x][y] == 1, True, None, None, None, None) + half = l // 2 + topLeftNode = dfs(grid, x, y, half) + topRightNode = dfs(grid, x, y+half, half) + bottomLeftNode = dfs(grid, x+half, y, half) + bottomRightNode = dfs(grid, x+half, y+half, half) + if topLeftNode.isLeaf and topRightNode.isLeaf and \ + bottomLeftNode.isLeaf and bottomRightNode.isLeaf and \ + topLeftNode.val == topRightNode.val == bottomLeftNode.val == bottomRightNode.val: + return Node(topLeftNode.val, True, None, None, None, None) + return Node(True, False, topLeftNode, topRightNode, bottomLeftNode, bottomRightNode) + + if not grid: + return None + return dfs(grid, 0, 0, len(grid)) diff --git a/Python/construct-string-from-binary-tree.py b/Python/construct-string-from-binary-tree.py new file mode 100644 index 000000000..b2dac24ee --- /dev/null +++ b/Python/construct-string-from-binary-tree.py @@ -0,0 +1,58 @@ +# Time: O(n) +# Space: O(h) + +# You need to construct a string consists of parenthesis and integers from a binary tree with +# the preorder traversing way. +# +# The null node needs to be represented by empty parenthesis pair "()". +# And you need to omit all the empty parenthesis pairs that don't affect +# the one-to-one mapping relationship between the string and the original binary tree. +# +# Example 1: +# Input: Binary tree: [1,2,3,4] +# 1 +# / \ +# 2 3 +# / +# 4 +# +# Output: "1(2(4))(3)" +# +# Explanation: Originallay it needs to be "1(2(4)())(3()())", +# but you need to omit all the unnecessary empty parenthesis pairs. +# And it will be "1(2(4))(3)". +# Example 2: +# Input: Binary tree: [1,2,3,null,4] +# 1 +# / \ +# 2 3 +# \ +# 4 +# +# Output: "1(2()(4))(3)" +# +# Explanation: Almost the same as the first example, +# except we can't omit the first parenthesis pair to break the one-to-one mapping relationship +# between the input and the output. + + +# Definition for a binary tree node. +# class TreeNode(object): +# def __init__(self, x): +# self.val = x +# self.left = None +# self.right = None + +class Solution(object): + def tree2str(self, t): + """ + :type t: TreeNode + :rtype: str + """ + if not t: return "" + s = str(t.val) + if t.left or t.right: + s += "(" + self.tree2str(t.left) + ")" + if t.right: + s += "(" + self.tree2str(t.right) + ")" + return s diff --git a/Python/construct-the-rectangle.py b/Python/construct-the-rectangle.py new file mode 100644 index 000000000..400d5f08c --- /dev/null +++ b/Python/construct-the-rectangle.py @@ -0,0 +1,37 @@ +# Time: O(1) +# Space: O(1) + +# For a web developer, it is very important to know how to design a web page's size. +# So, given a specific rectangular web page’s area, +# your job by now is to design a rectangular web page, whose length L +# and width W satisfy the following requirements: +# +# 1. The area of the rectangular web page you designed must equal to the given target area. +# +# 2. The width W should not be larger than the length L, which means L >= W. +# +# 3. The difference between length L and width W should be as small as possible. +# You need to output the length L and the width W of the web page you designed in sequence. +# Example: +# Input: 4 +# Output: [2, 2] +# Explanation: The target area is 4, and all the possible ways to construct it are [1,4], [2,2], [4,1]. +# But according to requirement 2, [1,4] is illegal; according to requirement 3, +# [4,1] is not optimal compared to [2,2]. So the length L is 2, and the width W is 2. +# Note: +# The given area won't exceed 10,000,000 and is a positive integer +# The web page's width and length you designed must be positive integers. + +import math + + +class Solution(object): + def constructRectangle(self, area): + """ + :type area: int + :rtype: List[int] + """ + w = int(math.sqrt(area)) + while area % w: + w -= 1 + return [area // w, w] diff --git a/Python/contain-virus.py b/Python/contain-virus.py new file mode 100644 index 000000000..3fc0e7569 --- /dev/null +++ b/Python/contain-virus.py @@ -0,0 +1,116 @@ +# Time: O((m * n)^(4/3)), days = O((m * n)^(1/3)) +# Space: O(m * n) + +# A virus is spreading rapidly, and your task is to quarantine the infected area by installing walls. +# +# The world is modeled as a 2-D array of cells, +# where 0 represents uninfected cells, +# and 1 represents cells contaminated with the virus. +# A wall (and only one wall) can be installed between any two 4-directionally adjacent cells, +# on the shared boundary. +# +# Every night, the virus spreads to all neighboring cells in all four directions unless blocked by a wall. +# Resources are limited. +# Each day, you can install walls around only one region -- +# the affected area (continuous block of infected cells) that +# threatens the most uninfected cells the following night. +# There will never be a tie. +# +# Can you save the day? If so, what is the number of walls required? +# If not, and the world becomes fully infected, return the number of walls used. +# +# Example 1: +# Input: grid = +# [[0,1,0,0,0,0,0,1], +# [0,1,0,0,0,0,0,1], +# [0,0,0,0,0,0,0,1], +# [0,0,0,0,0,0,0,0]] +# Output: 10 +# Explanation: +# There are 2 contaminated regions. +# On the first day, add 5 walls to quarantine the viral region on the left. The board after the virus spreads is: +# +# [[0,1,0,0,0,0,1,1], +# [0,1,0,0,0,0,1,1], +# [0,0,0,0,0,0,1,1], +# [0,0,0,0,0,0,0,1]] +# +# On the second day, add 5 walls to quarantine the viral region on the right. The virus is fully contained. +# +# Example 2: +# Input: grid = +# [[1,1,1], +# [1,0,1], +# [1,1,1]] +# Output: 4 +# Explanation: Even though there is only one cell saved, there are 4 walls built. +# Notice that walls are only built on the shared boundary of two different cells. +# +# Example 3: +# Input: grid = +# [[1,1,1,0,0,0,0,0,0], +# [1,0,1,0,1,1,1,1,1], +# [1,1,1,0,0,0,0,0,0]] +# Output: 13 +# +# Explanation: The region on the left only builds two new walls. +# Note: +# - The number of rows and columns of grid will each be in the range [1, 50]. +# - Each grid[i][j] will be either 0 or 1. +# - Throughout the described process, there is always a contiguous viral region +# that will infect strictly more uncontaminated squares in the next round. + +class Solution(object): + def containVirus(self, grid): + """ + :type grid: List[List[int]] + :rtype: int + """ + directions = [(0, 1), (0, -1), (-1, 0), (1, 0)] + + def dfs(grid, r, c, lookup, regions, frontiers, perimeters): + if (r, c) in lookup: + return + lookup.add((r, c)) + regions[-1].add((r, c)) + for d in directions: + nr, nc = r+d[0], c+d[1] + if not (0 <= nr < len(grid) and \ + 0 <= nc < len(grid[r])): + continue + if grid[nr][nc] == 1: + dfs(grid, nr, nc, lookup, regions, frontiers, perimeters) + elif grid[nr][nc] == 0: + frontiers[-1].add((nr, nc)) + perimeters[-1] += 1 + + result = 0 + while True: + lookup, regions, frontiers, perimeters = set(), [], [], [] + for r, row in enumerate(grid): + for c, val in enumerate(row): + if val == 1 and (r, c) not in lookup: + regions.append(set()) + frontiers.append(set()) + perimeters.append(0) + dfs(grid, r, c, lookup, regions, frontiers, perimeters) + + if not regions: break + + triage_idx = frontiers.index(max(frontiers, key = len)) + for i, region in enumerate(regions): + if i == triage_idx: + result += perimeters[i] + for r, c in region: + grid[r][c] = -1 + continue + for r, c in region: + for d in directions: + nr, nc = r+d[0], c+d[1] + if not (0 <= nr < len(grid) and \ + 0 <= nc < len(grid[r])): + continue + if grid[nr][nc] == 0: + grid[nr][nc] = 1 + + return result diff --git a/Python/container-with-most-water.py b/Python/container-with-most-water.py index aa81a7018..6c8016d76 100644 --- a/Python/container-with-most-water.py +++ b/Python/container-with-most-water.py @@ -1,13 +1,14 @@ +from __future__ import print_function # Time: O(n) # Space: O(1) # -# Given n non-negative integers a1, a2, ..., an, -# where each represents a point at coordinate (i, ai). -# n vertical lines are drawn such that the two endpoints of -# line i is at (i, ai) and (i, 0). Find two lines, -# which together with x-axis forms a container, +# Given n non-negative integers a1, a2, ..., an, +# where each represents a point at coordinate (i, ai). +# n vertical lines are drawn such that the two endpoints of +# line i is at (i, ai) and (i, 0). Find two lines, +# which together with x-axis forms a container, # such that the container contains the most water. -# +# # Note: You may not slant the container. # @@ -22,8 +23,8 @@ def maxArea(self, height): else: j -= 1 return max_area - + if __name__ == "__main__": height = [1, 2, 3, 4, 3, 2, 1, 5] result = Solution().maxArea(height) - print result \ No newline at end of file + print(result) \ No newline at end of file diff --git a/Python/contains-duplicate-ii.py b/Python/contains-duplicate-ii.py new file mode 100644 index 000000000..ca5206a55 --- /dev/null +++ b/Python/contains-duplicate-ii.py @@ -0,0 +1,24 @@ +# Time: O(n) +# Space: O(n) +# +# Given an array of integers and an integer k, return true if +# and only if there are two distinct indices i and j in the array +# such that nums[i] = nums[j] and the difference between i and j is at most k. +# + +class Solution: + # @param {integer[]} nums + # @param {integer} k + # @return {boolean} + def containsNearbyDuplicate(self, nums, k): + lookup = {} + for i, num in enumerate(nums): + if num not in lookup: + lookup[num] = i + else: + # It the value occurs before, check the difference. + if i - lookup[num] <= k: + return True + # Update the index of the value. + lookup[num] = i + return False diff --git a/Python/contains-duplicate-iii.py b/Python/contains-duplicate-iii.py new file mode 100644 index 000000000..778be9eb5 --- /dev/null +++ b/Python/contains-duplicate-iii.py @@ -0,0 +1,38 @@ +# Time: O(n * t) +# Space: O(max(k, t)) +# +# Given an array of integers, find out whether there +# are two distinct inwindowes i and j in the array such +# that the difference between nums[i] and nums[j] is +# at most t and the difference between i and j is at +# most k. +# + +# This is not the best solution +# since there is no built-in bst structure in Python. +# The better solution could be found in C++ solution. + +import collections + + +class Solution: + # @param {integer[]} nums + # @param {integer} k + # @param {integer} t + # @return {boolean} + def containsNearbyAlmostDuplicate(self, nums, k, t): + if k < 0 or t < 0: + return False + window = collections.OrderedDict() + for n in nums: + # Make sure window size + if len(window) > k: + window.popitem(False) + + bucket = n if not t else n // t + # At most 2t items. + for m in (window.get(bucket - 1), window.get(bucket), window.get(bucket + 1)): + if m is not None and abs(n - m) <= t: + return True + window[bucket] = n + return False diff --git a/Python/contains-duplicate.py b/Python/contains-duplicate.py new file mode 100644 index 000000000..a6ba14224 --- /dev/null +++ b/Python/contains-duplicate.py @@ -0,0 +1,13 @@ +# Time: O(n) +# Space: O(n) +# +# Given an array of integers, find if the array contains any duplicates. +# Your function should return true if any value appears at least twice in the array, +# and it should return false if every element is distinct. +# + +class Solution: + # @param {integer[]} nums + # @return {boolean} + def containsDuplicate(self, nums): + return len(nums) > len(set(nums)) diff --git a/Python/contiguous-array.py b/Python/contiguous-array.py new file mode 100644 index 000000000..104194c0b --- /dev/null +++ b/Python/contiguous-array.py @@ -0,0 +1,31 @@ +# Time: O(n) +# Space: O(n) + +# Given a binary array, find the maximum length of a contiguous subarray with equal number of 0 and 1. +# +# Example 1: +# Input: [0,1] +# Output: 2 +# Explanation: [0, 1] is the longest contiguous subarray with equal number of 0 and 1. +# Example 2: +# Input: [0,1,0] +# Output: 2 +# Explanation: [0, 1] (or [1, 0]) is a longest contiguous subarray with equal number of 0 and 1. +# Note: The length of the given binary array will not exceed 50,000. + +class Solution(object): + def findMaxLength(self, nums): + """ + :type nums: List[int] + :rtype: int + """ + result, count = 0, 0 + lookup = {0: -1} + for i, num in enumerate(nums): + count += 1 if num == 1 else -1 + if count in lookup: + result = max(result, i - lookup[count]) + else: + lookup[count] = i + + return result diff --git a/Python/continuous-subarray-sum.py b/Python/continuous-subarray-sum.py new file mode 100644 index 000000000..7e46a53bc --- /dev/null +++ b/Python/continuous-subarray-sum.py @@ -0,0 +1,40 @@ +# Time: O(n) +# Space: O(k) + +# Given a list of non-negative numbers and a target integer k, +# write a function to check if the array has a continuous subarray +# of size at least 2 that sums up to the multiple of k, that is, +# sums up to n*k where n is also an integer. +# +# Example 1: +# Input: [23, 2, 4, 6, 7], k=6 +# Output: True +# Explanation: Because [2, 4] is a continuous subarray of size 2 and sums up to 6. +# Example 2: +# Input: [23, 2, 6, 4, 7], k=6 +# Output: True +# Explanation: Because [23, 2, 6, 4, 7] is an continuous subarray of size 5 and sums up to 42. +# Note: +# The length of the array won't exceed 10,000. +# You may assume the sum of all the numbers is in the range of a signed 32-bit integer. + +class Solution(object): + def checkSubarraySum(self, nums, k): + """ + :type nums: List[int] + :type k: int + :rtype: bool + """ + count = 0 + lookup = {0: -1} + for i, num in enumerate(nums): + count += num + if k: + count %= k + if count in lookup: + if i - lookup[count] > 1: + return True + else: + lookup[count] = i + + return False diff --git a/Python/convert-a-number-to-hexadecimal.py b/Python/convert-a-number-to-hexadecimal.py new file mode 100644 index 000000000..49617b167 --- /dev/null +++ b/Python/convert-a-number-to-hexadecimal.py @@ -0,0 +1,56 @@ +# Time: O(logn) +# Space: O(1) + +# Given an integer, write an algorithm to convert it to hexadecimal. +# For negative integer, two’s complement method is used. +# +# IMPORTANT: +# You must not use any method provided by the library which converts/formats +# the number to hex directly. Such solution will result in disqualification of +# all your submissions to this problem. Users may report such solutions after the +# contest ends and we reserve the right of final decision and interpretation +# in the case of reported solutions. +# +# Note: +# +# All letters in hexadecimal (a-f) must be in lowercase. +# The hexadecimal string must not contain extra leading 0s. If the number is zero, +# it is represented by a single zero character '0'; otherwise, +# the first character in the hexadecimal string will not be the zero character. +# The given number is guaranteed to fit within the range of a 32-bit signed integer. +# You must not use any method provided by the library which converts/formats the number to hex directly. +# Example 1: +# +# Input: +# 26 +# +# Output: +# "1a" +# Example 2: +# +# Input: +# -1 +# +# Output: +# "ffffffff" + +class Solution(object): + def toHex(self, num): + """ + :type num: int + :rtype: str + """ + if not num: + return "0" + + result = [] + while num and len(result) != 8: + h = num & 15 + if h < 10: + result.append(str(chr(ord('0') + h))) + else: + result.append(str(chr(ord('a') + h-10))) + num >>= 4 + result.reverse() + + return "".join(result) diff --git a/Python/convert-binary-search-tree-to-sorted-doubly-linked-list.py b/Python/convert-binary-search-tree-to-sorted-doubly-linked-list.py new file mode 100644 index 000000000..95748c003 --- /dev/null +++ b/Python/convert-binary-search-tree-to-sorted-doubly-linked-list.py @@ -0,0 +1,30 @@ +# Time: O(n) +# Space: O(h) + +# Definition for a Node. +class Node(object): + def __init__(self, val, left, right): + self.val = val + self.left = left + self.right = right + + +class Solution(object): + def treeToDoublyList(self, root): + """ + :type root: Node + :rtype: Node + """ + if not root: + return None + left_head, left_tail, right_head, right_tail = root, root, root, root + if root.left: + left_head = self.treeToDoublyList(root.left) + left_tail = left_head.left + if root.right: + right_head = self.treeToDoublyList(root.right) + right_tail = right_head.left + left_tail.right, right_head.left = root, root + root.left, root.right = left_tail, right_head + left_head.left, right_tail.right = right_tail, left_head + return left_head diff --git a/Python/convert-bst-to-greater-tree.py b/Python/convert-bst-to-greater-tree.py new file mode 100644 index 000000000..5262d45dd --- /dev/null +++ b/Python/convert-bst-to-greater-tree.py @@ -0,0 +1,47 @@ +# Time: O(n) +# Space: O(h) + +# Given a Binary Search Tree (BST), +# convert it to a Greater Tree such that every key of +# the original BST is changed to the original key plus sum of +# all keys greater than the original key in BST. +# +# Example: +# +# Input: The root of a Binary Search Tree like this: +# 5 +# / \ +# 2 13 +# +# Output: The root of a Greater Tree like this: +# 18 +# / \ +# 20 13 + +# Definition for a binary tree node. +# class TreeNode(object): +# def __init__(self, x): +# self.val = x +# self.left = None +# self.right = None + +class Solution(object): + def convertBST(self, root): + """ + :type root: TreeNode + :rtype: TreeNode + """ + def convertBSTHelper(root, cur_sum): + if not root: + return cur_sum + + if root.right: + cur_sum = convertBSTHelper(root.right, cur_sum) + cur_sum += root.val + root.val = cur_sum; + if root.left: + cur_sum = convertBSTHelper(root.left, cur_sum) + return cur_sum + + convertBSTHelper(root, 0) + return root diff --git a/Python/convert-sorted-array-to-binary-search-tree.py b/Python/convert-sorted-array-to-binary-search-tree.py index 946a2a163..57d9165bf 100644 --- a/Python/convert-sorted-array-to-binary-search-tree.py +++ b/Python/convert-sorted-array-to-binary-search-tree.py @@ -1,7 +1,8 @@ +from __future__ import print_function # Time: O(n) # Space: O(logn) # -# Given an array where elements are sorted in ascending order, +# Given an array where elements are sorted in ascending order, # convert it to a height balanced BST. # # Definition for a binary tree node @@ -11,24 +12,44 @@ def __init__(self, x): self.left = None self.right = None -class Solution: - # @param num, a list of integers - # @return a tree node - def sortedArrayToBST(self, num): - return self.sortedArrayToBSTRecu(num, 0, len(num)) - - def sortedArrayToBSTRecu(self, num, start, end): + +class Solution(object): + def sortedArrayToBST(self, nums): + """ + :type nums: List[int] + :rtype: TreeNode + """ + return self.sortedArrayToBSTRecu(nums, 0, len(nums)) + + def sortedArrayToBSTRecu(self, nums, start, end): if start == end: return None - mid = start + (end - start) / 2 - node = TreeNode(num[mid]) - node.left = self.sortedArrayToBSTRecu(num, start, mid) - node.right = self.sortedArrayToBSTRecu(num, mid + 1, end) + mid = start + self.perfect_tree_pivot(end - start) + node = TreeNode(nums[mid]) + node.left = self.sortedArrayToBSTRecu(nums, start, mid) + node.right = self.sortedArrayToBSTRecu(nums, mid + 1, end) return node - + + def perfect_tree_pivot(self, n): + """ + Find the point to partition n keys for a perfect binary search tree + """ + x = 1 + # find a power of 2 <= n//2 + # while x <= n//2: # this loop could probably be written more elegantly :) + # x *= 2 + x = 1 << (n.bit_length() - 1) # use the left bit shift, same as multiplying x by 2**n-1 + + if x // 2 - 1 <= (n - x): + return x - 1 # case 1: the left subtree of the root is perfect and the right subtree has less nodes + else: + return n - x // 2 # case 2 == n - (x//2 - 1) - 1 : the left subtree of the root + # has more nodes and the right subtree is perfect. + + if __name__ == "__main__": num = [1, 2, 3] result = Solution().sortedArrayToBST(num) - print result.val - print result.left.val - print result.right.val + print(result.val) + print(result.left.val) + print(result.right.val) diff --git a/Python/convert-sorted-list-to-binary-search-tree.py b/Python/convert-sorted-list-to-binary-search-tree.py index f0624af6c..4875bcba4 100644 --- a/Python/convert-sorted-list-to-binary-search-tree.py +++ b/Python/convert-sorted-list-to-binary-search-tree.py @@ -1,7 +1,8 @@ +from __future__ import print_function # Time: O(n) # Space: O(logn) # -# Given a singly linked list where elements are sorted in ascending order, +# Given a singly linked list where elements are sorted in ascending order, # convert it to a height balanced BST. # # Definition for a binary tree node @@ -27,14 +28,14 @@ def sortedListToBST(self, head): current, length = current.next, length + 1 self.head = head return self.sortedListToBSTRecu(0, length) - + def sortedListToBSTRecu(self, start, end): if start == end: return None mid = start + (end - start) / 2 left = self.sortedListToBSTRecu(start, mid) current = TreeNode(self.head.val) - current.left = left + current.left = left self.head = self.head.next current.right = self.sortedListToBSTRecu(mid + 1, end) return current @@ -44,6 +45,6 @@ def sortedListToBSTRecu(self, start, end): head.next = ListNode(2) head.next.next = ListNode(3) result = Solution().sortedListToBST(head) - print result.val - print result.left.val - print result.right.val \ No newline at end of file + print(result.val) + print(result.left.val) + print(result.right.val) \ No newline at end of file diff --git a/Python/convex-polygon.py b/Python/convex-polygon.py new file mode 100644 index 000000000..a552142c3 --- /dev/null +++ b/Python/convex-polygon.py @@ -0,0 +1,22 @@ +# Time: O(n) +# Space: O(1) + +class Solution(object): + def isConvex(self, points): + """ + :type points: List[List[int]] + :rtype: bool + """ + def det(A): + return A[0][0]*A[1][1] - A[0][1]*A[1][0] + + n, prev, curr = len(points), 0, None + for i in xrange(len(points)): + A = [[points[(i+j) % n][0] - points[i][0], points[(i+j) % n][1] - points[i][1]] for j in (1, 2)] + curr = det(A) + if curr: + if curr * prev < 0: + return False + prev = curr + return True + diff --git a/Python/copy-list-with-random-pointer.py b/Python/copy-list-with-random-pointer.py index 9f4e8675f..695b144c5 100644 --- a/Python/copy-list-with-random-pointer.py +++ b/Python/copy-list-with-random-pointer.py @@ -1,9 +1,10 @@ +from __future__ import print_function # Time: O(n) # Space: O(1) # # A linked list is given such that each node contains an additional random pointer # which could point to any node in the list or null. -# +# # Return a deep copy of the list. # @@ -25,14 +26,14 @@ def copyRandomList(self, head): copied.next = current.next current.next = copied current = copied.next - + # update random node in copied list current = head while current: if current.random: current.next.random = current.random.next current = current.next.next - + # split copied list from combined one dummy = RandomListNode(0) copied_current, current = dummy, head @@ -50,19 +51,19 @@ class Solution2: def copyRandomList(self, head): dummy = RandomListNode(0) current, prev, copies = head, dummy, {} - + while current: copied = RandomListNode(current.label) copies[current] = copied prev.next = copied prev, current = prev.next, current.next - + current = head while current: if current.random: copies[current].random = copies[current.random] current = current.next - + return dummy.next @@ -71,6 +72,6 @@ def copyRandomList(self, head): head.next = RandomListNode(2) head.random = head.next result = Solution().copyRandomList(head) - print result.label - print result.next.label - print result.random.label \ No newline at end of file + print(result.label) + print(result.next.label) + print(result.random.label) \ No newline at end of file diff --git a/Python/count-and-say.py b/Python/count-and-say.py index 5b55c29a4..5b7d04825 100644 --- a/Python/count-and-say.py +++ b/Python/count-and-say.py @@ -1,14 +1,15 @@ -# Time: O(n^2) -# Space: O(n) +from __future__ import print_function +# Time: O(n * 2^n) +# Space: O(2^n) # # The count-and-say sequence is the sequence of integers beginning as follows: # 1, 11, 21, 1211, 111221, ... -# +# # 1 is read off as "one 1" or 11. # 11 is read off as "two 1s" or 21. # 21 is read off as "one 2, then one 1" or 1211. # Given an integer n, generate the nth sequence. -# +# # Note: The sequence of integers will be represented as a string. # @@ -19,20 +20,20 @@ def countAndSay(self, n): for i in xrange(n - 1): seq = self.getNext(seq) return seq - + def getNext(self, seq): i, next_seq = 0, "" while i < len(seq): - cnt = 1 + cnt = 1 while i < len(seq) - 1 and seq[i] == seq[i + 1]: cnt += 1 i += 1 - next_seq += "{}{}".format(cnt, seq[i]) + next_seq += str(cnt) + seq[i] i += 1 return next_seq if __name__ == "__main__": for i in xrange(1, 4): - print Solution().countAndSay(i) + print(Solution().countAndSay(i)) + - diff --git a/Python/count-binary-substrings.py b/Python/count-binary-substrings.py new file mode 100644 index 000000000..e28ba957a --- /dev/null +++ b/Python/count-binary-substrings.py @@ -0,0 +1,42 @@ +# Time: O(n) +# Space: O(1) + +# Give a string s, count the number of non-empty (contiguous) substrings +# that have the same number of 0's and 1's, and all the 0's and all the 1's +# in these substrings are grouped consecutively. +# +# Substrings that occur multiple times are counted the number of times they occur. +# +# Example 1: +# Input: "00110011" +# Output: 6 +# Explanation: There are 6 substrings that have equal number of consecutive 1's and 0's: +# "0011", "01", "1100", "10", "0011", and "01". +# +# Notice that some of these substrings repeat and are counted the number of times they occur. +# +# Also, "00110011" is not a valid substring because all the 0's (and 1's) are not grouped together. +# Example 2: +# Input: "10101" +# Output: 4 +# Explanation: There are 4 substrings: "10", "01", "10", "01" that have equal number of consecutive 1's and 0's. +# +# Note: +# s.length will be between 1 and 50,000. +# s will only consist of "0" or "1" characters. + +class Solution(object): + def countBinarySubstrings(self, s): + """ + :type s: str + :rtype: int + """ + result, prev, curr = 0, 0, 1 + for i in xrange(1, len(s)): + if s[i-1] != s[i]: + result += min(prev, curr) + prev, curr = curr, 1 + else: + curr += 1 + result += min(prev, curr) + return result diff --git a/Python/count-complete-tree-nodes.py b/Python/count-complete-tree-nodes.py new file mode 100644 index 000000000..dcd6e490a --- /dev/null +++ b/Python/count-complete-tree-nodes.py @@ -0,0 +1,56 @@ +# Time: O(h * logn) = O((logn)^2) +# Space: O(1) + +# Given a complete binary tree, count the number of nodes. +# +# In a complete binary tree every level, except possibly the last, +# is completely filled, and all nodes in the last level are as far +# left as possible. It can have between 1 and 2h nodes inclusive at +# the last level h. +# + +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, x): +# self.val = x +# self.left = None +# self.right = None + +class Solution: + # @param {TreeNode} root + # @return {integer} + def countNodes(self, root): + if root is None: + return 0 + + node, level = root, 0 + while node.left is not None: + node = node.left + level += 1 + + # Binary search. + left, right = 2 ** level, 2 ** (level + 1) + while left < right: + mid = left + (right - left) / 2 + if not self.exist(root, mid): + right = mid + else: + left = mid + 1 + + return left - 1 + + # Check if the nth node exist. + def exist(self, root, n): + k = 1 + while k <= n: + k <<= 1 + k >>= 2 + + node = root + while k > 0: + if (n & k) == 0: + node = node.left + else: + node = node.right + k >>= 1 + return node is not None diff --git a/Python/count-different-palindromic-subsequences.py b/Python/count-different-palindromic-subsequences.py new file mode 100644 index 000000000..f1b5ecf1e --- /dev/null +++ b/Python/count-different-palindromic-subsequences.py @@ -0,0 +1,69 @@ +# Time: O(n^2) +# Space: O(n^2) + +# Given a string S, find the number of different non-empty palindromic subsequences in S, +# and return that number modulo 10^9 + 7. +# +# A subsequence of a string S is obtained by deleting 0 or more characters from S. +# +# A sequence is palindromic if it is equal to the sequence reversed. +# +# Two sequences A_1, A_2, ... and B_1, B_2, ... are different if there is some i for which A_i != B_i. +# +# Example 1: +# Input: +# S = 'bccb' +# Output: 6 +# Explanation: +# The 6 different non-empty palindromic subsequences are 'b', 'c', 'bb', 'cc', 'bcb', 'bccb'. +# Note that 'bcb' is counted only once, even though it occurs twice. +# +# Example 2: +# Input: +# S = 'abcdabcdabcdabcdabcdabcdabcdabcddcbadcbadcbadcbadcbadcbadcbadcba' +# Output: 104860361 +# +# Explanation: +# There are 3104860382 different non-empty palindromic subsequences, which is 104860361 modulo 10^9 + 7. +# Note: +# - The length of S will be in the range [1, 1000]. +# - Each character S[i] will be in the set {'a', 'b', 'c', 'd'}. + +class Solution(object): + def countPalindromicSubsequences(self, S): + """ + :type S: str + :rtype: int + """ + def dp(i, j, prv, nxt, lookup): + if lookup[i][j] is not None: + return lookup[i][j] + result = 1 + if i <= j: + for x in xrange(4): + i0 = nxt[i][x] + j0 = prv[j][x] + if i <= i0 <= j: + result = (result + 1) % P + if None < i0 < j0: + result = (result + dp(i0+1, j0-1, prv, nxt, lookup)) % P + result %= P + lookup[i][j] = result + return result + + prv = [None] * len(S) + nxt = [None] * len(S) + + last = [None] * 4 + for i in xrange(len(S)): + last[ord(S[i])-ord('a')] = i + prv[i] = tuple(last) + + last = [None] * 4 + for i in reversed(xrange(len(S))): + last[ord(S[i])-ord('a')] = i + nxt[i] = tuple(last) + + P = 10**9 + 7 + lookup = [[None] * len(S) for _ in xrange(len(S))] + return dp(0, len(S)-1, prv, nxt, lookup) - 1 diff --git a/Python/count-numbers-with-unique-digits.py b/Python/count-numbers-with-unique-digits.py new file mode 100644 index 000000000..68812f89e --- /dev/null +++ b/Python/count-numbers-with-unique-digits.py @@ -0,0 +1,35 @@ +# Time: O(n) +# Space: O(1) + +# Given an integer n, count all numbers with unique digits, x, where 0 <= x <= 10n. +# +# Example: +# Given n = 2, return 91. (The answer should be the total numbers in the range of 0 <= x <= 100, +# excluding [11,22,33,44,55,66,77,88,99,100]) +# +# Hint: +# 1. A direct way is to use the backtracking approach. +# 2. Backtracking should contains three states which are (the current number, +# number of steps to get that number and a bitmask which represent which +# number is marked as visited so far in the current number). +# Start with state (0,0,0) and count all valid number till we reach +# number of steps equals to 10n. +# 3. This problem can also be solved using a dynamic programming approach and +# some knowledge of combinatorics. +# 4. Let f(k) = count of numbers with unique digits with length equals k. +# 5. f(1) = 10, ..., f(k) = 9 * 9 * 8 * ... (9 - k + 2) +# [The first factor is 9 because a number cannot start with 0]. + +class Solution(object): + def countNumbersWithUniqueDigits(self, n): + """ + :type n: int + :rtype: int + """ + if n == 0: + return 1 + count, fk = 10, 9 + for k in xrange(2, n+1): + fk *= 10 - (k-1) + count += fk + return count diff --git a/Python/count-of-range-sum.py b/Python/count-of-range-sum.py new file mode 100644 index 000000000..01242acc0 --- /dev/null +++ b/Python/count-of-range-sum.py @@ -0,0 +1,97 @@ +# Time: O(nlogn) +# Space: O(n) + +# Given an integer array nums, return the number of range +# sums that lie in [lower, upper] inclusive. +# Range sum S(i, j) is defined as the sum of the elements +# in nums between indices i and j (i <= j), inclusive. +# +# Note: +# A naive algorithm of O(n^2) is trivial. You MUST do better than that. +# +# Example: +# Given nums = [-2, 5, -1], lower = -2, upper = 2, +# Return 3. +# The three ranges are : [0, 0], [2, 2], [0, 2] and +# their respective sums are: -2, -1, 2. + +# Divide and Conquer solution. +class Solution(object): + def countRangeSum(self, nums, lower, upper): + """ + :type nums: List[int] + :type lower: int + :type upper: int + :rtype: int + """ + def countAndMergeSort(sums, start, end, lower, upper): + if end - start <= 1: # The size of range [start, end) less than 2 is always with count 0. + return 0 + mid = start + (end - start) / 2 + count = countAndMergeSort(sums, start, mid, lower, upper) + \ + countAndMergeSort(sums, mid, end, lower, upper) + j, k, r = mid, mid, mid + tmp = [] + for i in xrange(start, mid): + # Count the number of range sums that lie in [lower, upper]. + while k < end and sums[k] - sums[i] < lower: + k += 1 + while j < end and sums[j] - sums[i] <= upper: + j += 1 + count += j - k + + # Merge the two sorted arrays into tmp. + while r < end and sums[r] < sums[i]: + tmp.append(sums[r]) + r += 1 + tmp.append(sums[i]) + # Copy tmp back to sums. + sums[start:start+len(tmp)] = tmp + return count + + sums = [0] * (len(nums) + 1) + for i in xrange(len(nums)): + sums[i + 1] = sums[i] + nums[i] + return countAndMergeSort(sums, 0, len(sums), lower, upper) + + +# Divide and Conquer solution. +class Solution2(object): + def countRangeSum(self, nums, lower, upper): + """ + :type nums: List[int] + :type lower: int + :type upper: int + :rtype: int + """ + def countAndMergeSort(sums, start, end, lower, upper): + if end - start <= 0: # The size of range [start, end] less than 2 is always with count 0. + return 0 + + mid = start + (end - start) / 2 + count = countAndMergeSort(sums, start, mid, lower, upper) + \ + countAndMergeSort(sums, mid + 1, end, lower, upper) + j, k, r = mid + 1, mid + 1, mid + 1 + tmp = [] + for i in xrange(start, mid + 1): + # Count the number of range sums that lie in [lower, upper]. + while k <= end and sums[k] - sums[i] < lower: + k += 1 + while j <= end and sums[j] - sums[i] <= upper: + j += 1 + count += j - k + + # Merge the two sorted arrays into tmp. + while r <= end and sums[r] < sums[i]: + tmp.append(sums[r]) + r += 1 + tmp.append(sums[i]) + + # Copy tmp back to sums + sums[start:start+len(tmp)] = tmp + return count + + sums = [0] * (len(nums) + 1) + for i in xrange(len(nums)): + sums[i + 1] = sums[i] + nums[i] + return countAndMergeSort(sums, 0, len(sums) - 1, lower, upper) diff --git a/Python/count-of-smaller-numbers-after-self.py b/Python/count-of-smaller-numbers-after-self.py new file mode 100644 index 000000000..42f49c324 --- /dev/null +++ b/Python/count-of-smaller-numbers-after-self.py @@ -0,0 +1,164 @@ +# Time: O(nlogn) +# Space: O(n) + +# You are given an integer array nums and you have to +# return a new counts array. The counts array has the +# property where counts[i] is the number of smaller +# elements to the right of nums[i]. +# +# Example: +# +# Given nums = [5, 2, 6, 1] +# +# To the right of 5 there are 2 smaller elements (2 and 1). +# To the right of 2 there is only 1 smaller element (1). +# To the right of 6 there is 1 smaller element (1). +# To the right of 1 there is 0 smaller element. +# Return the array [2, 1, 1, 0]. + +# Divide and Conquer solution. +class Solution(object): + def countSmaller(self, nums): + """ + :type nums: List[int] + :rtype: List[int] + """ + def countAndMergeSort(num_idxs, start, end, counts): + if end - start <= 0: # The size of range [start, end] less than 2 is always with count 0. + return 0 + + mid = start + (end - start) / 2 + countAndMergeSort(num_idxs, start, mid, counts) + countAndMergeSort(num_idxs, mid + 1, end, counts) + r = mid + 1 + tmp = [] + for i in xrange(start, mid + 1): + # Merge the two sorted arrays into tmp. + while r <= end and num_idxs[r][0] < num_idxs[i][0]: + tmp.append(num_idxs[r]) + r += 1 + tmp.append(num_idxs[i]) + counts[num_idxs[i][1]] += r - (mid + 1) + + # Copy tmp back to num_idxs + num_idxs[start:start+len(tmp)] = tmp + + num_idxs = [] + counts = [0] * len(nums) + for i, num in enumerate(nums): + num_idxs.append((num, i)) + countAndMergeSort(num_idxs, 0, len(num_idxs) - 1, counts) + return counts + +# Time: O(nlogn) +# Space: O(n) +# BIT solution. +class Solution2(object): + def countSmaller(self, nums): + """ + :type nums: List[int] + :rtype: List[int] + """ + def binarySearch(A, target, compare): + start, end = 0, len(A) - 1 + while start <= end: + mid = start + (end - start) / 2 + if compare(target, A[mid]): + end = mid - 1 + else: + start = mid + 1 + return start + + class BIT(object): + def __init__(self, n): + self.__bit = [0] * n + + def add(self, i, val): + while i < len(self.__bit): + self.__bit[i] += val + i += (i & -i) + + def query(self, i): + ret = 0 + while i > 0: + ret += self.__bit[i] + i -= (i & -i) + return ret + + # Get the place (position in the ascending order) of each number. + sorted_nums, places = sorted(nums), [0] * len(nums) + for i, num in enumerate(nums): + places[i] = binarySearch(sorted_nums, num, lambda x, y: x <= y) + + # Count the smaller elements after the number. + ans, bit= [0] * len(nums), BIT(len(nums) + 1) + for i in reversed(xrange(len(nums))): + ans[i] = bit.query(places[i]) + bit.add(places[i] + 1, 1) + return ans + +# Time: O(nlogn) +# Space: O(n) +# BST solution. +class Solution3(object): + def countSmaller(self, nums): + """ + :type nums: List[int] + :rtype: List[int] + """ + res = [0] * len(nums) + bst = self.BST() + # Insert into BST and get left count. + for i in reversed(xrange(len(nums))): + bst.insertNode(nums[i]) + res[i] = bst.query(nums[i]) + + return res + + class BST(object): + class BSTreeNode(object): + def __init__(self, val): + self.val = val + self.count = 0 + self.left = self.right = None + + def __init__(self): + self.root = None + + # Insert node into BST. + def insertNode(self, val): + node = self.BSTreeNode(val) + if not self.root: + self.root = node + return + curr = self.root + while curr: + # Insert left if smaller. + if node.val < curr.val: + curr.count += 1 # Increase the number of left children. + if curr.left: + curr = curr.left; + else: + curr.left = node; + break + else: # Insert right if larger or equal. + if curr.right: + curr = curr.right + else: + curr.right = node + break + + # Query the smaller count of the value. + def query(self, val): + count = 0 + curr = self.root + while curr: + # Insert left. + if val < curr.val: + curr = curr.left + elif val > curr.val: + count += 1 + curr.count # Count the number of the smaller nodes. + curr = curr.right + else: # Equal. + return count + curr.count + return 0 diff --git a/Python/count-primes.py b/Python/count-primes.py new file mode 100644 index 000000000..569abf3d0 --- /dev/null +++ b/Python/count-primes.py @@ -0,0 +1,48 @@ +# Time: O(n) +# Space: O(n) + +# Description: +# +# Count the number of prime numbers less than a non-negative number, n +# +# Hint: The number n could be in the order of 100,000 to 5,000,000. + + +class Solution: + # @param {integer} n + # @return {integer} + def countPrimes(self, n): + if n <= 2: + return 0 + + is_prime = [True] * n + num = n / 2 + for i in xrange(3, n, 2): + if i * i >= n: + break + + if not is_prime[i]: + continue + + for j in xrange(i*i, n, 2*i): + if not is_prime[j]: + continue + + num -= 1 + is_prime[j] = False + + return num + + def countPrimes2(self, n): + """ + :type n: int + :rtype: int + """ + if n < 3: + return 0 + primes = [True] * n + primes[0] = primes[1] = False + for i in range(2, int(n ** 0.5) + 1): + if primes[i]: + primes[i * i: n: i] = [False] * len(primes[i * i: n: i]) + return sum(primes) diff --git a/Python/count-the-repetitions.py b/Python/count-the-repetitions.py new file mode 100644 index 000000000..37d8b47cf --- /dev/null +++ b/Python/count-the-repetitions.py @@ -0,0 +1,51 @@ +# Time: O(s1 * min(s2, n1)) +# Space: O(s2) + +# Define S = [s,n] as the string S which consists of n connected strings s. +# For example, ["abc", 3] ="abcabcabc". +# +# On the other hand, we define that string s1 can be obtained from string s2 +# if we can remove some characters from s2 such that it becomes s1. +# For example, “abc” can be obtained from “abdbec” based on our definition, but it can not be obtained from “acbbe”. +# +# You are given two non-empty strings s1 and s2 (each at most 100 characters long) +# and two integers 0 ≤ n1 ≤ 106 and 1 ≤ n2 ≤ 106. Now consider the strings S1 and S2, +# where S1=[s1,n1] and S2=[s2,n2]. Find the maximum integer M such that [S2,M] can be obtained from S1. +# +# Example: +# +# Input: +# s1="acb", n1=4 +# s2="ab", n2=2 +# +# Return: +# 2 + +class Solution(object): + def getMaxRepetitions(self, s1, n1, s2, n2): + """ + :type s1: str + :type n1: int + :type s2: str + :type n2: int + :rtype: int + """ + repeat_count = [0] * (len(s2)+1) + lookup = {} + j, count = 0, 0 + for k in xrange(1, n1+1): + for i in xrange(len(s1)): + if s1[i] == s2[j]: + j = (j + 1) % len(s2) + count += (j == 0) + + if j in lookup: # cyclic + i = lookup[j] + prefix_count = repeat_count[i] + pattern_count = (count - repeat_count[i]) * ((n1 - i) // (k - i)) + suffix_count = repeat_count[i + (n1 - i) % (k - i)] - repeat_count[i] + return (prefix_count + pattern_count + suffix_count) / n2 + lookup[j] = k + repeat_count[k] = count + + return repeat_count[n1] / n2 # not cyclic iff n1 <= s2 diff --git a/Python/count-univalue-subtrees.py b/Python/count-univalue-subtrees.py new file mode 100644 index 000000000..c70a5ba5d --- /dev/null +++ b/Python/count-univalue-subtrees.py @@ -0,0 +1,32 @@ +# Time: O(n) +# Space: O(h) +# +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, x): +# self.val = x +# self.left = None +# self.right = None + +class Solution: + # @param {TreeNode} root + # @return {integer} + def countUnivalSubtrees(self, root): + [is_uni, count] = self.isUnivalSubtrees(root, 0); + return count; + + def isUnivalSubtrees(self, root, count): + if not root: + return [True, count] + + [left, count] = self.isUnivalSubtrees(root.left, count) + [right, count] = self.isUnivalSubtrees(root.right, count) + if self.isSame(root, root.left, left) and \ + self.isSame(root, root.right, right): + count += 1 + return [True, count] + + return [False, count] + + def isSame(self, root, child, is_uni): + return not child or (is_uni and root.val == child.val) diff --git a/Python/counting-bits.py b/Python/counting-bits.py new file mode 100644 index 000000000..e3829c7e6 --- /dev/null +++ b/Python/counting-bits.py @@ -0,0 +1,56 @@ +from __future__ import print_function +# Time: O(n) +# Space: O(n) + +# Given a non negative integer number num. For every numbers i +# in the range 0 <= i <= num calculate the number +# of 1's in their binary representation and return them as an array. +# +# Example: +# For num = 5 you should return [0,1,1,2,1,2]. +# +# Follow up: +# +# It is very easy to come up with a solution with run +# time O(n*sizeof(integer)). But can you do it in +# linear time O(n) /possibly in a single pass? +# Space complexity should be O(n). +# Can you do it like a boss? Do it without using +# any builtin function like __builtin_popcount in c++ or +# in any other language. +# Hint: +# +# 1. You should make use of what you have produced already. +# 2. Divide the numbers in ranges like [2-3], [4-7], [8-15] +# and so on. And try to generate new range from previous. +# 3. Or does the odd/even status of the number help you in +# calculating the number of 1s? + + +class Solution(object): + def countBits(self, num): + """ + :type num: int + :rtype: List[int] + """ + res = [0] + for i in xrange(1, num + 1): + # Number of 1's in i = (i & 1) + number of 1's in (i / 2). + res.append((i & 1) + res[i >> 1]) + return res + + def countBits2(self, num): + """ + :type num: int + :rtype: List[int] + """ + s = [0] + while len(s) <= num: + s.extend(map(lambda x: x + 1, s)) + return s[:num + 1] + + +if __name__ == '__main__': + s = Solution() + r = s.countBits2(5) + print(r) diff --git a/Python/couples-holding-hands.py b/Python/couples-holding-hands.py new file mode 100644 index 000000000..0801bcd81 --- /dev/null +++ b/Python/couples-holding-hands.py @@ -0,0 +1,27 @@ +# Time: O(n) +# Space: O(n) + +class Solution(object): + def minSwapsCouples(self, row): + """ + :type row: List[int] + :rtype: int + """ + N = len(row)//2 + couples = [[] for _ in xrange(N)] + for seat, num in enumerate(row): + couples[num//2].append(seat//2) + adj = [[] for _ in xrange(N)] + for couch1, couch2 in couples: + adj[couch1].append(couch2) + adj[couch2].append(couch1) + + result = 0 + for couch in xrange(N): + if not adj[couch]: continue + couch1, couch2 = couch, adj[couch].pop() + while couch2 != couch: + result += 1 + adj[couch2].remove(couch1) + couch1, couch2 = couch2, adj[couch2].pop() + return result # also equals to N - (# of cycles) diff --git a/Python/course-schedule-ii.py b/Python/course-schedule-ii.py new file mode 100644 index 000000000..7b9f4d3cf --- /dev/null +++ b/Python/course-schedule-ii.py @@ -0,0 +1,77 @@ +# Time: O(|V| + |E|) +# Space: O(|E|) + +# There are a total of n courses you have to take, labeled from 0 to n - 1. +# +# Some courses may have prerequisites, for example to take course 0 you have to first take course 1, +# which is expressed as a pair: [0,1] +# +# Given the total number of courses and a list of prerequisite pairs, return the ordering of courses +# you should take to finish all courses. +# +# There may be multiple correct orders, you just need to return one of them. If it is impossible +# to finish all courses, return an empty array. +# +# For example: +# +# 2, [[1,0]] +# There are a total of 2 courses to take. To take course 1 you should have finished course 0. +# So the correct course order is [0,1] +# +# 4, [[1,0],[2,0],[3,1],[3,2]] +# There are a total of 4 courses to take. To take course 3 you should have finished both courses 1 and 2. +# Both courses 1 and 2 should be taken after you finished course 0. So one correct course order is [0,1,2,3]. +# Another correct ordering is[0,2,1,3]. +# +# Note: +# The input prerequisites is a graph represented by a list of edges, not adjacency matrices. +# Read more about how a graph is represented. +# +# Hints: +# This problem is equivalent to finding the topological order in a directed graph. +# If a cycle exists, no topological ordering exists and therefore it will be impossible to take all courses. +# Topological Sort via DFS - A great video tutorial (21 minutes) on Coursera explaining +# the basic concepts of Topological Sort. +# Topological sort could also be done via BFS. +# + +import collections + + +class Solution(object): + def findOrder(self, numCourses, prerequisites): + """ + :type numCourses: int + :type prerequisites: List[List[int]] + :rtype: List[int] + """ + res, zero_in_degree_queue, in_degree, out_degree = [], collections.deque(), {}, {} + + for i, j in prerequisites: + if i not in in_degree: + in_degree[i] = set() + if j not in out_degree: + out_degree[j] = set() + in_degree[i].add(j) + out_degree[j].add(i) + + for i in xrange(numCourses): + if i not in in_degree: + zero_in_degree_queue.append(i) + + while zero_in_degree_queue: + prerequisite = zero_in_degree_queue.popleft() + res.append(prerequisite) + + if prerequisite in out_degree: + for course in out_degree[prerequisite]: + in_degree[course].discard(prerequisite) + if not in_degree[course]: + zero_in_degree_queue.append(course) + + del out_degree[prerequisite] + + if out_degree: + return [] + + return res diff --git a/Python/course-schedule-iii.py b/Python/course-schedule-iii.py new file mode 100644 index 000000000..7ad66c09e --- /dev/null +++ b/Python/course-schedule-iii.py @@ -0,0 +1,46 @@ +# Time: O(nlogn) +# Space: O(k), k is the number of courses you can take + +# There are n different online courses numbered from 1 to n. +# Each course has some duration(course length) t and closed on dth day. +# A course should be taken continuously for t days and must be finished before or on the dth day. +# You will start at the 1st day. +# +# Given n online courses represented by pairs (t,d), your task is to find the maximal number of courses that can be taken. +# +# Example: +# Input: [[100, 200], [200, 1300], [1000, 1250], [2000, 3200]] +# Output: 3 +# Explanation: +# There're totally 4 courses, but you can take 3 courses at most: +# First, take the 1st course, it costs 100 days so you will finish it on the 100th day, +# and ready to take the next course on the 101st day. +# Second, take the 3rd course, it costs 1000 days so you will finish it on the 1100th day, +# and ready to take the next course on the 1101st day. +# Third, take the 2nd course, it costs 200 days so you will finish it on the 1300th day. +# The 4th course cannot be taken now, since you will finish it on the 3300th day, +# which exceeds the closed date. +# +# Note: +# The integer 1 <= d, t, n <= 10,000. +# You can't take two courses simultaneously. + +import collections +import heapq + + +class Solution(object): + def scheduleCourse(self, courses): + """ + :type courses: List[List[int]] + :rtype: int + """ + courses.sort(key=lambda t_end: t_end[1]) + max_heap = [] + now = 0 + for t, end in courses: + now += t + heapq.heappush(max_heap, -t) + if now > end: + now += heapq.heappop(max_heap) + return len(max_heap) diff --git a/Python/course-schedule.py b/Python/course-schedule.py new file mode 100644 index 000000000..73711a1db --- /dev/null +++ b/Python/course-schedule.py @@ -0,0 +1,76 @@ +from __future__ import print_function +# Time: O(|V| + |E|) +# Space: O(|E|) +# +# There are a total of n courses you have to take, labeled from 0 to n - 1. +# +# Some courses may have prerequisites, for example to take course 0 +# you have to first take course 1, which is expressed as a pair: [0,1] +# +# Given the total number of courses and a list of prerequisite pairs, +# is it possible for you to finish all courses? +# +# For example: +# +# 2, [[1,0]] +# There are a total of 2 courses to take. To take course 1 +# you should have finished course 0. So it is possible. +# +# 2, [[1,0],[0,1]] +# There are a total of 2 courses to take. To take course 1 you should have +# finished course 0, and to take course 0 you should also have finished course 1. So it is impossible. +# +# click to show more hints. +# +# Hints: +# This problem is equivalent to finding if a cycle exists in a directed graph. +# If a cycle exists, no topological ordering exists and therefore it will be impossible to take all courses. +# There are several ways to represent a graph. For example, the input prerequisites is a graph represented by +# a list of edges. Is this graph representation appropriate? +# Topological Sort via DFS - A great video tutorial (21 minutes) on Coursera explaining the basic concepts +# of Topological Sort. +# Topological sort could also be done via BFS. +# + +import collections + +class Solution(object): + def canFinish(self, numCourses, prerequisites): + """ + :type numCourses: int + :type prerequisites: List[List[int]] + :rtype: bool + """ + zero_in_degree_queue, in_degree, out_degree = collections.deque(), {}, {} + + for i, j in prerequisites: + if i not in in_degree: + in_degree[i] = set() + if j not in out_degree: + out_degree[j] = set() + in_degree[i].add(j) + out_degree[j].add(i) + + for i in xrange(numCourses): + if i not in in_degree: + zero_in_degree_queue.append(i) + + while zero_in_degree_queue: + prerequisite = zero_in_degree_queue.popleft() + + if prerequisite in out_degree: + for course in out_degree[prerequisite]: + in_degree[course].discard(prerequisite) + if not in_degree[course]: + zero_in_degree_queue.append(course) + + del out_degree[prerequisite] + + if out_degree: + return False + + return True + + +if __name__ == "__main__": + print(Solution().canFinish(1, [])) diff --git a/Python/cracking-the-safe.py b/Python/cracking-the-safe.py new file mode 100644 index 000000000..e4874bfcd --- /dev/null +++ b/Python/cracking-the-safe.py @@ -0,0 +1,95 @@ +# Time: O(k^n) +# Space: O(k^n) + +# There is a box protected by a password. +# The password is n digits, where each letter can be one of the first k digits 0, 1, ..., k-1. +# +# You can keep inputting the password, +# the password will automatically be matched against the last n digits entered. +# +# For example, assuming the password is "345", +# I can open it when I type "012345", but I enter a total of 6 digits. +# +# Please return any string of minimum length that is guaranteed to open the box after the entire string is inputted. +# +# Example 1: +# Input: n = 1, k = 2 +# Output: "01" +# Note: "10" will be accepted too. +# +# Example 2: +# Input: n = 2, k = 2 +# Output: "00110" +# Note: "01100", "10011", "11001" will be accepted too. +# +# Note: +# - n will be in the range [1, 4]. +# - k will be in the range [1, 10]. +# - k^n will be at most 4096. + +# https://en.wikipedia.org/wiki/De_Bruijn_sequence +# https://en.wikipedia.org/wiki/Lyndon_word +class Solution(object): + def crackSafe(self, n, k): + """ + :type n: int + :type k: int + :rtype: str + """ + M = k**(n-1) + P = [q*k+i for i in xrange(k) for q in xrange(M)] # rotate: i*k^(n-1) + q => q*k + i + result = [str(k-1)]*(n-1) + for i in xrange(k**n): + j = i + # concatenation in lexicographic order of Lyndon words + while P[j] >= 0: + result.append(str(j//M)) + P[j], j = -1, P[j] + return "".join(result) + + +# Time: O(n * k^n) +# Space: O(n * k^n) +class Solution2(object): + def crackSafe(self, n, k): + """ + :type n: int + :type k: int + :rtype: str + """ + result = [str(k-1)]*n + lookup = {"".join(result)} + total = k**n + while len(lookup) < total: + node = result[len(result)-n+1:] + for i in xrange(k): + neighbor = "".join(node) + str(i) + if neighbor not in lookup: + lookup.add(neighbor) + result.append(str(i)) + break + return "".join(result) + + +# Time: O(n * k^n) +# Space: O(n * k^n) +class Solution3(object): + def crackSafe(self, n, k): + """ + :type n: int + :type k: int + :rtype: str + """ + def dfs(k, node, lookup, result): + for i in xrange(k): + neighbor = node + str(i) + if neighbor not in lookup: + lookup.add(neighbor) + result.append(str(i)) + dfs(k, neighbor[1:], lookup, result) + break + + result = [str(k-1)]*(n-1) + lookup = set() + dfs(k, "".join(result), lookup, result) + return "".join(result) diff --git a/Python/create-maximum-number.py b/Python/create-maximum-number.py new file mode 100644 index 000000000..c946b12fa --- /dev/null +++ b/Python/create-maximum-number.py @@ -0,0 +1,71 @@ +# Time: O(k * (m + n + k)) ~ O(k * (m + n + k^2)) +# Space: O(m + n + k^2) +# +# Given two arrays of length m and n with digits 0-9 representing two numbers. +# Create the maximum number of length k <= m + n from digits of the two. +# The relative order of the digits from the same array must be preserved. +# Return an array of the k digits. You should try to optimize your time +# and space complexity. +# +# Example 1: +# nums1 = [3, 4, 6, 5] +# nums2 = [9, 1, 2, 5, 8, 3] +# k = 5 +# return [9, 8, 6, 5, 3] +# +# Example 2: +# nums1 = [6, 7] +# nums2 = [6, 0, 4] +# k = 5 +# return [6, 7, 6, 0, 4] +# +# Example 3: +# nums1 = [3, 9] +# nums2 = [8, 9] +# k = 3 +# return [9, 8, 9] +# + +# DP + Greedy solution. (280ms) +class Solution(object): + def maxNumber(self, nums1, nums2, k): + """ + :type nums1: List[int] + :type nums2: List[int] + :type k: int + :rtype: List[int] + """ + def get_max_digits(nums, start, end, max_digits): + max_digits[end] = max_digit(nums, end) + for i in reversed(xrange(start, end)): + max_digits[i] = delete_digit(max_digits[i + 1]) + + def max_digit(nums, k): + drop = len(nums) - k + res = [] + for num in nums: + while drop and res and res[-1] < num: + res.pop() + drop -= 1 + res.append(num) + return res[:k] + + def delete_digit(nums): + res = list(nums) + for i in xrange(len(res)): + if i == len(res) - 1 or res[i] < res[i + 1]: + res = res[:i] + res[i+1:] + break + return res + + def merge(a, b): + return [max(a, b).pop(0) for _ in xrange(len(a)+len(b))] + + m, n = len(nums1), len(nums2) + + max_digits1, max_digits2 = [[] for _ in xrange(k + 1)], [[] for _ in xrange(k + 1)] + get_max_digits(nums1, max(0, k - n), min(k, m), max_digits1) + get_max_digits(nums2, max(0, k - m), min(k, n), max_digits2) + + return max(merge(max_digits1[i], max_digits2[k-i]) \ + for i in xrange(max(0, k - n), min(k, m) + 1)) diff --git a/Python/custom-sort-string.py b/Python/custom-sort-string.py new file mode 100644 index 000000000..950760368 --- /dev/null +++ b/Python/custom-sort-string.py @@ -0,0 +1,39 @@ +# Time: O(n) +# Space: O(1) + +# S and T are strings composed of lowercase letters. In S, no letter occurs more than once. +# +# S was sorted in some custom order previously. +# We want to permute the characters of T so that they match the order that S was sorted. +# More specifically, if x occurs before y in S, then x should occur before y in the returned string. +# +# Return any permutation of T (as a string) that satisfies this property. +# +# Example : +# Input: +# S = "cba" +# T = "abcd" +# Output: "cbad" +# Explanation: +# "a", "b", "c" appear in S, so the order of "a", "b", "c" should be "c", "b", and "a". +# Since "d" does not appear in S, it can be at any position in T. "dcba", "cdba", "cbda" are also valid outputs. +# +# Note: +# - S has length at most 26, and no character is repeated in S. +# - T has length at most 200. +# - S and T consist of lowercase letters only. + +import collections + + +class Solution(object): + def customSortString(self, S, T): + """ + :type S: str + :type T: str + :rtype: str + """ + counter, s = collections.Counter(T), set(S) + result = [c*counter[c] for c in S] + result.extend([c*counter for c, counter in counter.iteritems() if c not in s]) + return "".join(result) diff --git a/Python/cut-off-trees-for-golf-event.py b/Python/cut-off-trees-for-golf-event.py new file mode 100644 index 000000000..e41eaad67 --- /dev/null +++ b/Python/cut-off-trees-for-golf-event.py @@ -0,0 +1,152 @@ +# Time: O(t * (logt + m * n)), t is the number of trees +# Space: O(t + m * n) + +# You are asked to cut off trees in a forest for a golf event. +# The forest is represented as a non-negative 2D map, in this map: +# +# 0 represents the obstacle can't be reached. +# 1 represents the ground can be walked through. +# The place with number bigger than 1 represents a tree can be walked through, +# and this positive number represents the tree's height. +# +# You are asked to cut off all the trees in this forest in the order of tree's height - +# always cut off the tree with lowest height first. And after cutting, the original place +# has the tree will become a grass (value 1). +# +# You will start from the point (0, 0) +# and you should output the minimum steps you need to walk to cut off all the trees. +# If you can't cut off all the trees, output -1 in that situation. +# +# You are guaranteed that no two trees have the same height and there is at least one tree needs to be cut off. +# +# Example 1: +# Input: +# [ +# [1,2,3], +# [0,0,4], +# [7,6,5] +# ] +# Output: 6 +# Example 2: +# Input: +# [ +# [1,2,3], +# [0,0,0], +# [7,6,5] +# ] +# Output: -1 +# Example 3: +# Input: +# [ +# [2,3,4], +# [0,0,5], +# [8,7,6] +# ] +# Output: 6 +# Explanation: You started from the point (0,0) and you can cut off the tree +# in (0,0) directly without walking. +# Hint: size of the given matrix will not exceed 50x50. + +# Solution Reference: +# 1. https://discuss.leetcode.com/topic/103532/my-python-solution-inspired-by-a-algorithm/2 +# 2. https://discuss.leetcode.com/topic/103562/python-solution-based-on-wufangjie-s-hadlock-s-algorithm +# 3. https://en.wikipedia.org/wiki/A*_search_algorithm +# 4. https://cg2010studio.files.wordpress.com/2011/12/dijkstra-vs-a-star.png + +import collections +import heapq + + +class Solution(object): + def cutOffTree(self, forest): + """ + :type forest: List[List[int]] + :rtype: int + """ + def dot(p1, p2): + return p1[0]*p2[0]+p1[1]*p2[1] + + def minStep(p1, p2): + min_steps = abs(p1[0]-p2[0])+abs(p1[1]-p2[1]) + closer, detour = [p1], [] + lookup = set() + while True: + if not closer: # cannot find a path in the closer expansions + if not detour: # no other possible path + return -1 + # try other possible paths in detour expansions with extra 2-step cost + min_steps += 2 + closer, detour = detour, closer + i, j = closer.pop() + if (i, j) == p2: + return min_steps + if (i, j) not in lookup: + lookup.add((i, j)) + for I, J in (i+1, j), (i-1, j), (i, j+1), (i, j-1): + if 0 <= I < m and 0 <= J < n and forest[I][J] and (I, J) not in lookup: + is_closer = dot((I-i, J-j), (p2[0]-i, p2[1]-j)) > 0 + (closer if is_closer else detour).append((I, J)) + return min_steps + + m, n = len(forest), len(forest[0]) + min_heap = [] + for i in xrange(m): + for j in xrange(n): + if forest[i][j] > 1: + heapq.heappush(min_heap, (forest[i][j], (i, j))) + + start = (0, 0) + result = 0 + while min_heap: + tree = heapq.heappop(min_heap) + step = minStep(start, tree[1]) + if step < 0: + return -1 + result += step + start = tree[1] + return result + + +# Time: O(t * (logt + m * n)), t is the number of trees +# Space: O(t + m * n) +class Solution_TLE(object): + def cutOffTree(self, forest): + """ + :type forest: List[List[int]] + :rtype: int + """ + def minStep(p1, p2): + min_steps = 0 + lookup = {p1} + q = collections.deque([p1]) + while q: + size = len(q) + for _ in xrange(size): + (i, j) = q.popleft() + if (i, j) == p2: + return min_steps + for i, j in (i+1, j), (i-1, j), (i, j+1), (i, j-1): + if not (0 <= i < m and 0 <= j < n and forest[i][j] and (i, j) not in lookup): + continue + q.append((i, j)) + lookup.add((i, j)) + min_steps += 1 + return -1 + + m, n = len(forest), len(forest[0]) + min_heap = [] + for i in xrange(m): + for j in xrange(n): + if forest[i][j] > 1: + heapq.heappush(min_heap, (forest[i][j], (i, j))) + + start = (0, 0) + result = 0 + while min_heap: + tree = heapq.heappop(min_heap) + step = minStep(start, tree[1]) + if step < 0: + return -1 + result += step + start = tree[1] + return result diff --git a/Python/daily-temperatures.py b/Python/daily-temperatures.py new file mode 100644 index 000000000..49e55ab96 --- /dev/null +++ b/Python/daily-temperatures.py @@ -0,0 +1,28 @@ +# Time: O(n) +# Space: O(n) + +# Given a list of daily temperatures, produce a list that, +# for each day in the input, tells you how many days you would have to wait until +# a warmer temperature. If there is no future day for which this is possible, put 0 instead. +# +# For example, given the list temperatures = [73, 74, 75, 71, 69, 72, 76, 73], +# your output should be [1, 1, 4, 2, 1, 1, 0, 0]. +# +# Note: The length of temperatures will be in the range [1, 30000]. +# Each temperature will be an integer in the range [30, 100]. + +class Solution(object): + def dailyTemperatures(self, temperatures): + """ + :type temperatures: List[int] + :rtype: List[int] + """ + result = [0] * len(temperatures) + stk = [] + for i in xrange(len(temperatures)): + while stk and \ + temperatures[stk[-1]] < temperatures[i]: + idx = stk.pop() + result[idx] = i-idx + stk.append(i) + return result diff --git a/Python/data-stream-as-disjoint-intervals.py b/Python/data-stream-as-disjoint-intervals.py new file mode 100644 index 000000000..7c3b3285a --- /dev/null +++ b/Python/data-stream-as-disjoint-intervals.py @@ -0,0 +1,71 @@ +# Time: addNum: O(n), getIntervals: O(n), n is the number of disjoint intervals. +# Space: O(n) + +# Given a data stream input of non-negative integers a1, a2, ..., an, ..., +# summarize the numbers seen so far as a list of disjoint intervals. +# +# For example, suppose the integers from the data stream are 1, 3, 7, 2, 6, +# ..., then the summary will be: +# +# [1, 1] +# [1, 1], [3, 3] +# [1, 1], [3, 3], [7, 7] +# [1, 3], [7, 7] +# [1, 3], [6, 7] +# +# Follow up: +# What if there are lots of merges and the number of disjoint intervals +# are small compared to the data stream's size? + +# Definition for an interval. +class Interval(object): + def __init__(self, s=0, e=0): + self.start = s + self.end = e + + +class SummaryRanges(object): + + def __init__(self): + """ + Initialize your data structure here. + """ + self.__intervals = [] + + def addNum(self, val): + """ + :type val: int + :rtype: void + """ + def upper_bound(nums, target): + left, right = 0, len(nums) - 1 + while left <= right: + mid = left + (right - left) / 2 + if nums[mid].start > target: + right = mid - 1 + else: + left = mid + 1 + return left + + i = upper_bound(self.__intervals, val) + start, end = val, val + if i != 0 and self.__intervals[i-1].end + 1 >= val: + i -= 1 + while i != len(self.__intervals) and \ + end + 1 >= self.__intervals[i].start: + start = min(start, self.__intervals[i].start) + end = max(end, self.__intervals[i].end); + del self.__intervals[i] + self.__intervals.insert(i, Interval(start, end)) + + def getIntervals(self): + """ + :rtype: List[Interval] + """ + return self.__intervals + + +# Your SummaryRanges object will be instantiated and called as such: +# obj = SummaryRanges() +# obj.addNum(val) +# param_2 = obj.getIntervals() diff --git a/Python/decode-string.py b/Python/decode-string.py new file mode 100644 index 000000000..c3030c3b5 --- /dev/null +++ b/Python/decode-string.py @@ -0,0 +1,47 @@ +# Time: O(n) +# Space: O(h), h is the depth of the recursion + +# Given an encoded string, return it's decoded string. +# +# The encoding rule is: k[encoded_string], +# where the encoded_string inside the square brackets is +# being repeated exactly k times. Note that k is guaranteed +# to be a positive integer. +# +# You may assume that the input string is always valid; +# No extra white spaces, square brackets are well-formed, etc. +# +# Furthermore, you may assume that the original data does not +# contain any digits and that digits are only for those repeat numbers, k. +# For example, there won't be input like 3a or 2[4]. +# +# Examples: +# +# s = "3[a]2[bc]", return "aaabcbc". +# s = "3[a2[c]]", return "accaccacc". +# s = "2[abc]3[cd]ef", return "abcabccdcdcdef". + +class Solution(object): + def decodeString(self, s): + """ + :type s: str + :rtype: str + """ + curr, nums, strs = [], [], [] + n = 0 + + for c in s: + if c.isdigit(): + n = n * 10 + ord(c) - ord('0') + elif c == '[': + nums.append(n) + n = 0 + strs.append(curr) + curr = [] + elif c == ']': + strs[-1].extend(curr * nums.pop()) + curr = strs.pop() + else: + curr.append(c) + + return "".join(strs[-1]) if strs else "".join(curr) diff --git a/Python/decode-ways-ii.py b/Python/decode-ways-ii.py new file mode 100644 index 000000000..f628b3f00 --- /dev/null +++ b/Python/decode-ways-ii.py @@ -0,0 +1,55 @@ +# Time: O(n) +# Space: O(1) + +# A message containing letters from A-Z is being encoded to numbers using the following mapping way: +# +# 'A' -> 1 +# 'B' -> 2 +# ... +# 'Z' -> 26 +# Beyond that, now the encoded string can also contain the character '*', +# which can be treated as one of the numbers from 1 to 9. +# +# Given the encoded message containing digits and the character '*', return the total number of ways to decode it. +# +# Also, since the answer may be very large, you should return the output mod 109 + 7. +# +# Example 1: +# Input: "*" +# Output: 9 +# Explanation: The encoded message can be decoded to the string: "A", "B", "C", "D", "E", "F", "G", "H", "I". +# Example 2: +# Input: "1*" +# Output: 9 + 9 = 18 +# Note: +# The length of the input string will fit in range [1, 105]. +# The input string will only contain the character '*' and digits '0' - '9'. + +class Solution(object): + def numDecodings(self, s): + """ + :type s: str + :rtype: int + """ + M, W = 1000000007, 3 + dp = [0] * W + dp[0] = 1 + dp[1] = 9 if s[0] == '*' else dp[0] if s[0] != '0' else 0 + for i in xrange(1, len(s)): + if s[i] == '*': + dp[(i + 1) % W] = 9 * dp[i % W] + if s[i - 1] == '1': + dp[(i + 1) % W] = (dp[(i + 1) % W] + 9 * dp[(i - 1) % W]) % M + elif s[i - 1] == '2': + dp[(i + 1) % W] = (dp[(i + 1) % W] + 6 * dp[(i - 1) % W]) % M + elif s[i - 1] == '*': + dp[(i + 1) % W] = (dp[(i + 1) % W] + 15 * dp[(i - 1) % W]) % M + else: + dp[(i + 1) % W] = dp[i % W] if s[i] != '0' else 0 + if s[i - 1] == '1': + dp[(i + 1) % W] = (dp[(i + 1) % W] + dp[(i - 1) % W]) % M + elif s[i - 1] == '2' and s[i] <= '6': + dp[(i + 1) % W] = (dp[(i + 1) % W] + dp[(i - 1) % W]) % M + elif s[i - 1] == '*': + dp[(i + 1) % W] = (dp[(i + 1) % W] + (2 if s[i] <= '6' else 1) * dp[(i - 1) % W]) % M + return dp[len(s) % W] diff --git a/Python/decode-ways.py b/Python/decode-ways.py index 6b84185bc..d5c3b9be4 100644 --- a/Python/decode-ways.py +++ b/Python/decode-ways.py @@ -1,36 +1,40 @@ +from __future__ import print_function # Time: O(n) # Space: O(1) # # A message containing letters from A-Z is being encoded to numbers using the following mapping: -# +# # 'A' -> 1 # 'B' -> 2 # ... # 'Z' -> 26 # Given an encoded message containing digits, determine the total number of ways to decode it. -# +# # For example, # Given encoded message "12", it could be decoded as "AB" (1 2) or "L" (12). -# +# # The number of ways decoding "12" is 2. # -class Solution: - # @param s, a string - # @return an integer +class Solution(object): def numDecodings(self, s): + """ + :type s: str + :rtype: int + """ if len(s) == 0 or s[0] == '0': return 0 prev, prev_prev = 1, 0 - for i in range(len(s)): - current = 0 + for i in xrange(len(s)): + cur = 0 if s[i] != '0': - current = prev + cur = prev if i > 0 and (s[i - 1] == '1' or (s[i - 1] == '2' and s[i] <= '6')): - current += prev_prev - prev, prev_prev = current, prev + cur += prev_prev + prev, prev_prev = cur, prev return prev - + + if __name__ == "__main__": for i in ["0", "10", "10", "103", "1032", "10323"]: - print Solution().numDecodings(i) \ No newline at end of file + print(Solution().numDecodings(i)) diff --git a/Python/decoded-string-at-index.py b/Python/decoded-string-at-index.py new file mode 100644 index 000000000..9765ded01 --- /dev/null +++ b/Python/decoded-string-at-index.py @@ -0,0 +1,64 @@ +# Time: O(n) +# Space: O(1) + +# An encoded string S is given. +# To find and write the decoded string to a tape, +# the encoded string is read one character at a time and the following steps are taken: +# +# If the character read is a letter, that letter is written onto the tape. +# If the character read is a digit (say d), +# the entire current tape is repeatedly written d-1 more times in total. +# Now for some encoded string S, and an index K, +# find and return the K-th letter (1 indexed) in the decoded string. +# +# Example 1: +# +# Input: S = "leet2code3", K = 10 +# Output: "o" +# Explanation: +# The decoded string is "leetleetcodeleetleetcodeleetleetcode". +# The 10th letter in the string is "o". +# Example 2: +# +# Input: S = "ha22", K = 5 +# Output: "h" +# Explanation: +# The decoded string is "hahahaha". The 5th letter is "h". +# Example 3: +# +# Input: S = "a2345678999999999999999", K = 1 +# Output: "a" +# Explanation: +# The decoded string is "a" repeated 8301530446056247680 times. The 1st letter is "a". +# +# Note: +# +# 2 <= S.length <= 100 +# S will only contain lowercase letters and digits 2 through 9. +# S starts with a letter. +# 1 <= K <= 10^9 +# The decoded string is guaranteed to have less than 2^63 letters. + +class Solution(object): + def decodeAtIndex(self, S, K): + """ + :type S: str + :type K: int + :rtype: str + """ + i = 0 + for c in S: + if c.isdigit(): + i *= int(c) + else: + i += 1 + + for c in reversed(S): + K %= i + if K == 0 and c.isalpha(): + return c + + if c.isdigit(): + i /= int(c) + else: + i -= 1 diff --git a/Python/degree-of-an-array.py b/Python/degree-of-an-array.py new file mode 100644 index 000000000..239c27095 --- /dev/null +++ b/Python/degree-of-an-array.py @@ -0,0 +1,44 @@ +# Time: O(n) +# Space: O(n) + +# Given a non-empty array of non-negative integers nums, +# the degree of this array is defined as the maximum frequency of any one of its elements. +# +# Your task is to find the smallest possible length of a (contiguous) subarray of nums, +# that has the same degree as nums. +# +# Example 1: +# Input: [1, 2, 2, 3, 1] +# Output: 2 +# Explanation: +# The input array has a degree of 2 because both elements 1 and 2 appear twice. +# Of the subarrays that have the same degree: +# [1, 2, 2, 3, 1], [1, 2, 2, 3], [2, 2, 3, 1], [1, 2, 2], [2, 2, 3], [2, 2] +# The shortest length is 2. So return 2. +# +# Example 2: +# Input: [1,2,2,3,1,4,2] +# Output: 6 +# Note: +# +# nums.length will be between 1 and 50,000. +# nums[i] will be an integer between 0 and 49,999. + +import collections + + +class Solution(object): + def findShortestSubArray(self, nums): + """ + :type nums: List[int] + :rtype: int + """ + counts = collections.Counter(nums) + left, right = {}, {} + for i, num in enumerate(nums): + left.setdefault(num, i) + right[num] = i + degree = max(counts.values()) + return min(right[num]-left[num]+1 \ + for num in counts.keys() \ + if counts[num] == degree) diff --git a/Python/delete-and-earn.py b/Python/delete-and-earn.py new file mode 100644 index 000000000..4f4c82aba --- /dev/null +++ b/Python/delete-and-earn.py @@ -0,0 +1,44 @@ +# Time: O(n) +# Space: O(1) + +# Given an array nums of integers, you can perform operations on the array. +# +# In each operation, you pick any nums[i] and delete it to earn nums[i] points. +# After, you must delete every element equal to nums[i] - 1 or nums[i] + 1. +# +# You start with 0 points. +# Return the maximum number of points you can earn by applying such operations. +# +# Example 1: +# Input: nums = [3, 4, 2] +# Output: 6 +# Explanation: +# Delete 4 to earn 4 points, consequently 3 is also deleted. +# Then, delete 2 to earn 2 points. 6 total points are earned. +# +# Example 2: +# Input: nums = [2, 2, 3, 3, 3, 4] +# Output: 9 +# Explanation: +# Delete 3 to earn 3 points, deleting both 2's and the 4. +# Then, delete 3 again to earn 3 points, and 3 again to earn 3 points. +# 9 total points are earned. +# +# Note: +# - The length of nums is at most 20000. +# - Each element nums[i] is an integer in the range [1, 10000]. + +class Solution(object): + def deleteAndEarn(self, nums): + """ + :type nums: List[int] + :rtype: int + """ + vals = [0] * 10001 + for num in nums: + vals[num] += num + val_i, val_i_1 = vals[0], 0 + for i in xrange(1, len(vals)): + val_i_1, val_i_2 = val_i, val_i_1 + val_i = max(vals[i] + val_i_2, val_i_1) + return val_i diff --git a/Python/delete-node-in-a-bst.py b/Python/delete-node-in-a-bst.py new file mode 100644 index 000000000..09b64141a --- /dev/null +++ b/Python/delete-node-in-a-bst.py @@ -0,0 +1,42 @@ +# Time: O(h) +# Space: O(h) + +# Definition for a binary tree node. +# class TreeNode(object): +# def __init__(self, x): +# self.val = x +# self.left = None +# self.right = None + +class Solution(object): + def deleteNode(self, root, key): + """ + :type root: TreeNode + :type key: int + :rtype: TreeNode + """ + if not root: + return root + + if root.val > key: + root.left = self.deleteNode(root.left, key) + elif root.val < key: + root.right = self.deleteNode(root.right, key) + else: + if not root.left: + right = root.right + del root + return right + elif not root.right: + left = root.left + del root + return left + else: + successor = root.right + while successor.left: + successor = successor.left + + root.val = successor.val + root.right = self.deleteNode(root.right, successor.val) + + return root diff --git a/Python/delete-node-in-a-linked-list.py b/Python/delete-node-in-a-linked-list.py new file mode 100644 index 000000000..9c3cce66b --- /dev/null +++ b/Python/delete-node-in-a-linked-list.py @@ -0,0 +1,24 @@ +# Time: O(1) +# Space: O(1) +# +# Write a function to delete a node (except the tail) in a singly linked list, +# given only access to that node. +# +# Supposed the linked list is 1 -> 2 -> 3 -> 4 and you are given the third node +# with value 3, the linked list should become 1 -> 2 -> 4 after calling your function. +# +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, x): +# self.val = x +# self.next = None + +class Solution: + # @param {ListNode} node + # @return {void} Do not return anything, modify node in-place instead. + def deleteNode(self, node): + if node and node.next: + node_to_delete = node.next + node.val = node_to_delete.val + node.next = node_to_delete.next + del node_to_delete diff --git a/Python/delete-operation-for-two-strings.py b/Python/delete-operation-for-two-strings.py new file mode 100644 index 000000000..557948096 --- /dev/null +++ b/Python/delete-operation-for-two-strings.py @@ -0,0 +1,30 @@ +# Time: O(m * n) +# Space: O(n) + +# Given two words word1 and word2, +# find the minimum number of steps required to make word1 and word2 the same, +# where in each step you can delete one character in either string. +# +# Example 1: +# Input: "sea", "eat" +# Output: 2 +# Explanation: You need one step to make "sea" to "ea" and another step to make "eat" to "ea". +# Note: +# The length of given words won't exceed 500. +# Characters in given words can only be lower-case letters. + +class Solution(object): + def minDistance(self, word1, word2): + """ + :type word1: str + :type word2: str + :rtype: int + """ + m, n = len(word1), len(word2) + dp = [[0] * (n+1) for _ in xrange(2)] + for i in xrange(m): + for j in xrange(n): + dp[(i+1)%2][j+1] = max(dp[i%2][j+1], \ + dp[(i+1)%2][j], \ + dp[i%2][j] + (word1[i] == word2[j])) + return m + n - 2*dp[m%2][n] diff --git a/Python/design-circular-deque.py b/Python/design-circular-deque.py new file mode 100644 index 000000000..fa591ebd7 --- /dev/null +++ b/Python/design-circular-deque.py @@ -0,0 +1,130 @@ +# Time: O(1) +# Space: O(k) + +# Design your implementation of the circular double-ended queue (deque). +# Your implementation should support following operations: +# +# MyCircularDeque(k): Constructor, set the size of the deque to be k. +# insertFront(): Adds an item at the front of Deque. Return true if the operation is successful. +# insertLast(): Adds an item at the rear of Deque. Return true if the operation is successful. +# deleteFront(): Deletes an item from the front of Deque. Return true if the operation is successful. +# deleteLast(): Deletes an item from the rear of Deque. Return true if the operation is successful. +# getFront(): Gets the front item from the Deque. If the deque is empty, return -1. +# getRear(): Gets the last item from Deque. If the deque is empty, return -1. +# isEmpty(): Checks whether Deque is empty or not. +# isFull(): Checks whether Deque is full or not. +# +# Example: +# +# MyCircularDeque circularDeque = new MycircularDeque(3); // set the size to be 3 +# circularDeque.insertLast(1); // return true +# circularDeque.insertLast(2); // return true +# circularDeque.insertFront(3); // return true +# circularDeque.insertFront(4); // return false, the queue is full +# circularDeque.getRear(); // return 32 +# circularDeque.isFull(); // return true +# circularDeque.deleteLast(); // return true +# circularDeque.insertFront(4); // return true +# circularDeque.getFront(); // return 4 +# +# Note: +# - All values will be in the range of [1, 1000]. +# - The number of operations will be in the range of [1, 1000]. +# - Please do not use the built-in Deque library. + +class MyCircularDeque(object): + + def __init__(self, k): + """ + Initialize your data structure here. Set the size of the deque to be k. + :type k: int + """ + self.__start = 0 + self.__size = 0 + self.__buffer = [0] * k + + def insertFront(self, value): + """ + Adds an item at the front of Deque. Return true if the operation is successful. + :type value: int + :rtype: bool + """ + if self.isFull(): + return False + self.__start = (self.__start-1) % len(self.__buffer) + self.__buffer[self.__start] = value + self.__size += 1 + return True + + def insertLast(self, value): + """ + Adds an item at the rear of Deque. Return true if the operation is successful. + :type value: int + :rtype: bool + """ + if self.isFull(): + return False + self.__buffer[(self.__start+self.__size) % len(self.__buffer)] = value + self.__size += 1 + return True + + def deleteFront(self): + """ + Deletes an item from the front of Deque. Return true if the operation is successful. + :rtype: bool + """ + if self.isEmpty(): + return False + self.__start = (self.__start+1) % len(self.__buffer) + self.__size -= 1 + return True + + def deleteLast(self): + """ + Deletes an item from the rear of Deque. Return true if the operation is successful. + :rtype: bool + """ + if self.isEmpty(): + return False + self.__size -= 1 + return True + + def getFront(self): + """ + Get the front item from the deque. + :rtype: int + """ + return -1 if self.isEmpty() else self.__buffer[self.__start] + + def getRear(self): + """ + Get the last item from the deque. + :rtype: int + """ + return -1 if self.isEmpty() else self.__buffer[(self.__start+self.__size-1) % len(self.__buffer)] + + def isEmpty(self): + """ + Checks whether the circular deque is empty or not. + :rtype: bool + """ + return self.__size == 0 + + def isFull(self): + """ + Checks whether the circular deque is full or not. + :rtype: bool + """ + return self.__size == len(self.__buffer) + + +# Your MyCircularDeque object will be instantiated and called as such: +# obj = MyCircularDeque(k) +# param_1 = obj.insertFront(value) +# param_2 = obj.insertLast(value) +# param_3 = obj.deleteFront() +# param_4 = obj.deleteLast() +# param_5 = obj.getFront() +# param_6 = obj.getRear() +# param_7 = obj.isEmpty() +# param_8 = obj.isFull() diff --git a/Python/design-circular-queue.py b/Python/design-circular-queue.py new file mode 100644 index 000000000..1c4051508 --- /dev/null +++ b/Python/design-circular-queue.py @@ -0,0 +1,111 @@ +# Time: O(1) +# Space: O(k) + +# Design your implementation of the circular queue. +# The circular queue is a linear data structure in which +# the operations are performed based on FIFO (First In First Out) +# principle and the last position is connected back to +# the first position to make a circle. It is also called ‘Ring Buffer’. +# One of the Benefits of the circular queue is that +# we can make use of the spaces in front of the queue. +# In a normal queue, once the queue becomes full, +# we can not insert the next element even if there is a space in front of the queue. +# But using the circular queue, we can use the space to store new values. +# Your implementation should support following operations: +# +# MyCircularQueue(k): Constructor, set the size of the queue to be k. +# Front: Get the front item from the queue. If the queue is empty, return -1. +# Rear: Get the last item from the queue. If the queue is empty, return -1. +# enQueue(value): Insert an element into the circular queue. Return true if the operation is successful. +# deQueue(): Delete an element from the circular queue. Return true if the operation is successful. +# isEmpty(): Checks whether the circular queue is empty or not. +# isFull(): Checks whether the circular queue is full or not. +# Example: +# +# MyCircularQueue circularQueue = new MycircularQueue(3); // set the size to be 3 +# circularQueue.enQueue(1); // return true +# circularQueue.enQueue(2); // return true +# circularQueue.enQueue(3); // return true +# circularQueue.enQueue(4); // return false, the queue is full +# circularQueue.Rear(); // return 3 +# circularQueue.isFull(); // return true +# circularQueue.deQueue(); // return true +# circularQueue.enQueue(4); // return true +# circularQueue.Rear(); // return 4 +# +# Note: +# - All values will be in the range of [1, 1000]. +# - The number of operations will be in the range of [1, 1000]. +# - Please do not use the built-in Queue library. + +class MyCircularQueue(object): + + def __init__(self, k): + """ + Initialize your data structure here. Set the size of the queue to be k. + :type k: int + """ + self.__start = 0 + self.__size = 0 + self.__buffer = [0] * k + + def enQueue(self, value): + """ + Insert an element into the circular queue. Return true if the operation is successful. + :type value: int + :rtype: bool + """ + if self.isFull(): + return False + self.__buffer[(self.__start+self.__size) % len(self.__buffer)] = value + self.__size += 1 + return True + + def deQueue(self): + """ + Delete an element from the circular queue. Return true if the operation is successful. + :rtype: bool + """ + if self.isEmpty(): + return False + self.__start = (self.__start+1) % len(self.__buffer) + self.__size -= 1 + return True + + def Front(self): + """ + Get the front item from the queue. + :rtype: int + """ + return -1 if self.isEmpty() else self.__buffer[self.__start] + + def Rear(self): + """ + Get the last item from the queue. + :rtype: int + """ + return -1 if self.isEmpty() else self.__buffer[(self.__start+self.__size-1) % len(self.__buffer)] + + def isEmpty(self): + """ + Checks whether the circular queue is empty or not. + :rtype: bool + """ + return self.__size == 0 + + def isFull(self): + """ + Checks whether the circular queue is full or not. + :rtype: bool + """ + return self.__size == len(self.__buffer) + + +# Your MyCircularQueue object will be instantiated and called as such: +# obj = MyCircularQueue(k) +# param_1 = obj.enQueue(value) +# param_2 = obj.deQueue() +# param_3 = obj.Front() +# param_4 = obj.Rear() +# param_5 = obj.isEmpty() +# param_6 = obj.isFull() diff --git a/Python/design-compressed-string-iterator.py b/Python/design-compressed-string-iterator.py new file mode 100644 index 000000000..e23233aa9 --- /dev/null +++ b/Python/design-compressed-string-iterator.py @@ -0,0 +1,41 @@ +# Time: O(1) +# Space: O(1) + +import re + + +class StringIterator(object): + + def __init__(self, compressedString): + """ + :type compressedString: str + """ + self.__result = re.findall(r"([a-zA-Z])(\d+)", compressedString) + self.__index, self.__num, self.__ch = 0, 0, ' ' + + def next(self): + """ + :rtype: str + """ + if not self.hasNext(): + return ' ' + if self.__num == 0: + self.__ch = self.__result[self.__index][0] + self.__num = int(self.__result[self.__index][1]) + self.__index += 1 + self.__num -= 1 + return self.__ch + + + def hasNext(self): + """ + :rtype: bool + """ + return self.__index != len(self.__result) or self.__num != 0 + + + +# Your StringIterator object will be instantiated and called as such: +# obj = StringIterator(compressedString) +# param_1 = obj.next() +# param_2 = obj.hasNext() diff --git a/Python/design-excel-sum-formula.py b/Python/design-excel-sum-formula.py new file mode 100644 index 000000000..edd5df557 --- /dev/null +++ b/Python/design-excel-sum-formula.py @@ -0,0 +1,93 @@ +# Time: set: O((r * c)^2) +# get: O(1) +# sum: O((r * c)^2) +# Space: O(r * c) + +import collections + + +class Excel(object): + + def __init__(self, H, W): + """ + :type H: int + :type W: str + """ + self.__exl = [[0 for _ in xrange(ord(W)-ord('A')+1)] \ + for _ in xrange(H+1)] + self.__fward = collections.defaultdict(lambda : collections.defaultdict(int)) + self.__bward = collections.defaultdict(set) + + + def set(self, r, c, v): + """ + :type r: int + :type c: str + :type v: int + :rtype: void + """ + self.__reset_dependency(r, c); + self.__update_others(r, c, v); + + + def get(self, r, c): + """ + :type r: int + :type c: str + :rtype: int + """ + return self.__exl[r][ord(c) - ord('A')] + + + def sum(self, r, c, strs): + """ + :type r: int + :type c: str + :type strs: List[str] + :rtype: int + """ + self.__reset_dependency(r, c) + result = self.__calc_and_update_dependency(r, c, strs) + self.__update_others(r, c, result) + return result + + + def __reset_dependency(self, r, c): + key = (r, c) + if key in self.__bward.keys(): + for k in self.__bward[key]: + self.__fward[k].pop(key, None) + self.__bward[key] = set() + + + def __calc_and_update_dependency(self, r, c, strs): + result = 0 + for s in strs: + s, e = s.split(':')[0], s.split(':')[1] if ':' in s else s + left, right, top, bottom = ord(s[0])-ord('A'), ord(e[0])-ord('A'), int(s[1:]), int(e[1:]) + for i in xrange(top, bottom+1): + for j in xrange(left, right+1): + result += self.__exl[i][j] + self.__fward[(i, chr(ord('A')+j))][(r, c)] += 1 + self.__bward[(r, c)].add((i, chr(ord('A')+j))) + return result + + + def __update_others(self, r, c, v): + prev = self.__exl[r][ord(c)-ord('A')] + self.__exl[r][ord(c)-ord('A')] = v + q = collections.deque() + q.append(((r, c), v-prev)) + while q: + key, diff = q.popleft() + if key in self.__fward: + for k, count in self.__fward[key].iteritems(): + q.append((k, diff*count)) + self.__exl[k[0]][ord(k[1])-ord('A')] += diff*count + + +# Your Excel object will be instantiated and called as such: +# obj = Excel(H, W) +# obj.set(r,c,v) +# param_2 = obj.get(r,c) +# param_3 = obj.sum(r,c,strs) diff --git a/Python/design-hashmap.py b/Python/design-hashmap.py new file mode 100644 index 000000000..0b9c6e75f --- /dev/null +++ b/Python/design-hashmap.py @@ -0,0 +1,125 @@ +# Time: O(1) +# Space: O(n) + +# Design a HashMap without using any built-in hash table libraries. +# +# To be specific, your design should include these two functions: +# +# put(key, value) : Insert a (key, value) pair into the HashMap. +# If the value already exists in the HashMap, update the value. +# get(key): Returns the value to which the specified key is mapped, +# or -1 if this map contains no mapping for the key. +# remove(key) : Remove the mapping for the value key if this map contains the mapping for the key. +# +# Example: +# +# MyHashMap hashMap = new MyHashMap(); +# hashMap.put(1, 1); +# hashMap.put(2, 2); +# hashMap.get(1); // returns 1 +# hashMap.get(3); // returns -1 (not found) +# hashMap.put(2, 1); // update the existing value +# hashMap.get(2); // returns 1 +# hashMap.remove(2); // remove the mapping for 2 +# hashMap.get(2); // returns -1 (not found) +# +# Note: +# - All values will be in the range of [1, 1000000]. +# - The number of operations will be in the range of [1, 10000]. +# - Please do not use the built-in HashMap library. + + +class ListNode(object): + def __init__(self, key, val): + self.val = val + self.key = key + self.next = None + self.prev = None + + +class LinkedList(object): + def __init__(self): + self.head = None + self.tail = None + + def insert(self, node): + node.next, node.prev = None, None # avoid dirty node + if self.head is None: + self.head = node + else: + self.tail.next = node + node.prev = self.tail + self.tail = node + + def delete(self, node): + if node.prev: + node.prev.next = node.next + else: + self.head = node.next + if node.next: + node.next.prev = node.prev + else: + self.tail = node.prev + node.next, node.prev = None, None # make node clean + + def find(self, key): + curr = self.head + while curr: + if curr.key == key: + break + curr = curr.next + return curr + + +class MyHashMap(object): + + def __init__(self): + """ + Initialize your data structure here. + """ + self.__data = [LinkedList() for _ in xrange(10000)] + + def put(self, key, value): + """ + value will always be positive. + :type key: int + :type value: int + :rtype: void + """ + l = self.__data[key % len(self.__data)] + node = l.find(key) + if node: + node.val = value + else: + l.insert(ListNode(key, value)) + + def get(self, key): + """ + Returns the value to which the specified key is mapped, or -1 if this map contains no mapping for the key + :type key: int + :rtype: int + """ + l = self.__data[key % len(self.__data)] + node = l.find(key) + if node: + return node.val + else: + return -1 + + def remove(self, key): + """ + Removes the mapping of the specified value key if this map contains a mapping for the key + :type key: int + :rtype: void + """ + l = self.__data[key % len(self.__data)] + node = l.find(key) + if node: + l.delete(node) + + +# Your MyHashMap object will be instantiated and called as such: +# obj = MyHashMap() +# obj.put(key,value) +# param_2 = obj.get(key) +# obj.remove(key) diff --git a/Python/design-hashset.py b/Python/design-hashset.py new file mode 100644 index 000000000..00f94e37f --- /dev/null +++ b/Python/design-hashset.py @@ -0,0 +1,115 @@ +# Time: O(1) +# Space: O(n) + +# Design a HashSet without using any built-in hash table libraries. +# +# To be specific, your design should include these two functions: +# +# add(value): Insert a value into the HashSet. +# contains(value) : Return whether the value exists in the HashSet or not. +# remove(value): Remove a value in the HashSet. If the value does not exist in the HashSet, do nothing. +# +# Example: +# +# MyHashSet hashSet = new MyHashSet(); +# hashSet.add(1); +# hashSet.add(2); +# hashSet.contains(1); // returns true +# hashSet.contains(3); // returns false (not found) +# hashSet.add(2); +# hashSet.contains(2); // returns true +# hashSet.remove(2); +# hashSet.contains(2); // returns false (already removed) +# +# Note: +# - All values will be in the range of [1, 1000000]. +# - The number of operations will be in the range of [1, 10000]. +# - Please do not use the built-in HashSet library. + + +class ListNode(object): + def __init__(self, key, val): + self.val = val + self.key = key + self.next = None + self.prev = None + + +class LinkedList(object): + def __init__(self): + self.head = None + self.tail = None + + def insert(self, node): + node.next, node.prev = None, None # avoid dirty node + if self.head is None: + self.head = node + else: + self.tail.next = node + node.prev = self.tail + self.tail = node + + def delete(self, node): + if node.prev: + node.prev.next = node.next + else: + self.head = node.next + if node.next: + node.next.prev = node.prev + else: + self.tail = node.prev + node.next, node.prev = None, None # make node clean + + def find(self, key): + curr = self.head + while curr: + if curr.key == key: + break + curr = curr.next + return curr + + +class MyHashSet(object): + + def __init__(self): + """ + Initialize your data structure here. + """ + self.__data = [LinkedList() for _ in xrange(10000)] + + def add(self, key): + """ + :type key: int + :rtype: void + """ + l = self.__data[key % len(self.__data)] + node = l.find(key) + if not node: + l.insert(ListNode(key, 0)) + + def remove(self, key): + """ + :type key: int + :rtype: void + """ + l = self.__data[key % len(self.__data)] + node = l.find(key) + if node: + l.delete(node) + + def contains(self, key): + """ + Returns true if this set did not already contain the specified element + :type key: int + :rtype: bool + """ + l = self.__data[key % len(self.__data)] + node = l.find(key) + return node is not None + + +# Your MyHashSet object will be instantiated and called as such: +# obj = MyHashSet() +# obj.add(key) +# obj.remove(key) +# param_3 = obj.contains(key) diff --git a/Python/design-hit-counter.py b/Python/design-hit-counter.py new file mode 100644 index 000000000..f5ad39b18 --- /dev/null +++ b/Python/design-hit-counter.py @@ -0,0 +1,44 @@ +# Time: O(1), amortized +# Space: O(k), k is the count of seconds. + +from collections import deque + +class HitCounter(object): + + def __init__(self): + """ + Initialize your data structure here. + """ + self.__k = 300 + self.__dq = deque() + self.__count = 0 + + def hit(self, timestamp): + """ + Record a hit. + @param timestamp - The current timestamp (in seconds granularity). + :type timestamp: int + :rtype: void + """ + self.getHits(timestamp) + if self.__dq and self.__dq[-1][0] == timestamp: + self.__dq[-1][1] += 1 + else: + self.__dq.append([timestamp, 1]) + self.__count += 1 + + def getHits(self, timestamp): + """ + Return the number of hits in the past 5 minutes. + @param timestamp - The current timestamp (in seconds granularity). + :type timestamp: int + :rtype: int + """ + while self.__dq and self.__dq[0][0] <= timestamp - self.__k: + self.__count -= self.__dq.popleft()[1] + return self.__count + +# Your HitCounter object will be instantiated and called as such: +# obj = HitCounter() +# obj.hit(timestamp) +# param_2 = obj.getHits(timestamp) diff --git a/Python/design-in-memory-file-system.py b/Python/design-in-memory-file-system.py new file mode 100644 index 000000000..0785bfff0 --- /dev/null +++ b/Python/design-in-memory-file-system.py @@ -0,0 +1,122 @@ +# Time: ls: O(l + klogk), l is the path length, k is the number of entries in the last level directory +# mkdir: O(l) +# addContentToFile: O(l + c), c is the content size +# readContentFromFile: O(l + c) +# Space: O(n + s), n is the number of dir/file nodes, s is the total content size. + +# Design an in-memory file system to simulate the following functions: +# +# ls: Given a path in string format. If it is a file path, +# return a list that only contains this file's name. +# If it is a directory path, return the list of file and directory names in this directory. +# Your output (file and directory names together) should in lexicographic order. +# +# mkdir: Given a directory path that does not exist, +# you should make a new directory according to the path. +# If the middle directories in the path don't exist either, +# you should create them as well. This function has void return type. +# +# addContentToFile: Given a file path and file content in string format. +# If the file doesn't exist, you need to create that file containing given content. +# If the file already exists, you need to append given content to original content. +# This function has void return type. +# +# readContentFromFile: Given a file path, return its content in string format. +# +# Example: +# Input: +# ["FileSystem","ls","mkdir","addContentToFile","ls","readContentFromFile"] +# [[],["/"],["/a/b/c"],["/a/b/c/d","hello"],["/"],["/a/b/c/d"]] +# Output: +# [null,[],null,null,["a"],"hello"] +# +# Note: +# 1. You can assume all file or directory paths are absolute paths +# which begin with / and do not end with / except that the path is just "/". +# 2. You can assume that all operations will be passed valid parameters and +# users will not attempt to retrieve file content or list a directory or file that does not exist. +# 3. You can assume that all directory names and file names only contain lower-case letters, +# and same names won't exist in the same directory. + + +class TrieNode(object): + + def __init__(self): + self.is_file = False + self.children = {} + self.content = "" + +class FileSystem(object): + + def __init__(self): + self.__root = TrieNode() + + + def ls(self, path): + """ + :type path: str + :rtype: List[str] + """ + curr = self.__getNode(path) + + if curr.is_file: + return [self.__split(path, '/')[-1]] + + return sorted(curr.children.keys()) + + + def mkdir(self, path): + """ + :type path: str + :rtype: void + """ + curr = self.__putNode(path) + curr.is_file = False + + + def addContentToFile(self, filePath, content): + """ + :type filePath: str + :type content: str + :rtype: void + """ + curr = self.__putNode(filePath) + curr.is_file = True + curr.content += content + + + def readContentFromFile(self, filePath): + """ + :type filePath: str + :rtype: str + """ + return self.__getNode(filePath).content + + + def __getNode(self, path): + curr = self.__root + for s in self.__split(path, '/'): + curr = curr.children[s] + return curr + + + def __putNode(self, path): + curr = self.__root + for s in self.__split(path, '/'): + if s not in curr.children: + curr.children[s] = TrieNode() + curr = curr.children[s] + return curr + + + def __split(self, path, delim): + if path == '/': + return [] + return path.split('/')[1:] + +# Your FileSystem object will be instantiated and called as such: +# obj = FileSystem() +# param_1 = obj.ls(path) +# obj.mkdir(path) +# obj.addContentToFile(filePath,content) +# param_4 = obj.readContentFromFile(filePath) diff --git a/Python/design-linked-list.py b/Python/design-linked-list.py new file mode 100644 index 000000000..94d48d082 --- /dev/null +++ b/Python/design-linked-list.py @@ -0,0 +1,107 @@ +# Time: O(n) +# Space: O(n) + +class Node: + def __init__(self, value): + self.val = value + self.next = self.prev = None + + +class MyLinkedList(object): + + def __init__(self): + """ + Initialize your data structure here. + """ + self.__head = self.__tail = Node(-1) + self.__head.next = self.__tail + self.__tail.prev = self.__head + self.__size = 0 + + def get(self, index): + """ + Get the value of the index-th node in the linked list. If the index is invalid, return -1. + :type index: int + :rtype: int + """ + if 0 <= index <= self.__size // 2: + return self.__forward(0, index, self.__head.next).val + elif self.__size // 2 < index < self.__size: + return self.__backward(self.__size, index, self.__tail).val + return -1 + + def addAtHead(self, val): + """ + Add a node of value val before the first element of the linked list. + After the insertion, the new node will be the first node of the linked list. + :type val: int + :rtype: void + """ + self.__add(self.__head, val) + + def addAtTail(self, val): + """ + Append a node of value val to the last element of the linked list. + :type val: int + :rtype: void + """ + self.__add(self.__tail.prev, val) + + def addAtIndex(self, index, val): + """ + Add a node of value val before the index-th node in the linked list. + If index equals to the length of linked list, + the node will be appended to the end of linked list. + If index is greater than the length, the node will not be inserted. + :type index: int + :type val: int + :rtype: void + """ + if 0 <= index <= self.__size // 2: + self.__add(self.__forward(0, index, self.__head.next).prev, val) + elif self.__size // 2 < index <= self.__size: + self.__add(self.__backward(self.__size, index, self.__tail).prev, val) + + def deleteAtIndex(self, index): + """ + Delete the index-th node in the linked list, if the index is valid. + :type index: int + :rtype: void + """ + if 0 <= index <= self.__size // 2: + self.__remove(self.__forward(0, index, self.__head.next)) + elif self.__size // 2 < index < self.__size: + self.__remove(self.__backward(self.__size, index, self.__tail)) + + def __add(self, preNode, val): + node = Node(val) + node.prev = preNode + node.next = preNode.next + node.prev.next = node.next.prev = node + self.__size += 1 + + def __remove(self, node): + node.prev.next = node.next + node.next.prev = node.prev + self.__size -= 1 + + def __forward(self, start, end, curr): + while start != end: + start += 1 + curr = curr.next + return curr + + def __backward(self, start, end, curr): + while start != end: + start -= 1 + curr = curr.prev + return curr + + +# Your MyLinkedList object will be instantiated and called as such: +# obj = MyLinkedList() +# param_1 = obj.get(index) +# obj.addAtHead(val) +# obj.addAtTail(val) +# obj.addAtIndex(index,val) +# obj.deleteAtIndex(index) diff --git a/Python/design-log-storage-system.py b/Python/design-log-storage-system.py new file mode 100644 index 000000000..ca9a90979 --- /dev/null +++ b/Python/design-log-storage-system.py @@ -0,0 +1,40 @@ +# Time: put: O(1) +# retrieve: O(n + dlogd), n is the size of the total logs +# , d is the size of the found logs +# Space: O(n) + +class LogSystem(object): + + def __init__(self): + self.__logs = [] + self.__granularity = {'Year': 4, 'Month': 7, 'Day': 10, \ + 'Hour': 13, 'Minute': 16, 'Second': 19} + + + def put(self, id, timestamp): + """ + :type id: int + :type timestamp: str + :rtype: void + """ + self.__logs.append((id, timestamp)) + + + def retrieve(self, s, e, gra): + """ + :type s: str + :type e: str + :type gra: str + :rtype: List[int] + """ + i = self.__granularity[gra] + begin = s[:i] + end = e[:i] + return sorted(id for id, timestamp in self.__logs \ + if begin <= timestamp[:i] <= end) + + +# Your LogSystem object will be instantiated and called as such: +# obj = LogSystem() +# obj.put(id,timestamp) +# param_2 = obj.retrieve(s,e,gra) diff --git a/Python/design-phone-directory.py b/Python/design-phone-directory.py new file mode 100644 index 000000000..2d039ca18 --- /dev/null +++ b/Python/design-phone-directory.py @@ -0,0 +1,61 @@ +# init: Time: O(n), Space: O(n) +# get: Time: O(1), Space: O(1) +# check: Time: O(1), Space: O(1) +# release: Time: O(1), Space: O(1) + +class PhoneDirectory(object): + + def __init__(self, maxNumbers): + """ + Initialize your data structure here + @param maxNumbers - The maximum numbers that can be stored in the phone directory. + :type maxNumbers: int + """ + self.__curr = 0 + self.__numbers = range(maxNumbers) + self.__used = [False] * maxNumbers + + + def get(self): + """ + Provide a number which is not assigned to anyone. + @return - Return an available number. Return -1 if none is available. + :rtype: int + """ + if self.__curr == len(self.__numbers): + return -1 + number = self.__numbers[self.__curr] + self.__curr += 1 + self.__used[number] = True + return number + + + def check(self, number): + """ + Check if a number is available or not. + :type number: int + :rtype: bool + """ + return 0 <= number < len(self.__numbers) and \ + not self.__used[number] + + + def release(self, number): + """ + Recycle or release a number. + :type number: int + :rtype: void + """ + if not 0 <= number < len(self.__numbers) or \ + not self.__used[number]: + return + self.__used[number] = False + self.__curr -= 1 + self.__numbers[self.__curr] = number + + +# Your PhoneDirectory object will be instantiated and called as such: +# obj = PhoneDirectory(maxNumbers) +# param_1 = obj.get() +# param_2 = obj.check(number) +# obj.release(number) diff --git a/Python/design-search-autocomplete-system.py b/Python/design-search-autocomplete-system.py new file mode 100644 index 000000000..cc540a426 --- /dev/null +++ b/Python/design-search-autocomplete-system.py @@ -0,0 +1,78 @@ +# Time: O(p^2), p is the length of the prefix +# Space: O(p * t + s), t is the number of nodes of trie +# , s is the size of the sentences + +import collections + + +class TrieNode(object): + + def __init__(self): + self.__TOP_COUNT = 3 + self.infos = [] + self.leaves = {} + + + def insert(self, s, times): + cur = self + cur.add_info(s, times) + for c in s: + if c not in cur.leaves: + cur.leaves[c] = TrieNode() + cur = cur.leaves[c] + cur.add_info(s, times) + + + def add_info(self, s, times): + for p in self.infos: + if p[1] == s: + p[0] = -times + break + else: + self.infos.append([-times, s]) + self.infos.sort() + if len(self.infos) > self.__TOP_COUNT: + self.infos.pop() + + +class AutocompleteSystem(object): + + def __init__(self, sentences, times): + """ + :type sentences: List[str] + :type times: List[int] + """ + self.__trie = TrieNode() + self.__cur_node = self.__trie + self.__search = [] + self.__sentence_to_count = collections.defaultdict(int) + for sentence, count in zip(sentences, times): + self.__sentence_to_count[sentence] = count + self.__trie.insert(sentence, count) + + + def input(self, c): + """ + :type c: str + :rtype: List[str] + """ + result = [] + if c == '#': + self.__sentence_to_count["".join(self.__search)] += 1 + self.__trie.insert("".join(self.__search), self.__sentence_to_count["".join(self.__search)]) + self.__cur_node = self.__trie + self.__search = [] + else: + self.__search.append(c) + if self.__cur_node: + if c not in self.__cur_node.leaves: + self.__cur_node = None + return [] + self.__cur_node = self.__cur_node.leaves[c] + result = [p[1] for p in self.__cur_node.infos] + return result + + +# Your AutocompleteSystem object will be instantiated and called as such: +# obj = AutocompleteSystem(sentences, times) +# param_1 = obj.input(c) diff --git a/Python/design-snake-game.py b/Python/design-snake-game.py new file mode 100644 index 000000000..f0792ac15 --- /dev/null +++ b/Python/design-snake-game.py @@ -0,0 +1,63 @@ +# Time: O(1) per move +# Space: O(s), s is the current length of the snake. + +from collections import defaultdict, deque + +class SnakeGame(object): + + def __init__(self, width,height,food): + """ + Initialize your data structure here. + @param width - screen width + @param height - screen height + @param food - A list of food positions + E.g food = [[1,1], [1,0]] means the first food is positioned at [1,1], the second is at [1,0]. + :type width: int + :type height: int + :type food: List[List[int]] + """ + self.__width = width + self.__height = height + self.__score = 0 + self.__food = deque(food) + self.__snake = deque([(0, 0)]) + self.__direction = {"U": (-1, 0), "L": (0, -1), "R": (0, 1), "D": (1, 0)} + self.__lookup = defaultdict(int) + self.__lookup[(0, 0)] += 1 + + def move(self, direction): + """ + Moves the snake. + @param direction - 'U' = Up, 'L' = Left, 'R' = Right, 'D' = Down + @return The game's score after the move. Return -1 if game over. + Game over when snake crosses the screen boundary or bites its body. + :type direction: str + :rtype: int + """ + def valid(x, y): + return 0 <= x < self.__height and \ + 0 <= y < self.__width and \ + (x, y) not in self.__lookup + d = self.__direction[direction] + x, y = self.__snake[-1][0] + d[0], self.__snake[-1][1] + d[1] + + tail = self.__snake[-1] + self.__lookup[self.__snake[0]] -= 1 + if self.__lookup[self.__snake[0]] == 0: + self.__lookup.pop(self.__snake[0]) + self.__snake.popleft() + if not valid(x, y): + return -1 + elif self.__food and (self.__food[0][0], self.__food[0][1]) == (x, y): + self.__score += 1 + self.__food.popleft() + self.__snake.appendleft(tail) + self.__lookup[tail] += 1 + self.__snake += (x, y), + self.__lookup[(x, y)] += 1 + return self.__score + + +# Your SnakeGame object will be instantiated and called as such: +# obj = SnakeGame(width, height, food) +# param_1 = obj.move(direction) diff --git a/Python/design-tic-tac-toe.py b/Python/design-tic-tac-toe.py new file mode 100644 index 000000000..b5f6d0aac --- /dev/null +++ b/Python/design-tic-tac-toe.py @@ -0,0 +1,56 @@ +# Time: O(1), per move. +# Space: O(n^2) + +try: + xrange # Python 2 +except NameError: + xrange = range # Python 3 + + +class TicTacToe(object): + + def __init__(self, n): + """ + Initialize your data structure here. + :type n: int + """ + self.__size = n + self.__rows = [[0, 0] for _ in xrange(n)] + self.__cols = [[0, 0] for _ in xrange(n)] + self.__diagonal = [0, 0] + self.__anti_diagonal = [0, 0] + + def move(self, row, col, player): + """ + Player {player} makes a move at ({row}, {col}). + @param row The row of the board. + @param col The column of the board. + @param player The player, can be either 1 or 2. + @return The current winning condition, can be either: + 0: No one wins. + 1: Player 1 wins. + 2: Player 2 wins. + :type row: int + :type col: int + :type player: int + :rtype: int + """ + i = player - 1 + self.__rows[row][i] += 1 + self.__cols[col][i] += 1 + if row == col: + self.__diagonal[i] += 1 + if col == len(self.__rows) - row - 1: + self.__anti_diagonal[i] += 1 + if any(self.__rows[row][i] == self.__size, + self.__cols[col][i] == self.__size, + self.__diagonal[i] == self.__size, + self.__anti_diagonal[i] == self.__size): + return player + + return 0 + + +# Your TicTacToe object will be instantiated and called as such: +# obj = TicTacToe(n) +# param_1 = obj.move(row,col,player) diff --git a/Python/design-twitter.py b/Python/design-twitter.py new file mode 100644 index 000000000..ed03792da --- /dev/null +++ b/Python/design-twitter.py @@ -0,0 +1,115 @@ +# Time: O(klogu), k is most recently number of tweets, +# u is the number of the user's following. +# Space: O(t + f), t is the total number of tweets, +# f is the total number of followings. + +# Design a simplified version of Twitter where users can post tweets, +# follow/unfollow another user and is able to see the 10 most recent +# tweets in the user's news feed. Your design should support the following methods: +# +# postTweet(userId, tweetId): Compose a new tweet. +# getNewsFeed(userId): Retrieve the 10 most recent tweet ids in the user's +# news feed. Each item in the news feed must be posted by users who the user followed +# or by the user herself. Tweets must be ordered from most recent to least recent. +# follow(followerId, followeeId): Follower follows a followee. +# unfollow(followerId, followeeId): Follower unfollows a followee. +# Example: +# +# Twitter twitter = new Twitter(); +# +# // User 1 posts a new tweet (id = 5). +# twitter.postTweet(1, 5); +# +# // User 1's news feed should return a list with 1 tweet id -> [5]. +# twitter.getNewsFeed(1); +# +# // User 1 follows user 2. +# twitter.follow(1, 2); +# +# // User 2 posts a new tweet (id = 6). +# twitter.postTweet(2, 6); +# +# // User 1's news feed should return a list with 2 tweet ids -> [6, 5]. +# // Tweet id 6 should precede tweet id 5 because it is posted after tweet id 5. +# twitter.getNewsFeed(1); +# +# // User 1 unfollows user 2. +# twitter.unfollow(1, 2); +# +# // User 1's news feed should return a list with 1 tweet id -> [5], +# // since user 1 is no longer following user 2. +# twitter.getNewsFeed(1); + +import collections +import heapq + + +class Twitter(object): + + def __init__(self): + """ + Initialize your data structure here. + """ + self.__number_of_most_recent_tweets = 10 + self.__followings = collections.defaultdict(set) + self.__messages = collections.defaultdict(list) + self.__time = 0 + + def postTweet(self, userId, tweetId): + """ + Compose a new tweet. + :type userId: int + :type tweetId: int + :rtype: void + """ + self.__time += 1 + self.__messages[userId].append((self.__time, tweetId)) + + def getNewsFeed(self, userId): + """ + Retrieve the 10 most recent tweet ids in the user's news feed. Each item in the news feed must be posted by users who the user followed or by the user herself. Tweets must be ordered from most recent to least recent. + :type userId: int + :rtype: List[int] + """ + max_heap = [] + if self.__messages[userId]: + heapq.heappush(max_heap, (-self.__messages[userId][-1][0], userId, 0)) + for uid in self.__followings[userId]: + if self.__messages[uid]: + heapq.heappush(max_heap, (-self.__messages[uid][-1][0], uid, 0)) + + result = [] + while max_heap and len(result) < self.__number_of_most_recent_tweets: + t, uid, curr = heapq.heappop(max_heap) + nxt = curr + 1; + if nxt != len(self.__messages[uid]): + heapq.heappush(max_heap, (-self.__messages[uid][-(nxt+1)][0], uid, nxt)) + result.append(self.__messages[uid][-(curr+1)][1]); + return result + + def follow(self, followerId, followeeId): + """ + Follower follows a followee. If the operation is invalid, it should be a no-op. + :type followerId: int + :type followeeId: int + :rtype: void + """ + if followerId != followeeId: + self.__followings[followerId].add(followeeId) + + def unfollow(self, followerId, followeeId): + """ + Follower unfollows a followee. If the operation is invalid, it should be a no-op. + :type followerId: int + :type followeeId: int + :rtype: void + """ + self.__followings[followerId].discard(followeeId) + + +# Your Twitter object will be instantiated and called as such: +# obj = Twitter() +# obj.postTweet(userId,tweetId) +# param_2 = obj.getNewsFeed(userId) +# obj.follow(followerId,followeeId) +# obj.unfollow(followerId,followeeId) diff --git a/Python/detect-capital.py b/Python/detect-capital.py new file mode 100644 index 000000000..c0949a7e5 --- /dev/null +++ b/Python/detect-capital.py @@ -0,0 +1,24 @@ +# Time: O(l) +# Space: O(1) + +# We define the usage of capitals in a word to be right when one of the following cases holds: +# +# All letters in this word are capitals, like "USA". +# All letters in this word are not capitals, like "leetcode". +# Only the first letter in this word is capital if it has more than one letter, like "Google". +# Otherwise, we define that this word doesn't use capitals in a right way. +# Example 1: +# Input: "USA" +# Output: True +# Example 2: +# Input: "FlaG" +# Output: False +# Note: The input will be a non-empty word consisting of uppercase and lowercase latin letters. + +class Solution(object): + def detectCapitalUse(self, word): + """ + :type word: str + :rtype: bool + """ + return word.isupper() or word.islower() or word.istitle() diff --git a/Python/diagonal-traverse.py b/Python/diagonal-traverse.py new file mode 100644 index 000000000..639c64adf --- /dev/null +++ b/Python/diagonal-traverse.py @@ -0,0 +1,55 @@ +# Time: O(m * n) +# Space: O(1) + +# Given a matrix of M x N elements (M rows, N columns), +# return all elements of the matrix in diagonal order as shown in the below image. +# +# Example: +# Input: +# [ +# [ 1, 2, 3 ], +# [ 4, 5, 6 ], +# [ 7, 8, 9 ] +# ] +# Output: [1,2,4,7,5,3,6,8,9] +# Explanation: +# +# Note: +# The total number of elements of the given matrix will not exceed 10,000. +# Show Company Tags + +class Solution(object): + def findDiagonalOrder(self, matrix): + """ + :type matrix: List[List[int]] + :rtype: List[int] + """ + if not matrix or not matrix[0]: + return [] + + result = [] + row, col, d = 0, 0, 0 + dirs = [(-1, 1), (1, -1)] + + for i in xrange(len(matrix) * len(matrix[0])): + result.append(matrix[row][col]) + row += dirs[d][0] + col += dirs[d][1] + + if row >= len(matrix): + row = len(matrix) - 1 + col += 2 + d = 1 - d + elif col >= len(matrix[0]): + col = len(matrix[0]) - 1 + row += 2 + d = 1 - d + elif row < 0: + row = 0 + d = 1 - d + elif col < 0: + col = 0 + d = 1 - d + + return result + diff --git a/Python/diameter-of-binary-tree.py b/Python/diameter-of-binary-tree.py new file mode 100644 index 000000000..f9997e95e --- /dev/null +++ b/Python/diameter-of-binary-tree.py @@ -0,0 +1,39 @@ +# Time: O(n) +# Space: O(h) + +# Given a binary tree, you need to compute the length of the diameter of the tree. +# The diameter of a binary tree is the length of the longest path between +# any two nodes in a tree. This path may or may not pass through the root. +# +# Example: +# Given a binary tree +# 1 +# / \ +# 2 3 +# / \ +# 4 5 +# Return 3, which is the length of the path [4,2,1,3] or [5,2,1,3]. +# +# Note: The length of path between two nodes is represented by the number of edges between them. + +# Definition for a binary tree node. +# class TreeNode(object): +# def __init__(self, x): +# self.val = x +# self.left = None +# self.right = None + +class Solution(object): + def diameterOfBinaryTree(self, root): + """ + :type root: TreeNode + :rtype: int + """ + return self.depth(root, 0)[1] + + def depth(self, root, diameter): + if not root: + return 0, diameter + left, diameter = self.depth(root.left, diameter) + right, diameter = self.depth(root.right, diameter) + return 1 + max(left, right), max(diameter, left + right) diff --git a/Python/different-ways-to-add-parentheses.py b/Python/different-ways-to-add-parentheses.py new file mode 100644 index 000000000..c88602a20 --- /dev/null +++ b/Python/different-ways-to-add-parentheses.py @@ -0,0 +1,79 @@ +# Time: O(n * 4^n / n^(3/2)) ~= n * Catalan numbers = n * (C(2n, n) - C(2n, n - 1)), +# due to the size of the results is Catalan numbers, +# and every way of evaluation is the length of the string, +# so the time complexity is at most n * Catalan numbers. +# Space: O(n * 4^n / n^(3/2)), the cache size of lookup is at most n * Catalan numbers. +# +# Given a string of numbers and operators, return all possible +# results from computing all the different possible ways to +# group numbers and operators. The valid operators are +, - and *. +# +# +# Example 1 +# Input: "2-1-1". +# +# ((2-1)-1) = 0 +# (2-(1-1)) = 2 +# Output: [0, 2] +# +# +# Example 2 +# Input: "2*3-4*5" +# +# (2*(3-(4*5))) = -34 +# ((2*3)-(4*5)) = -14 +# ((2*(3-4))*5) = -10 +# (2*((3-4)*5)) = -10 +# (((2*3)-4)*5) = 10 +# Output: [-34, -14, -10, -10, 10] +# + +import operator +import re + + +class Solution: + # @param {string} input + # @return {integer[]} + def diffWaysToCompute(self, input): + tokens = re.split('(\D)', input) + nums = map(int, tokens[::2]) + ops = map({'+': operator.add, '-': operator.sub, '*': operator.mul}.get, tokens[1::2]) + lookup = [[None for _ in xrange(len(nums))] for _ in xrange(len(nums))] + + def diffWaysToComputeRecu(left, right): + if left == right: + return [nums[left]] + if lookup[left][right]: + return lookup[left][right] + lookup[left][right] = [ops[i](x, y) + for i in xrange(left, right) + for x in diffWaysToComputeRecu(left, i) + for y in diffWaysToComputeRecu(i + 1, right)] + return lookup[left][right] + + return diffWaysToComputeRecu(0, len(nums) - 1) + +class Solution2: + # @param {string} input + # @return {integer[]} + def diffWaysToCompute(self, input): + lookup = [[None for _ in xrange(len(input) + 1)] for _ in xrange(len(input) + 1)] + ops = {'+': operator.add, '-': operator.sub, '*': operator.mul} + + def diffWaysToComputeRecu(left, right): + if lookup[left][right]: + return lookup[left][right] + result = [] + for i in xrange(left, right): + if input[i] in ops: + for x in diffWaysToComputeRecu(left, i): + for y in diffWaysToComputeRecu(i + 1, right): + result.append(ops[input[i]](x, y)) + + if not result: + result = [int(input[left:right])] + lookup[left][right] = result + return lookup[left][right] + + return diffWaysToComputeRecu(0, len(input)) diff --git a/Python/distinct-subsequences.py b/Python/distinct-subsequences.py index 48709899e..747aeee53 100644 --- a/Python/distinct-subsequences.py +++ b/Python/distinct-subsequences.py @@ -1,15 +1,16 @@ +from __future__ import print_function # Time: O(n^2) # Space: O(n) # # Given a string S and a string T, count the number of distinct subsequences of T in S. -# -# A subsequence of a string is a new string which is formed from the original string -# by deleting some (can be none) of the characters without disturbing the relative positions +# +# A subsequence of a string is a new string which is formed from the original string +# by deleting some (can be none) of the characters without disturbing the relative positions # of the remaining characters. (ie, "ACE" is a subsequence of "ABCDE" while "AEC" is not). -# +# # Here is an example: # S = "rabbbit", T = "rabbit" -# +# # Return 3. # @@ -23,10 +24,10 @@ def numDistinct(self, S, T): if S_char == T_char: ways[j + 1] += ways[j] return ways[len(T)] - + if __name__ == "__main__": S = "rabbbit" T = "rabbit" result = Solution().numDistinct(S, T) - print result - + print(result) + diff --git a/Python/distribute-candies.py b/Python/distribute-candies.py new file mode 100644 index 000000000..827919c11 --- /dev/null +++ b/Python/distribute-candies.py @@ -0,0 +1,37 @@ +# Time: O(n) +# Space: O(n) + +# Given an integer array with even length, where different numbers +# in this array represent different kinds of candies. +# Each number means one candy of the corresponding kind. +# You need to distribute these candies equally in number to brother and sister. +# Return the maximum number of kinds of candies the sister could gain. +# +# Example 1: +# Input: candies = [1,1,2,2,3,3] +# Output: 3 +# Explanation: +# There are three different kinds of candies (1, 2 and 3), and two candies for each kind. +# Optimal distribution: The sister has candies [1,2,3] and the brother has candies [1,2,3], too. +# The sister has three different kinds of candies. +# +# Example 2: +# Input: candies = [1,1,2,3] +# Output: 2 +# Explanation: For example, the sister has candies [2,3] and the brother has candies [1,1]. +# The sister has two different kinds of candies, the brother has only one kind of candies. +# +# Note: +# The length of the given array is in range [2, 10,000], and will be even. +# The number in given array is in range [-100,000, 100,000]. + + +class Solution(object): + + def distributeCandies(self, candies): + """ + :type candies: List[int] + :rtype: int + """ + lookup = set(candies) + return min(len(lookup), len(candies)/2) diff --git a/Python/divide-two-integers.py b/Python/divide-two-integers.py index ff8bd757e..b5f7341e9 100644 --- a/Python/divide-two-integers.py +++ b/Python/divide-two-integers.py @@ -1,12 +1,17 @@ -# Time: O(logn) +from __future__ import print_function +# Time: O(logn) = O(1) # Space: O(1) -# -# Divide two integers without using multiplication, division and mod operator. # +# Divide two integers without using multiplication, division and mod operator. + class Solution: - # @return an integer def divide(self, dividend, divisor): + """ + :type dividend: int + :type divisor: int + :rtype: int + """ result, dvd, dvs = 0, abs(dividend), abs(divisor) while dvd >= dvs: inc = dvs @@ -20,9 +25,29 @@ def divide(self, dividend, divisor): return -result else: return result - + + def divide2(self, dividend, divisor): + """ + :type dividend: int + :type divisor: int + :rtype: int + """ + positive = (dividend < 0) is (divisor < 0) + dividend, divisor = abs(dividend), abs(divisor) + res = 0 + while dividend >= divisor: + temp, i = divisor, 1 + while dividend >= temp: + dividend -= temp + res += i + i <<= 1 + temp <<= 1 + if not positive: + res = -res + return min(max(-2147483648, res), 2147483647) + if __name__ == "__main__": - print Solution().divide(123, 12) - print Solution().divide(123, -12) - print Solution().divide(-123, 12) - print Solution().divide(-123, -12) \ No newline at end of file + print(Solution().divide(123, 12)) + print(Solution().divide(123, -12)) + print(Solution().divide(-123, 12)) + print(Solution().divide(-123, -12)) diff --git a/Python/domino-and-tromino-tiling.py b/Python/domino-and-tromino-tiling.py new file mode 100644 index 000000000..401104ef0 --- /dev/null +++ b/Python/domino-and-tromino-tiling.py @@ -0,0 +1,81 @@ +# Time: O(logn) +# Space: O(logn) + +# We have two types of tiles: a 2x1 domino shape, and an "L" tromino shape. +# These shapes may be rotated. +# +# XX <- domino +# +# XX <- "L" tromino +# X +# Given N, how many ways are there to tile a 2 x N board? Return your answer modulo 10^9 + 7. +# +# (In a tiling, every square must be covered by a tile. +# Two tilings are different if and only if there are two 4-directionally adjacent cells on the board +# such that exactly one of the tilings has both squares occupied by a tile.) +# +# Example: +# Input: 3 +# Output: 5 +# Explanation: +# The five different ways are listed below, different letters indicates different tiles: +# XYZ XXZ XYY XXY XYY +# XYZ YYZ XZZ XYY XXY +# +# Note: +# - N will be in range [1, 1000]. + +import itertools + + +class Solution(object): + def numTilings(self, N): + """ + :type N: int + :rtype: int + """ + M = int(1e9+7) + + def matrix_expo(A, K): + if K == 0: + return [[int(i==j) for j in xrange(len(A))] \ + for i in xrange(len(A))] + if K == 1: + return A + if K % 2: + return matrix_mult(matrix_expo(A, K-1), A) + B = matrix_expo(A, K//2) + return matrix_mult(B, B) + + def matrix_mult(A, B): + ZB = zip(*B) + return [[sum(a*b for a, b in itertools.izip(row, col)) % M \ + for col in ZB] for row in A] + + T = [[1, 0, 0, 1], # #(|) = #(|) + #(=) + [1, 0, 1, 0], # #(「) = #(|) + #(L) + [1, 1, 0, 0], # #(L) = #(|) + #(「) + [1, 1, 1, 0]] # #(=) = #(|) + #(「) + #(L) + + return matrix_expo(T, N)[0][0] # T^N * [1, 0, 0, 0] + + +# Time: O(n) +# Space: O(1) +class Solution2(object): + def numTilings(self, N): + """ + :type N: int + :rtype: int + """ + # Prove: + # dp[n] = dp[n-1](|) + dp[n-2](=) + 2*(dp[n-3](「」) + ... + d[0](「 = ... = 」)) + # = dp[n-1] + dp[n-2] + dp[n-3] + dp[n-3] + 2*(dp[n-4] + ... + d[0]) + # = dp[n-1] + dp[n-3] + (dp[n-2] + dp[n-3] + 2*(dp[n-4] + ... + d[0]) + # = dp[n-1] + dp[n-3] + dp[n-1] + # = 2*dp[n-1] + dp[n-3] + M = int(1e9+7) + dp = [1, 1, 2] + for i in xrange(3, N+1): + dp[i%3] = (2*dp[(i-1)%3]%M + dp[(i-3)%3])%M + return dp[N%3] diff --git a/Python/dota2-senate.py b/Python/dota2-senate.py new file mode 100644 index 000000000..38d55d6bc --- /dev/null +++ b/Python/dota2-senate.py @@ -0,0 +1,68 @@ +# Time: O(n) +# Space: O(n) + +# In the world of Dota2, there are two parties: the Radiant and the Dire. +# +# The Dota2 senate consists of senators coming from two parties. +# Now the senate wants to make a decision about a change in the Dota2 game. +# The voting for this change is a round-based procedure. +# In each round, each senator can exercise one of the two rights: +# +# Ban one senator's right: +# A senator can make another senator lose all his rights in this and all the following rounds. +# Announce the victory: +# If this senator found the senators who still have rights to vote are all from the same party, +# he can announce the victory and make the decision about the change in the game. +# +# Given a string representing each senator's party belonging. +# The character 'R' and 'D' represent the Radiant party and the Dire party respectively. +# Then if there are n senators, the size of the given string will be n. +# +# The round-based procedure starts from the first senator to the last senator in the given order. +# This procedure will last until the end of voting. All the senators +# who have lost their rights will be skipped during the procedure. +# +# Suppose every senator is smart enough and will play the best strategy for his own party, +# you need to predict which party will finally announce the victory and make the change in the Dota2 game. +# The output should be Radiant or Dire. +# +# Example 1: +# Input: "RD" +# Output: "Radiant" +# Explanation: The first senator comes from Radiant and he can just ban the next senator's right in the round 1. +# And the second senator can't exercise any rights any more since his right has been banned. +# And in the round 2, the first senator can just announce the victory since he is the only guy in the senate who can vote. +# Example 2: +# Input: "RDD" +# Output: "Dire" +# Explanation: +# The first senator comes from Radiant and he can just ban the next senator's right in the round 1. +# And the second senator can't exercise any rights anymore since his right has been banned. +# And the third senator comes from Dire and he can ban the first senator's right in the round 1. +# And in the round 2, the third senator can just announce the victory since he is the only guy in the senate who can vote. +# Note: +# The length of the given string will in the range [1, 10,000]. + +import collections + + +class Solution(object): + def predictPartyVictory(self, senate): + """ + :type senate: str + :rtype: str + """ + n = len(senate) + radiant, dire = collections.deque(), collections.deque() + for i, c in enumerate(senate): + if c == 'R': + radiant.append(i) + else: + dire.append(i) + while radiant and dire: + r_idx, d_idx = radiant.popleft(), dire.popleft() + if r_idx < d_idx: + radiant.append(r_idx+n) + else: + dire.append(d_idx+n) + return "Radiant" if len(radiant) > len(dire) else "Dire" diff --git a/Python/dungeon-game.py b/Python/dungeon-game.py index 9348828cb..b02b08ecc 100644 --- a/Python/dungeon-game.py +++ b/Python/dungeon-game.py @@ -1,33 +1,34 @@ +from __future__ import print_function # Time: O(m * n) # Space: O(m + n) # -# The demons had captured the princess (P) and imprisoned her +# The demons had captured the princess (P) and imprisoned her # in the bottom-right corner of a dungeon. T -# he dungeon consists of M x N rooms laid out in a 2D grid. -# Our valiant knight (K) was initially positioned in the top-left room +# he dungeon consists of M x N rooms laid out in a 2D grid. +# Our valiant knight (K) was initially positioned in the top-left room # and must fight his way through the dungeon to rescue the princess. -# -# The knight has an initial health point represented by a positive integer. +# +# The knight has an initial health point represented by a positive integer. # If at any point his health point drops to 0 or below, he dies immediately. -# -# Some of the rooms are guarded by demons, -# so the knight loses health (negative integers) upon entering these rooms; +# +# Some of the rooms are guarded by demons, +# so the knight loses health (negative integers) upon entering these rooms; # other rooms are either empty (0's) or contain magic orbs that increase the knight's health (positive integers). -# -# In order to reach the princess as quickly as possible, +# +# In order to reach the princess as quickly as possible, # the knight decides to move only rightward or downward in each step. -# -# -# Write a function to determine the knight's minimum initial health +# +# +# Write a function to determine the knight's minimum initial health # so that he is able to rescue the princess. -# -# For example, given the dungeon below, the initial health of +# +# For example, given the dungeon below, the initial health of # the knight must be at least 7 if he follows the optimal path RIGHT-> RIGHT -> DOWN -> DOWN. -# +# # Notes: -# +# # The knight's health has no upper bound. -# Any room can contain threats or power-ups, even the first room the knight enters +# Any room can contain threats or power-ups, even the first room the knight enters # and the bottom-right room where the princess is imprisoned. # @@ -37,16 +38,16 @@ class Solution: def calculateMinimumHP(self, dungeon): DP = [float("inf") for _ in dungeon[0]] DP[-1] = 1 - + for i in reversed(xrange(len(dungeon))): DP[-1] = max(DP[-1] - dungeon[i][-1], 1) for j in reversed(xrange(len(dungeon[i]) - 1)): min_HP_on_exit = min(DP[j], DP[j + 1]) DP[j] = max(min_HP_on_exit - dungeon[i][j], 1) - + return DP[0] -# Time: O(m * n logk), where k is the possible maximum sum of loses +# Time: O(m * n logk), where k is the possible maximum sum of loses # Space: O(m + n) class Solution2: # @param dungeon, a list of lists of integers @@ -57,9 +58,9 @@ def calculateMinimumHP(self, dungeon): for room in rooms: if room < 0: maximum_loses += abs(room) - + return self.binarySearch(dungeon, maximum_loses) - + def binarySearch(self, dungeon, maximum_loses): start, end = 1, maximum_loses + 1 result = 0 @@ -70,20 +71,20 @@ def binarySearch(self, dungeon, maximum_loses): else: start = mid + 1 return start - + def DP(self, dungeon, HP): remain_HP = [0 for _ in dungeon[0]] remain_HP[0] = HP + dungeon[0][0] for j in xrange(1, len(remain_HP)): - if remain_HP[j - 1] > 0: + if remain_HP[j - 1] > 0: remain_HP[j] = max(remain_HP[j - 1] + dungeon[0][j], 0) - + for i in xrange(1, len(dungeon)): if remain_HP[0] > 0: remain_HP[0] = max(remain_HP[0] + dungeon[i][0], 0) else: remain_HP[0] = 0 - + for j in xrange(1, len(remain_HP)): remain = 0 if remain_HP[j - 1] > 0: @@ -98,10 +99,10 @@ def DP(self, dungeon, HP): dungeon = [[ -2, -3, 3], \ [ -5, -10, 1], \ [ 10, 30, -5]] - print Solution().calculateMinimumHP(dungeon) - + print(Solution().calculateMinimumHP(dungeon)) + dungeon = [[ -200]] - print Solution().calculateMinimumHP(dungeon) - + print(Solution().calculateMinimumHP(dungeon)) + dungeon = [[0, -3]] - print Solution().calculateMinimumHP(dungeon) \ No newline at end of file + print(Solution().calculateMinimumHP(dungeon)) \ No newline at end of file diff --git a/Python/edit-distance.py b/Python/edit-distance.py index 8498a2f57..f1daec88e 100644 --- a/Python/edit-distance.py +++ b/Python/edit-distance.py @@ -1,11 +1,12 @@ +from __future__ import print_function # Time: O(n * m) # Space: O(n + m) # -# Given two words word1 and word2, find the minimum number of steps +# Given two words word1 and word2, find the minimum number of steps # required to convert word1 to word2. (each operation is counted as 1 step.) -# +# # You have the following 3 operations permitted on a word: -# +# # a) Insert a character # b) Delete a character # c) Replace a character @@ -16,9 +17,9 @@ class Solution: def minDistance(self, word1, word2): if len(word1) < len(word2): return self.minDistance(word2, word1) - + distance = [i for i in xrange(len(word2) + 1)] - + for i in xrange(1, len(word1) + 1): pre_distance_i_j = distance[0] distance[0] = i @@ -37,10 +38,10 @@ def minDistance(self, word1, word2): # Space: O(n * m) class Solution2: # @return an integer - def minDistance(self, word1, word2): + def minDistance(self, word1, word2): distance = [[i] for i in xrange(len(word1) + 1)] distance[0] = [j for j in xrange(len(word2) + 1)] - + for i in xrange(1, len(word1) + 1): for j in xrange(1, len(word2) + 1): insert = distance[i][j - 1] + 1 @@ -49,10 +50,10 @@ def minDistance(self, word1, word2): if word1[i - 1] != word2[j - 1]: replace += 1 distance[i].append(min(insert, delete, replace)) - + return distance[-1][-1] if __name__ == "__main__": - print Solution().minDistance("Rabbit", "Racket") - print Solution2().minDistance("Rabbit", "Rabket") - print Solution().minDistance("Rabbit", "Rabbitt") + print(Solution().minDistance("Rabbit", "Racket")) + print(Solution2().minDistance("Rabbit", "Rabket")) + print(Solution().minDistance("Rabbit", "Rabbitt")) diff --git a/Python/elimination-game.py b/Python/elimination-game.py new file mode 100644 index 000000000..0dd69f485 --- /dev/null +++ b/Python/elimination-game.py @@ -0,0 +1,39 @@ +# Time: O(logn) +# Space: O(1) + +# There is a list of sorted integers from 1 to n. Starting from left to right, +# remove the first number and every other number afterward until you reach the end of the list. +# +# Repeat the previous step again, but this time from right to left, +# remove the right most number and every other number from the remaining numbers. +# +# We keep repeating the steps again, alternating left to right and right to left, +# until a single number remains. +# +# Find the last number that remains starting with a list of length n. +# +# Example: +# +# Input: +# n = 9, +# 1 2 3 4 5 6 7 8 9 +# 2 4 6 8 +# 2 6 +# 6 +# +# Output: +# 6 + +class Solution(object): + def lastRemaining(self, n): + """ + :type n: int + :rtype: int + """ + start, step, direction = 1, 2, 1 + while n > 1: + start += direction * (step * (n/2) - step/2) + n /= 2 + step *= 2 + direction *= -1 + return start diff --git a/Python/employee-free-time.py b/Python/employee-free-time.py new file mode 100644 index 000000000..bbd3f6abe --- /dev/null +++ b/Python/employee-free-time.py @@ -0,0 +1,58 @@ +# Time: O(m * logn), m is the number of schedule, n is the number of employees, m >= n +# Space: O(n) + +# We are given a list schedule of employees, which represents the working time for each employee. +# Each employee has a list of non-overlapping Intervals, and these intervals are in sorted order. +# Return the list of finite intervals representing common, positive-length free time for all employees, also in sorted order. +# +# Example 1: +# Input: schedule = [[[1,2],[5,6]],[[1,3]],[[4,10]]] +# Output: [[3,4]] +# Explanation: +# There are a total of three employees, and all common +# free time intervals would be [-inf, 1], [3, 4], [10, inf]. +# We discard any intervals that contain inf as they aren't finite. +# +# Example 2: +# Input: schedule = [[[1,3],[6,7]],[[2,4]],[[2,5],[9,12]]] +# Output: [[5,6],[7,9]] +# (Even though we are representing Intervals in the form [x, y], +# the objects inside are Intervals, not lists or arrays. +# For example, schedule[0][0].start = 1, schedule[0][0].end = 2, +# and schedule[0][0][0] is not defined.) +# +# Also, we wouldn't include intervals like [5, 5] in our answer, as they have zero length. +# +# Note: +# - schedule and schedule[i] are lists with lengths in range [1, 50]. +# - 0 <= schedule[i].start < schedule[i].end <= 10^8. + +# Definition for an interval. + +import heapq + + +class Interval(object): + def __init__(self, s=0, e=0): + self.start = s + self.end = e + + +class Solution(object): + def employeeFreeTime(self, schedule): + """ + :type schedule: List[List[Interval]] + :rtype: List[Interval] + """ + result = [] + min_heap = [(emp[0].start, eid, 0) for eid, emp in enumerate(schedule)] + heapq.heapify(min_heap) + last_end = -1 + while min_heap: + t, eid, i = heapq.heappop(min_heap) + if 0 <= last_end < t: + result.append(Interval(last_end, t)) + last_end = max(last_end, schedule[eid][i].end) + if i+1 < len(schedule[eid]): + heapq.heappush(min_heap, (schedule[eid][i+1].start, eid, i+1)) + return result diff --git a/Python/employee-importance.py b/Python/employee-importance.py new file mode 100644 index 000000000..06cf9d5da --- /dev/null +++ b/Python/employee-importance.py @@ -0,0 +1,75 @@ +# Time: O(n) +# Space: O(h) + +# You are given a data structure of employee information, +# which includes the employee's unique id, his importance value and his direct subordinates' id. +# +# For example, employee 1 is the leader of employee 2, and employee 2 is the leader of employee 3. +# They have importance value 15, 10 and 5, respectively. +# Then employee 1 has a data structure like [1, 15, [2]], and employee 2 has [2, 10, [3]], +# and employee 3 has [3, 5, []]. Note that although employee 3 is also a subordinate of employee 1, +# the relationship is not direct. +# +# Now given the employee information of a company, +# and an employee id, you need to return the total importance value of this employee and all his subordinates. +# +# Example 1: +# Input: [[1, 5, [2, 3]], [2, 3, []], [3, 3, []]], 1 +# Output: 11 +# +# Explanation: +# Employee 1 has importance value 5, and he has two direct subordinates: +# employee 2 and employee 3. They both have importance value 3. +# So the total importance value of employee 1 is 5 + 3 + 3 = 11. +# +# Note: +# One employee has at most one direct leader and may have several subordinates. +# The maximum number of employees won't exceed 2000. + +import collections + + +""" +# Employee info +class Employee(object): + def __init__(self, id, importance, subordinates): + # It's the unique id of each node. + # unique id of this employee + self.id = id + # the importance value of this employee + self.importance = importance + # the id of direct subordinates + self.subordinates = subordinates +""" +class Solution(object): + def getImportance(self, employees, id): + """ + :type employees: Employee + :type id: int + :rtype: int + """ + if employees[id-1] is None: + return 0 + result = employees[id-1].importance + for id in employees[id-1].subordinates: + result += self.getImportance(employees, id) + return result + + +# Time: O(n) +# Space: O(w), w is the max number of nodes in the levels of the tree +class Solution2(object): + def getImportance(self, employees, id): + """ + :type employees: Employee + :type id: int + :rtype: int + """ + result, q = 0, collections.deque([id]) + while q: + curr = q.popleft() + employee = employees[curr-1] + result += employee.importance; + for id in employee.subordinates: + q.append(id) + return result diff --git a/Python/encode-and-decode-strings.py b/Python/encode-and-decode-strings.py new file mode 100644 index 000000000..b6e33bbda --- /dev/null +++ b/Python/encode-and-decode-strings.py @@ -0,0 +1,30 @@ +# Time: O(n) +# Space: O(1) + +class Codec: + + def encode(self, strs): + """Encodes a list of strings to a single string. + + :type strs: List[str] + :rtype: str + """ + encoded_str = "" + for s in strs: + encoded_str += "%0*x" % (8, len(s)) + s + return encoded_str + + + def decode(self, s): + """Decodes a single string to a list of strings. + + :type s: str + :rtype: List[str] + """ + i = 0 + strs = [] + while i < len(s): + l = int(s[i:i+8], 16) + strs.append(s[i+8:i+8+l]) + i += 8+l + return strs diff --git a/Python/encode-and-decode-tinyurl.py b/Python/encode-and-decode-tinyurl.py new file mode 100644 index 000000000..8217902e4 --- /dev/null +++ b/Python/encode-and-decode-tinyurl.py @@ -0,0 +1,80 @@ +# Time: O(1) +# Space: O(n) + +# TinyURL is a URL shortening service where you enter a URL +# such as https://leetcode.com/problems/design-tinyurl and +# it returns a short URL such as http://tinyurl.com/4e9iAk. +# +# Design the encode and decode methods for the TinyURL service. +# There is no restriction on how your encode/decode algorithm should work. +# You just need to ensure that a URL can be encoded to a tiny URL +# and the tiny URL can be decoded to the original URL. + +import random + + +class Codec: + def __init__(self): + self.__random_length = 6 + self.__tiny_url = "http://tinyurl.com/" + self.__alphabet = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" + self.__lookup = {} + + def encode(self, longUrl): + """Encodes a URL to a shortened URL. + + :type longUrl: str + :rtype: str + """ + def getRand(): + rand = [] + for _ in xrange(self.__random_length): + rand += self.__alphabet[random.randint(0, len(self.__alphabet)-1)] + return "".join(rand) + + key = getRand() + while key in self.__lookup: + key = getRand() + self.__lookup[key] = longUrl + return self.__tiny_url + key + + def decode(self, shortUrl): + """Decodes a shortened URL to its original URL. + + :type shortUrl: str + :rtype: str + """ + return self.__lookup[shortUrl[len(self.__tiny_url):]] + + +from hashlib import sha256 + + +class Codec2: + + def __init__(self): + self._cache = {} + self.url = 'http://tinyurl.com/' + + def encode(self, long_url): + """Encodes a URL to a shortened URL. + + :type long_url: str + :rtype: str + """ + key = sha256(long_url.encode()).hexdigest()[:6] + self._cache[key] = long_url + return self.url + key + + def decode(self, short_url): + """Decodes a shortened URL to its original URL. + + :type short_url: str + :rtype: str + """ + key = short_url.replace(self.url, '') + return self._cache[key] + +# Your Codec object will be instantiated and called as such: +# codec = Codec() +# codec.decode(codec.encode(url)) diff --git a/Python/encode-n-ary-tree-to-binary-tree.py b/Python/encode-n-ary-tree-to-binary-tree.py new file mode 100644 index 000000000..c0842bbee --- /dev/null +++ b/Python/encode-n-ary-tree-to-binary-tree.py @@ -0,0 +1,69 @@ +# Time: O(n) +# Space: O(h) + +# Definition for a Node. +class Node(object): + def __init__(self, val, children): + self.val = val + self.children = children + + +# Definition for a binary tree node. +class TreeNode(object): + def __init__(self, x): + self.val = x + self.left = None + self.right = None + + +class Codec: + + def encode(self, root): + """Encodes an n-ary tree to a binary tree. + + :type root: Node + :rtype: TreeNode + """ + def encodeHelper(root, parent, index): + if not root: + return None + node = TreeNode(root.val); + if index+1 < len(parent.children): + node.left = encodeHelper(parent.children[index+1], parent, index+1) + if root.children: + node.right = encodeHelper(root.children[0], root, 0); + return node + + if not root: + return None + node = TreeNode(root.val); + if root.children: + node.right = encodeHelper(root.children[0], root, 0) + return node + + def decode(self, data): + """Decodes your binary tree to an n-ary tree. + + :type data: TreeNode + :rtype: Node + """ + def decodeHelper(root, parent): + if not root: + return + children = [] + node = Node(root.val, children) + decodeHelper(root.right, node) + parent.children.append(node) + decodeHelper(root.left, parent) + + if not data: + return None + children = [] + node = Node(data.val, children) + decodeHelper(data.right, node) + return node + + +# Your Codec object will be instantiated and called as such: +# codec = Codec() +# codec.decode(codec.encode(root)) diff --git a/Python/encode-string-with-shortest-length.py b/Python/encode-string-with-shortest-length.py new file mode 100644 index 000000000..667663b29 --- /dev/null +++ b/Python/encode-string-with-shortest-length.py @@ -0,0 +1,28 @@ +# Time: O(n^3) on average +# Space: O(n^2) + +class Solution(object): + def encode(self, s): + """ + :type s: str + :rtype: str + """ + def encode_substr(dp, s, i, j): + temp = s[i:j+1] + pos = (temp + temp).find(temp, 1) # O(n) on average + if pos >= len(temp): + return temp + return str(len(temp)/pos) + '[' + dp[i][i + pos - 1] + ']' + + dp = [["" for _ in xrange(len(s))] for _ in xrange(len(s))] + for length in xrange(1, len(s)+1): + for i in xrange(len(s)+1-length): + j = i+length-1 + dp[i][j] = s[i:i+length] + for k in xrange(i, j): + if len(dp[i][k]) + len(dp[k+1][j]) < len(dp[i][j]): + dp[i][j] = dp[i][k] + dp[k+1][j] + encoded_string = encode_substr(dp, s, i, j) + if len(encoded_string) < len(dp[i][j]): + dp[i][j] = encoded_string + return dp[0][len(s) - 1] diff --git a/Python/equal-tree-partition.py b/Python/equal-tree-partition.py new file mode 100644 index 000000000..52476fa96 --- /dev/null +++ b/Python/equal-tree-partition.py @@ -0,0 +1,33 @@ +# Time: O(n) +# Space: O(n) + +# Definition for a binary tree node. +# class TreeNode(object): +# def __init__(self, x): +# self.val = x +# self.left = None +# self.right = None + +import collections + + +class Solution(object): + def checkEqualTree(self, root): + """ + :type root: TreeNode + :rtype: bool + """ + def getSumHelper(node, lookup): + if not node: + return 0 + total = node.val + \ + getSumHelper(node.left, lookup) + \ + getSumHelper(node.right, lookup) + lookup[total] += 1 + return total + + lookup = collections.defaultdict(int) + total = getSumHelper(root, lookup) + if total == 0: + return lookup[total] > 1 + return total%2 == 0 and (total/2) in lookup diff --git a/Python/erect-the-fence.py b/Python/erect-the-fence.py new file mode 100644 index 000000000..47dde4d13 --- /dev/null +++ b/Python/erect-the-fence.py @@ -0,0 +1,58 @@ +# Time: O(nlogn) +# Space: O(n) + +# There are some trees, where each tree is represented by +# (x,y) coordinate in a two-dimensional garden. +# Your job is to fence the entire garden using the minimum length of rope +# as it is expensive. The garden is well fenced only if all the trees are enclosed. +# Your task is to help find the coordinates of trees which are exactly located on the fence perimeter. +# +# Example 1: +# Input: [[1,1],[2,2],[2,0],[2,4],[3,3],[4,2]] +# Output: [[1,1],[2,0],[4,2],[3,3],[2,4]] +# +# Example 2: +# Input: [[1,2],[2,2],[4,2]] +# Output: [[1,2],[2,2],[4,2]] +# +# Even you only have trees in a line, you need to use rope to enclose them. +# Note: +# +# All trees should be enclosed together. +# You cannot cut the rope to enclose trees that will separate them in more than one group. +# All input integers will range from 0 to 100. +# The garden has at least one tree. +# All coordinates are distinct. +# Input points have NO order. No order required for output. + +# Definition for a point. +# class Point(object): +# def __init__(self, a=0, b=0): +# self.x = a +# self.y = b + +import itertools + + +# Monotone Chain Algorithm +class Solution(object): + def outerTrees(self, points): + """ + :type points: List[Point] + :rtype: List[Point] + """ + def orientation(p, q, r): + return (q.y - p.y) * (r.x - q.x) - \ + (q.x - p.x) * (r.y - q.y) + + hull = [] + points.sort(key=lambda p: (p.x, p.y)) + + for i in itertools.chain(xrange(len(points)), \ + reversed(xrange(len(points)))): + while len(hull) >= 2 and \ + orientation(hull[-2], hull[-1], points[i]) > 0: + hull.pop() + hull.append(points[i]) + + return list(set(hull)) diff --git a/Python/escape-the-ghosts.py b/Python/escape-the-ghosts.py new file mode 100644 index 000000000..0dff9431f --- /dev/null +++ b/Python/escape-the-ghosts.py @@ -0,0 +1,55 @@ +# Time: O(n) +# Space: O(1) + +# You are playing a simplified Pacman game. +# You start at the point (0, 0), and your destination is (target[0], target[1]). +# There are several ghosts on the map, the i-th ghost starts at (ghosts[i][0], ghosts[i][1]). +# +# Each turn, you and all ghosts simultaneously *may* move +# in one of 4 cardinal directions: north, east, west, or south, +# going from the previous point to a new point 1 unit of distance away. +# +# You escape if and only if you can reach the target before any ghost +# reaches you (for any given moves the ghosts may take.) +# If you reach any square (including the target) at the same time as a ghost, +# it doesn't count as an escape. +# +# Return True if and only if it is possible to escape. +# +# Example 1: +# Input: +# ghosts = [[1, 0], [0, 3]] +# target = [0, 1] +# Output: true +# Explanation: +# You can directly reach the destination (0, 1) at time 1, +# while the ghosts located at (1, 0) or (0, 3) have no way to catch up with you. +# +# Example 2: +# Input: +# ghosts = [[1, 0]] +# target = [2, 0] +# Output: false +# Explanation: +# You need to reach the destination (2, 0), but the ghost at (1, 0) lies between you and the destination. +# Example 3: +# Input: +# ghosts = [[2, 0]] +# target = [1, 0] +# Output: false +# Explanation: +# The ghost can reach the target at the same time as you. +# +# Note: +# - All points have coordinates with absolute value <= 10000. +# - The number of ghosts will not exceed 100. + +class Solution(object): + def escapeGhosts(self, ghosts, target): + """ + :type ghosts: List[List[int]] + :type target: List[int] + :rtype: bool + """ + total = abs(target[0])+abs(target[1]) + return all(total < abs(target[0]-i)+abs(target[1]-j) for i, j in ghosts) diff --git a/Python/evaluate-division.py b/Python/evaluate-division.py new file mode 100644 index 000000000..0a46fadd4 --- /dev/null +++ b/Python/evaluate-division.py @@ -0,0 +1,59 @@ +# Time: O(e + q * |V|!), |V| is the number of variables +# Space: O(e) + +# Equations are given in the format A / B = k, +# where A and B are variables represented as strings, +# and k is a real number (floating point number). +# Given some queries, return the answers. +# If the answer does not exist, return -1.0. +# +# Example: +# Given a / b = 2.0, b / c = 3.0. +# queries are: a / c = ?, b / a = ?, a / e = ?, a / a = ?, x / x = ? . +# return [6.0, 0.5, -1.0, 1.0, -1.0 ]. +# +# The input is: +# vector> euqations, vector& values, vector> query . +# +# where equations.size() == values.size(),the values are positive. +# this represents the equations.return vector. . +# The example above: equations = [ ["a", "b"], ["b", "c"] ]. +# values = [2.0, 3.0]. queries = [ ["a", "c"], ["b", "a"], ["a", "e"], ["a", "a"], ["x", "x"] ]. +# +# The input is always valid. You may assume that +# evaluating the queries will result in no division by zero and there is no contradiction. + +import collections + + +class Solution(object): + def calcEquation(self, equations, values, query): + """ + :type equations: List[List[str]] + :type values: List[float] + :type query: List[List[str]] + :rtype: List[float] + """ + def check(up, down, lookup, visited): + if up in lookup and down in lookup[up]: + return (True, lookup[up][down]) + for k, v in lookup[up].iteritems(): + if k not in visited: + visited.add(k) + tmp = check(k, down, lookup, visited) + if tmp[0]: + return (True, v * tmp[1]) + return (False, 0) + + lookup = collections.defaultdict(dict) + for i, e in enumerate(equations): + lookup[e[0]][e[1]] = values[i] + if values[i]: + lookup[e[1]][e[0]] = 1.0 / values[i] + + result = [] + for q in query: + visited = set() + tmp = check(q[0], q[1], lookup, visited) + result.append(tmp[1] if tmp[0] else -1) + return result diff --git a/Python/evaluate-reverse-polish-notation.py b/Python/evaluate-reverse-polish-notation.py index e437aaf09..50606d757 100644 --- a/Python/evaluate-reverse-polish-notation.py +++ b/Python/evaluate-reverse-polish-notation.py @@ -1,10 +1,11 @@ +from __future__ import print_function # Time: O(n) # Space: O(n) # # Evaluate the value of an arithmetic expression in Reverse Polish Notation. -# +# # Valid operators are +, -, *, /. Each operand may be an integer or another expression. -# +# # Some examples: # ["2", "1", "+", "3", "*"] -> ((2 + 1) * 3) -> 9 # ["4", "13", "5", "/", "+"] -> (4 + (13 / 5)) -> 6 @@ -23,8 +24,8 @@ def evalRPN(self, tokens): y, x = numerals.pop(), numerals.pop() numerals.append(int(operators[token](x * 1.0, y))) return numerals.pop() - + if __name__ == "__main__": - print Solution().evalRPN(["2", "1", "+", "3", "*"]) - print Solution().evalRPN(["4", "13", "5", "/", "+"]) - print Solution().evalRPN(["10","6","9","3","+","-11","*","/","*","17","+","5","+"]) \ No newline at end of file + print(Solution().evalRPN(["2", "1", "+", "3", "*"])) + print(Solution().evalRPN(["4", "13", "5", "/", "+"])) + print(Solution().evalRPN(["10","6","9","3","+","-11","*","/","*","17","+","5","+"])) \ No newline at end of file diff --git a/Python/exam-room.py b/Python/exam-room.py new file mode 100644 index 000000000..03fcba211 --- /dev/null +++ b/Python/exam-room.py @@ -0,0 +1,132 @@ +# Time: seat: O(logn) on average, +# leave: O(logn) +# Space: O(n) + +# In an exam room, there are N seats in a single row, +# numbered 0, 1, 2, ..., N-1. +# +# When a student enters the room, +# they must sit in the seat that maximizes the distance to the closest person. +# If there are multiple such seats, they sit in the seat with +# the lowest number. +# (Also, if no one is in the room, then the student sits at seat number 0.) +# +# Return a class ExamRoom(int N) that exposes two functions: +# ExamRoom.seat() returning an int representing what seat the student sat in, +# and ExamRoom.leave(int p) representing that the student in seat number p now +# leaves the room. +# It is guaranteed that any calls to ExamRoom.leave(p) have a student sitting +# in seat p. +# +# Example 1: +# +# Input: ["ExamRoom","seat","seat","seat","seat","leave","seat"], +# [[10],[],[],[],[],[4],[]] +# Output: [null,0,9,4,2,null,5] +# Explanation: +# ExamRoom(10) -> null +# seat() -> 0, no one is in the room, then the student sits at seat number 0. +# seat() -> 9, the student sits at the last seat number 9. +# seat() -> 4, the student sits at the last seat number 4. +# seat() -> 2, the student sits at the last seat number 2. +# leave(4) -> null +# seat() -> 5, the student sits at the last seat number 5. +# +# Note: +# - 1 <= N <= 10^9 +# - ExamRoom.seat() and ExamRoom.leave() will be called at most 10^4 times +# across all test cases. +# - Calls to ExamRoom.leave(p) are guaranteed to have a student currently +# sitting in seat number p. + + +import heapq + +LEN, POS, L, R = range(4) +LEFT, RIGHT = range(2) + + +class ExamRoom(object): + + def __init__(self, N): + """ + :type N: int + """ + self.__num = N + self.__max_heap = [(-self.__num, 0, -1, self.__num)] + self.__seats = {-1: [-1, self.__num], + self.__num: [-1, self.__num]} + + def seat(self): + """ + :rtype: int + """ + while self.__max_heap[0][L] not in self.__seats or \ + self.__max_heap[0][R] not in self.__seats or \ + self.__seats[self.__max_heap[0][L]][RIGHT] != \ + self.__max_heap[0][R] or \ + self.__seats[self.__max_heap[0][R]][LEFT] != \ + self.__max_heap[0][L]: + heapq.heappop(self.__max_heap) # lazy deletion + + curr = heapq.heappop(self.__max_heap) + if curr[L] == -1 and curr[R] == self.__num: + heapq.heappush(self.__max_heap, (-(curr[R]-1), + curr[R]-1, + curr[L]+1, curr[R])) + elif curr[L] == -1: + heapq.heappush(self.__max_heap, (-(curr[R]//2), + curr[R]//2, + curr[L]+1, curr[R])) + elif curr[R] == self.__num: + heapq.heappush(self.__max_heap, (-((curr[R]-1-curr[L])//2), + (curr[R]-1-curr[L])//2+curr[L], + curr[L], curr[R]-1)) + else: + heapq.heappush(self.__max_heap, (-((curr[POS]-curr[L])//2), + (curr[POS]-curr[L])//2+curr[L], + curr[L], curr[POS])) + heapq.heappush(self.__max_heap, (-((curr[R]-curr[POS])//2), + (curr[R]-curr[POS])//2+curr[POS], + curr[POS], curr[R])) + self.__seats[curr[POS]] = [curr[L], curr[R]] + self.__seats[curr[L]][RIGHT] = curr[POS] + self.__seats[curr[R]][LEFT] = curr[POS] + return curr[POS] + + def leave(self, p): + """ + :type p: int + :rtype: void + """ + neighbors = self.__seats[p] + self.__seats.pop(p) + if neighbors[LEFT] == -1 and neighbors[RIGHT] == self.__num: + heapq.heappush(self.__max_heap, + (-neighbors[RIGHT], + neighbors[LEFT]+1, + neighbors[LEFT], neighbors[RIGHT])) + elif neighbors[LEFT] == -1: + heapq.heappush(self.__max_heap, + (-neighbors[RIGHT], + neighbors[LEFT]+1, + neighbors[LEFT], neighbors[RIGHT])) + elif neighbors[RIGHT] == self.__num: + heapq.heappush(self.__max_heap, + (-(neighbors[RIGHT]-1-neighbors[LEFT]), + neighbors[RIGHT]-1, + neighbors[LEFT], neighbors[RIGHT])) + else: + heapq.heappush(self.__max_heap, + (-((neighbors[RIGHT]-neighbors[LEFT])//2), + (neighbors[RIGHT]-neighbors[LEFT])//2 + + neighbors[LEFT], + neighbors[LEFT], neighbors[RIGHT])) + self.__seats[neighbors[LEFT]][RIGHT] = neighbors[RIGHT] + self.__seats[neighbors[RIGHT]][LEFT] = neighbors[LEFT] + + +# Your ExamRoom object will be instantiated and called as such: +# obj = ExamRoom(N) +# param_1 = obj.seat() +# obj.leave(p) diff --git a/Python/excel-sheet-column-number.py b/Python/excel-sheet-column-number.py index 7ea985aa3..ceac0cb02 100644 --- a/Python/excel-sheet-column-number.py +++ b/Python/excel-sheet-column-number.py @@ -1,30 +1,33 @@ +from __future__ import print_function # Time: O(n) # Space: O(1) -# + # Related to question Excel Sheet Column Title -# +# # Given a column title as appear in an Excel sheet, return its corresponding column number. -# +# # For example: -# +# # A -> 1 # B -> 2 # C -> 3 # ... # Z -> 26 # AA -> 27 -# AB -> 28 -# +# AB -> 28 -class Solution: - # @param s, a string - # @return an integer +class Solution(object): def titleToNumber(self, s): + """ + :type s: str + :rtype: int + """ result = 0 for i in xrange(len(s)): result *= 26 result += ord(s[i]) - ord('A') + 1 return result + if __name__ == "__main__": - print Solution().titleToNumber("AAAB") \ No newline at end of file + print(Solution().titleToNumber("AAAB")) diff --git a/Python/excel-sheet-column-title.py b/Python/excel-sheet-column-title.py index fe8cb669b..0b833b155 100644 --- a/Python/excel-sheet-column-title.py +++ b/Python/excel-sheet-column-title.py @@ -1,30 +1,34 @@ +from __future__ import print_function # Time: O(logn) # Space: O(1) -# + # Given a positive integer, return its corresponding column title as appear in an Excel sheet. -# +# # For example: -# +# # 1 -> A # 2 -> B # 3 -> C # ... # 26 -> Z # 27 -> AA -# 28 -> AB -# +# 28 -> AB + +class Solution(object): + def convertToTitle(self, n): + """ + :type n: int + :rtype: str + """ + result, dvd = "", n -class Solution: - # @return a string - def convertToTitle(self, num): - result, dvd = "", num - while dvd: result += chr((dvd - 1) % 26 + ord('A')) dvd = (dvd - 1) / 26 - + return result[::-1] - + + if __name__ == "__main__": for i in xrange(1, 29): - print Solution().convertToTitle(i) \ No newline at end of file + print(Solution().convertToTitle(i)) diff --git a/Python/exclusive-time-of-functions.py b/Python/exclusive-time-of-functions.py new file mode 100644 index 000000000..fff0bcbf7 --- /dev/null +++ b/Python/exclusive-time-of-functions.py @@ -0,0 +1,62 @@ +# Time: O(n) +# Space: O(n) + +# Given the running logs of n functions that are executed +# in a nonpreemptive single threaded CPU, find the exclusive time of these functions. +# +# Each function has a unique id, start from 0 to n-1. +# A function may be called recursively or by another function. +# +# A log is a string has this format : function_id:start_or_end:timestamp. +# For example, "0:start:0" means function 0 starts from the very beginning of time 0. +# "0:end:0" means function 0 ends to the very end of time 0. +# +# Exclusive time of a function is defined as the time spent within this function, +# the time spent by calling other functions should not be considered as +# this function's exclusive time. +# You should return the exclusive time of each function sorted by their function id. +# +# Example 1: +# Input: +# n = 2 +# logs = +# ["0:start:0", +# "1:start:2", +# "1:end:5", +# "0:end:6"] +# Output:[3, 4] +# +# Explanation: +# Function 0 starts at time 0, then it executes 2 units of time and reaches the end of time 1. +# Now function 0 calls function 1, function 1 starts at time 2, executes 4 units of time and end at time 5. +# Function 0 is running again at time 6, and also end at the time 6, thus executes 1 unit of time. +# So function 0 totally execute 2 + 1 = 3 units of time, and function 1 totally execute 4 units of time. +# +# Note: +# Input logs will be sorted by timestamp, NOT log id. +# Your output should be sorted by function id, +# which means the 0th element of your output corresponds to the exclusive time of function 0. +# Two functions won't start or end at the same time. +# Functions could be called recursively, and will always end. +# 1 <= n <= 100 + +class Solution(object): + def exclusiveTime(self, n, logs): + """ + :type n: int + :type logs: List[str] + :rtype: List[int] + """ + result = [0] * n + stk, prev = [], 0 + for log in logs: + tokens = log.split(":") + if tokens[1] == "start": + if stk: + result[stk[-1]] += int(tokens[2]) - prev + stk.append(int(tokens[0])) + prev = int(tokens[2]) + else: + result[stk.pop()] += int(tokens[2]) - prev + 1 + prev = int(tokens[2]) + 1 + return result diff --git a/Python/expression-add-operators.py b/Python/expression-add-operators.py new file mode 100644 index 000000000..f26c4c7ad --- /dev/null +++ b/Python/expression-add-operators.py @@ -0,0 +1,67 @@ +# Time: O(4^n) +# Space: O(n) +# +# Given a string that contains only digits 0-9 +# and a target value, return all possibilities +# to add operators +, -, or * between the digits +# so they evaluate to the target value. +# +# Examples: +# "123", 6 -> ["1+2+3", "1*2*3"] +# "232", 8 -> ["2*3+2", "2+3*2"] +# "00", 0 -> ["0+0", "0-0", "0*0"] +# "3456237490", 9191 -> [] +# + +class Solution(object): + def addOperators(self, num, target): + """ + :type num: str + :type target: int + :rtype: List[str] + """ + result, expr = [], [] + val, i = 0, 0 + val_str = "" + while i < len(num): + val = val * 10 + ord(num[i]) - ord('0') + val_str += num[i] + # Avoid "00...". + if str(val) != val_str: + break + expr.append(val_str) + self.addOperatorsDFS(num, target, i + 1, 0, val, expr, result) + expr.pop() + i += 1 + return result + + def addOperatorsDFS(self, num, target, pos, operand1, operand2, expr, result): + if pos == len(num) and operand1 + operand2 == target: + result.append("".join(expr)) + else: + val, i = 0, pos + val_str = "" + while i < len(num): + val = val * 10 + ord(num[i]) - ord('0') + val_str += num[i] + # Avoid "00...". + if str(val) != val_str: + break + + # Case '+': + expr.append("+" + val_str) + self.addOperatorsDFS(num, target, i + 1, operand1 + operand2, val, expr, result) + expr.pop() + + # Case '-': + expr.append("-" + val_str) + self.addOperatorsDFS(num, target, i + 1, operand1 + operand2, -val, expr, result) + expr.pop() + + # Case '*': + expr.append("*" + val_str) + self.addOperatorsDFS(num, target, i + 1, operand1, operand2 * val, expr, result) + expr.pop() + + i += 1 + diff --git a/Python/expressive-words.py b/Python/expressive-words.py new file mode 100644 index 000000000..bb0c21d76 --- /dev/null +++ b/Python/expressive-words.py @@ -0,0 +1,60 @@ +# Time: O(n + s), n is the sum of all word lengths, s is the length of S +# Space: O(l + s), l is the max word length + +# Sometimes people repeat letters to represent extra feeling, +# such as "hello" -> "heeellooo", "hi" -> "hiiii". +# Here, we have groups, of adjacent letters that are all the same character, +# and adjacent characters to the group are different. +# A group is extended if that group is length 3 or more, so "e" and "o" +# would be extended in the first example, and "i" would be extended in the second example. +# As another example, the groups of "abbcccaaaa" would be "a", "bb", "ccc", and "aaaa"; +# and "ccc" and "aaaa" are the extended groups of that string. +# +# For some given string S, a query word is stretchy +# if it can be made to be equal to S by extending some groups. +# Formally, we are allowed to repeatedly choose a group (as defined above) of characters c, +# and add some number of the same character c to it so that the length of the group is 3 or more. +# Note that we cannot extend a group of size one like "h" to a group of size two like "hh" - +# all extensions must leave the group extended - ie., at least 3 characters long. +# +# Given a list of query words, return the number of words that are stretchy. +# +# Example: +# Input: +# S = "heeellooo" +# words = ["hello", "hi", "helo"] +# Output: 1 +# Explanation: +# We can extend "e" and "o" in the word "hello" to get "heeellooo". +# We can't extend "helo" to get "heeellooo" because the group "ll" is not extended. +# +# Notes: +# - 0 <= len(S) <= 100. +# - 0 <= len(words) <= 100. +# - 0 <= len(words[i]) <= 100. +# - S and all words in words consist only of lowercase letters + +import itertools + + +class Solution(object): + def expressiveWords(self, S, words): + """ + :type S: str + :type words: List[str] + :rtype: int + """ + # Run length encoding + def RLE(S): + return itertools.izip(*[(k, len(list(grp))) + for k, grp in itertools.groupby(S)]) + + R, count = RLE(S) + result = 0 + for word in words: + R2, count2 = RLE(word) + if R2 != R: + continue + result += all(c1 >= max(c2, 3) or c1 == c2 + for c1, c2 in itertools.izip(count, count2)) + return result diff --git a/Python/factor-combinations.py b/Python/factor-combinations.py new file mode 100644 index 000000000..9e2c1a687 --- /dev/null +++ b/Python/factor-combinations.py @@ -0,0 +1,23 @@ +# Time: O(nlogn) +# Space: O(logn) + +class Solution: + # @param {integer} n + # @return {integer[][]} + def getFactors(self, n): + result = [] + factors = [] + self.getResult(n, result, factors) + return result + + def getResult(self, n, result, factors): + i = 2 if not factors else factors[-1] + while i <= n / i: + if n % i == 0: + factors.append(i); + factors.append(n / i); + result.append(list(factors)); + factors.pop(); + self.getResult(n / i, result, factors); + factors.pop() + i += 1 diff --git a/Python/factorial-trailing-zeroes.py b/Python/factorial-trailing-zeroes.py index d649a86ff..f071fd710 100644 --- a/Python/factorial-trailing-zeroes.py +++ b/Python/factorial-trailing-zeroes.py @@ -1,8 +1,9 @@ -# Time: O(logn) +from __future__ import print_function +# Time: O(logn) = O(1) # Space: O(1) # # Given an integer n, return the number of trailing zeroes in n!. -# +# # Note: Your solution should be in logarithmic time complexity. # @@ -16,4 +17,4 @@ def trailingZeroes(self, n): return result if __name__ == "__main__": - print Solution().trailingZeroes(100) \ No newline at end of file + print(Solution().trailingZeroes(100)) diff --git a/Python/falling-squares.py b/Python/falling-squares.py new file mode 100644 index 000000000..9d76976cb --- /dev/null +++ b/Python/falling-squares.py @@ -0,0 +1,248 @@ +# Time: O(nlogn) +# Space: O(n) + +# On an infinite number line (x-axis), we drop given squares in the order they are given. +# +# The i-th square dropped (positions[i] = (left, side_length)) is a square +# with the left-most point being positions[i][0] and sidelength positions[i][1]. +# +# The square is dropped with the bottom edge parallel to the number line, +# and from a higher height than all currently landed squares. +# We wait for each square to stick before dropping the next. +# +# The squares are infinitely sticky on their bottom edge, and will remain fixed +# to any positive length surface they touch (either the number line or another square). +# Squares dropped adjacent to each other will not stick together prematurely. +# +# Return a list ans of heights. Each height ans[i] represents the current highest height +# of any square we have dropped, after dropping squares represented by positions[0], positions[1], ..., positions[i]. +# +# Example 1: +# Input: [[1, 2], [2, 3], [6, 1]] +# Output: [2, 5, 5] +# Explanation: +# +# After the first drop of +# positions[0] = [1, 2]: +# _aa +# _aa +# ------- +# The maximum height of any square is 2. +# +# After the second drop of +# positions[1] = [2, 3]: +# __aaa +# __aaa +# __aaa +# _aa__ +# _aa__ +# -------------- +# The maximum height of any square is 5. +# The larger square stays on top of the smaller square despite where its center +# of gravity is, because squares are infinitely sticky on their bottom edge. +# +# After the third drop of +# positions[1] = [6, 1]: +# __aaa +# __aaa +# __aaa +# _aa +# _aa___a +# -------------- +# The maximum height of any square is still 5. +# +# Thus, we return an answer of +# [2, 5, 5] +# . +# +# Example 2: +# Input: [[100, 100], [200, 100]] +# Output: [100, 100] +# Explanation: Adjacent squares don't get stuck prematurely - only their bottom edge can stick to surfaces. +# Note: +# +# 1 <= positions.length <= 1000. +# 1 <= positions[0] <= 10^8. +# 1 <= positions[1] <= 10^6. + +# Time: O(nlogn) ~ O(n^2) +# Space: O(n) +import bisect + + +class Solution(object): + def fallingSquares(self, positions): + result = [] + pos = [-1] + heights = [0] + maxH = 0 + for left, side in positions: + l = bisect.bisect_right(pos, left) + r = bisect.bisect_left(pos, left+side) + high = max(heights[l-1:r] or [0]) + side + pos[l:r] = [left, left+side] # Time: O(n) + heights[l:r] = [high, heights[r-1]] # Time: O(n) + maxH = max(maxH, high) + result.append(maxH) + return result + + +class SegmentTree(object): + def __init__(self, N, update_fn, query_fn): + self.N = N + self.H = 1 + while (1 << self.H) < N: + self.H += 1 + + self.update_fn = update_fn + self.query_fn = query_fn + self.tree = [0] * (2 * N) + self.lazy = [0] * N + + def __apply(self, x, val): + self.tree[x] = self.update_fn(self.tree[x], val) + if x < self.N: + self.lazy[x] = self.update_fn(self.lazy[x], val) + + def __pull(self, x): + while x > 1: + x /= 2 + self.tree[x] = self.query_fn(self.tree[x*2], self.tree[x*2 + 1]) + self.tree[x] = self.update_fn(self.tree[x], self.lazy[x]) + + def __push(self, x): + for h in xrange(self.H, 0, -1): + y = x >> h + if self.lazy[y]: + self.__apply(y*2, self.lazy[y]) + self.__apply(y*2 + 1, self.lazy[y]) + self.lazy[y] = 0 + + def update(self, L, R, h): + L += self.N + R += self.N + L0, R0 = L, R + while L <= R: + if L & 1: + self.__apply(L, h) + L += 1 + if R & 1 == 0: + self.__apply(R, h) + R -= 1 + L /= 2; R /= 2 + self.__pull(L0) + self.__pull(R0) + + def query(self, L, R): + L += self.N + R += self.N + self.__push(L); self.__push(R) + result = 0 + while L <= R: + if L & 1: + result = self.query_fn(result, self.tree[L]) + L += 1 + if R & 1 == 0: + result = self.query_fn(result, self.tree[R]) + R -= 1 + L /= 2; R /= 2 + return result + + +# Time: O(nlogn) +# Space: O(n) +# Segment Tree solution. +class Solution2(object): + def fallingSquares(self, positions): + index = set() + for left, size in positions: + index.add(left); + index.add(left+size-1) + index = sorted(list(index)) + tree = SegmentTree(len(index), max, max) + max_height = 0 + result = [] + for left, size in positions: + L, R = bisect.bisect_left(index, left), bisect.bisect_left(index, left+size-1) + h = tree.query(L, R) + size + tree.update(L, R, h) + max_height = max(max_height, h) + result.append(max_height) + return result + + +# Time: O(n * sqrt(n)) +# Space: O(n) +class Solution3(object): + def fallingSquares(self, positions): + def query(heights, left, right, B, blocks, blocks_read): + result = 0 + while left % B and left <= right: + result = max(result, heights[left], blocks[left//B]) + left += 1 + while right % B != B-1 and left <= right: + result = max(result, heights[right], blocks[right//B]) + right -= 1 + while left <= right: + result = max(result, blocks[left//B], blocks_read[left//B]) + left += B + return result + + def update(heights, left, right, B, blocks, blocks_read, h): + while left % B and left <= right: + heights[left] = max(heights[left], h) + blocks_read[left//B] = max(blocks_read[left//B], h) + left += 1 + while right % B != B-1 and left <= right: + heights[right] = max(heights[right], h) + blocks_read[right//B] = max(blocks_read[right//B], h) + right -= 1 + while left <= right: + blocks[left//B] = max(blocks[left//B], h) + left += B + + index = set() + for left, size in positions: + index.add(left); + index.add(left+size-1) + index = sorted(list(index)) + W = len(index) + B = int(W**.5) + heights = [0] * W + blocks = [0] * (B+2) + blocks_read = [0] * (B+2) + + max_height = 0 + result = [] + for left, size in positions: + L, R = bisect.bisect_left(index, left), bisect.bisect_left(index, left+size-1) + h = query(heights, L, R, B, blocks, blocks_read) + size + update(heights, L, R, B, blocks, blocks_read, h) + max_height = max(max_height, h) + result.append(max_height) + return result + + +# Time: O(n^2) +# Space: O(n) +class Solution4(object): + def fallingSquares(self, positions): + """ + :type positions: List[List[int]] + :rtype: List[int] + """ + heights = [0] * len(positions) + for i in xrange(len(positions)): + left_i, size_i = positions[i] + right_i = left_i + size_i + heights[i] += size_i + for j in xrange(i+1, len(positions)): + left_j, size_j = positions[j] + right_j = left_j + size_j + if left_j < right_i and left_i < right_j: # intersect + heights[j] = max(heights[j], heights[i]) + + result = [] + for height in heights: + result.append(max(result[-1], height) if result else height) + return result diff --git a/Python/find-all-anagrams-in-a-string.py b/Python/find-all-anagrams-in-a-string.py new file mode 100644 index 000000000..4122bc0bc --- /dev/null +++ b/Python/find-all-anagrams-in-a-string.py @@ -0,0 +1,59 @@ +# Time: O(n) +# Space: O(1) + +# Given a string s and a non-empty string p, find all the start indices +# of p's anagrams in s. +# +# Strings consists of lowercase English letters only and the length of +# both strings s and p will not be larger than 20,100. +# +# The order of output does not matter. +# +# Example 1: +# +# Input: +# s: "cbaebabacd" p: "abc" +# +# Output: +# [0, 6] +# +# Explanation: +# The substring with start index = 0 is "cba", which is an anagram of "abc". +# The substring with start index = 6 is "bac", which is an anagram of "abc". +# Example 2: +# +# Input: +# s: "abab" p: "ab" +# +# Output: +# [0, 1, 2] +# +# Explanation: +# The substring with start index = 0 is "ab", which is an anagram of "ab". +# The substring with start index = 1 is "ba", which is an anagram of "ab". +# The substring with start index = 2 is "ab", which is an anagram of "ab". + +class Solution(object): + def findAnagrams(self, s, p): + """ + :type s: str + :type p: str + :rtype: List[int] + """ + result = [] + + cnts = [0] * 26 + for c in p: + cnts[ord(c) - ord('a')] += 1 + + left, right = 0, 0 + while right < len(s): + cnts[ord(s[right]) - ord('a')] -= 1 + while left <= right and cnts[ord(s[right]) - ord('a')] < 0: + cnts[ord(s[left]) - ord('a')] += 1 + left += 1 + if right - left + 1 == len(p): + result.append(left) + right += 1 + + return result diff --git a/Python/find-all-duplicates-in-an-array.py b/Python/find-all-duplicates-in-an-array.py new file mode 100644 index 000000000..47a601d3a --- /dev/null +++ b/Python/find-all-duplicates-in-an-array.py @@ -0,0 +1,64 @@ +# Time: O(n) +# Space: O(1) + +# Given an array of integers, 1 <= a[i] <= n (n = size of array), +# some elements appear twice and others appear once. +# Find all the elements that appear twice in this array. +# Could you do it without extra space and in O(n) runtime? +# +# Example: +# Input +# +# [4,3,2,7,8,2,3,1] +# +# Output +# +# [2,3] + +class Solution(object): + def findDuplicates(self, nums): + """ + :type nums: List[int] + :rtype: List[int] + """ + result = [] + for i in nums: + if nums[abs(i)-1] < 0: + result.append(abs(i)) + else: + nums[abs(i)-1] *= -1 + return result + + +# Time: O(n) +# Space: O(1) +class Solution2(object): + def findDuplicates(self, nums): + """ + :type nums: List[int] + :rtype: List[int] + """ + result = [] + i = 0 + while i < len(nums): + if nums[i] != nums[nums[i]-1]: + nums[nums[i]-1], nums[i] = nums[i], nums[nums[i]-1] + else: + i += 1 + + for i in xrange(len(nums)): + if i != nums[i]-1: + result.append(nums[i]) + return result + + +# Time: O(n) +# Space: O(n), this doesn't satisfy the question +from collections import Counter +class Solution3(object): + def findDuplicates(self, nums): + """ + :type nums: List[int] + :rtype: List[int] + """ + return [elem for elem, count in Counter(nums).items() if count == 2] diff --git a/Python/find-all-numbers-disappeared-in-an-array.py b/Python/find-all-numbers-disappeared-in-an-array.py new file mode 100644 index 000000000..6461968c6 --- /dev/null +++ b/Python/find-all-numbers-disappeared-in-an-array.py @@ -0,0 +1,58 @@ +from __future__ import print_function +# Time: O(n) +# Space: O(1) + +# Given an array of integers where 1 <= a[i] <= n (n = size of array), +# some elements appear twice and others appear once. +# +# Find all the elements of [1, n] inclusive that do not appear in this array. +# +# Could you do it without extra space and in O(n) runtime? +# You may assume the returned list does not count as extra space. +# +# Example: +# +# Input: +# [4,3,2,7,8,2,3,1] +# +# Output: +# [5,6] + + +class Solution(object): + def findDisappearedNumbers(self, nums): + """ + :type nums: List[int] + :rtype: List[int] + """ + for i in xrange(len(nums)): + if nums[abs(nums[i]) - 1] > 0: + nums[abs(nums[i]) - 1] *= -1 + + result = [] + for i in xrange(len(nums)): + if nums[i] > 0: + result.append(i+1) + else: + nums[i] *= -1 + return result + + def findDisappearedNumbers2(self, nums): + """ + :type nums: List[int] + :rtype: List[int] + """ + return list(set(range(1, len(nums) + 1)) - set(nums)) + + def findDisappearedNumbers3(self, nums): + for i in range(len(nums)): + index = abs(nums[i]) - 1 + nums[index] = - abs(nums[index]) + + return [i + 1 for i in range(len(nums)) if nums[i] > 0] + + +if __name__ == '__main__': + s = Solution() + r = s.findDisappearedNumbers([4, 3, 2, 7, 8, 2, 3, 1]) + print(r) diff --git a/Python/find-anagram-mappings.py b/Python/find-anagram-mappings.py new file mode 100644 index 000000000..69a2bf25d --- /dev/null +++ b/Python/find-anagram-mappings.py @@ -0,0 +1,41 @@ +# Time: O(n) +# Space: O(n) + +# Given two lists Aand B, and B is an anagram of A. +# B is an anagram of A means B is made by randomizing the order of the elements in A. +# +# We want to find an index mapping P, from A to B. A mapping P[i] = j +# means the ith element in A appears in B at index j. +# +# These lists A and B may contain duplicates. If there are multiple answers, output any of them. +# +# For example, given +# A = [12, 28, 46, 32, 50] +# B = [50, 12, 32, 46, 28] +# +# We should return +# [1, 4, 3, 2, 0] +# as P[0] = 1 because the 0th element of A appears at B[1], and P[1] = 4 +# because the 1st element of A appears at B[4], and so on. +# +# Note: +# - A, B have equal lengths in range [1, 100]. +# - A[i], B[i] are integers in range [0, 10^5]. + +import collections + + +class Solution(object): + def anagramMappings(self, A, B): + """ + :type A: List[int] + :type B: List[int] + :rtype: List[int] + """ + lookup = collections.defaultdict(collections.deque) + for i, n in enumerate(B): + lookup[n].append(i) + result = [] + for n in A: + result.append(lookup[n].popleft()) + return result diff --git a/Python/find-and-replace-in-string.py b/Python/find-and-replace-in-string.py new file mode 100644 index 000000000..7f085d4ed --- /dev/null +++ b/Python/find-and-replace-in-string.py @@ -0,0 +1,76 @@ +# Time: O(n + m), m is the number of targets +# Space: O(n) + +# To some string S, we will perform some replacement operations that +# replace groups of letters with new ones (not necessarily the same size). +# +# Each replacement operation has 3 parameters: a starting index i, +# a source word x and a target word y. +# The rule is that if x starts at position i in the original string S, +# then we will replace that occurrence of x with y. If not, we do nothing. +# +# For example, if we have S = "abcd" and we have some replacement operation +# i = 2, x = "cd", y = "ffff", then because "cd" starts at position 2 +# in the original string S, we will replace it with "ffff". +# +# Using another example on S = "abcd", if we have both the replacement +# operation +# i = 0, x = "ab", y = "eee", as well as another replacement operation i = 2, +# x = "ec", y = "ffff", this second operation does nothing because in the +# original string S[2] = 'c', which doesn't match x[0] = 'e'. +# +# All these operations occur simultaneously. +# It's guaranteed that there won't be any overlap in replacement: +# for example, S = "abc", indexes = [0, 1], sources = ["ab","bc"] +# is not a valid test case. +# +# Example 1: +# +# Input: S = "abcd", indexes = [0,2], sources = ["a","cd"], +# targets = ["eee","ffff"] +# Output: "eeebffff" +# Explanation: "a" starts at index 0 in S, so it's replaced by "eee". +# "cd" starts at index 2 in S, so it's replaced by "ffff". +# Example 2: +# +# Input: S = "abcd", indexes = [0,2], sources = ["ab","ec"], +# targets = ["eee","ffff"] +# Output: "eeecd" +# Explanation: "ab" starts at index 0 in S, so it's replaced by "eee". +# "ec" doesn't starts at index 2 in the original S, so we do nothing. +# Notes: +# 1. 0 <= indexes.length = sources.length = targets.length <= 100 +# 2. 0 < indexes[i] < S.length <= 1000 +# 3. All characters in given inputs are lowercase letters. + +try: + xrange # Python 2 +except NameError: + xrange = range # Python 3 + + +class Solution(object): + def findReplaceString(self, S, indexes, sources, targets): + """ + :type S: str + :type indexes: List[int] + :type sources: List[str] + :type targets: List[str] + :rtype: str + """ + S = list(S) + bucket = [None] * len(S) + for i in xrange(len(indexes)): + if all(indexes[i]+k < len(S) and + S[indexes[i]+k] == sources[i][k] + for k in xrange(len(sources[i]))): + bucket[indexes[i]] = (len(sources[i]), list(targets[i])) + result = [] + last = 0 + for i in xrange(len(S)): + if bucket[i]: + result.extend(bucket[i][1]) + last = i + bucket[i][0] + elif i >= last: + result.append(S[i]) + return "".join(result) diff --git a/Python/find-bottom-left-tree-value.py b/Python/find-bottom-left-tree-value.py new file mode 100644 index 000000000..696f0bae9 --- /dev/null +++ b/Python/find-bottom-left-tree-value.py @@ -0,0 +1,68 @@ +# Time: O(n) +# Space: O(h) + +# Given a binary tree, find the leftmost value in the last row of the tree. +# +# Example 1: +# Input: +# +# 2 +# / \ +# 1 3 +# +# Output: +# 1 +# Example 2: +# Input: +# +# 1 +# / \ +# 2 3 +# / / \ +# 4 5 6 +# / +# 7 +# +# Output: +# 7 +# Note: You may assume the tree (i.e., the given root node) is not NULL. + +# Definition for a binary tree node. +# class TreeNode(object): +# def __init__(self, x): +# self.val = x +# self.left = None +# self.right = None + +class Solution(object): + def findBottomLeftValue(self, root): + """ + :type root: TreeNode + :rtype: int + """ + def findBottomLeftValueHelper(root, curr_depth, max_depth, bottom_left_value): + if not root: + return max_depth, bottom_left_value + if not root.left and not root.right and curr_depth+1 > max_depth: + return curr_depth+1, root.val + max_depth, bottom_left_value = findBottomLeftValueHelper(root.left, curr_depth+1, max_depth, bottom_left_value) + max_depth, bottom_left_value = findBottomLeftValueHelper(root.right, curr_depth+1, max_depth, bottom_left_value) + return max_depth, bottom_left_value + + result, max_depth = 0, 0 + return findBottomLeftValueHelper(root, 0, max_depth, result)[1] + + +# Time: O(n) +# Space: O(n) +class Solution2(object): + def findBottomLeftValue(self, root): + """ + :type root: TreeNode + :rtype: int + """ + last_node, queue = None, [root] + while queue: + last_node = queue.pop(0) + queue.extend([n for n in [last_node.right, last_node.left] if n]) + return last_node.value diff --git a/Python/find-duplicate-file-in-system.py b/Python/find-duplicate-file-in-system.py new file mode 100644 index 000000000..d0b2fb0dd --- /dev/null +++ b/Python/find-duplicate-file-in-system.py @@ -0,0 +1,69 @@ +# Time: O(n * l), l is the average length of file content +# Space: O(n * l) + +# Given a list of directory info including directory path, +# and all the files with contents in this directory, +# you need to find out all the groups of duplicate files +# in the file system in terms of their paths. +# +# A group of duplicate files consists of at least two files that have exactly the same content. +# +# A single directory info string in the input list has the following format: +# +# "root/d1/d2/.../dm f1.txt(f1_content) f2.txt(f2_content) ... fn.txt(fn_content)" +# +# It means there are n files (f1.txt, f2.txt ... fn.txt +# with content f1_content, f2_content ... fn_content, respectively) in +# directory root/d1/d2/.../dm. Note that n >= 1 and m >= 0. If m = 0, it means the directory is just the root directory. +# +# The output is a list of group of duplicate file paths. For each group, +# it contains all the file paths of the files that have the same content. +# A file path is a string that has the following format: +# +# "directory_path/file_name.txt" +# +# Example 1: +# Input: +# ["root/a 1.txt(abcd) 2.txt(efgh)", "root/c 3.txt(abcd)", "root/c/d 4.txt(efgh)", "root 4.txt(efgh)"] +# Output: +# [["root/a/2.txt","root/c/d/4.txt","root/4.txt"],["root/a/1.txt","root/c/3.txt"]] +# Note: +# No order is required for the final output. +# You may assume the directory name, file name and file content only has letters +# and digits, and the length of file content is in the range of [1,50]. +# +# The number of files given is in the range of [1,20000]. +# You may assume no files or directories share the same name in the same directory. +# You may assume each given directory info represents a unique directory. +# Directory path and file info are separated by a single blank space. +# +# Follow-up beyond contest: +# 1. Imagine you are given a real file system, how will you search files? DFS or BFS? +# 2. If the file content is very large (GB level), how will you modify your solution? +# 3. If you can only read the file by 1kb each time, how will you modify your solution? +# 4. What is the time complexity of your modified solution? +# What is the most time-consuming part and memory consuming part of it? How to optimize? +# 5. How to make sure the duplicated files you find are not false positive? + +import collections + + +class Solution(object): + def findDuplicate(self, paths): + """ + :type paths: List[str] + :rtype: List[List[str]] + """ + files = collections.defaultdict(list) + for path in paths: + s = path.split(" ") + for i in xrange(1,len(s)): + file_name = s[0] + "/" + s[i][0:s[i].find("(")] + file_content = s[i][s[i].find("(")+1:s[i].find(")")] + files[file_content].append(file_name) + + result = [] + for file_content, file_names in files.iteritems(): + if len(file_names)>1: + result.append(file_names) + return result diff --git a/Python/find-duplicate-subtrees.py b/Python/find-duplicate-subtrees.py new file mode 100644 index 000000000..8c75ca059 --- /dev/null +++ b/Python/find-duplicate-subtrees.py @@ -0,0 +1,78 @@ +# Time: O(n) +# Space: O(n) + +# Given a binary tree, return all duplicate subtrees. +# For each kind of duplicate subtrees, you only need to return the root node of any one of them. +# +# Two trees are duplicate if they have the same structure with same node values. +# +# Example 1: +# 1 +# / \ +# 2 3 +# / / \ +# 4 2 4 +# / +# 4 +# The following are two duplicate subtrees: +# 2 +# / +# 4 +# and +# 4 +# Therefore, you need to return above trees' root in the form of a list. + +# Definition for a binary tree node. +# class TreeNode(object): +# def __init__(self, x): +# self.val = x +# self.left = None +# self.right = None + +import collections + + +class Solution(object): + def findDuplicateSubtrees(self, root): + """ + :type root: TreeNode + :rtype: List[TreeNode] + """ + def getid(root, lookup, trees): + if root: + node_id = lookup[root.val, \ + getid(root.left, lookup, trees), \ + getid(root.right, lookup, trees)] + trees[node_id].append(root) + return node_id + trees = collections.defaultdict(list) + lookup = collections.defaultdict() + lookup.default_factory = lookup.__len__ + getid(root, lookup, trees) + return [roots[0] for roots in trees.values() if len(roots) > 1] + + +# Time: O(n * h) +# Space: O(n * h) +class Solution2(object): + def findDuplicateSubtrees(self, root): + """ + :type root: TreeNode + :rtype: List[TreeNode] + """ + def postOrderTraversal(node, lookup, result): + if not node: + return "" + s = "(" + postOrderTraversal(node.left, lookup, result) + \ + str(node.val) + \ + postOrderTraversal(node.right, lookup, result) + \ + ")" + if lookup[s] == 1: + result.append(node) + lookup[s] += 1 + return s + + lookup = collections.defaultdict(int) + result = [] + postOrderTraversal(root, lookup, result) + return result diff --git a/Python/find-eventual-safe-states.py b/Python/find-eventual-safe-states.py new file mode 100644 index 000000000..48455c8a4 --- /dev/null +++ b/Python/find-eventual-safe-states.py @@ -0,0 +1,51 @@ +# Time: O(|V| + |E|) +# Space: O(|V|) + +# In a directed graph, we start at some node and every turn, walk along a directed edge of the graph. +# If we reach a node that is terminal (that is, it has no outgoing directed edges), we stop. +# +# Now, say our starting node is eventually safe if and only if we must eventually walk to a terminal node. +# More specifically, there exists a natural number K so that for any choice of where to walk, +# we must have stopped at a terminal node in less than K steps. +# +# Which nodes are eventually safe? Return them as an array in sorted order. +# +# The directed graph has N nodes with labels 0, 1, ..., N-1, where N is the length of graph. +# The graph is given in the following form: graph[i] is a list of labels j +# such that (i, j) is a directed edge of the graph. +# +# Example: +# Input: graph = [[1,2],[2,3],[5],[0],[5],[],[]] +# Output: [2,4,5,6] +# +# Note: +# - graph will have length at most 10000. +# - The number of edges in the graph will not exceed 32000. +# - Each graph[i] will be a sorted list of different integers, chosen within the range [0, graph.length - 1]. + +import collections + + +class Solution(object): + def eventualSafeNodes(self, graph): + """ + :type graph: List[List[int]] + :rtype: List[int] + """ + WHITE, GRAY, BLACK = 0, 1, 2 + + def dfs(graph, node, lookup): + if lookup[node] != WHITE: + return lookup[node] == BLACK + lookup[node] = GRAY + for child in graph[node]: + if lookup[child] == BLACK: + continue + if lookup[child] == GRAY or \ + not dfs(graph, child, lookup): + return False + lookup[node] = BLACK + return True + + lookup = collections.defaultdict(int) + return filter(lambda node: dfs(graph, node, lookup), xrange(len(graph))) diff --git a/Python/find-k-closest-elements.py b/Python/find-k-closest-elements.py new file mode 100644 index 000000000..232df2879 --- /dev/null +++ b/Python/find-k-closest-elements.py @@ -0,0 +1,39 @@ +# Time: O(logn + k) +# Space: O(1) + +# Given a sorted array, two integers k and x, find the k closest elements to x in the array. +# The result should also be sorted in ascending order. +# If there is a tie, the smaller elements are always preferred. +# +# Example 1: +# Input: [1,2,3,4,5], k=4, x=3 +# Output: [1,2,3,4] +# Example 2: +# Input: [1,2,3,4,5], k=4, x=-1 +# Output: [1,2,3,4] +# Note: +# The value k is positive and will always be smaller than the length of the sorted array. +# Length of the given array is positive and will not exceed 10^4 +# Absolute value of elements in the array and x will not exceed 10^4 + +import bisect + + +class Solution(object): + def findClosestElements(self, arr, k, x): + """ + :type arr: List[int] + :type k: int + :type x: int + :rtype: List[int] + """ + i = bisect.bisect_left(arr, x) + left, right = i-1, i + while k: + if right >= len(arr) or \ + (left >= 0 and abs(arr[left]-x) <= abs(arr[right]-x)): + left -= 1 + else: + right += 1 + k -= 1 + return arr[left+1:right] diff --git a/Python/find-k-pairs-with-smallest-sums.py b/Python/find-k-pairs-with-smallest-sums.py new file mode 100644 index 000000000..6a1750aee --- /dev/null +++ b/Python/find-k-pairs-with-smallest-sums.py @@ -0,0 +1,63 @@ +# Time: O(k * log(min(n, m, k))), where n is the size of num1, and m is the size of num2. +# Space: O(min(n, m, k)) + +# You are given two integer arrays nums1 +# and nums2 sorted in ascending order and an integer k. +# +# Define a pair (u,v) which consists of one element +# from the first array and one element from the second array. +# +# Find the k pairs (u1,v1),(u2,v2) ...(uk,vk) with the smallest sums. +# +# Example 1: +# Given nums1 = [1,7,11], nums2 = [2,4,6], k = 3 +# +# Return: [1,2],[1,4],[1,6] +# +# The first 3 pairs are returned from the sequence: +# [1,2],[1,4],[1,6],[7,2],[7,4],[11,2],[7,6],[11,4],[11,6] +# Example 2: +# Given nums1 = [1,1,2], nums2 = [1,2,3], k = 2 +# +# Return: [1,1],[1,1] +# +# The first 2 pairs are returned from the sequence: +# [1,1],[1,1],[1,2],[2,1],[1,2],[2,2],[1,3],[1,3],[2,3] +# Example 3: +# Given nums1 = [1,2], nums2 = [3], k = 3 +# +# Return: [1,3],[2,3] +# +# All possible pairs are returned from the sequence: +# [1,3],[2,3] + +from heapq import heappush, heappop + +class Solution(object): + def kSmallestPairs(self, nums1, nums2, k): + """ + :type nums1: List[int] + :type nums2: List[int] + :type k: int + :rtype: List[List[int]] + """ + pairs = [] + if len(nums1) > len(nums2): + tmp = self.kSmallestPairs(nums2, nums1, k) + for pair in tmp: + pairs.append([pair[1], pair[0]]) + return pairs + + min_heap = [] + def push(i, j): + if i < len(nums1) and j < len(nums2): + heappush(min_heap, [nums1[i] + nums2[j], i, j]) + + push(0, 0) + while min_heap and len(pairs) < k: + _, i, j = heappop(min_heap) + pairs.append([nums1[i], nums2[j]]) + push(i, j + 1) + if j == 0: + push(i + 1, 0) # at most queue min(n, m) space + return pairs diff --git a/Python/find-k-th-smallest-pair-distance.py b/Python/find-k-th-smallest-pair-distance.py new file mode 100644 index 000000000..970fe106a --- /dev/null +++ b/Python/find-k-th-smallest-pair-distance.py @@ -0,0 +1,49 @@ +# Time: O(nlogn + nlogw), n = len(nums), w = max(nums)-min(nums) +# Space: O(1) + +# Given an integer array, return the k-th smallest distance among all the pairs. +# The distance of a pair (A, B) is defined as the absolute difference between A and B. +# +# Example 1: +# Input: +# nums = [1,3,1] +# k = 1 +# Output: 0 +# Explanation: +# Here are all the pairs: +# (1,3) -> 2 +# (1,1) -> 0 +# (3,1) -> 2 +# Then the 1st smallest distance pair is (1,1), and its distance is 0. +# +# Note: +# 2 <= len(nums) <= 10000. +# 0 <= nums[i] < 1000000. +# 1 <= k <= len(nums) * (len(nums) - 1) / 2. + +# Binary search with sliding window solution +class Solution(object): + def smallestDistancePair(self, nums, k): + """ + :type nums: List[int] + :type k: int + :rtype: int + """ + # Sliding window solution + def possible(guess, nums, k): + count, left = 0, 0 + for right, num in enumerate(nums): + while num-nums[left] > guess: + left += 1 + count += right-left + return count >= k + + nums.sort() + left, right = 0, nums[-1]-nums[0]+1 + while left < right: + mid = left + (right-left)/2 + if possible(mid, nums, k): + right = mid + else: + left = mid+1 + return left diff --git a/Python/find-largest-value-in-each-tree-row.py b/Python/find-largest-value-in-each-tree-row.py new file mode 100644 index 000000000..0343ca2a7 --- /dev/null +++ b/Python/find-largest-value-in-each-tree-row.py @@ -0,0 +1,59 @@ +# Time: O(n) +# Space: O(h) + +# You need to find the largest value in each row of a binary tree. +# +# Example: +# Input: +# +# 1 +# / \ +# 3 2 +# / \ \ +# 5 3 9 +# +# Output: [1, 3, 9] + +# Definition for a binary tree node. +# class TreeNode(object): +# def __init__(self, x): +# self.val = x +# self.left = None +# self.right = None + +class Solution(object): + def largestValues(self, root): + """ + :type root: TreeNode + :rtype: List[int] + """ + def largestValuesHelper(root, depth, result): + if not root: + return + if depth == len(result): + result.append(root.val) + else: + result[depth] = max(result[depth], root.val) + largestValuesHelper(root.left, depth+1, result) + largestValuesHelper(root.right, depth+1, result) + + result = [] + largestValuesHelper(root, 0, result) + return result + + +# Time: O(n) +# Space: O(n) +class Solution2(object): + def largestValues(self, root): + """ + :type root: TreeNode + :rtype: List[int] + """ + result = [] + curr = [root] + while any(curr): + result.append(max(node.val for node in curr)) + curr = [child for node in curr for child in (node.left, node.right) if child] + return result + diff --git a/Python/find-leaves-of-binary-tree.py b/Python/find-leaves-of-binary-tree.py new file mode 100644 index 000000000..0560ed748 --- /dev/null +++ b/Python/find-leaves-of-binary-tree.py @@ -0,0 +1,29 @@ +# Time: O(n) +# Space: O(h) + +# Definition for a binary tree node. +# class TreeNode(object): +# def __init__(self, x): +# self.val = x +# self.left = None +# self.right = None + +class Solution(object): + def findLeaves(self, root): + """ + :type root: TreeNode + :rtype: List[List[int]] + """ + def findLeavesHelper(node, result): + if not node: + return -1 + level = 1 + max(findLeavesHelper(node.left, result), \ + findLeavesHelper(node.right, result)) + if len(result) < level + 1: + result.append([]) + result[level].append(node.val) + return level + + result = [] + findLeavesHelper(root, result) + return result diff --git a/Python/find-median-from-data-stream.py b/Python/find-median-from-data-stream.py new file mode 100644 index 000000000..df79d898e --- /dev/null +++ b/Python/find-median-from-data-stream.py @@ -0,0 +1,64 @@ +# Time: O(nlogn) for total n addNums, O(logn) per addNum, O(1) per findMedian. +# Space: O(n), total space + +# Median is the middle value in an ordered integer list. +# If the size of the list is even, there is no middle value. +# So the median is the mean of the two middle value. +# +# Examples: +# [2,3,4] , the median is 3 +# +# [2,3], the median is (2 + 3) / 2 = 2.5 +# +# Design a data structure that supports the following two operations: +# +# void addNum(int num) - Add a integer number from the data stream to the data structure. +# double findMedian() - Return the median of all elements so far. +# For example: +# +# add(1) +# add(2) +# findMedian() -> 1.5 +# add(3) +# findMedian() -> 2 + +# Heap solution. +from heapq import heappush, heappop + +class MedianFinder: + def __init__(self): + """ + Initialize your data structure here. + """ + self.__max_heap = [] + self.__min_heap = [] + + def addNum(self, num): + """ + Adds a num into the data structure. + :type num: int + :rtype: void + """ + # Balance smaller half and larger half. + if not self.__max_heap or num > -self.__max_heap[0]: + heappush(self.__min_heap, num) + if len(self.__min_heap) > len(self.__max_heap) + 1: + heappush(self.__max_heap, -heappop(self.__min_heap)) + else: + heappush(self.__max_heap, -num) + if len(self.__max_heap) > len(self.__min_heap): + heappush(self.__min_heap, -heappop(self.__max_heap)) + + def findMedian(self): + """ + Returns the median of current data stream + :rtype: float + """ + return (-self.__max_heap[0] + self.__min_heap[0]) / 2.0 \ + if len(self.__min_heap) == len(self.__max_heap) \ + else self.__min_heap[0] + +# Your MedianFinder object will be instantiated and called as such: +# mf = MedianFinder() +# mf.addNum(1) +# mf.findMedian() diff --git a/Python/find-minimum-in-rotated-sorted-array-ii.py b/Python/find-minimum-in-rotated-sorted-array-ii.py index 0340791a2..7ca773143 100644 --- a/Python/find-minimum-in-rotated-sorted-array-ii.py +++ b/Python/find-minimum-in-rotated-sorted-array-ii.py @@ -1,59 +1,60 @@ +from __future__ import print_function # Time: O(logn) ~ O(n) # Space: O(1) # # Follow up for "Find Minimum in Rotated Sorted Array": # What if duplicates are allowed? -# +# # Would this affect the run-time complexity? How and why? # Suppose a sorted array is rotated at some pivot unknown to you beforehand. -# +# # (i.e., 0 1 2 4 5 6 7 might become 4 5 6 7 0 1 2). -# +# # Find the minimum element. -# +# # The array may contain duplicates. # +class Solution(object): + def findMin(self, nums): + """ + :type nums: List[int] + :rtype: int + """ + left, right = 0, len(nums) - 1 + while left < right: + mid = left + (right - left) / 2 -class Solution: - # @param num, a list of integer - # @return an integer - def findMin(self, num): - low, high = 0, len(num) - - while low < high - 1 and num[low] >= num[high - 1]: - mid = low + (high - low) / 2 - - if num[mid] > num[low]: - low = mid + 1 - elif num[mid] < num[low]: - if mid == high - 1: - return num[mid] - else: - high = mid + 1 + if nums[mid] == nums[right]: + right -= 1 + elif nums[mid] < nums[right]: + right = mid else: - low += 1 - - return num[low] - -class Solution2: - # @param num, a list of integer - # @return an integer - def findMin(self, num): - low, high = 0, len(num) - 1 - - while low < high and num[low] >= num[high]: - mid = low + (high - low) / 2 - - if num[mid] > num[low]: - low = mid + 1 - elif num[mid] < num[low]: - high = mid + left = mid + 1 + + return nums[left] + + +class Solution2(object): + def findMin(self, nums): + """ + :type nums: List[int] + :rtype: int + """ + left, right = 0, len(nums) - 1 + while left < right and nums[left] >= nums[right]: + mid = left + (right - left) / 2 + + if nums[mid] == nums[left]: + left += 1 + elif nums[mid] < nums[left]: + right = mid else: - low += 1 + left = mid + 1 + + return nums[left] - return num[low] if __name__ == "__main__": - print Solution().findMin([3, 1, 1, 2, 2, 3]) - print Solution2().findMin([2, 2, 2, 3, 3, 1]) \ No newline at end of file + print(Solution().findMin([3, 1, 1, 2, 2, 3])) + print(Solution2().findMin([2, 2, 2, 3, 3, 1])) diff --git a/Python/find-minimum-in-rotated-sorted-array.py b/Python/find-minimum-in-rotated-sorted-array.py index b54c78042..e850ea1cf 100644 --- a/Python/find-minimum-in-rotated-sorted-array.py +++ b/Python/find-minimum-in-rotated-sorted-array.py @@ -1,3 +1,4 @@ +from __future__ import print_function # Time: O(logn) # Space: O(1) # @@ -10,46 +11,47 @@ # You may assume no duplicate exists in the array. # -class Solution: - # @param num, a list of integer - # @return an integer - def findMin(self, num): - low, high = 0, len(num) - - while low < high - 1 and num[low] >= num[high - 1]: - mid = low + (high - low) / 2 - - if num[mid] > num[low]: - low = mid + 1 - elif num[mid] < num[low]: - if mid == high - 1: - return num[mid] - else: - high = mid + 1 +class Solution(object): + def findMin(self, nums): + """ + :type nums: List[int] + :rtype: int + """ + left, right = 0, len(nums) + target = nums[-1] + + while left < right: + mid = left + (right - left) / 2 + + if nums[mid] <= target: + right = mid else: - return num[mid] - - return num[low] - -class Solution2: - # @param num, a list of integer - # @return an integer - def findMin(self, num): - low, high = 0, len(num) - 1 - - while low < high and num[low] >= num[high]: - mid = low + (high - low) / 2 - - if num[mid] >= num[low]: - low = mid + 1 + left = mid + 1 + + return nums[left] + + +class Solution2(object): + def findMin(self, nums): + """ + :type nums: List[int] + :rtype: int + """ + left, right = 0, len(nums) - 1 + while left < right and nums[left] >= nums[right]: + mid = left + (right - left) / 2 + + if nums[mid] < nums[left]: + right = mid else: - high = mid + left = mid + 1 + + return nums[left] - return num[low] if __name__ == "__main__": - print Solution().findMin([1]) - print Solution().findMin([1, 2]) - print Solution().findMin([2, 1]) - print Solution().findMin([3, 1, 2]) - print Solution().findMin([2, 3, 1]) \ No newline at end of file + print(Solution().findMin([1])) + print(Solution().findMin([1, 2])) + print(Solution().findMin([2, 1])) + print(Solution().findMin([3, 1, 2])) + print(Solution().findMin([2, 3, 1])) diff --git a/Python/find-mode-in-binary-search-tree.py b/Python/find-mode-in-binary-search-tree.py new file mode 100644 index 000000000..facb55471 --- /dev/null +++ b/Python/find-mode-in-binary-search-tree.py @@ -0,0 +1,61 @@ +# Time: O(n) +# Space: O(1) + +# Given a binary search tree (BST) with duplicates, +# find all the mode(s) (the most frequently occurred element) in the given BST. +# +# Assume a BST is defined as follows: +# +# The left subtree of a node contains only nodes with keys less than or equal to the node's key. +# The right subtree of a node contains only nodes with keys greater than or equal to the node's key. +# Both the left and right subtrees must also be binary search trees. +# For example: +# Given BST [1,null,2,2], +# 1 +# \ +# 2 +# / +# 2 +# return [2]. +# +# Note: If a tree has more than one mode, you can return them in any order. +# +# Follow up: Could you do that without using any extra space? +# (Assume that the implicit stack space incurred due to recursion does not count). + +# Definition for a binary tree node. +# class TreeNode(object): +# def __init__(self, x): +# self.val = x +# self.left = None +# self.right = None + +class Solution(object): + def findMode(self, root): + """ + :type root: TreeNode + :rtype: List[int] + """ + def inorder(root, prev, cnt, max_cnt, result): + if not root: + return prev, cnt, max_cnt + + prev, cnt, max_cnt = inorder(root.left, prev, cnt, max_cnt, result) + if prev: + if root.val == prev.val: + cnt += 1 + else: + cnt = 1 + if cnt > max_cnt: + max_cnt = cnt; + del result[:] + result.append(root.val) + elif cnt == max_cnt: + result.append(root.val) + return inorder(root.right, root, cnt, max_cnt, result) + + if not root: + return [] + result = [] + inorder(root, None, 1, 0, result); + return result diff --git a/Python/find-peak-element.py b/Python/find-peak-element.py index 84eb19221..4d6569ac5 100644 --- a/Python/find-peak-element.py +++ b/Python/find-peak-element.py @@ -1,39 +1,42 @@ +from __future__ import print_function # Time: O(logn) # Space: O(1) -# + # A peak element is an element that is greater than its neighbors. -# -# Given an input array where num[i] != num[i+1], find a peak element and return its index. -# -# The array may contain multiple peaks, in that case return the index to any one of the peaks is fine. -# +# +# Given an input array where num[i] != num[i+1], +# find a peak element and return its index. +# +# The array may contain multiple peaks, in that case +# return the index to any one of the peaks is fine. +# # You may imagine that num[-1] = num[n] = -infinite. -# -# For example, in array [1, 2, 3, 1], 3 is a peak element and your function should return the index number 2. +# +# For example, in array [1, 2, 3, 1], 3 is a peak element +# and your function should return the index number 2. # # Note: # Your solution should be in logarithmic complexity. -# -class Solution: - # @param num, a list of integer - # @return an integer - def findPeakElement(self, num): - low, high = 0, len(num) - 1 - - while low < high: - mid = low + (high - low) / 2 - if (mid == 0 or num[mid - 1] <= num[mid]) and \ - (mid == len(num) - 1 or num[mid + 1] <= num[mid]): - return mid - elif mid > 0 and num[mid - 1] > num[mid]: - high = mid - 1 + +class Solution(object): + def findPeakElement(self, nums): + """ + :type nums: List[int] + :rtype: int + """ + left, right = 0, len(nums) - 1 + + while left < right: + mid = left + (right - left) / 2 + if nums[mid] > nums[mid + 1]: + right = mid else: - low = mid + 1 - mid = low + (high - low) / 2 - - return low + left = mid + 1 + + return left + if __name__ == "__main__": # print Solution().findPeakElement([1,2,1]) - print Solution().findPeakElement([1,2,3, 1]) \ No newline at end of file + print(Solution().findPeakElement([1,2,3,1])) diff --git a/Python/find-permutation.py b/Python/find-permutation.py new file mode 100644 index 000000000..4a9710abf --- /dev/null +++ b/Python/find-permutation.py @@ -0,0 +1,14 @@ +# Time: O(n) +# Space: O(1) + +class Solution(object): + def findPermutation(self, s): + """ + :type s: str + :rtype: List[int] + """ + result = [] + for i in xrange(len(s)+1): + if i == len(s) or s[i] == 'I': + result += range(i+1, len(result), -1) + return result diff --git a/Python/find-pivot-index.py b/Python/find-pivot-index.py new file mode 100644 index 000000000..37880950f --- /dev/null +++ b/Python/find-pivot-index.py @@ -0,0 +1,44 @@ +# Time: O(n) +# Space: O(1) + +# Given an array of integers nums, write a method that returns the "pivot" index of this array. +# +# We define the pivot index as the index where the sum of the numbers to +# the left of the index is equal to the sum of the numbers to the right of the index. +# +# If no such index exists, we should return -1. If there are multiple pivot indexes, +# you should return the left-most pivot index. +# +# Example 1: +# Input: +# nums = [1, 7, 3, 6, 5, 6] +# Output: 3 +# Explanation: +# The sum of the numbers to the left of index 3 (nums[3] = 6) is equal to the sum of numbers to the right of index 3. +# Also, 3 is the first index where this occurs. +# +# Example 2: +# Input: +# nums = [1, 2, 3] +# Output: -1 +# Explanation: +# There is no index that satisfies the conditions in the problem statement. +# +# Note: +# - The length of nums will be in the range [0, 10000]. +# - Each element nums[i] will be an integer in the range [-1000, 1000]. + +class Solution(object): + def pivotIndex(self, nums): + """ + :type nums: List[int] + :rtype: int + """ + total = sum(nums) + left_sum = 0 + for i, num in enumerate(nums): + if left_sum == (total-left_sum-num): + return i + left_sum += num + return -1 + diff --git a/Python/find-right-interval.py b/Python/find-right-interval.py new file mode 100644 index 000000000..4e00fb8d0 --- /dev/null +++ b/Python/find-right-interval.py @@ -0,0 +1,58 @@ +# Time: O(nlogn) +# Space: O(n) + +# Given a set of intervals, for each of the interval i, +# check if there exists an interval j whose start point is bigger than or +# equal to the end point of the interval i, which can be called that j is on the "right" of i. +# +# For any interval i, you need to store the minimum interval j's index, +# which means that the interval j has the minimum start point to +# build the "right" relationship for interval i. If the interval j doesn't exist, +# store -1 for the interval i. Finally, you need output the stored value of each interval as an array. +# +# Note: +# You may assume the interval's end point is always bigger than its start point. +# You may assume none of these intervals have the same start point. +# Example 1: +# Input: [ [1,2] ] +# +# Output: [-1] +# +# Explanation: There is only one interval in the collection, so it outputs -1. +# Example 2: +# Input: [ [3,4], [2,3], [1,2] ] +# +# Output: [-1, 0, 1] +# +# Explanation: There is no satisfied "right" interval for [3,4]. +# For [2,3], the interval [3,4] has minimum-"right" start point; +# For [1,2], the interval [2,3] has minimum-"right" start point. +# Example 3: +# Input: [ [1,4], [2,3], [3,4] ] +# +# Output: [-1, 2, -1] +# +# Explanation: There is no satisfied "right" interval for [1,4] and [3,4]. +# For [2,3], the interval [3,4] has minimum-"right" start point. +# +# Definition for an interval. +# class Interval(object): +# def __init__(self, s=0, e=0): +# self.start = s +# self.end = e + +import bisect + + +class Solution(object): + def findRightInterval(self, intervals): + """ + :type intervals: List[Interval] + :rtype: List[int] + """ + sorted_intervals = sorted((interval.start, i) for i, interval in enumerate(intervals)) + result = [] + for interval in intervals: + idx = bisect.bisect_left(sorted_intervals, (interval.end,)) + result.append(sorted_intervals[idx][1] if idx < len(sorted_intervals) else -1) + return result diff --git a/Python/find-smallest-letter-greater-than-target.py b/Python/find-smallest-letter-greater-than-target.py new file mode 100644 index 000000000..2e20c9ad1 --- /dev/null +++ b/Python/find-smallest-letter-greater-than-target.py @@ -0,0 +1,55 @@ +# Time: O(logn) +# Space: O(1) + +# Given a list of sorted characters letters containing only lowercase letters, +# and given a target letter target, find the smallest element in the list that is larger than the given target. +# +# Letters also wrap around. For example, if the target is target = 'z' and letters = ['a', 'b'], the answer is 'a'. +# +# Examples: +# Input: +# letters = ["c", "f", "j"] +# target = "a" +# Output: "c" +# +# Input: +# letters = ["c", "f", "j"] +# target = "c" +# Output: "f" +# +# Input: +# letters = ["c", "f", "j"] +# target = "d" +# Output: "f" +# +# Input: +# letters = ["c", "f", "j"] +# target = "g" +# Output: "j" +# +# Input: +# letters = ["c", "f", "j"] +# target = "j" +# Output: "c" +# +# Input: +# letters = ["c", "f", "j"] +# target = "k" +# Output: "c" +# Note: +# - letters has a length in range [2, 10000]. +# - letters consists of lowercase letters, and contains at least 2 unique letters. +# - target is a lowercase letter. + +import bisect + + +class Solution(object): + def nextGreatestLetter(self, letters, target): + """ + :type letters: List[str] + :type target: str + :rtype: str + """ + i = bisect.bisect_right(letters, target) + return letters[0] if i == len(letters) else letters[i] diff --git a/Python/find-the-celebrity.py b/Python/find-the-celebrity.py new file mode 100644 index 000000000..103354840 --- /dev/null +++ b/Python/find-the-celebrity.py @@ -0,0 +1,29 @@ +# Time: O(n) +# Space: O(1) +# +# The knows API is already defined for you. +# @param a, person a +# @param b, person b +# @return a boolean, whether a knows b +# def knows(a, b): +# + +class Solution(object): + def findCelebrity(self, n): + """ + :type n: int + :rtype: int + """ + candidate = 0 + # Find the candidate. + for i in xrange(1, n): + if knows(candidate, i): # noqa + candidate = i # All candidates < i are not celebrity candidates. + # Verify the candidate. + for i in xrange(n): + candidate_knows_i = knows(candidate, i) # noqa + i_knows_candidate = knows(i, candidate) # noqa + if i != candidate and (candidate_knows_i or + not i_knows_candidate): + return -1 + return candidate diff --git a/Python/find-the-closest-palindrome.py b/Python/find-the-closest-palindrome.py new file mode 100644 index 000000000..f4b9bae21 --- /dev/null +++ b/Python/find-the-closest-palindrome.py @@ -0,0 +1,27 @@ +# Time: O(l) +# Space: O(l) + +# Given an integer n, find the closest integer (not including itself), which is a palindrome. +# +# The 'closest' is defined as absolute difference minimized between two integers. +# +# Example 1: +# Input: "123" +# Output: "121" +# Note: +# The input n is a positive integer represented by string, whose length will not exceed 18. +# If there is a tie, return the smaller one as answer. + +class Solution(object): + def nearestPalindromic(self, n): + """ + :type n: str + :rtype: str + """ + l = len(n) + candidates = set((str(10**l + 1), str(10**(l - 1) - 1))) + prefix = int(n[:(l + 1)/2]) + for i in map(str, (prefix-1, prefix, prefix+1)): + candidates.add(i + [i, i[:-1]][l%2][::-1]) + candidates.discard(n) + return min(candidates, key=lambda x: (abs(int(x) - int(n)), int(x))) diff --git a/Python/find-the-derangement-of-an-array.py b/Python/find-the-derangement-of-an-array.py new file mode 100644 index 000000000..67ac500b2 --- /dev/null +++ b/Python/find-the-derangement-of-an-array.py @@ -0,0 +1,15 @@ +# Time: O(n) +# Space: O(1) + +class Solution(object): + def findDerangement(self, n): + """ + :type n: int + :rtype: int + """ + M = 1000000007 + mul, total = 1, 0 + for i in reversed(xrange(n+1)): + total = (total + M + (1 if i % 2 == 0 else -1) * mul) % M + mul = (mul * i) % M + return total diff --git a/Python/find-the-difference.py b/Python/find-the-difference.py new file mode 100644 index 000000000..691f63a9e --- /dev/null +++ b/Python/find-the-difference.py @@ -0,0 +1,64 @@ +from __future__ import print_function +# Time: O(n) +# Space: O(1) + +# Given two strings s and t which consist of only lowercase letters. +# +# String t is generated by random shuffling string s +# and then add one more letter at a random position. +# +# Find the letter that was added in t. +# +# Example: +# +# Input: +# s = "abcd" +# t = "abcde" +# +# Output: +# e +# +# Explanation: +# 'e' is the letter that was added. + +import operator +import collections +from functools import reduce + + +class Solution(object): + def findTheDifference(self, s, t): + """ + :type s: str + :type t: str + :rtype: str + """ + return chr(reduce(operator.xor, map(ord, s), 0) ^ reduce(operator.xor, map(ord, t), 0)) + + def findTheDifference2(self, s, t): + """ + :type s: str + :type t: str + :rtype: str + """ + t = list(t) + s = list(s) + for i in s: + t.remove(i) + return t[0] + + def findTheDifference3(self, s, t): + return chr(reduce(operator.xor, map(ord, s + t))) + + def findTheDifference4(self, s, t): + return list((collections.Counter(t) - collections.Counter(s)))[0] + + def findTheDifference5(self, s, t): + s, t = sorted(s), sorted(t) + return t[-1] if s == t[:-1] else [x[1] for x in zip(s, t) if x[0] != x[1]][0] + + +if __name__ == '__main__': + s = Solution() + r = s.findTheDifference2('abcd', 'abcde') + print(r) diff --git a/Python/find-the-duplicate-number.py b/Python/find-the-duplicate-number.py new file mode 100644 index 000000000..011afc5be --- /dev/null +++ b/Python/find-the-duplicate-number.py @@ -0,0 +1,85 @@ +# Time: O(n) +# Space: O(1) +# +# Given an array nums containing n + 1 integers where each integer +# is between 1 and n (inclusive), prove that at least one duplicate +# element must exist. Assume that there is only one duplicate number, +# find the duplicate one. +# +# Note: +# - You must not modify the array (assume the array is read only). +# - You must use only constant extra space. +# - Your runtime complexity should be less than O(n^2). +# + +# Two pointers method, same as Linked List Cycle II. +class Solution(object): + def findDuplicate(self, nums): + """ + :type nums: List[int] + :rtype: int + """ + # Treat each (key, value) pair of the array as the (pointer, next) node of the linked list, + # thus the duplicated number will be the begin of the cycle in the linked list. + # Besides, there is always a cycle in the linked list which + # starts from the first element of the array. + slow = nums[0] + fast = nums[nums[0]] + while slow != fast: + slow = nums[slow] + fast = nums[nums[fast]] + + fast = 0 + while slow != fast: + slow = nums[slow] + fast = nums[fast] + return slow + + +# Time: O(nlogn) +# Space: O(1) +# Binary search method. +class Solution2(object): + def findDuplicate(self, nums): + """ + :type nums: List[int] + :rtype: int + """ + left, right = 1, len(nums) - 1 + + while left <= right: + mid = left + (right - left) / 2 + # Get count of num <= mid. + count = 0 + for num in nums: + if num <= mid: + count += 1 + if count > mid: + right = mid - 1 + else: + left = mid + 1 + return left + +# Time: O(n) +# Space: O(n) +class Solution3(object): + def findDuplicate(self, nums): + """ + :type nums: List[int] + :rtype: int + """ + duplicate = 0 + # Mark the value as visited by negative. + for num in nums: + if nums[abs(num) - 1] > 0: + nums[abs(num) - 1] *= -1 + else: + duplicate = abs(num) + break + # Rollback the value. + for num in nums: + if nums[abs(num) - 1] < 0: + nums[abs(num) - 1] *= -1 + else: + break + return duplicate diff --git a/Python/first-bad-version.py b/Python/first-bad-version.py new file mode 100644 index 000000000..9a46a2d39 --- /dev/null +++ b/Python/first-bad-version.py @@ -0,0 +1,38 @@ +# Time: O(logn) +# Space: O(1) +# +# You are a product manager and currently leading a team to +# develop a new product. Unfortunately, the latest version of +# your product fails the quality check. Since each version is +# developed based on the previous version, all the versions +# after a bad version are also bad. +# +# Suppose you have n versions [1, 2, ..., n] and you want to +# find out the first bad one, which causes all the following +# ones to be bad. +# +# You are given an API bool isBadVersion(version) which will +# return whether version is bad. Implement a function to find +# the first bad version. You should minimize the number of +# calls to the API. +# + +# The isBadVersion API is already defined for you. +# @param version, an integer +# @return a bool +# def isBadVersion(version): + +class Solution(object): + def firstBadVersion(self, n): + """ + :type n: int + :rtype: int + """ + left, right = 1, n + while left <= right: + mid = left + (right - left) / 2 + if isBadVersion(mid): # noqa + right = mid - 1 + else: + left = mid + 1 + return left diff --git a/Python/first-missing-positive.py b/Python/first-missing-positive.py index b2fa788f0..0fec5e20d 100644 --- a/Python/first-missing-positive.py +++ b/Python/first-missing-positive.py @@ -1,12 +1,13 @@ +from __future__ import print_function # Time: O(n) -# Space: O(n) +# Space: O(1) # # Given an unsorted integer array, find the first missing positive integer. -# +# # For example, # Given [1,2,0] return 3, # and [3,4,-1,1] return 2. -# +# # Your algorithm should run in O(n) time and uses constant space. # @@ -20,12 +21,12 @@ def firstMissingPositive(self, A): A[A[i]-1], A[i] = A[i], A[A[i]-1] else: i += 1 - + for i, integer in enumerate(A): if integer != i + 1: return i + 1 return len(A) + 1 - + if __name__ == "__main__": - print Solution().firstMissingPositive([1,2,0]) - print Solution().firstMissingPositive([3,4,-1,1]) \ No newline at end of file + print(Solution().firstMissingPositive([1,2,0])) + print(Solution().firstMissingPositive([3,4,-1,1])) diff --git a/Python/first-unique-character-in-a-string.py b/Python/first-unique-character-in-a-string.py new file mode 100644 index 000000000..9dbbff04e --- /dev/null +++ b/Python/first-unique-character-in-a-string.py @@ -0,0 +1,34 @@ +# Time: O(n) +# Space: O(n) + +# Given a string, find the first non-repeating character in it and +# return it's index. If it doesn't exist, return -1. +# +# Examples: +# +# s = "leetcode" +# return 0. +# +# s = "loveleetcode", +# return 2. +# Note: You may assume the string contain only lowercase letters. + + +from collections import defaultdict + +class Solution(object): + def firstUniqChar(self, s): + """ + :type s: str + :rtype: int + """ + lookup = defaultdict(int) + candidtates = set() + for i, c in enumerate(s): + if lookup[c]: + candidtates.discard(lookup[c]) + else: + lookup[c] = i+1 + candidtates.add(i+1) + + return min(candidtates)-1 if candidtates else -1 diff --git a/Python/fizz-buzz.py b/Python/fizz-buzz.py new file mode 100644 index 000000000..9c56e8524 --- /dev/null +++ b/Python/fizz-buzz.py @@ -0,0 +1,74 @@ +# Time: O(n) +# Space: O(1) + +# Write a program that outputs the string representation of numbers from 1 to n. +# +# But for multiples of three it should output “Fizz” instead of the number and for +# the multiples of five output “Buzz”. For numbers which are multiples of both three +# and five output “FizzBuzz”. +# +# Example: +# +# n = 15, +# +# Return: +# [ +# "1", +# "2", +# "Fizz", +# "4", +# "Buzz", +# "Fizz", +# "7", +# "8", +# "Fizz", +# "Buzz", +# "11", +# "Fizz", +# "13", +# "14", +# "FizzBuzz" +# ] + +class Solution(object): + def fizzBuzz(self, n): + """ + :type n: int + :rtype: List[str] + """ + result = [] + + for i in xrange(1, n+1): + if i % 15 == 0: + result.append("FizzBuzz") + elif i % 5 == 0: + result.append("Buzz") + elif i % 3 == 0: + result.append("Fizz") + else: + result.append(str(i)) + + return result + + def fizzBuzz2(self, n): + """ + :type n: int + :rtype: List[str] + """ + l = [str(x) for x in range(n + 1)] + l3 = range(0, n + 1, 3) + l5 = range(0, n + 1, 5) + for i in l3: + l[i] = 'Fizz' + for i in l5: + if l[i] == 'Fizz': + l[i] += 'Buzz' + else: + l[i] = 'Buzz' + return l[1:] + + def fizzBuzz3(self, n): + return ['Fizz' * (not i % 3) + 'Buzz' * (not i % 5) or str(i) for i in range(1, n + 1)] + + def fizzBuzz4(self, n): + return ['FizzBuzz'[i % -3 & -4:i % -5 & 8 ^ 12] or repr(i) for i in range(1, n + 1)] diff --git a/Python/flatten-2d-vector.py b/Python/flatten-2d-vector.py new file mode 100644 index 000000000..7443014cb --- /dev/null +++ b/Python/flatten-2d-vector.py @@ -0,0 +1,32 @@ +# Time: O(1) +# Space: O(1) + +class Vector2D: + x, y = 0, 0 + vec = None + + # Initialize your data structure here. + # @param {integer[][]} vec2d + def __init__(self, vec2d): + self.vec = vec2d + self.x = 0 + if self.x != len(self.vec): + self.y = 0 + self.adjustNextIter() + + # @return {integer} + def next(self): + ret = self.vec[self.x][self.y] + self.y += 1 + self.adjustNextIter() + return ret + + # @return {boolean} + def hasNext(self): + return self.x != len(self.vec) and self.y != len(self.vec[self.x]) + + def adjustNextIter(self): + while self.x != len(self.vec) and self.y == len(self.vec[self.x]): + self.x += 1 + if self.x != len(self.vec): + self.y = 0 diff --git a/Python/flatten-a-multilevel-doubly-linked-list.py b/Python/flatten-a-multilevel-doubly-linked-list.py new file mode 100644 index 000000000..ff2dc661b --- /dev/null +++ b/Python/flatten-a-multilevel-doubly-linked-list.py @@ -0,0 +1,33 @@ +# Time: O(n) +# Space: O(1) + +# Definition for a Node. +class Node(object): + def __init__(self, val, prev, next, child): + self.val = val + self.prev = prev + self.next = next + self.child = child + + +class Solution(object): + def flatten(self, head): + """ + :type head: Node + :rtype: Node + """ + curr = head + while curr: + if curr.child: + curr_next = curr.next + curr.child.prev = curr + curr.next = curr.child + last_child = curr + while last_child.next: + last_child = last_child.next + if curr_next: + last_child.next = curr_next + curr_next.prev = last_child + curr.child = None + curr = curr.next + return head diff --git a/Python/flatten-binary-tree-to-linked-list.py b/Python/flatten-binary-tree-to-linked-list.py index 8d081290a..92e726125 100644 --- a/Python/flatten-binary-tree-to-linked-list.py +++ b/Python/flatten-binary-tree-to-linked-list.py @@ -1,11 +1,12 @@ +from __future__ import print_function # Time: O(n) -# Space: O(logn) +# Space: O(h), h is height of binary tree # # Given a binary tree, flatten it to a linked list in-place. -# +# # For example, # Given -# +# # 1 # / \ # 2 5 @@ -37,7 +38,7 @@ class Solution: # @return nothing, do it in place def flatten(self, root): return self.flattenRecu(root, None) - + def flattenRecu(self, root, list_head): if root != None: list_head = self.flattenRecu(root.right, list_head) @@ -47,7 +48,7 @@ def flattenRecu(self, root, list_head): return root else: return list_head - + class Solution2: list_head = None # @param root, a tree node @@ -60,7 +61,7 @@ def flatten(self, root): root.left = None self.list_head = root return root - + if __name__ == "__main__": root = TreeNode(1) root.left = TreeNode(2) @@ -69,9 +70,9 @@ def flatten(self, root): root.right = TreeNode(5) root.right.right = TreeNode(6) result = Solution().flatten(root) - print result.val - print result.right.val - print result.right.right.val - print result.right.right.right.val - print result.right.right.right.right.val - print result.right.right.right.right.right.val \ No newline at end of file + print(result.val) + print(result.right.val) + print(result.right.right.val) + print(result.right.right.right.val) + print(result.right.right.right.right.val) + print(result.right.right.right.right.right.val) diff --git a/Python/flatten-nested-list-iterator.py b/Python/flatten-nested-list-iterator.py new file mode 100644 index 000000000..255847377 --- /dev/null +++ b/Python/flatten-nested-list-iterator.py @@ -0,0 +1,66 @@ +# Time: O(n), n is the number of the integers. +# Space: O(h), h is the depth of the nested lists. + +# """ +# This is the interface that allows for creating nested lists. +# You should not implement it, or speculate about its implementation +# """ +#class NestedInteger(object): +# def isInteger(self): +# """ +# @return True if this NestedInteger holds a single integer, rather than a nested list. +# :rtype bool +# """ +# +# def getInteger(self): +# """ +# @return the single integer that this NestedInteger holds, if it holds a single integer +# Return None if this NestedInteger holds a nested list +# :rtype int +# """ +# +# def getList(self): +# """ +# @return the nested list that this NestedInteger holds, if it holds a nested list +# Return None if this NestedInteger holds a single integer +# :rtype List[NestedInteger] +# """ + +class NestedIterator(object): + + def __init__(self, nestedList): + """ + Initialize your data structure here. + :type nestedList: List[NestedInteger] + """ + self.__depth = [[nestedList, 0]] + + + def next(self): + """ + :rtype: int + """ + nestedList, i = self.__depth[-1] + self.__depth[-1][1] += 1 + return nestedList[i].getInteger() + + + def hasNext(self): + """ + :rtype: bool + """ + while self.__depth: + nestedList, i = self.__depth[-1] + if i == len(nestedList): + self.__depth.pop() + elif nestedList[i].isInteger(): + return True + else: + self.__depth[-1][1] += 1 + self.__depth.append([nestedList[i].getList(), 0]) + return False + + +# Your NestedIterator object will be instantiated and called as such: +# i, v = NestedIterator(nestedList), [] +# while i.hasNext(): v.append(i.next()) diff --git a/Python/flip-game-ii.py b/Python/flip-game-ii.py new file mode 100644 index 000000000..4135d1b7e --- /dev/null +++ b/Python/flip-game-ii.py @@ -0,0 +1,71 @@ +# Time: O(n + c^2) +# Space: O(c) + +import itertools +import re + + +# The best theory solution (DP, O(n + c^2)) could be seen here: +# https://leetcode.com/discuss/64344/theory-matters-from-backtracking-128ms-to-dp-0m +class Solution(object): + def canWin(self, s): + g, g_final = [0], 0 + for p in itertools.imap(len, re.split('-+', s)): + while len(g) <= p: + # Theorem 2: g[game] = g[subgame1]^g[subgame2]^g[subgame3]...; + # and find first missing number. + g += min(set(xrange(p)) - {x^y for x, y in itertools.izip(g[:len(g)/2], g[-2:-len(g)/2-2:-1])}), + g_final ^= g[p] + return g_final > 0 # Theorem 1: First player must win iff g(current_state) != 0 + + +# Time: O(n + c^3 * 2^c * logc), n is length of string, c is count of "++" +# Space: O(c * 2^c) +# hash solution. +# We have total O(2^c) game strings, +# and each hash key in hash table would cost O(c), +# each one has O(c) choices to the next one, +# and each one would cost O(clogc) to sort, +# so we get O((c * 2^c) * (c * clogc)) = O(c^3 * 2^c * logc) time. +# To cache the results of all combinations, thus O(c * 2^c) space. +class Solution2(object): + def canWin(self, s): + """ + :type s: str + :rtype: bool + """ + lookup = {} + + def canWinHelper(consecutives): # O(2^c) time + consecutives = tuple(sorted(c for c in consecutives if c >= 2)) # O(clogc) time + if consecutives not in lookup: + lookup[consecutives] = any(not canWinHelper(consecutives[:i] + (j, c-2-j) + consecutives[i+1:]) # O(c) time + for i, c in enumerate(consecutives) # O(c) time + for j in xrange(c - 1)) # O(c) time + return lookup[consecutives] # O(c) time + + # re.findall: O(n) time, canWinHelper: O(c) in depth + return canWinHelper(map(len, re.findall(r'\+\++', s))) + + +# Time: O(c * n * c!), n is length of string, c is count of "++" +# Space: O(c * n), recursion would be called at most c in depth. +# Besides, it costs n space for modifying string at each depth. +class Solution3(object): + def canWin(self, s): + """ + :type s: str + :rtype: bool + """ + i, n = 0, len(s) - 1 + is_win = False + while not is_win and i < n: # O(n) time + if s[i] == '+': + while not is_win and i < n and s[i+1] == '+': # O(c) time + # t(n, c) = c * (t(n, c-1) + n) + n = ... + # = c! * t(n, 0) + n * c! * (c + 1) * (1/0! + 1/1! + ... 1/c!) + # = n * c! + n * c! * (c + 1) * O(e) = O(c * n * c!) + is_win = not self.canWin(s[:i] + '--' + s[i+2:]) # O(n) space + i += 1 + i += 1 + return is_win diff --git a/Python/flip-game.py b/Python/flip-game.py new file mode 100644 index 000000000..b366ed982 --- /dev/null +++ b/Python/flip-game.py @@ -0,0 +1,32 @@ +# Time: O(c * n + n) = O(n * (c+1)) +# Space: O(n) + +# This solution compares only O(1) times for the two consecutive "+" +class Solution(object): + def generatePossibleNextMoves(self, s): + """ + :type s: str + :rtype: List[str] + """ + res = [] + i, n = 0, len(s) - 1 + while i < n: # O(n) time + if s[i] == '+': + while i < n and s[i+1] == '+': # O(c) time + res.append(s[:i] + '--' + s[i+2:]) # O(n) time and space + i += 1 + i += 1 + return res + + +# Time: O(c * m * n + n) = O(c * n + n), where m = 2 in this question +# Space: O(n) +# This solution compares O(m) = O(2) times for two consecutive "+", where m is length of the pattern +class Solution2(object): + def generatePossibleNextMoves(self, s): + """ + :type s: str + :rtype: List[str] + """ + return [s[:i] + "--" + s[i+2:] for i in xrange(len(s) - 1) if s[i:i+2] == "++"] + diff --git a/Python/flipping-an-image.py b/Python/flipping-an-image.py new file mode 100644 index 000000000..9de3b1071 --- /dev/null +++ b/Python/flipping-an-image.py @@ -0,0 +1,47 @@ +# Time: O(n^2) +# Space: O(1) + +# Given a binary matrix A, we want to flip the image horizontally, +# then invert it, and return the resulting image. +# +# To flip an image horizontally means that each row of the image is reversed. +# For example, flipping [1, 1, 0] horizontally results in [0, 1, 1]. +# +# To invert an image means that each 0 is replaced by 1, and each 1 is +# replaced by 0. +# For example, inverting [0, 1, 1] results in [1, 0, 0]. +# +# Example 1: +# +# Input: [[1,1,0],[1,0,1],[0,0,0]] +# Output: [[1,0,0],[0,1,0],[1,1,1]] +# Explanation: First reverse each row: [[0,1,1],[1,0,1],[0,0,0]]. +# Then, invert the image: [[1,0,0],[0,1,0],[1,1,1]] +# Example 2: +# +# Input: [[1,1,0,0],[1,0,0,1],[0,1,1,1],[1,0,1,0]] +# Output: [[1,1,0,0],[0,1,1,0],[0,0,0,1],[1,0,1,0]] +# Explanation: First reverse each row: +# [[0,0,1,1],[1,0,0,1],[1,1,1,0],[0,1,0,1]]. +# Then invert the image: [[1,1,0,0],[0,1,1,0],[0,0,0,1],[1,0,1,0]] +# +# Notes: +# 1. 1 <= A.length = A[0].length <= 20 +# 2. 0 <= A[i][j] <= 1 + +try: + xrange # Python 2 +except NameError: + xrange = range # Python 3 + + +class Solution(object): + def flipAndInvertImage(self, A): + """ + :type A: List[List[int]] + :rtype: List[List[int]] + """ + for row in A: + for i in xrange((len(row)+1) // 2): + row[i], row[~i] = row[~i] ^ 1, row[i] ^ 1 + return A diff --git a/Python/flood-fill.py b/Python/flood-fill.py new file mode 100644 index 000000000..701117bea --- /dev/null +++ b/Python/flood-fill.py @@ -0,0 +1,57 @@ +# Time: O(m * n) +# Space: O(m * n) + +# An image is represented by a 2-D array of integers, +# each integer representing the pixel value of the image (from 0 to 65535). +# +# Given a coordinate (sr, sc) representing the starting pixel (row and column) +# of the flood fill, and a pixel value newColor, "flood fill" the image. +# +# To perform a "flood fill", consider the starting pixel, +# plus any pixels connected 4-directionally to the starting pixel of the same color as the starting pixel, +# plus any pixels connected 4-directionally to those pixels (also with the same color as the starting pixel), +# and so on. Replace the color of all of the aforementioned pixels with the newColor. +# +# At the end, return the modified image. +# +# Example 1: +# Input: +# image = [[1,1,1],[1,1,0],[1,0,1]] +# sr = 1, sc = 1, newColor = 2 +# Output: [[2,2,2],[2,2,0],[2,0,1]] +# Explanation: +# From the center of the image (with position (sr, sc) = (1, 1)), all pixels connected +# by a path of the same color as the starting pixel are colored with the new color. +# Note the bottom corner is not colored 2, because it is not 4-directionally connected +# to the starting pixel. +# +# Note: +# - The length of image and image[0] will be in the range [1, 50]. +# - The given starting pixel will satisfy 0 <= sr < image.length and 0 <= sc < image[0].length. +# - The value of each color in image[i][j] and newColor will be an integer in [0, 65535]. + +class Solution(object): + def floodFill(self, image, sr, sc, newColor): + """ + :type image: List[List[int]] + :type sr: int + :type sc: int + :type newColor: int + :rtype: List[List[int]] + """ + directions = [(0, -1), (0, 1), (-1, 0), (1, 0)] + + def dfs(image, r, c, newColor, color): + if not (0 <= r < len(image) and \ + 0 <= c < len(image[0]) and \ + image[r][c] == color): + return + + image[r][c] = newColor + for d in directions: + dfs(image, r+d[0], c+d[1], newColor, color) + + color = image[sr][sc] + if color == newColor: return image + dfs(image, sr, sc, newColor, color) + return image diff --git a/Python/fraction-addition-and-subtraction.py b/Python/fraction-addition-and-subtraction.py new file mode 100644 index 000000000..f2ac5ecb0 --- /dev/null +++ b/Python/fraction-addition-and-subtraction.py @@ -0,0 +1,56 @@ +# Time: O(nlogx), x is the max denominator +# Space: O(n) + +# Given a string representing an expression of fraction addition and subtraction, +# you need to return the calculation result in string format. +# The final result should be irreducible fraction. If your final result is an integer, say 2, +# you need to change it to the format of fraction that has denominator 1. +# So in this case, 2 should be converted to 2/1. +# +# Example 1: +# Input:"-1/2+1/2" +# Output: "0/1" +# Example 2: +# Input:"-1/2+1/2+1/3" +# Output: "1/3" +# Example 3: +# Input:"1/3-1/2" +# Output: "-1/6" +# Example 4: +# Input:"5/3+1/3" +# Output: "2/1" +# Note: +# The input string only contains '0' to '9', '/', '+' and '-'. So does the output. +# Each fraction (input and output) has format ±numerator/denominator. +# If the first input fraction or the output is positive, then '+' will be omitted. +# The input only contains valid irreducible fractions, +# where the numerator and denominator of each fraction will +# always be in the range [1,10]. If the denominator is 1, +# it means this fraction is actually an integer in a fraction format defined above. +# The number of given fractions will be in the range [1,10]. +# The numerator and denominator of the final result are guaranteed to be valid and in the range of 32-bit int. + +import re + + +class Solution(object): + def fractionAddition(self, expression): + """ + :type expression: str + :rtype: str + """ + def gcd(a, b): + while b: + a, b = b, a%b + return a + + ints = map(int, re.findall('[+-]?\d+', expression)) + A, B = 0, 1 + for i in xrange(0, len(ints), 2): + a, b = ints[i], ints[i+1] + A = A * b + a * B + B *= b + g = gcd(A, B) + A //= g + B //= g + return '%d/%d' % (A, B) diff --git a/Python/fraction-to-recurring-decimal.py b/Python/fraction-to-recurring-decimal.py index 13aa5a9e4..02b14c678 100644 --- a/Python/fraction-to-recurring-decimal.py +++ b/Python/fraction-to-recurring-decimal.py @@ -1,52 +1,51 @@ +from __future__ import print_function # Time: O(logn), where logn is the length of result strings # Space: O(1) + +# Given two integers representing the numerator and denominator of a fraction, +# return the fraction in string format. # -# Given two integers representing the numerator and denominator of a fraction, return the fraction in string format. -# # If the fractional part is repeating, enclose the repeating part in parentheses. -# +# # For example, -# +# # Given numerator = 1, denominator = 2, return "0.5". # Given numerator = 2, denominator = 1, return "2". # Given numerator = 2, denominator = 3, return "0.(6)". -# -class Solution: - # @return a string +class Solution(object): def fractionToDecimal(self, numerator, denominator): + """ + :type numerator: int + :type denominator: int + :rtype: str + """ + result = "" + if (numerator > 0 and denominator < 0) or (numerator < 0 and denominator > 0): + result = "-" + dvd, dvs = abs(numerator), abs(denominator) - integer, decimal, dict = "", "", {} - - if dvd > dvs: - integer = str(dvd / dvs) - dvd %= dvs - else: - integer = "0" - + result += str(dvd / dvs) + dvd %= dvs + if dvd > 0: - integer += "." - - idx = 0 - while dvd: - if dvd in dict: - decimal = decimal[:dict[dvd]] + "(" + decimal[dict[dvd]:] + ")" - break - - dict[dvd] = idx - idx += 1 - + result += "." + + lookup = {} + while dvd and dvd not in lookup: + lookup[dvd] = len(result) dvd *= 10 - decimal += str(dvd / dvs) + result += str(dvd / dvs) dvd %= dvs - - if (numerator > 0 and denominator < 0) or (numerator < 0 and denominator > 0): - return "-" + integer + decimal - else: - return integer + decimal + + if dvd in lookup: + result = result[:lookup[dvd]] + "(" + result[lookup[dvd]:] + ")" + + return result + if __name__ == "__main__": - print Solution().fractionToDecimal(1, 9) - print Solution().fractionToDecimal(-50, 8) - print Solution().fractionToDecimal(22, 2) - print Solution().fractionToDecimal(-22, -2) \ No newline at end of file + print(Solution().fractionToDecimal(1, 9)) + print(Solution().fractionToDecimal(-50, 8)) + print(Solution().fractionToDecimal(22, 2)) + print(Solution().fractionToDecimal(-22, -2)) diff --git a/Python/freedom-trail.py b/Python/freedom-trail.py new file mode 100644 index 000000000..c7d1a9bb2 --- /dev/null +++ b/Python/freedom-trail.py @@ -0,0 +1,64 @@ +# Time: O(k) ~ O(k * r^2) +# Space: O(r) + +# In the video game Fallout 4, the quest "Road to Freedom" +# requires players to reach a metal dial called the "Freedom Trail Ring", +# and use the dial to spell a specific keyword in order to open the door. +# +# Given a string ring, which represents the code engraved on the outer ring +# and another string key, which represents the keyword needs to be spelled. +# You need to find the minimum number of steps in order to spell all the characters in the keyword. +# +# Initially, the first character of the ring is aligned at 12:00 direction. +# You need to spell all the characters in the string key one by one +# by rotating the ring clockwise or anticlockwise to make each character of +# the string key aligned at 12:00 direction and then by pressing the center button. +# At the stage of rotating the ring to spell the key character key[i]: +# You can rotate the ring clockwise or anticlockwise one place, which counts as 1 step. +# The final purpose of the rotation is to align one of the string ring's +# characters at the 12:00 direction, where this character must equal to the character key[i]. +# If the character key[i] has been aligned at the 12:00 direction, +# you need to press the center button to spell, which also counts as 1 step. +# After the pressing, you could begin to spell the next character in the key (next stage), +# otherwise, you've finished all the spelling. +# Example: +# +# Input: ring = "godding", key = "gd" +# Output: 4 +# Explanation: +# For the first key character 'g', since it is already in place, we just need 1 step to spell this character. +# For the second key character 'd', +# we need to rotate the ring "godding" anticlockwise by two steps to make it become "ddinggo". +# Also, we need 1 more step for spelling. +# So the final output is 4. +# Note: +# Length of both ring and key will be in range 1 to 100. +# There are only lowercase letters in both strings and might be some duplcate characters in both strings. +# It's guaranteed that string key could always be spelled by rotating the string ring. + +import collections + + +class Solution(object): + def findRotateSteps(self, ring, key): + """ + :type ring: str + :type key: str + :rtype: int + """ + lookup = collections.defaultdict(list) + for i in xrange(len(ring)): + lookup[ring[i]].append(i) + + dp = [[0] * len(ring) for _ in xrange(2)] + prev = [0] + for i in xrange(1, len(key)+1): + dp[i%2] = [float("inf")] * len(ring) + for j in lookup[key[i-1]]: + for k in prev: + dp[i%2][j] = min(dp[i%2][j], + min((k+len(ring)-j) % len(ring), \ + (j+len(ring)-k) % len(ring)) + \ + dp[(i-1) % 2][k]) + prev = lookup[key[i-1]] + return min(dp[len(key)%2]) + len(key) diff --git a/Python/friend-circles.py b/Python/friend-circles.py new file mode 100644 index 000000000..486294dfd --- /dev/null +++ b/Python/friend-circles.py @@ -0,0 +1,66 @@ +# Time: O(n^2) +# Space: O(n) + +# There are N students in a class. Some of them are friends, while some are not. +# Their friendship is transitive in nature. +# For example, if A is a direct friend of B, and B is a direct friend of C, +# then A is an indirect friend of C. +# And we defined a friend circle is a group of students who are direct or indirect friends. +# +# Given a N*N matrix M representing the friend relationship between students in the class. +# If M[i][j] = 1, then the ith and jth students are direct friends with each other, +# otherwise not. And you have to output the total number of friend circles among all the students. +# +# Example 1: +# Input: +# [[1,1,0], +# [1,1,0], +# [0,0,1]] +# Output: 2 +# Explanation:The 0th and 1st students are direct friends, so they are in a friend circle. +# The 2nd student himself is in a friend circle. So return 2. +# +# Example 2: +# Input: +# [[1,1,0], +# [1,1,1], +# [0,1,1]] +# Output: 1 +# Explanation:The 0th and 1st students are direct friends, the 1st and 2nd students are direct friends, +# so the 0th and 2nd students are indirect friends. All of them are in the same friend circle, so return 1. +# +# Note: +# N is in range [1,200]. +# M[i][i] = 1 for all students. +# If M[i][j] = 1, then M[j][i] = 1. + +class Solution(object): + def findCircleNum(self, M): + """ + :type M: List[List[int]] + :rtype: int + """ + + class UnionFind(object): + def __init__(self, n): + self.set = range(n) + self.count = n + + def find_set(self, x): + if self.set[x] != x: + self.set[x] = self.find_set(self.set[x]) # path compression. + return self.set[x] + + def union_set(self, x, y): + x_root, y_root = map(self.find_set, (x, y)) + if x_root != y_root: + self.set[min(x_root, y_root)] = max(x_root, y_root) + self.count -= 1 + + circles = UnionFind(len(M)) + for i in xrange(len(M)): + for j in xrange(len(M)): + if M[i][j] and i != j: + circles.union_set(i, j) + return circles.count + diff --git a/Python/friends-of-appropriate-ages.py b/Python/friends-of-appropriate-ages.py new file mode 100644 index 000000000..b1ccb730d --- /dev/null +++ b/Python/friends-of-appropriate-ages.py @@ -0,0 +1,61 @@ +# Time: O(a^2 + n), a is the number of ages, +# n is the number of people +# Space: O(a) + +# Some people will make friend requests. +# The list of their ages is given and ages[i] is the age of the ith person. +# +# Person A will NOT friend request person B (B != A) +# if any of the following conditions are true: +# +# age[B] <= 0.5 * age[A] + 7 +# age[B] > age[A] +# age[B] > 100 && age[A] < 100 +# Otherwise, A will friend request B. +# +# Note that if A requests B, B does not necessarily request A. +# Also, people will not friend request themselves. +# +# How many total friend requests are made? +# +# Example 1: +# +# Input: [16,16] +# Output: 2 +# Explanation: 2 people friend request each other. +# Example 2: +# +# Input: [16,17,18] +# Output: 2 +# Explanation: Friend requests are made 17 -> 16, 18 -> 17. +# Example 3: +# +# Input: [20,30,100,110,120] +# Output: +# Explanation: Friend requests are made 110 -> 100, 120 -> 110, 120 -> 100. +# +# Notes: +# - 1 <= ages.length <= 20000. +# - 1 <= ages[i] <= 120. + +try: + xrange # Python 2 +except NameError: + xrange = range # Python 3 + +import collections + + +class Solution(object): + def numFriendRequests(self, ages): + """ + :type ages: List[int] + :rtype: int + """ + def request(a, b): + return 0.5*a+7 < b <= a + + c = collections.Counter(ages) + return sum(int(request(a, b)) * c[a]*(c[b]-int(a == b)) + for a in c + for b in c) diff --git a/Python/frog-jump.py b/Python/frog-jump.py new file mode 100644 index 000000000..3b1ca8637 --- /dev/null +++ b/Python/frog-jump.py @@ -0,0 +1,57 @@ +# Time: O(n) ~ O(n^2) +# Space: O(n) + +# A frog is crossing a river. The river is divided into x units and +# at each unit there may or may not exist a stone. +# The frog can jump on a stone, but it must not jump into the water. +# +# Given a list of stones' positions (in units) in sorted ascending order, +# determine if the frog is able to cross the river by landing on the last stone. +# Initially, the frog is on the first stone and assume the first jump must be 1 unit. +# +# If the frog has just jumped k units, then its next jump must be +# either k - 1, k, or k + 1 units. Note that the frog can only jump in the forward direction. +# +# Note: +# +# The number of stones is >= 2 and is < 1,100. +# Each stone's position will be a non-negative integer < 231. +# The first stone's position is always 0. +# Example 1: +# +# [0,1,3,5,6,8,12,17] +# +# There are a total of 8 stones. +# The first stone at the 0th unit, second stone at the 1st unit, +# third stone at the 3rd unit, and so on... +# The last stone at the 17th unit. +# +# Return true. The frog can jump to the last stone by jumping +# 1 unit to the 2nd stone, then 2 units to the 3rd stone, then +# 2 units to the 4th stone, then 3 units to the 6th stone, +# 4 units to the 7th stone, and 5 units to the 8th stone. +# Example 2: +# +# [0,1,2,3,4,8,9,11] +# +# Return false. There is no way to jump to the last stone as +# the gap between the 5th and 6th stone is too large. + +# DP with hash table +class Solution(object): + def canCross(self, stones): + """ + :type stones: List[int] + :rtype: bool + """ + if stones[1] != 1: + return False + + last_jump_units = {s: set() for s in stones} + last_jump_units[1].add(1) + for s in stones[:-1]: + for j in last_jump_units[s]: + for k in (j-1, j, j+1): + if k > 0 and s+k in last_jump_units: + last_jump_units[s+k].add(k) + return bool(last_jump_units[stones[-1]]) diff --git a/Python/game-of-life.py b/Python/game-of-life.py new file mode 100644 index 000000000..8e2a4132a --- /dev/null +++ b/Python/game-of-life.py @@ -0,0 +1,64 @@ +# Time: O(m * n) +# Space: O(1) + +# According to the Wikipedia's article: +# "The Game of Life, also known simply as Life, +# is a cellular automaton devised by the British +# mathematician John Horton Conway in 1970." +# +# Given a board with m by n cells, each cell has +# an initial state live (1) or dead (0). +# Each cell interacts with its eight neighbors +# (horizontal, vertical, diagonal) +# using the following four rules +# (taken from the above Wikipedia article): +# +# - Any live cell with fewer than two live neighbors dies, +# as if caused by under-population. +# - Any live cell with two or three live neighbors lives +# on to the next generation. +# - Any live cell with more than three live neighbors dies, +# as if by over-population.. +# - Any dead cell with exactly three live neighbors +# becomes a live cell, as if by reproduction. +# +# Write a function to compute the next state +# (after one update) of the board given its current state. +# +# Follow up: +# - Could you solve it in-place? Remember that the board needs +# to be updated at the same time: You cannot update some cells +# first and then use their updated values to update other cells. +# - In this question, we represent the board using a 2D array. +# In principle, the board is infinite, which would cause problems +# when the active area encroaches the border of the array. +# How would you address these problems? +# + +class Solution(object): + def gameOfLife(self, board): + """ + :type board: List[List[int]] + :rtype: void Do not return anything, modify board in-place instead. + """ + m = len(board) + n = len(board[0]) if m else 0 + for i in xrange(m): + for j in xrange(n): + count = 0 + ## Count live cells in 3x3 block. + for I in xrange(max(i-1, 0), min(i+2, m)): + for J in xrange(max(j-1, 0), min(j+2, n)): + count += board[I][J] & 1 + + # if (count == 4 && board[i][j]) means: + # Any live cell with three live neighbors lives. + # if (count == 3) means: + # Any live cell with two live neighbors. + # Any dead cell with exactly three live neighbors lives. + if (count == 4 and board[i][j]) or count == 3: + board[i][j] |= 2 # Mark as live. + + for i in xrange(m): + for j in xrange(n): + board[i][j] >>= 1 # Update to the next state. diff --git a/Python/gas-station.py b/Python/gas-station.py index 72d6a0806..45413aa93 100644 --- a/Python/gas-station.py +++ b/Python/gas-station.py @@ -1,13 +1,14 @@ +from __future__ import print_function # Time: O(n) # Space: O(1) # # There are N gas stations along a circular route, where the amount of gas at station i is gas[i]. -# +# # You have a car with an unlimited gas tank and it costs cost[i] of gas to travel from station i to its next station (i+1). # You begin the journey with an empty tank at one of the gas stations. -# +# # Return the starting gas station's index if you can travel around the circuit once, otherwise return -1. -# +# # Note: # The solution is guaranteed to be unique. # @@ -27,11 +28,11 @@ def canCompleteCircuit(self, gas, cost): current_sum = 0 if total_sum >= 0: return start - + return -1 if __name__ == "__main__": - print Solution().canCompleteCircuit([1, 2, 3], [3, 2, 1]) - print Solution().canCompleteCircuit([1, 2, 3], [2, 2, 2]) - print Solution().canCompleteCircuit([1, 2, 3], [1, 2, 3]) - print Solution().canCompleteCircuit([1, 2, 3], [1, 2, 4]) + print(Solution().canCompleteCircuit([1, 2, 3], [3, 2, 1])) + print(Solution().canCompleteCircuit([1, 2, 3], [2, 2, 2])) + print(Solution().canCompleteCircuit([1, 2, 3], [1, 2, 3])) + print(Solution().canCompleteCircuit([1, 2, 3], [1, 2, 4])) diff --git a/Python/generalized-abbreviation.py b/Python/generalized-abbreviation.py new file mode 100644 index 000000000..850259e04 --- /dev/null +++ b/Python/generalized-abbreviation.py @@ -0,0 +1,26 @@ +# Time: O(n * 2^n) +# Space: O(n) + +class Solution(object): + def generateAbbreviations(self, word): + """ + :type word: str + :rtype: List[str] + """ + def generateAbbreviationsHelper(word, i, cur, res): + if i == len(word): + res.append("".join(cur)) + return + cur.append(word[i]) + generateAbbreviationsHelper(word, i + 1, cur, res) + cur.pop() + if not cur or not cur[-1][-1].isdigit(): + for l in xrange(1, len(word) - i + 1): + cur.append(str(l)) + generateAbbreviationsHelper(word, i + l, cur, res) + cur.pop() + + res, cur = [], [] + generateAbbreviationsHelper(word, 0, cur, res) + return res + diff --git a/Python/generate-parentheses.py b/Python/generate-parentheses.py index 0b9712875..113f59077 100644 --- a/Python/generate-parentheses.py +++ b/Python/generate-parentheses.py @@ -1,11 +1,12 @@ +from __future__ import print_function # Time: O(4^n / n^(3/2)) ~= Catalan numbers # Space: O(n) # -# Given n pairs of parentheses, write a function to generate +# Given n pairs of parentheses, write a function to generate # all combinations of well-formed parentheses. -# +# # For example, given n = 3, a solution set is: -# +# # "((()))", "(()())", "(())()", "()(())", "()()()" # @@ -16,7 +17,7 @@ def generateParenthesis(self, n): result = [] self.generateParenthesisRecu(result, "", n, n) return result - + def generateParenthesisRecu(self, result, current, left, right): if left == 0 and right == 0: result.append(current) @@ -26,4 +27,4 @@ def generateParenthesisRecu(self, result, current, left, right): self.generateParenthesisRecu(result, current + ")", left, right - 1) if __name__ == "__main__": - print Solution().generateParenthesis(3) \ No newline at end of file + print(Solution().generateParenthesis(3)) \ No newline at end of file diff --git a/Python/generate-random-point-in-a-circle.py b/Python/generate-random-point-in-a-circle.py new file mode 100644 index 000000000..c044ce113 --- /dev/null +++ b/Python/generate-random-point-in-a-circle.py @@ -0,0 +1,61 @@ +# Time: O(1) +# Space: O(1) + +# Given the radius and x-y positions of the center of a circle, +# write a function randPoint which generates a uniform random point in the circle. +# +# Note: +# +# input and output values are in floating-point. +# radius and x-y position of the center of the circle is passed into the class constructor. +# a point on the circumference of the circle is considered to be in the circle. +# randPoint returns a size 2 array containing x-position and y-position of the random point, in that order. +# Example 1: +# +# Input: +# ["Solution","randPoint","randPoint","randPoint"] +# [[1,0,0],[],[],[]] +# Output: [null,[-0.72939,-0.65505],[-0.78502,-0.28626],[-0.83119,-0.19803]] +# Example 2: +# +# Input: +# ["Solution","randPoint","randPoint","randPoint"] +# [[10,5,-7.5],[],[],[]] +# Output: [null,[11.52438,-8.33273],[2.46992,-16.21705],[11.13430,-12.42337]] +# Explanation of Input Syntax: +# +# The input is two lists: the subroutines called and their arguments. +# Solution's constructor has three arguments, the radius, x-position of the center, +# and y-position of the center of the circle. randPoint has no arguments. +# Arguments are always wrapped with a list, even if there aren't any. + + +import random +import math + +class Solution(object): + + def __init__(self, radius, x_center, y_center): + """ + :type radius: float + :type x_center: float + :type y_center: float + """ + self.__radius = radius + self.__x_center = x_center + self.__y_center = y_center + + + def randPoint(self): + """ + :rtype: List[float] + """ + r = (self.__radius) * math.sqrt(random.uniform(0, 1)) + theta = (2*math.pi) * random.uniform(0, 1) + return (r*math.cos(theta) + self.__x_center, + r*math.sin(theta) + self.__y_center) + + +# Your Solution object will be instantiated and called as such: +# obj = Solution(radius, x_center, y_center) +# param_1 = obj.randPoint() diff --git a/Python/global-and-local-inversions.py b/Python/global-and-local-inversions.py new file mode 100644 index 000000000..2c7e22024 --- /dev/null +++ b/Python/global-and-local-inversions.py @@ -0,0 +1,33 @@ +# Time: O(n) +# Space: O(1) + +# We have some permutation A of [0, 1, ..., N - 1], where N is the length of A. +# The number of (global) inversions is the number of i < j with 0 <= i < j < N and A[i] > A[j]. +# The number of local inversions is the number of i with 0 <= i < N and A[i] > A[i+1]. +# Return true if and only if the number of global inversions is equal to the number of local inversions. +# +# Example 1: +# +# Input: A = [1,0,2] +# Output: true +# Explanation: There is 1 global inversion, and 1 local inversion. +# +# Example 2: +# +# Input: A = [1,2,0] +# Output: false +# Explanation: There are 2 global inversions, and 1 local inversion. +# +# Note: +# - A will be a permutation of [0, 1, ..., A.length - 1]. +# - A will have length in range [1, 5000]. +# - The time limit for this problem has been reduced. + +class Solution(object): + def isIdealPermutation(self, A): + """ + :type A: List[int] + :rtype: bool + """ + return all(abs(v-i) <= 1 for i,v in enumerate(A)) + diff --git a/Python/goat-latin.py b/Python/goat-latin.py new file mode 100644 index 000000000..fe1c3e56d --- /dev/null +++ b/Python/goat-latin.py @@ -0,0 +1,58 @@ +# Time: O(n + w^2), n = w * l, +# n is the length of S, +# w is the number of word, +# l is the average length of word +# Space: O(n) + +# A sentence S is given, composed of words separated by spaces. +# Each word consists of lowercase and uppercase letters only. +# +# We would like to convert the sentence to "Goat Latin" +# (a made-up language similar to Pig Latin.) +# +# The rules of Goat Latin are as follows: +# +# If a word begins with a vowel (a, e, i, o, or u), append "ma" to the end of +# the word. +# For example, the word 'apple' becomes 'applema'. +# +# If a word begins with a consonant (i.e. not a vowel), +# remove the first letter and append it to the end, then add "ma". +# For example, the word "goat" becomes "oatgma". +# +# Add one letter 'a' to the end of each word per its word index in the +# sentence, +# starting with 1. +# For example, the first word gets "a" added to the end, +# the second word gets "aa" added to the end and so on. +# Return the final sentence representing the conversion from S to Goat Latin. +# +# Example 1: +# +# Input: "I speak Goat Latin" +# Output: "Imaa peaksmaaa oatGmaaaa atinLmaaaaa" +# Example 2: +# +# Input: "The quick brown fox jumped over the lazy dog" +# Output: "heTmaa uickqmaaa rownbmaaaa oxfmaaaaa umpedjmaaaaaa +# overmaaaaaaa hetmaaaaaaaa azylmaaaaaaaaa ogdmaaaaaaaaaa" +# +# Notes: +# - S contains only uppercase, lowercase and spaces. Exactly one space between +# each word. +# - 1 <= S.length <= 100. + + +class Solution(object): + def toGoatLatin(self, S): + """ + :type S: str + :rtype: str + """ + def convert(S): + vowel = set('aeiouAEIOU') + for i, word in enumerate(S.split(), 1): + if word[0] not in vowel: + word = word[1:] + word[:1] + yield word + 'ma' + 'a'*i + return " ".join(convert(S)) diff --git a/Python/graph-valid-tree.py b/Python/graph-valid-tree.py new file mode 100644 index 000000000..7e0be8344 --- /dev/null +++ b/Python/graph-valid-tree.py @@ -0,0 +1,65 @@ +# Time: O(|V| + |E|) +# Space: O(|V| + |E|) + +import collections + + +# BFS solution. Same complexity but faster version. +class Solution(object): + # @param {integer} n + # @param {integer[][]} edges + # @return {boolean} + def validTree(self, n, edges): + if len(edges) != n - 1: # Check number of edges. + return False + + # init node's neighbors in dict + neighbors = collections.defaultdict(list) + for u, v in edges: + neighbors[u].append(v) + neighbors[v].append(u) + + # BFS to check whether the graph is valid tree. + visited = {} + q = collections.deque([0]) + while q: + curr = q.popleft() + visited[curr] = True + for node in neighbors[curr]: + if node not in visited: + visited[node] = True + q.append(node) + + return len(visited) == n + + +# Time: O(|V| + |E|) +# Space: O(|V| + |E|) +# BFS solution. +class Solution2(object): + # @param {integer} n + # @param {integer[][]} edges + # @return {boolean} + def validTree(self, n, edges): + # A structure to track each node's [visited_from, neighbors] + visited_from = [-1] * n + neighbors = collections.defaultdict(list) + for u, v in edges: + neighbors[u].append(v) + neighbors[v].append(u) + + # BFS to check whether the graph is valid tree. + visited = {} + q = collections.deque([0]) + while q: + i = q.popleft() + visited[i] = True + for node in neighbors[i]: + if node != visited_from[i]: + if node in visited: + return False + else: + visited[node] = True + visited_from[node] = i + q.append(node) + return len(visited) == n diff --git a/Python/gray-code.py b/Python/gray-code.py index 2bad42513..8cd9f0411 100644 --- a/Python/gray-code.py +++ b/Python/gray-code.py @@ -1,40 +1,51 @@ +from __future__ import print_function # Time: O(2^n) # Space: O(1) -# + # The gray code is a binary numeral system where two successive values differ in only one bit. -# -# Given a non-negative integer n representing the total number of bits in the code, +# +# Given a non-negative integer n representing the total number of bits in the code, # print the sequence of gray code. A gray code sequence must begin with 0. -# +# # For example, given n = 2, return [0,1,3,2]. Its gray code sequence is: -# +# # 00 - 0 # 01 - 1 # 11 - 3 # 10 - 2 # Note: # For a given n, a gray code sequence is not uniquely defined. -# -# For example, [0,2,3,1] is also a valid gray code sequence according to the above definition. -# -# For now, the judge is able to judge based on one instance of gray code sequence. Sorry about that. +# +# For example, [0,2,3,1] is also a valid gray code sequence according +# to the above definition. +# +# For now, the judge is able to judge based on one instance of gray code +# sequence. Sorry about that. -class Solution: - # @return a list of integers +class Solution(object): def grayCode(self, n): + """ + :type n: int + :rtype: List[int] + """ result = [0] - for i in xrange(0, n): + for i in xrange(n): for n in reversed(result): result.append(1 << i | n) return result -# proof of closed form formula could be found here: + +# Proof of closed form formula could be found here: # http://math.stackexchange.com/questions/425894/proof-of-closed-form-formula-to-convert-a-binary-number-to-its-gray-code -class Solution2: - # @return a list of integers +class Solution2(object): def grayCode(self, n): + """ + :type n: int + :rtype: List[int] + """ return [i >> 1 ^ i for i in xrange(1 << n)] + if __name__ == "__main__": - print Solution().grayCode(0) - print Solution().grayCode(2) + print(Solution().grayCode(0)) + print(Solution().grayCode(2)) diff --git a/Python/group-anagrams.py b/Python/group-anagrams.py new file mode 100644 index 000000000..ade7079f8 --- /dev/null +++ b/Python/group-anagrams.py @@ -0,0 +1,31 @@ +from __future__ import print_function +# Time: O(n * glogg), g is the max size of groups. +# Space: O(n) +# +# Given an array of strings, return all groups of strings that are anagrams. +# +# Note: All inputs will be in lower-case. +# + +import collections + + +class Solution(object): + def groupAnagrams(self, strs): + """ + :type strs: List[str] + :rtype: List[List[str]] + """ + anagrams_map, result = collections.defaultdict(list), [] + for s in strs: + sorted_str = ("").join(sorted(s)) + anagrams_map[sorted_str].append(s) + for anagram in anagrams_map.values(): + anagram.sort() + result.append(anagram) + return result + + +if __name__ == "__main__": + result = Solution().groupAnagrams(["cat", "dog", "act", "mac"]) + print(result) diff --git a/Python/group-shifted-strings.py b/Python/group-shifted-strings.py new file mode 100644 index 000000000..9f58ee361 --- /dev/null +++ b/Python/group-shifted-strings.py @@ -0,0 +1,29 @@ +# Time: O(nlogn) +# Space: O(n) + +import collections + + +class Solution: + # @param {string[]} strings + # @return {string[][]} + def groupStrings(self, strings): + groups = collections.defaultdict(list) + for s in strings: # Grouping. + groups[self.hashStr(s)].append(s) + + result = [] + for key, val in groups.iteritems(): + result.append(sorted(val)) + + return result + + def hashStr(self, s): + base = ord(s[0]) + hashcode = "" + for i in xrange(len(s)): + if ord(s[i]) - base >= 0: + hashcode += unichr(ord('a') + ord(s[i]) - base) + else: + hashcode += unichr(ord('a') + ord(s[i]) - base + 26) + return hashcode diff --git a/Python/guess-number-higher-or-lower-ii.py b/Python/guess-number-higher-or-lower-ii.py new file mode 100644 index 000000000..9de4bf3bc --- /dev/null +++ b/Python/guess-number-higher-or-lower-ii.py @@ -0,0 +1,49 @@ +# Time: O(n^2) +# Space: O(n^2) + +# We are playing the Guess Game. The game is as follows: +# +# I pick a number from 1 to n. You have to guess which number I picked. +# +# Every time you guess wrong, I'll tell you whether the number I picked is higher or lower. +# +# However, when you guess a particular number x, and you guess wrong, +# you pay $x. You win the game when you guess the number I picked. +# +# Example: +# +# n = 10, I pick 8. +# +# First round: You guess 5, I tell you that it's higher. You pay $5. +# Second round: You guess 7, I tell you that it's higher. You pay $7. +# Third round: You guess 9, I tell you that it's lower. You pay $9. +# +# Game over. 8 is the number I picked. +# +# You end up paying $5 + $7 + $9 = $21. +# Given a particular n >= 1, find out how much money you need to have to guarantee a win. +# +# Hint: +# +# The best strategy to play the game is to minimize the maximum loss +# you could possibly face. Another strategy is to minimize the expected loss. +# Here, we are interested in the first scenario. +# Take a small example (n = 3). What do you end up paying in the worst case? +# Check out this article if you're still stuck. +# The purely recursive implementation of minimax would be worthless +# for even a small n. You MUST use dynamic programming. +# As a follow-up, how would you modify your code to solve the problem of +# minimizing the expected loss, instead of the worst-case loss? + +class Solution(object): + def getMoneyAmount(self, n): + """ + :type n: int + :rtype: int + """ + pay = [[0] * n for _ in xrange(n+1)] + for i in reversed(xrange(n)): + for j in xrange(i+1, n): + pay[i][j] = min(k+1 + max(pay[i][k-1], pay[k+1][j]) \ + for k in xrange(i, j+1)) + return pay[0][n-1] diff --git a/Python/guess-number-higher-or-lower.py b/Python/guess-number-higher-or-lower.py new file mode 100644 index 000000000..600f58c23 --- /dev/null +++ b/Python/guess-number-higher-or-lower.py @@ -0,0 +1,32 @@ +# Time: O(logn) +# Space: O(1) + +# Given an array nums containing n + 1 integers where each integer is +# between 1 and n (inclusive), prove that at least one duplicate number +# must exist. Assume that there is only one duplicate number, find the duplicate one. +# +# Note: +# You must not modify the array (assume the array is read only). +# You must use only constant, O(1) extra space. +# Your runtime complexity should be less than O(n2). +# There is only one duplicate number in the array, but it could be repeated more than once. + +# The guess API is already defined for you. +# @param num, your guess +# @return -1 if my number is lower, 1 if my number is higher, otherwise return 0 +# def guess(num): + +class Solution(object): + def guessNumber(self, n): + """ + :type n: int + :rtype: int + """ + left, right = 1, n + while left <= right: + mid = left + (right - left) / 2 + if guess(mid) <= 0: # noqa + right = mid - 1 + else: + left = mid + 1 + return left diff --git a/Python/guess-the-word.py b/Python/guess-the-word.py new file mode 100644 index 000000000..88cd9e325 --- /dev/null +++ b/Python/guess-the-word.py @@ -0,0 +1,125 @@ +# Time: O(n^2) +# Space: O(n) + +# This problem is an interactive problem new to the LeetCode platform. +# +# We are given a word list of unique words, each word is 6 letters long, +# and one word in this list is chosen as secret. +# +# You may call master.guess(word) to guess a word. +# The guessed word should have type string and must be from the original +# list with 6 lowercase letters. +# +# This function returns an integer type, +# representing the number of exact matches (value and position) +# of your guess to the secret word. +# Also, if your guess is not in the given wordlist, it will return -1 instead. +# +# For each test case, you have 10 guesses to guess the word. +# At the end of any number of calls, if you have made 10 or +# less calls to master.guess +# and at least one of these guesses was the secret, you pass the testcase. +# +# Besides the example test case below, +# there will be 5 additional test cases, each with 100 words in the word list. +# The letters of each word in those testcases were chosen independently at +# random from 'a' to 'z', +# such that every word in the given word lists is unique. +# +# Example 1: +# Input: secret = "acckzz", wordlist = ["acckzz","ccbazz","eiowzz","abcczz"] +# +# Explanation: +# +# master.guess("aaaaaa") returns -1, because "aaaaaa" is not in wordlist. +# master.guess("acckzz") returns 6, because "acckzz" is secret +# and has all 6 matches. +# master.guess("ccbazz") returns 3, because "ccbazz" has 3 matches. +# master.guess("eiowzz") returns 2, because "eiowzz" has 2 matches. +# master.guess("abcczz") returns 4, because "abcczz" has 4 matches. +# +# We made 5 calls to master.guess and one of them was the secret, +# so we pass the test case. +# Note: Any solutions that attempt to circumvent the judge will result +# in disqualification. +# +# """ +# This is Master's API interface. +# You should not implement it, or speculate about its implementation +# """ +# class Master(object): +# def guess(self, word): +# """ +# :type word: str +# :rtype int +# """ + +import collections +import itertools + +try: + xrange # Python 2 +except NameError: + xrange = range # Python 3 + + +class Solution(object): + def findSecretWord(self, wordlist, master): + """ + :type wordlist: List[Str] + :type master: Master + :rtype: None + """ + def solve(H, possible): + min_max_group, best_guess = possible, None + for guess in possible: + groups = [[] for _ in xrange(7)] + for j in possible: + if j != guess: + groups[H[guess][j]].append(j) + max_group = max(groups, key=len) + if len(max_group) < len(min_max_group): + min_max_group, best_guess = max_group, guess + return best_guess + + H = [[sum(a == b for a, b in itertools.izip(wordlist[i], wordlist[j])) + for j in xrange(len(wordlist))] + for i in xrange(len(wordlist))] + possible = range(len(wordlist)) + n = 0 + while possible and n < 6: + guess = solve(H, possible) + n = master.guess(wordlist[guess]) + possible = [j for j in possible if H[guess][j] == n] + + +# Time: O(n^2) +# Space: O(n) +class Solution2(object): + def findSecretWord(self, wordlist, master): + """ + :type wordlist: List[Str] + :type master: Master + :rtype: None + """ + def solve(H, possible): + min_max_group, best_guess = possible, None + for guess in possible: + groups = [[] for _ in xrange(7)] + for j in possible: + if j != guess: + groups[H[guess][j]].append(j) + max_group = groups[0] + if len(max_group) < len(min_max_group): + min_max_group, best_guess = max_group, guess + return best_guess + + H = [[sum(a == b for a, b in itertools.izip(wordlist[i], wordlist[j])) + for j in xrange(len(wordlist))] + for i in xrange(len(wordlist))] + possible = range(len(wordlist)) + n = 0 + while possible and n < 6: + guess = solve(H, possible) + n = master.guess(wordlist[guess]) + possible = [j for j in possible if H[guess][j] == n] diff --git a/Python/h-index-ii.py b/Python/h-index-ii.py new file mode 100644 index 000000000..c20392b38 --- /dev/null +++ b/Python/h-index-ii.py @@ -0,0 +1,26 @@ +# Time: O(logn) +# Space: O(1) +# +# Follow up for H-Index: What if the citations array is sorted in +# ascending order? Could you optimize your algorithm? +# +# Hint: +# +# Expected runtime complexity is in O(log n) and the input is sorted. +# + +class Solution(object): + def hIndex(self, citations): + """ + :type citations: List[int] + :rtype: int + """ + n = len(citations) + left, right = 0, n - 1 + while left <= right: + mid = (left + right) / 2 + if citations[mid] >= n - mid: + right = mid - 1 + else: + left = mid + 1 + return n - left diff --git a/Python/h-index.py b/Python/h-index.py new file mode 100644 index 000000000..ab47ad881 --- /dev/null +++ b/Python/h-index.py @@ -0,0 +1,70 @@ +# Time: O(n) +# Space: O(n) + +# Given an array of citations (each citation is a non-negative integer) +# of a researcher, write a function to compute the researcher's h-index. +# +# According to the definition of h-index on Wikipedia: +# "A scientist has index h if h of his/her N papers have +# at least h citations each, and the other N − h papers have +# no more than h citations each." +# +# For example, given citations = [3, 0, 6, 1, 5], +# which means the researcher has 5 papers in total +# and each of them had received 3, 0, 6, 1, 5 citations respectively. +# Since the researcher has 3 papers with at least 3 citations each and +# the remaining two with no more than 3 citations each, his h-index is 3. +# +# Note: If there are several possible values for h, the maximum one is taken as the h-index. +# + +# Counting sort. +class Solution(object): + def hIndex(self, citations): + """ + :type citations: List[int] + :rtype: int + """ + n = len(citations); + count = [0] * (n + 1) + for x in citations: + # Put all x >= n in the same bucket. + if x >= n: + count[n] += 1 + else: + count[x] += 1 + + h = 0 + for i in reversed(xrange(0, n + 1)): + h += count[i] + if h >= i: + return i + return h + +# Time: O(nlogn) +# Space: O(1) +class Solution2(object): + def hIndex(self, citations): + """ + :type citations: List[int] + :rtype: int + """ + citations.sort(reverse=True) + h = 0 + for x in citations: + if x >= h + 1: + h += 1 + else: + break + return h + +# Time: O(nlogn) +# Space: O(n) +class Solution3(object): + def hIndex(self, citations): + """ + :type citations: List[int] + :rtype: int + """ + return sum(x >= i + 1 for i, x in enumerate(sorted(citations, reverse=True))) + diff --git a/Python/hamming-distance.py b/Python/hamming-distance.py new file mode 100644 index 000000000..31d0c6ce7 --- /dev/null +++ b/Python/hamming-distance.py @@ -0,0 +1,45 @@ +# Time: O(1) +# Space: O(1) + +# The Hamming distance between two integers is the number of positions +# at which the corresponding bits are different. +# +# Given two integers x and y, calculate the Hamming distance. +# +# Note: +# 0 ≤ x, y < 231. +# +# Example: +# +# Input: x = 1, y = 4 +# +# Output: 2 +# +# Explanation: +# 1 (0 0 0 1) +# 4 (0 1 0 0) +# ↑ ↑ +# +# The above arrows point to positions where the corresponding bits are different. + +class Solution(object): + def hammingDistance(self, x, y): + """ + :type x: int + :type y: int + :rtype: int + """ + distance = 0 + z = x ^ y + while z: + distance += 1 + z &= z - 1 + return distance + + def hammingDistance2(self, x, y): + """ + :type x: int + :type y: int + :rtype: int + """ + return bin(x ^ y).count('1') diff --git a/Python/hand-of-straights.py b/Python/hand-of-straights.py new file mode 100644 index 000000000..18edd0229 --- /dev/null +++ b/Python/hand-of-straights.py @@ -0,0 +1,62 @@ +# Time: O(nlogn) +# Space: O(n) + +# Alice has a hand of cards, given as an array of integers. +# +# Now she wants to rearrange the cards into groups +# so that each group is size W, +# and consists of W consecutive cards. +# +# Return true if and only if she can. +# +# Example 1: +# +# Input: hand = [1,2,3,6,2,3,4,7,8], W = 3 +# Output: true +# Explanation: Alice's hand can be rearranged as [1,2,3],[2,3,4],[6,7,8]. +# Example 2: +# +# Input: hand = [1,2,3,4,5], W = 4 +# Output: false +# Explanation: Alice's hand can't be rearranged into groups of 4. +# +# Note: +# - 1 <= hand.length <= 10000 +# - 0 <= hand[i] <= 10^9 +# - 1 <= W <= hand.length + +import collections +import heapq + +try: + xrange # Python 2 +except NameError: + xrange = range # Python 3 + + +class Solution(object): + def isNStraightHand(self, hand, W): + """ + :type hand: List[int] + :type W: int + :rtype: bool + """ + if len(hand) % W: + return False + + counts = collections.defaultdict(int) + for i in hand: + counts[i] += 1 + + min_heap = hand[:] + heapq.heapify(min_heap) + for _ in xrange(len(min_heap)//W): + while counts[min_heap[0]] == 0: + heapq.heappop(min_heap) + start = heapq.heappop(min_heap) + for _ in xrange(W): + counts[start] -= 1 + if counts[start] < 0: + return False + start += 1 + return True diff --git a/Python/happy-number.py b/Python/happy-number.py new file mode 100644 index 000000000..588f655e5 --- /dev/null +++ b/Python/happy-number.py @@ -0,0 +1,34 @@ +# Time: O(k), where k is the steps to be happy number +# Space: O(k) +# +# Write an algorithm to determine if a number is "happy". +# +# A happy number is a number defined by the following process: +# Starting with any positive integer, replace the number by the sum +# of the squares of its digits, and repeat the process until +# the number equals 1 (where it will stay), or it loops endlessly +# in a cycle which does not include 1. Those numbers for which +# this process ends in 1 are happy numbers. +# +# Example: 19 is a happy number +# +# 1^2 + 9^2 = 82 +# 8^2 + 2^2 = 68 +# 6^2 + 8^2 = 100 +# 1^2 + 0^2 + 0^2 = 1 +# +class Solution: + # @param {integer} n + # @return {boolean} + def isHappy(self, n): + lookup = {} + while n != 1 and n not in lookup: + lookup[n] = True + n = self.nextNumber(n) + return n == 1 + + def nextNumber(self, n): + new = 0 + for char in str(n): + new += int(char)**2 + return new diff --git a/Python/heaters.py b/Python/heaters.py new file mode 100644 index 000000000..c2f8cdecd --- /dev/null +++ b/Python/heaters.py @@ -0,0 +1,50 @@ +# Time: O((m + n) * logn), m is the number of the houses, n is the number of the heaters. +# Space: O(1) + +# Winter is coming! Your first job during the contest is to +# design a standard heater with fixed warm radius to warm all the houses. +# +# Now, you are given positions of houses and heaters on a horizontal line, +# find out minimum radius of heaters so that all houses could be covered by those heaters. +# +# So, your input will be the positions of houses and heaters seperately, +# and your expected output will be the minimum radius standard of heaters. +# +# Note: +# Numbers of houses and heaters you are given are non-negative and will not exceed 25000. +# Positions of houses and heaters you are given are non-negative and will not exceed 10^9. +# As long as a house is in the heaters' warm radius range, it can be warmed. +# All the heaters follow your radius standard and the warm radius will the same. +# Example 1: +# Input: [1,2,3],[2] +# Output: 1 +# Explanation: The only heater was placed in the position 2, and if we use the radius 1 standard, +# then all the houses can be warmed. +# Example 2: +# Input: [1,2,3,4],[1,4] +# Output: 1 +# Explanation: The two heater was placed in the position 1 and 4. We need to use radius 1 standard, +# then all the houses can be warmed. + +import bisect + + +class Solution(object): + def findRadius(self, houses, heaters): + """ + :type houses: List[int] + :type heaters: List[int] + :rtype: int + """ + heaters.sort() + min_radius = 0 + for house in houses: + equal_or_larger = bisect.bisect_left(heaters, house) + curr_radius = float("inf") + if equal_or_larger != len(heaters): + curr_radius = heaters[equal_or_larger] - house + if equal_or_larger != 0: + smaller = equal_or_larger-1 + curr_radius = min(curr_radius, house - heaters[smaller]) + min_radius = max(min_radius, curr_radius) + return min_radius diff --git a/Python/house-robber-ii.py b/Python/house-robber-ii.py new file mode 100644 index 000000000..fdbe57a29 --- /dev/null +++ b/Python/house-robber-ii.py @@ -0,0 +1,38 @@ +from __future__ import print_function +# Time: O(n) +# Space: O(1) +# +# Note: This is an extension of House Robber. +# +# After robbing those houses on that street, the thief has found himself a new place +# for his thievery so that he will not get too much attention. This time, all houses +# at this place are arranged in a circle. That means the first house is the neighbor +# of the last one. Meanwhile, the security system for these houses remain the same as +# for those in the previous street. +# +# Given a list of non-negative integers representing the amount of money of each house, +# determine the maximum amount of money you can rob tonight without alerting the police. +# +class Solution: + # @param {integer[]} nums + # @return {integer} + def rob(self, nums): + if len(nums) == 0: + return 0 + + if len(nums) == 1: + return nums[0] + + return max(self.robRange(nums, 0, len(nums) - 1),\ + self.robRange(nums, 1, len(nums))) + + def robRange(self, nums, start, end): + num_i, num_i_1 = nums[start], 0 + for i in xrange(start + 1, end): + num_i_1, num_i_2 = num_i, num_i_1 + num_i = max(nums[i] + num_i_2, num_i_1); + + return num_i + +if __name__ == '__main__': + print(Solution().rob([8,4,8,5,9,6,5,4,4,10])) diff --git a/Python/house-robber-iii.py b/Python/house-robber-iii.py new file mode 100644 index 000000000..a1ed8c3a1 --- /dev/null +++ b/Python/house-robber-iii.py @@ -0,0 +1,49 @@ +# Time: O(n) +# Space: O(h) + +# The thief has found himself a new place for his thievery again. +# There is only one entrance to this area, called the "root." +# Besides the root, each house has one and only one parent house. +# After a tour, the smart thief realized that "all houses in this +# place forms a binary tree". It will automatically contact the +# police if two directly-linked houses were broken into on the +# same night. +# +# Determine the maximum amount of money the thief can rob tonight +# without alerting the police. +# +# Example 1: +# 3 +# / \ +# 2 3 +# \ \ +# 3 1 +# Maximum amount of money the thief can rob = 3 + 3 + 1 = 7. +# Example 2: +# 3 +# / \ +# 4 5 +# / \ \ +# 1 3 1 +# Maximum amount of money the thief can rob = 4 + 5 = 9. +# +# Definition for a binary tree node. +# class TreeNode(object): +# def __init__(self, x): +# self.val = x +# self.left = None +# self.right = None + +class Solution(object): + def rob(self, root): + """ + :type root: TreeNode + :rtype: int + """ + def robHelper(root): + if not root: + return (0, 0) + left, right = robHelper(root.left), robHelper(root.right) + return (root.val + left[1] + right[1], max(left) + max(right)) + + return max(robHelper(root)) diff --git a/Python/house-robber.py b/Python/house-robber.py new file mode 100644 index 000000000..4dfec5567 --- /dev/null +++ b/Python/house-robber.py @@ -0,0 +1,42 @@ +from __future__ import print_function +# Time: O(n) +# Space: O(1) +# +# You are a professional robber planning to rob houses along a street. +# Each house has a certain amount of money stashed, the only constraint stopping you +# from robbing each of them is that adjacent houses have security system connected +# and it will automatically contact the police if two adjacent houses were broken into on the same night. +# +# Given a list of non-negative integers representing the amount of money of each house, +# determine the maximum amount of money you can rob tonight without alerting the police. +# +class Solution: + # @param num, a list of integer + # @return an integer + def rob(self, num): + if len(num) == 0: + return 0 + + if len(num) == 1: + return num[0] + + num_i, num_i_1 = max(num[1], num[0]), num[0] + for i in xrange(2, len(num)): + num_i_1, num_i_2 = num_i, num_i_1 + num_i = max(num[i] + num_i_2, num_i_1); + + return num_i + + def rob2(self, nums): + """ + :type nums: List[int] + :rtype: int + """ + last, now = 0, 0 + for i in nums: + last, now = now, max(last + i, now) + return now + + +if __name__ == '__main__': + print(Solution().rob([8,4,8,5,9,6,5,4,4,10])) diff --git a/Python/image-overlap.py b/Python/image-overlap.py new file mode 100644 index 000000000..ba533a8ad --- /dev/null +++ b/Python/image-overlap.py @@ -0,0 +1,50 @@ +# Time: O(n^4) +# Space: O(n^2) + +# Two images A and B are given, represented as binary, +# square matrices of the same size. +# (A binary matrix has only 0s and 1s as values.) +# +# We translate one image however we choose (sliding it left, right, up, +# or down any number of units), and place it on top of the other image. +# After, the overlap of this translation is the number of positions that +# have a 1 in both images. +# (Note also that a translation does not include any kind of rotation.) +# +# What is the largest possible overlap? +# +# Example 1: +# +# Input: A = [[1,1,0], +# [0,1,0], +# [0,1,0]] +# B = [[0,0,0], +# [0,1,1], +# [0,0,1]] +# Output: 3 +# Explanation: We slide A to right by 1 unit and down by 1 unit. +# +# Notes: +# 1. 1 <= A.length = A[0].length = B.length = B[0].length <= 30 +# 2. 0 <= A[i][j], B[i][j] <= 1 + + +class Solution(object): + def largestOverlap(self, A, B): + """ + :type A: List[List[int]] + :type B: List[List[int]] + :rtype: int + """ + count = [0] * (2*len(A)-1)**2 + for i, row in enumerate(A): + for j, v in enumerate(row): + if not v: + continue + for i2, row2 in enumerate(B): + for j2, v2 in enumerate(row2): + if not v2: + continue + count[(len(A)-1+i-i2)*(2*len(A)-1) + + len(A)-1+j-j2] += 1 + return max(count) diff --git a/Python/image-smoother.py b/Python/image-smoother.py new file mode 100644 index 000000000..cd51b2fbc --- /dev/null +++ b/Python/image-smoother.py @@ -0,0 +1,46 @@ +# Time: O(m * n) +# Space: O(1) + +# Given a 2D integer matrix M representing the gray scale of an image, +# you need to design a smoother to make the gray scale of each cell becomes +# the average gray scale (rounding down) of all the 8 surrounding cells and itself. +# If a cell has less than 8 surrounding cells, then use as many as you can. +# +# Example 1: +# Input: +# [[1,1,1], +# [1,0,1], +# [1,1,1]] +# Output: +# [[0, 0, 0], +# [0, 0, 0], +# [0, 0, 0]] +# Explanation: +# For the point (0,0), (0,2), (2,0), (2,2): floor(3/4) = floor(0.75) = 0 +# For the point (0,1), (1,0), (1,2), (2,1): floor(5/6) = floor(0.83333333) = 0 +# For the point (1,1): floor(8/9) = floor(0.88888889) = 0 +# Note: +# The value in the given matrix is in the range of [0, 255]. +# The length and width of the given matrix are in the range of [1, 150]. + +class Solution(object): + def imageSmoother(self, M): + """ + :type M: List[List[int]] + :rtype: List[List[int]] + """ + def getGray(M, i, j): + total, count = 0, 0.0 + for r in xrange(-1, 2): + for c in xrange(-1, 2): + ii, jj = i + r, j + c + if 0 <= ii < len(M) and 0 <= jj < len(M[0]): + total += M[ii][jj] + count += 1.0 + return int(total / count) + + result = [[0 for _ in xrange(len(M[0]))] for _ in xrange(len(M))] + for i in xrange(len(M)): + for j in xrange(len(M[0])): + result[i][j] = getGray(M, i, j); + return result diff --git a/Python/implement-magic-dictionary.py b/Python/implement-magic-dictionary.py new file mode 100644 index 000000000..7dd43640c --- /dev/null +++ b/Python/implement-magic-dictionary.py @@ -0,0 +1,75 @@ +# Time: O(n), n is the length of the word +# Space: O(d) + +# Implement a magic directory with buildDict, and search methods. +# +# For the method buildDict, you'll be given a list of non-repetitive words to build a dictionary. +# +# For the method search, you'll be given a word, and judge whether +# if you modify exactly one character into another character in this word, +# the modified word is in the dictionary you just built. +# +# Example 1: +# Input: buildDict(["hello", "leetcode"]), Output: Null +# Input: search("hello"), Output: False +# Input: search("hhllo"), Output: True +# Input: search("hell"), Output: False +# Input: search("leetcoded"), Output: False +# Note: +# You may assume that all the inputs are consist of lowercase letters a-z. +# For contest purpose, the test data is rather small by now. +# You could think about highly efficient algorithm after the contest. +# Please remember to RESET your class variables declared in class MagicDictionary, +# as static/class variables are persisted across multiple test cases. Please see here for more details. + +import collections + + +class MagicDictionary(object): + + def __init__(self): + """ + Initialize your data structure here. + """ + _trie = lambda: collections.defaultdict(_trie) + self.trie = _trie() + + + def buildDict(self, dictionary): + """ + Build a dictionary through a list of words + :type dictionary: List[str] + :rtype: void + """ + for word in dictionary: + reduce(dict.__getitem__, word, self.trie).setdefault("_end") + + + def search(self, word): + """ + Returns if there is any word in the trie that equals to the given word after modifying exactly one character + :type word: str + :rtype: bool + """ + def find(word, curr, i, mistakeAllowed): + if i == len(word): + return "_end" in curr and not mistakeAllowed + + if word[i] not in curr: + return any(find(word, curr[c], i+1, False) for c in curr if c != "_end") \ + if mistakeAllowed else False + + if mistakeAllowed: + return find(word, curr[word[i]], i+1, True) or \ + any(find(word, curr[c], i+1, False) \ + for c in curr if c not in ("_end", word[i])) + return find(word, curr[word[i]], i+1, False) + + return find(word, self.trie, 0, True) + + + +# Your MagicDictionary object will be instantiated and called as such: +# obj = MagicDictionary() +# obj.buildDict(dict) +# param_2 = obj.search(word) diff --git a/Python/implement-queue-using-stacks.py b/Python/implement-queue-using-stacks.py new file mode 100644 index 000000000..9c32c8197 --- /dev/null +++ b/Python/implement-queue-using-stacks.py @@ -0,0 +1,45 @@ +# Time: O(1), amortized +# Space: O(n) +# +# Implement the following operations of a queue using stacks. +# +# push(x) -- Push element x to the back of queue. +# pop() -- Removes the element from in front of queue. +# peek() -- Get the front element. +# empty() -- Return whether the queue is empty. +# +# Notes: +# You must use only standard operations of a stack +# -- which means only push to top, peek/pop from top, size, and is empty operations are valid. +# Depending on your language, stack may not be supported natively. +# You may simulate a stack by using a list or deque (double-ended queue), +# as long as you use only standard operations of a stack. +# You may assume that all operations are valid +# (for example, no pop or peek operations will be called on an empty queue). +# + +class Queue: + # initialize your data structure here. + def __init__(self): + self.A, self.B = [], [] + + # @param x, an integer + # @return nothing + def push(self, x): + self.A.append(x) + + # @return an integer + def pop(self): + self.peek() + return self.B.pop() + + # @return an integer + def peek(self): + if not self.B: + while self.A: + self.B.append(self.A.pop()) + return self.B[-1] + + # @return an boolean + def empty(self): + return not self.A and not self.B diff --git a/Python/implement-rand10-using-rand7.py b/Python/implement-rand10-using-rand7.py new file mode 100644 index 000000000..eacada556 --- /dev/null +++ b/Python/implement-rand10-using-rand7.py @@ -0,0 +1,73 @@ +# Time: O(1.189), counted by statistics, limit would be O(log10/log7) = O(1.183) +# Space: O(1) + +# Given a function rand7 which generates a uniform random integer in the range 1 to 7, +# write a function rand10 which generates a uniform random integer in the range 1 to 10. +# +# Do NOT use system's Math.random(). +# +# Example 1: +# +# Input: 1 +# Output: [7] +# Example 2: +# +# Input: 2 +# Output: [8,4] +# Example 3: +# +# Input: 3 +# Output: [8,1,10] +# +# Note: +# +# rand7 is predefined. +# Each testcase has one argument: n, the number of times that rand10 is called. +# +# Follow up: +# - What is the expected value for the number of calls to rand7() function? +# - Could you minimize the number of calls to rand7()? +# +# The rand7() API is already defined for you. + +import random + + +def rand7(): + return random.randint(1, 7) + + +# Reference: https://leetcode.com/problems/implement-rand10-using-rand7/discuss/151567/C++JavaPython-Average-1.199-Call-rand7-Per-rand10 +class Solution(object): + def __init__(self): + self.__cache = [] + + def rand10(self): + """ + :rtype: int + """ + def generate(cache): + n = 32 + curr = sum((rand7()-1) * (7**i) for i in xrange(n)) + rang = 7**n + while curr < rang//10*10: + cache.append(curr%10+1) + curr /= 10 + rang /= 10 + + while not self.__cache: + generate(self.__cache) + return self.__cache.pop() + + +# Time: O(2 * (1 + (9/49) + (9/49)^2 + ...)) = O(2/(1-(9/49)) = O(2.45) +# Space: O(1) +class Solution2(object): + def rand10(self): + """ + :rtype: int + """ + while True: + x = (rand7()-1)*7 + (rand7()-1) + if x < 40: + return x%10 + 1 diff --git a/Python/implement-stack-using-queues.py b/Python/implement-stack-using-queues.py new file mode 100644 index 000000000..8713e606f --- /dev/null +++ b/Python/implement-stack-using-queues.py @@ -0,0 +1,95 @@ +# Time: push: O(n), pop: O(1), top: O(1) +# Space: O(n) +# +# Implement the following operations of a stack using queues. +# +# push(x) -- Push element x onto stack. +# pop() -- Removes the element on top of the stack. +# top() -- Get the top element. +# empty() -- Return whether the stack is empty. +# Notes: +# You must use only standard operations of a queue -- which +# means only push to back, peek/pop from front, size, and is +# empty operations are valid. +# Depending on your language, queue may not be supported natively. +# You may simulate a queue by using a list or deque (double-ended +# queue), as long as you use only standard operations of a queue. +# You may assume that all operations are valid (for example, no pop +# or top operations will be called on an empty stack). + +import collections + + +class Queue: + def __init__(self): + self.data = collections.deque() + + def push(self, x): + self.data.append(x) + + def peek(self): + return self.data[0] + + def pop(self): + return self.data.popleft() + + def size(self): + return len(self.data) + + def empty(self): + return len(self.data) == 0 + + +class Stack: + # initialize your data structure here. + def __init__(self): + self.q_ = Queue() + + # @param x, an integer + # @return nothing + def push(self, x): + self.q_.push(x) + for _ in xrange(self.q_.size() - 1): + self.q_.push(self.q_.pop()) + + # @return nothing + def pop(self): + self.q_.pop() + + # @return an integer + def top(self): + return self.q_.peek() + + # @return an boolean + def empty(self): + return self.q_.empty() + + +# Time: push: O(1), pop: O(n), top: O(1) +# Space: O(n) +class Stack2: + # initialize your data structure here. + def __init__(self): + self.q_ = Queue() + self.top_ = None + + # @param x, an integer + # @return nothing + def push(self, x): + self.q_.push(x) + self.top_ = x + + # @return nothing + def pop(self): + for _ in xrange(self.q_.size() - 1): + self.top_ = self.q_.pop() + self.q_.push(self.top_) + self.q_.pop() + + # @return an integer + def top(self): + return self.top_ + + # @return an boolean + def empty(self): + return self.q_.empty() diff --git a/Python/implement-strstr.py b/Python/implement-strstr.py index 543835dd4..b7f812d80 100644 --- a/Python/implement-strstr.py +++ b/Python/implement-strstr.py @@ -1,31 +1,27 @@ -# Time: O(n + m) -# Space: O(m) +from __future__ import print_function +# Time: O(n + k) +# Space: O(k) # # Implement strStr(). -# +# # Returns a pointer to the first occurrence of needle in haystack, # or null if needle is not part of haystack. # # Wiki of KMP algorithm: # http://en.wikipedia.org/wiki/Knuth-Morris-Pratt_algorithm -class Solution: - # @param haystack, a string - # @param needle, a string - # @return a string or None +class Solution(object): def strStr(self, haystack, needle): - if len(haystack) < len(needle): - return None - - if len(needle) == 0: - return haystack - - i = self.KMP(haystack, needle) - if i > -1: - return haystack[i:] - else: - return None - + """ + :type haystack: str + :type needle: str + :rtype: int + """ + if not needle: + return 0 + + return self.KMP(haystack, needle) + def KMP(self, text, pattern): prefix = self.getPrefix(pattern) j = -1 @@ -37,10 +33,10 @@ def KMP(self, text, pattern): if j == len(pattern) - 1: return i - j return -1 - + def getPrefix(self, pattern): prefix = [-1] * len(pattern) - j = - 1 + j = -1 for i in xrange(1, len(pattern)): while j > -1 and pattern[j + 1] != pattern[i]: j = prefix[j] @@ -48,19 +44,23 @@ def getPrefix(self, pattern): j += 1 prefix[i] = j return prefix - -# Time: (n * m) -# Space: (1) -class Solution2: - # @param haystack, a string - # @param needle, a string - # @return a string or None + + +# Time: O(n * k) +# Space: O(k) +class Solution2(object): def strStr(self, haystack, needle): + """ + :type haystack: str + :type needle: str + :rtype: int + """ for i in xrange(len(haystack) - len(needle) + 1): if haystack[i : i + len(needle)] == needle: - return haystack[i:] - return None - + return i + return -1 + + if __name__ == "__main__": - print Solution().strStr("a", "") - print Solution().strStr("abababcdab", "ababcdx") + print(Solution().strStr("a", "")) + print(Solution().strStr("abababcdab", "ababcdx")) diff --git a/Python/implement-trie-prefix-tree.py b/Python/implement-trie-prefix-tree.py new file mode 100644 index 000000000..a062f91ba --- /dev/null +++ b/Python/implement-trie-prefix-tree.py @@ -0,0 +1,61 @@ +# Time: O(n), per operation +# Space: O(1) +# +# Implement a trie with insert, search, and startsWith methods. +# +# Note: +# You may assume that all inputs are consist of lowercase letters a-z. +# + +class TrieNode: + # Initialize your data structure here. + def __init__(self): + self.is_string = False + self.leaves = {} + + +class Trie: + + def __init__(self): + self.root = TrieNode() + + # @param {string} word + # @return {void} + # Inserts a word into the trie. + def insert(self, word): + cur = self.root + for c in word: + if not c in cur.leaves: + cur.leaves[c] = TrieNode() + cur = cur.leaves[c] + cur.is_string = True + + # @param {string} word + # @return {boolean} + # Returns if the word is in the trie. + def search(self, word): + node = self.childSearch(word) + if node: + return node.is_string + return False + + # @param {string} prefix + # @return {boolean} + # Returns if there is any word in the trie + # that starts with the given prefix. + def startsWith(self, prefix): + return self.childSearch(prefix) is not None + + def childSearch(self, word): + cur = self.root + for c in word: + if c in cur.leaves: + cur = cur.leaves[c] + else: + return None + return cur + +# Your Trie object will be instantiated and called as such: +# trie = Trie() +# trie.insert("somestring") +# trie.search("key") diff --git a/Python/increasing-subsequences.py b/Python/increasing-subsequences.py new file mode 100644 index 000000000..6639959bc --- /dev/null +++ b/Python/increasing-subsequences.py @@ -0,0 +1,38 @@ +# Time: O(n * 2^n) +# Space: O(n^2) + +# Given an integer array, your task is +# to find all the different possible increasing +# subsequences of the given array, +# and the length of an increasing subsequence should be at least 2 . +# +# Example: +# Input: [4, 6, 7, 7] +# Output: [[4, 6], [4, 7], [4, 6, 7], [4, 6, 7, 7], [6, 7], [6, 7, 7], [7,7], [4,7,7]] +# Note: +# The length of the given array will not exceed 15. +# The range of integer in the given array is [-100,100]. +# The given array may contain duplicates, +# and two equal integers should also be considered as a special case of increasing sequence. + +class Solution(object): + def findSubsequences(self, nums): + """ + :type nums: List[int] + :rtype: List[List[int]] + """ + def findSubsequencesHelper(nums, pos, seq, result): + if len(seq) >= 2: + result.append(list(seq)) + lookup = set() + for i in xrange(pos, len(nums)): + if (not seq or nums[i] >= seq[-1]) and \ + nums[i] not in lookup: + lookup.add(nums[i]) + seq.append(nums[i]) + findSubsequencesHelper(nums, i+1, seq, result) + seq.pop() + + result, seq = [], [] + findSubsequencesHelper(nums, 0, seq, result) + return result diff --git a/Python/increasing-triplet-subsequence.py b/Python/increasing-triplet-subsequence.py new file mode 100644 index 000000000..e5a940433 --- /dev/null +++ b/Python/increasing-triplet-subsequence.py @@ -0,0 +1,57 @@ +# Time: O(n) +# Space: O(1) + +# Given an unsorted array return whether an increasing +# subsequence of length 3 exists or not in the array. + +# Formally the function should: +# Return true if there exists i, j, k +# such that arr[i] < arr[j] < arr[k] +# given 0 <= i < j < k <= n-1 else return false. +# Your algorithm should run in O(n) time complexity and O(1) space complexity. + +# Examples: +# Given [1, 2, 3, 4, 5], +# return true. + +# Given [5, 4, 3, 2, 1], +# return false. + +import bisect + + +class Solution(object): + def increasingTriplet(self, nums): + """ + :type nums: List[int] + :rtype: bool + """ + min_num, a, b = float("inf"), float("inf"), float("inf") + for c in nums: + if min_num >= c: + min_num = c + elif b >= c: + a, b = min_num, c + else: # a < b < c + return True + return False + +# Time: O(n * logk) +# Space: O(k) +# Generalization of k-uplet. +class Solution_Generalization(object): + def increasingTriplet(self, nums): + """ + :type nums: List[int] + :rtype: bool + """ + def increasingKUplet(nums, k): + inc = [float('inf')] * (k - 1) + for num in nums: + i = bisect.bisect_left(inc, num) + if i >= k - 1: + return True + inc[i] = num + return k == 0 + + return increasingKUplet(nums, 3) diff --git a/Python/inorder-successor-in-bst.py b/Python/inorder-successor-in-bst.py new file mode 100644 index 000000000..a3d6ee653 --- /dev/null +++ b/Python/inorder-successor-in-bst.py @@ -0,0 +1,34 @@ +# Time: O(h) +# Space: O(1) + +# Definition for a binary tree node. +# class TreeNode(object): +# def __init__(self, x): +# self.val = x +# self.left = None +# self.right = None + +class Solution(object): + def inorderSuccessor(self, root, p): + """ + :type root: TreeNode + :type p: TreeNode + :rtype: TreeNode + """ + # If it has right subtree. + if p and p.right: + p = p.right + while p.left: + p = p.left + return p + + # Search from root. + successor = None + while root and root != p: + if root.val > p.val: + successor = root + root = root.left + else: + root = root.right + + return successor diff --git a/Python/insert-delete-getrandom-o1-duplicates-allowed.py b/Python/insert-delete-getrandom-o1-duplicates-allowed.py new file mode 100644 index 000000000..f0dbf9c63 --- /dev/null +++ b/Python/insert-delete-getrandom-o1-duplicates-allowed.py @@ -0,0 +1,93 @@ +# Time: O(1) +# Space: O(n) + +# Design a data structure that supports all following operations in average O(1) time. +# +# Note: Duplicate elements are allowed. +# insert(val): Inserts an item val to the collection. +# remove(val): Removes an item val from the collection if present. +# getRandom: Returns a random element from current collection of elements. +# The probability of each element being returned is linearly related to +# the number of same value the collection contains. +# Example: +# +# // Init an empty collection. +# RandomizedCollection collection = new RandomizedCollection(); +# +# // Inserts 1 to the collection. Returns true as the collection did not contain 1. +# collection.insert(1); +# +# // Inserts another 1 to the collection. Returns false as the collection contained 1. Collection now contains [1,1]. +# collection.insert(1); +# +# // Inserts 2 to the collection, returns true. Collection now contains [1,1,2]. +# collection.insert(2); +# +# // getRandom should return 1 with the probability 2/3, and returns 2 with the probability 1/3. +# collection.getRandom(); +# +# // Removes 1 from the collection, returns true. Collection now contains [1,2]. +# collection.remove(1); +# +# // getRandom should return 1 and 2 both equally likely. +# collection.getRandom(); + +from random import randint +from collections import defaultdict + +class RandomizedCollection(object): + + def __init__(self): + """ + Initialize your data structure here. + """ + self.__list = [] + self.__used = defaultdict(list) + + + def insert(self, val): + """ + Inserts a value to the collection. Returns true if the collection did not already contain the specified element. + :type val: int + :rtype: bool + """ + has = val in self.__used + + self.__list += val, + self.__used[val] += len(self.__list)-1, + + return not has + + + def remove(self, val): + """ + Removes a value from the collection. Returns true if the collection contained the specified element. + :type val: int + :rtype: bool + """ + if val not in self.__used: + return False + + self.__used[self.__list[-1]][-1] = self.__used[val][-1] + self.__list[self.__used[val][-1]], self.__list[-1] = self.__list[-1], self.__list[self.__used[val][-1]] + + self.__used[val].pop() + if not self.__used[val]: + self.__used.pop(val) + self.__list.pop() + + return True + + def getRandom(self): + """ + Get a random element from the collection. + :rtype: int + """ + return self.__list[randint(0, len(self.__list)-1)] + + +# Your RandomizedCollection object will be instantiated and called as such: +# obj = RandomizedCollection() +# param_1 = obj.insert(val) +# param_2 = obj.remove(val) +# param_3 = obj.getRandom() diff --git a/Python/insert-delete-getrandom-o1.py b/Python/insert-delete-getrandom-o1.py new file mode 100644 index 000000000..9ae83a0be --- /dev/null +++ b/Python/insert-delete-getrandom-o1.py @@ -0,0 +1,94 @@ +# Time: O(1) +# Space: O(n) + +# Design a data structure that supports all following operations in O(1) time. +# +# insert(val): Inserts an item val to the set if not already present. +# remove(val): Removes an item val from the set if present. +# getRandom: Returns a random element from current set of elements. +# Each element must have the same probability of being returned. +# +# Example: +# +# // Init an empty set. +# RandomizedSet randomSet = new RandomizedSet(); +# +# // Inserts 1 to the set. Returns true as 1 was inserted successfully. +# randomSet.insert(1); +# +# // Returns false as 2 does not exist in the set. +# randomSet.remove(2); +# +# // Inserts 2 to the set, returns true. Set now contains [1,2]. +# randomSet.insert(2); +# +# // getRandom should return either 1 or 2 randomly. +# randomSet.getRandom(); +# +# // Removes 1 from the set, returns true. Set now contains [2]. +# randomSet.remove(1); +# +# // 2 was already in the set, so return false. +# randomSet.insert(2); +# +# // Since 1 is the only number in the set, getRandom always return 1. +# randomSet.getRandom(); + + +from random import randint + +class RandomizedSet(object): + + def __init__(self): + """ + Initialize your data structure here. + """ + self.__set = [] + self.__used = {} + + + def insert(self, val): + """ + Inserts a value to the set. Returns true if the set did not already contain the specified element. + :type val: int + :rtype: bool + """ + if val in self.__used: + return False + + self.__set += val, + self.__used[val] = len(self.__set)-1 + + return True + + + def remove(self, val): + """ + Removes a value from the set. Returns true if the set contained the specified element. + :type val: int + :rtype: bool + """ + if val not in self.__used: + return False + + self.__used[self.__set[-1]] = self.__used[val] + self.__set[self.__used[val]], self.__set[-1] = self.__set[-1], self.__set[self.__used[val]] + + self.__used.pop(val) + self.__set.pop() + + return True + + def getRandom(self): + """ + Get a random element from the set. + :rtype: int + """ + return self.__set[randint(0, len(self.__set)-1)] + + +# Your RandomizedSet object will be instantiated and called as such: +# obj = RandomizedSet() +# param_1 = obj.insert(val) +# param_2 = obj.remove(val) +# param_3 = obj.getRandom() diff --git a/Python/insert-interval.py b/Python/insert-interval.py index f108a7078..695f19cdd 100644 --- a/Python/insert-interval.py +++ b/Python/insert-interval.py @@ -1,47 +1,49 @@ +from __future__ import print_function # Time: O(n) # Space: O(1) -# -# Given a set of non-overlapping intervals, insert a new interval into the intervals (merge if necessary). -# + +# Given a set of non-overlapping intervals, insert a new interval into the +# intervals (merge if necessary). # You may assume that the intervals were initially sorted according to their start times. -# +# # Example 1: # Given intervals [1,3],[6,9], insert and merge [2,5] in as [1,5],[6,9]. -# +# # Example 2: # Given [1,2],[3,5],[6,7],[8,10],[12,16], insert and merge [4,9] in as [1,2],[3,10],[12,16]. -# -# This is because the new interval [4,9] overlaps with [3,5],[6,7],[8,10]. # +# This is because the new interval [4,9] overlaps with [3,5],[6,7],[8,10]. # Definition for an interval. class Interval: def __init__(self, s=0, e=0): self.start = s self.end = e - + def __repr__(self): return "[{}, {}]".format(self.start, self.end) -class Solution: - # @param intervals, a list of Intervals - # @param newInterval, a Interval - # @return a list of Interval + +class Solution(object): def insert(self, intervals, newInterval): - return self.merge(intervals + [newInterval]) - - def merge(self, intervals): - if len(intervals) == 0: - return intervals - intervals.sort(key = lambda x: x.start) - result = [intervals[0]] - for i in range(1, len(intervals)): - prev, current = result[-1], intervals[i] - if current.start <= prev.end: - prev.end = max(prev.end, current.end) - else: - result.append(current) + """ + :type intervals: List[Interval] + :type newInterval: Interval + :rtype: List[Interval] + """ + result = [] + i = 0 + while i < len(intervals) and newInterval.start > intervals[i].end: + result += intervals[i], + i += 1 + while i < len(intervals) and newInterval.end >= intervals[i].start: + newInterval = Interval(min(newInterval.start, intervals[i].start), \ + max(newInterval.end, intervals[i].end)) + i += 1 + result += newInterval, + result += intervals[i:] return result + if __name__ == "__main__": - print Solution().insert([Interval(1, 2), Interval(3, 5), Interval(6, 7), Interval(8, 10), Interval(12, 16)], Interval(4, 9)) \ No newline at end of file + print(Solution().insert([Interval(1, 2), Interval(3, 5), Interval(6, 7), Interval(8, 10), Interval(12, 16)], Interval(4, 9))) diff --git a/Python/insert-into-a-binary-search-tree.py b/Python/insert-into-a-binary-search-tree.py new file mode 100644 index 000000000..c2ae3738d --- /dev/null +++ b/Python/insert-into-a-binary-search-tree.py @@ -0,0 +1,86 @@ +# Time: O(h) +# Space: O(1) + +# Given the root node of a binary search tree (BST) and +# a value to be inserted into the tree, insert the value into the BST. +# Return the root node of the BST after the insertion. It is guaranteed +# that the new value does not exist in the original BST. +# +# Note that there may exist multiple valid ways for the insertion, +# as long as the tree remains a BST after insertion. You can return any of them. +# +# For example, +# +# Given the tree: +# 4 +# / \ +# 2 7 +# / \ +# 1 3 +# And the value to insert: 5 +# You can return this binary search tree: +# +# 4 +# / \ +# 2 7 +# / \ / +# 1 3 5 +# This tree is also valid: +# +# 5 +# / \ +# 2 7 +# / \ +# 1 3 +# \ +# 4 + + +# Definition for a binary tree node. +class TreeNode(object): + def __init__(self, x): + self.val = x + self.left = None + self.right = None + + +class Solution(object): + def insertIntoBST(self, root, val): + """ + :type root: TreeNode + :type val: int + :rtype: TreeNode + """ + curr, parent = root, None + while curr: + parent = curr + if val <= curr.val: + curr = curr.left + else: + curr = curr.right + if not parent: + root = TreeNode(val) + elif val <= parent.val: + parent.left = TreeNode(val) + else: + parent.right = TreeNode(val) + return root + + +# Time: O(h) +# Space: O(h) +class Solution2(object): + def insertIntoBST(self, root, val): + """ + :type root: TreeNode + :type val: int + :rtype: TreeNode + """ + if not root: + root = TreeNode(val) + else: + if val <= root.val: + root.left = self.insertIntoBST(root.left, val) + else: + root.right = self.insertIntoBST(root.right, val) + return root diff --git a/Python/insert-into-a-cyclic-sorted-list.py b/Python/insert-into-a-cyclic-sorted-list.py new file mode 100644 index 000000000..b1f6e7a0c --- /dev/null +++ b/Python/insert-into-a-cyclic-sorted-list.py @@ -0,0 +1,43 @@ +# Time: O(n) +# Space: O(1) + +# Definition for a Node. +class Node(object): + def __init__(self, val, next): + self.val = val + self.next = next + + +class Solution(object): + def insert(self, head, insertVal): + """ + :type head: Node + :type insertVal: int + :rtype: Node + """ + def insertAfter(node, val): + node.next = Node(val, node.next) + + if not head: + node = Node(insertVal, None) + node.next = node + return node + + curr = head + while True: + if curr.val < curr.next.val: + if curr.val <= insertVal and \ + insertVal <= curr.next.val: + insertAfter(curr, insertVal) + break + elif curr.val > curr.next.val: + if curr.val <= insertVal or \ + insertVal <= curr.next.val: + insertAfter(curr, insertVal) + break + else: + if curr.next == head: + insertAfter(curr, insertVal) + break + curr = curr.next + return head diff --git a/Python/insertion-sort-list.py b/Python/insertion-sort-list.py index fa7c9616c..d92f95881 100644 --- a/Python/insertion-sort-list.py +++ b/Python/insertion-sort-list.py @@ -1,3 +1,4 @@ +from __future__ import print_function # Time: O(n ^ 2) # Space: O(1) # @@ -9,7 +10,7 @@ class ListNode: def __init__(self, x): self.val = x self.next = None - + def __repr__(self): if self: return "{} -> {}".format(self.val, repr(self.next)) @@ -22,7 +23,7 @@ class Solution: def insertionSortList(self, head): if head is None or self.isSorted(head): return head - + dummy = ListNode(-2147483648) dummy.next = head cur, sorted_tail = head.next, head @@ -31,13 +32,13 @@ def insertionSortList(self, head): while prev.next.val < cur.val: prev = prev.next if prev == sorted_tail: - cur, sorted_tail = cur.next, cur + cur, sorted_tail = cur.next, cur else: cur.next, prev.next, sorted_tail.next = prev.next, cur, cur.next cur = sorted_tail.next - + return dummy.next - + def isSorted(self, head): while head and head.next: if head.val > head.next.val: @@ -49,4 +50,4 @@ def isSorted(self, head): head = ListNode(3) head.next = ListNode(2) head.next.next = ListNode(1) - print Solution().insertionSortList(head) + print(Solution().insertionSortList(head)) diff --git a/Python/integer-break.py b/Python/integer-break.py new file mode 100644 index 000000000..ca3bab0e5 --- /dev/null +++ b/Python/integer-break.py @@ -0,0 +1,77 @@ +# Time: O(logn), pow is O(logn). +# Space: O(1) + +# Given a positive integer n, break it into the sum of +# at least two positive integers and maximize the product +# of those integers. Return the maximum product you can get. +# +# For example, given n = 2, return 1 (2 = 1 + 1); given n = 10, +# return 36 (10 = 3 + 3 + 4). +# +# Note: you may assume that n is not less than 2. +# +# Hint: +# +# There is a simple O(n) solution to this problem. +# You may check the breaking results of n ranging from 7 to 10 +# to discover the regularities. + +class Solution(object): + def integerBreak(self, n): + """ + :type n: int + :rtype: int + """ + if n < 4: + return n - 1 + + # Proof. + # 1. Let n = a1 + a2 + ... + ak, product = a1 * a2 * ... * ak + # - For each ai >= 4, we can always maximize the product by: + # ai <= 2 * (ai - 2) + # - For each aj >= 5, we can always maximize the product by: + # aj <= 3 * (aj - 3) + # + # Conclusion 1: + # - For n >= 4, the max of the product must be in the form of + # 3^a * 2^b, s.t. 3a + 2b = n + # + # 2. To maximize the product = 3^a * 2^b s.t. 3a + 2b = n + # - For each b >= 3, we can always maximize the product by: + # 3^a * 2^b <= 3^(a+2) * 2^(b-3) s.t. 3(a+2) + 2(b-3) = n + # + # Conclusion 2: + # - For n >= 4, the max of the product must be in the form of + # 3^Q * 2^R, 0 <= R < 3 s.t. 3Q + 2R = n + # i.e. + # if n = 3Q + 0, the max of the product = 3^Q * 2^0 + # if n = 3Q + 2, the max of the product = 3^Q * 2^1 + # if n = 3Q + 2*2, the max of the product = 3^Q * 2^2 + + res = 0 + if n % 3 == 0: # n = 3Q + 0, the max is 3^Q * 2^0 + res = 3 ** (n // 3) + elif n % 3 == 2: # n = 3Q + 2, the max is 3^Q * 2^1 + res = 3 ** (n // 3) * 2 + else: # n = 3Q + 4, the max is 3^Q * 2^2 + res = 3 ** (n // 3 - 1) * 4 + return res + + +# Time: O(n) +# Space: O(1) +# DP solution. +class Solution2(object): + def integerBreak(self, n): + """ + :type n: int + :rtype: int + """ + if n < 4: + return n - 1 + + # integerBreak(n) = max(integerBreak(n - 2) * 2, integerBreak(n - 3) * 3) + res = [0, 1, 2, 3] + for i in xrange(4, n + 1): + res[i % 4] = max(res[(i - 2) % 4] * 2, res[(i - 3) % 4] * 3) + return res[n % 4] diff --git a/Python/integer-replacement.py b/Python/integer-replacement.py new file mode 100644 index 000000000..4820bf85a --- /dev/null +++ b/Python/integer-replacement.py @@ -0,0 +1,73 @@ +# Time: O(logn) +# Space: O(1) + +# Given a positive integer n and you can do operations as follow: +# +# If n is even, replace n with n/2. +# If n is odd, you can replace n with either n + 1 or n - 1. +# What is the minimum number of replacements needed for n to become 1? +# +# Example 1: +# +# Input: +# 8 +# +# Output: +# 3 +# +# Explanation: +# 8 -> 4 -> 2 -> 1 +# Example 2: +# +# Input: +# 7 +# +# Output: +# 4 +# +# Explanation: +# 7 -> 8 -> 4 -> 2 -> 1 +# or +# 7 -> 6 -> 3 -> 2 -> 1 + +# Iterative solution. +class Solution(object): + def integerReplacement(self, n): + """ + :type n: int + :rtype: int + """ + result = 0 + while n != 1: + b = n & 3 + if n == 3: + n -= 1 + elif b == 3: + n += 1 + elif b == 1: + n -= 1 + else: + n /= 2 + result += 1 + + return result + + +# Time: O(logn) +# Space: O(logn) +# Recursive solution. +class Solution2(object): + def integerReplacement(self, n): + """ + :type n: int + :rtype: int + """ + if n < 4: + return [0, 0, 1, 2][n] + if n % 4 in (0, 2): + return self.integerReplacement(n / 2) + 1 + elif n % 4 == 1: + return self.integerReplacement((n - 1) / 4) + 3 + else: + return self.integerReplacement((n + 1) / 4) + 3 + diff --git a/Python/integer-to-english-words.py b/Python/integer-to-english-words.py new file mode 100644 index 000000000..6097cf02f --- /dev/null +++ b/Python/integer-to-english-words.py @@ -0,0 +1,64 @@ +# Time: O(logn) = O(1), n is the value of the integer, which is less than 2^31 - 1 +# Space: O(1) +# +# Convert a non-negative integer to its english words representation. +# Given input is guaranteed to be less than 2^31 - 1. +# +# For example, +# 123 -> "One Hundred Twenty Three" +# 12345 -> "Twelve Thousand Three Hundred Forty Five" +# 1234567 -> "One Million Two Hundred Thirty Four Thousand Five Hundred Sixty Seven" +# +# Hint: +# +# 1. Did you see a pattern in dividing the number into chunk of words? +# For example, 123 and 123000. +# +# 2. Group the number by thousands (3 digits). You can write a helper +# function that takes a number less than 1000 and convert just that chunk to words. +# +# 3. There are many edge cases. What are some good test cases? +# Does your code work with input such as 0? Or 1000010? +# (middle chunk is zero and should not be printed out) +# + +class Solution(object): + def numberToWords(self, num): + """ + :type num: int + :rtype: str + """ + if num == 0: + return "Zero" + + lookup = {0: "Zero", 1:"One", 2: "Two", 3: "Three", 4: "Four", \ + 5: "Five", 6: "Six", 7: "Seven", 8: "Eight", 9: "Nine", \ + 10: "Ten", 11: "Eleven", 12: "Twelve", 13: "Thirteen", 14: "Fourteen", \ + 15: "Fifteen", 16: "Sixteen", 17: "Seventeen", 18: "Eighteen", 19: "Nineteen", \ + 20: "Twenty", 30: "Thirty", 40: "Forty", 50: "Fifty", 60: "Sixty", \ + 70: "Seventy", 80: "Eighty", 90: "Ninety"} + unit = ["", "Thousand", "Million", "Billion"] + + res, i = [], 0 + while num: + cur = num % 1000 + if num % 1000: + res.append(self.threeDigits(cur, lookup, unit[i])) + num //= 1000 + i += 1 + return " ".join(res[::-1]) + + def threeDigits(self, num, lookup, unit): + res = [] + if num / 100: + res = [lookup[num / 100] + " " + "Hundred"] + if num % 100: + res.append(self.twoDigits(num % 100, lookup)) + if unit != "": + res.append(unit) + return " ".join(res) + + def twoDigits(self, num, lookup): + if num in lookup: + return lookup[num] + return lookup[(num / 10) * 10] + " " + lookup[num % 10] diff --git a/Python/integer-to-roman.py b/Python/integer-to-roman.py index 3aa53f397..1d4e591f9 100644 --- a/Python/integer-to-roman.py +++ b/Python/integer-to-roman.py @@ -1,26 +1,33 @@ +from __future__ import print_function # Time: O(n) # Space: O(1) # # Given an integer, convert it to a roman numeral. -# +# # Input is guaranteed to be within the range from 1 to 3999. # - -class Solution: - # @return a string +class Solution(object): def intToRoman(self, num): - numeral_map = {1: "I", 4: "IV", 5: "V", 9: "IX", 10: "X", 40: "XL", 50: "L", 90: "XC", 100: "C", 400: "CD", 500: "D", 900: "CM", 1000: "M"} - keyset, result = sorted(numeral_map.keys()), "" - + """ + :type num: int + :rtype: str + """ + numeral_map = {1: "I", 4: "IV", 5: "V", 9: "IX", \ + 10: "X", 40: "XL", 50: "L", 90: "XC", \ + 100: "C", 400: "CD", 500: "D", 900: "CM", \ + 1000: "M"} + keyset, result = sorted(numeral_map.keys()), [] + while num > 0: for key in reversed(keyset): while num / key > 0: num -= key result += numeral_map[key] - - return result + + return "".join(result) + if __name__ == "__main__": - print Solution().intToRoman(999) - print Solution().intToRoman(3999) \ No newline at end of file + print(Solution().intToRoman(999)) + print(Solution().intToRoman(3999)) diff --git a/Python/interleaving-string.py b/Python/interleaving-string.py index fd92f6141..4c90b3bec 100644 --- a/Python/interleaving-string.py +++ b/Python/interleaving-string.py @@ -1,17 +1,18 @@ +from __future__ import print_function # Time: O(m * n) # Space: O(m + n) # # Given s1, s2, s3, find whether s3 is formed by the interleaving of s1 and s2. -# +# # For example, # Given: # s1 = "aabcc", # s2 = "dbbca", -# +# # When s3 = "aadbbcbcac", return true. # When s3 = "aadbbbaccc", return false. # - + # Time: O(m * n) # Space: O(m + n) # Dynamic Programming + Sliding Window @@ -55,7 +56,7 @@ def isInterleave(self, s1, s2, s3): # Time: O(m * n) # Space: O(m * n) -# Recursive + Hash +# Recursive + Hash class Solution3: # @return a boolean def isInterleave(self, s1, s2, s3): @@ -63,25 +64,25 @@ def isInterleave(self, s1, s2, s3): if len(s1) + len(s2) != len(s3): return False return self.isInterleaveRecu(s1, s2, s3, 0, 0, 0) - + def isInterleaveRecu(self, s1, s2, s3, a, b, c): if repr([a, b]) in self.match.keys(): return self.match[repr([a, b])] - + if c == len(s3): return True - + result = False if a < len(s1) and s1[a] == s3[c]: result = result or self.isInterleaveRecu(s1, s2, s3, a + 1, b, c + 1) if b < len(s2) and s2[b] == s3[c]: result = result or self.isInterleaveRecu(s1, s2, s3, a, b + 1, c + 1) - - self.match[repr([a, b])] = result - + + self.match[repr([a, b])] = result + return result if __name__ == "__main__": - print Solution().isInterleave("a", "", "a") - print Solution().isInterleave("aabcc", "dbbca", "aadbbcbcac") - print Solution().isInterleave("aabcc", "dbbca", "aadbbbaccc") + print(Solution().isInterleave("a", "", "a")) + print(Solution().isInterleave("aabcc", "dbbca", "aadbbcbcac")) + print(Solution().isInterleave("aabcc", "dbbca", "aadbbbaccc")) diff --git a/Python/intersection-of-two-arrays-ii.py b/Python/intersection-of-two-arrays-ii.py new file mode 100644 index 000000000..df7776dca --- /dev/null +++ b/Python/intersection-of-two-arrays-ii.py @@ -0,0 +1,164 @@ +# If the given array is not sorted and the memory is unlimited: +# - Time: O(m + n) +# - Space: O(min(m, n)) +# elif the given array is already sorted: +# if m << n or m >> n: +# - Time: O(min(m, n) * log(max(m, n))) +# - Space: O(1) +# else: +# - Time: O(m + n) +# - Soace: O(1) +# else: (the given array is not sorted and the memory is limited) +# - Time: O(max(m, n) * log(max(m, n))) +# - Space: O(1) + +# Given two arrays, write a function to compute their intersection. +# +# Example: +# Given nums1 = [1, 2, 2, 1], nums2 = [2, 2], return [2, 2]. +# +# Note: +# Each element in the result should appear as many times as it shows in both arrays. +# The result can be in any order. +# +# Follow up: +# - What if the given array is already sorted? How would you optimize your algorithm? +# - What if nums1's size is small compared to num2's size? Which algorithm is better? +# - What if elements of nums2 are stored on disk, and the memory is limited such that +# you cannot load all elements into the memory at once? + + +# If the given array is not sorted and the memory is unlimited. +# Time: O(m + n) +# Space: O(min(m, n)) +# Hash solution. +import collections + + +class Solution(object): + def intersect(self, nums1, nums2): + """ + :type nums1: List[int] + :type nums2: List[int] + :rtype: List[int] + """ + if len(nums1) > len(nums2): + return self.intersect(nums2, nums1) + + lookup = collections.defaultdict(int) + for i in nums1: + lookup[i] += 1 + + res = [] + for i in nums2: + if lookup[i] > 0: + res += i, + lookup[i] -= 1 + + return res + + def intersect2(self, nums1, nums2): + """ + :type nums1: List[int] + :type nums2: List[int] + :rtype: List[int] + """ + c = collections.Counter(nums1) & collections.Counter(nums2) + intersect = [] + for i in c: + intersect.extend([i] * c[i]) + return intersect + + +# If the given array is already sorted, and the memory is limited, and (m << n or m >> n). +# Time: O(min(m, n) * log(max(m, n))) +# Space: O(1) +# Binary search solution. +class Solution(object): + def intersect(self, nums1, nums2): + """ + :type nums1: List[int] + :type nums2: List[int] + :rtype: List[int] + """ + if len(nums1) > len(nums2): + return self.intersect(nums2, nums1) + + def binary_search(compare, nums, left, right, target): + while left < right: + mid = left + (right - left) / 2 + if compare(nums[mid], target): + right = mid + else: + left = mid + 1 + return left + + nums1.sort(), nums2.sort() # Make sure it is sorted, doesn't count in time. + + res = [] + left = 0 + for i in nums1: + left = binary_search(lambda x, y: x >= y, nums2, left, len(nums2), i) + if left != len(nums2) and nums2[left] == i: + res += i, + left += 1 + + return res + + +# If the given array is already sorted, and the memory is limited or m ~ n. +# Time: O(m + n) +# Soace: O(1) +# Two pointers solution. +class Solution(object): + def intersect(self, nums1, nums2): + """ + :type nums1: List[int] + :type nums2: List[int] + :rtype: List[int] + """ + nums1.sort(), nums2.sort() # Make sure it is sorted, doesn't count in time. + + res = [] + + it1, it2 = 0, 0 + while it1 < len(nums1) and it2 < len(nums2): + if nums1[it1] < nums2[it2]: + it1 += 1 + elif nums1[it1] > nums2[it2]: + it2 += 1 + else: + res += nums1[it1], + it1 += 1 + it2 += 1 + + return res + + +# If the given array is not sorted, and the memory is limited. +# Time: O(max(m, n) * log(max(m, n))) +# Space: O(1) +# Two pointers solution. +class Solution(object): + def intersect(self, nums1, nums2): + """ + :type nums1: List[int] + :type nums2: List[int] + :rtype: List[int] + """ + nums1.sort(), nums2.sort() # O(max(m, n) * log(max(m, n))) + + res = [] + + it1, it2 = 0, 0 + while it1 < len(nums1) and it2 < len(nums2): + if nums1[it1] < nums2[it2]: + it1 += 1 + elif nums1[it1] > nums2[it2]: + it2 += 1 + else: + res += nums1[it1], + it1 += 1 + it2 += 1 + + return res diff --git a/Python/intersection-of-two-arrays.py b/Python/intersection-of-two-arrays.py new file mode 100644 index 000000000..fb90f68eb --- /dev/null +++ b/Python/intersection-of-two-arrays.py @@ -0,0 +1,107 @@ +# Time: O(m + n) +# Space: O(min(m, n)) + +# Given two arrays, write a function to compute their intersection. +# +# Example: +# Given nums1 = [1, 2, 2, 1], nums2 = [2, 2], return [2]. +# +# Note: +# Each element in the result must be unique. +# The result can be in any order. + +# Hash solution. + + +class Solution(object): + def intersection(self, nums1, nums2): + """ + :type nums1: List[int] + :type nums2: List[int] + :rtype: List[int] + """ + if len(nums1) > len(nums2): + return self.intersection(nums2, nums1) + + lookup = set() + for i in nums1: + lookup.add(i) + + res = [] + for i in nums2: + if i in lookup: + res += i, + lookup.discard(i) + + return res + + def intersection2(self, nums1, nums2): + """ + :type nums1: List[int] + :type nums2: List[int] + :rtype: List[int] + """ + return list(set(nums1) & set(nums2)) + + +# Time: O(max(m, n) * log(max(m, n))) +# Space: O(1) +# Binary search solution. +class Solution2(object): + def intersection(self, nums1, nums2): + """ + :type nums1: List[int] + :type nums2: List[int] + :rtype: List[int] + """ + if len(nums1) > len(nums2): + return self.intersection(nums2, nums1) + + def binary_search(compare, nums, left, right, target): + while left < right: + mid = left + (right - left) / 2 + if compare(nums[mid], target): + right = mid + else: + left = mid + 1 + return left + + nums1.sort(), nums2.sort() + + res = [] + left = 0 + for i in nums1: + left = binary_search(lambda x, y: x >= y, nums2, left, len(nums2), i) + if left != len(nums2) and nums2[left] == i: + res += i, + left = binary_search(lambda x, y: x > y, nums2, left, len(nums2), i) + + return res + + +# Time: O(max(m, n) * log(max(m, n))) +# Space: O(1) +# Two pointers solution. +class Solution3(object): + def intersection(self, nums1, nums2): + """ + :type nums1: List[int] + :type nums2: List[int] + :rtype: List[int] + """ + nums1.sort(), nums2.sort() + res = [] + + it1, it2 = 0, 0 + while it1 < len(nums1) and it2 < len(nums2): + if nums1[it1] < nums2[it2]: + it1 += 1 + elif nums1[it1] > nums2[it2]: + it2 += 1 + else: + if not res or res[-1] != nums1[it1]: + res += nums1[it1], + it1 += 1 + it2 += 1 + + return res diff --git a/Python/intersection-of-two-linked-lists.py b/Python/intersection-of-two-linked-lists.py index d2d7af59f..3d18b7918 100644 --- a/Python/intersection-of-two-linked-lists.py +++ b/Python/intersection-of-two-linked-lists.py @@ -2,20 +2,20 @@ # Space: O(1) # # Write a program to find the node at which the intersection of two singly linked lists begins. -# -# +# +# # For example, the following two linked lists: -# +# # A: a1 - a2 # \ # c1 - c2 - c3 -# / +# / # B: b1 - b2 - b3 # begin to intersect at node c1. -# -# +# +# # Notes: -# +# # If the two linked lists have no intersection at all, return null. # The linked lists must retain their original structure after the function returns. # You may assume there are no cycles anywhere in the entire linked structure. @@ -33,12 +33,15 @@ class Solution: # @return the intersected ListNode def getIntersectionNode(self, headA, headB): curA, curB = headA, headB - tailA, tailB = None, None - + begin, tailA, tailB = None, None, None + + # a->c->b->c + # b->c->a->c while curA and curB: if curA == curB: - return curA - + begin = curA + break + if curA.next: curA = curA.next elif tailA is None: @@ -46,7 +49,7 @@ def getIntersectionNode(self, headA, headB): curA = headB else: break - + if curB.next: curB = curB.next elif tailB is None: @@ -54,5 +57,5 @@ def getIntersectionNode(self, headA, headB): curB = headA else: break - - return None \ No newline at end of file + + return begin diff --git a/Python/invert-binary-tree.py b/Python/invert-binary-tree.py new file mode 100644 index 000000000..18b38c497 --- /dev/null +++ b/Python/invert-binary-tree.py @@ -0,0 +1,100 @@ +# Time: O(n) +# Space: O(h) +# +# Invert a binary tree. +# +# 4 +# / \ +# 2 7 +# / \ / \ +# 1 3 6 9 +# to +# 4 +# / \ +# 7 2 +# / \ / \ +# 9 6 3 1 +# + +# Time: O(n) +# Space: O(w), w is the max number of the nodes of the levels. + +import collections + + +# BFS solution. +class Queue: + def __init__(self): + self.data = collections.deque() + + def push(self, x): + self.data.append(x) + + def peek(self): + return self.data[0] + + def pop(self): + return self.data.popleft() + + def size(self): + return len(self.data) + + def empty(self): + return len(self.data) == 0 + +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, x): +# self.val = x +# self.left = None +# self.right = None + +class Solution: + # @param {TreeNode} root + # @return {TreeNode} + def invertTree(self, root): + if root is not None: + nodes = Queue() + nodes.push(root) + while not nodes.empty(): + node = nodes.pop() + node.left, node.right = node.right, node.left + if node.left is not None: + nodes.push(node.left) + if node.right is not None: + nodes.push(node.right) + + return root + +# Time: O(n) +# Space: O(h) +# Stack solution. +class Solution2: + # @param {TreeNode} root + # @return {TreeNode} + def invertTree(self, root): + if root is not None: + nodes = [] + nodes.append(root) + while nodes: + node = nodes.pop() + node.left, node.right = node.right, node.left + if node.left is not None: + nodes.append(node.left) + if node.right is not None: + nodes.append(node.right) + + return root + +# Time: O(n) +# Space: O(h) +# DFS, Recursive solution. +class Solution3: + # @param {TreeNode} root + # @return {TreeNode} + def invertTree(self, root): + if root is not None: + root.left, root.right = self.invertTree(root.right), \ + self.invertTree(root.left) + + return root diff --git a/Python/ip-to-cidr.py b/Python/ip-to-cidr.py new file mode 100644 index 000000000..fe9a17788 --- /dev/null +++ b/Python/ip-to-cidr.py @@ -0,0 +1,74 @@ +# Time: O(n) +# Space: O(1) + +# Given a start IP address ip and a number of ips we need to cover n, +# return a representation of the range as a list (of smallest possible length) of CIDR blocks. +# +# A CIDR block is a string consisting of an IP, followed by a slash, +# and then the prefix length. For example: "123.45.67.89/20". +# That prefix length "20" represents the number of common prefix bits in the specified range. +# +# Example 1: +# Input: ip = "255.0.0.7", n = 10 +# Output: ["255.0.0.7/32","255.0.0.8/29","255.0.0.16/32"] +# Explanation: +# The initial ip address, when converted to binary, looks like this (spaces added for clarity): +# 255.0.0.7 -> 11111111 00000000 00000000 00000111 +# The address "255.0.0.7/32" specifies all addresses with a common prefix of 32 bits to the given address, +# ie. just this one address. +# +# The address "255.0.0.8/29" specifies all addresses with a common prefix of 29 bits to the given address: +# 255.0.0.8 -> 11111111 00000000 00000000 00001000 +# Addresses with common prefix of 29 bits are: +# 11111111 00000000 00000000 00001000 +# 11111111 00000000 00000000 00001001 +# 11111111 00000000 00000000 00001010 +# 11111111 00000000 00000000 00001011 +# 11111111 00000000 00000000 00001100 +# 11111111 00000000 00000000 00001101 +# 11111111 00000000 00000000 00001110 +# 11111111 00000000 00000000 00001111 +# +# The address "255.0.0.16/32" specifies all addresses with a common prefix of 32 bits to the given address, +# ie. just 11111111 00000000 00000000 00010000. +# +# In total, the answer specifies the range of 10 ips starting with the address 255.0.0.7 . +# +# There were other representations, such as: +# ["255.0.0.7/32","255.0.0.8/30", "255.0.0.12/30", "255.0.0.16/32"], +# but our answer was the shortest possible. +# +# Also note that a representation beginning with say, "255.0.0.7/30" would be incorrect, +# because it includes addresses like 255.0.0.4 = 11111111 00000000 00000000 00000100 +# that are outside the specified range. +# Note: +# - ip will be a valid IPv4 address. +# - Every implied address ip + x (for x < n) will be a valid IPv4 address. +# - n will be an integer in the range [1, 1000]. + +class Solution(object): + def ipToCIDR(self, ip, n): + """ + :type ip: str + :type n: int + :rtype: List[str] + """ + def ipToInt(ip): + result = 0 + for i in ip.split('.'): + result = 256 * result + int(i) + return result + + def intToIP(n): + return ".".join(str((n >> i) % 256) \ + for i in (24, 16, 8, 0)) + + start = ipToInt(ip) + result = [] + while n: + mask = max(33-(start & ~(start-1)).bit_length(), \ + 33-n.bit_length()) + result.append(intToIP(start) + '/' + str(mask)) + start += 1 << (32-mask) + n -= 1 << (32-mask) + return result diff --git a/Python/ipo.py b/Python/ipo.py new file mode 100644 index 000000000..dffd3caf4 --- /dev/null +++ b/Python/ipo.py @@ -0,0 +1,50 @@ +# Time: O(nlogn) +# Space: O(n) + +# Suppose LeetCode will start its IPO soon. In order to sell a good price of its shares to Venture Capital, +# LeetCode would like to work on some projects to increase its capital before the IPO. +# Since it has limited resources, it can only finish at most k distinct projects before the IPO. +# Help LeetCode design the best way to maximize its total capital after finishing at most k distinct projects. +# +# You are given several projects. For each project i, it has a pure profit Pi and a minimum capital +# of Ci is needed to start the corresponding project. Initially, you have W capital. +# When you finish a project, you will obtain its pure profit and the profit will be added to your total capital. +# +# To sum up, pick a list of at most k distinct projects from given projects to maximize +# your final capital, and output your final maximized capital. +# +# Example 1: +# Input: k=2, W=0, Profits=[1,2,3], Capital=[0,1,1]. +# +# Output: 4 +# +# Explanation: Since your initial capital is 0, you can only start the project indexed 0. +# After finishing it you will obtain profit 1 and your capital becomes 1. +# With capital 1, you can either start the project indexed 1 or the project indexed 2. +# Since you can choose at most 2 projects, you need to finish the project indexed 2 to get the maximum capital. +# Therefore, output the final maximized capital, which is 0 + 1 + 3 = 4. +# Note: +# You may assume all numbers in the input are non-negative integers. +# The length of Profits array and Capital array will not exceed 50,000. +# The answer is guaranteed to fit in a 32-bit signed integer. + +import heapq + + +class Solution(object): + def findMaximizedCapital(self, k, W, Profits, Capital): + """ + :type k: int + :type W: int + :type Profits: List[int] + :type Capital: List[int] + :rtype: int + """ + curr = [] + future = sorted(zip(Capital, Profits), reverse=True) + for _ in xrange(k): + while future and future[-1][0] <= W: + heapq.heappush(curr, -future.pop()[1]) + if curr: + W -= heapq.heappop(curr) + return W diff --git a/Python/is-graph-bipartite.py b/Python/is-graph-bipartite.py new file mode 100644 index 000000000..5b9890e91 --- /dev/null +++ b/Python/is-graph-bipartite.py @@ -0,0 +1,63 @@ +# Time: O(|V| + |E|) +# Space: O(|V|) + +# Given a graph, return true if and only if it is bipartite. +# +# Recall that a graph is bipartite if we can split it's set of nodes into +# two independent subsets A and B such that every edge in the graph has +# one node in A and another node in B. +# +# The graph is given in the following form: graph[i] is a list of indexes j +# for which the edge between nodes i and j exists. +# Each node is an integer between 0 and graph.length - 1. +# There are no self edges or parallel edges: graph[i] does not contain i, +# and it doesn't contain any element twice. +# +# Example 1: +# Input: [[1,3], [0,2], [1,3], [0,2]] +# Output: true +# Explanation: +# The graph looks like this: +# 0----1 +# | | +# | | +# 3----2 +# We can divide the vertices into two groups: {0, 2} and {1, 3}. +# +# Example 2: +# Input: [[1,2,3], [0,2], [0,1,3], [0,2]] +# Output: false +# Explanation: +# The graph looks like this: +# 0----1 +# | \ | +# | \ | +# 3----2 +# We cannot find a way to divide the set of nodes into two independent ubsets. +# +# Note: +# - graph will have length in range [1, 100]. +# - graph[i] will contain integers in range [0, graph.length - 1]. +# - graph[i] will not contain i or duplicate values. + +class Solution(object): + def isBipartite(self, graph): + """ + :type graph: List[List[int]] + :rtype: bool + """ + color = {} + for node in xrange(len(graph)): + if node in color: + continue + stack = [node] + color[node] = 0 + while stack: + curr = stack.pop() + for neighbor in graph[curr]: + if neighbor not in color: + stack.append(neighbor) + color[neighbor] = color[curr] ^ 1 + elif color[neighbor] == color[curr]: + return False + return True diff --git a/Python/is-subsequence.py b/Python/is-subsequence.py new file mode 100644 index 000000000..a47504b7d --- /dev/null +++ b/Python/is-subsequence.py @@ -0,0 +1,41 @@ +# Time: O(n) +# Space: O(1) + +# Given a string s and a string t, check if s is subsequence of t. +# +# You may assume that there is only lower case English letters in both s and t. +# t is potentially a very long (length ~= 500,000) string, and s is a short string (<=100). +# +# A subsequence of a string is a new string which is formed from +# the original string by deleting some (can be none) of the characters +# without disturbing the relative positions of the remaining characters. +# (ie, "ace" is a subsequence of "abcde" while "aec" is not). +# +# Example 1: +# s = "abc", t = "ahbgdc" +# +# Return true. +# +# Example 2: +# s = "axc", t = "ahbgdc" +# +# Return false. + +# Greedy solution. +class Solution(object): + def isSubsequence(self, s, t): + """ + :type s: str + :type t: str + :rtype: bool + """ + if not s: + return True + + i = 0 + for c in t: + if c == s[i]: + i += 1 + if i == len(s): + break + return i == len(s) diff --git a/Python/island-perimeter.py b/Python/island-perimeter.py new file mode 100644 index 000000000..fc6c9017a --- /dev/null +++ b/Python/island-perimeter.py @@ -0,0 +1,48 @@ +# Time: O(m * n) +# Space: O(1) + +# You are given a map in form of a two-dimensional integer grid +# where 1 represents land and 0 represents water. +# Grid cells are connected horizontally/vertically (not diagonally). +# The grid is completely surrounded by water, and there is exactly one island +# (i.e., one or more connected land cells). +# The island doesn't have "lakes" (water inside that isn't connected to +# the water around the island). One cell is a square with side length 1. +# The grid is rectangular, width and height don't exceed 100. +# Determine the perimeter of the island. +# +# Example: +# +# [[0,1,0,0], +# [1,1,1,0], +# [0,1,0,0], +# [1,1,0,0]] +# +# Answer: 16 +import operator + + +class Solution(object): + def islandPerimeter(self, grid): + """ + :type grid: List[List[int]] + :rtype: int + """ + count, repeat = 0, 0 + + for i in xrange(len(grid)): + for j in xrange(len(grid[i])): + if grid[i][j] == 1: + count += 1 + if i != 0 and grid[i - 1][j] == 1: + repeat += 1 + if j != 0 and grid[i][j - 1] == 1: + repeat += 1 + + return 4*count - 2*repeat + +# Since there are no lakes, every pair of neighbour cells with different values is part of the perimeter +# (more precisely, the edge between them is). So just count the differing pairs, both horizontally and vertically +# (for the latter I simply transpose the grid). + def islandPerimeter2(self, grid): + return sum(sum(map(operator.ne, [0] + row, row + [0])) for row in grid + map(list, zip(*grid))) diff --git a/Python/isomorphic-strings.py b/Python/isomorphic-strings.py new file mode 100644 index 000000000..4268ab641 --- /dev/null +++ b/Python/isomorphic-strings.py @@ -0,0 +1,61 @@ +# Time: O(n) +# Space: O(1) + +# Given two strings s and t, determine if they are isomorphic. +# +# Two strings are isomorphic if the characters in s can be replaced to get t. +# +# All occurrences of a character must be replaced with another character +# while preserving the order of characters. No two characters may map to +# the same character but a character may map to itself. +# +# For example, +# Given "egg", "add", return true. +# +# Given "foo", "bar", return false. +# +# Given "paper", "title", return true. +# +# Note: +# You may assume both s and t have the same length. + +from itertools import izip # Generator version of zip. + +class Solution(object): + def isIsomorphic(self, s, t): + """ + :type s: str + :type t: str + :rtype: bool + """ + if len(s) != len(t): + return False + + s2t, t2s = {}, {} + for p, w in izip(s, t): + if w not in s2t and p not in t2s: + s2t[w] = p + t2s[p] = w + elif w not in s2t or s2t[w] != p: + # Contradict mapping. + return False + return True + + +# Time: O(n) +# Space: O(1) +class Solution2(object): + def isIsomorphic(self, s, t): + if len(s) != len(t): + return False + + return self.halfIsom(s, t) and self.halfIsom(t, s) + + def halfIsom(self, s, t): + lookup = {} + for i in xrange(len(s)): + if s[i] not in lookup: + lookup[s[i]] = t[i] + elif lookup[s[i]] != t[i]: + return False + return True diff --git a/Python/jewels-and-stones.py b/Python/jewels-and-stones.py new file mode 100644 index 000000000..61fa6936d --- /dev/null +++ b/Python/jewels-and-stones.py @@ -0,0 +1,33 @@ +# Time: O(m + n) +# Space: O(n) + +# You're given strings J representing the types of stones that are jewels, +# and S representing the stones you have. +# Each character in S is a type of stone you have. +# You want to know how many of the stones you have are also jewels. +# +# The letters in J are guaranteed distinct, and all characters in J and S are letters. +# Letters are case sensitive, so "a" is considered a different type of stone from "A". +# +# Example 1: +# Input: J = "aA", S = "aAAbbbb" +# Output: 3 +# Example 2: +# +# Input: J = "z", S = "ZZ" +# Output: 0 +# +# Note: +# - S and J will consist of letters and have length at most 50. +# - The characters in J are distinct. + +class Solution(object): + def numJewelsInStones(self, J, S): + """ + :type J: str + :type S: str + :rtype: int + """ + lookup = set(J) + return sum(s in lookup for s in S) + diff --git a/Python/judge-route-circle.py b/Python/judge-route-circle.py new file mode 100644 index 000000000..8aafd1fee --- /dev/null +++ b/Python/judge-route-circle.py @@ -0,0 +1,34 @@ +# Time: O(n) +# Space: O(1) + +# Initially, there is a Robot at position (0, 0). Given a sequence of its moves, +# judge if this robot makes a circle, which means it moves back to the original place. +# +# The move sequence is represented by a string. And each move is represent by a character. +# The valid robot moves are R (Right), L (Left), U (Up) and D (down). +# The output should be true or false representing whether the robot makes a circle. +# +# Example 1: +# Input: "UD" +# Output: true +# Example 2: +# Input: "LL" +# Output: false + +class Solution(object): + def judgeCircle(self, moves): + """ + :type moves: str + :rtype: bool + """ + v, h = 0, 0 + for move in moves: + if move == 'U': + v += 1 + elif move == 'D': + v -= 1 + elif move == 'R': + h += 1 + elif move == 'L': + h -= 1 + return v == 0 and h == 0 diff --git a/Python/jump-game-ii.py b/Python/jump-game-ii.py index 6a660882e..50c30b2dc 100644 --- a/Python/jump-game-ii.py +++ b/Python/jump-game-ii.py @@ -1,32 +1,37 @@ -# Time: O(n^2) +from __future__ import print_function +# Time: O(n) # Space: O(1) # # Given an array of non-negative integers, you are initially positioned at the first index of the array. -# +# # Each element in the array represents your maximum jump length at that position. -# +# # Your goal is to reach the last index in the minimum number of jumps. -# +# # For example: # Given array A = [2,3,1,1,4] -# +# # The minimum number of jumps to reach the last index is 2. (Jump 1 step from index 0 to 1, then 3 steps to the last index.) # -class Solution: +# not pass on leetcode because of time limit +class Solution(object): # @param A, a list of integers # @return an integer def jump(self, A): - result, prev_reachable, reachable = 0, -1, 0 - while reachable > prev_reachable: - if reachable >= len(A) - 1: - return result - result += 1 - prev_reachable = reachable - for i, length in enumerate(A[:reachable + 1]): - reachable = max(reachable, i + length) - return -1 - + jump_count = 0 + reachable = 0 + curr_reachable = 0 + for i, length in enumerate(A): + if i > reachable: + return -1 + if i > curr_reachable: + curr_reachable = reachable + jump_count += 1 + reachable = max(reachable, i + length) + return jump_count + + if __name__ == "__main__": - print Solution().jump([2,3,1,1,4]) - print Solution().jump([3,2,1,0,4]) + print(Solution().jump([2,3,1,1,4])) + print(Solution().jump([3,2,1,0,4])) diff --git a/Python/jump-game.py b/Python/jump-game.py index f3e18cce6..8cd87f9c5 100644 --- a/Python/jump-game.py +++ b/Python/jump-game.py @@ -1,15 +1,16 @@ +from __future__ import print_function # Time: O(n) # Space: O(1) # # Given an array of non-negative integers, you are initially positioned at the first index of the array. -# +# # Each element in the array represents your maximum jump length at that position. -# +# # Determine if you are able to reach the last index. -# +# # For example: # A = [2,3,1,1,4], return true. -# +# # A = [3,2,1,0,4], return false. class Solution: @@ -22,7 +23,7 @@ def canJump(self, A): break reachable = max(reachable, i + length) return reachable >= len(A) - 1 - + if __name__ == "__main__": - print Solution().canJump([2,3,1,1,4]) - print Solution().canJump([3,2,1,0,4]) \ No newline at end of file + print(Solution().canJump([2,3,1,1,4])) + print(Solution().canJump([3,2,1,0,4])) \ No newline at end of file diff --git a/Python/k-diff-pairs-in-an-array.py b/Python/k-diff-pairs-in-an-array.py new file mode 100644 index 000000000..9331f8a74 --- /dev/null +++ b/Python/k-diff-pairs-in-an-array.py @@ -0,0 +1,46 @@ +# Time: O(n) +# Space: O(n) + +# Total Accepted: 5671 +# Total Submissions: 20941 +# Difficulty: Easy +# Contributors: murali.kf370 +# Given an array of integers and an integer k, +# you need to find the number of unique k-diff pairs in the array. +# Here a k-diff pair is defined as an integer pair (i, j), +# where i and j are both numbers in the array and their absolute difference is k. +# +# Example 1: +# Input: [3, 1, 4, 1, 5], k = 2 +# Output: 2 +# Explanation: There are two 2-diff pairs in the array, (1, 3) and (3, 5). +# Although we have two 1s in the input, we should only return the number of unique pairs. +# Example 2: +# Input:[1, 2, 3, 4, 5], k = 1 +# Output: 4 +# Explanation: There are four 1-diff pairs in the array, (1, 2), (2, 3), (3, 4) and (4, 5). +# Example 3: +# Input: [1, 3, 1, 5, 4], k = 0 +# Output: 1 +# Explanation: There is one 0-diff pair in the array, (1, 1). +# Note: +# The pairs (i, j) and (j, i) count as the same pair. +# The length of the array won't exceed 10,000. +# All the integers in the given input belong to the range: [-1e7, 1e7]. + +class Solution(object): + def findPairs(self, nums, k): + """ + :type nums: List[int] + :type k: int + :rtype: int + """ + if k < 0: return 0 + result, lookup = set(), set() + for num in nums: + if num-k in lookup: + result.add(num-k) + if num+k in lookup: + result.add(num) + lookup.add(num) + return len(result) diff --git a/Python/k-empty-slots.py b/Python/k-empty-slots.py new file mode 100644 index 000000000..c87e9005a --- /dev/null +++ b/Python/k-empty-slots.py @@ -0,0 +1,54 @@ +# Time: O(n) +# Space: O(n) + +# There is a garden with N slots. In each slot, there is a flower. +# The N flowers will bloom one by one in N days. In each day, +# there will be exactly one flower blooming and it will be in the status of blooming since then. +# +# Given an array flowers consists of number from 1 to N. +# Each number in the array represents the place where the flower will open in that day. +# +# For example, flowers[i] = x means that the unique flower that blooms at day i +# will be at position x, where i and x will be in the range from 1 to N. +# +# Also given an integer k, you need to output in which day there exists two flowers +# in the status of blooming, and also the number of flowers between them is k and +# these flowers are not blooming. +# +# If there isn't such day, output -1. +# +# Example 1: +# Input: +# flowers: [1,3,2] +# k: 1 +# Output: 2 +# Explanation: In the second day, the first and the third flower have become blooming. +# +# Example 2: +# Input: +# flowers: [1,2,3] +# k: 1 +# Output: -1 +# Note: +# The given array will be in the range [1, 20000]. + +class Solution(object): + def kEmptySlots(self, flowers, k): + """ + :type flowers: List[int] + :type k: int + :rtype: int + """ + days = [0] * len(flowers) + for i in xrange(len(flowers)): + days[flowers[i]-1] = i + result = float("inf") + i, left, right = 0, 0, k+1 + while right < len(days): + if days[i] < days[left] or days[i] <= days[right]: + if i == right: + result = min(result, max(days[left], days[right])) + left, right = i, k+1+i; + i += 1 + return -1 if result == float("inf") else result+1 + diff --git a/Python/k-inverse-pairs-array.py b/Python/k-inverse-pairs-array.py new file mode 100644 index 000000000..1845a3c44 --- /dev/null +++ b/Python/k-inverse-pairs-array.py @@ -0,0 +1,42 @@ +# Time: O(n * k) +# Space: O(k) + +# Given two integers n and k, find how many different arrays consist of numbers +# from 1 to n such that there are exactly k inverse pairs. +# +# We define an inverse pair as following: For ith and jth element in the array, +# if i < j and a[i] > a[j] then it's an inverse pair; Otherwise, it's not. +# +# Since the answer may very large, the answer should be modulo 109 + 7. +# +# Example 1: +# Input: n = 3, k = 0 +# Output: 1 +# Explanation: +# Only the array [1,2,3] which consists of numbers from 1 to 3 has exactly 0 inverse pair. +# Example 2: +# Input: n = 3, k = 1 +# Output: 2 +# Explanation: +# The array [1,3,2] and [2,1,3] have exactly 1 inverse pair. +# Note: +# The integer n is in the range [1, 1000] and k is in the range [0, 1000]. + +class Solution(object): + def kInversePairs(self, n, k): + """ + :type n: int + :type k: int + :rtype: int + """ + M = 1000000007 + dp = [[0]*(k+1) for _ in xrange(2)] + dp[0][0] = 1 + for i in xrange(1, n+1): + dp[i%2] = [0]*(k+1) + dp[i%2][0] = 1 + for j in xrange(1, k+1): + dp[i%2][j] = (dp[i%2][j-1] + dp[(i-1)%2][j]) % M + if j-i >= 0: + dp[i%2][j] = (dp[i%2][j] - dp[(i-1)%2][j-i]) % M + return dp[n%2][k] diff --git a/Python/k-similar-strings.py b/Python/k-similar-strings.py new file mode 100644 index 000000000..e662050ad --- /dev/null +++ b/Python/k-similar-strings.py @@ -0,0 +1,72 @@ +# Time: O(n * n!/(c_a!*...*c_z!), n is the length of A, B, +# c_a...c_z is the count of each alphabet, +# n = sum(c_a...c_z) +# Space: O(n * n!/(c_a!*...*c_z!) + +# Strings A and B are K-similar (for some non-negative integer K) +# if we can swap the positions of two letters +# in A exactly K times so that the resulting string equals B. +# +# Given two anagrams A and B, return the smallest K for which A and B are +# K-similar. +# +# Example 1: +# +# Input: A = "ab", B = "ba" +# Output: 1 +# Example 2: +# +# Input: A = "abc", B = "bca" +# Output: 2 +# Example 3: +# +# Input: A = "abac", B = "baca" +# Output: 2 +# Example 4: +# +# Input: A = "aabc", B = "abca" +# Output: 2 +# Note: +# - 1 <= A.length == B.length <= 20 +# - A and B contain only lowercase letters from +# the set {'a', 'b', 'c', 'd', 'e', 'f'} + +import collections + +try: + xrange # Python 2 +except NameError: + xrange = range # Python 3 + + +class Solution(object): + def kSimilarity(self, A, B): + """ + :type A: str + :type B: str + :rtype: int + """ + def neighbors(s, B): + for i, c in enumerate(s): + if c != B[i]: + break + t = list(s) + for j in xrange(i+1, len(s)): + if t[j] == B[i]: + t[i], t[j] = t[j], t[i] + yield "".join(t) + t[j], t[i] = t[i], t[j] + + q = collections.deque([A]) + lookup = set() + result = 0 + while q: + for _ in xrange(len(q)): + s = q.popleft() + if s == B: + return result + for t in neighbors(s, B): + if t not in lookup: + lookup.add(t) + q.append(t) + result += 1 diff --git a/Python/k-th-smallest-in-lexicographical-order.py b/Python/k-th-smallest-in-lexicographical-order.py new file mode 100644 index 000000000..8a15760be --- /dev/null +++ b/Python/k-th-smallest-in-lexicographical-order.py @@ -0,0 +1,102 @@ +# Time: O(logn) +# Space: O(logn) + +# Given integers n and k, find the lexicographically k-th smallest integer in the range from 1 to n. +# +# Note: 1 <= k <= n <= 109. +# +# Example: +# +# Input: +# n: 13 k: 2 +# +# Output: +# 10 +# +# Explanation: +# The lexicographical order is [1, 10, 11, 12, 13, 2, 3, 4, 5, 6, 7, 8, 9], +# so the second smallest number is 10. + +class Solution(object): + def findKthNumber(self, n, k): + """ + :type n: int + :type k: int + :rtype: int + """ + result = 0 + + cnts = [0] * 10 + for i in xrange(1, 10): + cnts[i] = cnts[i - 1] * 10 + 1 + + nums = [] + i = n + while i: + nums.append(i % 10) + i /= 10 + + total, target = n, 0 + i = len(nums) - 1 + while i >= 0 and k > 0: + target = target*10 + nums[i] + start = int(i == len(nums)-1) + for j in xrange(start, 10): + candidate = result*10 + j + if candidate < target: + num = cnts[i+1] + elif candidate > target: + num = cnts[i] + else: + num = total - cnts[i + 1]*(j-start) - cnts[i]*(9-j) + if k > num: + k -= num + else: + result = candidate + k -= 1 + total = num-1 + break + i -= 1 + + return result + + +# Time: O(logn * logn) +# Space: O(logn) +class Solution2(object): + def findKthNumber(self, n, k): + """ + :type n: int + :type k: int + :rtype: int + """ + def count(n, prefix): + result, number = 0, 1 + while prefix <= n: + result += number + prefix *= 10 + number *= 10 + result -= max(number/10 - (n - prefix/10 + 1), 0) + return result + + def findKthNumberHelper(n, k, cur, index): + if cur: + index += 1 + if index == k: + return (cur, index) + + i = int(cur == 0) + while i <= 9: + cur = cur * 10 + i + cnt = count(n, cur) + if k > cnt + index: + index += cnt + elif cur <= n: + result = findKthNumberHelper(n, k, cur, index) + if result[0]: + return result + i += 1 + cur /= 10 + return (0, index) + + return findKthNumberHelper(n, k, 0, 0)[0] diff --git a/Python/k-th-smallest-prime-fraction.py b/Python/k-th-smallest-prime-fraction.py new file mode 100644 index 000000000..110ec687c --- /dev/null +++ b/Python/k-th-smallest-prime-fraction.py @@ -0,0 +1,64 @@ +# Time: O(nlogr) +# Space: O(1) + +# Another cool O(n) solution by using quick select with median of median could be found here: +# https://leetcode.com/problems/k-th-smallest-prime-fraction/discuss/115545/O(n) + +# A sorted list A contains 1, plus some number of primes. +# Then, for every p < q in the list, we consider the fraction p/q. +# +# What is the K-th smallest fraction considered? +# Return your answer as an array of ints, where answer[0] = p and answer[1] = q. +# +# Examples: +# Input: A = [1, 2, 3, 5], K = 3 +# Output: [2, 5] +# Explanation: +# The fractions to be considered in sorted order are: +# 1/5, 1/3, 2/5, 1/2, 3/5, 2/3. +# The third fraction is 2/5. +# +# Input: A = [1, 7], K = 1 +# Output: [1, 7] +# +# Note: +# - A will have length between 2 and 2000. +# - Each A[i] will be between 1 and 30000. +# - K will be between 1 and A.length * (A.length + 1) / 2. + +class Solution(object): + def kthSmallestPrimeFraction(self, A, K): + """ + :type A: List[int] + :type K: int + :rtype: List[int] + """ + def check(mid, A, K, result): + tmp = [0]*2 + count = 0 + j = 0 + for i in xrange(len(A)): + while j < len(A): + if i < j and A[i] < A[j]*mid: + if tmp[0] == 0 or \ + tmp[0]*A[j] < tmp[1]*A[i]: + tmp[0] = A[i] + tmp[1] = A[j] + break + j += 1 + count += len(A)-j + if count == K: + result[:] = tmp + return count >= K + + result = [] + left, right = 0.0, 1.0 + while right-left > 1e-8: + mid = left + (right-left) / 2.0 + if check(mid, A, K, result): + right = mid + else: + left = mid + if result: + break + return result diff --git a/Python/k-th-symbol-in-grammar.py b/Python/k-th-symbol-in-grammar.py new file mode 100644 index 000000000..8980720a1 --- /dev/null +++ b/Python/k-th-symbol-in-grammar.py @@ -0,0 +1,49 @@ +# Time: O(logn) = O(1) because n is 32-bit integer +# Space: O(1) + +# On the first row, we write a 0. +# Now in every subsequent row, +# we look at the previous row and replace each occurrence of 0 with 01, +# and each occurrence of 1 with 10. +# +# Given row N and index K, return the K-th indexed symbol in row N. +# (The values of K are 1-indexed.) (1 indexed). +# +# Examples: +# Input: N = 1, K = 1 +# Output: 0 +# +# Input: N = 2, K = 1 +# Output: 0 +# +# Input: N = 2, K = 2 +# Output: 1 +# +# Input: N = 4, K = 5 +# Output: 1 +# +# Explanation: +# row 1: 0 +# row 2: 01 +# row 3: 0110 +# row 4: 01101001 +# +# Note: +# - N will be an integer in the range [1, 30]. +# - K will be an integer in the range [1, 2^(N-1)]. + +class Solution(object): + def kthGrammar(self, N, K): + """ + :type N: int + :type K: int + :rtype: int + """ + def bitCount(n): + result = 0 + while n: + n &= n - 1 + result += 1 + return result + + return bitCount(K-1) % 2 diff --git a/Python/keyboard-row.py b/Python/keyboard-row.py new file mode 100644 index 000000000..ef825d00c --- /dev/null +++ b/Python/keyboard-row.py @@ -0,0 +1,51 @@ +# Time: O(n) +# Space: O(1) + +# Given a List of words, return the words that can be typed +# using letters of alphabet on only one row's of American keyboard like the image below. +# +# Example 1: +# Input: ["Hello", "Alaska", "Dad", "Peace"] +# Output: ["Alaska", "Dad"] +# Note: +# You may use one character in the keyboard more than once. +# You may assume the input string will only contain letters of alphabet. + +class Solution(object): + def findWords(self, words): + """ + :type words: List[str] + :rtype: List[str] + """ + rows = [set(['q', 'w', 'e', 'r', 't', 'y','u', 'i', 'o', 'p']), + set(['a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l']), + set(['z', 'x', 'c', 'v', 'b' ,'n', 'm'])] + + result = [] + for word in words: + k = 0 + for i in xrange(len(rows)): + if word[0].lower() in rows[i]: + k = i + break + for c in word: + if c.lower() not in rows[k]: + break + else: + result.append(word) + return result + + +class Solution2(object): + def findWords(self, words): + """ + :type words: List[str] + :rtype: List[str] + """ + keyboard_rows = ['qwertyuiop', 'asdfghjkl', 'zxcvbnm'] + single_row_words = [] + for word in words: + for row in keyboard_rows: + if all(letter in row for letter in word.lower()): + single_row_words.append(word) + return single_row_words diff --git a/Python/keys-and-rooms.py b/Python/keys-and-rooms.py new file mode 100644 index 000000000..3e562a163 --- /dev/null +++ b/Python/keys-and-rooms.py @@ -0,0 +1,55 @@ +# Time: O(n!) +# Space: O(n) + +# There are N rooms and you start in room 0. +# Each room has a distinct number in 0, 1, 2, ..., N-1, +# and each room may have some keys to access the next room. +# +# Formally, each room i has a list of keys rooms[i], +# and each key rooms[i][j] is an integer in [0, 1, ..., N-1] +# where N = rooms.length. +# A key rooms[i][j] = v opens the room with number v. +# +# Initially, all the rooms start locked (except for room 0). +# You can walk back and forth between rooms freely. +# Return true if and only if you can enter every room. +# +# Example 1: +# +# Input: [[1],[2],[3],[]] +# Output: true +# Explanation: +# We start in room 0, and pick up key 1. +# We then go to room 1, and pick up key 2. +# We then go to room 2, and pick up key 3. +# We then go to room 3. Since we were able to go to every room, +# we return true. +# Example 2: +# +# Input: [[1,3],[3,0,1],[2],[0]] +# Output: false +# Explanation: We can't enter the room with number 2. +# +# Note: +# - 1 <= rooms.length <= 1000 +# - 0 <= rooms[i].length <= 1000 +# - The number of keys in all rooms combined is at most 3000. + + +class Solution(object): + def canVisitAllRooms(self, rooms): + """ + :type rooms: List[List[int]] + :rtype: bool + """ + lookup = set([0]) + stack = [0] + while stack: + node = stack.pop() + for nei in rooms[node]: + if nei not in lookup: + lookup.add(nei) + if len(lookup) == len(rooms): + return True + stack.append(nei) + return len(lookup) == len(rooms) diff --git a/Python/kill-process.py b/Python/kill-process.py new file mode 100644 index 000000000..4eb4695ce --- /dev/null +++ b/Python/kill-process.py @@ -0,0 +1,56 @@ +# Time: O(n) +# Space: O(n) + +import collections + + +# DFS solution. +class Solution(object): + def killProcess(self, pid, ppid, kill): + """ + :type pid: List[int] + :type ppid: List[int] + :type kill: int + :rtype: List[int] + """ + def killAll(pid, children, killed): + killed.append(pid) + for child in children[pid]: + killAll(child, children, killed) + + result = [] + children = collections.defaultdict(set) + for i in xrange(len(pid)): + children[ppid[i]].add(pid[i]) + killAll(kill, children, result) + return result + + +# Time: O(n) +# Space: O(n) +# BFS solution. +class Solution2(object): + def killProcess(self, pid, ppid, kill): + """ + :type pid: List[int] + :type ppid: List[int] + :type kill: int + :rtype: List[int] + """ + def killAll(pid, children, killed): + killed.append(pid) + for child in children[pid]: + killAll(child, children, killed) + + result = [] + children = collections.defaultdict(set) + for i in xrange(len(pid)): + children[ppid[i]].add(pid[i]) + q = collections.deque() + q.append(kill) + while q: + p = q.popleft() + result.append(p); + for child in children[p]: + q.append(child) + return result diff --git a/Python/knight-probability-in-chessboard.py b/Python/knight-probability-in-chessboard.py new file mode 100644 index 000000000..2f493ca96 --- /dev/null +++ b/Python/knight-probability-in-chessboard.py @@ -0,0 +1,52 @@ +# Time: O(k * n^2) +# Space: O(n^2) + +# On an NxN chessboard, a knight starts at the r-th row and c-th column and +# attempts to make exactly K moves. The rows and columns are 0 indexed, +# so the top-left square is (0, 0), and the bottom-right square is (N-1, N-1). +# +# A chess knight has 8 possible moves it can make, as illustrated below. +# Each move is two squares in a cardinal direction, then one square in an orthogonal direction. +# +# Each time the knight is to move, it chooses one of eight possible moves uniformly +# at random (even if the piece would go off the chessboard) and moves there. +# +# The knight continues moving until it has made exactly K moves or has moved off the chessboard. +# Return the probability that the knight remains on the board after it has stopped moving. +# +# Example: +# Input: 3, 2, 0, 0 +# Output: 0.0625 +# +# Explanation: There are two moves (to (1,2), (2,1)) that will keep the knight on the board. +# From each of those positions, there are also two moves that will keep the knight on the board. +# The total probability the knight stays on the board is 0.0625. +# +# Note: +# N will be between 1 and 25. +# K will be between 0 and 100. +# The knight always initially starts on the board. + +class Solution(object): + def knightProbability(self, N, K, r, c): + """ + :type N: int + :type K: int + :type r: int + :type c: int + :rtype: float + """ + directions = \ + [[ 1, 2], [ 1, -2], [ 2, 1], [ 2, -1], \ + [-1, 2], [-1, -2], [-2, 1], [-2, -1]]; + dp = [[[1 for _ in xrange(N)] for _ in xrange(N)] for _ in xrange(2)] + for step in xrange(1, K+1): + for i in xrange(N): + for j in xrange(N): + dp[step%2][i][j] = 0 + for direction in directions: + rr, cc = i+direction[0], j+direction[1] + if 0 <= cc < N and 0 <= rr < N: + dp[step%2][i][j] += 0.125 * dp[(step-1)%2][rr][cc]; + + return dp[K%2][r][c] diff --git a/Python/koko-eating-bananas.py b/Python/koko-eating-bananas.py new file mode 100644 index 000000000..515315d15 --- /dev/null +++ b/Python/koko-eating-bananas.py @@ -0,0 +1,53 @@ +# Time: O(nlogr) +# Space: O(1) + +# Koko loves to eat bananas. +# There are N piles of bananas, the i-th pile has piles[i] bananas. +# The guards have gone and will come back in H hours. +# +# Koko can decide her bananas-per-hour eating speed of K. +# Each hour, she chooses some pile of bananas, and eats K bananas from that pile. +# If the pile has less than K bananas, she eats all of them instead, +# and won't eat any more bananas during this hour. +# +# Koko likes to eat slowly, but still wants to finish eating +# all the bananas before the guards come back. +# +# Return the minimum integer K such that she can eat all the bananas within H hours. +# +# Example 1: +# +# Input: piles = [3,6,7,11], H = 8 +# Output: 4 +# Example 2: +# +# Input: piles = [30,11,23,4,20], H = 5 +# Output: 30 +# Example 3: +# +# Input: piles = [30,11,23,4,20], H = 6 +# Output: 23 +# +# Note: +# - 1 <= piles.length <= 10^4 +# - piles.length <= H <= 10^9 +# - 1 <= piles[i] <= 10^9 + +class Solution(object): + def minEatingSpeed(self, piles, H): + """ + :type piles: List[int] + :type H: int + :rtype: int + """ + def possible(piles, H, K): + return sum((pile-1)//K+1 for pile in piles) <= H + + left, right = 1, max(piles) + while left <= right: + mid = left + (right-left)//2 + if possible(piles, H, mid): + right = mid-1 + else: + left = mid+1 + return left diff --git a/Python/kth-largest-element-in-a-stream.py b/Python/kth-largest-element-in-a-stream.py new file mode 100644 index 000000000..27e57d241 --- /dev/null +++ b/Python/kth-largest-element-in-a-stream.py @@ -0,0 +1,56 @@ +# Time: O(nlogk) +# Space: O(k) + +# Design a class to find the kth largest element in a stream. +# Note that it is the kth largest element in the sorted order, +# not the kth distinct element. +# +# Your KthLargest class will have a constructor +# which accepts an integer k and an integer array nums, +# which contains initial elements from the stream. +# For each call to the method KthLargest.add, +# return the element representing the kth largest element in the stream. +# +# Example: +# +# int k = 3; +# int[] arr = [4,5,8,2]; +# KthLargest kthLargest = new KthLargest(3, arr); +# kthLargest.add(3); // returns 4 +# kthLargest.add(5); // returns 5 +# kthLargest.add(10); // returns 5 +# kthLargest.add(9); // returns 8 +# kthLargest.add(4); // returns 8 +# Note: +# - You may assume that nums' length ≥ k-1 and k ≥ 1. + +import heapq + + +class KthLargest(object): + + def __init__(self, k, nums): + """ + :type k: int + :type nums: List[int] + """ + self.__k = k + self.__min_heap = [] + for n in nums: + self.add(n) + + + def add(self, val): + """ + :type val: int + :rtype: int + """ + heapq.heappush(self.__min_heap, val) + if len(self.__min_heap) > self.__k: + heapq.heappop(self.__min_heap) + return self.__min_heap[0] + + +# Your KthLargest object will be instantiated and called as such: +# obj = KthLargest(k, nums) +# param_1 = obj.add(val) diff --git a/Python/kth-largest-element-in-an-array.py b/Python/kth-largest-element-in-an-array.py new file mode 100644 index 000000000..55e1af2a9 --- /dev/null +++ b/Python/kth-largest-element-in-an-array.py @@ -0,0 +1,33 @@ +# Time: O(n) ~ O(n^2) +# Space: O(1) + +from random import randint + +class Solution: + # @param {integer[]} nums + # @param {integer} k + # @return {integer} + def findKthLargest(self, nums, k): + left, right = 0, len(nums) - 1 + while left <= right: + pivot_idx = randint(left, right) + new_pivot_idx = self.PartitionAroundPivot(left, right, pivot_idx, nums) + if new_pivot_idx == k - 1: + return nums[new_pivot_idx] + elif new_pivot_idx > k - 1: + right = new_pivot_idx - 1 + else: # new_pivot_idx < k - 1. + left = new_pivot_idx + 1 + + def PartitionAroundPivot(self, left, right, pivot_idx, nums): + pivot_value = nums[pivot_idx] + new_pivot_idx = left + nums[pivot_idx], nums[right] = nums[right], nums[pivot_idx] + for i in xrange(left, right): + if nums[i] > pivot_value: + nums[i], nums[new_pivot_idx] = nums[new_pivot_idx], nums[i] + new_pivot_idx += 1 + + nums[right], nums[new_pivot_idx] = nums[new_pivot_idx], nums[right] + return new_pivot_idx + diff --git a/Python/kth-smallest-element-in-a-bst.py b/Python/kth-smallest-element-in-a-bst.py new file mode 100644 index 000000000..89457f4e9 --- /dev/null +++ b/Python/kth-smallest-element-in-a-bst.py @@ -0,0 +1,39 @@ +# Time: O(max(h, k)) +# Space: O(h) + +# Given a binary search tree, write a function kthSmallest to find the kth smallest element in it. +# +# Note: +# You may assume k is always valid, 1 ≤ k ≤ BST's total elements. +# +# Follow up: +# What if the BST is modified (insert/delete operations) often and +# you need to find the kth smallest frequently? How would you optimize the kthSmallest routine? +# + +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, x): +# self.val = x +# self.left = None +# self.right = None + +class Solution: + # @param {TreeNode} root + # @param {integer} k + # @return {integer} + def kthSmallest(self, root, k): + s, cur, rank = [], root, 0 + + while s or cur: + if cur: + s.append(cur) + cur = cur.left + else: + cur = s.pop() + rank += 1 + if rank == k: + return cur.val + cur = cur.right + + return float("-inf") diff --git a/Python/kth-smallest-element-in-a-sorted-matrix.py b/Python/kth-smallest-element-in-a-sorted-matrix.py new file mode 100644 index 000000000..1bf17df12 --- /dev/null +++ b/Python/kth-smallest-element-in-a-sorted-matrix.py @@ -0,0 +1,52 @@ +# Time: O(k * log(min(n, m, k))), with n x m matrix +# Space: O(min(n, m, k)) + +# Given a n x n matrix where each of the rows and +# columns are sorted in ascending order, +# find the kth smallest element in the matrix. +# +# Note that it is the kth smallest element in the sorted order, +# not the kth distinct element. +# +# Example: +# +# matrix = [ +# [ 1, 5, 9], +# [10, 11, 13], +# [12, 13, 15] +# ], +# k = 8, +# +# return 13. +# Note: +# You may assume k is always valid, 1 <= k <= n^2. + +from heapq import heappush, heappop + +class Solution(object): + def kthSmallest(self, matrix, k): + """ + :type matrix: List[List[int]] + :type k: int + :rtype: int + """ + kth_smallest = 0 + min_heap = [] + + def push(i, j): + if len(matrix) > len(matrix[0]): + if i < len(matrix[0]) and j < len(matrix): + heappush(min_heap, [matrix[j][i], i, j]) + else: + if i < len(matrix) and j < len(matrix[0]): + heappush(min_heap, [matrix[i][j], i, j]) + + push(0, 0) + while min_heap and k > 0: + kth_smallest, i, j = heappop(min_heap) + push(i, j + 1) + if j == 0: + push(i + 1, 0) + k -= 1 + + return kth_smallest diff --git a/Python/kth-smallest-number-in-multiplication-table.py b/Python/kth-smallest-number-in-multiplication-table.py new file mode 100644 index 000000000..17c4225b8 --- /dev/null +++ b/Python/kth-smallest-number-in-multiplication-table.py @@ -0,0 +1,51 @@ +# Time: O(m * log(m * n)) +# Space: O(1) + +# Nearly every one have used the Multiplication Table. +# But could you find out the k-th smallest number quickly from the multiplication table? +# +# Given the height m and the length n of a m * n Multiplication Table, and a positive integer k, +# you need to return the k-th smallest number in this table. +# +# Example 1: +# Input: m = 3, n = 3, k = 5 +# Output: +# Explanation: +# The Multiplication Table: +# 1 2 3 +# 2 4 6 +# 3 6 9 +# +# The 5-th smallest number is 3 (1, 2, 2, 3, 3). +# Example 2: +# Input: m = 2, n = 3, k = 6 +# Output: +# Explanation: +# The Multiplication Table: +# 1 2 3 +# 2 4 6 +# +# The 6-th smallest number is 6 (1, 2, 2, 3, 4, 6). +# Note: +# The m and n will be in the range [1, 30000]. +# The k will be in the range [1, m * n] + +class Solution(object): + def findKthNumber(self, m, n, k): + """ + :type m: int + :type n: int + :type k: int + :rtype: int + """ + def count(target, m, n): + return sum(min(target//i, n) for i in xrange(1, m+1)) + + left, right = 1, m*n; + while left <= right: + mid = left + (right-left)/2 + if count(mid, m, n) >= k: + right = mid-1 + else: + left = mid+1 + return left diff --git a/Python/largest-bst-subtree.py b/Python/largest-bst-subtree.py new file mode 100644 index 000000000..78359e2bb --- /dev/null +++ b/Python/largest-bst-subtree.py @@ -0,0 +1,43 @@ +# Time: O(n) +# Space: O(h) + +# Definition for a binary tree node. +# class TreeNode(object): +# def __init__(self, x): +# self.val = x +# self.left = None +# self.right = None + +class Solution(object): + def largestBSTSubtree(self, root): + """ + :type root: TreeNode + :rtype: int + """ + if root is None: + return 0 + + max_size = [1] + def largestBSTSubtreeHelper(root): + if root.left is None and root.right is None: + return 1, root.val, root.val + + left_size, left_min, left_max = 0, root.val, root.val + if root.left is not None: + left_size, left_min, left_max = largestBSTSubtreeHelper(root.left) + + right_size, right_min, right_max = 0, root.val, root.val + if root.right is not None: + right_size, right_min, right_max = largestBSTSubtreeHelper(root.right) + + size = 0 + if (root.left is None or left_size > 0) and \ + (root.right is None or right_size > 0) and \ + left_max <= root.val <= right_min: + size = 1 + left_size + right_size + max_size[0] = max(max_size[0], size) + + return size, left_min, right_max + + largestBSTSubtreeHelper(root) + return max_size[0] diff --git a/Python/largest-divisible-subset.py b/Python/largest-divisible-subset.py new file mode 100644 index 000000000..e908d1887 --- /dev/null +++ b/Python/largest-divisible-subset.py @@ -0,0 +1,48 @@ +# Time: O(n^2) +# Space: O(n) + +# Given a set of distinct positive integers, +# find the largest subset such that every pair (Si, Sj) of +# elements in this subset satisfies: Si % Sj = 0 or Sj % Si = 0. +# +# If there are multiple solutions, return any subset is fine. +# +# Example 1: +# +# nums: [1,2,3] +# +# Result: [1,2] (of course, [1,3] will also be ok) +# Example 2: +# +# nums: [1,2,4,8] +# +# Result: [1,2,4,8] + +class Solution(object): + def largestDivisibleSubset(self, nums): + """ + :type nums: List[int] + :rtype: List[int] + """ + if not nums: + return [] + + nums.sort() + dp = [1] * len(nums) + prev = [-1] * len(nums) + largest_idx = 0 + for i in xrange(len(nums)): + for j in xrange(i): + if nums[i] % nums[j] == 0: + if dp[i] < dp[j] + 1: + dp[i] = dp[j] + 1 + prev[i] = j + if dp[largest_idx] < dp[i]: + largest_idx = i + + result = [] + i = largest_idx + while i != -1: + result.append(nums[i]) + i = prev[i] + return result[::-1] diff --git a/Python/largest-number-at-least-twice-of-others.py b/Python/largest-number-at-least-twice-of-others.py new file mode 100644 index 000000000..e47058e62 --- /dev/null +++ b/Python/largest-number-at-least-twice-of-others.py @@ -0,0 +1,33 @@ +# Time: O(n) +# Space: O(1) + +# In a given integer array nums, there is always exactly one largest element. +# Find whether the largest element in the array is +# at least twice as much as every other number in the array. +# If it is, return the index of the largest element, otherwise return -1. +# +# Example 1: +# Input: nums = [3, 6, 1, 0] +# Output: 1 +# Explanation: 6 is the largest integer, and for every other number in the array x, +# 6 is more than twice as big as x. The index of value 6 is 1, so we return 1. +# +# Example 2: +# Input: nums = [1, 2, 3, 4] +# Output: -1 +# Explanation: 4 isn't at least as big as twice the value of 3, so we return -1. +# +# Note: +# - nums will have a length in the range [1, 50]. +# - Every nums[i] will be an integer in the range [0, 99]. + +class Solution(object): + def dominantIndex(self, nums): + """ + :type nums: List[int] + :rtype: int + """ + m = max(nums) + if all(m >= 2*x for x in nums if x != m): + return nums.index(m) + return -1 diff --git a/Python/largest-number.py b/Python/largest-number.py index ab28367cf..c1bd3c1df 100644 --- a/Python/largest-number.py +++ b/Python/largest-number.py @@ -1,10 +1,11 @@ -# Time: O(n^2) -# Space: O(n) +from __future__ import print_function +# Time: O(nlogn) +# Space: O(1) # # Given a list of non negative integers, arrange them such that they form the largest number. -# +# # For example, given [3, 30, 34, 5, 9], the largest formed number is 9534330. -# +# # Note: The result may be very large, so you need to return a string instead of an integer. # @@ -19,4 +20,4 @@ def largestNumber(self, num): if __name__ == "__main__": num = [3, 30, 34, 5, 9] - print Solution().largestNumber(num) + print(Solution().largestNumber(num)) diff --git a/Python/largest-palindrome-product.py b/Python/largest-palindrome-product.py new file mode 100644 index 000000000..66c4b3fee --- /dev/null +++ b/Python/largest-palindrome-product.py @@ -0,0 +1,32 @@ +# Time: O(10^(2n)) +# Space: O(n) + +# Find the largest palindrome made from the product of two n-digit numbers. +# Since the result could be very large, you should return the largest palindrome mod 1337. +# +# Example: +# Input: 2 +# Output: 987 +# Explanation: 99 x 91 = 9009, 9009 % 1337 = 987 +# +# Note: +# The range of n is [1,8]. + +class Solution_TLE(object): + def largestPalindrome(self, n): + """ + :type n: int + :rtype: int + """ + if n == 1: + return 9 + + upper, lower = 10**n-1, 10**(n-1) + for i in reversed(xrange(lower, upper+1)): + candidate = int(str(i) + str(i)[::-1]) + j = upper + while j * j >= candidate: + if candidate % j == 0: + return candidate % 1337 + j -= 1 + return -1 diff --git a/Python/largest-plus-sign.py b/Python/largest-plus-sign.py new file mode 100644 index 000000000..3d068753c --- /dev/null +++ b/Python/largest-plus-sign.py @@ -0,0 +1,34 @@ +# Time: O(n^2) +# Space: O(n^2) + +class Solution(object): + def orderOfLargestPlusSign(self, N, mines): + """ + :type N: int + :type mines: List[List[int]] + :rtype: int + """ + lookup = {tuple(mine) for mine in mines} + dp = [[0] * N for _ in xrange(N)] + result = 0 + for i in xrange(N): + l = 0 + for j in xrange(N): + l = 0 if (i, j) in lookup else l+1 + dp[i][j] = l + l = 0 + for j in reversed(xrange(N)): + l = 0 if (i, j) in lookup else l+1 + dp[i][j] = min(dp[i][j], l) + + for j in xrange(N): + l = 0 + for i in xrange(N): + l = 0 if (i, j) in lookup else l+1 + dp[i][j] = min(dp[i][j], l) + l = 0 + for i in reversed(xrange(N)): + l = 0 if (i, j) in lookup else l+1 + dp[i][j] = min(dp[i][j], l) + result = max(result, dp[i][j]) + return result diff --git a/Python/largest-rectangle-in-histogram.py b/Python/largest-rectangle-in-histogram.py index 2313b011f..db40803f7 100644 --- a/Python/largest-rectangle-in-histogram.py +++ b/Python/largest-rectangle-in-histogram.py @@ -1,10 +1,11 @@ +from __future__ import print_function # Time: O(n) # Space: O(n) # -# Given n non-negative integers representing the histogram's bar +# Given n non-negative integers representing the histogram's bar # height where the width of each bar is 1, # find the area of largest rectangle in the histogram. -# +# # For example, # Given height = [2,1,5,6,2,3], # return 10. @@ -16,18 +17,18 @@ class Solution: def largestRectangleArea(self, height): increasing, area, i = [], 0, 0 while i <= len(height): - if len(increasing) == 0 or (i < len(height) and height[i] > height[increasing[-1]]): + if not increasing or (i < len(height) and height[i] > height[increasing[-1]]): increasing.append(i) i += 1 else: last = increasing.pop() - if len(increasing) == 0: + if not increasing: area = max(area, height[last] * i) else: area = max(area, height[last] * (i - increasing[-1] - 1 )) return area if __name__ == "__main__": - print Solution().largestRectangleArea([2, 0, 2]) - print Solution().largestRectangleArea([2, 1, 5, 6, 2, 3]) - \ No newline at end of file + print(Solution().largestRectangleArea([2, 0, 2])) + print(Solution().largestRectangleArea([2, 1, 5, 6, 2, 3])) + diff --git a/Python/largest-sum-of-averages.py b/Python/largest-sum-of-averages.py new file mode 100644 index 000000000..48b56640b --- /dev/null +++ b/Python/largest-sum-of-averages.py @@ -0,0 +1,56 @@ +# Time: O(k * n^2) +# Space: O(n) + +# We partition a row of numbers A into at most K adjacent (non-empty) groups, +# then our score is the sum of the average of each group. What is the largest +# score we can achieve? +# +# Note that our partition must use every number in A, and that scores are not +# necessarily integers. +# +# Example: +# Input: +# A = [9,1,2,3,9] +# K = 3 +# Output: 20 +# Explanation: +# The best choice is to partition A into [9], [1, 2, 3], [9]. The answer is +# 39 + (1 + 2 + 3) / 3 + 9 = 20. +# We could have also partitioned A into [9, 1], [2], [3, 9], for example. +# That partition would lead to a score of 5 + 2 + 6 = 13, which is worse. +# +# Note: +# - 1 <= A.length <= 100. +# - 1 <= A[i] <= 10000. +# - 1 <= K <= A.length. +# Answers within 10^-6 of the correct answer will be accepted as correct. + +try: + xrange # Python 2 +except NameError: + xrange = range # Python 3 + + +class Solution(object): + def largestSumOfAverages(self, A, K): + """ + :type A: List[int] + :type K: int + :rtype: float + """ + accum_sum = [A[0]] + for i in xrange(1, len(A)): + accum_sum.append(A[i]+accum_sum[-1]) + + dp = [[0]*len(A) for _ in xrange(2)] + for k in xrange(1, K+1): + for i in xrange(k-1, len(A)): + if k == 1: + dp[k % 2][i] = float(accum_sum[i])/(i+1) + else: + for j in xrange(k-2, i): + dp[k % 2][i] = \ + max(dp[k % 2][i], + dp[(k-1) % 2][j] + + float(accum_sum[i]-accum_sum[j])/(i-j)) + return dp[K % 2][-1] diff --git a/Python/largest-triangle-area.py b/Python/largest-triangle-area.py new file mode 100644 index 000000000..cf8f2b08f --- /dev/null +++ b/Python/largest-triangle-area.py @@ -0,0 +1,44 @@ +# Time: O(n^3) +# Space: O(1) + +# You have a list of points in the plane. Return the area of the largest +# triangle +# that can be formed by any 3 of the points. +# +# Example: +# Input: points = [[0,0],[0,1],[1,0],[0,2],[2,0]] +# Output: 2 +# Explanation: +# The five points are show in the figure below. The red triangle is the +# largest. +# +# Notes: +# - 3 <= points.length <= 50. +# - No points will be duplicated. +# - -50 <= points[i][j] <= 50. +# - Answers within 10^-6 of the true value will be accepted as correct. + +try: + xrange # Python 2 +except NameError: + xrange = range # Python 3 + + +class Solution(object): + def largestTriangleArea(self, points): + """ + :type points: List[List[int]] + :rtype: float + """ + result = 0 + for i in xrange(len(points)-2): + for j in xrange(i+1, len(points)-1): + for k in xrange(j+1, len(points)): + result = max(result, + 0.5 * abs(points[i][0] * points[j][1] + + points[j][0] * points[k][1] + + points[k][0] * points[i][1] - + points[j][0] * points[i][1] - + points[k][0] * points[j][1] - + points[i][0] * points[k][1])) + return result diff --git a/Python/leaf-similar-trees.py b/Python/leaf-similar-trees.py new file mode 100644 index 000000000..07c1b5348 --- /dev/null +++ b/Python/leaf-similar-trees.py @@ -0,0 +1,43 @@ +# Time: O(n) +# Space: O(h) + +# Consider all the leaves of a binary tree. +# From left to right order, +# the values of those leaves form a leaf value sequence. +# +# For example, in the given tree above, the leaf value sequence is (6, 7, 4, 9, 8). +# Two binary trees are considered leaf-similar if their leaf value sequence is the same. +# Return true if and only if the two given trees with head nodes root1 and root2 are leaf-similar. +# +# Note: +# - Both of the given trees will have between 1 and 100 nodes. + +import itertools + + +# Definition for a binary tree node. +class TreeNode(object): + def __init__(self, x): + self.val = x + self.left = None + self.right = None + + +class Solution(object): + def leafSimilar(self, root1, root2): + """ + :type root1: TreeNode + :type root2: TreeNode + :rtype: bool + """ + def dfs(node): + if not node: + return + if not node.left and not node.right: + yield node.val + for i in dfs(node.left): + yield i + for i in dfs(node.right): + yield i + return all(a == b for a, b in + itertools.izip_longest(dfs(root1), dfs(root2))) diff --git a/Python/lemonade-change.py b/Python/lemonade-change.py new file mode 100644 index 000000000..7f00e3d8b --- /dev/null +++ b/Python/lemonade-change.py @@ -0,0 +1,75 @@ +# Time: O(n) +# Space: O(1) + +# At a lemonade stand, each lemonade costs $5. +# +# Customers are standing in a queue to buy from you, +# and order one at a time (in the order specified by bills). +# +# Each customer will only buy one lemonade and pay with either a $5, $10, +# or $20 bill. You must provide the correct change to each customer, +# so that the net transaction is that the customer pays $5. +# +# Note that you don't have any change in hand at first. +# +# Return true if and only if you can provide every customer +# with correct change. +# +# Example 1: +# +# Input: [5,5,5,10,20] +# Output: true +# Explanation: +# From the first 3 customers, we collect three $5 bills in order. +# From the fourth customer, we collect a $10 bill and give back a $5. +# From the fifth customer, we give a $10 bill and a $5 bill. +# Since all customers got correct change, we output true. +# Example 2: +# +# Input: [5,5,10] +# Output: true +# Example 3: +# +# Input: [10,10] +# Output: false +# Example 4: +# +# Input: [5,5,10,10,20] +# Output: false +# Explanation: +# From the first two customers in order, we collect two $5 bills. +# For the next two customers in order, we collect a $10 bill and +# give back a $5 bill. +# For the last customer, we can't give change of $15 back +# because we only have two $10 bills. +# Since not every customer received correct change, the answer is false. +# +# Note: +# - 0 <= bills.length <= 10000 +# - bills[i] will be either 5, 10, or 20. + + +class Solution(object): + def lemonadeChange(self, bills): + """ + :type bills: List[int] + :rtype: bool + """ + five, ten = 0, 0 + for bill in bills: + if bill == 5: + five += 1 + elif bill == 10: + if not five: + return False + five -= 1 + ten += 1 + else: + if ten and five: + ten -= 1 + five -= 1 + elif five >= 3: + five -= 3 + else: + return False + return True diff --git a/Python/length-of-last-word.py b/Python/length-of-last-word.py index 0e713d1a1..8630b312e 100644 --- a/Python/length-of-last-word.py +++ b/Python/length-of-last-word.py @@ -1,13 +1,14 @@ +from __future__ import print_function # Time: O(n) # Space: O(1) # # Given a string s consists of upper/lower-case alphabets and empty space characters ' ', return the length of last word in the string. -# +# # If the last word does not exist, return 0. -# +# # Note: A word is defined as a character sequence consists of non-space characters only. -# -# For example, +# +# For example, # Given s = "Hello World", # return 5. # @@ -34,5 +35,5 @@ def lengthOfLastWord(self, s): return len(s.strip().split(" ")[-1]) if __name__ == "__main__": - print Solution().lengthOfLastWord("Hello World") - print Solution2().lengthOfLastWord("") + print(Solution().lengthOfLastWord("Hello World")) + print(Solution2().lengthOfLastWord("")) diff --git a/Python/length-of-longest-fibonacci-subsequence.py b/Python/length-of-longest-fibonacci-subsequence.py new file mode 100644 index 000000000..f599a7e5f --- /dev/null +++ b/Python/length-of-longest-fibonacci-subsequence.py @@ -0,0 +1,50 @@ +# Time: O(n^2) +# Space: O(n) + +# A sequence X_1, X_2, ..., X_n is fibonacci-like if: +# +# n >= 3 +# X_i + X_{i+1} = X_{i+2} for all i + 2 <= n +# Given a strictly increasing array A of positive integers forming a sequence, +# find the length of the longest fibonacci-like subsequence of A. +# If one does not exist, return 0. +# +# (Recall that a subsequence is derived from another sequence A by +# deleting any number of elements (including none) from A, +# without changing the order of the remaining elements. +# For example, [3, 5, 8] is a subsequence of [3, 4, 5, 6, 7, 8].) +# +# Example 1: +# +# Input: [1,2,3,4,5,6,7,8] +# Output: 5 +# Explanation: +# The longest subsequence that is fibonacci-like: [1,2,3,5,8]. +# Example 2: +# +# Input: [1,3,7,11,12,14,18] +# Output: 3 +# Explanation: +# The longest subsequence that is fibonacci-like: +# [1,11,12], [3,11,14] or [7,11,18]. +# +# Note: +# - 3 <= A.length <= 1000 +# - 1 <= A[0] < A[1] < ... < A[A.length - 1] <= 10^9 +# - (The time limit has been reduced by 50% for submissions in Java, C, and C++.) + +class Solution(object): + def lenLongestFibSubseq(self, A): + """ + :type A: List[int] + :rtype: int + """ + lookup = set(A) + result = 2 + for i in xrange(len(A)): + for j in xrange(i+1, len(A)): + x, y, l = A[i], A[j], 2 + while x+y in lookup: + x, y, l = y, x+y, l+1 + result = max(result, l) + return result if result > 2 else 0 diff --git a/Python/letter-case-permutation.py b/Python/letter-case-permutation.py new file mode 100644 index 000000000..07fe2a9c0 --- /dev/null +++ b/Python/letter-case-permutation.py @@ -0,0 +1,37 @@ +# Time: O(n * 2^n) +# Space: O(n * 2^n) + +# Given a string S, we can transform every letter individually to be lowercase +# or uppercase to create another string. Return a list of all possible strings we could create. +# +# Examples: +# Input: S = "a1b2" +# Output: ["a1b2", "a1B2", "A1b2", "A1B2"] +# +# Input: S = "3z4" +# Output: ["3z4", "3Z4"] +# +# Input: S = "12345" +# Output: ["12345"] +# +# Note: +# - S will be a string with length at most 12. +# - S will consist only of letters or digits. + +class Solution(object): + def letterCasePermutation(self, S): + """ + :type S: str + :rtype: List[str] + """ + result = [[]] + for c in S: + if c.isalpha(): + for i in xrange(len(result)): + result.append(result[i][:]) + result[i].append(c.lower()) + result[-1].append(c.upper()) + else: + for s in result: + s.append(c) + return map("".join, result) diff --git a/Python/letter-combinations-of-a-phone-number.py b/Python/letter-combinations-of-a-phone-number.py index a818ce42b..bc85125db 100644 --- a/Python/letter-combinations-of-a-phone-number.py +++ b/Python/letter-combinations-of-a-phone-number.py @@ -1,12 +1,13 @@ -# Time: O(4^n) -# Space: O(1) +from __future__ import print_function +# Time: O(n * 4^n) +# Space: O(n) # # Given a digit string, return all possible letter combinations that the number could represent. -# +# # A mapping of digit to letters (just like on the telephone buttons) is given below. -# +# # lookup = ["", "", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"] -# +# # Input:Digit string "23" # Output: ["ad", "ae", "af", "bd", "be", "bf", "cd", "ce", "cf"]. # Note: @@ -17,28 +18,36 @@ class Solution: # @return a list of strings, [s1, s2] def letterCombinations(self, digits): - lookup, result = ["", "", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"], [""] + if not digits: + return [] - for digit in digits: + lookup, result = ["", "", "abc", "def", "ghi", "jkl", "mno", \ + "pqrs", "tuv", "wxyz"], [""] + + for digit in reversed(digits): choices = lookup[int(digit)] m, n = len(choices), len(result) - result.extend([result[i % n] for i in xrange(n, m * n)]) + result += [result[i % n] for i in xrange(n, m * n)] for i in xrange(m * n): - result[i] += choices[i / n] - + result[i] = choices[i / n] + result[i] + return result -# Time: O(4^n) + +# Time: O(n * 4^n) # Space: O(n) # Recursive Solution class Solution2: # @return a list of strings, [s1, s2] def letterCombinations(self, digits): - lookup, result = ["", "", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"], [] + if not digits: + return [] + lookup, result = ["", "", "abc", "def", "ghi", "jkl", "mno", \ + "pqrs", "tuv", "wxyz"], [] self.letterCombinationsRecu(result, digits, lookup, "", 0) return result - + def letterCombinationsRecu(self, result, digits, lookup, cur, n): if n == len(digits): result.append(cur) @@ -47,4 +56,4 @@ def letterCombinationsRecu(self, result, digits, lookup, cur, n): self.letterCombinationsRecu(result, digits, lookup, cur + choice, n + 1) if __name__ == "__main__": - print Solution().letterCombinations("23") + print(Solution().letterCombinations("23")) diff --git a/Python/lexicographical-numbers.py b/Python/lexicographical-numbers.py new file mode 100644 index 000000000..20e3f896a --- /dev/null +++ b/Python/lexicographical-numbers.py @@ -0,0 +1,37 @@ +# Time: O(n) +# Space: O(1) + +# Given an integer n, return 1 - n in lexicographical order. +# +# For example, given 13, return: [1,10,11,12,13,2,3,4,5,6,7,8,9]. +# +# Please optimize your algorithm to use less time and space. +# The input size may be as large as 5,000,000. + +class Solution(object): + def lexicalOrder(self, n): + result = [] + + i = 1 + while len(result) < n: + k = 0 + while i * 10**k <= n: + result.append(i * 10**k) + k += 1 + + num = result[-1] + 1 + while num <= n and num % 10: + result.append(num) + num += 1 + + if not num % 10: + num -= 1 + else: + num /= 10 + + while num % 10 == 9: + num /= 10 + + i = num+1 + + return result diff --git a/Python/lfu-cache.py b/Python/lfu-cache.py new file mode 100644 index 000000000..8812da57b --- /dev/null +++ b/Python/lfu-cache.py @@ -0,0 +1,135 @@ +# Time: O(1), per operation +# Space: O(k), k is the capacity of cache + +# Design and implement a data structure for Least Frequently Used (LFU) cache. +# It should support the following operations: get and put. +# +# get(key) - Get the value (will always be positive) of the key +# if the key exists in the cache, otherwise return -1. +# put(key, value) - Set or insert the value if the key is not already present. +# When the cache reaches its capacity, +# it should invalidate the least frequently used item before inserting a new item. +# For the purpose of this problem, when there is a tie +# (i.e., two or more keys that have the same frequency), +# the least recently used key would be evicted. +# +# Follow up: +# Could you do both operations in O(1) time complexity? +# +# Example: +# +# LFUCache cache = new LFUCache( 2 /* capacity */ ); +# +# cache.put(1, 1); +# cache.put(2, 2); +# cache.get(1); // returns 1 +# cache.put(3, 3); // evicts key 2 +# cache.get(2); // returns -1 (not found) +# cache.get(3); // returns 3. +# cache.put(4, 4); // evicts key 1. +# cache.get(1); // returns -1 (not found) +# cache.get(3); // returns 3 +# cache.get(4); // returns 4 + +import collections + + +class ListNode(object): + def __init__(self, key, value, freq): + self.key = key + self.val = value + self.freq = freq + self.next = None + self.prev = None + + +class LinkedList(object): + def __init__(self): + self.head = None + self.tail = None + + def append(self, node): + node.next, node.prev = None, None # avoid dirty node + if self.head is None: + self.head = node + else: + self.tail.next = node + node.prev = self.tail + self.tail = node + + def delete(self, node): + if node.prev: + node.prev.next = node.next + else: + self.head = node.next + if node.next: + node.next.prev = node.prev + else: + self.tail = node.prev + node.next, node.prev = None, None # make node clean + + +class LFUCache(object): + + def __init__(self, capacity): + """ + :type capacity: int + """ + self.__capa = capacity + self.__size = 0 + self.__min_freq = 0 + self.__freq_to_nodes = collections.defaultdict(LinkedList) + self.__key_to_node = {} + + + def get(self, key): + """ + :type key: int + :rtype: int + """ + if key not in self.__key_to_node: + return -1 + + old_node = self.__key_to_node[key] + self.__key_to_node[key] = ListNode(key, old_node.val, old_node.freq) + self.__freq_to_nodes[old_node.freq].delete(old_node) + if not self.__freq_to_nodes[self.__key_to_node[key].freq].head: + del self.__freq_to_nodes[self.__key_to_node[key].freq] + if self.__min_freq == self.__key_to_node[key].freq: + self.__min_freq += 1 + + self.__key_to_node[key].freq += 1 + self.__freq_to_nodes[self.__key_to_node[key].freq].append(self.__key_to_node[key]) + + return self.__key_to_node[key].val + + + def put(self, key, value): + """ + :type key: int + :type value: int + :rtype: void + """ + if self.__capa <= 0: + return + + if self.get(key) != -1: + self.__key_to_node[key].val = value + return + + if self.__size == self.__capa: + del self.__key_to_node[self.__freq_to_nodes[self.__min_freq].head.key] + self.__freq_to_nodes[self.__min_freq].delete(self.__freq_to_nodes[self.__min_freq].head) + if not self.__freq_to_nodes[self.__min_freq].head: + del self.__freq_to_nodes[self.__min_freq] + self.__size -= 1 + + self.__min_freq = 1 + self.__key_to_node[key] = ListNode(key, value, self.__min_freq) + self.__freq_to_nodes[self.__key_to_node[key].freq].append(self.__key_to_node[key]) + self.__size += 1 + +# Your LFUCache object will be instantiated and called as such: +# obj = LFUCache(capacity) +# param_1 = obj.get(key) +# obj.put(key,value) diff --git a/Python/license-key-formatting.py b/Python/license-key-formatting.py new file mode 100644 index 000000000..9c729793b --- /dev/null +++ b/Python/license-key-formatting.py @@ -0,0 +1,51 @@ +# Time: O(n) +# Space: O(1) + +# Now you are given a string S, which represents a software license key which we would like to format. +# The string S is composed of alphanumerical characters and dashes. +# The dashes split the alphanumerical characters within the string into groups. +# (i.e. if there are M dashes, the string is split into M+1 groups). +# The dashes in the given string are possibly misplaced. +# +# We want each group of characters to be of length K +# (except for possibly the first group, which could be shorter, +# but still must contain at least one character). +# To satisfy this requirement, we will reinsert dashes. +# Additionally, all the lower case letters in the string must be converted to upper case. +# +# So, you are given a non-empty string S, representing a license key to format, +# and an integer K. And you need to return the license key formatted according to the description above. +# +# Example 1: +# Input: S = "2-4A0r7-4k", K = 4 +# +# Output: "24A0-R74K" +# +# Explanation: The string S has been split into two parts, each part has 4 characters. +# Example 2: +# Input: S = "2-4A0r7-4k", K = 3 +# +# Output: "24-A0R-74K" +# +# Explanation: The string S has been split into three parts, each part has 3 characters +# except the first part as it could be shorter as said above. +# Note: +# The length of string S will not exceed 12,000, and K is a positive integer. +# String S consists only of alphanumerical characters (a-z and/or A-Z and/or 0-9) and dashes(-). +# String S is non-empty. + +class Solution(object): + def licenseKeyFormatting(self, S, K): + """ + :type S: str + :type K: int + :rtype: str + """ + result = [] + for i in reversed(xrange(len(S))): + if S[i] == '-': + continue + if len(result) % (K + 1) == K: + result += '-' + result += S[i].upper() + return "".join(reversed(result)) diff --git a/Python/line-reflection.py b/Python/line-reflection.py new file mode 100644 index 000000000..9929b8371 --- /dev/null +++ b/Python/line-reflection.py @@ -0,0 +1,54 @@ +# Time: O(n) +# Space: O(n) + +import collections + + +# Hash solution. +class Solution(object): + def isReflected(self, points): + """ + :type points: List[List[int]] + :rtype: bool + """ + if not points: + return True + groups_by_y = collections.defaultdict(set) + left, right = float("inf"), float("-inf") + for p in points: + groups_by_y[p[1]].add(p[0]) + left, right = min(left, p[0]), max(right, p[0]) + mid = left + right + for group in groups_by_y.values(): + for x in group: + if mid - x not in group: + return False + return True + + +# Time: O(nlogn) +# Space: O(n) +# Two pointers solution. +class Solution2(object): + def isReflected(self, points): + """ + :type points: List[List[int]] + :rtype: bool + """ + if not points: + return True + points.sort() + # Space: O(n) + points[len(points)/2:] = sorted(points[len(points)/2:], \ + lambda x, y: y[1] - x[1] if x[0] == y[0] else \ + x[0] - y[0]) + mid = points[0][0] + points[-1][0] + left, right = 0, len(points) - 1 + while left <= right: + if (mid != points[left][0] + points[right][0]) or \ + (points[left][0] != points[right][0] and \ + points[left][1] != points[right][1]): + return False + left += 1 + right -= 1 + return True diff --git a/Python/linked-list-components.py b/Python/linked-list-components.py new file mode 100644 index 000000000..eb49d7589 --- /dev/null +++ b/Python/linked-list-components.py @@ -0,0 +1,60 @@ +# Time: O(m + n), m is the number of G, n is the number of nodes +# Space: O(m) + +# We are given head, the head node of a linked list +# containing unique integer values. +# +# We are also given the list G, a subset of the values in the linked list. +# +# Return the number of connected components in G, +# where two values are connected if they appear consecutively +# in the linked list. +# +# Example 1: +# +# Input: +# head: 0->1->2->3 +# G = [0, 1, 3] +# Output: 2 +# Explanation: +# 0 and 1 are connected, so [0, 1] and [3] are the two connected components. +# Example 2: +# +# Input: +# head: 0->1->2->3->4 +# G = [0, 3, 1, 4] +# Output: 2 +# Explanation: +# 0 and 1 are connected, 3 and 4 are connected, so [0, 1] and [3, 4] are +# the two connected components. +# Note: +# - If N is the length of the linked list given by head, 1 <= N <= 10000. +# - The value of each node in the linked list will be in the range [0, N - 1]. +# - 1 <= G.length <= 10000. +# - G is a subset of all values in the linked list. + + +# Definition for singly-linked list. +class ListNode(object): + def __init__(self, x): + self.val = x + self.next = None + + +class Solution(object): + def numComponents(self, head, G): + """ + :type head: ListNode + :type G: List[int] + :rtype: int + """ + lookup = set(G) + dummy = ListNode(-1) + dummy.next = head + curr = dummy + result = 0 + while curr and curr.next: + if curr.val not in lookup and curr.next.val in lookup: + result += 1 + curr = curr.next + return result diff --git a/Python/linked-list-cycle-ii.py b/Python/linked-list-cycle-ii.py index 74ea144ca..3cfb64a41 100644 --- a/Python/linked-list-cycle-ii.py +++ b/Python/linked-list-cycle-ii.py @@ -1,8 +1,9 @@ +from __future__ import print_function # Time: O(n) # Space: O(1) # # Given a linked list, return the node where the cycle begins. If there is no cycle, return null. -# +# # Follow up: # Can you solve it without using extra space? # @@ -12,13 +13,13 @@ class ListNode: def __init__(self, x): self.val = x self.next = None - + def __str__(self): if self: return "{}".format(self.val) else: return None - + class Solution: # @param head, a ListNode # @return a list node @@ -38,4 +39,4 @@ def detectCycle(self, head): head.next = ListNode(2) head.next.next = ListNode(3) head.next.next.next = head.next - print Solution().detectCycle(head) \ No newline at end of file + print(Solution().detectCycle(head)) \ No newline at end of file diff --git a/Python/linked-list-cycle.py b/Python/linked-list-cycle.py index b2288624d..8910df7c1 100644 --- a/Python/linked-list-cycle.py +++ b/Python/linked-list-cycle.py @@ -1,8 +1,9 @@ +from __future__ import print_function # Time: O(n) # Space: O(1) # # Given a linked list, determine if it has a cycle in it. -# +# # Follow up: # Can you solve it without using extra space? # @@ -29,4 +30,4 @@ def hasCycle(self, head): head.next = ListNode(2) head.next.next = ListNode(3) head.next.next.next = head.next - print Solution().hasCycle(head) \ No newline at end of file + print(Solution().hasCycle(head)) \ No newline at end of file diff --git a/Python/linked-list-random-node.py b/Python/linked-list-random-node.py new file mode 100644 index 000000000..7ac0718fc --- /dev/null +++ b/Python/linked-list-random-node.py @@ -0,0 +1,53 @@ +# Time: O(n) +# Space: O(1) + +# Given a singly linked list, return a random node's value from the linked list. +# Each node must have the same probability of being chosen. +# +# Follow up: +# What if the linked list is extremely large and its length is unknown to you? +# Could you solve this efficiently without using extra space? +# +# Example: +# +# // Init a singly linked list [1,2,3]. +# ListNode head = new ListNode(1); +# head.next = new ListNode(2); +# head.next.next = new ListNode(3); +# Solution solution = new Solution(head); +# +# // getRandom() should return either 1, 2, or 3 randomly. +# Each element should have equal probability of returning. +# solution.getRandom(); + + +from random import randint + +class Solution(object): + + def __init__(self, head): + """ + @param head The linked list's head. Note that the head is guanranteed to be not null, so it contains at least one node. + :type head: ListNode + """ + self.__head = head + + + # Proof of Reservoir Sampling: + # https://discuss.leetcode.com/topic/53753/brief-explanation-for-reservoir-sampling + def getRandom(self): + """ + Returns a random node's value. + :rtype: int + """ + reservoir = -1 + curr, n = self.__head, 0 + while curr: + reservoir = curr.val if randint(1, n+1) == 1 else reservoir + curr, n = curr.next, n+1 + return reservoir + + +# Your Solution object will be instantiated and called as such: +# obj = Solution(head) +# param_1 = obj.getRandom() diff --git a/Python/logger-rate-limiter.py b/Python/logger-rate-limiter.py new file mode 100644 index 000000000..7fe8abc07 --- /dev/null +++ b/Python/logger-rate-limiter.py @@ -0,0 +1,34 @@ +# Time: O(1), amortized +# Space: O(k), k is the max number of printed messages in last 10 seconds + +import collections + + +class Logger(object): + + def __init__(self): + """ + Initialize your data structure here. + """ + self.__dq = collections.deque() + self.__printed = set() + + def shouldPrintMessage(self, timestamp, message): + """ + Returns true if the message should be printed in the given timestamp, otherwise returns false. The timestamp is in seconds granularity. + :type timestamp: int + :type message: str + :rtype: bool + """ + while self.__dq and self.__dq[0][0] <= timestamp - 10: + self.__printed.remove(self.__dq.popleft()[1]) + if message in self.__printed: + return False + self.__dq.append((timestamp, message)) + self.__printed.add(message) + return True + + +# Your Logger object will be instantiated and called as such: +# obj = Logger() +# param_1 = obj.shouldPrintMessage(timestamp,message) diff --git a/Python/lonely-pixel-i.py b/Python/lonely-pixel-i.py new file mode 100644 index 000000000..28f8ba9a1 --- /dev/null +++ b/Python/lonely-pixel-i.py @@ -0,0 +1,33 @@ +# Time: O(m * n) +# Space: O(m + n) + +class Solution(object): + def findLonelyPixel(self, picture): + """ + :type picture: List[List[str]] + :rtype: int + """ + rows, cols = [0] * len(picture), [0] * len(picture[0]) + for i in xrange(len(picture)): + for j in xrange(len(picture[0])): + if picture[i][j] == 'B': + rows[i] += 1 + cols[j] += 1 + + result = 0 + for i in xrange(len(picture)): + if rows[i] == 1: + for j in xrange(len(picture[0])): + result += picture[i][j] == 'B' and cols[j] == 1 + return result + + +class Solution2(object): + def findLonelyPixel(self, picture): + """ + :type picture: List[List[str]] + :type N: int + :rtype: int + """ + return sum(col.count('B') == 1 == picture[col.index('B')].count('B') \ + for col in zip(*picture)) diff --git a/Python/lonely-pixel-ii.py b/Python/lonely-pixel-ii.py new file mode 100644 index 000000000..e4046be81 --- /dev/null +++ b/Python/lonely-pixel-ii.py @@ -0,0 +1,42 @@ +# Time: O(m * n) +# Space: O(m * n) + +import collections + + +class Solution(object): + def findBlackPixel(self, picture, N): + """ + :type picture: List[List[str]] + :type N: int + :rtype: int + """ + rows, cols = [0] * len(picture), [0] * len(picture[0]) + lookup = collections.defaultdict(int) + for i in xrange(len(picture)): + for j in xrange(len(picture[0])): + if picture[i][j] == 'B': + rows[i] += 1 + cols[j] += 1 + lookup[tuple(picture[i])] += 1 + + result = 0 + for i in xrange(len(picture)): + if rows[i] == N and lookup[tuple(picture[i])] == N: + for j in xrange(len(picture[0])): + result += picture[i][j] == 'B' and cols[j] == N + return result + + +class Solution2(object): + def findBlackPixel(self, picture, N): + """ + :type picture: List[List[str]] + :type N: int + :rtype: int + """ + lookup = collections.Counter(map(tuple, picture)) + cols = [col.count('B') for col in zip(*picture)] + return sum(N * zip(row, cols).count(('B', N)) \ + for row, cnt in lookup.iteritems() \ + if cnt == N == row.count('B')) diff --git a/Python/longest-absolute-file-path.py b/Python/longest-absolute-file-path.py new file mode 100644 index 000000000..065458aa2 --- /dev/null +++ b/Python/longest-absolute-file-path.py @@ -0,0 +1,68 @@ +# Time: O(n) +# Space: O(d), d is the max depth of the paths + +# Suppose we abstract our file system by a string in the following manner: +# +# The string "dir\n\tsubdir1\n\tsubdir2\n\t\tfile.ext" represents: +# +# dir +# subdir1 +# subdir2 +# file.ext +# The directory dir contains an empty sub-directory subdir1 and a sub-directory subdir2 containing a file file.ext. +# +# The string "dir\n\tsubdir1\n\t\tfile1.ext\n\t\tsubsubdir1\n\tsubdir2\n\t\tsubsubdir2\n\t\t\tfile2.ext" represents: +# +# dir +# subdir1 +# file1.ext +# subsubdir1 +# subdir2 +# subsubdir2 +# file2.ext +# The directory dir contains two sub-directories subdir1 and subdir2. +# subdir1 contains a file file1.ext and an empty second-level sub-directory subsubdir1. +# subdir2 contains a second-level sub-directory subsubdir2 containing a file file2.ext. +# +# We are interested in finding the longest (number of characters) absolute path to a file within our file system. +# For example, in the second example above, the longest absolute path is "dir/subdir2/subsubdir2/file2.ext", +# and its length is 32 (not including the double quotes). +# +# Given a string representing the file system in the above format, +# return the length of the longest absolute path to file in the abstracted file system. +# If there is no file in the system, return 0. +# +# Note: +# The name of a file contains at least a . and an extension. +# The name of a directory or sub-directory will not contain a .. +# Time complexity required: O(n) where n is the size of the input string. +# +# Notice that a/aa/aaa/file1.txt is not the longest file path, if there is +# another path aaaaaaaaaaaaaaaaaaaaa/sth.png. + + +class Solution(object): + def lengthLongestPath(self, input): + """ + :type input: str + :rtype: int + """ + def split_iter(s, tok): + start = 0 + for i in xrange(len(s)): + if s[i] == tok: + yield s[start:i] + start = i + 1 + yield s[start:] + + + max_len = 0 + path_len = {0: 0} + for line in split_iter(input, '\n'): + name = line.lstrip('\t') + depth = len(line) - len(name) + if '.' in name: + max_len = max(max_len, path_len[depth] + len(name)) + else: + path_len[depth + 1] = path_len[depth] + len(name) + 1 + return max_len diff --git a/Python/longest-common-prefix.py b/Python/longest-common-prefix.py index 2ffa96cfb..a05860d10 100644 --- a/Python/longest-common-prefix.py +++ b/Python/longest-common-prefix.py @@ -1,22 +1,25 @@ -# Time: O(n1 + n2 + ...) +from __future__ import print_function +# Time: O(n * k), k is the length of the common prefix # Space: O(1) -# -# Write a function to find the longest common prefix string amongst an array of strings. -# -class Solution: - # @return a string +# Write a function to find the longest common prefix string +# amongst an array of strings. + +class Solution(object): def longestCommonPrefix(self, strs): - if len(strs) == 0: + """ + :type strs: List[str] + :rtype: str + """ + if not strs: return "" - longest = strs[0] - for string in strs[1:]: - i = 0 - while i < len(string) and i < len(longest) and string[i] == longest[i]: - i += 1 - longest = longest[:i] - return longest - + + for i in xrange(len(strs[0])): + for string in strs[1:]: + if i >= len(string) or string[i] != strs[0][i]: + return strs[0][:i] + return strs[0] + + if __name__ == "__main__": - print Solution().longestCommonPrefix(["hello", "heaven", "heavy"]) - \ No newline at end of file + print(Solution().longestCommonPrefix(["hello", "heaven", "heavy"])) diff --git a/Python/longest-consecutive-sequence.py b/Python/longest-consecutive-sequence.py index df70be6e9..b8d05a320 100644 --- a/Python/longest-consecutive-sequence.py +++ b/Python/longest-consecutive-sequence.py @@ -1,12 +1,13 @@ +from __future__ import print_function # Time: O(n) # Space: O(n) # # Given an unsorted array of integers, find the length of the longest consecutive elements sequence. -# +# # For example, # Given [100, 4, 200, 1, 3, 2], # The longest consecutive elements sequence is [1, 2, 3, 4]. Return its length: 4. -# +# # Your algorithm should run in O(n) complexity. # @@ -24,4 +25,4 @@ def longestConsecutive(self, num): return result if __name__ == "__main__": - print Solution().longestConsecutive([100, 4, 200, 1, 3, 2]) \ No newline at end of file + print(Solution().longestConsecutive([100, 4, 200, 1, 3, 2])) \ No newline at end of file diff --git a/Python/longest-continuous-increasing-subsequence.py b/Python/longest-continuous-increasing-subsequence.py new file mode 100644 index 000000000..9ae2635e1 --- /dev/null +++ b/Python/longest-continuous-increasing-subsequence.py @@ -0,0 +1,32 @@ +# Time: O(n) +# Space: O(1) + +# Given an unsorted array of integers, +# find the length of longest continuous increasing subsequence. +# +# Example 1: +# Input: [1,3,5,4,7] +# Output: 3 +# Explanation: The longest continuous increasing subsequence is [1,3,5], its length is 3. +# Even though [1,3,5,7] is also an increasing subsequence, +# it's not a continuous one where 5 and 7 are separated by 4. +# Example 2: +# Input: [2,2,2,2,2] +# Output: 1 +# Explanation: The longest continuous increasing subsequence is [2], its length is 1. +# Note: Length of the array will not exceed 10,000. + +class Solution(object): + def findLengthOfLCIS(self, nums): + """ + :type nums: List[int] + :rtype: int + """ + result, count = 0, 0 + for i in xrange(len(nums)): + if i == 0 or nums[i-1] < nums[i]: + count += 1 + result = max(result, count) + else: + count = 1 + return result diff --git a/Python/longest-harmonious-subsequence.py b/Python/longest-harmonious-subsequence.py new file mode 100644 index 000000000..7f394cf97 --- /dev/null +++ b/Python/longest-harmonious-subsequence.py @@ -0,0 +1,32 @@ +# Time: O(n) +# Space: O(n) + +# We define a harmonious array is an array where the difference +# between its maximum value and its minimum value is exactly 1. +# +# Now, given an integer array, you need to find the length of its +# longest harmonious subsequence among all its possible subsequences. +# +# Example 1: +# Input: [1,3,2,2,5,2,3,7] +# Output: 5 +# Explanation: The longest harmonious subsequence is [3,2,2,2,3]. +# Note: The length of the input array will not exceed 20,000. + +import collections + + +class Solution(object): + def findLHS(self, nums): + """ + :type nums: List[int] + :rtype: int + """ + lookup = collections.defaultdict(int) + result = 0 + for num in nums: + lookup[num] += 1 + for diff in [-1, 1]: + if (num + diff) in lookup: + result = max(result, lookup[num] + lookup[num + diff]) + return result diff --git a/Python/longest-increasing-path-in-a-matrix.py b/Python/longest-increasing-path-in-a-matrix.py new file mode 100644 index 000000000..c95688f2e --- /dev/null +++ b/Python/longest-increasing-path-in-a-matrix.py @@ -0,0 +1,60 @@ +# Time: O(m * n) +# Space: O(m * n) + +# Given an integer matrix, find the length of the longest increasing path. +# +# From each cell, you can either move to four directions: left, right, up +# or down. You may NOT move diagonally or move outside of the boundary +# (i.e. wrap-around is not allowed). +# +# Example 1: +# +# nums = [ +# [9,9,4], +# [6,6,8], +# [2,1,1] +# ] +# Return 4 +# The longest increasing path is [1, 2, 6, 9]. +# +# Example 2: +# +# nums = [ +# [3,4,5], +# [3,2,6], +# [2,2,1] +# ] +# Return 4 +# The longest increasing path is [3, 4, 5, 6]. Moving diagonally is not allowed. + +# DFS + Memorization solution. +class Solution(object): + def longestIncreasingPath(self, matrix): + """ + :type matrix: List[List[int]] + :rtype: int + """ + if not matrix: + return 0 + + def longestpath(matrix, i, j, max_lengths): + if max_lengths[i][j]: + return max_lengths[i][j] + + max_depth = 0 + directions = [(0, -1), (0, 1), (-1, 0), (1, 0)] + for d in directions: + x, y = i + d[0], j + d[1] + if 0 <= x < len(matrix) and 0 <= y < len(matrix[0]) and \ + matrix[x][y] < matrix[i][j]: + max_depth = max(max_depth, longestpath(matrix, x, y, max_lengths)); + max_lengths[i][j] = max_depth + 1 + return max_lengths[i][j] + + res = 0 + max_lengths = [[0 for _ in xrange(len(matrix[0]))] for _ in xrange(len(matrix))] + for i in xrange(len(matrix)): + for j in xrange(len(matrix[0])): + res = max(res, longestpath(matrix, i, j, max_lengths)) + + return res diff --git a/Python/longest-increasing-subsequence.py b/Python/longest-increasing-subsequence.py new file mode 100644 index 000000000..f176202be --- /dev/null +++ b/Python/longest-increasing-subsequence.py @@ -0,0 +1,61 @@ +# Time: O(nlogn) +# Space: O(n) +# +# Given an unsorted array of integers, +# find the length of longest increasing subsequence. +# +# For example, +# Given [10, 9, 2, 5, 3, 7, 101, 18], +# The longest increasing subsequence is [2, 3, 7, 101], +# therefore the length is 4. Note that there may be more +# than one LIS combination, it is only necessary for you to return the length. +# +# Your algorithm should run in O(n2) complexity. +# +# Follow up: Could you improve it to O(n log n) time complexity? +# + +# Binary search solution. +class Solution(object): + def lengthOfLIS(self, nums): + """ + :type nums: List[int] + :rtype: int + """ + LIS = [] + def insert(target): + left, right = 0, len(LIS) - 1 + # Find the first index "left" which satisfies LIS[left] >= target + while left <= right: + mid = left + (right - left) // 2 + if LIS[mid] >= target: + right = mid - 1 + else: + left = mid + 1 + # If not found, append the target. + if left == len(LIS): + LIS.append(target); + else: + LIS[left] = target + + for num in nums: + insert(num) + + return len(LIS) + +# Time: O(n^2) +# Space: O(n) +# Traditional DP solution. +class Solution2(object): + def lengthOfLIS(self, nums): + """ + :type nums: List[int] + :rtype: int + """ + dp = [] # dp[i]: the length of LIS ends with nums[i] + for i in xrange(len(nums)): + dp.append(1) + for j in xrange(i): + if nums[j] < nums[i]: + dp[i] = max(dp[i], dp[j] + 1) + return max(dp) if dp else 0 diff --git a/Python/longest-line-of-consecutive-one-in-a-matrix.py b/Python/longest-line-of-consecutive-one-in-a-matrix.py new file mode 100644 index 000000000..4f6845c00 --- /dev/null +++ b/Python/longest-line-of-consecutive-one-in-a-matrix.py @@ -0,0 +1,22 @@ +# Time: O(m * n) +# Space: O(n) + +class Solution(object): + def longestLine(self, M): + """ + :type M: List[List[int]] + :rtype: int + """ + if not M: return 0 + result = 0 + dp = [[[0] * 4 for _ in xrange(len(M[0]))] for _ in xrange(2)] + for i in xrange(len(M)): + for j in xrange(len(M[0])): + dp[i % 2][j][:] = [0] * 4 + if M[i][j] == 1: + dp[i % 2][j][0] = dp[i % 2][j - 1][0]+1 if j > 0 else 1 + dp[i % 2][j][1] = dp[(i-1) % 2][j][1]+1 if i > 0 else 1 + dp[i % 2][j][2] = dp[(i-1) % 2][j-1][2]+1 if (i > 0 and j > 0) else 1 + dp[i % 2][j][3] = dp[(i-1) % 2][j+1][3]+1 if (i > 0 and j < len(M[0])-1) else 1 + result = max(result, max(dp[i % 2][j])) + return result diff --git a/Python/longest-mountain-in-array.py b/Python/longest-mountain-in-array.py new file mode 100644 index 000000000..2fe19618d --- /dev/null +++ b/Python/longest-mountain-in-array.py @@ -0,0 +1,51 @@ +# Time: O(n) +# Space: O(1) + +# Let's call any (contiguous) subarray B (of A) a mountain +# if the following properties hold: +# +# B.length >= 3 +# There exists some 0 < i < B.length - 1 +# such that B[0] < B[1] < ... B[i-1] < B[i] > B[i+1] > ... > B[B.length - 1] +# (Note that B could be any subarray of A, including the entire array A.) +# +# Given an array A of integers, return the length of the longest mountain. +# +# Return 0 if there is no mountain. +# +# Example 1: +# +# Input: [2,1,4,7,3,2,5] +# Output: 5 +# Explanation: The largest mountain is [1,4,7,3,2] which has length 5. +# Example 2: +# +# Input: [2,2,2] +# Output: 0 +# Explanation: There is no mountain. +# +# Note: +# - 0 <= A.length <= 10000 +# - 0 <= A[i] <= 10000 + +try: + xrange # Python 2 +except NameError: + xrange = range # Python 3 + + +class Solution(object): + def longestMountain(self, A): + """ + :type A: List[int] + :rtype: int + """ + result, up_len, down_len = 0, 0, 0 + for i in xrange(1, len(A)): + if (down_len and A[i-1] < A[i]) or A[i-1] == A[i]: + up_len, down_len = 0, 0 + up_len += A[i-1] < A[i] + down_len += A[i-1] > A[i] + if up_len and down_len: + result = max(result, up_len+down_len+1) + return result diff --git a/Python/longest-palindrome.py b/Python/longest-palindrome.py new file mode 100644 index 000000000..9f672acf5 --- /dev/null +++ b/Python/longest-palindrome.py @@ -0,0 +1,42 @@ +# Time: O(n) +# Space: O(1) + +# Given a string which consists of lowercase or uppercase letters, +# find the length of the longest palindromes that can be built with those letters. +# +# This is case sensitive, for example "Aa" is not considered a palindrome here. +# +# Note: +# Assume the length of given string will not exceed 1,010. +# +# Example: +# +# Input: +# "abccccdd" +# +# Output: +# 7 +# +# Explanation: +# One longest palindrome that can be built is "dccaccd", whose length is 7. +import collections + + +class Solution(object): + def longestPalindrome(self, s): + """ + :type s: str + :rtype: int + """ + odds = 0 + for k, v in collections.Counter(s).iteritems(): + odds += v & 1 + return len(s) - odds + int(odds > 0) + + def longestPalindrome2(self, s): + """ + :type s: str + :rtype: int + """ + odd = sum(map(lambda x: x & 1, collections.Counter(s).values())) + return len(s) - odd + int(odd > 0) diff --git a/Python/longest-palindromic-subsequence.py b/Python/longest-palindromic-subsequence.py new file mode 100644 index 000000000..2c0cd5f5c --- /dev/null +++ b/Python/longest-palindromic-subsequence.py @@ -0,0 +1,37 @@ +# Time: O(n^2) +# Space: O(n) + +# Given a string s, find the longest palindromic subsequence's length in s. +# You may assume that the maximum length of s is 1000. +# +# Example 1: +# Input: +# +# "bbbab" +# Output: +# 4 +# One possible longest palindromic subsequence is "bbbb". +# Example 2: +# Input: +# +# "cbbd" +# Output: +# 2 + +class Solution(object): + def longestPalindromeSubseq(self, s): + """ + :type s: str + :rtype: int + """ + if s == s[::-1]: + return len(s) + + dp = [[1] * len(s) for _ in xrange(2)] + for i in reversed(xrange(len(s))): + for j in xrange(i+1, len(s)): + if s[i] == s[j]: + dp[i%2][j] = 2 + dp[(i+1)%2][j-1] if i+1 <= j-1 else 2 + else: + dp[i%2][j] = max(dp[(i+1)%2][j], dp[i%2][j-1]) + return dp[0][-1] diff --git a/Python/longest-palindromic-substring.py b/Python/longest-palindromic-substring.py index e882d6bf6..7eae3f3f2 100644 --- a/Python/longest-palindromic-substring.py +++ b/Python/longest-palindromic-substring.py @@ -1,3 +1,4 @@ +from __future__ import print_function # Time: O(n) # Space: O(n) # @@ -8,40 +9,44 @@ # Manacher's Algorithm # http://leetcode.com/2011/11/longest-palindromic-substring-part-ii.html -class Solution: +class Solution(object): def longestPalindrome(self, s): - string = self.preProcess(s) - palindrome = [0] * len(string) + """ + :type s: str + :rtype: str + """ + def preProcess(s): + if not s: + return ['^', '$'] + T = ['^'] + for c in s: + T += ['#', c] + T += ['#', '$'] + return T + + T = preProcess(s) + P = [0] * len(T) center, right = 0, 0 - for i in xrange(1, len(string) - 1): + for i in xrange(1, len(T) - 1): i_mirror = 2 * center - i if right > i: - palindrome[i] = min(right - i, palindrome[i_mirror]) + P[i] = min(right - i, P[i_mirror]) else: - palindrome[i] = 0 + P[i] = 0 + + while T[i + 1 + P[i]] == T[i - 1 - P[i]]: + P[i] += 1 + + if i + P[i] > right: + center, right = i, i + P[i] + + max_i = 0 + for i in xrange(1, len(T) - 1): + if P[i] > P[max_i]: + max_i = i + start = (max_i - 1 - P[max_i]) / 2 + return s[start : start + P[max_i]] - while string[i + 1 + palindrome[i]] == string[i - 1 - palindrome[i]]: - palindrome[i] += 1 - if i + palindrome[i] > right: - center, right = i, i + palindrome[i] - - max_len, max_center = 0, 0 - for i in xrange(1, len(string) - 1): - if palindrome[i] > max_len: - max_len = palindrome[i] - max_center = i - start = (max_center - 1 - max_len) / 2 - return s[start : start + max_len] - - def preProcess(self, s): - if len(s) == 0: - return "^$" - string = "^" - for i in s: - string += "#" + i - string += "#$" - return string - if __name__ == "__main__": - print Solution().longestPalindrome("abb") \ No newline at end of file + print(Solution().longestPalindrome("abb")) diff --git a/Python/longest-repeating-character-replacement.py b/Python/longest-repeating-character-replacement.py new file mode 100644 index 000000000..64810432f --- /dev/null +++ b/Python/longest-repeating-character-replacement.py @@ -0,0 +1,57 @@ +# Time: O(n) +# Space: O(1) + +# Given a string that consists of only uppercase English letters, +# you can replace any letter in the string with another letter at most k times. +# Find the length of a longest substring containing all repeating letters +# you can get after performing the above operations. +# +# Note: +# Both the string's length and k will not exceed 104. +# +# Example 1: +# +# Input: +# s = "ABAB", k = 2 +# +# Output: +# 4 +# +# Explanation: +# Replace the two 'A's with two 'B's or vice versa. +# Example 2: +# +# Input: +# s = "AABABBA", k = 1 +# +# Output: +# 4 +# +# Explanation: +# Replace the one 'A' in the middle with 'B' and form "AABBBBA". +# The substring "BBBB" has the longest repeating letters, which is 4. + +class Solution(object): + def characterReplacement(self, s, k): + """ + :type s: str + :type k: int + :rtype: int + """ + res = 0 + + cnts = [0] * 26 + times, i, j = k, 0, 0 + while j < len(s): + cnts[ord(s[j]) - ord('A')] += 1 + if s[j] != s[i]: + times -= 1 + if times < 0: + res = max(res, j - i) + while i < j and times < 0: + cnts[ord(s[i]) - ord('A')] -= 1 + i += 1 + times = k - (j - i + 1 - cnts[ord(s[i]) - ord('A')]) + j += 1 + + return max(res, j - i + min(i, times)) diff --git a/Python/longest-substring-with-at-least-k-repeating-characters.py b/Python/longest-substring-with-at-least-k-repeating-characters.py new file mode 100644 index 000000000..d17bae4d2 --- /dev/null +++ b/Python/longest-substring-with-at-least-k-repeating-characters.py @@ -0,0 +1,55 @@ +# Time: O(26 * n) = O(n) +# Space: O(26) = O(1) + +# Find the length of the longest substring T of a given string +# (consists of lowercase letters only) such that every character in T +# appears no less than k times. +# +# Example 1: +# +# Input: +# s = "aaabb", k = 3 +# +# Output: +# 3 +# +# The longest substring is "aaa", as 'a' is repeated 3 times. +# Example 2: +# +# Input: +# s = "ababbc", k = 2 +# +# Output: +# 5 +# +# The longest substring is "ababb", as 'a' is repeated 2 times and 'b' is repeated 3 times. + +# Recursive solution. +class Solution(object): + def longestSubstring(self, s, k): + """ + :type s: str + :type k: int + :rtype: int + """ + def longestSubstringHelper(s, k, start, end): + count = [0] * 26 + for i in xrange(start, end): + count[ord(s[i]) - ord('a')] += 1 + max_len = 0 + i = start + while i < end: + while i < end and count[ord(s[i]) - ord('a')] < k: + i += 1 + j = i + while j < end and count[ord(s[j]) - ord('a')] >= k: + j += 1 + + if i == start and j == end: + return end - start + + max_len = max(max_len, longestSubstringHelper(s, k, i, j)) + i = j + return max_len + + return longestSubstringHelper(s, k, 0, len(s)) diff --git a/Python/longest-substring-with-at-most-k-distinct-characters.py b/Python/longest-substring-with-at-most-k-distinct-characters.py new file mode 100644 index 000000000..fd974b83c --- /dev/null +++ b/Python/longest-substring-with-at-most-k-distinct-characters.py @@ -0,0 +1,24 @@ +# Time: O(n) +# Space: O(1) + +class Solution(object): + def lengthOfLongestSubstringKDistinct(self, s, k): + """ + :type s: str + :type k: int + :rtype: int + """ + longest, start, distinct_count, visited = 0, 0, 0, [0 for _ in xrange(256)] + for i, char in enumerate(s): + if visited[ord(char)] == 0: + distinct_count += 1 + visited[ord(char)] += 1 + + while distinct_count > k: + visited[ord(s[start])] -= 1 + if visited[ord(s[start])] == 0: + distinct_count -= 1 + start += 1 + + longest = max(longest, i - start + 1) + return longest diff --git a/Python/longest-substring-with-at-most-two-distinct-characters.py b/Python/longest-substring-with-at-most-two-distinct-characters.py index 6ffc113c2..9f9afb8ee 100644 --- a/Python/longest-substring-with-at-most-two-distinct-characters.py +++ b/Python/longest-substring-with-at-most-two-distinct-characters.py @@ -1,11 +1,12 @@ -# Time: O(n^2) +from __future__ import print_function +# Time: O(n) # Space: O(1) # -# Given a string, find the length of the longest substring T +# Given a string, find the length of the longest substring T # that contains at most 2 distinct characters. -# +# # For example, Given s = "eceba", -# +# # T is "ece" which its length is 3. # @@ -18,15 +19,15 @@ def lengthOfLongestSubstringTwoDistinct(self, s): if visited[ord(char)] == 0: distinct_count += 1 visited[ord(char)] += 1 - + while distinct_count > 2: visited[ord(s[start])] -= 1 if visited[ord(s[start])] == 0: distinct_count -= 1 start += 1 - + longest = max(longest, i - start + 1) return longest - + if __name__ == "__main__": - print Solution().lengthOfLongestSubstringTwoDistinct("eceba") \ No newline at end of file + print(Solution().lengthOfLongestSubstringTwoDistinct("eceba")) diff --git a/Python/longest-substring-without-repeating-characters.py b/Python/longest-substring-without-repeating-characters.py index c7f711c73..29f7aceaf 100644 --- a/Python/longest-substring-without-repeating-characters.py +++ b/Python/longest-substring-without-repeating-characters.py @@ -1,8 +1,9 @@ +from __future__ import print_function # Time: O(n) # Space: O(1) # # Given a string, find the length of the longest substring without repeating characters. -# For example, the longest substring without repeating letters for "abcabcbb" is "abc", which the length is 3. +# For example, the longest substring without repeating letters for "abcabcbb" is "abc", which the length is 3. # For "bbbbb" the longest substring is "b", with the length of 1. # @@ -22,4 +23,4 @@ def lengthOfLongestSubstring(self, s): return longest if __name__ == "__main__": - print Solution().lengthOfLongestSubstring("abcabcbb") + print(Solution().lengthOfLongestSubstring("abcabcbb")) diff --git a/Python/longest-uncommon-subsequence-i.py b/Python/longest-uncommon-subsequence-i.py new file mode 100644 index 000000000..9d64937f1 --- /dev/null +++ b/Python/longest-uncommon-subsequence-i.py @@ -0,0 +1,35 @@ +# Time: O(min(a, b)) +# Space: O(1) + +# Given a group of two strings, you need to find the longest uncommon subsequence of this group of two strings. +# The longest uncommon subsequence is defined as the longest subsequence of one of these strings +# and this subsequence should not be any subsequence of the other strings. +# +# A subsequence is a sequence that can be derived from one sequence +# by deleting some characters without changing the order of the remaining elements. +# Trivially, any string is a subsequence of itself and an empty string is a subsequence of any string. +# +# The input will be two strings, and the output needs to be the length of the longest uncommon subsequence. +# If the longest uncommon subsequence doesn't exist, return -1. +# +# Example 1: +# Input: "aba", "cdc" +# Output: 3 +# Explanation: The longest uncommon subsequence is "aba" (or "cdc"), +# because "aba" is a subsequence of "aba", +# but not a subsequence of any other strings in the group of two strings. +# Note: +# +# Both strings' lengths will not exceed 100. +# Only letters from a ~ z will appear in input strings. + +class Solution(object): + def findLUSlength(self, a, b): + """ + :type a: str + :type b: str + :rtype: int + """ + if a == b: + return -1 + return max(len(a), len(b)) diff --git a/Python/longest-uncommon-subsequence-ii.py b/Python/longest-uncommon-subsequence-ii.py new file mode 100644 index 000000000..c73d74a3f --- /dev/null +++ b/Python/longest-uncommon-subsequence-ii.py @@ -0,0 +1,49 @@ +# Time: O(l * n^2) +# Space: O(1) + +# Given a list of strings, you need to find the longest uncommon subsequence among them. +# The longest uncommon subsequence is defined as the longest subsequence of one of these strings +# and this subsequence should not be any subsequence of the other strings. +# +# A subsequence is a sequence that can be derived from one sequence +# by deleting some characters without changing the order of the remaining elements. +# Trivially, any string is a subsequence of itself and an empty string is a subsequence of any string. +# +# The input will be a list of strings, and the output needs to be the length of the longest uncommon subsequence. +# If the longest uncommon subsequence doesn't exist, return -1. +# +# Example 1: +# Input: "aba", "cdc", "eae" +# Output: 3 +# Note: +# +# All the given strings' lengths will not exceed 10. +# The length of the given list will be in the range of [2, 50]. + +class Solution(object): + def findLUSlength(self, strs): + """ + :type strs: List[str] + :rtype: int + """ + def isSubsequence(a, b): + i = 0 + for j in xrange(len(b)): + if i >= len(a): + break + if a[i] == b[j]: + i += 1 + return i == len(a) + + strs.sort(key=len, reverse=True) + for i in xrange(len(strs)): + all_of = True + for j in xrange(len(strs)): + if len(strs[j]) < len(strs[i]): + break + if i != j and isSubsequence(strs[i], strs[j]): + all_of = False + break + if all_of: + return len(strs[i]) + return -1 diff --git a/Python/longest-univalue-path.py b/Python/longest-univalue-path.py new file mode 100644 index 000000000..d7fd3b9b9 --- /dev/null +++ b/Python/longest-univalue-path.py @@ -0,0 +1,57 @@ +# Time: O(n) +# Space: O(h) + +# Given a binary tree, find the length of the longest path +# where each node in the path has the same value. This path may or may not pass through the root. +# +# Note: The length of path between two nodes is represented by the number of edges between them. +# +# Example 1: +# Input: +# +# 5 +# / \ +# 4 5 +# / \ \ +# 1 1 5 +# Output: +# 2 +# +# Example 2: +# Input: +# +# 1 +# / \ +# 4 5 +# / \ \ +# 4 4 5 +# Output: +# 2 +# +# Note: The given binary tree has not more than 10000 nodes. The height of the tree is not more than 1000. + +# Definition for a binary tree node. +# class TreeNode(object): +# def __init__(self, x): +# self.val = x +# self.left = None +# self.right = None + +class Solution(object): + def longestUnivaluePath(self, root): + """ + :type root: TreeNode + :rtype: int + """ + result = [0] + def dfs(node): + if not node: + return 0 + left, right = dfs(node.left), dfs(node.right) + left = (left+1) if node.left and node.left.val == node.val else 0 + right = (right+1) if node.right and node.right.val == node.val else 0 + result[0] = max(result[0], left+right) + return max(left, right) + + dfs(root) + return result[0] diff --git a/Python/longest-valid-parentheses.py b/Python/longest-valid-parentheses.py index 8d8b1647e..47a503b55 100644 --- a/Python/longest-valid-parentheses.py +++ b/Python/longest-valid-parentheses.py @@ -1,43 +1,37 @@ +from __future__ import print_function # Time: O(n) # Space: O(1) # -# Given a string containing just the characters '(' and ')', +# Given a string containing just the characters '(' and ')', # find the length of the longest valid (well-formed) parentheses substring. -# +# # For "(()", the longest valid parentheses substring is "()", which has length = 2. -# +# # Another example is ")()())", where the longest valid parentheses substring is "()()", which has length = 4. # -class Solution: - # @param s, a string - # @return an integer +class Solution(object): def longestValidParentheses(self, s): - longest = 0 - - start, depth = -1, 0 - for i in xrange(len(s)): - if s[i] == "(": - depth += 1 - else: - depth -= 1 - if depth < 0: - start, depth = i, 0 - elif depth == 0: - longest = max(longest, i - start) - - start, depth = len(s), 0 - for i in reversed(xrange(len(s))): - if s[i] == ")": - depth += 1 - else: - depth -= 1 - if depth < 0: - start, depth = i, 0 - elif depth == 0: - longest = max(longest, start - i) - - return longest + """ + :type s: str + :rtype: int + """ + def length(it, start, c): + depth, longest = 0, 0 + for i in it: + if s[i] == c: + depth += 1 + else: + depth -= 1 + if depth < 0: + start, depth = i, 0 + elif depth == 0: + longest = max(longest, abs(i - start)) + return longest + + return max(length(xrange(len(s)), -1, '('), \ + length(reversed(xrange(len(s))), len(s), ')')) + # Time: O(n) # Space: O(n) @@ -49,16 +43,16 @@ def longestValidParentheses(self, s): for i in xrange(len(s)): if s[i] == '(': indices.append(i) - elif len(indices) == 0: + elif not indices: last = i else: indices.pop() - if len(indices) == 0: + if not indices: longest = max(longest, i - last) else: longest = max(longest, i - indices[-1]) return longest if __name__ == "__main__": - print Solution().longestValidParentheses("()") - print Solution().longestValidParentheses(")()())") + print(Solution().longestValidParentheses("()")) + print(Solution().longestValidParentheses(")()())")) diff --git a/Python/longest-word-in-dictionary-through-deleting.py b/Python/longest-word-in-dictionary-through-deleting.py new file mode 100644 index 000000000..548d3797c --- /dev/null +++ b/Python/longest-word-in-dictionary-through-deleting.py @@ -0,0 +1,43 @@ +# Time: O((d * l) * logd), l is the average length of words +# Space: O(1) + +# Given a string and a string dictionary, +# find the longest string in the dictionary +# that can be formed by deleting some characters of the given string. +# If there are more than one possible results, +# return the longest word with the smallest lexicographical order. +# If there is no possible result, return the empty string. +# +# Example 1: +# Input: +# s = "abpcplea", d = ["ale","apple","monkey","plea"] +# +# Output: +# "apple" +# Example 2: +# Input: +# s = "abpcplea", d = ["a","b","c"] +# +# Output: +# "a" +# Note: +# All the strings in the input will only contain lower-case letters. +# The size of the dictionary won't exceed 1,000. +# The length of all the strings in the input won't exceed 1,000. + +class Solution(object): + def findLongestWord(self, s, d): + """ + :type s: str + :type d: List[str] + :rtype: str + """ + d.sort(key = lambda x: (-len(x), x)) + for word in d: + i = 0 + for c in s: + if i < len(word) and word[i] == c: + i += 1 + if i == len(word): + return word + return "" diff --git a/Python/longest-word-in-dictionary.py b/Python/longest-word-in-dictionary.py new file mode 100644 index 000000000..c437f244a --- /dev/null +++ b/Python/longest-word-in-dictionary.py @@ -0,0 +1,53 @@ +# Time: O(n), n is the total sum of the lengths of words +# Space: O(t), t is the number of nodes in trie + +# Given a list of strings words representing an English Dictionary, +# find the longest word in words that can be built one character at a time by other words in words. +# If there is more than one possible answer, return the longest word with the smallest lexicographical order. +# +# If there is no answer, return the empty string. +# Example 1: +# Input: +# words = ["w","wo","wor","worl", "world"] +# Output: "world" +# Explanation: +# The word "world" can be built one character at a time by "w", "wo", "wor", and "worl". +# +# Example 2: +# Input: +# words = ["a", "banana", "app", "appl", "ap", "apply", "apple"] +# Output: "apple" +# Explanation: +# Both "apply" and "apple" can be built from other words in the dictionary. +# However, "apple" is lexicographically smaller than "apply". +# +# Note: +# - All the strings in the input will only contain lowercase letters. +# - The length of words will be in the range [1, 1000]. +# - The length of words[i] will be in the range [1, 30]. + +import collections + + +class Solution(object): + def longestWord(self, words): + """ + :type words: List[str] + :rtype: str + """ + _trie = lambda: collections.defaultdict(_trie) + trie = _trie() + for i, word in enumerate(words): + reduce(dict.__getitem__, word, trie)["_end"] = i + + # DFS + stack = trie.values() + result = "" + while stack: + curr = stack.pop() + if "_end" in curr: + word = words[curr["_end"]] + if len(word) > len(result) or (len(word) == len(result) and word < result): + result = word + stack += [curr[letter] for letter in curr if letter != "_end"] + return result diff --git a/Python/loud-and-rich.py b/Python/loud-and-rich.py new file mode 100644 index 000000000..6626a5579 --- /dev/null +++ b/Python/loud-and-rich.py @@ -0,0 +1,74 @@ +# Time: O(q + r) +# Space: O(q + r) + +# In a group of N people (labelled 0, 1, 2, ..., N-1), +# each person has different amounts of money, and different levels of +# quietness. +# +# For convenience, we'll call the person with label x, simply "person x". +# +# We'll say that richer[i] = [x, y] +# if person x definitely has more money than person y. +# Note that richer may only be a subset of valid observations. +# +# Also, we'll say quiet[x] = q if person x has quietness q. +# +# Now, return answer, where answer[x] = y if y is the least quiet person +# (that is, the person y with the smallest value of quiet[y]), +# among all people who definitely have equal to or more money than person x. +# +# Example 1: +# +# Input: richer = [[1,0],[2,1],[3,1],[3,7],[4,3],[5,3],[6,3]], +# quiet = [3,2,5,4,6,1,7,0] +# Output: [5,5,2,5,4,5,6,7] +# Explanation: +# answer[0] = 5. +# Person 5 has more money than 3, which has more money than 1, +# which has more money than 0. +# The only person who is quieter (has lower quiet[x]) is person 7, but +# it isn't clear if they have more money than person 0. +# +# answer[7] = 7. +# Among all people that definitely have equal to or more money than person 7 +# (which could be persons 3, 4, 5, 6, or 7), the person who is the quietest +# (has lower quiet[x]) +# is person 7. +# +# The other answers can be filled out with similar reasoning. +# Note: +# - 1 <= quiet.length = N <= 500 +# - 0 <= quiet[i] < N, all quiet[i] are different. +# - 0 <= richer.length <= N * (N-1) / 2 +# - 0 <= richer[i][j] < N +# - richer[i][0] != richer[i][1] +# - richer[i]'s are all different. +# - The observations in richer are all logically consistent. + +try: + xrange # Python 2 +except NameError: + xrange = range # Python 3 + + +class Solution(object): + def loudAndRich(self, richer, quiet): + """ + :type richer: List[List[int]] + :type quiet: List[int] + :rtype: List[int] + """ + def dfs(graph, quiet, node, result): + if result[node] is None: + result[node] = node + for nei in graph[node]: + smallest_person = dfs(graph, quiet, nei, result) + if quiet[smallest_person] < quiet[result[node]]: + result[node] = smallest_person + return result[node] + + graph = [[] for _ in xrange(len(quiet))] + for u, v in richer: + graph[v].append(u) + result = [None]*len(quiet) + return map(lambda x: dfs(graph, quiet, x, result), xrange(len(quiet))) diff --git a/Python/lowest-common-ancestor-of-a-binary-search-tree.py b/Python/lowest-common-ancestor-of-a-binary-search-tree.py new file mode 100644 index 000000000..fab9e33a7 --- /dev/null +++ b/Python/lowest-common-ancestor-of-a-binary-search-tree.py @@ -0,0 +1,40 @@ +# Time: O(n) +# Space: O(1) +# +# Given a binary search tree (BST), find the lowest common ancestor (LCA) +# of two given nodes in the BST. +# +# According to the definition of LCA on Wikipedia: “The lowest common ancestor +# is defined between two nodes v and w as the lowest node in T that has both v +# and w as descendants (where we allow a node to be a descendant of itself).” +# +# _______6______ +# / \ +# ___2__ ___8__ +# / \ / \ +# 0 _4 7 9 +# / \ +# 3 5 +# For example, the lowest common ancestor (LCA) of nodes 2 and 8 is 6. +# Another example is LCA of nodes 2 and 4 is 2, since a node can be a +# descendant of itself according to the LCA definition. +# +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, x): +# self.val = x +# self.left = None +# self.right = None + +class Solution: + # @param {TreeNode} root + # @param {TreeNode} p + # @param {TreeNode} q + # @return {TreeNode} + def lowestCommonAncestor(self, root, p, q): + s, b = sorted([p.val, q.val]) + while not s <= root.val <= b: + # Keep searching since root is outside of [s, b]. + root = root.left if s <= root.val else root.right + # s <= root.val <= b. + return root diff --git a/Python/lowest-common-ancestor-of-a-binary-tree.py b/Python/lowest-common-ancestor-of-a-binary-tree.py new file mode 100644 index 000000000..190662c69 --- /dev/null +++ b/Python/lowest-common-ancestor-of-a-binary-tree.py @@ -0,0 +1,47 @@ +# Time: O(n) +# Space: O(h) +# +# Given a binary tree, find the lowest common ancestor (LCA) +# of two given nodes in the tree. +# +# According to the definition of LCA on Wikipedia: “The lowest +# common ancestor is defined between two nodes v and w as the +# lowest node in T that has both v and w as descendants (where we +# allow a node to be a descendant of itself).” +# +# _______3______ +# / \ +# ___5__ ___1__ +# / \ / \ +# 6 _2 0 8 +# / \ +# 7 4 +# For example, the lowest common ancestor (LCA) of nodes 5 and 1 is 3. +# Another example is LCA of nodes 5 and 4 is 5, since a node can be a +# descendant of itself according to the LCA definition. +# +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, x): +# self.val = x +# self.left = None +# self.right = None + +class Solution: + # @param {TreeNode} root + # @param {TreeNode} p + # @param {TreeNode} q + # @return {TreeNode} + def lowestCommonAncestor(self, root, p, q): + if root in (None, p, q): + return root + + left, right = [self.lowestCommonAncestor(child, p, q) \ + for child in (root.left, root.right)] + # 1. If the current subtree contains both p and q, + # return their LCA. + # 2. If only one of them is in that subtree, + # return that one of them. + # 3. If neither of them is in that subtree, + # return the node of that subtree. + return root if left and right else left or right diff --git a/Python/lru-cache.py b/Python/lru-cache.py index b0aa97aa1..25e02e3de 100644 --- a/Python/lru-cache.py +++ b/Python/lru-cache.py @@ -1,35 +1,53 @@ -# Time: O(1) -# Space: O(n) +from __future__ import print_function +# Time: O(1), per operation. +# Space: O(k), k is the capacity of cache. + +# Design and implement a data structure for Least Recently Used (LRU) cache. +# It should support the following operations: get and put. +# +# get(key) - Get the value (will always be positive) of the key if the key exists in the cache, +# otherwise return -1. +# put(key, value) - Set or insert the value if the key is not already present. +# When the cache reached its capacity, +# it should invalidate the least recently used item before inserting a new item. +# +# Follow up: +# Could you do both operations in O(1) time complexity? +# +# Example: # -# Design and implement a data structure for Least Recently Used (LRU) cache. -# It should support the following operations: get and set. -# -# get(key) - Get the value (will always be positive) of the key if the key exists in the cache, otherwise return -1. -# -# set(key, value) - Set or insert the value if the key is not already present. -# When the cache reached its capacity, it should invalidate the least recently used item before inserting a new item. +# LRUCache cache = new LRUCache( 2 /* capacity */ ); # +# cache.put(1, 1); +# cache.put(2, 2); +# cache.get(1); // returns 1 +# cache.put(3, 3); // evicts key 2 +# cache.get(2); // returns -1 (not found) +# cache.put(4, 4); // evicts key 1 +# cache.get(1); // returns -1 (not found) +# cache.get(3); // returns 3 -class ListNode: +class ListNode(object): def __init__(self, key, val): self.val = val self.key = key self.next = None self.prev = None -class LinkedList: +class LinkedList(object): def __init__(self): self.head = None self.tail = None - + def insert(self, node): + node.next, node.prev = None, None # avoid dirty node if self.head is None: self.head = node else: self.tail.next = node node.prev = self.tail self.tail = node - + def delete(self, node): if node.prev: node.prev.next = node.next @@ -39,21 +57,21 @@ def delete(self, node): node.next.prev = node.prev else: self.tail = node.prev - del node + node.next, node.prev = None, None # make node clean -class LRUCache: +class LRUCache(object): # @param capacity, an integer def __init__(self, capacity): self.list = LinkedList() self.dict = {} self.capacity = capacity - + def _insert(self, key, val): node = ListNode(key, val) self.list.insert(node) self.dict[key] = node - + # @return an integer def get(self, key): @@ -63,22 +81,22 @@ def get(self, key): self._insert(key, val) return val return -1 - + # @param key, an integer # @param value, an integer # @return nothing - def set(self, key, val): + def put(self, key, val): if key in self.dict: self.list.delete(self.dict[key]) elif len(self.dict) == self.capacity: del self.dict[self.list.head.key] self.list.delete(self.list.head) self._insert(key, val) - - + + import collections -class LRUCache2: +class LRUCache2(object): def __init__(self, capacity): self.cache = collections.OrderedDict() self.capacity = capacity @@ -91,19 +109,20 @@ def get(self, key): self.cache[key] = val return val - def set(self, key, value): + def put(self, key, value): if key in self.cache: del self.cache[key] elif len(self.cache) == self.capacity: self.cache.popitem(last=False) - self.cache[key] = value - + self.cache[key] = value + + if __name__ == "__main__": cache = LRUCache(3) cache.set(1, 1) cache.set(2, 2) cache.set(3, 3) - print cache.get(1) + print(cache.get(1)) cache.set(4, 4) - print cache.get(2) - \ No newline at end of file + print(cache.get(2)) + diff --git a/Python/magic-squares-in-grid.py b/Python/magic-squares-in-grid.py new file mode 100644 index 000000000..6671bb251 --- /dev/null +++ b/Python/magic-squares-in-grid.py @@ -0,0 +1,72 @@ +# Time: O(m * n) +# Space: O(1) + +# A 3 x 3 magic square is a 3 x 3 grid filled with +# distinct numbers from 1 to 9 such that each row, column, +# and both diagonals all have the same sum. +# +# Given an grid of integers, how many 3 x 3 "magic square" subgrids are there? +# (Each subgrid is contiguous). +# +# Example 1: +# +# Input: [[4,3,8,4], +# [9,5,1,9], +# [2,7,6,2]] +# Output: 1 +# Explanation: +# The following subgrid is a 3 x 3 magic square: +# 438 +# 951 +# 276 +# +# while this one is not: +# 384 +# 519 +# 762 +# +# In total, there is only one magic square inside the given grid. +# Note: +# - 1 <= grid.length <= 10 +# - 1 <= grid[0].length <= 10 +# - 0 <= grid[i][j] <= 15 + +try: + xrange # Python 2 +except NameError: + xrange = range # Python 3 + + +class Solution(object): + def numMagicSquaresInside(self, grid): + """ + :type grid: List[List[int]] + :rtype: int + """ + def magic(grid, r, c): + expect = k * (k**2+1) // 2 + nums = set() + min_num = float("inf") + sum_diag, sum_anti = 0, 0 + for i in xrange(k): + sum_diag += grid[r+i][c+i] + sum_anti += grid[r+i][c+k-1-i] + sum_r, sum_c = 0, 0 + for j in xrange(k): + min_num = min(min_num, grid[r+i][c+j]) + nums.add(grid[r+i][c+j]) + sum_r += grid[r+i][c+j] + sum_c += grid[r+j][c+i] + if not (sum_r == sum_c == expect): + return False + return sum_diag == sum_anti == expect and \ + len(nums) == k**2 and \ + min_num == 1 + + k = 3 + result = 0 + for r in xrange(len(grid)-k+1): + for c in xrange(len(grid[r])-k+1): + if magic(grid, r, c): + result += 1 + return result diff --git a/Python/magical-string.py b/Python/magical-string.py new file mode 100644 index 000000000..4954f9c1d --- /dev/null +++ b/Python/magical-string.py @@ -0,0 +1,49 @@ +# Time: O(n) +# Space: O(logn) + +# A magical string S consists of only '1' and '2' and obeys the following rules: +# +# The string S is magical because concatenating +# the number of contiguous occurrences of characters '1' and '2' generates the string S itself. +# +# The first few elements of string S is the following: S = "1221121221221121122……" +# +# If we group the consecutive '1's and '2's in S, it will be: +# +# 1 22 11 2 1 22 1 22 11 2 11 22 ...... +# +# and the occurrences of '1's or '2's in each group are: +# +# 1 2 2 1 1 2 1 2 2 1 2 2 ...... +# +# You can see that the occurrence sequence above is the S itself. +# +# Given an integer N as input, return the number of '1's in the first N number in the magical string S. +# +# Note: N will not exceed 100,000. +# +# Example 1: +# Input: 6 +# Output: 3 +# Explanation: The first 6 elements of magical string S is "12211" and it contains three 1's, so return 3. + +# the solution comes from https://discuss.leetcode.com/topic/75242/o-log-n-space-using-recursive-generators + +import itertools + + +class Solution(object): + def magicalString(self, n): + """ + :type n: int + :rtype: int + """ + def gen(): # see figure 1 on page 3 of http://www.emis.ams.org/journals/JIS/VOL15/Nilsson/nilsson5.pdf + for c in 1, 2, 2: + yield c + for i, c in enumerate(gen()): + if i > 1: + for _ in xrange(c): + yield i % 2 + 1 + + return sum(c & 1 for c in itertools.islice(gen(), n)) diff --git a/Python/majority-element-ii.py b/Python/majority-element-ii.py new file mode 100644 index 000000000..40f02dee3 --- /dev/null +++ b/Python/majority-element-ii.py @@ -0,0 +1,52 @@ +# Time: O(n) +# Space: O(1) + +# Given an integer array of size n, +# find all elements that appear more than [n/3] times. +# The algorithm should run in linear time and in O(1) space. +import collections + + +class Solution(object): + def majorityElement(self, nums): + """ + :type nums: List[int] + :rtype: List[int] + """ + k, n, cnts = 3, len(nums), collections.defaultdict(int) + + for i in nums: + cnts[i] += 1 + # Detecting k items in cnts, at least one of them must have exactly + # one in it. We will discard those k items by one for each. + # This action keeps the same mojority numbers in the remaining numbers. + # Because if x / n > 1 / k is true, then (x - 1) / (n - k) > 1 / k is also true. + if len(cnts) == k: + for j in cnts.keys(): + cnts[j] -= 1 + if cnts[j] == 0: + del cnts[j] + + # Resets cnts for the following counting. + for i in cnts.keys(): + cnts[i] = 0 + + # Counts the occurrence of each candidate integer. + for i in nums: + if i in cnts: + cnts[i] += 1 + + # Selects the integer which occurs > [n / k] times. + result = [] + for i in cnts.keys(): + if cnts[i] > n / k: + result.append(i) + + return result + + def majorityElement2(self, nums): + """ + :type nums: List[int] + :rtype: List[int] + """ + return [i[0] for i in collections.Counter(nums).items() if i[1] > len(nums) / 3] diff --git a/Python/majority-element.py b/Python/majority-element.py index bfd6f0dc0..d4fc73781 100644 --- a/Python/majority-element.py +++ b/Python/majority-element.py @@ -1,28 +1,39 @@ +from __future__ import print_function # Time: O(n) # Space: O(1) # # Given an array of size n, find the majority element. # The majority element is the element that appears more than [n/2] times. -# -# You may assume that the array is non-empty and the majority element always exist in the array. # +# You may assume that the array is non-empty and the majority element always exist in the array. +import collections + class Solution: - # @param num, a list of integers - # @return an integer - def majorityElement(self, num): + def majorityElement(self, nums): + """ + :type nums: List[int] + :rtype: int + """ idx, cnt = 0, 1 - - for i in xrange(1, len(num)): - if num[idx] == num[i]: + + for i in xrange(1, len(nums)): + if nums[idx] == nums[i]: cnt += 1 else: cnt -= 1 if cnt == 0: idx = i cnt = 1 - - return num[idx] + + return nums[idx] + + def majorityElement2(self, nums): + """ + :type nums: List[int] + :rtype: int + """ + return sorted(collections.Counter(nums).items(), key=lambda a: a[1], reverse=True)[0][0] if __name__ == "__main__": - print Solution().majorityElement([1, 2, 3, 4, 5, 5, 5, 5, 5, 5, 6]) \ No newline at end of file + print(Solution().majorityElement([1, 2, 3, 4, 5, 5, 5, 5, 5, 5, 6])) \ No newline at end of file diff --git a/Python/making-a-large-island.py b/Python/making-a-large-island.py new file mode 100644 index 000000000..669a6bd06 --- /dev/null +++ b/Python/making-a-large-island.py @@ -0,0 +1,77 @@ +# Time: O(n^2) +# Space: O(n^2) + +# In a 2D grid of 0s and 1s, we change at most one 0 to a 1. +# +# After, what is the size of the largest island? +# (An island is a 4-directionally connected group of 1s). +# +# Example 1: +# +# Input: [[1, 0], [0, 1]] +# Output: 3 +# Explanation: Change one 0 to 1 and connect two 1s, +# then we get an island with area = 3. +# Example 2: +# +# Input: [[1, 1], [1, 0]] +# Output: 4 +# Explanation: Change the 0 to 1 and make the island bigger, +# only one island with area = 1. +# Example 3: +# +# Input: [[1, 1], [1, 1]] +# Output: 4 +# Explanation: Can't change any 0 to 1, only one island with area = 1. +# +# Notes: +# - 1 <= grid.length = grid[0].length <= 50. +# - 0 <= grid[i][j] <= 1. + +try: + xrange # Python 2 +except NameError: + xrange = range # Python 3 + + +class Solution(object): + def largestIsland(self, grid): + """ + :type grid: List[List[int]] + :rtype: int + """ + directions = [(0, -1), (0, 1), (-1, 0), (1, 0)] + + def dfs(r, c, index, grid): + if not (0 <= r < len(grid) and + 0 <= c < len(grid[0]) and + grid[r][c] == 1): + return 0 + result = 1 + grid[r][c] = index + for d in directions: + result += dfs(r+d[0], c+d[1], index, grid) + return result + + area = {} + index = 2 + for r in xrange(len(grid)): + for c in xrange(len(grid[r])): + if grid[r][c] == 1: + area[index] = dfs(r, c, index, grid) + index += 1 + + result = max(area.values() or [0]) + for r in xrange(len(grid)): + for c in xrange(len(grid[r])): + if grid[r][c] == 0: + seen = set() + for d in directions: + nr, nc = r+d[0], c+d[1] + if not (0 <= nr < len(grid) and + 0 <= nc < len(grid[0]) and + grid[nr][nc] > 1): + continue + seen.add(grid[nr][nc]) + result = max(result, 1 + sum(area[i] for i in seen)) + return result diff --git a/Python/map-sum-pairs.py b/Python/map-sum-pairs.py new file mode 100644 index 000000000..1bc72f6cc --- /dev/null +++ b/Python/map-sum-pairs.py @@ -0,0 +1,73 @@ +# Time: O(n), n is the length of key +# Space: O(t), t is the number of nodes in trie + +# Implement a MapSum class with insert, and sum methods. +# +# For the method insert, you'll be given a pair of (string, integer). +# The string represents the key and the integer represents the value. +# If the key already existed, then the original key-value pair will be overridden to the new one. +# +# For the method sum, you'll be given a string representing the prefix, +# and you need to return the sum of all the pairs' value whose key starts with the prefix. +# +# Example 1: +# Input: insert("apple", 3), Output: Null +# Input: sum("ap"), Output: 3 +# Input: insert("app", 2), Output: Null +# Input: sum("ap"), Output: 5 + +import collections + + +class MapSum(object): + + def __init__(self): + """ + Initialize your data structure here. + """ + _trie = lambda: collections.defaultdict(_trie) + self.__root = _trie() + + + def insert(self, key, val): + """ + :type key: str + :type val: int + :rtype: void + """ + # Time: O(n) + curr = self.__root + for c in key: + curr = curr[c] + delta = val + if "_end" in curr: + delta -= curr["_end"] + + curr = self.__root + for c in key: + curr = curr[c] + if "_count" in curr: + curr["_count"] += delta + else: + curr["_count"] = delta + curr["_end"] = val + + + def sum(self, prefix): + """ + :type prefix: str + :rtype: int + """ + # Time: O(n) + curr = self.__root + for c in prefix: + if c not in curr: + return 0 + curr = curr[c] + return curr["_count"] + + +# Your MapSum object will be instantiated and called as such: +# obj = MapSum() +# obj.insert(key,val) +# param_2 = obj.sum(prefix) diff --git a/Python/masking-personal-information.py b/Python/masking-personal-information.py new file mode 100644 index 000000000..ff473d5c6 --- /dev/null +++ b/Python/masking-personal-information.py @@ -0,0 +1,86 @@ +# Time: O(1) +# Space: O(1) + +# We are given a personal information string S, +# which may represent either an email address or a phone number. +# +# We would like to mask this personal information according to the following +# rules: +# +# 1. Email address: +# We define a name to be a string of length ≥ 2 consisting of only lowercase +# letters a-z or uppercase letters A-Z. +# An email address starts with a name, followed by the symbol '@', followed by +# a name, followed by the dot '.' and followed by a name. +# All email addresses are guaranteed to be valid and in the format of +# "name1@name2.name3". +# To mask an email, all names must be converted to lowercase and all letters +# between the first and last letter of the first name must be replaced by +# 5 asterisks '*'. +# +# 2. Phone number: +# A phone number is a string consisting of only the digits 0-9 or the +# characters from the set {'+', '-', '(', ')', ' '}. You may assume +# a phone number contains 10 to 13 digits. +# The last 10 digits make up the local number, while the digits before those +# make up the country code. Note that the country code is optional. We want to +# expose only the last 4 digits and mask all other digits. +# The local number should be formatted and masked as "***-***-1111", +# where 1 represents the exposed digits. +# To mask a phone number with country code like "+111 111 111 1111", +# we write it in the form "+***-***-***-1111". The '+' sign and the first '-' +# sign before the local number should only exist if there is a country code. +# For example, a 12 digit phone number mask should start with "+**-". +# +# Note that extraneous characters like "(", ")", " ", as well as extra dashes +# or plus signs not part of the above formatting scheme should be removed. +# +# Return the correct "mask" of the information provided. +# +# Example 1: +# +# Input: "LeetCode@LeetCode.com" +# Output: "l*****e@leetcode.com" +# Explanation: All names are converted to lowercase, and the letters between +# the first and last letter of the first name is replaced by 5 asterisks. +# Therefore, "leetcode" -> "l*****e". +# Example 2: +# +# Input: "AB@qq.com" +# Output: "a*****b@qq.com" +# Explanation: There must be 5 asterisks between the first and last letter +# of the first name "ab". Therefore, "ab" -> "a*****b". +# Example 3: +# +# Input: "1(234)567-890" +# Output: "***-***-7890" +# Explanation: 10 digits in the phone number, which means all digits make up +# the local number. +# Example 4: +# +# Input: "86-(10)12345678" +# Output: "+**-***-***-5678" +# Explanation: 12 digits, 2 digits for country code and 10 digits for local +# number. +# Notes: +# +# S.length <= 40. +# Emails have length at least 8. +# Phone numbers have length at least 10. + + +class Solution(object): + def maskPII(self, S): + """ + :type S: str + :rtype: str + """ + if '@' in S: + first, after = S.split('@') + return "{}*****{}@{}".format(first[0], first[-1], after).lower() + + digits = filter(lambda x: x.isdigit(), S) + local = "***-***-{}".format(digits[-4:]) + if len(digits) == 10: + return local + return "+{}-{}".format('*' * (len(digits) - 10), local) diff --git a/Python/matchsticks-to-square.py b/Python/matchsticks-to-square.py new file mode 100644 index 000000000..18338e2ee --- /dev/null +++ b/Python/matchsticks-to-square.py @@ -0,0 +1,60 @@ +# Time: O(n * s * 2^n), s is the number of subset of which sum equals to side length. +# Space: O(n * (2^n + s)) + +# Remember the story of Little Match Girl? By now, you know exactly +# what matchsticks the little match girl has, please find out a way +# you can make one square by using up all those matchsticks. +# You should not break any stick, but you can link them up, +# and each matchstick must be used exactly one time. +# +# Your input will be several matchsticks the girl has, +# represented with their stick length. +# Your output will either be true or false, +# to represent whether you could make one square using all the matchsticks the little match girl has. +# +# Example 1: +# Input: [1,1,2,2,2] +# Output: true +# +# Explanation: You can form a square with length 2, one side of the square came two sticks with length 1. +# Example 2: +# Input: [3,3,3,3,4] +# Output: false +# +# Explanation: You cannot find a way to form a square with all the matchsticks. +# Note: +# The length sum of the given matchsticks is in the range of 0 to 10^9. +# The length of the given matchstick array will not exceed 15. + +class Solution(object): + def makesquare(self, nums): + """ + :type nums: List[int] + :rtype: bool + """ + total_len = sum(nums) + if total_len % 4: + return False + + side_len = total_len / 4 + fullset = (1 << len(nums)) - 1 + + used_subsets = [] + valid_half_subsets = [0] * (1 << len(nums)) + + for subset in xrange(fullset+1): + subset_total_len = 0 + for i in xrange(len(nums)): + if subset & (1 << i): + subset_total_len += nums[i] + + if subset_total_len == side_len: + for used_subset in used_subsets: + if (used_subset & subset) == 0: + valid_half_subset = used_subset | subset + valid_half_subsets[valid_half_subset] = True + if valid_half_subsets[fullset ^ valid_half_subset]: + return True + used_subsets.append(subset) + + return False diff --git a/Python/max-area-of-island.py b/Python/max-area-of-island.py new file mode 100644 index 000000000..ee8db6201 --- /dev/null +++ b/Python/max-area-of-island.py @@ -0,0 +1,55 @@ +# Time: O(m * n) +# Space: O(m * n), the max depth of dfs may be m * n + +# Given a non-empty 2D array grid of 0's and 1's, +# an island is a group of 1's (representing land) connected 4-directionally (horizontal or vertical.) +# You may assume all four edges of the grid are surrounded by water. +# +# Find the maximum area of an island in the given 2D array. (If there is no island, the maximum area is 0.) +# +# Example 1: +# [[0,0,1,0,0,0,0,1,0,0,0,0,0], +# [0,0,0,0,0,0,0,1,1,1,0,0,0], +# [0,1,1,0,1,0,0,0,0,0,0,0,0], +# [0,1,0,0,1,1,0,0,1,0,1,0,0], +# [0,1,0,0,1,1,0,0,1,1,1,0,0], +# [0,0,0,0,0,0,0,0,0,0,1,0,0], +# [0,0,0,0,0,0,0,1,1,1,0,0,0], +# [0,0,0,0,0,0,0,1,1,0,0,0,0]] +# +# Given the above grid, return 6. Note the answer is not 11, +# because the island must be connected 4-directionally. +# +# Example 2: +# [[0,0,0,0,0,0,0,0]] +# +# Given the above grid, return 0. +# +# Note: The length of each dimension in the given grid does not exceed 50. + +class Solution(object): + def maxAreaOfIsland(self, grid): + """ + :type grid: List[List[int]] + :rtype: int + """ + directions = [[-1, 0], [ 1, 0], [ 0, 1], [ 0, -1]] + + def dfs(i, j, grid, area): + if not (0 <= i < len(grid) and \ + 0 <= j < len(grid[0]) and \ + grid[i][j] > 0): + return False + grid[i][j] *= -1 + area[0] += 1 + for d in directions: + dfs(i+d[0], j+d[1], grid, area) + return True + + result = 0 + for i in xrange(len(grid)): + for j in xrange(len(grid[0])): + area = [0] + if dfs(i, j, grid, area): + result = max(result, area[0]) + return result diff --git a/Python/max-chunks-to-make-sorted-ii.py b/Python/max-chunks-to-make-sorted-ii.py new file mode 100644 index 000000000..af0b3e7c7 --- /dev/null +++ b/Python/max-chunks-to-make-sorted-ii.py @@ -0,0 +1,49 @@ +# Time: O(nlogn) +# Space: O(n) + +# This question is the same as "Max Chunks to Make Sorted" +# except the integers of the given array are not necessarily distinct, +# the input array could be up to length 2000, and the elements could be up to 10**8. +# +# Given an array arr of integers (not necessarily distinct), +# we split the array into some number of "chunks" (partitions), +# and individually sort each chunk. +# After concatenating them, the result equals the sorted array. +# +# What is the most number of chunks we could have made? +# +# Example 1: +# +# Input: arr = [5,4,3,2,1] +# Output: 1 +# Explanation: +# Splitting into two or more chunks will not return the required result. +# For example, splitting into [5, 4], [3, 2, 1] will result in [4, 5, 1, 2, 3], which isn't sorted. +# Example 2: +# +# Input: arr = [2,1,3,4,4] +# Output: 4 +# Explanation: +# We can split into two chunks, such as [2, 1], [3, 4, 4]. +# However, splitting into [2, 1], [3], [4], [4] is the highest number of chunks possible. +# +# Note: +# - arr will have length in range [1, 2000]. +# - arr[i] will be an integer in range [0, 10**8]. + +class Solution(object): + def maxChunksToSorted(self, arr): + """ + :type arr: List[int] + :rtype: int + """ + def compare(i1, i2): + return arr[i1]-arr[i2] if arr[i1] != arr[i2] else i1-i2 + + idxs = [i for i in xrange(len(arr))] + result, max_i = 0, 0 + for i, v in enumerate(sorted(idxs, cmp=compare)): + max_i = max(max_i, v) + if max_i == i: + result += 1 + return result diff --git a/Python/max-chunks-to-make-sorted.py b/Python/max-chunks-to-make-sorted.py new file mode 100644 index 000000000..2677bb63b --- /dev/null +++ b/Python/max-chunks-to-make-sorted.py @@ -0,0 +1,41 @@ +# Time: O(n) +# Space: O(1) + +# Given an array arr that is a permutation of [0, 1, ..., arr.length - 1], +# we split the array into some number of "chunks" (partitions), and individually sort each chunk. +# After concatenating them, the result equals the sorted array. +# +# What is the most number of chunks we could have made? +# +# Example 1: +# +# Input: arr = [4,3,2,1,0] +# Output: 1 +# Explanation: +# Splitting into two or more chunks will not return the required result. +# For example, splitting into [4, 3], [2, 1, 0] will result in [3, 4, 0, 1, 2], which isn't sorted. +# +# Example 2: +# +# Input: arr = [1,0,2,3,4] +# Output: 4 +# Explanation: +# We can split into two chunks, such as [1, 0], [2, 3, 4]. +# However, splitting into [1, 0], [2], [3], [4] is the highest number of chunks possible. +# +# Note: +# - arr will have length in range [1, 10]. +# - arr[i] will be a permutation of [0, 1, ..., arr.length - 1]. + +class Solution(object): + def maxChunksToSorted(self, arr): + """ + :type arr: List[int] + :rtype: int + """ + result, max_i = 0, 0 + for i, v in enumerate(arr): + max_i = max(max_i, v) + if max_i == i: + result += 1 + return result diff --git a/Python/max-consecutive-ones-ii.py b/Python/max-consecutive-ones-ii.py new file mode 100644 index 000000000..a674080fd --- /dev/null +++ b/Python/max-consecutive-ones-ii.py @@ -0,0 +1,17 @@ +# Time: O(n) +# Space: O(1) + +class Solution(object): + def findMaxConsecutiveOnes(self, nums): + """ + :type nums: List[int] + :rtype: int + """ + result, prev, curr = 0, 0, 0 + for n in nums: + if n == 0: + result = max(result, prev+curr+1) + prev, curr = curr, 0 + else: + curr += 1 + return min(max(result, prev+curr+1), len(nums)) diff --git a/Python/max-consecutive-ones.py b/Python/max-consecutive-ones.py new file mode 100644 index 000000000..88f26e1f2 --- /dev/null +++ b/Python/max-consecutive-ones.py @@ -0,0 +1,26 @@ +# Time: O(n) +# Space: O(1) + +# Given a binary array, find the maximum number of consecutive 1s in this array. +# +# Example 1: +# Input: [1,1,0,1,1,1] +# Output: 3 +# Explanation: The first two digits or the last three digits are consecutive 1s. +# The maximum number of consecutive 1s is 3. +# Note: +# +# The input array will only contain 0 and 1. +# The length of input array is a positive integer and will not exceed 10,000 + +class Solution(object): + def findMaxConsecutiveOnes(self, nums): + """ + :type nums: List[int] + :rtype: int + """ + result, local_max = 0, 0 + for n in nums: + local_max = (local_max + 1 if n else 0) + result = max(result, local_max) + return result diff --git a/Python/max-increase-to-keep-city-skyline.py b/Python/max-increase-to-keep-city-skyline.py new file mode 100644 index 000000000..902b54825 --- /dev/null +++ b/Python/max-increase-to-keep-city-skyline.py @@ -0,0 +1,56 @@ +# Time: O(n^2) +# Space: O(n) + +# In a 2 dimensional array grid, each value grid[i][j] represents the height of +# a building located there. We are allowed to increase the height of any number of buildings, +# by any amount (the amounts can be different for different buildings). +# Height 0 is considered to be a building as well. +# +# At the end, the "skyline" when viewed from all four directions of the grid, +# i.e. top, bottom, left, and right, must be the same as the skyline of the original grid. +# A city's skyline is the outer contour of the rectangles formed by +# all the buildings when viewed from a distance. See the following example. +# +# What is the maximum total sum that the height of the buildings can be increased? +# +# Example: +# Input: grid = [[3,0,8,4],[2,4,5,7],[9,2,6,3],[0,3,1,0]] +# Output: 35 +# Explanation: +# The grid is: +# [ [3, 0, 8, 4], +# [2, 4, 5, 7], +# [9, 2, 6, 3], +# [0, 3, 1, 0] ] +# +# The skyline viewed from top or bottom is: [9, 4, 8, 7] +# The skyline viewed from left or right is: [8, 7, 9, 3] +# +# The grid after increasing the height of buildings without affecting skylines is: +# +# gridNew = [ [8, 4, 8, 7], +# [7, 4, 7, 7], +# [9, 4, 8, 7], +# [3, 3, 3, 3] ] +# +# Notes: +# - 1 < grid.length = grid[0].length <= 50. +# - All heights grid[i][j] are in the range [0, 100]. +# - All buildings in grid[i][j] occupy the entire grid cell: +# that is, they are a 1 x 1 x grid[i][j] rectangular prism. + +import itertools + + +class Solution(object): + def maxIncreaseKeepingSkyline(self, grid): + """ + :type grid: List[List[int]] + :rtype: int + """ + row_maxes = [max(row) for row in grid] + col_maxes = [max(col) for col in itertools.izip(*grid)] + + return sum(min(row_maxes[r], col_maxes[c])-val \ + for r, row in enumerate(grid) \ + for c, val in enumerate(row)) diff --git a/Python/max-points-on-a-line.py b/Python/max-points-on-a-line.py index cf47fa2ac..7655881d1 100644 --- a/Python/max-points-on-a-line.py +++ b/Python/max-points-on-a-line.py @@ -1,23 +1,29 @@ +from __future__ import print_function # Time: O(n^2) # Space: O(n) # # Given n points on a 2D plane, find the maximum number of points that lie on the same straight line. # +import collections + + # Definition for a point class Point: def __init__(self, a=0, b=0): self.x = a self.y = b -class Solution: - # @param points, a list of Points - # @return an integer +class Solution(object): def maxPoints(self, points): + """ + :type points: List[Point] + :rtype: int + """ max_points = 0 for i, start in enumerate(points): - slope_count, same, current_max = {}, 1, 0 - for j in range(i + 1, len(points)): + slope_count, same = collections.defaultdict(int), 1 + for j in xrange(i + 1, len(points)): end = points[j] if start.x == end.x and start.y == end.y: same += 1 @@ -25,17 +31,15 @@ def maxPoints(self, points): slope = float("inf") if start.x - end.x != 0: slope = (start.y - end.y) * 1.0 / (start.x - end.x) - if slope not in slope_count: - slope_count[slope] = 1 - else: - slope_count[slope] += 1 - + slope_count[slope] += 1 + + current_max = same for slope in slope_count: current_max = max(current_max, slope_count[slope] + same) - - max_points = max(max_points, current_max, same) - + + max_points = max(max_points, current_max) + return max_points if __name__ == "__main__": - print Solution().maxPoints([Point(), Point(), Point()]) + print(Solution().maxPoints([Point(), Point(), Point()])) diff --git a/Python/max-stack.py b/Python/max-stack.py new file mode 100644 index 000000000..7bf4ec185 --- /dev/null +++ b/Python/max-stack.py @@ -0,0 +1,85 @@ +# Time: push: O(1) +# pop: O(n), there is no built-in SortedDict in python. If applied, it could be reduced to O(logn) +# popMax: O(n) +# top: O(1) +# peekMax: O(1) +# Space: O(n), n is the number of values in the current stack + +import collections + + +class MaxStack(object): + + def __init__(self): + """ + initialize your data structure here. + """ + self.__idx_to_val = collections.defaultdict(int) + self.__val_to_idxs = collections.defaultdict(list) + self.__top = None + self.__max = None + + + def push(self, x): + """ + :type x: int + :rtype: void + """ + idx = self.__val_to_idxs[self.__top][-1]+1 if self.__val_to_idxs else 0 + self.__idx_to_val[idx] = x + self.__val_to_idxs[x].append(idx) + self.__top = x + self.__max = max(self.__max, x) + + + def pop(self): + """ + :rtype: int + """ + val = self.__top + self.__remove(val) + return val + + + def top(self): + """ + :rtype: int + """ + return self.__top + + + def peekMax(self): + """ + :rtype: int + """ + return self.__max + + + def popMax(self): + """ + :rtype: int + """ + val = self.__max + self.__remove(val) + return val + + + def __remove(self, val): + idx = self.__val_to_idxs[val][-1] + self.__val_to_idxs[val].pop(); + if not self.__val_to_idxs[val]: + del self.__val_to_idxs[val] + del self.__idx_to_val[idx] + if val == self.__top: + self.__top = self.__idx_to_val[max(self.__idx_to_val.keys())] if self.__idx_to_val else None + if val == self.__max: + self.__max = max(self.__val_to_idxs.keys()) if self.__val_to_idxs else None + + +# Your MaxStack object will be instantiated and called as such: +# obj = MaxStack() +# obj.push(x) +# param_2 = obj.pop() +# param_3 = obj.top() +# param_4 = obj.peekMax() +# param_5 = obj.popMax() diff --git a/Python/max-sum-of-sub-matrix-no-larger-than-k.py b/Python/max-sum-of-sub-matrix-no-larger-than-k.py new file mode 100644 index 000000000..5d18781af --- /dev/null +++ b/Python/max-sum-of-sub-matrix-no-larger-than-k.py @@ -0,0 +1,139 @@ +# Time: O(min(m, n)^2 * max(m, n) * log(max(m, n))) +# Space: O(max(m, n)) + +# Given a non-empty 2D matrix matrix and an integer k, +# find the max sum of a rectangle in the matrix such that its sum is no larger than k. +# +# Example: +# Given matrix = [ +# [1, 0, 1], +# [0, -2, 3] +# ] +# k = 2 +# The answer is 2. Because the sum of rectangle [[0, 1], [-2, 3]] +# is 2 and 2 is the max number no larger than k (k = 2). +# +# Note: +# The rectangle inside the matrix must have an area > 0. +# What if the number of rows is much larger than the number of columns? + +# Time: O(min(m, n)^2 * max(m, n)^2) +# Space: O(max(m, n)) + +# Given a non-empty 2D matrix matrix and an integer k, +# find the max sum of a rectangle in the matrix such that its sum is no larger than k. +# +# Example: +# Given matrix = [ +# [1, 0, 1], +# [0, -2, 3] +# ] +# k = 2 +# The answer is 2. Because the sum of rectangle [[0, 1], [-2, 3]] +# is 2 and 2 is the max number no larger than k (k = 2). +# +# Note: +# The rectangle inside the matrix must have an area > 0. +# What if the number of rows is much larger than the number of columns? + +# Time: O(min(m, n)^2 * max(m, n)^2) +# Space: O(max(m, n)) +from bisect import bisect_left, insort + +class Solution(object): + def maxSumSubmatrix(self, matrix, k): + """ + :type matrix: List[List[int]] + :type k: int + :rtype: int + """ + if not matrix: + return 0 + + m = min(len(matrix), len(matrix[0])) + n = max(len(matrix), len(matrix[0])) + result = float("-inf") + + for i in xrange(m): + sums = [0] * n + for j in xrange(i, m): + for l in xrange(n): + sums[l] += matrix[j][l] if m == len(matrix) else matrix[l][j] + + # Find the max subarray no more than K. + accu_sum_set, accu_sum = [0], 0 + for sum in sums: + accu_sum += sum + it = bisect_left(accu_sum_set, accu_sum - k) # Time: O(logn) + if it != len(accu_sum_set): + result = max(result, accu_sum - accu_sum_set[it]) + insort(accu_sum_set, accu_sum) # Time: O(n) + + return result + + +# Time: O(min(m, n)^2 * max(m, n) * log(max(m, n))) ~ O(min(m, n)^2 * max(m, n)^2) +# Space: O(max(m, n)) +class Solution_TLE(object): + def maxSumSubmatrix(self, matrix, k): + """ + :type matrix: List[List[int]] + :type k: int + :rtype: int + """ + class BST(object): # not avl, rbtree + def __init__(self, val): + self.val = val + self.left = None + self.right = None + + def insert(self, val): # Time: O(h) = O(logn) ~ O(n) + curr = self + while curr: + if curr.val >= val: + if curr.left: + curr = curr.left + else: + curr.left = BST(val) + return + else: + if curr.right: + curr = curr.right + else: + curr.right = BST(val) + return + + def lower_bound(self, val): # Time: O(h) = O(logn) ~ O(n) + result, curr = None, self + while curr: + if curr.val >= val: + result, curr = curr, curr.left + else: + curr = curr.right + return result + + + if not matrix: + return 0 + + m = min(len(matrix), len(matrix[0])) + n = max(len(matrix), len(matrix[0])) + result = float("-inf") + + for i in xrange(m): + sums = [0] * n + for j in xrange(i, m): + for l in xrange(n): + sums[l] += matrix[j][l] if m == len(matrix) else matrix[l][j] + + # Find the max subarray no more than K. + accu_sum_set = BST(0) + accu_sum = 0 + for sum in sums: + accu_sum += sum + node = accu_sum_set.lower_bound(accu_sum - k); + if node: + result = max(result, accu_sum - node.val) + accu_sum_set.insert(accu_sum) + + return result diff --git a/Python/maximal-rectangle.py b/Python/maximal-rectangle.py index 05a99c943..5d7754c37 100644 --- a/Python/maximal-rectangle.py +++ b/Python/maximal-rectangle.py @@ -1,17 +1,56 @@ +from __future__ import print_function # Time: O(n^2) # Space: O(n) -# -# Given a 2D binary matrix filled with 0's and 1's, + +# Given a 2D binary matrix filled with 0's and 1's, # find the largest rectangle containing all ones and return its area. -# -class Solution: - # @param matrix, a list of lists of 1 length string - # @return an integer +# Ascending stack solution. +class Solution(object): + def maximalRectangle(self, matrix): + """ + :type matrix: List[List[str]] + :rtype: int + """ + def largestRectangleArea(heights): + increasing, area, i = [], 0, 0 + while i <= len(heights): + if not increasing or (i < len(heights) and heights[i] > heights[increasing[-1]]): + increasing.append(i) + i += 1 + else: + last = increasing.pop() + if not increasing: + area = max(area, heights[last] * i) + else: + area = max(area, heights[last] * (i - increasing[-1] - 1 )) + return area + + if not matrix: + return 0 + + result = 0 + heights = [0] * len(matrix[0]) + for i in xrange(len(matrix)): + for j in xrange(len(matrix[0])): + heights[j] = heights[j] + 1 if matrix[i][j] == '1' else 0 + result = max(result, largestRectangleArea(heights)) + + return result + + +# Time: O(n^2) +# Space: O(n) +# DP solution. +class Solution2(object): def maximalRectangle(self, matrix): - if len(matrix) == 0: + """ + :type matrix: List[List[str]] + :rtype: int + """ + if not matrix: return 0 - + result = 0 m = len(matrix) n = len(matrix[0]) @@ -30,7 +69,7 @@ def maximalRectangle(self, matrix): H[j] = 0 R[j] = n left = j + 1 - + right = n for j in reversed(xrange(n)): if matrix[i][j] == '1': @@ -38,7 +77,7 @@ def maximalRectangle(self, matrix): result = max(result, H[j] * (R[j] - L[j])) else: right = j - + return result if __name__ == "__main__": @@ -48,4 +87,4 @@ def maximalRectangle(self, matrix): "11110", "11111", "00000"] - print Solution().maximalRectangle(matrix) + print(Solution().maximalRectangle(matrix)) diff --git a/Python/maximal-square.py b/Python/maximal-square.py new file mode 100644 index 000000000..bb9029526 --- /dev/null +++ b/Python/maximal-square.py @@ -0,0 +1,127 @@ +# Time: O(n^2) +# Space: O(n) +# +# Given a 2D binary matrix filled with 0's and 1's, +# find the largest square containing all 1's and return its area. +# +# For example, given the following matrix: +# +# 1 0 1 0 0 +# 1 0 1 1 1 +# 1 1 1 1 1 +# 1 0 0 1 0 +# Return 4. +# + +# DP with sliding window. +class Solution: + # @param {character[][]} matrix + # @return {integer} + def maximalSquare(self, matrix): + if not matrix: + return 0 + + m, n = len(matrix), len(matrix[0]) + size = [[0 for j in xrange(n)] for i in xrange(2)] + max_size = 0 + + for j in xrange(n): + if matrix[0][j] == '1': + size[0][j] = 1 + max_size = max(max_size, size[0][j]) + + for i in xrange(1, m): + if matrix[i][0] == '1': + size[i % 2][0] = 1 + else: + size[i % 2][0] = 0 + for j in xrange(1, n): + if matrix[i][j] == '1': + size[i % 2][j] = min(size[i % 2][j - 1], \ + size[(i - 1) % 2][j], \ + size[(i - 1) % 2][j - 1]) + 1 + max_size = max(max_size, size[i % 2][j]) + else: + size[i % 2][j] = 0 + + return max_size * max_size + + +# Time: O(n^2) +# Space: O(n^2) +# DP. +class Solution2: + # @param {character[][]} matrix + # @return {integer} + def maximalSquare(self, matrix): + if not matrix: + return 0 + + m, n = len(matrix), len(matrix[0]) + size = [[0 for j in xrange(n)] for i in xrange(m)] + max_size = 0 + + for j in xrange(n): + if matrix[0][j] == '1': + size[0][j] = 1 + max_size = max(max_size, size[0][j]) + + for i in xrange(1, m): + if matrix[i][0] == '1': + size[i][0] = 1 + else: + size[i][0] = 0 + for j in xrange(1, n): + if matrix[i][j] == '1': + size[i][j] = min(size[i][j - 1], \ + size[i - 1][j], \ + size[i - 1][j - 1]) + 1 + max_size = max(max_size, size[i][j]) + else: + size[i][j] = 0 + + return max_size * max_size + + +# Time: O(n^2) +# Space: O(n^2) +# DP. +class Solution3: + # @param {character[][]} matrix + # @return {integer} + def maximalSquare(self, matrix): + if not matrix: + return 0 + + H, W = 0, 1 + # DP table stores (h, w) for each (i, j). + table = [[[0, 0] for j in xrange(len(matrix[0]))] \ + for i in xrange(len(matrix))] + for i in reversed(xrange(len(matrix))): + for j in reversed(xrange(len(matrix[i]))): + # Find the largest h such that (i, j) to (i + h - 1, j) are feasible. + # Find the largest w such that (i, j) to (i, j + w - 1) are feasible. + if matrix[i][j] == '1': + h, w = 1, 1 + if i + 1 < len(matrix): + h = table[i + 1][j][H] + 1 + if j + 1 < len(matrix[i]): + w = table[i][j + 1][W] + 1 + table[i][j] = [h, w] + + # A table stores the length of largest square for each (i, j). + s = [[0 for j in xrange(len(matrix[0]))] \ + for i in xrange(len(matrix))] + max_square_area = 0 + for i in reversed(xrange(len(matrix))): + for j in reversed(xrange(len(matrix[i]))): + side = min(table[i][j][H], table[i][j][W]) + if matrix[i][j] == '1': + # Get the length of largest square with bottom-left corner (i, j). + if i + 1 < len(matrix) and j + 1 < len(matrix[i + 1]): + side = min(s[i + 1][j + 1] + 1, side) + s[i][j] = side + max_square_area = max(max_square_area, side * side) + + return max_square_area; + diff --git a/Python/maximize-distance-to-closest-person.py b/Python/maximize-distance-to-closest-person.py new file mode 100644 index 000000000..d0634a8cf --- /dev/null +++ b/Python/maximize-distance-to-closest-person.py @@ -0,0 +1,54 @@ +# Time: O(n) +# Space: O(1) + +# In a row of seats, 1 represents a person sitting in that seat, +# and 0 represents that the seat is empty. +# +# There is at least one empty seat, and at least one person sitting. +# +# Alex wants to sit in the seat such that the distance between him +# and the closest person to him is maximized. +# +# Return that maximum distance to closest person. +# +# Example 1: +# +# Input: [1,0,0,0,1,0,1] +# Output: 2 +# Explanation: +# If Alex sits in the second open seat (seats[2]), +# then the closest person has distance 2. +# If Alex sits in any other open seat, the closest person has distance 1. +# Thus, the maximum distance to the closest person is 2. +# Example 2: +# +# Input: [1,0,0,0] +# Output: 3 +# Explanation: +# If Alex sits in the last seat, the closest person is 3 seats away. +# This is the maximum distance possible, so the answer is 3. +# Note: +# - 1 <= seats.length <= 20000 +# - seats contains only 0s or 1s, at least one 0, and at least one 1. + +try: + xrange # Python 2 +except NameError: + xrange = range # Python 3 + + +class Solution(object): + def maxDistToClosest(self, seats): + """ + :type seats: List[int] + :rtype: int + """ + prev, result = -1, 1 + for i in xrange(len(seats)): + if seats[i]: + if prev < 0: + result = i + else: + result = max(result, (i-prev)//2) + prev = i + return max(result, len(seats)-1-prev) diff --git a/Python/maximum-average-subarray-i.py b/Python/maximum-average-subarray-i.py new file mode 100644 index 000000000..d59f8ccfa --- /dev/null +++ b/Python/maximum-average-subarray-i.py @@ -0,0 +1,27 @@ +# Time: O(n) +# Space: O(1) + +# Given an array consisting of n integers, +# find the contiguous subarray of given length k that has the maximum average value. +# And you need to output the maximum average value. +# +# Example 1: +# Input: [1,12,-5,-6,50,3], k = 4 +# Output: 12.75 +# Explanation: Maximum average is (12-5-6+50)/4 = 51/4 = 12.75 +# Note: +# 1 <= k <= n <= 30,000. +# Elements of the given array will be in the range [-10,000, 10,000]. + +class Solution(object): + def findMaxAverage(self, nums, k): + """ + :type nums: List[int] + :type k: int + :rtype: float + """ + result = total = sum(nums[:k]) + for i in xrange(k, len(nums)): + total += nums[i] - nums[i-k] + result = max(result, total) + return float(result) / k diff --git a/Python/maximum-average-subarray-ii.py b/Python/maximum-average-subarray-ii.py new file mode 100644 index 000000000..1c2ae6a50 --- /dev/null +++ b/Python/maximum-average-subarray-ii.py @@ -0,0 +1,28 @@ +# Time: O(n) +# Space: O(n) + +class Solution(object): + def findMaxAverage(self, nums, k): + """ + :type nums: List[int] + :type k: int + :rtype: float + """ + def getDelta(avg, nums, k): + accu = [0.0] * (len(nums) + 1) + minval_pos = None + delta = 0.0 + for i in xrange(len(nums)): + accu[i+1] = nums[i] + accu[i] - avg + if i >= (k-1): + if minval_pos == None or accu[i-k+1] < accu[minval_pos]: + minval_pos = i-k+1 + if accu[i+1] - accu[minval_pos] >= 0: + delta = max(delta, (accu[i+1] - accu[minval_pos]) / (i+1 - minval_pos)) + return delta + + left, delta = min(nums), float("inf") + while delta > 1e-5: + delta = getDelta(left, nums, k) + left += delta + return left diff --git a/Python/maximum-binary-tree.py b/Python/maximum-binary-tree.py new file mode 100644 index 000000000..be1c46f71 --- /dev/null +++ b/Python/maximum-binary-tree.py @@ -0,0 +1,50 @@ +# Time: O(n) +# Space: O(n) + +# 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. +# +# Example 1: +# Input: [3,2,1,6,0,5] +# Output: return the tree root node representing the following tree: +# +# 6 +# / \ +# 3 5 +# \ / +# 2 0 +# \ +# 1 +# Note: +# The size of the given array will be in the range [1,1000]. + + +# Definition for a binary tree node. +class TreeNode(object): + def __init__(self, x): + self.val = x + self.left = None + self.right = None + + +class Solution(object): + def constructMaximumBinaryTree(self, nums): + """ + :type nums: List[int] + :rtype: TreeNode + """ + # https://github.com/kamyu104/LintCode/blob/master/C++/max-tree.cpp + nodeStack = [] + for num in nums: + node = TreeNode(num); + while nodeStack and num > nodeStack[-1].val: + node.left = nodeStack.pop() + if nodeStack: + nodeStack[-1].right = node + nodeStack.append(node) + return nodeStack[0] diff --git a/Python/maximum-depth-of-binary-tree.py b/Python/maximum-depth-of-binary-tree.py index 14c258da1..b0d4292d0 100644 --- a/Python/maximum-depth-of-binary-tree.py +++ b/Python/maximum-depth-of-binary-tree.py @@ -1,8 +1,9 @@ +from __future__ import print_function # Time: O(n) -# Space: O(logn) +# Space: O(h), h is height of binary tree # # Given a binary tree, find its maximum depth. -# +# # The maximum depth is the number of nodes along the longest path from the root node down to the farthest leaf node. # @@ -19,7 +20,7 @@ class Solution: def maxDepth(self, root): if root is None: return 0 - else: + else: return max(self.maxDepth(root.left), self.maxDepth(root.right)) + 1 if __name__ == "__main__": @@ -27,4 +28,4 @@ def maxDepth(self, root): root.left = TreeNode(2) root.right = TreeNode(3) root.left.left = TreeNode(4) - print Solution().maxDepth(root) \ No newline at end of file + print(Solution().maxDepth(root)) diff --git a/Python/maximum-depth-of-n-ary-tree.py b/Python/maximum-depth-of-n-ary-tree.py new file mode 100644 index 000000000..088901eef --- /dev/null +++ b/Python/maximum-depth-of-n-ary-tree.py @@ -0,0 +1,35 @@ +# Time: O(n) +# Space: O(h) + +# Given a n-ary tree, find its maximum depth. +# +# The maximum depth is the number of nodes along the longest path +# from the root node down to the farthest leaf node. +# +# For example, given a 3-ary tree: +# +# We should return its max depth, which is 3. +# +# Note: +# - The depth of the tree is at most 1000. +# - The total number of nodes is at most 5000. + +# Definition for a Node. +class Node(object): + def __init__(self, val, children): + self.val = val + self.children = children + + +class Solution(object): + def maxDepth(self, root): + """ + :type root: Node + :rtype: int + """ + if not root: + return 0 + depth = 0 + for child in root.children: + depth = max(depth, self.maxDepth(child)) + return 1+depth diff --git a/Python/maximum-distance-in-arrays.py b/Python/maximum-distance-in-arrays.py new file mode 100644 index 000000000..8f300bca8 --- /dev/null +++ b/Python/maximum-distance-in-arrays.py @@ -0,0 +1,17 @@ +# Time: O(n) +# Space: O(1) + +class Solution(object): + def maxDistance(self, arrays): + """ + :type arrays: List[List[int]] + :rtype: int + """ + result, min_val, max_val = 0, arrays[0][0], arrays[0][-1] + for i in xrange(1, len(arrays)): + result = max(result, \ + max(max_val - arrays[i][0], \ + arrays[i][-1] - min_val)) + min_val = min(min_val, arrays[i][0]) + max_val = max(max_val, arrays[i][-1]) + return result diff --git a/Python/maximum-gap.py b/Python/maximum-gap.py index 6bc30d14f..5dd4b7ccc 100644 --- a/Python/maximum-gap.py +++ b/Python/maximum-gap.py @@ -1,79 +1,83 @@ +from __future__ import print_function # Time: O(n) # Space: O(n) + +# Given an unsorted array, find the maximum difference between # -# Given an unsorted array, find the maximum difference between -# # the successive elements in its sorted form. -# +# # Try to solve it in linear time/space. -# +# # Return 0 if the array contains less than 2 elements. -# +# # You may assume all elements in the array are non-negative integers -# -# and fit in the 32-bit signed integer range. # +# and fit in the 32-bit signed integer range. # bucket sort -class Solution: - # @param num, a list of integer - # @return an integer - def maximumGap(self, num): - if len(num) < 2: +# Time: O(n) +# Space: O(n) +class Solution(object): + def maximumGap(self, nums): + """ + :type nums: List[int] + :rtype: int + """ + if len(nums) < 2: return 0 - - unique_num = self.removeDuplicate(num) - - max_val, min_val = max(unique_num), min(unique_num) - gap = (max_val - min_val) / (len(unique_num) - 1) + + # Init bucket. + max_val, min_val = max(nums), min(nums) + gap = max(1, (max_val - min_val) / (len(nums) - 1)) bucket_size = (max_val - min_val) / gap + 1 - max_bucket = [float("-inf") for _ in xrange(bucket_size)] - min_bucket = [float("inf") for _ in xrange(bucket_size)] + bucket = [{'min':float("inf"), 'max':float("-inf")} \ + for _ in xrange(bucket_size)] - for i in unique_num: - if i in (max_val, min_val): - continue - idx = (i - min_val) / gap - max_bucket[idx] = max(max_bucket[idx], i) - min_bucket[idx] = min(min_bucket[idx], i) - - max_gap = 0 - pre = min_val + # Find the bucket where the n should be put. + for n in nums: + # min_val / max_val is in the first / last bucket. + if n in (max_val, min_val): + continue + i = (n - min_val) / gap + bucket[i]['min'] = min(bucket[i]['min'], n) + bucket[i]['max'] = max(bucket[i]['max'], n) + + # Count each bucket gap between the first and the last bucket. + max_gap, pre_bucket_max = 0, min_val for i in xrange(bucket_size): - if max_bucket[i] == float("-inf") and min_bucket[i] == float("inf"): + # Skip the bucket it empty. + if bucket[i]['min'] == float("inf") and \ + bucket[i]['max'] == float("-inf"): continue - max_gap = max(max_gap, min_bucket[i] - pre) - pre = max_bucket[i] - max_gap = max(max_gap, max_val - pre) - + max_gap = max(max_gap, bucket[i]['min'] - pre_bucket_max) + pre_bucket_max = bucket[i]['max'] + # Count the last bucket. + max_gap = max(max_gap, max_val - pre_bucket_max) + return max_gap - - def removeDuplicate(self, num): - dict = {} - unique_num = [] - for i in num: - if i not in dict: - unique_num.append(i) - dict[i] = True - return unique_num + # Time: O(nlogn) # Space: O(n) -class Solution2: - # @param num, a list of integer - # @return an integer - def maximumGap(self, num): - if len(num) < 2: +class Solution2(object): + def maximumGap(self, nums): + """ + :type nums: List[int] + :rtype: int + """ + + if len(nums) < 2: return 0 - - num.sort() - pre = num[0] + + nums.sort() + pre = nums[0] max_gap = float("-inf") - - for i in num: + + for i in nums: max_gap = max(max_gap, i - pre) pre = i return max_gap - + + if __name__ == "__main__": - print Solution().maximumGap([3, 1, 1, 1, 5, 5, 5, 5]) + print(Solution().maximumGap([3, 1, 1, 1, 5, 5, 5, 5])) diff --git a/Python/maximum-length-of-pair-chain.py b/Python/maximum-length-of-pair-chain.py new file mode 100644 index 000000000..c6586aa3c --- /dev/null +++ b/Python/maximum-length-of-pair-chain.py @@ -0,0 +1,32 @@ +# Time: O(nlogn) +# Space: O(1) + +# You are given n pairs of numbers. +# In every pair, the first number is always smaller than the second number. +# +# Now, we define a pair (c, d) can follow another pair (a, b) +# if and only if b < c. Chain of pairs can be formed in this fashion. +# +# Given a set of pairs, find the length longest chain which can be formed. +# You needn't use up all the given pairs. You can select pairs in any order. +# +# Example 1: +# Input: [[1,2], [2,3], [3,4]] +# Output: 2 +# Explanation: The longest chain is [1,2] -> [3,4] +# Note: +# The number of given pairs will be in the range [1, 1000]. + +class Solution(object): + def findLongestChain(self, pairs): + """ + :type pairs: List[List[int]] + :rtype: int + """ + pairs.sort(key=lambda x: x[1]) + cnt, i = 0, 0 + for j in xrange(len(pairs)): + if j == 0 or pairs[i][1] < pairs[j][0]: + cnt += 1 + i = j + return cnt diff --git a/Python/maximum-length-of-repeated-subarray.py b/Python/maximum-length-of-repeated-subarray.py new file mode 100644 index 000000000..563ee2608 --- /dev/null +++ b/Python/maximum-length-of-repeated-subarray.py @@ -0,0 +1,116 @@ +# Time: O(m * n) +# Space: O(min(m, n)) + +# Given two integer arrays A and B, +# return the maximum length of an subarray that appears in both arrays. +# +# Example 1: +# Input: +# A: [1,2,3,2,1] +# B: [3,2,1,4,7] +# Output: 3 +# Explanation: +# The repeated subarray with maximum length is [3, 2, 1]. +# Note: +# 1 <= len(A), len(B) <= 1000 +# 0 <= A[i], B[i] < 100 + +# dp solution (3752 ms) + +import collections + + +class Solution(object): + def findLength(self, A, B): + """ + :type A: List[int] + :type B: List[int] + :rtype: int + """ + if len(A) < len(B): return self.findLength(B, A) + result = 0 + dp = [[0] * (len(B)+1) for _ in xrange(2)] + for i in xrange(len(A)): + for j in xrange(len(B)): + if A[i] == B[j]: + dp[(i+1)%2][j+1] = dp[i%2][j]+1 + else: + dp[(i+1)%2][j+1] = 0 + result = max(result, max(dp[(i+1)%2])) + return result + + +# Time: O(m * n * log(min(m, n))) +# Space: O(min(m, n)) +# Binary search + rolling hash solution (226 ms) +class Solution2(object): + def findLength(self, A, B): + """ + :type A: List[int] + :type B: List[int] + :rtype: int + """ + if len(A) > len(B): return self.findLength(B, A) + M, p = 10**9+7, 113 + p_inv = pow(p, M-2, M) + def check(guess): + def rolling_hashes(source, length): + if length == 0: + yield 0, 0 + return + + val, power = 0, 1 + for i, x in enumerate(source): + val = (val + x*power) % M + if i < length - 1: + power = (power*p) % M + else: + yield val, i-(length-1) + val = (val-source[i-(length-1)])*p_inv % M + + hashes = collections.defaultdict(list) + for hash_val, i in rolling_hashes(A, guess): + hashes[hash_val].append(i) + for hash_val, j in rolling_hashes(B, guess): + if any(A[i:i+guess] == B[j:j+guess] for i in hashes[hash_val]): + return True + return False + + left, right = 0, min(len(A), len(B)) + 1 + while left < right: + mid = left + (right-left)/2 + if not check(mid): # find the min idx such that check(idx) == false + right = mid + else: + left = mid+1 + return left-1 + + +# Time: O(m * n * min(m, n) * log(min(m, n))) +# Space: O(min(m^2, n^2)) +# Binary search (122 ms) +class Solution3(object): + def findLength(self, A, B): + """ + :type A: List[int] + :type B: List[int] + :rtype: int + """ + if len(A) > len(B): return self.findLength(B, A) + + def check(length): + lookup = set(A[i:i+length] \ + for i in xrange(len(A)-length+1)) + return any(B[j:j+length] in lookup \ + for j in xrange(len(B)-length+1)) + + A = ''.join(map(chr, A)) + B = ''.join(map(chr, B)) + left, right = 0, min(len(A), len(B)) + 1 + while left < right: + mid = left + (right-left)/2 + if not check(mid): # find the min idx such that check(idx) == false + right = mid + else: + left = mid+1 + return left-1 diff --git a/Python/maximum-product-of-three-numbers.py b/Python/maximum-product-of-three-numbers.py new file mode 100644 index 000000000..73a618c2d --- /dev/null +++ b/Python/maximum-product-of-three-numbers.py @@ -0,0 +1,42 @@ +# Time: O(n) +# Space: O(1) + +# Given an integer array, find three numbers whose product is maximum and output the maximum product. +# +# Example 1: +# Input: [1,2,3] +# Output: 6 +# Example 2: +# Input: [1,2,3,4] +# Output: 24 +# Note: +# The length of the given array will be in range [3,104] and all elements are in the range [-1000, 1000]. +# Multiplication of any three numbers in the input won't exceed the range of 32-bit signed integer. + +class Solution(object): + def maximumProduct(self, nums): + """ + :type nums: List[int] + :rtype: int + """ + min1, min2 = float("inf"), float("inf") + max1, max2, max3 = float("-inf"), float("-inf"), float("-inf") + + for n in nums: + if n <= min1: + min2 = min1 + min1 = n + elif n <= min2: + min2 = n + + if n >= max1: + max3 = max2 + max2 = max1 + max1 = n + elif n >= max2: + max3 = max2 + max2 = n + elif n >= max3: + max3 = n + + return max(min1 * min2 * max1, max1 * max2 * max3) diff --git a/Python/maximum-product-of-word-lengths.py b/Python/maximum-product-of-word-lengths.py new file mode 100644 index 000000000..66a870888 --- /dev/null +++ b/Python/maximum-product-of-word-lengths.py @@ -0,0 +1,87 @@ +# Time: O(n) ~ O(n^2) +# Space: O(n) + +# Given a string array words, find the maximum value of +# length(word[i]) * length(word[j]) where the two words +# do not share common letters. You may assume that each +# word will contain only lower case letters. If no such +# two words exist, return 0. +# +# Example 1: +# Given ["abcw", "baz", "foo", "bar", "xtfn", "abcdef"] +# Return 16 +# The two words can be "abcw", "xtfn". +# +# Example 2: +# Given ["a", "ab", "abc", "d", "cd", "bcd", "abcd"] +# Return 4 +# The two words can be "ab", "cd". +# +# Example 3: +# Given ["a", "aa", "aaa", "aaaa"] +# Return 0 +# No such pair of words. +# +# Follow up: +# Could you do better than O(n2), where n is the number of words? + +# Counting Sort + Pruning + Bit Manipulation +class Solution(object): + def maxProduct(self, words): + """ + :type words: List[str] + :rtype: int + """ + def counting_sort(words): + k = 1000 # k is max length of words in the dictionary + buckets = [[] for _ in xrange(k)] + for word in words: + buckets[len(word)].append(word) + res = [] + for i in reversed(xrange(k)): + if buckets[i]: + res += buckets[i] + return res + + words = counting_sort(words) + bits = [0] * len(words) + for i, word in enumerate(words): + for c in word: + bits[i] |= (1 << (ord(c) - ord('a'))) + + max_product = 0 + for i in xrange(len(words) - 1): + if len(words[i]) ** 2 <= max_product: + break + for j in xrange(i + 1, len(words)): + if len(words[i]) * len(words[j]) <= max_product: + break + if not (bits[i] & bits[j]): + max_product = len(words[i]) * len(words[j]) + return max_product + +# Time: O(nlogn) ~ O(n^2) +# Space: O(n) +# Sorting + Pruning + Bit Manipulation +class Solution2(object): + def maxProduct(self, words): + """ + :type words: List[str] + :rtype: int + """ + words.sort(key=lambda x: len(x), reverse=True) + bits = [0] * len(words) + for i, word in enumerate(words): + for c in word: + bits[i] |= (1 << (ord(c) - ord('a'))) + + max_product = 0 + for i in xrange(len(words) - 1): + if len(words[i]) ** 2 <= max_product: + break + for j in xrange(i + 1, len(words)): + if len(words[i]) * len(words[j]) <= max_product: + break + if not (bits[i] & bits[j]): + max_product = len(words[i]) * len(words[j]) + return max_product diff --git a/Python/maximum-product-subarray.py b/Python/maximum-product-subarray.py index 50d08388a..b776fb259 100644 --- a/Python/maximum-product-subarray.py +++ b/Python/maximum-product-subarray.py @@ -1,8 +1,9 @@ +from __future__ import print_function # Time: O(n) # Space: O(1) # # Find the contiguous subarray within an array (containing at least one number) which has the largest product. -# +# # For example, given the array [2,3,-2,4], # the contiguous subarray [2,3] has the largest product = 6. @@ -31,5 +32,5 @@ def maxProduct(self, A): return global_max if __name__ == "__main__": - print Solution().maxProduct([2, 3, -2, 4]) - print Solution().maxProduct([-4,-3]) \ No newline at end of file + print(Solution().maxProduct([2, 3, -2, 4])) + print(Solution().maxProduct([-4,-3])) \ No newline at end of file diff --git a/Python/maximum-size-subarray-sum-equals-k.py b/Python/maximum-size-subarray-sum-equals-k.py new file mode 100644 index 000000000..393505529 --- /dev/null +++ b/Python/maximum-size-subarray-sum-equals-k.py @@ -0,0 +1,21 @@ +# Time: O(n) +# Space: O(n) + +class Solution(object): + def maxSubArrayLen(self, nums, k): + """ + :type nums: List[int] + :type k: int + :rtype: int + """ + sums = {} + cur_sum, max_len = 0, 0 + for i in xrange(len(nums)): + cur_sum += nums[i] + if cur_sum == k: + max_len = i + 1 + elif cur_sum - k in sums: + max_len = max(max_len, i - sums[cur_sum - k]) + if cur_sum not in sums: + sums[cur_sum] = i # Only keep the smallest index. + return max_len diff --git a/Python/maximum-subarray.py b/Python/maximum-subarray.py index 50ff72130..de9a3a4b1 100644 --- a/Python/maximum-subarray.py +++ b/Python/maximum-subarray.py @@ -1,26 +1,32 @@ +from __future__ import print_function # Time: O(n) # Space: O(1) # # Find the contiguous subarray within an array (containing at least one number) which has the largest sum. -# +# # For example, given the array [-2,1,-3,4,-1,2,1,-5,4], # the contiguous subarray [4,-1,2,1] has the largest sum = 6. -# +# # click to show more practice. -# +# # More practice: # If you have figured out the O(n) solution, try coding another solution using the divide and conquer approach, which is more subtle. # -class Solution: - # @param A, a list of integers - # @return an integer - def maxSubArray(self, A): - global_max, local_max = float("-inf"), 0 - for x in A: +class Solution(object): + def maxSubArray(self, nums): + """ + :type nums: List[int] + :rtype: int + """ + if max(nums) < 0: + return max(nums) + global_max, local_max = 0, 0 + for x in nums: local_max = max(0, local_max + x) global_max = max(global_max, local_max) return global_max + if __name__ == "__main__": - print Solution().maxSubArray([-2,1,-3,4,-1,2,1,-5,4]) \ No newline at end of file + print(Solution().maxSubArray([-2,1,-3,4,-1,2,1,-5,4])) diff --git a/Python/maximum-sum-of-3-non-overlapping-subarrays.py b/Python/maximum-sum-of-3-non-overlapping-subarrays.py new file mode 100644 index 000000000..30b4f8a1d --- /dev/null +++ b/Python/maximum-sum-of-3-non-overlapping-subarrays.py @@ -0,0 +1,62 @@ +# Time: O(n) +# Space: O(n) + +# In a given array nums of positive integers, find three non-overlapping subarrays with maximum sum. +# +# Each subarray will be of size k, and we want to maximize the sum of all 3*k entries. +# +# Return the result as a list of indices representing the starting position of each interval (0-indexed). +# If there are multiple answers, return the lexicographically smallest one. +# +# Example: +# Input: [1,2,1,2,6,7,5,1], 2 +# Output: [0, 3, 5] + # +# Explanation: Subarrays [1, 2], [2, 6], [7, 5] correspond to the starting indices [0, 3, 5]. +# We could have also taken [2, 1], but an answer of [1, 3, 5] would be lexicographically larger. +# +# Note: +# - nums.length will be between 1 and 20000. +# - nums[i] will be between 1 and 65535. +# - k will be between 1 and floor(nums.length / 3). + +class Solution(object): + def maxSumOfThreeSubarrays(self, nums, k): + """ + :type nums: List[int] + :type k: int + :rtype: List[int] + """ + n = len(nums) + accu = [0] + for num in nums: + accu.append(accu[-1]+num) + + left_pos = [0] * n + total = accu[k]-accu[0] + for i in xrange(k, n): + if accu[i+1]-accu[i+1-k] > total: + left_pos[i] = i+1-k + total = accu[i+1]-accu[i+1-k] + else: + left_pos[i] = left_pos[i-1] + + right_pos = [n-k] * n + total = accu[n]-accu[n-k] + for i in reversed(xrange(n-k)): + if accu[i+k]-accu[i] > total: + right_pos[i] = i; + total = accu[i+k]-accu[i] + else: + right_pos[i] = right_pos[i+1] + + result, max_sum = [], 0 + for i in xrange(k, n-2*k+1): + left, right = left_pos[i-1], right_pos[i+k] + total = (accu[i+k]-accu[i]) + \ + (accu[left+k]-accu[left]) + \ + (accu[right+k]-accu[right]) + if total > max_sum: + max_sum = total + result = [left, i, right] + return result diff --git a/Python/maximum-swap.py b/Python/maximum-swap.py new file mode 100644 index 000000000..c82f48198 --- /dev/null +++ b/Python/maximum-swap.py @@ -0,0 +1,33 @@ +# Time: O(logn), logn is the length of the number string +# Space: O(logn) + +# Given a non-negative integer, you could swap two digits at most once +# to get the maximum valued number. Return the maximum valued number you could get. +# +# Example 1: +# Input: 2736 +# Output: 7236 +# Explanation: Swap the number 2 and the number 7. +# Example 2: +# Input: 9973 +# Output: 9973 +# Explanation: No swap. +# Note: +# The given number is in the range [0, 10^8] + +class Solution(object): + def maximumSwap(self, num): + """ + :type num: int + :rtype: int + """ + digits = list(str(num)) + left, right = 0, 0 + max_idx = len(digits)-1 + for i in reversed(xrange(len(digits))): + if digits[i] > digits[max_idx]: + max_idx = i + elif digits[max_idx] > digits[i]: + left, right = i, max_idx + digits[left], digits[right] = digits[right], digits[left] + return int("".join(digits)) diff --git a/Python/maximum-vacation-days.py b/Python/maximum-vacation-days.py new file mode 100644 index 000000000..ad87e5d9c --- /dev/null +++ b/Python/maximum-vacation-days.py @@ -0,0 +1,21 @@ +# Time: O(n^2 * k) +# Space: O(k) + +class Solution(object): + def maxVacationDays(self, flights, days): + """ + :type flights: List[List[int]] + :type days: List[List[int]] + :rtype: int + """ + if not days or not flights: + return 0 + dp = [[0] * len(days) for _ in xrange(2)] + for week in reversed(xrange(len(days[0]))): + for cur_city in xrange(len(days)): + dp[week % 2][cur_city] = days[cur_city][week] + dp[(week+1) % 2][cur_city] + for dest_city in xrange(len(days)): + if flights[cur_city][dest_city] == 1: + dp[week % 2][cur_city] = max(dp[week % 2][cur_city], \ + days[dest_city][week] + dp[(week+1) % 2][dest_city]) + return dp[0][0] diff --git a/Python/maximum-width-of-binary-tree.py b/Python/maximum-width-of-binary-tree.py new file mode 100644 index 000000000..b0362eee8 --- /dev/null +++ b/Python/maximum-width-of-binary-tree.py @@ -0,0 +1,83 @@ +# Time: O(n) +# Space: O(h) + +# Given a binary tree, write a function to get the maximum width of the given tree. +# The width of a tree is the maximum width among all levels. The binary tree has the same structure +# as a full binary tree, but some nodes are null. +# +# The width of one level is defined as the length between the end-nodes +# (the leftmost and right most non-null nodes in the level, +# where the null nodes between the end-nodes are also counted into the length calculation. +# +# Example 1: +# Input: +# +# 1 +# / \ +# 3 2 +# / \ \ +# 5 3 9 +# +# Output: 4 +# Explanation: The maximum width existing in the third level with the length 4 (5,3,null,9). +# Example 2: +# Input: +# +# 1 +# / +# 3 +# / \ +# 5 3 +# +# Output: 2 +# Explanation: The maximum width existing in the third level with the length 2 (5,3). +# Example 3: +# Input: +# +# 1 +# / \ +# 3 2 +# / +# 5 +# +# Output: 2 +# Explanation: The maximum width existing in the second level with the length 2 (3,2). +# Example 4: +# Input: +# +# 1 +# / \ +# 3 2 +# / \ +# 5 9 +# / \ +# 6 7 +# Output: 8 +# Explanation:The maximum width existing in the fourth level with the length 8 (6,null,null,null,null,null,null,7). +# +# Note: Answer will in the range of 32-bit signed integer. +# +# Definition for a binary tree node. +# class TreeNode(object): +# def __init__(self, x): +# self.val = x +# self.left = None +# self.right = None + +class Solution(object): + def widthOfBinaryTree(self, root): + """ + :type root: TreeNode + :rtype: int + """ + def dfs(node, i, depth, leftmosts): + if not node: + return 0 + if depth >= len(leftmosts): + leftmosts.append(i) + return max(i-leftmosts[depth]+1, \ + dfs(node.left, i*2, depth+1, leftmosts), \ + dfs(node.right, i*2+1, depth+1, leftmosts)) + + leftmosts = [] + return dfs(root, 1, 0, leftmosts) diff --git a/Python/maximum-xor-of-two-numbers-in-an-array.py b/Python/maximum-xor-of-two-numbers-in-an-array.py new file mode 100644 index 000000000..6a3156b19 --- /dev/null +++ b/Python/maximum-xor-of-two-numbers-in-an-array.py @@ -0,0 +1,36 @@ +# Time: O(n) +# Space: O(n) + +# Given a non-empty array of numbers, a0, a1, a2, ... , an-1, where 0 <= ai < 231. +# +# Find the maximum result of ai XOR aj, where 0 <= i, j < n. +# +# Could you do this in O(n) runtime? +# +# Example: +# +# Input: [3, 10, 5, 25, 2, 8] +# +# Output: 28 +# +# Explanation: The maximum result is 5 ^ 25 = 28. + +class Solution(object): + def findMaximumXOR(self, nums): + """ + :type nums: List[int] + :rtype: int + """ + result = 0 + + for i in reversed(xrange(32)): + result <<= 1 + prefixes = set() + for n in nums: + prefixes.add(n >> i) + for p in prefixes: + if (result | 1) ^ p in prefixes: + result += 1 + break + + return result diff --git a/Python/median-of-two-sorted-arrays.py b/Python/median-of-two-sorted-arrays.py index 47f6c6862..5e8c460b5 100644 --- a/Python/median-of-two-sorted-arrays.py +++ b/Python/median-of-two-sorted-arrays.py @@ -1,72 +1,128 @@ -# Time: O(log(m + n)) -# Space: O(log(m + n)) -# -# There are two sorted arrays A and B of size m and n respectively. -# Find the median of the two sorted arrays. The overall run time complexity should be O(log (m+n)). -# - -class Solution: - # @return a float - def findMedianSortedArrays(self, A, B): - lenA, lenB = len(A), len(B) - if (lenA + lenB) % 2 == 1: - return self.getKth(A, 0, B, 0, (lenA + lenB)/2 + 1) - else: - return (self.getKth(A, 0, B, 0, (lenA + lenB)/2) + self.getKth(A, 0, B, 0, (lenA + lenB)/2 + 1)) * 0.5 - - def getKth(self, A, i, B, j, k): - lenA, lenB = len(A) - i, len(B) - j - if lenA > lenB: - return self.getKth(B, j, A, i, k) - - if lenA == 0: - return B[j + k - 1] - - if k == 1: - return min(A[i], B[j]) - - pa = min(k/2, lenA) - pb = k - pa - - if A[i + pa - 1] < B[j + pb - 1]: - return self.getKth(A, i + pa, B, j , k - pa) - elif A[i + pa - 1] > B[j + pb - 1]: - return self.getKth(A, i, B, j + pb, k - pb) - else: - return A[i + pa - 1] +from __future__ import print_function +# Time: O(log(min(m, n))) +# Space: O(1) -# using list slicing (O(k)) may be slower than solution1 -class Solution2: - # @return a float - def findMedianSortedArrays(self, A, B): - lenA, lenB = len(A), len(B) - if (lenA + lenB) % 2 == 1: - return self.getKth(A, B, (lenA + lenB)/2 + 1) +# There are two sorted arrays nums1 and nums2 of size m and n respectively. +# Find the median of the two sorted arrays. +# The overall run time complexity should be O(log (m+n)). + +class Solution(object): + def findMedianSortedArrays(self, nums1, nums2): + """ + :type nums1: List[int] + :type nums2: List[int] + :rtype: float + """ + len1, len2 = len(nums1), len(nums2) + if (len1 + len2) % 2 == 1: + return self.getKth(nums1, nums2, (len1 + len2)/2 + 1) else: - return (self.getKth(A, B, (lenA + lenB)/2) + self.getKth(A, B, (lenA + lenB)/2 + 1)) * 0.5 - + return (self.getKth(nums1, nums2, (len1 + len2)/2) + \ + self.getKth(nums1, nums2, (len1 + len2)/2 + 1)) * 0.5 + def getKth(self, A, B, k): - lenA, lenB = len(A), len(B) - if lenA > lenB: + m, n = len(A), len(B) + if m > n: return self.getKth(B, A, k) - - if lenA == 0: - return B[k - 1] - - if k == 1: - return min(A[0], B[0]) - - pa = min(k/2, lenA) - pb = k - pa - - if A[pa - 1] < B[pb - 1]: - return self.getKth(A[pa:], B, k - pa) - elif A[pa - 1] > B[pb - 1]: - return self.getKth(A, B[pb:], k - pb) + + left, right = 0, m + while left < right: + mid = left + (right - left) / 2 + if 0 <= k - 1 - mid < n and A[mid] >= B[k - 1 - mid]: + right = mid + else: + left = mid + 1 + + Ai_minus_1 = A[left - 1] if left - 1 >= 0 else float("-inf") + Bj = B[k - 1 - left] if k - 1 - left >= 0 else float("-inf") + + return max(Ai_minus_1, Bj) + + +# Time: O(log(max(m, n)) * log(max_val - min_val)) +# Space: O(1) +# Generic solution. +class Solution_Generic(object): + def findMedianSortedArrays(self, nums1, nums2): + """ + :type nums1: List[int] + :type nums2: List[int] + :rtype: float + """ + len1, len2 = len(nums1), len(nums2) + if (len1 + len2) % 2 == 1: + return self.getKth([nums1, nums2], (len1 + len2)/2 + 1) + else: + return (self.getKth([nums1, nums2], (len1 + len2)/2) + \ + self.getKth([nums1, nums2], (len1 + len2)/2 + 1)) * 0.5 + + def getKth(self, arrays, k): + def binary_search(array, left, right, target, compare): + while left <= right: + mid = left + (right - left) / 2 + if compare(array, mid, target): + right = mid - 1 + else: + left = mid + 1 + return left + + def match(arrays, num, target): + res = 0 + for array in arrays: + if array: + res += len(array) - binary_search(array, 0, len(array) - 1, num, \ + lambda array, x, y: array[x] > y) + return res < target + + left, right = float("inf"), float("-inf") + for array in arrays: + if array: + left = min(left, array[0]) + right = max(right, array[-1]) + + return binary_search(arrays, left, right, k, match) + +class Solution_3(object): + def findMedianSortedArrays(self, A, B): + + if A is None and B is None: + return -1.0 + lenA = len(A) + lenB = len(B) + lenn = lenA + lenB; + + indexA,indexB,indexC = 0,0,0 + C = [False for i in xrange(lenn)] + while indexA < lenA and indexB < lenB: + if A[indexA] < B[indexB]: + C[indexC] = A[indexA] + indexC += 1 + indexA += 1 + else: + C[indexC] = B[indexB] + indexC += 1 + indexB += 1 + + while indexA < lenA: + C[indexC] = A[indexA] + indexC += 1 + indexA += 1 + + while indexB < lenB: + C[indexC] = B[indexB] + indexC += 1 + indexB += 1 + + indexM1 = (lenn - 1) / 2 + indexM2 = lenn / 2 + + if (lenn % 2 == 0): + return (C[indexM1] + C[indexM2]) / 2.0 else: - return A[pa - 1] - + return C[indexM2] / 1.0 + if __name__ == "__main__": - print Solution().findMedianSortedArrays([1, 3, 5, 7], [2, 4, 6]) - print Solution().findMedianSortedArrays([1, 3, 5], [2, 4, 6]) - \ No newline at end of file + print(Solution().findMedianSortedArrays([1, 3, 5, 7], [2, 4, 6])) + print(Solution_Generic().findMedianSortedArrays([1, 3, 5], [2, 4, 6])) + print(Solution_3().findMedianSortedArrays([1, 3, 5], [2, 4, 6])) + diff --git a/Python/meeting-rooms-ii.py b/Python/meeting-rooms-ii.py new file mode 100644 index 000000000..5d6903447 --- /dev/null +++ b/Python/meeting-rooms-ii.py @@ -0,0 +1,34 @@ +# Time: O(nlogn) +# Space: O(n) + +# Definition for an interval. +# class Interval: +# def __init__(self, s=0, e=0): +# self.start = s +# self.end = e + +class Solution: + # @param {Interval[]} intervals + # @return {integer} + def minMeetingRooms(self, intervals): + starts, ends = [], [] + for i in intervals: + starts.append(i.start) + ends.append(i.end) + + starts.sort() + ends.sort() + + s, e = 0, 0 + min_rooms, cnt_rooms = 0, 0 + while s < len(starts): + if starts[s] < ends[e]: + cnt_rooms += 1 # Acquire a room. + # Update the min number of rooms. + min_rooms = max(min_rooms, cnt_rooms) + s += 1 + else: + cnt_rooms -= 1 # Release a room. + e += 1 + + return min_rooms diff --git a/Python/meeting-rooms.py b/Python/meeting-rooms.py new file mode 100644 index 000000000..de7d864c6 --- /dev/null +++ b/Python/meeting-rooms.py @@ -0,0 +1,19 @@ +# Time: O(nlogn) +# Space: O(n) +# +# Definition for an interval. +# class Interval: +# def __init__(self, s=0, e=0): +# self.start = s +# self.end = e + +class Solution: + # @param {Interval[]} intervals + # @return {boolean} + def canAttendMeetings(self, intervals): + intervals.sort(key=lambda x: x.start) + + for i in xrange(1, len(intervals)): + if intervals[i].start < intervals[i-1].end: + return False + return True diff --git a/Python/merge-intervals.py b/Python/merge-intervals.py index 49641bb7d..dad170b34 100644 --- a/Python/merge-intervals.py +++ b/Python/merge-intervals.py @@ -1,8 +1,9 @@ -# Time: O(n^2) +from __future__ import print_function +# Time: O(nlogn) # Space: O(1) # # Given a collection of intervals, merge all overlapping intervals. -# +# # For example, # Given [1,3],[2,6],[8,10],[15,18], # return [1,6],[8,10],[15,18]. @@ -13,25 +14,29 @@ class Interval: def __init__(self, s=0, e=0): self.start = s self.end = e - + def __repr__(self): return "[{}, {}]".format(self.start, self.end) -class Solution: - # @param intervals, a list of Interval - # @return a list of Interval + +class Solution(object): def merge(self, intervals): - if len(intervals) == 0: + """ + :type intervals: List[Interval] + :rtype: List[Interval] + """ + if not intervals: return intervals - intervals.sort(key = lambda x: x.start) + intervals.sort(key=lambda x: x.start) result = [intervals[0]] - for i in range(1, len(intervals)): + for i in xrange(1, len(intervals)): prev, current = result[-1], intervals[i] - if current.start <= prev.end: + if current.start <= prev.end: prev.end = max(prev.end, current.end) else: result.append(current) return result + if __name__ == "__main__": - print Solution().merge([Interval(1, 3), Interval(2, 6), Interval(8, 10), Interval(15,18)]) + print(Solution().merge([Interval(1, 3), Interval(2, 6), Interval(8, 10), Interval(15,18)])) diff --git a/Python/merge-k-sorted-lists.py b/Python/merge-k-sorted-lists.py index 0cd4e311b..37b677712 100644 --- a/Python/merge-k-sorted-lists.py +++ b/Python/merge-k-sorted-lists.py @@ -1,44 +1,115 @@ +from __future__ import print_function # Time: O(nlogk) # Space: O(1) -# -# Merge k sorted linked lists and return it as one sorted list. Analyze and describe its complexity. -import heapq + +# Merge k sorted linked lists and return it as one sorted list. +# Analyze and describe its complexity. # Definition for singly-linked list. -class ListNode: +class ListNode(object): def __init__(self, x): self.val = x self.next = None - - def __repr__(self): - if self: - return "{} -> {}".format(self.val, repr(self.next)) -class Solution: + def __repr__(self): + if self: + return "{} -> {}".format(self.val, self.next) + + +# Merge two by two solution. +class Solution(object): + def mergeKLists(self, lists): + """ + :type lists: List[ListNode] + :rtype: ListNode + """ + def mergeTwoLists(l1, l2): + curr = dummy = ListNode(0) + while l1 and l2: + if l1.val < l2.val: + curr.next = l1 + l1 = l1.next + else: + curr.next = l2 + l2 = l2.next + curr = curr.next + curr.next = l1 or l2 + return dummy.next + + if not lists: + return None + left, right = 0, len(lists) - 1; + while right > 0: + if left >= right: + left = 0 + else: + lists[left] = mergeTwoLists(lists[left], lists[right]) + left += 1 + right -= 1 + return lists[0] + + +# Time: O(nlogk) +# Space: O(logk) +# Divide and Conquer solution. +class Solution2: + # @param a list of ListNode + # @return a ListNode + def mergeKLists(self, lists): + def mergeTwoLists(l1, l2): + curr = dummy = ListNode(0) + while l1 and l2: + if l1.val < l2.val: + curr.next = l1 + l1 = l1.next + else: + curr.next = l2 + l2 = l2.next + curr = curr.next + curr.next = l1 or l2 + return dummy.next + + def mergeKListsHelper(lists, begin, end): + if begin > end: + return None + if begin == end: + return lists[begin] + return mergeTwoLists(mergeKListsHelper(lists, begin, (begin + end) / 2), \ + mergeKListsHelper(lists, (begin + end) / 2 + 1, end)) + + return mergeKListsHelper(lists, 0, len(lists) - 1) + + +# Time: O(nlogk) +# Space: O(k) +# Heap solution. +import heapq +class Solution3: # @param a list of ListNode # @return a ListNode def mergeKLists(self, lists): dummy = ListNode(0) current = dummy - + heap = [] for sorted_list in lists: if sorted_list: heapq.heappush(heap, (sorted_list.val, sorted_list)) - + while heap: smallest = heapq.heappop(heap)[1] current.next = smallest current = current.next if smallest.next: heapq.heappush(heap, (smallest.next.val, smallest.next)) - + return dummy.next + if __name__ == "__main__": list1 = ListNode(1) list1.next = ListNode(3) list2 = ListNode(2) list2.next = ListNode(4) - - print Solution().mergeKLists([list1, list2]) \ No newline at end of file + + print(Solution().mergeKLists([list1, list2])) diff --git a/Python/merge-sorted-array.py b/Python/merge-sorted-array.py index 1f07ae31e..411451fc0 100644 --- a/Python/merge-sorted-array.py +++ b/Python/merge-sorted-array.py @@ -1,10 +1,11 @@ +from __future__ import print_function # Time: O(n) # Space: O(1) # # Given two sorted integer arrays A and B, merge B into A as one sorted array. -# +# # Note: -# You may assume that A has enough space (size that is greater or equal to m + n) to hold additional elements from B. +# You may assume that A has enough space (size that is greater or equal to m + n) to hold additional elements from B. # The number of elements initialized in A and B are m and n respectively. # @@ -16,7 +17,7 @@ class Solution: # @return nothing def merge(self, A, m, B, n): last, i, j = m + n - 1, m - 1, n - 1 - + while i >= 0 and j >= 0: if A[i] > B[j]: A[last] = A[i] @@ -24,7 +25,7 @@ def merge(self, A, m, B, n): else: A[last] = B[j] last, j = last - 1, j - 1 - + while j >= 0: A[last] = B[j] last, j = last - 1, j - 1 @@ -33,4 +34,35 @@ def merge(self, A, m, B, n): A = [1, 3, 5, 0, 0, 0, 0] B = [2, 4, 6, 7] Solution().merge(A, 3, B, 4) - print A \ No newline at end of file + print(A) + + +# Time: O(n) +# Space: O(n) +# you may get a input like this, +# nums1 : [0] +# m : 0 +# nums2 : [1] +# n : 1 +# so you need to judge if n is still large than 0 +class Solution2: + def merge(self, nums1, m, nums2, n): + """ + :type nums1: List[int] + :type m: int + :type nums2: List[int] + :type n: int + :rtype: void Do not return anything, modify nums1 in-place instead. + """ + while m > 0 and n > 0: + if nums1[m-1] > nums2[n-1]: + nums1[m+n-1] = nums1[m-1] + m -= 1 + else: + nums1[m+n-1] = nums2[n-1] + n -= 1 + if n > 0: + nums1[:n] = nums2[:n] # Space: O(n), + # Reference: + # - https://stackoverflow.com/questions/4948293/python-slice-assignment-memory-usage + # - https://stackoverflow.com/questions/10623302/how-assignment-works-with-python-list-slice diff --git a/Python/merge-two-binary-trees.py b/Python/merge-two-binary-trees.py new file mode 100644 index 000000000..89341b1e3 --- /dev/null +++ b/Python/merge-two-binary-trees.py @@ -0,0 +1,53 @@ +# Time: O(n) +# Space: O(h) + +# Given two binary trees and imagine that +# when you put one of them to cover the other, +# some nodes of the two trees are overlapped +# while the others are not. +# +# You need to merge them into a new binary tree. +# The merge rule is that if two nodes overlap, +# then sum node values up as the new value of the merged node. +# Otherwise, the NOT null node will be used as the node of new tree. +# +# Example 1: +# Input: +# Tree 1 Tree 2 +# 1 2 +# / \ / \ +# 3 2 1 3 +# / \ \ +# 5 4 7 +# Output: +# Merged tree: +# 3 +# / \ +# 4 5 +# / \ \ +# 5 4 7 +# +# Note: The merging process must start from the root nodes of both trees. + +# Definition for a binary tree node. +# class TreeNode(object): +# def __init__(self, x): +# self.val = x +# self.left = None +# self.right = None + +class Solution(object): + def mergeTrees(self, t1, t2): + """ + :type t1: TreeNode + :type t2: TreeNode + :rtype: TreeNode + """ + if t1 is None: + return t2 + if t2 is None: + return t1 + t1.val += t2.val + t1.left = self.mergeTrees(t1.left, t2.left) + t1.right = self.mergeTrees(t1.right, t2.right) + return t1 diff --git a/Python/merge-two-sorted-lists.py b/Python/merge-two-sorted-lists.py index 609c90a57..26c2ec5c5 100644 --- a/Python/merge-two-sorted-lists.py +++ b/Python/merge-two-sorted-lists.py @@ -1,48 +1,47 @@ +from __future__ import print_function # Time: O(n) # Space: O(1) # -# Merge two sorted linked lists and return it as a new list. +# Merge two sorted linked lists and return it as a new list. # The new list should be made by splicing together the nodes of the first two lists. # # Definition for singly-linked list. -class ListNode: +class ListNode(object): def __init__(self, x): self.val = x self.next = None - + def __repr__(self): if self: return "{} -> {}".format(self.val, self.next) -class Solution: - # @param two ListNodes - # @return a ListNode + +class Solution(object): def mergeTwoLists(self, l1, l2): - dummy = ListNode(0) - current = dummy - + """ + :type l1: ListNode + :type l2: ListNode + :rtype: ListNode + """ + curr = dummy = ListNode(0) while l1 and l2: if l1.val < l2.val: - current.next = l1 + curr.next = l1 l1 = l1.next else: - current.next = l2 + curr.next = l2 l2 = l2.next - current = current.next - - if l1: - current.next = l1 - else: - current.next = l2 - + curr = curr.next + curr.next = l1 or l2 return dummy.next + if __name__ == "__main__": l1 = ListNode(0) l1.next = ListNode(1) l2 = ListNode (2) l2.next = ListNode(3) - print Solution().mergeTwoLists(l1, l2) - - \ No newline at end of file + print(Solution().mergeTwoLists(l1, l2)) + + diff --git a/Python/middle-of-the-linked-list.py b/Python/middle-of-the-linked-list.py new file mode 100644 index 000000000..b21eea685 --- /dev/null +++ b/Python/middle-of-the-linked-list.py @@ -0,0 +1,40 @@ +# Time: O(n) +# Space: O(1) + +# Given a non-empty, singly linked list with head node head, +# return a middle node of linked list. +# +# If there are two middle nodes, return the second middle node. +# +# Example 1: +# +# Input: [1,2,3,4,5] +# Output: Node 3 from this list (Serialization: [3,4,5]) +# The returned node has value 3. (The judge's serialization of this node is [3,4,5]). +# Note that we returned a ListNode object ans, such that: +# ans.val = 3, ans.next.val = 4, ans.next.next.val = 5, and ans.next.next.next = NULL. +# Example 2: +# +# Input: [1,2,3,4,5,6] +# Output: Node 4 from this list (Serialization: [4,5,6]) +# Since the list has two middle nodes with values 3 and 4, we return the second one. +# +# Note: +# - The number of nodes in the given list will be between 1 and 100. + +# Definition for singly-linked list. +class ListNode(object): + def __init__(self, x): + self.val = x + self.next = None + +class Solution(object): + def middleNode(self, head): + """ + :type head: ListNode + :rtype: ListNode + """ + slow, fast = head, head + while fast and fast.next: + slow, fast = slow.next, fast.next.next + return slow diff --git a/Python/min-cost-climbing-stairs.py b/Python/min-cost-climbing-stairs.py new file mode 100644 index 000000000..89c0c3380 --- /dev/null +++ b/Python/min-cost-climbing-stairs.py @@ -0,0 +1,31 @@ +# Time: O(n) +# Space: O(1) + +# On a staircase, the i-th step has some non-negative cost cost[i] assigned (0 indexed). +# +# Once you pay the cost, you can either climb one or two steps. +# You need to find minimum cost to reach the top of the floor, +# and you can either start from the step with index 0, or the step with index 1. +# +# Example 1: +# Input: cost = [10, 15, 20] +# Output: 15 +# Explanation: Cheapest is start on cost[1], pay that cost and go to the top. +# Example 2: +# Input: cost = [1, 100, 1, 1, 1, 100, 1, 1, 100, 1] +# Output: 6 +# Explanation: Cheapest is start on cost[0], and only step on 1s, skipping cost[3]. +# Note: +# - cost will have a length in the range [2, 1000]. +# - Every cost[i] will be an integer in the range [0, 999]. + +class Solution(object): + def minCostClimbingStairs(self, cost): + """ + :type cost: List[int] + :rtype: int + """ + dp = [0] * 3 + for i in reversed(xrange(len(cost))): + dp[i%3] = cost[i] + min(dp[(i+1)%3], dp[(i+2)%3]) + return min(dp[0], dp[1]) diff --git a/Python/min-stack.py b/Python/min-stack.py index 4036555b5..ff31748a8 100644 --- a/Python/min-stack.py +++ b/Python/min-stack.py @@ -2,7 +2,7 @@ # Space: O(1) # # Design a stack that supports push, pop, top, and retrieving the minimum element in constant time. -# +# # push(x) -- Push element x onto stack. # pop() -- Removes the element on top of the stack. # top() -- Get the top element. @@ -13,11 +13,11 @@ class MinStack: def __init__(self): self.min = None self.stack = [] - + # @param x, an integer # @return an integer def push(self, x): - if len(self.stack) == 0: + if not self.stack: self.stack.append(0) self.min = x else: @@ -38,7 +38,7 @@ def top(self): return x + self.min else: return self.min - + # @return an integer def getMin(self): return self.min @@ -67,11 +67,11 @@ def pop(self): self.minStack[-1][1] -= 1 if self.minStack[-1][1] == 0: self.minStack.pop() - + # @return an integer def top(self): return self.stack[-1] - + # @return an integer def getMin(self): return self.minStack[-1][0] @@ -80,4 +80,4 @@ def getMin(self): stack = MinStack() stack.push(-1) print [stack.top(), stack.getMin()] - \ No newline at end of file + diff --git a/Python/minesweeper.py b/Python/minesweeper.py new file mode 100644 index 000000000..e5ee0b93d --- /dev/null +++ b/Python/minesweeper.py @@ -0,0 +1,140 @@ +# Time: O(m * n) +# Space: O(m + n) + +# Let's play the minesweeper game (Wikipedia, online game)! +# +# You are given a 2D char matrix representing the game board. 'M' represents an unrevealed mine, +# 'E' represents an unrevealed empty square, 'B' represents a revealed blank square that has no adjacent +# (above, below, left, right, and all 4 diagonals) mines, digit ('1' to '8') represents +# how many mines are adjacent to this revealed square, and finally 'X' represents a revealed mine. +# +# Now given the next click position (row and column indices) among all the unrevealed squares ('M' or 'E'), +# return the board after revealing this position according to the following rules: +# +# If a mine ('M') is revealed, then the game is over - change it to 'X'. +# If an empty square ('E') with no adjacent mines is revealed, then change it to revealed blank ('B') +# and all of its adjacent unrevealed squares should be revealed recursively. +# If an empty square ('E') with at least one adjacent mine is revealed, then change it to a digit ('1' to '8') +# representing the number of adjacent mines. +# Return the board when no more squares will be revealed. +# +# Example 1: +# Input: +# [['E', 'E', 'E', 'E', 'E'], +# ['E', 'E', 'M', 'E', 'E'], +# ['E', 'E', 'E', 'E', 'E'], +# ['E', 'E', 'E', 'E', 'E']] +# Click : [3,0] +# Output: +# [['B', '1', 'E', '1', 'B'], +# ['B', '1', 'M', '1', 'B'], +# ['B', '1', '1', '1', 'B'], +# ['B', 'B', 'B', 'B', 'B']] +# +# Example 2: +# Input: +# [['B', '1', 'E', '1', 'B'], +# ['B', '1', 'M', '1', 'B'], +# ['B', '1', '1', '1', 'B'], +# ['B', 'B', 'B', 'B', 'B']] +# +# Click : [1,2] +# Output: +# [['B', '1', 'E', '1', 'B'], +# ['B', '1', 'X', '1', 'B'], +# ['B', '1', '1', '1', 'B'], +# ['B', 'B', 'B', 'B', 'B']] +# +# Note: +# The range of the input matrix's height and width is [1,50]. +# The click position will only be an unrevealed square ('M' or 'E'), +# which also means the input board contains at least one clickable square. +# The input board won't be a stage when game is over (some mines have been revealed). +# For simplicity, not mentioned rules should be ignored in this problem. +# For example, you don't need to reveal all the unrevealed mines when the game is over, +# consider any cases that you will win the game or flag any squares. + +import collections + + +class Solution(object): + def updateBoard(self, board, click): + """ + :type board: List[List[str]] + :type click: List[int] + :rtype: List[List[str]] + """ + q = collections.deque([click]) + while q: + row, col = q.popleft() + if board[row][col] == 'M': + board[row][col] = 'X' + else: + count = 0 + for i in xrange(-1, 2): + for j in xrange(-1, 2): + if i == 0 and j == 0: + continue + r, c = row + i, col + j + if not (0 <= r < len(board)) or not (0 <= c < len(board[r])): + continue + if board[r][c] == 'M' or board[r][c] == 'X': + count += 1 + + if count: + board[row][col] = chr(count + ord('0')) + else: + board[row][col] = 'B' + for i in xrange(-1, 2): + for j in xrange(-1, 2): + if i == 0 and j == 0: + continue + r, c = row + i, col + j + if not (0 <= r < len(board)) or not (0 <= c < len(board[r])): + continue + if board[r][c] == 'E': + q.append((r, c)) + board[r][c] = ' ' + + return board + + +# Time: O(m * n) +# Space: O(m * n) +class Solution2(object): + def updateBoard(self, board, click): + """ + :type board: List[List[str]] + :type click: List[int] + :rtype: List[List[str]] + """ + row, col = click[0], click[1] + if board[row][col] == 'M': + board[row][col] = 'X' + else: + count = 0 + for i in xrange(-1, 2): + for j in xrange(-1, 2): + if i == 0 and j == 0: + continue + r, c = row + i, col + j + if not (0 <= r < len(board)) or not (0 <= c < len(board[r])): + continue + if board[r][c] == 'M' or board[r][c] == 'X': + count += 1 + + if count: + board[row][col] = chr(count + ord('0')) + else: + board[row][col] = 'B' + for i in xrange(-1, 2): + for j in xrange(-1, 2): + if i == 0 and j == 0: + continue + r, c = row + i, col + j + if not (0 <= r < len(board)) or not (0 <= c < len(board[r])): + continue + if board[r][c] == 'E': + self.updateBoard(board, (r, c)) + + return board diff --git a/Python/mini-parser.py b/Python/mini-parser.py new file mode 100644 index 000000000..b4f724978 --- /dev/null +++ b/Python/mini-parser.py @@ -0,0 +1,98 @@ +# Time: O(n) +# Space: O(h) + +# Given a nested list of integers represented as a string, implement a parser to deserialize it. +# +# Each element is either an integer, or a list -- whose elements may also be integers or other lists. +# +# Note: You may assume that the string is well-formed: +# +# String is non-empty. +# String does not contain white spaces. +# String contains only digits 0-9, [, - ,, ]. +# Example 1: +# +# Given s = "324", +# +# You should return a NestedInteger object which contains a single integer 324. +# Example 2: +# +# Given s = "[123,[456,[789]]]", +# +# Return a NestedInteger object containing a nested list with 2 elements: +# +# 1. An integer containing value 123. +# 2. A nested list containing two elements: +# i. An integer containing value 456. +# ii. A nested list with one element: +# a. An integer containing value 789. +# +# """ +# This is the interface that allows for creating nested lists. +# You should not implement it, or speculate about its implementation +# """ +class NestedInteger(object): + def __init__(self, value=None): + """ + If value is not specified, initializes an empty list. + Otherwise initializes a single integer equal to value. + """ + + def isInteger(self): + """ + @return True if this NestedInteger holds a single integer, rather than a nested list. + :rtype bool + """ + + def add(self, elem): + """ + Set this NestedInteger to hold a nested list and adds a nested integer elem to it. + :rtype void + """ + + def setInteger(self, value): + """ + Set this NestedInteger to hold a single integer equal to value. + :rtype void + """ + + def getInteger(self): + """ + @return the single integer that this NestedInteger holds, if it holds a single integer + Return None if this NestedInteger holds a nested list + :rtype int + """ + + def getList(self): + """ + @return the nested list that this NestedInteger holds, if it holds a nested list + Return None if this NestedInteger holds a single integer + :rtype List[NestedInteger] + """ + + +class Solution(object): + def deserialize(self, s): + if not s: + return NestedInteger() + + if s[0] != '[': + return NestedInteger(int(s)) + + stk = [] + + i = 0 + for j in xrange(len(s)): + if s[j] == '[': + stk += NestedInteger(), + i = j+1 + elif s[j] in ',]': + if s[j-1].isdigit(): + stk[-1].add(NestedInteger(int(s[i:j]))) + if s[j] == ']' and len(stk) > 1: + cur = stk[-1] + stk.pop(); + stk[-1].add(cur) + i = j+1 + + return stk[-1] diff --git a/Python/minimize-max-distance-to-gas-station.py b/Python/minimize-max-distance-to-gas-station.py new file mode 100644 index 000000000..a8676a4a4 --- /dev/null +++ b/Python/minimize-max-distance-to-gas-station.py @@ -0,0 +1,41 @@ +# Time: O(nlogr) +# Space: O(1) + +# On a horizontal number line, we have gas stations at positions +# stations[0], stations[1], ..., stations[N-1], where N = stations.length. +# +# Now, we add K more gas stations so that D, +# the maximum distance between adjacent gas stations, is minimized. +# +# Return the smallest possible value of D. +# +# Example: +# +# Input: stations = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], K = 9 +# Output: 0.500000 +# +# Note: +# - stations.length will be an integer in range [10, 2000]. +# - stations[i] will be an integer in range [0, 10^8]. +# - K will be an integer in range [1, 10^6]. +# - Answers within 10^-6 of the true value will be accepted as correct. + +class Solution(object): + def minmaxGasDist(self, stations, K): + """ + :type stations: List[int] + :type K: int + :rtype: float + """ + def possible(stations, K, guess): + return sum(int((stations[i+1]-stations[i]) / guess) + for i in xrange(len(stations)-1)) <= K + + left, right = 0, 10**8 + while right-left > 1e-6: + mid = left + (right-left)/2.0 + if possible(mid): + right = mid + else: + left = mid + return left diff --git a/Python/minimum-absolute-difference-in-bst.py b/Python/minimum-absolute-difference-in-bst.py new file mode 100644 index 000000000..daaafe6df --- /dev/null +++ b/Python/minimum-absolute-difference-in-bst.py @@ -0,0 +1,46 @@ +# Time: O(n) +# Space: O(h) + +# Given a binary search tree with non-negative values, +# find the minimum absolute difference between values of any two nodes. +# +# Example: +# +# Input: +# +# 1 +# \ +# 3 +# / +# 2 +# +# Output: +# 1 +# +# Explanation: +# The minimum absolute difference is 1, +# which is the difference between 2 and 1 (or between 2 and 3). +# Note: There are at least two nodes in this BST. +# +# Definition for a binary tree node. +# class TreeNode(object): +# def __init__(self, x): +# self.val = x +# self.left = None +# self.right = None + +class Solution(object): + def getMinimumDifference(self, root): + """ + :type root: TreeNode + :rtype: int + """ + def inorderTraversal(root, prev, result): + if not root: + return (result, prev) + + result, prev = inorderTraversal(root.left, prev, result) + if prev: result = min(result, root.val - prev.val) + return inorderTraversal(root.right, root, result) + + return inorderTraversal(root, None, float("inf"))[0] diff --git a/Python/minimum-ascii-delete-sum-for-two-strings.py b/Python/minimum-ascii-delete-sum-for-two-strings.py new file mode 100644 index 000000000..6fd3f7683 --- /dev/null +++ b/Python/minimum-ascii-delete-sum-for-two-strings.py @@ -0,0 +1,72 @@ +# Time: O(m * n) +# Space: O(n) + +# Given two strings s1, s2, find the lowest ASCII sum of deleted characters to make two strings equal. +# +# Example 1: +# Input: s1 = "sea", s2 = "eat" +# Output: 231 +# Explanation: Deleting "s" from "sea" adds the ASCII value of "s" (115) to the sum. +# Deleting "t" from "eat" adds 116 to the sum. +# At the end, both strings are equal, and 115 + 116 = 231 is the minimum sum possible to achieve this. +# +# Example 2: +# Input: s1 = "delete", s2 = "leet" +# Output: 403 +# Explanation: Deleting "dee" from "delete" to turn the string into "let", +# adds 100[d]+101[e]+101[e] to the sum. Deleting "e" from "leet" adds 101[e] to the sum. +# At the end, both strings are equal to "let", and the answer is 100+101+101+101 = 403. +# If instead we turned both strings into "lee" or "eet", we would get answers of 433 or 417, which are higher. +# +# Note: +# - 0 < s1.length, s2.length <= 1000. +# - All elements of each string will have an ASCII value in [97, 122]. + +# DP with rolling window +class Solution(object): + def minimumDeleteSum(self, s1, s2): + """ + :type s1: str + :type s2: str + :rtype: int + """ + dp = [[0] * (len(s2)+1) for _ in xrange(2)] + for j in xrange(len(s2)): + dp[0][j+1] = dp[0][j] + ord(s2[j]) + + for i in xrange(len(s1)): + dp[(i+1)%2][0] = dp[i%2][0] + ord(s1[i]) + for j in xrange(len(s2)): + if s1[i] == s2[j]: + dp[(i+1)%2][j+1] = dp[i%2][j] + else: + dp[(i+1)%2][j+1] = min(dp[i%2][j+1] + ord(s1[i]), \ + dp[(i+1)%2][j] + ord(s2[j])) + + return dp[len(s1)%2][-1] + + +# Time: O(m * n) +# Space: O(m * n) +class Solution2(object): + def minimumDeleteSum(self, s1, s2): + """ + :type s1: str + :type s2: str + :rtype: int + """ + dp = [[0] * (len(s2)+1) for _ in xrange(len(s1)+1)] + for i in xrange(len(s1)): + dp[i+1][0] = dp[i][0] + ord(s1[i]) + for j in xrange(len(s2)): + dp[0][j+1] = dp[0][j] + ord(s2[j]) + + for i in xrange(len(s1)): + for j in xrange(len(s2)): + if s1[i] == s2[j]: + dp[i+1][j+1] = dp[i][j] + else: + dp[i+1][j+1] = min(dp[i][j+1] + ord(s1[i]), \ + dp[i+1][j] + ord(s2[j])) + + return dp[-1][-1] diff --git a/Python/minimum-cost-to-hire-k-workers.py b/Python/minimum-cost-to-hire-k-workers.py new file mode 100644 index 000000000..c43b0fe9d --- /dev/null +++ b/Python/minimum-cost-to-hire-k-workers.py @@ -0,0 +1,60 @@ +# Time: O(nlogn) +# Space : O(n) + +# There are N workers. +# The i-th worker has a quality[i] and a minimum wage expectation wage[i]. +# +# Now we want to hire exactly K workers to form a paid group. +# When hiring a group of K workers, we must pay them according to +# the following rules: +# +# Every worker in the paid group should be paid in the ratio of +# their quality compared to other workers in the paid group. +# Every worker in the paid group must be paid at least their minimum wage +# expectation. +# Return the least amount of money needed to form a paid group satisfying +# the above conditions. +# +# Example 1: +# +# Input: quality = [10,20,5], wage = [70,50,30], K = 2 +# Output: 105.00000 +# Explanation: We pay 70 to 0-th worker and 35 to 2-th worker. +# Example 2: +# +# Input: quality = [3,1,10,10,1], wage = [4,8,2,2,7], K = 3 +# Output: 30.66667 +# Explanation: We pay 4 to 0-th worker, 13.33333 to 2-th and 3-th workers +# seperately. +# +# Note: +# - 1 <= K <= N <= 10000, where N = quality.length = wage.length +# - 1 <= quality[i] <= 10000 +# - 1 <= wage[i] <= 10000 +# - Answers within 10^-5 of the correct answer will be considered correct. + +import itertools +import heapq + + +class Solution(object): + def mincostToHireWorkers(self, quality, wage, K): + """ + :type quality: List[int] + :type wage: List[int] + :type K: int + :rtype: float + """ + workers = [[float(w)/q, q] for w, q in itertools.izip(wage, quality)] + workers.sort() + result = float("inf") + qsum = 0 + max_heap = [] + for r, q in workers: + qsum += q + heapq.heappush(max_heap, -q) + if len(max_heap) > K: + qsum -= -heapq.heappop(max_heap) + if len(max_heap) == K: + result = min(result, qsum*r) + return result diff --git a/Python/minimum-depth-of-binary-tree.py b/Python/minimum-depth-of-binary-tree.py index 5494074ec..5421138ce 100644 --- a/Python/minimum-depth-of-binary-tree.py +++ b/Python/minimum-depth-of-binary-tree.py @@ -1,8 +1,9 @@ +from __future__ import print_function # Time: O(n) -# Space: O(logn) +# Space: O(h), h is height of binary tree # # Given a binary tree, find its minimum depth. -# +# # The minimum depth is the number of nodes along the shortest path from the root node down to the nearest leaf node. # @@ -19,7 +20,7 @@ class Solution: def minDepth(self, root): if root is None: return 0 - + if root.left and root.right: return min(self.minDepth(root.left), self.minDepth(root.right)) + 1 else: @@ -28,4 +29,4 @@ def minDepth(self, root): if __name__ == "__main__": root = TreeNode(1) root.left = TreeNode(2) - print Solution().minDepth(root) \ No newline at end of file + print(Solution().minDepth(root)) diff --git a/Python/minimum-distance-between-bst-nodes.py b/Python/minimum-distance-between-bst-nodes.py new file mode 100644 index 000000000..dfb2ad825 --- /dev/null +++ b/Python/minimum-distance-between-bst-nodes.py @@ -0,0 +1,54 @@ +# Time: O(n) +# Space: O(h) + +# Given a Binary Search Tree (BST) with the root node root, +# return the minimum difference between the values of any two different nodes in the tree. +# +# Example : +# +# Input: root = [4,2,6,1,3,null,null] +# Output: 1 +# Explanation: +# Note that root is a TreeNode object, not an array. +# +# The given tree [4,2,6,1,3,null,null] is represented by the following diagram: +# +# 4 +# / \ +# 2 6 +# / \ +# 1 3 +# +# while the minimum difference in this tree is 1, +# it occurs between node 1 and node 2, also between node 3 and node 2. +# +# Note: +# - The size of the BST will be between 2 and 100. +# - The BST is always valid, each node's value is an integer, and each node's value is different. + +# Definition for a binary tree node. +# class TreeNode(object): +# def __init__(self, x): +# self.val = x +# self.left = None +# self.right = None + +class Solution(object): + def minDiffInBST(self, root): + """ + :type root: TreeNode + :rtype: int + """ + def dfs(node): + if not node: + return + dfs(node.left) + self.result = min(self.result, node.val-self.prev) + self.prev = node.val + dfs(node.right) + + self.prev = float('-inf') + self.result = float('inf') + dfs(root) + return self.result + diff --git a/Python/minimum-factorization.py b/Python/minimum-factorization.py new file mode 100644 index 000000000..756c20e81 --- /dev/null +++ b/Python/minimum-factorization.py @@ -0,0 +1,19 @@ +# Time: O(loga) +# Space: O(1) + +class Solution(object): + def smallestFactorization(self, a): + """ + :type a: int + :rtype: int + """ + if a < 2: + return a + result, mul = 0, 1 + for i in reversed(xrange(2, 10)): + while a % i == 0: + a /= i + result = mul*i + result + mul *= 10 + return result if a == 1 and result < 2**31 else 0 + diff --git a/Python/minimum-genetic-mutation.py b/Python/minimum-genetic-mutation.py new file mode 100644 index 000000000..bd89371fe --- /dev/null +++ b/Python/minimum-genetic-mutation.py @@ -0,0 +1,66 @@ +# Time: O(n * b), n is the length of gene string, b is size of bank +# Space: O(b) + +# A gene string can be represented by an 8-character long string, +# with choices from "A","C","G","T". +# Suppose we need to investigate about a mutation (mutation from "start" to "end"), +# where ONE mutation is defined as ONE single character changed in the gene string. +# For example, "AACCGGTT" -> "AACCGGTA" is 1 mutation. +# Also, there is a given gene "bank", which records all the valid gene mutations. +# A gene must be in the bank to make it a valid gene string. +# +# Now, given 3 things - start, end, bank, +# your task is to determine what is the minimum number of mutations needed to +# mutate from "start" to "end". If there is no such a mutation, return -1. +# +# NOTE: 1. Starting point is assumed to be valid, so it might not be included in the bank. +# 2. If multiple mutations are needed, all mutations during in the sequence must be valid. +# +# For example, +# +# bank: "AACCGGTA" +# start: "AACCGGTT" +# end: "AACCGGTA" +# return: 1 +# +# bank: "AACCGGTA", "AACCGCTA", "AAACGGTA" +# start: "AACCGGTT" +# end: "AAACGGTA" +# return: 2 +# +# bank: "AAAACCCC", "AAACCCCC", "AACCCCCC" +# start: "AAAAACCC" +# end: "AACCCCCC" +# return: 3 + +from collections import deque + +class Solution(object): + def minMutation(self, start, end, bank): + """ + :type start: str + :type end: str + :type bank: List[str] + :rtype: int + """ + lookup = {} + for b in bank: + lookup[b] = False + + q = deque([(start, 0)]) + while q: + cur, level = q.popleft() + if cur == end: + return level + + for i in xrange(len(cur)): + for c in ['A', 'T', 'C', 'G']: + if cur[i] == c: + continue + + next_str = cur[:i] + c + cur[i+1:] + if next_str in lookup and lookup[next_str] == False: + q.append((next_str, level+1)) + lookup[next_str] = True + + return -1 diff --git a/Python/minimum-height-trees.py b/Python/minimum-height-trees.py new file mode 100644 index 000000000..41ba4bc8c --- /dev/null +++ b/Python/minimum-height-trees.py @@ -0,0 +1,95 @@ +# Time: O(n) +# Space: O(n) + +# For a undirected graph with tree characteristics, we can +# choose any node as the root. The result graph is then a +# rooted tree. Among all possible rooted trees, those with +# minimum height are called minimum height trees (MHTs). +# Given such a graph, write a function to find all the +# MHTs and return a list of their root labels. +# +# Format +# The graph contains n nodes which are labeled from 0 to n - 1. +# You will be given the number n and a list of undirected +# edges (each edge is a pair of labels). +# +# You can assume that no duplicate edges will appear in edges. +# Since all edges are undirected, [0, 1] is the same as [1, 0] +# and thus will not appear together in edges. +# +# Example 1: +# +# Given n = 4, edges = [[1, 0], [1, 2], [1, 3]] +# +# 0 +# | +# 1 +# / \ +# 2 3 +# return [1] +# +# Example 2: +# +# Given n = 6, edges = [[0, 3], [1, 3], [2, 3], [4, 3], [5, 4]] +# +# 0 1 2 +# \ | / +# 3 +# | +# 4 +# | +# 5 +# return [3, 4] +# +# Hint: +# +# How many MHTs can a graph have at most? +# Note: +# +# (1) According to the definition of tree on Wikipedia: +# "a tree is an undirected graph in which any two vertices +# are connected by exactly one path. In other words, +# any connected graph without simple cycles is a tree." +# +# (2) The height of a rooted tree is the number of edges on the +# longest downward path between the root and a leaf. + +import collections + + +class Solution(object): + def findMinHeightTrees(self, n, edges): + """ + :type n: int + :type edges: List[List[int]] + :rtype: List[int] + """ + if n == 1: + return [0] + + neighbors = collections.defaultdict(set) + for u, v in edges: + neighbors[u].add(v) + neighbors[v].add(u) + + pre_level, unvisited = [], set() + for i in xrange(n): + if len(neighbors[i]) == 1: # A leaf. + pre_level.append(i) + unvisited.add(i) + + # A graph can have 2 MHTs at most. + # BFS from the leaves until the number + # of the unvisited nodes is less than 3. + while len(unvisited) > 2: + cur_level = [] + for u in pre_level: + unvisited.remove(u) + for v in neighbors[u]: + if v in unvisited: + neighbors[v].remove(u) + if len(neighbors[v]) == 1: + cur_level.append(v) + pre_level = cur_level + + return list(unvisited) diff --git a/Python/minimum-index-sum-of-two-lists.py b/Python/minimum-index-sum-of-two-lists.py new file mode 100644 index 000000000..4a953c004 --- /dev/null +++ b/Python/minimum-index-sum-of-two-lists.py @@ -0,0 +1,51 @@ +# Time: O((m + n) * l), m is the size of list1, n is the size of list2 +# Space: O(m * l), l is the average length of string + +# Suppose Andy and Doris want to choose a restaurant for dinner, +# and they both have a list of favorite restaurants represented by strings. +# +# You need to help them find out their common interest with the least list index sum. +# If there is a choice tie between answers, output all of them with no order requirement. +# You could assume there always exists an answer. +# +# Example 1: +# Input: +# ["Shogun", "Tapioca Express", "Burger King", "KFC"] +# ["Piatti", "The Grill at Torrey Pines", "Hungry Hunter Steakhouse", "Shogun"] +# Output: ["Shogun"] +# Explanation: The only restaurant they both like is "Shogun". +# Example 2: +# Input: +# ["Shogun", "Tapioca Express", "Burger King", "KFC"] +# ["KFC", "Shogun", "Burger King"] +# Output: ["Shogun"] +# Explanation: The restaurant they both like and have the least index sum is "Shogun" with index sum 1 (0+1). +# Note: +# The length of both lists will be in the range of [1, 1000]. +# The length of strings in both lists will be in the range of [1, 30]. +# The index is starting from 0 to the list length minus 1. +# No duplicates in both lists. + +class Solution(object): + def findRestaurant(self, list1, list2): + """ + :type list1: List[str] + :type list2: List[str] + :rtype: List[str] + """ + lookup = {} + for i, s in enumerate(list1): + lookup[s] = i + + result = [] + min_sum = float("inf") + for j, s in enumerate(list2): + if j > min_sum: + break + if s in lookup: + if j + lookup[s] < min_sum: + result = [s] + min_sum = j + lookup[s] + elif j + lookup[s] == min_sum: + result.append(s) + return result diff --git a/Python/minimum-moves-to-equal-array-elements-ii.py b/Python/minimum-moves-to-equal-array-elements-ii.py new file mode 100644 index 000000000..49207b3bd --- /dev/null +++ b/Python/minimum-moves-to-equal-array-elements-ii.py @@ -0,0 +1,46 @@ +# Time: O(n) on average +# Space: O(1) + +from random import randint + +# Quick select solution. +class Solution(object): + def minMoves2(self, nums): + """ + :type nums: List[int] + :rtype: int + """ + def kthElement(nums, k): + def PartitionAroundPivot(left, right, pivot_idx, nums): + pivot_value = nums[pivot_idx] + new_pivot_idx = left + nums[pivot_idx], nums[right] = nums[right], nums[pivot_idx] + for i in xrange(left, right): + if nums[i] > pivot_value: + nums[i], nums[new_pivot_idx] = nums[new_pivot_idx], nums[i] + new_pivot_idx += 1 + + nums[right], nums[new_pivot_idx] = nums[new_pivot_idx], nums[right] + return new_pivot_idx + + left, right = 0, len(nums) - 1 + while left <= right: + pivot_idx = randint(left, right) + new_pivot_idx = PartitionAroundPivot(left, right, pivot_idx, nums) + if new_pivot_idx == k - 1: + return nums[new_pivot_idx] + elif new_pivot_idx > k - 1: + right = new_pivot_idx - 1 + else: # new_pivot_idx < k - 1. + left = new_pivot_idx + 1 + + median = kthElement(nums, len(nums)/2 + 1) + return sum(abs(num - median) for num in nums) + + def minMoves22(self, nums): + """ + :type nums: List[int] + :rtype: int + """ + median = sorted(nums)[len(nums) / 2] + return sum(abs(num - median) for num in nums) diff --git a/Python/minimum-moves-to-equal-array-elements.py b/Python/minimum-moves-to-equal-array-elements.py new file mode 100644 index 000000000..fd4afdff2 --- /dev/null +++ b/Python/minimum-moves-to-equal-array-elements.py @@ -0,0 +1,27 @@ +# Time: O(n) +# Space: O(1) + +# Given a non-empty integer array of size n, +# find the minimum number of moves required to make all array elements equal, +# where a move is incrementing n - 1 elements by 1. +# +# Example: +# +# Input: +# [1,2,3] +# +# Output: +# 3 +# +# Explanation: +# Only three moves are needed (remember each move increments two elements): +# +# [1,2,3] => [2,3,3] => [3,4,3] => [4,4,4] + +class Solution(object): + def minMoves(self, nums): + """ + :type nums: List[int] + :rtype: int + """ + return sum(nums) - len(nums) * min(nums) diff --git a/Python/minimum-number-of-arrows-to-burst-balloons.py b/Python/minimum-number-of-arrows-to-burst-balloons.py new file mode 100644 index 000000000..04248ea80 --- /dev/null +++ b/Python/minimum-number-of-arrows-to-burst-balloons.py @@ -0,0 +1,48 @@ +# Time: O(nlogn) +# Space: O(1) + +# There are a number of spherical balloons spread in two-dimensional space. +# For each balloon, provided input is the start and end coordinates of the horizontal diameter. +# Since it's horizontal, y-coordinates don't matter and hence the x-coordinates of start and +# end of the diameter suffice. Start is always smaller than end. There will be at most 104 balloons. +# +# An arrow can be shot up exactly vertically from different points along the x-axis. +# A balloon with xstart and xend bursts by an arrow shot at x if xstart <= x <= xend. +# There is no limit to the number of arrows that can be shot. +# An arrow once shot keeps travelling up infinitely. +# The problem is to find the minimum number of arrows that must be shot to burst all balloons. +# +# Example: +# +# Input: +# [[10,16], [2,8], [1,6], [7,12]] +# +# Output: +# 2 +# +# Explanation: +# One way is to shoot one arrow for example at x = 6 (bursting the balloons [2,8] and [1,6]) +# and another arrow at x = 11 (bursting the other two balloons). + +class Solution(object): + def findMinArrowShots(self, points): + """ + :type points: List[List[int]] + :rtype: int + """ + if not points: + return 0 + + points.sort() + + result = 0 + i = 0 + while i < len(points): + j = i + 1 + right_bound = points[i][1] + while j < len(points) and points[j][0] <= right_bound: + right_bound = min(right_bound, points[j][1]) + j += 1 + result += 1 + i = j + return result diff --git a/Python/minimum-number-of-refueling-stops.py b/Python/minimum-number-of-refueling-stops.py new file mode 100644 index 000000000..f39d48883 --- /dev/null +++ b/Python/minimum-number-of-refueling-stops.py @@ -0,0 +1,76 @@ +# Time: O(nlogn) +# Space: O(n) + +# A car travels from a starting position to a destination +# which is target miles east of the starting position. +# +# Along the way, there are gas stations. +# Each station[i] represents a gas station that is station[i][0] miles +# east of the starting position, and has station[i][1] liters of gas. +# +# The car starts with an infinite tank of gas, which initially has startFuel +# liters of fuel in it. It uses 1 liter of gas per 1 mile that it drives. +# +# When the car reaches a gas station, it may stop and refuel, +# transferring all the gas from the station into the car. +# +# What is the least number of refueling stops the car must make +# in order to reach its destination? If it cannot reach the destination, return -1. +# +# Note that if the car reaches a gas station with 0 fuel left, +# the car can still refuel there. +# If the car reaches the destination with 0 fuel left, +# it is still considered to have arrived. +# +# Example 1: +# +# Input: target = 1, startFuel = 1, stations = [] +# Output: 0 +# Explanation: We can reach the target without refueling. +# Example 2: +# +# Input: target = 100, startFuel = 1, stations = [[10,100]] +# Output: -1 +# Explanation: We can't reach the target (or even the first gas station). +# Example 3: +# +# Input: target = 100, startFuel = 10, stations = [[10,60],[20,30],[30,30],[60,40]] +# Output: 2 +# Explanation: +# We start with 10 liters of fuel. +# We drive to position 10, expending 10 liters of fuel. We refuel from 0 liters to 60 liters of gas. +# Then, we drive from position 10 to position 60 (expending 50 liters of fuel), +# and refuel from 10 liters to 50 liters of gas. We then drive to and reach the target. +# We made 2 refueling stops along the way, so we return 2. +# +# Note: +# - 1 <= target, startFuel, stations[i][1] <= 10^9 +# - 0 <= stations.length <= 500 +# - 0 < stations[0][0] < stations[1][0] < ... < stations[stations.length-1][0] < target + +import heapq + + +class Solution(object): + def minRefuelStops(self, target, startFuel, stations): + """ + :type target: int + :type startFuel: int + :type stations: List[List[int]] + :rtype: int + """ + max_heap = [] + stations.append((target, float("inf"))) + + result = prev = 0 + for location, capacity in stations: + startFuel -= location - prev + while max_heap and startFuel < 0: + startFuel += -heapq.heappop(max_heap) + result += 1 + if startFuel < 0: + return -1 + heapq.heappush(max_heap, -capacity) + prev = location + + return result diff --git a/Python/minimum-path-sum.py b/Python/minimum-path-sum.py index f6c4edfd0..cedfa2733 100644 --- a/Python/minimum-path-sum.py +++ b/Python/minimum-path-sum.py @@ -1,9 +1,10 @@ +from __future__ import print_function # Time: O(m * n) # Space: O(m + n) # -# Given a m x n grid filled with non-negative numbers, +# Given a m x n grid filled with non-negative numbers, # find a path from top left to bottom right which minimizes the sum of all numbers along its path. -# +# # Note: You can only move either down or right at any point in time. # @@ -19,12 +20,12 @@ def minPathSum(self, grid): sum[0] += grid[i][0] for j in xrange(1, len(grid[0])): sum[j] = min(sum[j - 1], sum[j]) + grid[i][j] - + return sum[-1] - + if __name__ == "__main__": - print Solution().minPathSum([[0,1] - ,[1,0]]) - print Solution().minPathSum([[1,3,1] + print(Solution().minPathSum([[0,1] + ,[1,0]])) + print(Solution().minPathSum([[1,3,1] ,[1,5,1] - ,[4,2,1]]) \ No newline at end of file + ,[4,2,1]])) \ No newline at end of file diff --git a/Python/minimum-size-subarray-sum.py b/Python/minimum-size-subarray-sum.py new file mode 100644 index 000000000..e9ba17541 --- /dev/null +++ b/Python/minimum-size-subarray-sum.py @@ -0,0 +1,60 @@ +# Time: O(n) +# Space: O(1) +# +# Given an array of n positive integers and a positive integer s, +# find the minimal length of a subarray of which the sum ≥ s. If there isn't one, return 0 instead. +# +# For example, given the array [2,3,1,2,4,3] and s = 7, +# the subarray [4,3] has the minimal length under the problem constraint. +# +# More practice: +# If you have figured out the O(n) solution, try coding another solution of which the time complexity is O(n log n). +# + +# Sliding window solution. +class Solution: + # @param {integer} s + # @param {integer[]} nums + # @return {integer} + def minSubArrayLen(self, s, nums): + start = 0 + sum = 0 + min_size = float("inf") + for i in xrange(len(nums)): + sum += nums[i] + while sum >= s: + min_size = min(min_size, i - start + 1) + sum -= nums[start] + start += 1 + + return min_size if min_size != float("inf") else 0 + +# Time: O(nlogn) +# Space: O(n) +# Binary search solution. +class Solution2: + # @param {integer} s + # @param {integer[]} nums + # @return {integer} + def minSubArrayLen(self, s, nums): + min_size = float("inf") + sum_from_start = [n for n in nums] + for i in xrange(len(sum_from_start) - 1): + sum_from_start[i + 1] += sum_from_start[i] + for i in xrange(len(sum_from_start)): + end = self.binarySearch(lambda x, y: x <= y, sum_from_start, \ + i, len(sum_from_start), \ + sum_from_start[i] - nums[i] + s) + if end < len(sum_from_start): + min_size = min(min_size, end - i + 1) + + return min_size if min_size != float("inf") else 0 + + def binarySearch(self, compare, A, start, end, target): + while start < end: + mid = start + (end - start) / 2 + if compare(target, A[mid]): + end = mid + else: + start = mid + 1 + return start diff --git a/Python/minimum-swaps-to-make-sequences-increasing.py b/Python/minimum-swaps-to-make-sequences-increasing.py new file mode 100644 index 000000000..4ba4acc85 --- /dev/null +++ b/Python/minimum-swaps-to-make-sequences-increasing.py @@ -0,0 +1,43 @@ +# Time: O(n) +# Space: O(1) + +# We have two integer sequences A and B of the same non-zero length. +# +# We are allowed to swap elements A[i] and B[i]. +# Note that both elements are in the same index position in their respective sequences. +# +# At the end of some number of swaps, A and B are both strictly increasing. +# (A sequence is strictly increasing if and only if A[0] < A[1] < A[2] < ... < A[A.length - 1].) +# +# Given A and B, return the minimum number of swaps to make both sequences strictly increasing. +# It is guaranteed that the given input always makes it possible. +# +# Example: +# Input: A = [1,3,5,4], B = [1,2,3,7] +# Output: 1 +# Explanation: +# Swap A[3] and B[3]. Then the sequences are: +# A = [1, 3, 5, 7] and B = [1, 2, 3, 4] +# which are both strictly increasing. +# +# Note: +# - A, B are arrays with the same length, and that length will be in the range [1, 1000]. +# - A[i], B[i] are integer values in the range [0, 2000]. + +class Solution(object): + def minSwap(self, A, B): + """ + :type A: List[int] + :type B: List[int] + :rtype: int + """ + dp_no_swap, dp_swap = [0]*2, [1]*2 + for i in xrange(1, len(A)): + dp_no_swap[i%2], dp_swap[i%2] = float("inf"), float("inf") + if A[i-1] < A[i] and B[i-1] < B[i]: + dp_no_swap[i%2] = min(dp_no_swap[i%2], dp_no_swap[(i-1)%2]) + dp_swap[i%2] = min(dp_swap[i%2], dp_swap[(i-1)%2]+1) + if A[i-1] < B[i] and B[i-1] < A[i]: + dp_no_swap[i%2] = min(dp_no_swap[i%2], dp_swap[(i-1)%2]) + dp_swap[i%2] = min(dp_swap[i%2], dp_no_swap[(i-1)%2]+1) + return min(dp_no_swap[(len(A)-1)%2], dp_swap[(len(A)-1)%2]) diff --git a/Python/minimum-time-difference.py b/Python/minimum-time-difference.py new file mode 100644 index 000000000..1366a336a --- /dev/null +++ b/Python/minimum-time-difference.py @@ -0,0 +1,23 @@ +# Time: O(nlogn) +# Space: O(n) + +# Given a list of 24-hour clock time points in "Hour:Minutes" format, +# find the minimum minutes difference between any two time points in the list. +# +# Example 1: +# Input: ["23:59","00:00"] +# Output: 1 +# Note: +# The number of time points in the given list is at least 2 and won't exceed 20000. +# The input time is legal and ranges from 00:00 to 23:59. + +class Solution(object): + def findMinDifference(self, timePoints): + """ + :type timePoints: List[str] + :rtype: int + """ + minutes = map(lambda x: int(x[:2]) * 60 + int(x[3:]), timePoints) + minutes.sort() + return min((y - x) % (24 * 60) \ + for x, y in zip(minutes, minutes[1:] + minutes[:1])) diff --git a/Python/minimum-unique-word-abbreviation.py b/Python/minimum-unique-word-abbreviation.py new file mode 100644 index 000000000..3a3f3324b --- /dev/null +++ b/Python/minimum-unique-word-abbreviation.py @@ -0,0 +1,40 @@ +# Time: O(2^n) +# Space: O(n) + +class Solution(object): + def minAbbreviation(self, target, dictionary): + """ + :type target: str + :type dictionary: List[str] + :rtype: str + """ + def bits_len(target, bits): + return sum(((bits >> i) & 3) == 0 for i in xrange(len(target)-1)) + + diffs = [] + for word in dictionary: + if len(word) != len(target): + continue + diffs.append(sum(2**i for i, c in enumerate(word) if target[i] != c)) + + if not diffs: + return str(len(target)) + + bits = 2**len(target) - 1 + for i in xrange(2**len(target)): + if all(d & i for d in diffs) and bits_len(target, i) > bits_len(target, bits): + bits = i + + abbr = [] + pre = 0 + for i in xrange(len(target)): + if bits & 1: + if i - pre > 0: + abbr.append(str(i - pre)) + pre = i + 1 + abbr.append(str(target[i])) + elif i == len(target) - 1: + abbr.append(str(i - pre + 1)) + bits >>= 1 + + return "".join(abbr) diff --git a/Python/minimum-window-subsequence.py b/Python/minimum-window-subsequence.py new file mode 100644 index 000000000..3fb65630c --- /dev/null +++ b/Python/minimum-window-subsequence.py @@ -0,0 +1,29 @@ +# Time: O(s * t) +# Space: O(s) + +class Solution(object): + def minWindow(self, S, T): + """ + :type S: str + :type T: str + :rtype: str + """ + dp = [[None for _ in xrange(len(S))] for _ in xrange(2)] + for j, c in enumerate(S): + if c == T[0]: + dp[0][j] = j + + for i in xrange(1, len(T)): + prev = None + dp[i%2] = [None] * len(S) + for j, c in enumerate(S): + if prev is not None and c == T[i]: + dp[i%2][j] = prev + if dp[(i-1)%2][j] is not None: + prev = dp[(i-1)%2][j] + + start, end = 0, len(S) + for j, i in enumerate(dp[(len(T)-1)%2]): + if i >= 0 and j-i < end-start: + start, end = i, j + return S[start:end+1] if end < len(S) else "" diff --git a/Python/minimum-window-substring.py b/Python/minimum-window-substring.py index 37a001902..3936f8c74 100644 --- a/Python/minimum-window-substring.py +++ b/Python/minimum-window-substring.py @@ -1,49 +1,57 @@ +from __future__ import print_function # Time: O(n) -# Space: O(1) +# Space: O(k), k is the number of different characters + +# Given a string S and a string T, find the minimum window in S which +# will contain all the characters in T in complexity O(n). # -# Given a string S and a string T, find the minimum window in S which will contain all the characters in T in complexity O(n). -# # For example, # S = "ADOBECODEBANC" # T = "ABC" # Minimum window is "BANC". -# +# # Note: -# If there is no such window in S that covers all characters in T, return the emtpy string "". -# -# If there are multiple such windows, you are guaranteed that there will always be only one unique minimum window in S. +# If there is no such window in S that covers all characters in T, +# return the emtpy string "". # +# If there are multiple such windows, you are guaranteed that +# there will always be only one unique minimum window in S. -class Solution: - # @return a string - def minWindow(self, S, T): +class Solution(object): + def minWindow(self, s, t): + """ + :type s: str + :type t: str + :rtype: str + """ current_count = [0 for i in xrange(52)] expected_count = [0 for i in xrange(52)] - - for char in T: + + for char in t: expected_count[ord(char) - ord('a')] += 1 - + i, count, start, min_width, min_start = 0, 0, 0, float("inf"), 0 - while i < len(S): - current_count[ord(S[i]) - ord('a')] += 1 - if current_count[ord(S[i]) - ord('a')] <= expected_count[ord(S[i]) - ord('a')]: + while i < len(s): + current_count[ord(s[i]) - ord('a')] += 1 + if current_count[ord(s[i]) - ord('a')] <= expected_count[ord(s[i]) - ord('a')]: count += 1 - - if count == len(T): - while expected_count[ord(S[start]) - ord('a')] == 0 or\ - current_count[ord(S[start]) - ord('a')] > expected_count[ord(S[start]) - ord('a')]: - current_count[ord(S[start]) - ord('a')] -= 1 + + if count == len(t): + while expected_count[ord(s[start]) - ord('a')] == 0 or \ + current_count[ord(s[start]) - ord('a')] > expected_count[ord(s[start]) - ord('a')]: + current_count[ord(s[start]) - ord('a')] -= 1 start += 1 - + if min_width > i - start + 1: min_width = i - start + 1 min_start = start i += 1 - + if min_width == float("inf"): return "" - - return S[min_start:min_start + min_width] + + return s[min_start:min_start + min_width] + if __name__ == "__main__": - print Solution().minWindow("ADOBECODEBANC", "ABC") + print(Solution().minWindow("ADOBECODEBANC", "ABC")) diff --git a/Python/mirror-reflection.py b/Python/mirror-reflection.py new file mode 100644 index 000000000..a7de7a1aa --- /dev/null +++ b/Python/mirror-reflection.py @@ -0,0 +1,59 @@ +# Time: O(1) +# Space: O(1) + +# There is a special square room with mirrors on each of the four walls. +# Except for the southwest corner, there are receptors on each of +# the remaining corners, +# numbered 0, 1, and 2. +# +# The square room has walls of length p, +# and a laser ray from the southwest corner first meets +# the east wall at a distance q from the 0th receptor. +# +# Return the number of the receptor that the ray meets first. +# (It is guaranteed that the ray will meet a receptor eventually.) +# +# Example 1: +# +# Input: p = 2, q = 1 +# Output: 2 +# Explanation: The ray meets receptor 2 the first time it gets reflected back +# to the left wall. +# +# Note: +# - 1 <= p <= 1000 +# - 0 <= q <= p + + +class Solution(object): + def mirrorReflection(self, p, q): + """ + :type p: int + :type q: int + :rtype: int + """ + # explanation commented in the following solution + return 2 if (p & -p) > (q & -q) else 0 if (p & -p) < (q & -q) else 1 + + +# Time: O(log(max(p, q))) = O(1) due to 32-bit integer +# Space: O(1) +class Solution2(object): + def mirrorReflection(self, p, q): + """ + :type p: int + :type q: int + :rtype: int + """ + def gcd(a, b): + while b: + a, b = b, a % b + return a + + lcm = p*q // gcd(p, q) + # let a = lcm / p, b = lcm / q + if lcm // p % 2 == 1: + if lcm // q % 2 == 1: + return 1 # a is odd, b is odd <=> (p & -p) == (q & -q) + return 2 # a is odd, b is even <=> (p & -p) > (q & -q) + return 0 # a is even, b is odd <=> (p & -p) < (q & -q) diff --git a/Python/missing-number.py b/Python/missing-number.py new file mode 100644 index 000000000..b2b3426f0 --- /dev/null +++ b/Python/missing-number.py @@ -0,0 +1,30 @@ +# Time: O(n) +# Space: O(1) +# +# Given an array containing n distinct numbers taken from +# 0, 1, 2, ..., n, find the one that is missing from the array. +# +# For example, +# Given nums = [0, 1, 3] return 2. +# +# Note: +# Your algorithm should run in linear runtime complexity. +# Could you implement it using only constant extra space complexity? +# + +import operator + + +class Solution(object): + def missingNumber(self, nums): + """ + :type nums: List[int] + :rtype: int + """ + return reduce(operator.xor, nums, \ + reduce(operator.xor, xrange(len(nums) + 1))) + + +class Solution2(object): + def missingNumber(self, nums): + return sum(xrange(len(nums)+1)) - sum(nums) diff --git a/Python/missing-ranges.py b/Python/missing-ranges.py index a4d0d1378..3af0ae2c5 100644 --- a/Python/missing-ranges.py +++ b/Python/missing-ranges.py @@ -1,40 +1,42 @@ +from __future__ import print_function # Time: O(n) # Space: O(1) # # Given a sorted integer array where the range of elements are [lower, upper] inclusive, # return its missing ranges. -# -# For example, given [0, 1, 3, 50, 75], lower = 0 and upper = 99, +# +# For example, given [0, 1, 3, 50, 75], lower = 0 and upper = 99, # return ["2", "4->49", "51->74", "76->99"]. # -class Solution: - # @param A, a list of integers - # @param lower, an integer - # @param upper, an integer - # @return a list of strings - def findMissingRanges(self, A, lower, upper): +class Solution(object): + def findMissingRanges(self, nums, lower, upper): + """ + :type nums: List[int] + :type lower: int + :type upper: int + :rtype: List[str] + """ + def getRange(lower, upper): + if lower == upper: + return "{}".format(lower) + else: + return "{}->{}".format(lower, upper) ranges = [] pre = lower - 1 - - for i in xrange(len(A) + 1): - if i == len(A): + + for i in xrange(len(nums) + 1): + if i == len(nums): cur = upper + 1 else: - cur = A[i] - + cur = nums[i] if cur - pre >= 2: - ranges.append(self.getRange(pre + 1, cur - 1)) - + ranges.append(getRange(pre + 1, cur - 1)) + pre = cur - + return ranges - - def getRange(self, lower, upper): - if lower == upper: - return "{}".format(lower) - else: - return "{}->{}".format(lower, upper) - + + if __name__ == "__main__": - print Solution().findMissingRanges([0, 1, 3, 50, 75], 0, 99) \ No newline at end of file + print(Solution().findMissingRanges([0, 1, 3, 50, 75], 0, 99)) diff --git a/Python/monotone-increasing-digits.py b/Python/monotone-increasing-digits.py new file mode 100644 index 000000000..b7b1e508f --- /dev/null +++ b/Python/monotone-increasing-digits.py @@ -0,0 +1,38 @@ +# Time: O(logn) = O(1) +# Space: O(logn) = O(1) + +# Given a non-negative integer N, +# find the largest number that is less than or equal to N with monotone increasing digits. +# +# (Recall that an integer has monotone increasing digits if and only +# if each pair of adjacent digits x and y satisfy x <= y.) +# +# Example 1: +# Input: N = 10 +# Output: 9 +# +# Example 2: +# Input: N = 1234 +# Output: 1234 +# +# Example 3: +# Input: N = 332 +# Output: 299 +# +# Note: N is an integer in the range [0, 10^9]. + +class Solution(object): + def monotoneIncreasingDigits(self, N): + """ + :type N: int + :rtype: int + """ + nums = map(int, list(str(N))) + leftmost_inverted_idx = len(nums) + for i in reversed(xrange(1, len(nums))): + if nums[i-1] > nums[i]: + leftmost_inverted_idx = i + nums[i-1] -= 1 + for i in xrange(leftmost_inverted_idx, len(nums)): + nums[i] = 9 + return int("".join(map(str, nums))) diff --git a/Python/most-common-word.py b/Python/most-common-word.py new file mode 100644 index 000000000..6d6fc26c3 --- /dev/null +++ b/Python/most-common-word.py @@ -0,0 +1,60 @@ +# Time: O(m + n), m is the size of banned, n is the size of paragraph +# Space: O(m + n) + +# Given a paragraph and a list of banned words, +# return the most frequent word that is not in the list of banned words. +# It is guaranteed there is at least one word that isn't banned, and that the answer is unique. +# +# Words in the list of banned words are given in lowercase, and free of +# punctuation. +# Words in the paragraph are not case sensitive. +# The answer is in lowercase. +# +# Example: +# Input: +# paragraph = "Bob hit a ball, the hit BALL flew far after it was hit." +# banned = ["hit"] +# Output: "ball" +# Explanation: +# "hit" occurs 3 times, but it is a banned word. +# "ball" occurs twice (and no other word does), so it is the most frequent +# non-banned word in the paragraph. +# Note that words in the paragraph are not case sensitive, +# that punctuation is ignored (even if adjacent to words, such as "ball,"), +# and that "hit" isn't the answer even though it occurs more because it is +# banned. +# +# Note: +# - 1 <= paragraph.length <= 1000. +# - 1 <= banned.length <= 100. +# - 1 <= banned[i].length <= 10. +# - The answer is unique, and written in lowercase +# (even if its occurrences in paragraph may have uppercase symbols, +# and even if it is a proper noun.) +# - paragraph only consists of letters, spaces, +# or the punctuation symbols !?',;. +# - Different words in paragraph are always separated by a space. +# - There are no hyphens or hyphenated words. +# - Words only consist of letters, never apostrophes or +# other punctuation symbols. + +import collections + + +class Solution(object): + def mostCommonWord(self, paragraph, banned): + """ + :type paragraph: str + :type banned: List[str] + :rtype: str + """ + lookup = set(banned) + counts = collections.Counter(word.strip("!?',;.") + for word in paragraph.lower().split()) + + result = '' + for word in counts: + if (not result or counts[word] > counts[result]) and \ + word not in lookup: + result = word + return result diff --git a/Python/most-frequent-subtree-sum.py b/Python/most-frequent-subtree-sum.py new file mode 100644 index 000000000..202eb6baa --- /dev/null +++ b/Python/most-frequent-subtree-sum.py @@ -0,0 +1,54 @@ +# Time: O(n) +# Space: O(n) + +# Given the root of a tree, you are asked to find the most frequent subtree sum. +# The subtree sum of a node is defined as the sum of all the node values formed by +# the subtree rooted at that node (including the node itself). +# So what is the most frequent subtree sum value? If there is a tie, +# return all the values with the highest frequency in any order. +# +# Examples 1 +# Input: +# +# 5 +# / \ +# 2 -3 +# return [2, -3, 4], since all the values happen only once, return all of them in any order. +# Examples 2 +# Input: +# +# 5 +# / \ +# 2 -5 +# return [2], since 2 happens twice, however -5 only occur once. +# Note: You may assume the sum of values in any subtree is in the range of 32-bit signed integer. + +# Definition for a binary tree node. +# class TreeNode(object): +# def __init__(self, x): +# self.val = x +# self.left = None +# self.right = None + +import collections + + +class Solution(object): + def findFrequentTreeSum(self, root): + """ + :type root: TreeNode + :rtype: List[int] + """ + def countSubtreeSumHelper(root, counts): + if not root: + return 0 + total = root.val + \ + countSubtreeSumHelper(root.left, counts) + \ + countSubtreeSumHelper(root.right, counts) + counts[total] += 1 + return total + + counts = collections.defaultdict(int) + countSubtreeSumHelper(root, counts) + max_count = max(counts.values()) if counts else 0 + return [total for total, count in counts.iteritems() if count == max_count] diff --git a/Python/most-profit-assigning-work.py b/Python/most-profit-assigning-work.py new file mode 100644 index 000000000..d81c72f0c --- /dev/null +++ b/Python/most-profit-assigning-work.py @@ -0,0 +1,52 @@ +# Time: O(mlogm + nlogn), m is the number of workers, +# , n is the number of jobs +# Space: O(n) + +# We have jobs: difficulty[i] is the difficulty of the ith job, +# and profit[i] is the profit of the ith job. +# +# Now we have some workers. worker[i] is the ability of the ith worker, +# which means that this worker can only complete a job with difficulty +# at most worker[i]. +# +# Every worker can be assigned at most one job, but one job can be completed +# multiple times. +# +# For example, if 3 people attempt the same job that pays $1, then the total +# profit will be $3. +# If a worker cannot complete any job, his profit is $0. +# +# What is the most profit we can make? +# +# Example 1: +# +# Input: difficulty = [2,4,6,8,10], profit = [10,20,30,40,50], +# worker = [4,5,6,7] +# Output: 100 +# Explanation: Workers are assigned jobs of difficulty [4,4,6,6] and +# they get profit of [20,20,30,30] seperately. +# +# Notes: +# - 1 <= difficulty.length = profit.length <= 10000 +# - 1 <= worker.length <= 10000 +# - difficulty[i], profit[i], worker[i] are in range [1, 10^5] + + +class Solution(object): + def maxProfitAssignment(self, difficulty, profit, worker): + """ + :type difficulty: List[int] + :type profit: List[int] + :type worker: List[int] + :rtype: int + """ + jobs = zip(difficulty, profit) + jobs.sort() + worker.sort() + result, i, max_profit = 0, 0, 0 + for ability in worker: + while i < len(jobs) and jobs[i][0] <= ability: + max_profit = max(max_profit, jobs[i][1]) + i += 1 + result += max_profit + return result diff --git a/Python/move-zeroes.py b/Python/move-zeroes.py new file mode 100644 index 000000000..26caf64dd --- /dev/null +++ b/Python/move-zeroes.py @@ -0,0 +1,56 @@ +from __future__ import print_function +# Time: O(n) +# Space: O(1) + +# Given an array nums, write a function to move all 0's +# to the end of it while maintaining the relative order +# of the non-zero elements. +# +# For example, given nums = [0, 1, 0, 3, 12], after +# calling your function, nums should be [1, 3, 12, 0, 0]. +# +# Note: +# You must do this in-place without making a copy of the array. +# Minimize the total number of operations. + + +class Solution(object): + def moveZeroes(self, nums): + """ + :type nums: List[int] + :rtype: void Do not return anything, modify nums in-place instead. + """ + pos = 0 + for i in xrange(len(nums)): + if nums[i]: + nums[i], nums[pos] = nums[pos], nums[i] + pos += 1 + + def moveZeroes2(self, nums): + """ + :type nums: List[int] + :rtype: void Do not return anything, modify nums in-place instead. + """ + nums.sort(cmp=lambda a, b: 0 if b else -1) + + +class Solution2(object): + def moveZeroes(self, nums): + """ + :type nums: List[int] + :rtype: void Do not return anything, modify nums in-place instead. + """ + pos = 0 + for i in xrange(len(nums)): + if nums[i]: + nums[pos] = nums[i] + pos += 1 + + for i in xrange(pos, len(nums)): + nums[i] = 0 + + +if __name__ == '__main__': + s = Solution() + r = s.moveZeroes([0, 1, 0, 3, 12]) + print(r) diff --git a/Python/moving-average-from-data-stream.py b/Python/moving-average-from-data-stream.py new file mode 100644 index 000000000..d61bdaded --- /dev/null +++ b/Python/moving-average-from-data-stream.py @@ -0,0 +1,31 @@ +# Time: O(1) +# Space: O(w) + +from collections import deque + +class MovingAverage(object): + + def __init__(self, size): + """ + Initialize your data structure here. + :type size: int + """ + self.__size = size + self.__sum = 0 + self.__q = deque([]) + + def next(self, val): + """ + :type val: int + :rtype: float + """ + if len(self.__q) == self.__size: + self.__sum -= self.__q.popleft() + self.__sum += val + self.__q.append(val) + return 1.0 * self.__sum / len(self.__q) + + +# Your MovingAverage object will be instantiated and called as such: +# obj = MovingAverage(size) +# param_1 = obj.next(val) diff --git a/Python/multiply-strings.py b/Python/multiply-strings.py index e5af568af..fd4fdeb90 100644 --- a/Python/multiply-strings.py +++ b/Python/multiply-strings.py @@ -1,32 +1,46 @@ +from __future__ import print_function # Time: O(m * n) # Space: O(m + n) # # Given two numbers represented as strings, return multiplication of the numbers as a string. -# +# # Note: The numbers can be arbitrarily large and are non-negative. # -class Solution: - # @param num1, a string - # @param num2, a string - # @return a string +class Solution(object): def multiply(self, num1, num2): + """ + :type num1: str + :type num2: str + :rtype: str + """ num1, num2 = num1[::-1], num2[::-1] - result = [0 for i in xrange(len(num1) + len(num2))] + res = [0] * (len(num1) + len(num2)) for i in xrange(len(num1)): for j in xrange(len(num2)): - result[i + j] += int(num1[i]) * int(num2[j]) - - carry, num3 = 0, [] - for digit in result: - sum = carry + digit - carry = sum / 10 - num3.insert(0, str(sum % 10)) - - while len(num3) > 1 and num3[0] == "0": - del num3[0] - - return ''.join(num3) + res[i + j] += int(num1[i]) * int(num2[j]) + res[i + j + 1] += res[i + j] / 10 + res[i + j] %= 10 + + # Skip leading 0s. + i = len(res) - 1 + while i > 0 and res[i] == 0: + i -= 1 + + return ''.join(map(str, res[i::-1])) + +# Time: O(m * n) +# Space: O(m + n) +# Using built-in bignum solution. +class Solution2(object): + def multiply(self, num1, num2): + """ + :type num1: str + :type num2: str + :rtype: str + """ + return str(int(num1) * int(num2)) + if __name__ == "__main__": - print Solution().multiply("123", "1000") \ No newline at end of file + print(Solution().multiply("123", "1000")) diff --git a/Python/my-calendar-i.py b/Python/my-calendar-i.py new file mode 100644 index 000000000..050d01f9b --- /dev/null +++ b/Python/my-calendar-i.py @@ -0,0 +1,95 @@ +# Time: O(nlogn) on average, O(n^2) on worst case +# Space: O(n) + +# Implement a MyCalendar class to store your events. +# A new event can be added if adding the event will not cause a double booking. +# +# Your class will have the method, book(int start, int end). +# Formally, this represents a booking on the half open interval [start, end), +# the range of real numbers x such that start <= x < end. +# +# A double booking happens when two events have some non-empty intersection +# (ie., there is some time that is common to both events.) +# +# For each call to the method MyCalendar.book, +# return true if the event can be added to the calendar successfully without causing a double booking. +# Otherwise, return false and do not add the event to the calendar. +# +# Your class will be called like this: MyCalendar cal = new MyCalendar(); MyCalendar.book(start, end) +# Example 1: +# MyCalendar(); +# MyCalendar.book(10, 20); // returns true +# MyCalendar.book(15, 25); // returns false +# MyCalendar.book(20, 30); // returns true +# Explanation: +# The first event can be booked. The second can't because time 15 is already booked by another event. +# The third event can be booked, as the first event takes every time less than 20, but not including 20. +# +# Note: +# - The number of calls to MyCalendar.book per test case will be at most 1000. +# - In calls to MyCalendar.book(start, end), start and end are integers in the range [0, 10^9]. + +class Node: + def __init__(self, start, end): + self.__start = start + self.__end = end + self.__left = None + self.__right = None + + + def insert(self, node): + if node.__start >= self.__end: + if not self.__right: + self.__right = node + return True + return self.__right.insert(node) + elif node.__end <= self.__start: + if not self.__left: + self.__left = node + return True + return self.__left.insert(node) + else: + return False + + +class MyCalendar(object): + def __init__(self): + self.__root = None + + + def book(self, start, end): + """ + :type start: int + :type end: int + :rtype: bool + """ + if self.__root is None: + self.__root = Node(start, end) + return True + return self.root.insert(Node(start, end)) + + +# Time: O(n^2) +# Space: O(n) +class MyCalendar2(object): + + def __init__(self): + self.__calendar = [] + + + def book(self, start, end): + """ + :type start: int + :type end: int + :rtype: bool + """ + for i, j in self.__calendar: + if start < j and end > i: + return False + self.__calendar.append((start, end)) + return True + + +# Your MyCalendar object will be instantiated and called as such: +# obj = MyCalendar() +# param_1 = obj.book(start,end) diff --git a/Python/my-calendar-ii.py b/Python/my-calendar-ii.py new file mode 100644 index 000000000..104ccf4b5 --- /dev/null +++ b/Python/my-calendar-ii.py @@ -0,0 +1,64 @@ +# Time: O(n^2) +# Space: O(n) + +# Implement a MyCalendarTwo class to store your events. +# A new event can be added if adding the event will not cause a triple booking. +# +# Your class will have one method, book(int start, int end). +# Formally, this represents a booking on the half open interval [start, end), +# the range of real numbers x such that start <= x < end. +# +# A triple booking happens when three events have some non-empty intersection +# (ie., there is some time that is common to all 3 events.) +# +# For each call to the method MyCalendar.book, +# return true if the event can be added to the calendar successfully without causing a triple booking. +# Otherwise, return false and do not add the event to the calendar. +# +# Your class will be called like this: MyCalendar cal = new MyCalendar(); MyCalendar.book(start, end) +# Example 1: +# MyCalendar(); +# MyCalendar.book(10, 20); // returns true +# MyCalendar.book(50, 60); // returns true +# MyCalendar.book(10, 40); // returns true +# MyCalendar.book(5, 15); // returns false +# MyCalendar.book(5, 10); // returns true +# MyCalendar.book(25, 55); // returns true +# +# Explanation: +# The first two events can be booked. The third event can be double booked. +# The fourth event (5, 15) can't be booked, because it would result in a triple booking. +# The fifth event (5, 10) can be booked, as it does not use time 10 which is already double booked. +# The sixth event (25, 55) can be booked, as the time in [25, 40) will be double booked with the third event; +# the time [40, 50) will be single booked, and the time [50, 55) will be double booked with the second event. +# +# Note: +# - The number of calls to MyCalendar.book per test case will be at most 1000. +# - In calls to MyCalendar.book(start, end), start and end are integers in the range [0, 10^9]. + +class MyCalendarTwo(object): + + def __init__(self): + self.__overlaps = [] + self.__calendar = [] + + + def book(self, start, end): + """ + :type start: int + :type end: int + :rtype: bool + """ + for i, j in self.__overlaps: + if start < j and end > i: + return False + for i, j in self.__calendar: + if start < j and end > i: + self.__overlaps.append((max(start, i), min(end, j))) + self.__calendar.append((start, end)) + return True + + +# Your MyCalendarTwo object will be instantiated and called as such: +# obj = MyCalendarTwo() +# param_1 = obj.book(start,end) diff --git a/Python/my-calendar-iii.py b/Python/my-calendar-iii.py new file mode 100644 index 000000000..eacc90de9 --- /dev/null +++ b/Python/my-calendar-iii.py @@ -0,0 +1,72 @@ +# Time: O(n^2) +# Space: O(n) + +# Implement a MyCalendarThree class to store your events. A new event can always be added. +# +# Your class will have one method, book(int start, int end). +# Formally, this represents a booking on the half open interval [start, end), +# the range of real numbers x such that start <= x < end. +# +# A K-booking happens when K events have some non-empty intersection +# (ie., there is some time that is common to all K events.) +# +# For each call to the method MyCalendar.book, +# return an integer K representing the largest integer such that there exists a K-booking in the calendar. +# +# Your class will be called like this: +# MyCalendarThree cal = new MyCalendarThree(); MyCalendarThree.book(start, end) +# Example 1: +# MyCalendarThree(); +# MyCalendarThree.book(10, 20); // returns 1 +# MyCalendarThree.book(50, 60); // returns 1 +# MyCalendarThree.book(10, 40); // returns 2 +# MyCalendarThree.book(5, 15); // returns 3 +# MyCalendarThree.book(5, 10); // returns 3 +# MyCalendarThree.book(25, 55); // returns 3 +# Explanation: +# The first two events can be booked and are disjoint, so the maximum K-booking is a 1-booking. +# The third event [10, 40) intersects the first event, and the maximum K-booking is a 2-booking. +# The remaining events cause the maximum K-booking to be only a 3-booking. +# Note that the last event locally causes a 2-booking, but the answer is still 3 because +# eg. [10, 20), [10, 40), and [5, 15) are still triple booked. +# Note: +# - The number of calls to MyCalendarThree.book per test case will be at most 400. +# - In calls to MyCalendarThree.book(start, end), start and end are integers in the range [0, 10^9]. + +import bisect + + +class MyCalendarThree(object): + + def __init__(self): + self.__books = [] + + + def book(self, start, end): + """ + :type start: int + :type end: int + :rtype: int + """ + i = bisect.bisect_left(self.__books, (start, 1)) + if i < len(self.__books) and self.__books[i][0] == start: + self.__books[i] = (self.__books[i][0], self.__books[i][1]+1) + else: + self.__books.insert(i, (start, 1)) + + j = bisect.bisect_left(self.__books, (end, 1)) + if j < len(self.__books) and self.__books[j][0] == end: + self.__books[j] = (self.__books[j][0], self.__books[j][1]-1) + else: + self.__books.insert(j, (end, -1)) + + result, cnt = 0, 0 + for book in self.__books: + cnt += book[1] + result = max(result, cnt) + return result + + +# Your MyCalendarThree object will be instantiated and called as such: +# obj = MyCalendarThree() +# param_1 = obj.book(start,end) diff --git a/Python/n-ary-tree-level-order-traversal.py b/Python/n-ary-tree-level-order-traversal.py new file mode 100644 index 000000000..1765b3f70 --- /dev/null +++ b/Python/n-ary-tree-level-order-traversal.py @@ -0,0 +1,40 @@ +# Time: O(n) +# Space: O(w) + +# Given an n-ary tree, return the level order traversal of +# its nodes' values. (ie, from left to right, level by level). +# +# For example, given a 3-ary tree: +# +# We should return its level order traversal: +# [ +# [1], +# [3,2,4], +# [5,6] +# ] +# +# Note: +# - The depth of the tree is at most 1000. +# - The total number of nodes is at most 5000. + + +# Definition for a Node. +class Node(object): + def __init__(self, val, children): + self.val = val + self.children = children + + +class Solution(object): + def levelOrder(self, root): + """ + :type root: Node + :rtype: List[List[int]] + """ + if not root: + return [] + result, q = [], [root] + while q: + result.append([node.val for node in q]) + q = [child for node in q for child in node.children if child] + return result diff --git a/Python/n-ary-tree-postorder-traversal.py b/Python/n-ary-tree-postorder-traversal.py new file mode 100644 index 000000000..e972ba569 --- /dev/null +++ b/Python/n-ary-tree-postorder-traversal.py @@ -0,0 +1,49 @@ +# Time: O(n) +# Space: O(h) + +# Given an n-ary tree, return the postorder traversal of its nodes' values. +# For example, given a 3-ary tree: +# Return its postorder traversal as: [5,6,3,2,4,1]. +# Note: Recursive solution is trivial, could you do it iteratively? + +# Definition for a Node. +class Node(object): + def __init__(self, val, children): + self.val = val + self.children = children + + +class Solution(object): + def postorder(self, root): + """ + :type root: Node + :rtype: List[int] + """ + if not root: + return [] + result, stack = [], [root] + while stack: + node = stack.pop() + result.append(node.val) + for child in node.children: + if child: + stack.append(child) + return result[::-1] + + +class Solution2(object): + def postorder(self, root): + """ + :type root: Node + :rtype: List[int] + """ + def dfs(root, result): + for child in root.children: + if child: + dfs(child, result) + result.append(root.val) + + result = [] + if root: + dfs(root, result) + return result diff --git a/Python/n-ary-tree-preorder-traversal.py b/Python/n-ary-tree-preorder-traversal.py new file mode 100644 index 000000000..6ad99cdd7 --- /dev/null +++ b/Python/n-ary-tree-preorder-traversal.py @@ -0,0 +1,51 @@ +# Time: O(n) +# Space: O(h) + +# Given an n-ary tree, return the preorder traversal of its nodes' values. +# +# For example, given a 3-ary tree: +# Return its preorder traversal as: [1,3,5,6,2,4]. +# +# Note: Recursive solution is trivial, could you do it iteratively? + +# Definition for a Node. +class Node(object): + def __init__(self, val, children): + self.val = val + self.children = children + + +class Solution(object): + def preorder(self, root): + """ + :type root: Node + :rtype: List[int] + """ + if not root: + return [] + result, stack = [], [root] + while stack: + node = stack.pop() + result.append(node.val) + for child in reversed(node.children): + if child: + stack.append(child) + return result + + +class Solution2(object): + def preorder(self, root): + """ + :type root: Node + :rtype: List[int] + """ + def dfs(root, result): + result.append(root.val) + for child in root.children: + if child: + dfs(child, result) + + result = [] + if root: + dfs(root, result) + return result diff --git a/Python/n-queens-ii.py b/Python/n-queens-ii.py index 56e4c8f22..0c5c76c14 100644 --- a/Python/n-queens-ii.py +++ b/Python/n-queens-ii.py @@ -1,8 +1,10 @@ +from __future__ import print_function +from functools import reduce # Time: O(n!) # Space: O(n) # # Follow up for N-Queens problem. -# +# # Now, instead outputting board configurations, return the total number of distinct solutions. # @@ -14,7 +16,7 @@ def totalNQueens(self, n): self.main_diag = [False] * (2 * n) self.anti_diag = [False] * (2 * n) return self.totalNQueensRecu([], 0, n) - + def totalNQueensRecu(self, solution, row, n): if row == n: return 1 @@ -31,7 +33,7 @@ class Solution2: # @return an integer def totalNQueens(self, n): return self.totalNQueensRecu([], 0, n) - + def totalNQueensRecu(self, solution, row, n): if row == n: return 1 @@ -42,4 +44,4 @@ def totalNQueensRecu(self, solution, row, n): return result if __name__ == "__main__": - print Solution().totalNQueens(8) + print(Solution().totalNQueens(8)) diff --git a/Python/n-queens.py b/Python/n-queens.py index 30612250a..c510ca966 100644 --- a/Python/n-queens.py +++ b/Python/n-queens.py @@ -1,66 +1,82 @@ +from __future__ import print_function # Time: O(n!) # Space: O(n) -# -# The n-queens puzzle is the problem of placing n queens on + +# The n-queens puzzle is the problem of placing n queens on # an nxn chess board such that no two queens attack each other. -# +# # Given an integer n, return all distinct solutions to the n-queens puzzle. -# -# Each solution contains a distinct board configuration of the n-queens' placement, +# +# Each solution contains a distinct board configuration of the n-queens' placement, # where 'Q' and '.' both indicate a queen and an empty space respectively. -# +# # For example, # There exist two distinct solutions to the 4-queens puzzle: -# +# # [ # [".Q..", // Solution 1 # "...Q", # "Q...", # "..Q."], -# +# # ["..Q.", // Solution 2 # "Q...", # "...Q", # ".Q.."] # ] -# quick solution for checking if it is diagonally legal -class Solution: - # @return an integer +class Solution(object): def solveNQueens(self, n): - self.cols = [False] * n - self.main_diag = [False] * (2 * n) - self.anti_diag = [False] * (2 * n) - self.solutions = [] - self.solveNQueensRecu([], 0, n) - return self.solutions - - - def solveNQueensRecu(self, solution, row, n): - if row == n: - self.solutions.append(map(lambda x: '.' * x + "Q" + '.' * (n - x - 1), solution)) - else: + """ + :type n: int + :rtype: List[List[str]] + """ + def dfs(curr, cols, main_diag, anti_diag, result): + row, n = len(curr), len(cols) + if row == n: + result.append(map(lambda x: '.'*x + "Q" + '.'*(n-x-1), curr)) + return for i in xrange(n): - if not self.cols[i] and not self.main_diag[row + i] and not self.anti_diag[row - i + n]: - self.cols[i] = self.main_diag[row + i] = self.anti_diag[row - i + n] = True - self.solveNQueensRecu(solution + [i], row + 1, n) - self.cols[i] = self.main_diag[row + i] = self.anti_diag[row - i + n] = False + if cols[i] or main_diag[row+i] or anti_diag[row-i+n]: + continue + cols[i] = main_diag[row+i] = anti_diag[row-i+n] = True + curr.append(i) + dfs(curr, cols, main_diag, anti_diag, result) + curr.pop() + cols[i] = main_diag[row+i] = anti_diag[row-i+n] = False + + result = [] + cols, main_diag, anti_diag = [False]*n, [False]*(2*n), [False]*(2*n) + dfs([], cols, main_diag, anti_diag, result) + return result + -# slower solution -class Solution2: - # @return an integer +# For any point (x,y), if we want the new point (p,q) don't share the same row, column, or diagonal. +# then there must have ```p+q != x+y``` and ```p-q!= x-y``` +# the former focus on eliminate 'left bottom right top' diagonal; +# the latter focus on eliminate 'left top right bottom' diagonal + +# - col_per_row: the list of column index per row +# - cur_row:current row we are seraching for valid column +# - xy_diff:the list of x-y +# - xy_sum:the list of x+y +class Solution2(object): def solveNQueens(self, n): - self.solutions = [] - self.solveNQueensRecu([], 0, n) - return self.solutions - - def solveNQueensRecu(self, solution, row, n): - if row == n: - self.solutions.append(map(lambda x: '.' * x + "Q" + '.' * (n - x - 1), solution)) - else: - for i in xrange(n): - if i not in solution and reduce(lambda acc, j: abs(row - j) != abs(i - solution[j]) and acc, xrange(len(solution)), True): - self.solveNQueensRecu(solution + [i], row + 1, n) + """ + :type n: int + :rtype: List[List[str]] + """ + def dfs(col_per_row, xy_diff, xy_sum): + cur_row = len(col_per_row) + if cur_row == n: + ress.append(col_per_row) + for col in range(n): + if col not in col_per_row and cur_row-col not in xy_diff and cur_row+col not in xy_sum: + dfs(col_per_row+[col], xy_diff+[cur_row-col], xy_sum+[cur_row+col]) + ress = [] + dfs([], [], []) + return [['.'*i + 'Q' + '.'*(n-i-1) for i in res] for res in ress] + if __name__ == "__main__": - print Solution().solveNQueens(8) + print(Solution().solveNQueens(8)) diff --git a/Python/nested-list-weight-sum-ii.py b/Python/nested-list-weight-sum-ii.py new file mode 100644 index 000000000..8eb003d4f --- /dev/null +++ b/Python/nested-list-weight-sum-ii.py @@ -0,0 +1,51 @@ +# Time: O(n) +# Space: O(h) + +# """ +# This is the interface that allows for creating nested lists. +# You should not implement it, or speculate about its implementation +# """ +#class NestedInteger(object): +# def isInteger(self): +# """ +# @return True if this NestedInteger holds a single integer, rather than a nested list. +# :rtype bool +# """ +# +# def getInteger(self): +# """ +# @return the single integer that this NestedInteger holds, if it holds a single integer +# Return None if this NestedInteger holds a nested list +# :rtype int +# """ +# +# def getList(self): +# """ +# @return the nested list that this NestedInteger holds, if it holds a nested list +# Return None if this NestedInteger holds a single integer +# :rtype List[NestedInteger] +# """ + +class Solution(object): + def depthSumInverse(self, nestedList): + """ + :type nestedList: List[NestedInteger] + :rtype: int + """ + def depthSumInverseHelper(list, depth, result): + if len(result) < depth + 1: + result.append(0) + if list.isInteger(): + result[depth] += list.getInteger() + else: + for l in list.getList(): + depthSumInverseHelper(l, depth + 1, result) + + result = [] + for list in nestedList: + depthSumInverseHelper(list, 0, result) + + sum = 0 + for i in reversed(xrange(len(result))): + sum += result[i] * (len(result) - i) + return sum diff --git a/Python/nested-list-weight-sum.py b/Python/nested-list-weight-sum.py new file mode 100644 index 000000000..f014799e1 --- /dev/null +++ b/Python/nested-list-weight-sum.py @@ -0,0 +1,43 @@ +# Time: O(n) +# Space: O(h) + +# """ +# This is the interface that allows for creating nested lists. +# You should not implement it, or speculate about its implementation +# """ +#class NestedInteger(object): +# def isInteger(self): +# """ +# @return True if this NestedInteger holds a single integer, rather than a nested list. +# :rtype bool +# """ +# +# def getInteger(self): +# """ +# @return the single integer that this NestedInteger holds, if it holds a single integer +# Return None if this NestedInteger holds a nested list +# :rtype int +# """ +# +# def getList(self): +# """ +# @return the nested list that this NestedInteger holds, if it holds a nested list +# Return None if this NestedInteger holds a single integer +# :rtype List[NestedInteger] +# """ + +class Solution(object): + def depthSum(self, nestedList): + """ + :type nestedList: List[NestedInteger] + :rtype: int + """ + def depthSumHelper(nestedList, depth): + res = 0 + for l in nestedList: + if l.isInteger(): + res += l.getInteger() * depth + else: + res += depthSumHelper(l.getList(), depth + 1) + return res + return depthSumHelper(nestedList, 1) diff --git a/Python/network-delay-time.py b/Python/network-delay-time.py new file mode 100644 index 000000000..a78da2f02 --- /dev/null +++ b/Python/network-delay-time.py @@ -0,0 +1,50 @@ +# Time: O((|E| + |V|) * log|V|) = O(|E| * log|V|) by using binary heap, +# if we can further to use Fibonacci heap, it would be O(|E| + |V| * log|V|) +# Space: O(|E| + |V|) = O(|E|) + +# There are N network nodes, labelled 1 to N. +# +# Given times, a list of travel times as directed edges times[i] = (u, v, w), +# where u is the source node, v is the target node, +# and w is the time it takes for a signal to travel from source to target. +# +# Now, we send a signal from a certain node K. +# How long will it take for all nodes to receive the signal? If it is impossible, return -1. +# +# Note: +# - N will be in the range [1, 100]. +# - K will be in the range [1, N]. +# - The length of times will be in the range [1, 6000]. +# - All edges times[i] = (u, v, w) will have 1 <= u, v <= N and 1 <= w <= 100. + +import collections +import heapq + +# Dijkstra's algorithm +class Solution(object): + def networkDelayTime(self, times, N, K): + """ + :type times: List[List[int]] + :type N: int + :type K: int + :rtype: int + """ + adj = [[] for _ in xrange(N)] + for u, v, w in times: + adj[u-1].append((v-1, w)) + + result = 0 + lookup = set() + best = collections.defaultdict(lambda: float("inf")) + min_heap = [(0, K-1)] + while min_heap and len(lookup) != N: + result, u = heapq.heappop(min_heap) + lookup.add(u) + if best[u] < result: + continue + for v, w in adj[u]: + if v in lookup: continue + if result+w < best[v]: + best[v] = result+w + heapq.heappush(min_heap, (result+w, v)) + return result if len(lookup) == N else -1 diff --git a/Python/next-closest-time.py b/Python/next-closest-time.py new file mode 100644 index 000000000..d69cd9ed2 --- /dev/null +++ b/Python/next-closest-time.py @@ -0,0 +1,40 @@ +# Time: O(1) +# Space: O(1) + +# Given a time represented in the format "HH:MM", +# form the next closest time by reusing the current digits. +# There is no limit on how many times a digit can be reused. +# +# You may assume the given input string is always valid. +# For example, "01:34", "12:09" are all valid. "1:34", "12:9" are all invalid. +# +# Example 1: +# +# Input: "19:34" +# Output: "19:39" +# Explanation: The next closest time choosing from digits 1, 9, 3, 4, is 19:39, which occurs 5 minutes later. +# It is not 19:33, because this occurs 23 hours and 59 minutes later. +# +# Example 2: +# +# Input: "23:59" +# Output: "22:22" +# Explanation: The next closest time choosing from digits 2, 3, 5, 9, is 22:22. +# It may be assumed that the returned time is next day's time since it is smaller than the input time numerically. + +class Solution(object): + def nextClosestTime(self, time): + """ + :type time: str + :rtype: str + """ + h, m = time.split(":") + curr = int(h) * 60 + int(m) + result = None + for i in xrange(curr+1, curr+1441): + t = i % 1440 + h, m = t // 60, t % 60 + result = "%02d:%02d" % (h, m) + if set(result) <= set(time): + break + return result diff --git a/Python/next-greater-element-i.py b/Python/next-greater-element-i.py new file mode 100644 index 000000000..10463b33a --- /dev/null +++ b/Python/next-greater-element-i.py @@ -0,0 +1,41 @@ +# Time: O(m + n) +# Space: O(m + n) + +# You are given two arrays (without duplicates) nums1 and nums2 where nums1’s elements are subset of nums2. +# Find all the next greater numbers for nums1's elements in the corresponding places of nums2. +# +# The Next Greater Number of a number x in nums1 is the first greater number to its right in nums2. +# If it does not exist, output -1 for this number. +# +# Example 1: +# Input: nums1 = [4,1,2], nums2 = [1,3,4,2]. +# Output: [-1,3,-1] +# Explanation: +# For number 4 in the first array, you cannot find the next greater number for it in the second array, so output -1. +# For number 1 in the first array, the next greater number for it in the second array is 3. +# For number 2 in the first array, there is no next greater number for it in the second array, so output -1. +# Example 2: +# Input: nums1 = [2,4], nums2 = [1,2,3,4]. +# Output: [3,-1] +# Explanation: +# For number 2 in the first array, the next greater number for it in the second array is 3. +# For number 4 in the first array, there is no next greater number for it in the second array, so output -1. +# Note: +# All elements in nums1 and nums2 are unique. +# The length of both nums1 and nums2 would not exceed 1000. + +class Solution(object): + def nextGreaterElement(self, findNums, nums): + """ + :type findNums: List[int] + :type nums: List[int] + :rtype: List[int] + """ + stk, lookup = [], {} + for num in nums: + while stk and num > stk[-1]: + lookup[stk.pop()] = num + stk.append(num) + while stk: + lookup[stk.pop()] = -1 + return map(lambda x : lookup[x], findNums) diff --git a/Python/next-greater-element-ii.py b/Python/next-greater-element-ii.py new file mode 100644 index 000000000..6a75d5e1f --- /dev/null +++ b/Python/next-greater-element-ii.py @@ -0,0 +1,30 @@ +# Time: O(n) +# Space: O(n) + +# Given a circular array (the next element of the last element is the first element of the array), +# print the Next Greater Number for every element. +# The Next Greater Number of a number x is the first greater number to its traversing-order next in the array, +# which means you could search circularly to find its next greater number. +# If it doesn't exist, output -1 for this number. +# +# Example 1: +# Input: [1,2,1] +# Output: [2,-1,2] +# Explanation: The first 1's next greater number is 2; +# The number 2 can't find next greater number; +# The second 1's next greater number needs to search circularly, which is also 2. +# Note: The length of given array won't exceed 10000. + +class Solution(object): + def nextGreaterElements(self, nums): + """ + :type nums: List[int] + :rtype: List[int] + """ + result, stk = [0] * len(nums), [] + for i in reversed(xrange(2*len(nums))): + while stk and stk[-1] <= nums[i % len(nums)]: + stk.pop() + result[i % len(nums)] = stk[-1] if stk else -1 + stk.append(nums[i % len(nums)]) + return result diff --git a/Python/next-greater-element-iii.py b/Python/next-greater-element-iii.py new file mode 100644 index 000000000..2f0752522 --- /dev/null +++ b/Python/next-greater-element-iii.py @@ -0,0 +1,44 @@ +# Time: O(logn) = O(1) +# Space: O(logn) = O(1) + +# Given a positive 32-bit integer n, you need to find the smallest 32-bit integer +# which has exactly the same digits existing in the integer n and is greater in value than n. +# If no such positive 32-bit integer exists, you need to return -1. +# +# Example 1: +# Input: 12 +# Output: 21 +# Example 2: +# Input: 21 +# Output: -1 + +try: + xrange # Python 2 +except NameError: + xrange = range # Python 3 + + +class Solution(object): + def nextGreaterElement(self, n): + """ + :type n: int + :rtype: int + """ + digits = map(int, list(str(n))) + k, l = -1, 0 + for i in xrange(len(digits) - 1): + if digits[i] < digits[i + 1]: + k = i + + if k == -1: + digits.reverse() + return -1 + + for i in xrange(k + 1, len(digits)): + if digits[i] > digits[k]: + l = i + + digits[k], digits[l] = digits[l], digits[k] + digits[k + 1:] = digits[:k:-1] + result = int("".join(map(str, digits))) + return -1 if result >= 0x7FFFFFFF else result diff --git a/Python/next-permutation.py b/Python/next-permutation.py index 6af93140a..4384fd7ea 100644 --- a/Python/next-permutation.py +++ b/Python/next-permutation.py @@ -1,12 +1,13 @@ +from __future__ import print_function # Time: O(n) # Space: O(1) # # Implement next permutation, which rearranges numbers into the lexicographically next greater permutation of numbers. -# +# # If such arrangement is not possible, it must rearrange it as the lowest possible order (ie, sorted in ascending order). -# +# # The replacement must be in-place, do not allocate extra memory. -# +# # Here are some examples. Inputs are in the left-hand column and its corresponding outputs are in the right-hand column. # 1,2,3 -> 1,3,2 # 3,2,1 -> 1,2,3 @@ -14,29 +15,30 @@ # class Solution: - # @param num, a list of integer - # @return a list of integer + # @param {integer[]} nums + # @return {void} Do not return anything, modify nums in-place instead. def nextPermutation(self, num): k, l = -1, 0 for i in xrange(len(num) - 1): if num[i] < num[i + 1]: k = i - + if k == -1: - return num[::-1] - - for i in xrange(len(num)): + num.reverse() + return + + for i in xrange(k + 1, len(num)): if num[i] > num[k]: l = i - + num[k], num[l] = num[l], num[k] - return num[:k + 1] + num[:k:-1] + num[k + 1:] = num[:k:-1] if __name__ == "__main__": num = [1, 4, 3, 2] - num = Solution().nextPermutation(num) - print num - num = Solution().nextPermutation(num) - print num - num = Solution().nextPermutation(num) - print num \ No newline at end of file + Solution().nextPermutation(num) + print(num) + Solution().nextPermutation(num) + print(num) + Solution().nextPermutation(num) + print(num) diff --git a/Python/nim-game.py b/Python/nim-game.py new file mode 100644 index 000000000..e14d788df --- /dev/null +++ b/Python/nim-game.py @@ -0,0 +1,26 @@ +# Time: O(1) +# Space: O(1) +# +# You are playing the following Nim Game with your friend: +# There is a heap of stones on the table, each time one of +# you take turns to remove 1 to 3 stones. +# The one who removes the last stone will be the winner. +# You will take the first turn to remove the stones. +# +# Both of you are very clever and have optimal strategies for +# the game. Write a function to determine whether you can win +# the game given the number of stones in the heap. +# +# For example, if there are 4 stones in the heap, then you will +# never win the game: no matter 1, 2, or 3 stones you remove, +# the last stone will always be removed by your friend. +# + + +class Solution(object): + def canWinNim(self, n): + """ + :type n: int + :rtype: bool + """ + return n % 4 != 0 diff --git a/Python/non-decreasing-array.py b/Python/non-decreasing-array.py new file mode 100644 index 000000000..d556d531d --- /dev/null +++ b/Python/non-decreasing-array.py @@ -0,0 +1,42 @@ +# Time: O(n) +# Space: O(1) + +# Given an array with n integers, your task is to check if +# it could become non-decreasing by modifying at most 1 element. +# +# We define an array is non-decreasing if array[i] <= array[i + 1] holds for every i (1 <= i < n). +# +# Example 1: +# Input: [4,2,3] +# Output: True +# Explanation: You could modify the first +# 4 +# to +# 1 +# to get a non-decreasing array. +# Example 2: +# Input: [4,2,1] +# Output: False +# Explanation: You can't get a non-decreasing array by modify at most one element. +# Note: The n belongs to [1, 10,000]. + +class Solution(object): + def checkPossibility(self, nums): + """ + :type nums: List[int] + :rtype: bool + """ + modified, prev = False, nums[0] + for i in xrange(1, len(nums)): + if prev > nums[i]: + if modified: + return False + if i-2 < 0 or nums[i-2] <= nums[i]: + prev = nums[i] # nums[i-1] = nums[i], prev = nums[i] +# else: +# prev = nums[i-1] # nums[i] = nums[i-1], prev = nums[i] + modified = True + else: + prev = nums[i] + return True + diff --git a/Python/non-negative-integers-without-consecutive-ones.py b/Python/non-negative-integers-without-consecutive-ones.py new file mode 100644 index 000000000..5847fe8ba --- /dev/null +++ b/Python/non-negative-integers-without-consecutive-ones.py @@ -0,0 +1,41 @@ +# Time: O(1) +# Space: O(1) + +# Given a positive integer n, find the number of non-negative integers less than +# or equal to n, whose binary representations do NOT contain consecutive ones. +# +# Example 1: +# Input: 5 +# Output: 5 +# Explanation: +# Here are the non-negative integers <= 5 with their corresponding binary representations: +# 0 : 0 +# 1 : 1 +# 2 : 10 +# 3 : 11 +# 4 : 100 +# 5 : 101 +# Among them, only integer 3 disobeys the rule (two consecutive ones) and the other 5 satisfy the rule. +# Note: 1 <= n <= 10^9 + +class Solution(object): + def findIntegers(self, num): + """ + :type num: int + :rtype: int + """ + dp = [0] * 32 + dp[0], dp[1] = 1, 2 + for i in xrange(2, len(dp)): + dp[i] = dp[i-1] + dp[i-2] + result, prev_bit = 0, 0 + for i in reversed(xrange(31)): + if (num & (1 << i)) != 0: + result += dp[i] + if prev_bit == 1: + result -= 1 + break + prev_bit = 1 + else: + prev_bit = 0 + return result + 1 diff --git a/Python/non-overlapping-intervals.py b/Python/non-overlapping-intervals.py new file mode 100644 index 000000000..16a204dd6 --- /dev/null +++ b/Python/non-overlapping-intervals.py @@ -0,0 +1,50 @@ +# Time: O(nlogn) +# Space: O(1) + +# Given a collection of intervals, find the minimum number of intervals +# you need to remove to make the rest of the intervals non-overlapping. +# +# Note: +# You may assume the interval's end point is always bigger than its start point. +# Intervals like [1,2] and [2,3] have borders "touching" but they don't overlap each other. +# Example 1: +# Input: [ [1,2], [2,3], [3,4], [1,3] ] +# +# Output: 1 +# +# Explanation: [1,3] can be removed and the rest of intervals are non-overlapping. +# Example 2: +# Input: [ [1,2], [1,2], [1,2] ] +# +# Output: 2 +# +# Explanation: You need to remove two [1,2] to make the rest of intervals non-overlapping. +# Example 3: +# Input: [ [1,2], [2,3] ] +# +# Output: 0 +# +# Explanation: You don't need to remove any of the intervals since they're already non-overlapping. + +# Definition for an interval. +# class Interval(object): +# def __init__(self, s=0, e=0): +# self.start = s +# self.end = e + +class Solution(object): + def eraseOverlapIntervals(self, intervals): + """ + :type intervals: List[Interval] + :rtype: int + """ + intervals.sort(key=lambda interval: interval.start) + result, prev = 0, 0 + for i in xrange(1, len(intervals)): + if intervals[i].start < intervals[prev].end: + if intervals[i].end < intervals[prev].end: + prev = i + result += 1 + else: + prev = i + return result diff --git a/Python/nth-digit.py b/Python/nth-digit.py new file mode 100644 index 000000000..e0c7b72a3 --- /dev/null +++ b/Python/nth-digit.py @@ -0,0 +1,45 @@ +# Time: O(logn) +# Space: O(1) + +# Find the nth digit of the infinite integer sequence +# 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, ... +# +# Note: +# n is positive and will fit within the range of a 32-bit signed integer (n < 231). +# +# Example 1: +# +# Input: +# 3 +# +# Output: +# 3 +# Example 2: +# +# Input: +# 11 +# +# Output: +# 0 +# +# Explanation: +# The 11th digit of the sequence 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, +# ... is a 0, which is part of the number 10. + +class Solution(object): + def findNthDigit(self, n): + """ + :type n: int + :rtype: int + """ + digit_len = 1 + while n > digit_len * 9 * (10 ** (digit_len-1)): + n -= digit_len * 9 * (10 ** (digit_len-1)) + digit_len += 1 + + num = 10 ** (digit_len-1) + (n-1)/digit_len + + nth_digit = num / (10 ** ((digit_len-1) - ((n-1)%digit_len))) + nth_digit %= 10 + + return nth_digit diff --git a/Python/nth-magical-number.py b/Python/nth-magical-number.py new file mode 100644 index 000000000..abd892553 --- /dev/null +++ b/Python/nth-magical-number.py @@ -0,0 +1,55 @@ +# Time: O(logn) +# Space: O(1) + +# A positive integer is magical if it is divisible by either A or B. +# +# Return the N-th magical number. +# Since the answer may be very large, return it modulo 10^9 + 7. +# +# Example 1: +# +# Input: N = 1, A = 2, B = 3 +# Output: 2 +# Example 2: +# +# Input: N = 4, A = 2, B = 3 +# Output: 6 +# Example 3: +# +# Input: N = 5, A = 2, B = 4 +# Output: 10 +# Example 4: +# +# Input: N = 3, A = 6, B = 4 +# Output: 8 +# +# Note: +# - 1 <= N <= 10^9 +# - 2 <= A <= 40000 +# - 2 <= B <= 40000 + +class Solution(object): + def nthMagicalNumber(self, N, A, B): + """ + :type N: int + :type A: int + :type B: int + :rtype: int + """ + def gcd(a, b): + while b: + a, b = b, a % b + return a + + def check(A, B, N, lcm, target): + return target//A + target//B - target//lcm >= N + + lcm = A*B // gcd(A, B) + left, right = min(A, B), max(A, B)*N + while left <= right: + mid = left + (right-left)//2 + if check(A, B, N, lcm, mid): + right = mid-1 + else: + left = mid+1 + return left % (10**9 + 7) diff --git a/Python/number-complement.py b/Python/number-complement.py new file mode 100644 index 000000000..97d814779 --- /dev/null +++ b/Python/number-complement.py @@ -0,0 +1,41 @@ +# Time: O(1) +# Space: O(1) + +# Given a positive integer, output its complement number. +# The complement strategy is to flip the bits of its binary representation. +# +# Note: +# The given integer is guaranteed to fit within the range of a 32-bit signed integer. +# You could assume no leading zero bit in the integer’s binary representation. +# Example 1: +# Input: 5 +# Output: 2 +# Explanation: The binary representation of 5 is 101 (no leading zero bits), and its complement is 010. So you need to output 2. +# Example 2: +# Input: 1 +# Output: 0 +# Explanation: The binary representation of 1 is 1 (no leading zero bits), and its complement is 0. So you need to output 0. + + +class Solution(object): + def findComplement(self, num): + """ + :type num: int + :rtype: int + """ + return 2 ** (len(bin(num)) - 2) - 1 - num + + +class Solution2(object): + def findComplement(self, num): + i = 1 + while i <= num: + i <<= 1 + return (i - 1) ^ num + + +class Solution3(object): + def findComplement(self, num): + bits = '{0:b}'.format(num) + complement_bits = ''.join('1' if bit == '0' else '0' for bit in bits) + return int(complement_bits, 2) diff --git a/Python/number-of-1-bits.py b/Python/number-of-1-bits.py new file mode 100644 index 000000000..099a184fc --- /dev/null +++ b/Python/number-of-1-bits.py @@ -0,0 +1,22 @@ +from __future__ import print_function +# Time: O(logn) = O(32) +# Space: O(1) +# +# Write a function that takes an unsigned integer +# and returns the number of '1' bits it has (also known as the Hamming weight). +# +# For example, the 32-bit integer '11' has binary representation 00000000000000000000000000001011, +# so the function should return 3. +# +class Solution: + # @param n, an integer + # @return an integer + def hammingWeight(self, n): + result = 0 + while n: + n &= n - 1 + result += 1 + return result + +if __name__ == '__main__': + print(Solution().hammingWeight(11)) diff --git a/Python/number-of-atoms.py b/Python/number-of-atoms.py new file mode 100644 index 000000000..ac37e2251 --- /dev/null +++ b/Python/number-of-atoms.py @@ -0,0 +1,72 @@ +# Time: O(n) +# Space: O(n) + +# Given a chemical formula (given as a string), return the count of each atom. +# +# An atomic element always starts with an uppercase character, +# then zero or more lowercase letters, representing the name. +# +# 1 or more digits representing the count of that element may follow if the count is greater than 1. +# If the count is 1, no digits will follow. For example, H2O and H2O2 are possible, but H1O2 is impossible. +# +# Two formulas concatenated together produce another formula. For example, H2O2He3Mg4 is also a formula. +# +# A formula placed in parentheses, and a count (optionally added) is also a formula. +# For example, (H2O2) and (H2O2)3 are formulas. +# +# Given a formula, output the count of all elements as a string in the following form: +# the first name (in sorted order), followed by its count (if that count is more than 1), +# followed by the second name (in sorted order), +# followed by its count (if that count is more than 1), and so on. +# +# Example 1: +# Input: +# formula = "H2O" +# Output: "H2O" +# Explanation: +# The count of elements are {'H': 2, 'O': 1}. +# +# Example 2: +# Input: +# formula = "Mg(OH)2" +# Output: "H2MgO2" +# Explanation: +# The count of elements are {'H': 2, 'Mg': 1, 'O': 2}. +# +# Example 3: +# Input: +# formula = "K4(ON(SO3)2)2" +# Output: "K4N2O14S4" +# Explanation: +# The count of elements are {'K': 4, 'N': 2, 'O': 14, 'S': 4}. +# Note: +# +# All atom names consist of lowercase letters, except for the first character which is uppercase. +# The length of formula will be in the range [1, 1000]. +# formula will only consist of letters, digits, and round parentheses, +# and is a valid formula as defined in the problem. + +import collections +import re + + +class Solution(object): + def countOfAtoms(self, formula): + """ + :type formula: str + :rtype: str + """ + parse = re.findall(r"([A-Z][a-z]*)(\d*)|(\()|(\))(\d*)", formula) + stk = [collections.Counter()] + for name, m1, left_open, right_open, m2 in parse: + if name: + stk[-1][name] += int(m1 or 1) + if left_open: + stk.append(collections.Counter()) + if right_open: + top = stk.pop() + for k, v in top.iteritems(): + stk[-1][k] += v * int(m2 or 1) + + return "".join(name + (str(stk[-1][name]) if stk[-1][name] > 1 else '') \ + for name in sorted(stk[-1])) diff --git a/Python/number-of-boomerangs.py b/Python/number-of-boomerangs.py new file mode 100644 index 000000000..84ff01205 --- /dev/null +++ b/Python/number-of-boomerangs.py @@ -0,0 +1,58 @@ +# Time: O(n^2) +# Space: O(n) + +# Given n points in the plane that are all pairwise distinct, +# a "boomerang" is a tuple of points (i, j, k) such that the distance +# between i and j equals the distance between i and k (the order of the tuple matters). +# +# Find the number of boomerangs. You may assume that n will be at most 500 +# and coordinates of points are all in the range [-10000, 10000] (inclusive). +# +# Example: +# Input: +# [[0,0],[1,0],[2,0]] +# +# Output: +# 2 +# +# Explanation: +# The two boomerangs are [[1,0],[0,0],[2,0]] and [[1,0],[2,0],[0,0]] +import collections + + +class Solution(object): + def numberOfBoomerangs(self, points): + """ + :type points: List[List[int]] + :rtype: int + """ + result = 0 + + for i in xrange(len(points)): + group = collections.defaultdict(int) + for j in xrange(len(points)): + if j == i: + continue + dx, dy = points[i][0] - points[j][0], points[i][1] - points[j][1] + group[dx**2 + dy**2] += 1 + + for _, v in group.iteritems(): + if v > 1: + result += v * (v-1) + + return result + + def numberOfBoomerangs2(self, points): + """ + :type points: List[List[int]] + :rtype: int + """ + cnt = 0 + for a, i in enumerate(points): + dis_list = [] + for b, k in enumerate(points[:a] + points[a + 1:]): + dis_list.append((k[0] - i[0]) ** 2 + (k[1] - i[1]) ** 2) + for z in collections.Counter(dis_list).values(): + if z > 1: + cnt += z * (z - 1) + return cnt diff --git a/Python/number-of-connected-components-in-an-undirected-graph.py b/Python/number-of-connected-components-in-an-undirected-graph.py new file mode 100644 index 000000000..e8b27f59e --- /dev/null +++ b/Python/number-of-connected-components-in-an-undirected-graph.py @@ -0,0 +1,31 @@ +# Time: O(nlog*n) ~= O(n), n is the length of the positions +# Space: O(n) + +class UnionFind(object): + def __init__(self, n): + self.set = range(n) + self.count = n + + def find_set(self, x): + if self.set[x] != x: + self.set[x] = self.find_set(self.set[x]) # path compression. + return self.set[x] + + def union_set(self, x, y): + x_root, y_root = map(self.find_set, (x, y)) + if x_root != y_root: + self.set[min(x_root, y_root)] = max(x_root, y_root) + self.count -= 1 + + +class Solution(object): + def countComponents(self, n, edges): + """ + :type n: int + :type edges: List[List[int]] + :rtype: int + """ + union_find = UnionFind(n) + for i, j in edges: + union_find.union_set(i, j) + return union_find.count diff --git a/Python/number-of-corner-rectangles.py b/Python/number-of-corner-rectangles.py new file mode 100644 index 000000000..5988e1885 --- /dev/null +++ b/Python/number-of-corner-rectangles.py @@ -0,0 +1,50 @@ +# Time: O(n * m^2), n is the number of rows with 1s, m is the number of cols with 1s +# Space: O(n * m) + +# Given a grid where each entry is only 0 or 1, find the number of corner rectangles. +# +# A corner rectangle is 4 distinct 1s on the grid that form an axis-aligned rectangle. +# Note that only the corners need to have the value 1. Also, all four 1s used must be distinct. +# +# Example 1: +# Input: grid = +# [[1, 0, 0, 1, 0], +# [0, 0, 1, 0, 1], +# [0, 0, 0, 1, 0], +# [1, 0, 1, 0, 1]] +# Output: 1 +# Explanation: There is only one corner rectangle, with corners grid[1][2], grid[1][4], grid[3][2], grid[3][4]. +# +# Example 2: +# Input: grid = +# [[1, 1, 1], +# [1, 1, 1], +# [1, 1, 1]] +# Output: 9 +# Explanation: There are four 2x2 rectangles, four 2x3 and 3x2 rectangles, and one 3x3 rectangle. +# +# Example 3: +# Input: grid = +# [[1, 1, 1, 1]] +# Output: 0 +# Explanation: Rectangles must have four distinct corners. +# Note: +# - The number of rows and columns of grid will each be in the range [1, 200]. +# - Each grid[i][j] will be either 0 or 1. +# - The number of 1s in the grid will be at most 6000. + +class Solution(object): + def countCornerRectangles(self, grid): + """ + :type grid: List[List[int]] + :rtype: int + """ + rows = [[c for c, val in enumerate(row) if val] + for row in grid] + result = 0 + for i in xrange(len(rows)): + lookup = set(rows[i]) + for j in xrange(i): + count = sum(1 for c in rows[j] if c in lookup) + result += count*(count-1)/2 + return result diff --git a/Python/number-of-digit-one.py b/Python/number-of-digit-one.py new file mode 100644 index 000000000..97dcab395 --- /dev/null +++ b/Python/number-of-digit-one.py @@ -0,0 +1,39 @@ +# Time: O(logn) +# Space: O(1) +# +# Given an integer n, count the total number of digit 1 appearing +# in all non-negative integers less than or equal to n. +# +# For example: +# Given n = 13, +# Return 6, because digit 1 occurred in the following numbers: +# 1, 10, 11, 12, 13. +# + +class Solution: + # @param {integer} n + # @return {integer} + def countDigitOne(self, n): + k = 1; + cnt, multiplier, left_part = 0, 1, n + + while left_part > 0: + # split number into: left_part, curr, right_part + curr = left_part % 10 + right_part = n % multiplier + + # count of (c000 ~ oooc000) = (ooo + (k < curr)? 1 : 0) * 1000 + cnt += (left_part / 10 + (k < curr)) * multiplier + + # if k == 0, oooc000 = (ooo - 1) * 1000 + if k == 0 and multiplier > 1: + cnt -= multiplier + + # count of (oook000 ~ oookxxx): count += xxx + 1 + if curr == k: + cnt += right_part + 1 + + left_part /= 10 + multiplier *= 10 + + return cnt diff --git a/Python/number-of-distinct-islands-ii.py b/Python/number-of-distinct-islands-ii.py new file mode 100644 index 000000000..49de00acc --- /dev/null +++ b/Python/number-of-distinct-islands-ii.py @@ -0,0 +1,44 @@ +# Time: O((m * n) * log(m * n)) +# Space: O(m * n) + +class Solution(object): + def numDistinctIslands2(self, grid): + """ + :type grid: List[List[int]] + :rtype: int + """ + directions = [(0, -1), (0, 1), (-1, 0), (1, 0)] + + def dfs(i, j, grid, island): + if not (0 <= i < len(grid) and \ + 0 <= j < len(grid[0]) and \ + grid[i][j] > 0): + return False + grid[i][j] *= -1 + island.append((i, j)); + for d in directions: + dfs(i+d[0], j+d[1], grid, island) + return True + + def normalize(island): + shapes = [[] for _ in xrange(8)] + for x, y in island: + rotations_and_reflections = [[ x, y], [ x, -y], [-x, y], [-x, -y], + [ y, x], [ y, -x], [-y, x], [-y, -x]] + for i in xrange(len(rotations_and_reflections)): + shapes[i].append(rotations_and_reflections[i]) + for shape in shapes: + shape.sort() # Time: O(ilogi), i is the size of the island, the max would be (m * n) + origin = list(shape[0]) + for p in shape: + p[0] -= origin[0] + p[1] -= origin[1] + return min(shapes) + + islands = set() + for i in xrange(len(grid)): + for j in xrange(len(grid[0])): + island = [] + if dfs(i, j, grid, island): + islands.add(str(normalize(island))) + return len(islands) diff --git a/Python/number-of-distinct-islands.py b/Python/number-of-distinct-islands.py new file mode 100644 index 000000000..dc471e867 --- /dev/null +++ b/Python/number-of-distinct-islands.py @@ -0,0 +1,30 @@ +# Time: O(m * n) +# Space: O(m * n) + +class Solution(object): + def numDistinctIslands(self, grid): + """ + :type grid: List[List[int]] + :rtype: int + """ + directions = {'l':[-1, 0], 'r':[ 1, 0], \ + 'u':[ 0, 1], 'd':[ 0, -1]} + + def dfs(i, j, grid, island): + if not (0 <= i < len(grid) and \ + 0 <= j < len(grid[0]) and \ + grid[i][j] > 0): + return False + grid[i][j] *= -1 + for k, v in directions.iteritems(): + island.append(k); + dfs(i+v[0], j+v[1], grid, island) + return True + + islands = set() + for i in xrange(len(grid)): + for j in xrange(len(grid[0])): + island = [] + if dfs(i, j, grid, island): + islands.add("".join(island)) + return len(islands) diff --git a/Python/number-of-islands-ii.py b/Python/number-of-islands-ii.py new file mode 100644 index 000000000..c76a1a5df --- /dev/null +++ b/Python/number-of-islands-ii.py @@ -0,0 +1,43 @@ +# Time: O(klog*k) ~= O(k), k is the length of the positions +# Space: O(k) + +class Solution(object): + def numIslands2(self, m, n, positions): + """ + :type m: int + :type n: int + :type positions: List[List[int]] + :rtype: List[int] + """ + def node_id(node, n): + return node[0] * n + node[1] + + def find_set(x): + if set[x] != x: + set[x] = find_set(set[x]) # path compression. + return set[x] + + def union_set(x, y): + x_root, y_root = find_set(x), find_set(y) + set[min(x_root, y_root)] = max(x_root, y_root) + + numbers = [] + number = 0 + directions = [(0, -1), (0, 1), (-1, 0), (1, 0)] + set = {} + for position in positions: + node = (position[0], position[1]) + set[node_id(node, n)] = node_id(node, n) + number += 1 + + for d in directions: + neighbor = (position[0] + d[0], position[1] + d[1]) + if 0 <= neighbor[0] < m and 0 <= neighbor[1] < n and \ + node_id(neighbor, n) in set: + if find_set(node_id(node, n)) != find_set(node_id(neighbor, n)): + # Merge different islands, amortised time: O(log*k) ~= O(1) + union_set(node_id(node, n), node_id(neighbor, n)) + number -= 1 + numbers.append(number) + + return numbers diff --git a/Python/number-of-islands.py b/Python/number-of-islands.py new file mode 100644 index 000000000..6f34748dd --- /dev/null +++ b/Python/number-of-islands.py @@ -0,0 +1,54 @@ +# Time: O(m * n) +# Space: O(m * n) +# +# Given a 2d grid map of '1's (land) and '0's (water), count the number of islands. +# An island is surrounded by water and is formed by connecting adjacent lands horizontally +# or vertically. You may assume all four edges of the grid are all surrounded by water. +# +# Example 1: +# +# 11110 +# 11010 +# 11000 +# 00000 +# Answer: 1 +# +# Example 2: +# +# 11000 +# 11000 +# 00100 +# 00011 +# Answer: 3 +# + +class Solution: + # @param {boolean[][]} grid a boolean 2D matrix + # @return {int} an integer + def numIslands(self, grid): + if not grid: + return 0 + + row = len(grid) + col = len(grid[0]) + count = 0 + for i in xrange(row): + for j in xrange(col): + if grid[i][j] == '1': + self.dfs(grid, row, col, i, j) + count += 1 + return count + + def dfs(self, grid, row, col, x, y): + if grid[x][y] == '0': + return + grid[x][y] = '0' + + if x != 0: + self.dfs(grid, row, col, x - 1, y) + if x != row - 1: + self.dfs(grid, row, col, x + 1, y) + if y != 0: + self.dfs(grid, row, col, x, y - 1) + if y != col - 1: + self.dfs(grid, row, col, x, y + 1) diff --git a/Python/number-of-lines-to-write-string.py b/Python/number-of-lines-to-write-string.py new file mode 100644 index 000000000..b2e047002 --- /dev/null +++ b/Python/number-of-lines-to-write-string.py @@ -0,0 +1,55 @@ +# Time: O(n) +# Space: O(1) + +# We are to write the letters of a given string S, from left to right into lines. +# Each line has maximum width 100 units, and if writing a letter would cause the width +# of the line to exceed 100 units, it is written on the next line. +# We are given an array widths, an array where widths[0] is the width of 'a', widths[1] +# is the width of 'b', ..., and widths[25] is the width of 'z'. +# +# Now answer two questions: how many lines have at least one character from S, +# and what is the width used by the last such line? Return your answer as an integer list of length 2. +# +# Example : +# Input: +# widths = [10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10] +# S = "abcdefghijklmnopqrstuvwxyz" +# Output: [3, 60] +# +# Explanation: +# All letters have the same length of 10. To write all 26 letters, +# we need two full lines and one line with 60 units. +# Example : +# Input: +# widths = [4,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10] +# S = "bbbcccdddaaa" +# Output: [2, 4] +# +# Explanation: +# All letters except 'a' have the same length of 10, and +# "bbbcccdddaa" will cover 9 * 10 + 2 * 4 = 98 units. +# For the last 'a', it is written on the second line because +# there is only 2 units left in the first line. +# So the answer is 2 lines, plus 4 units in the second line. +# +# Note: +# - The length of S will be in the range [1, 1000]. +# - S will only contain lowercase letters. +# - widths is an array of length 26. +# - widths[i] will be in the range of [2, 10]. + +class Solution(object): + def numberOfLines(self, widths, S): + """ + :type widths: List[int] + :type S: str + :rtype: List[int] + """ + result = [1, 0] + for c in S: + w = widths[ord(c)-ord('a')] + result[1] += w + if result[1] > 100: + result[0] += 1 + result[1] = w + return result diff --git a/Python/number-of-longest-increasing-subsequence.py b/Python/number-of-longest-increasing-subsequence.py new file mode 100644 index 000000000..3068056a1 --- /dev/null +++ b/Python/number-of-longest-increasing-subsequence.py @@ -0,0 +1,38 @@ +# Time: O(n^2) +# Space: O(n) + +# Given an unsorted array of integers, find the number of longest increasing subsequence. +# +# Example 1: +# Input: [1,3,5,4,7] +# Output: 2 +# Explanation: The two longest increasing subsequence are [1, 3, 4, 7] and [1, 3, 5, 7]. +# Example 2: +# Input: [2,2,2,2,2] +# Output: 5 +# Explanation: The length of longest continuous increasing subsequence is 1, and there are +# 5 subsequences' length is 1, so output 5. +# Note: Length of the given array will be not exceed 2000 and the answer is guaranteed +# to be fit in 32-bit signed int. + +class Solution(object): + def findNumberOfLIS(self, nums): + """ + :type nums: List[int] + :rtype: int + """ + result, max_len = 0, 0 + dp = [[1, 1] for _ in xrange(len(nums))] # {length, number} pair + for i in xrange(len(nums)): + for j in xrange(i): + if nums[i] > nums[j]: + if dp[i][0] == dp[j][0]+1: + dp[i][1] += dp[j][1] + elif dp[i][0] < dp[j][0]+1: + dp[i] = [dp[j][0]+1, dp[j][1]] + if max_len == dp[i][0]: + result += dp[i][1] + elif max_len < dp[i][0]: + max_len = dp[i][0] + result = dp[i][1] + return result diff --git a/Python/number-of-matching-subsequences.py b/Python/number-of-matching-subsequences.py new file mode 100644 index 000000000..f6d3602a2 --- /dev/null +++ b/Python/number-of-matching-subsequences.py @@ -0,0 +1,35 @@ +# Time: O(n + w), n is the size of S, w is the size of words +# Space: O(1) + +# Given string S and a dictionary of words words, find the number of words[i] that is a subsequence of S. +# +# Example : +# Input: +# S = "abcde" +# words = ["a", "bb", "acd", "ace"] +# Output: 3 +# Explanation: There are three words in words that are a subsequence of S: "a", "acd", "ace". +# +# Note: +# - All words in words and S will only consists of lowercase letters. +# - The length of S will be in the range of [1, 50000]. +# - The length of words will be in the range of [1, 5000]. +# - The length of words[i] will be in the range of [1, 50]. + +import collections + + +class Solution(object): + def numMatchingSubseq(self, S, words): + """ + :type S: str + :type words: List[str] + :rtype: int + """ + waiting = collections.defaultdict(list) + for word in words: + waiting[word[0]].append(iter(word[1:])) + for c in S: + for it in waiting.pop(c, ()): + waiting[next(it, None)].append(it) + return len(waiting[None]) diff --git a/Python/number-of-segments-in-a-string.py b/Python/number-of-segments-in-a-string.py new file mode 100644 index 000000000..1a5c0086f --- /dev/null +++ b/Python/number-of-segments-in-a-string.py @@ -0,0 +1,34 @@ +# Time: O(n) +# Space: O(1) + +# Count the number of segments in a string, +# where a segment is defined to be a contiguous +# sequence of non-space characters. +# +# Please note that the string does not +# contain any non-printable characters. +# +# Example: +# +# Input: "Hello, my name is John" +# Output: 5 + + +class Solution(object): + def countSegments(self, s): + """ + :type s: str + :rtype: int + """ + result = int(len(s) and s[-1] != ' ') + for i in xrange(1, len(s)): + if s[i] == ' ' and s[i-1] != ' ': + result += 1 + return result + + def countSegments2(self, s): + """ + :type s: str + :rtype: int + """ + return len([i for i in s.strip().split(' ') if i]) diff --git a/Python/number-of-subarrays-with-bounded-maximum.py b/Python/number-of-subarrays-with-bounded-maximum.py new file mode 100644 index 000000000..17ab89de5 --- /dev/null +++ b/Python/number-of-subarrays-with-bounded-maximum.py @@ -0,0 +1,37 @@ +# Time: O(n) +# Space: O(1) + +# We are given an array A of positive integers, +# and two positive integers L and R (L <= R). +# +# Return the number of (contiguous, non-empty) subarrays +# such that the value of the maximum array element in that subarray is at least L and at most R. +# +# Example : +# Input: +# A = [2, 1, 4, 3] +# L = 2 +# R = 3 +# Output: 3 +# Explanation: There are three subarrays that meet the requirements: [2], [2, 1], [3]. +# +# Note: +# - L, R and A[i] will be an integer in the range [0, 10^9]. +# - The length of A will be in the range of [1, 50000]. + +class Solution(object): + def numSubarrayBoundedMax(self, A, L, R): + """ + :type A: List[int] + :type L: int + :type R: int + :rtype: int + """ + def count(A, bound): + result, curr = 0, 0 + for i in A : + curr = curr + 1 if i <= bound else 0 + result += curr + return result + + return count(A, R) - count(A, L-1) diff --git a/Python/odd-even-linked-list.py b/Python/odd-even-linked-list.py new file mode 100644 index 000000000..166f649f1 --- /dev/null +++ b/Python/odd-even-linked-list.py @@ -0,0 +1,43 @@ +# Time: O(n) +# Space: O(1) + +# Given a singly linked list, group all odd nodes +# together followed by the even nodes. +# Please note here we are talking about the node number +# and not the value in the nodes. +# +# You should try to do it in place. The program should run +# in O(1) space complexity and O(nodes) time complexity. +# +# Example: +# Given 1->2->3->4->5->NULL, +# return 1->3->5->2->4->NULL. +# +# Note: +# The relative order inside both the even and odd groups +# should remain as it was in the input. +# The first node is considered odd, the second node even +# and so on ... + +# Definition for singly-linked list. +# class ListNode(object): +# def __init__(self, x): +# self.val = x +# self.next = None + +class Solution(object): + def oddEvenList(self, head): + """ + :type head: ListNode + :rtype: ListNode + """ + if head: + odd_tail, cur = head, head.next + while cur and cur.next: + even_head = odd_tail.next + odd_tail.next = cur.next + odd_tail = odd_tail.next + cur.next = odd_tail.next + odd_tail.next = even_head + cur = cur.next + return head diff --git a/Python/one-edit-distance.py b/Python/one-edit-distance.py index 8a5dd426b..cb61cdd9e 100644 --- a/Python/one-edit-distance.py +++ b/Python/one-edit-distance.py @@ -1,20 +1,23 @@ +from __future__ import print_function # Time: O(m + n) # Space: O(1) # # Given two strings S and T, determine if they are both one edit distance apart. # -class Solution: - # @param s, a string - # @param t, a string - # @return a boolean +class Solution(object): def isOneEditDistance(self, s, t): + """ + :type s: str + :type t: str + :rtype: bool + """ m, n = len(s), len(t) if m > n: return self.isOneEditDistance(t, s) if n - m > 1: return False - + i, shift = 0, n - m while i < m and s[i] == t[i]: i += 1 @@ -22,8 +25,9 @@ def isOneEditDistance(self, s, t): i += 1 while i < m and s[i] == t[i + shift]: i += 1 - + return i == m - + + if __name__ == "__main__": - print Solution().isOneEditDistance("teacher", "acher") \ No newline at end of file + print(Solution().isOneEditDistance("teacher", "acher")) diff --git a/Python/ones-and-zeroes.py b/Python/ones-and-zeroes.py new file mode 100644 index 000000000..19a0b3f80 --- /dev/null +++ b/Python/ones-and-zeroes.py @@ -0,0 +1,48 @@ +# Time: O(s * m * n), s is the size of the array. +# Space: O(m * n) + +# In the computer world, use restricted resource you have to +# generate maximum benefit is what we always want to pursue. +# +# For now, suppose you are a dominator of m 0s and n 1s respectively. +# On the other hand, there is an array with strings consisting of only 0s and 1s. +# +# Now your task is to find the maximum number of strings that you can form +# with given m 0s and n 1s. Each 0 and 1 can be used at most once. +# +# Note: +# The given numbers of 0s and 1s will both not exceed 100 +# The size of given string array won't exceed 600. +# Example 1: +# Input: Array = {"10", "0001", "111001", "1", "0"}, m = 5, n = 3 +# Output: 4 +# +# Explanation: This are totally 4 strings can be formed +# by the using of 5 0s and 3 1s, which are “10,”0001”,”1”,”0” +# Example 2: +# Input: Array = {"10", "0", "1"}, m = 1, n = 1 +# Output: 2 +# +# Explanation: You could form "10", but then you'd have nothing left. Better form "0" and "1". + +class Solution(object): + def findMaxForm(self, strs, m, n): + """ + :type strs: List[str] + :type m: int + :type n: int + :rtype: int + """ + dp = [[0 for _ in xrange(n+1)] for _ in xrange(m+1)] + for s in strs: + zero_count, one_count = 0, 0 + for c in s: + if c == '0': + zero_count += 1 + elif c == '1': + one_count += 1 + + for i in reversed(xrange(zero_count, m+1)): + for j in reversed(xrange(one_count, n+1)): + dp[i][j] = max(dp[i][j], dp[i-zero_count][j-one_count]+1) + return dp[m][n] diff --git a/Python/open-the-lock.py b/Python/open-the-lock.py new file mode 100644 index 000000000..99c9c25b5 --- /dev/null +++ b/Python/open-the-lock.py @@ -0,0 +1,77 @@ +# Time: O(k * n^k + d), n is the number of alphabets, +# k is the length of target, +# d is the size of deadends +# Space: O(k * n^k + d) + +# You have a lock in front of you with 4 circular wheels. +# Each wheel has 10 slots: '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'. +# The wheels can rotate freely and wrap around: +# for example we can turn '9' to be '0', or '0' to be '9'. +# Each move consists of turning one wheel one slot. +# +# The lock initially starts at '0000', a string representing the state of the 4 wheels. +# +# You are given a list of deadends dead ends, +# meaning if the lock displays any of these codes, +# the wheels of the lock will stop turning and you will be unable to open it. +# +# Given a target representing the value of the wheels that will unlock the lock, +# return the minimum total number of turns required to open the lock, or -1 if it is impossible. +# +# Example 1: +# Input: deadends = ["0201","0101","0102","1212","2002"], target = "0202" +# Output: 6 +# Explanation: +# A sequence of valid moves would be "0000" -> "1000" -> "1100" -> "1200" -> "1201" -> "1202" -> "0202". +# Note that a sequence like "0000" -> "0001" -> "0002" -> "0102" -> "0202" would be invalid, +# because the wheels of the lock become stuck after the display becomes the dead end "0102". +# +# Example 2: +# Input: deadends = ["8888"], target = "0009" +# Output: 1 +# Explanation: +# We can turn the last wheel in reverse to move from "0000" -> "0009". +# +# Example 3: +# Input: deadends = ["8887","8889","8878","8898","8788","8988","7888","9888"], target = "8888" +# Output: -1 +# Explanation: +# We can't reach the target without getting stuck. +# +# Example 4: +# Input: deadends = ["0000"], target = "8888" +# Output: -1 +# +# Note: +# - The length of deadends will be in the range [1, 500]. +# - target will not be in the list deadends. +# - Every string in deadends and the string target will be a string of 4 digits +# from the 10,000 possibilities '0000' to '9999'. + +class Solution(object): + def openLock(self, deadends, target): + """ + :type deadends: List[str] + :type target: str + :rtype: int + """ + dead = set(deadends) + q = ["0000"] + lookup = {"0000"} + depth = 0 + while q: + next_q = [] + for node in q: + if node == target: return depth + if node in dead: continue + for i in xrange(4): + n = int(node[i]) + for d in (-1, 1): + nn = (n+d) % 10 + neighbor = node[:i] + str(nn) + node[i+1:] + if neighbor not in lookup: + lookup.add(neighbor) + next_q.append(neighbor) + q, next_q = next_q, [] + depth += 1 + return -1 diff --git a/Python/optimal-account-balancing.py b/Python/optimal-account-balancing.py new file mode 100644 index 000000000..32ffe34e4 --- /dev/null +++ b/Python/optimal-account-balancing.py @@ -0,0 +1,40 @@ +# Time: O(n * 2^n), n is the size of the debt. +# Space: O(n * 2^n) + +import collections + + +class Solution(object): + def minTransfers(self, transactions): + """ + :type transactions: List[List[int]] + :rtype: int + """ + account = collections.defaultdict(int) + for transaction in transactions: + account[transaction[0]] += transaction[2] + account[transaction[1]] -= transaction[2] + + debt = [] + for v in account.values(): + if v: + debt.append(v) + + if not debt: + return 0 + + n = 1 << len(debt) + dp, subset = [float("inf")] * n, [] + for i in xrange(1, n): + net_debt, number = 0, 0 + for j in xrange(len(debt)): + if i & 1 << j: + net_debt += debt[j] + number += 1 + if net_debt == 0: + dp[i] = number - 1 + for s in subset: + if (i & s) == s: + dp[i] = min(dp[i], dp[s] + dp[i - s]) + subset.append(i) + return dp[-1] diff --git a/Python/optimal-division.py b/Python/optimal-division.py new file mode 100644 index 000000000..5283e2e81 --- /dev/null +++ b/Python/optimal-division.py @@ -0,0 +1,44 @@ +# Time: O(n) +# Space: O(1) + +# Given a list of positive integers, the adjacent integers will perform the float division. +# For example, [2,3,4] -> 2 / 3 / 4. +# +# However, you can add any number of parenthesis at any position to change the priority of operations. +# You should find out how to add parenthesis to get the maximum result, +# and return the corresponding expression in string format. Your expression should NOT contain redundant parenthesis. +# +# Example: +# Input: [1000,100,10,2] +# Output: "1000/(100/10/2)" +# Explanation: +# 1000/(100/10/2) = 1000/((100/10)/2) = 200 +# However, the bold parenthesis in "1000/((100/10)/2)" are redundant, +# since they don't influence the operation priority. So you should return "1000/(100/10/2)". +# +# Other cases: +# 1000/(100/10)/2 = 50 +# 1000/(100/(10/2)) = 50 +# 1000/100/10/2 = 0.5 +# 1000/100/(10/2) = 2 +# Note: +# +# The length of the input array is [1, 10]. +# Elements in the given array will be in range [2, 1000]. +# There is only one optimal division for each test case. + +class Solution(object): + def optimalDivision(self, nums): + """ + :type nums: List[int] + :rtype: str + """ + if len(nums) == 1: + return str(nums[0]) + if len(nums) == 2: + return str(nums[0]) + "/" + str(nums[1]) + result = [str(nums[0]) + "/(" + str(nums[1])] + for i in xrange(2, len(nums)): + result += "/" + str(nums[i]) + result += ")" + return "".join(result) diff --git a/Python/out-of-boundary-paths.py b/Python/out-of-boundary-paths.py new file mode 100644 index 000000000..259406840 --- /dev/null +++ b/Python/out-of-boundary-paths.py @@ -0,0 +1,42 @@ +# Time: O(N * m * n) +# Space: O(m * n) + +# There is an m by n grid with a ball. Given the start coordinate (i,j) of the ball, +# you can move the ball to adjacent cell or cross the grid boundary in +# four directions (up, down, left, right). However, you can at most move N times. +# Find out the number of paths to move the ball out of grid boundary. +# The answer may be very large, return it after mod 109 + 7. +# +# Example 1: +# Input:m = 2, n = 2, N = 2, i = 0, j = 0 +# Output: 6 +# +# Example 2: +# Input:m = 1, n = 3, N = 3, i = 0, j = 1 +# Output: 12 +# +# Note: +# Once you move the ball out of boundary, you cannot move it back. +# The length and height of the grid is in range [1,50]. +# N is in range [0,50]. + +class Solution(object): + def findPaths(self, m, n, N, x, y): + """ + :type m: int + :type n: int + :type N: int + :type x: int + :type y: int + :rtype: int + """ + M = 1000000000 + 7 + dp = [[[0 for _ in xrange(n)] for _ in xrange(m)] for _ in xrange(2)] + for moves in xrange(N): + for i in xrange(m): + for j in xrange(n): + dp[(moves + 1) % 2][i][j] = (((1 if (i == 0) else dp[moves % 2][i - 1][j]) + \ + (1 if (i == m - 1) else dp[moves % 2][i + 1][j])) % M + \ + ((1 if (j == 0) else dp[moves % 2][i][j - 1]) + \ + (1 if (j == n - 1) else dp[moves % 2][i][j + 1])) % M) % M + return dp[N % 2][x][y] diff --git a/Python/output-contest-matches.py b/Python/output-contest-matches.py new file mode 100644 index 000000000..8887fbf89 --- /dev/null +++ b/Python/output-contest-matches.py @@ -0,0 +1,14 @@ +# Time: O(n) +# Space: O(n) + +class Solution(object): + def findContestMatch(self, n): + """ + :type n: int + :rtype: str + """ + matches = map(str, range(1, n+1)) + while len(matches)/2: + matches = ["({},{})".format(matches[i], matches[-i-1]) for i in xrange(len(matches)/2)] + return matches[0] + diff --git a/Python/pacific-atlantic-water-flow.py b/Python/pacific-atlantic-water-flow.py new file mode 100644 index 000000000..628dbe9d6 --- /dev/null +++ b/Python/pacific-atlantic-water-flow.py @@ -0,0 +1,68 @@ +# Time: O(m * n) +# Space: O(m * n) + +# Given an m x n matrix of non-negative integers representing the height of +# each unit cell in a continent, the "Pacific ocean" touches the left and +# top edges of the matrix and the "Atlantic ocean" touches the right and bottom edges. +# +# Water can only flow in four directions (up, down, left, or right) +# from a cell to another one with height equal or lower. +# +# Find the list of grid coordinates where water can flow to both the Pacific and Atlantic ocean. +# +# Note: +# The order of returned grid coordinates does not matter. +# Both m and n are less than 150. +# Example: +# +# Given the following 5x5 matrix: +# +# Pacific ~ ~ ~ ~ ~ +# ~ 1 2 2 3 (5) * +# ~ 3 2 3 (4) (4) * +# ~ 2 4 (5) 3 1 * +# ~ (6) (7) 1 4 5 * +# ~ (5) 1 1 2 4 * +# * * * * * Atlantic +# +# Return: +# +# [[0, 4], [1, 3], [1, 4], [2, 2], [3, 0], [3, 1], [4, 0]] (positions with parentheses in above matrix). + +class Solution(object): + def pacificAtlantic(self, matrix): + """ + :type matrix: List[List[int]] + :rtype: List[List[int]] + """ + PACIFIC, ATLANTIC = 1, 2 + + def pacificAtlanticHelper(matrix, x, y, prev_height, prev_val, visited, res): + if (not 0 <= x < len(matrix)) or \ + (not 0 <= y < len(matrix[0])) or \ + matrix[x][y] < prev_height or \ + (visited[x][y] | prev_val) == visited[x][y]: + return + + visited[x][y] |= prev_val + if visited[x][y] == (PACIFIC | ATLANTIC): + res.append((x, y)) + + for d in [(0, -1), (0, 1), (-1, 0), (1, 0)]: + pacificAtlanticHelper(matrix, x + d[0], y + d[1], matrix[x][y], visited[x][y], visited, res) + + if not matrix: + return [] + + res = [] + m, n = len(matrix),len(matrix[0]) + visited = [[0 for _ in xrange(n)] for _ in xrange(m)] + + for i in xrange(m): + pacificAtlanticHelper(matrix, i, 0, float("-inf"), PACIFIC, visited, res) + pacificAtlanticHelper(matrix, i, n - 1, float("-inf"), ATLANTIC, visited, res) + for j in xrange(n): + pacificAtlanticHelper(matrix, 0, j, float("-inf"), PACIFIC, visited, res) + pacificAtlanticHelper(matrix, m - 1, j, float("-inf"), ATLANTIC, visited, res) + + return res diff --git a/Python/paint-fence.py b/Python/paint-fence.py new file mode 100644 index 000000000..be60c6e62 --- /dev/null +++ b/Python/paint-fence.py @@ -0,0 +1,42 @@ +# Time: O(n) +# Space: O(1) + +# DP solution with rolling window. +class Solution(object): + def numWays(self, n, k): + """ + :type n: int + :type k: int + :rtype: int + """ + if n == 0: + return 0 + elif n == 1: + return k + ways = [0] * 3 + ways[0] = k + ways[1] = (k - 1) * ways[0] + k + for i in xrange(2, n): + ways[i % 3] = (k - 1) * (ways[(i - 1) % 3] + ways[(i - 2) % 3]) + return ways[(n - 1) % 3] + +# Time: O(n) +# Space: O(n) +# DP solution. +class Solution2(object): + def numWays(self, n, k): + """ + :type n: int + :type k: int + :rtype: int + """ + if n == 0: + return 0 + elif n == 1: + return k + ways = [0] * n + ways[0] = k + ways[1] = (k - 1) * ways[0] + k + for i in xrange(2, n): + ways[i] = (k - 1) * (ways[i - 1] + ways[i - 2]) + return ways[n - 1] diff --git a/Python/paint-house-ii.py b/Python/paint-house-ii.py new file mode 100644 index 000000000..18557f1c2 --- /dev/null +++ b/Python/paint-house-ii.py @@ -0,0 +1,41 @@ +# Time: O(n * k) +# Space: O(k) + +class Solution2(object): + def minCostII(self, costs): + """ + :type costs: List[List[int]] + :rtype: int + """ + return min(reduce(self.combine, costs)) if costs else 0 + + def combine(self, tmp, house): + smallest, k, i = min(tmp), len(tmp), tmp.index(min(tmp)) + tmp, tmp[i] = [smallest] * k, min(tmp[:i] + tmp[i+1:]) + return map(sum, zip(tmp, house)) + + +class Solution2(object): + def minCostII(self, costs): + """ + :type costs: List[List[int]] + :rtype: int + """ + if not costs: + return 0 + + n = len(costs) + k = len(costs[0]) + min_cost = [costs[0], [0] * k] + for i in xrange(1, n): + smallest, second_smallest = float("inf"), float("inf") + for j in xrange(k): + if min_cost[(i - 1) % 2][j] < smallest: + smallest, second_smallest = min_cost[(i - 1) % 2][j], smallest + elif min_cost[(i - 1) % 2][j] < second_smallest: + second_smallest = min_cost[(i - 1) % 2][j] + for j in xrange(k): + min_j = smallest if min_cost[(i - 1) % 2][j] != smallest else second_smallest + min_cost[i % 2][j] = costs[i][j] + min_j + + return min(min_cost[(n - 1) % 2]) diff --git a/Python/paint-house.py b/Python/paint-house.py new file mode 100644 index 000000000..d59b581a8 --- /dev/null +++ b/Python/paint-house.py @@ -0,0 +1,43 @@ +# Time: O(n) +# Space: O(1) + +class Solution(object): + def minCost(self, costs): + """ + :type costs: List[List[int]] + :rtype: int + """ + if not costs: + return 0 + + min_cost = [costs[0], [0, 0, 0]] + + n = len(costs) + for i in xrange(1, n): + min_cost[i % 2][0] = costs[i][0] + \ + min(min_cost[(i - 1) % 2][1], min_cost[(i - 1) % 2][2]) + min_cost[i % 2][1] = costs[i][1] + \ + min(min_cost[(i - 1) % 2][0], min_cost[(i - 1) % 2][2]) + min_cost[i % 2][2] = costs[i][2] + \ + min(min_cost[(i - 1) % 2][0], min_cost[(i - 1) % 2][1]) + + return min(min_cost[(n - 1) % 2]) + +# Time: O(n) +# Space: O(n) +class Solution2(object): + def minCost(self, costs): + """ + :type costs: List[List[int]] + :rtype: int + """ + if not costs: + return 0 + + n = len(costs) + for i in xrange(1, n): + costs[i][0] += min(costs[i - 1][1], costs[i - 1][2]) + costs[i][1] += min(costs[i - 1][0], costs[i - 1][2]) + costs[i][2] += min(costs[i - 1][0], costs[i - 1][1]) + + return min(costs[n - 1]) diff --git a/Python/palindrome-linked-list.py b/Python/palindrome-linked-list.py new file mode 100644 index 000000000..21f542737 --- /dev/null +++ b/Python/palindrome-linked-list.py @@ -0,0 +1,38 @@ +# Time: O(n) +# Space: O(1) +# +# Given a singly linked list, determine if it is a palindrome. +# +# Follow up: +# Could you do it in O(n) time and O(1) space? +# +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, x): +# self.val = x +# self.next = None +# + +class Solution: + # @param {ListNode} head + # @return {boolean} + def isPalindrome(self, head): + reverse, fast = None, head + # Reverse the first half part of the list. + while fast and fast.next: + fast = fast.next.next + head.next, reverse, head = reverse, head, head.next + + # If the number of the nodes is odd, + # set the head of the tail list to the next of the median node. + tail = head.next if fast else head + + # Compare the reversed first half list with the second half list. + # And restore the reversed first half list. + is_palindrome = True + while reverse: + is_palindrome = is_palindrome and reverse.val == tail.val + reverse.next, head, reverse = head, reverse, reverse.next + tail = tail.next + + return is_palindrome diff --git a/Python/palindrome-number.py b/Python/palindrome-number.py index ab1a797c6..df81686eb 100644 --- a/Python/palindrome-number.py +++ b/Python/palindrome-number.py @@ -1,16 +1,17 @@ +from __future__ import print_function # Time: O(1) # Space: O(1) # # Determine whether an integer is a palindrome. Do this without extra space. -# +# # Some hints: # Could negative integers be palindromes? (ie, -1) -# +# # If you are thinking of converting the integer to string, note the restriction of using extra space. -# -# You could also try reversing an integer. However, if you have solved the problem "Reverse Integer", +# +# You could also try reversing an integer. However, if you have solved the problem "Reverse Integer", # you know that the reversed integer might overflow. How would you handle such case? -# +# # There is a more generic way of solving this problem. # @@ -20,15 +21,15 @@ def isPalindrome(self, x): if x < 0: return False copy, reverse = x, 0 - + while copy: reverse *= 10 reverse += copy % 10 - copy /= 10 - + copy //= 10 + return x == reverse if __name__ == "__main__": - print Solution().isPalindrome(12321) - print Solution().isPalindrome(12320) - print Solution().isPalindrome(-12321) \ No newline at end of file + print(Solution().isPalindrome(12321)) + print(Solution().isPalindrome(12320)) + print(Solution().isPalindrome(-12321)) diff --git a/Python/palindrome-pairs.py b/Python/palindrome-pairs.py new file mode 100644 index 000000000..eb9283bae --- /dev/null +++ b/Python/palindrome-pairs.py @@ -0,0 +1,145 @@ +# Time: O(n * k^2), n is the number of the words, k is the max length of the words. +# Space: O(n * k) + +# Given a list of unique words. Find all pairs of indices (i, j) +# in the given list, so that the concatenation of the two words, +# i.e. words[i] + words[j] is a palindrome. +# +# Example 1: +# Given words = ["bat", "tab", "cat"] +# Return [[0, 1], [1, 0]] +# The palindromes are ["battab", "tabbat"] +# Example 2: +# Given words = ["abcd", "dcba", "lls", "s", "sssll"] +# Return [[0, 1], [1, 0], [3, 2], [2, 4]] +# The palindromes are ["dcbaabcd", "abcddcba", "slls", "llssssll"] + +import collections + + +class Solution(object): + def palindromePairs(self, words): + """ + :type words: List[str] + :rtype: List[List[int]] + """ + res = [] + lookup = {} + for i, word in enumerate(words): + lookup[word] = i + + for i in xrange(len(words)): + for j in xrange(len(words[i]) + 1): + prefix = words[i][j:] + suffix = words[i][:j] + if prefix == prefix[::-1] and \ + suffix[::-1] in lookup and lookup[suffix[::-1]] != i: + res.append([i, lookup[suffix[::-1]]]) + if j > 0 and suffix == suffix[::-1] and \ + prefix[::-1] in lookup and lookup[prefix[::-1]] != i: + res.append([lookup[prefix[::-1]], i]) + return res + +# Time: O(n * k^2), n is the number of the words, k is the max length of the words. +# Space: O(n * k^2) +# Manacher solution. +class Solution_TLE(object): + def palindromePairs(self, words): + """ + :type words: List[str] + :rtype: List[List[int]] + """ + def manacher(s, P): + def preProcess(s): + if not s: + return ['^', '$'] + T = ['^'] + for c in s: + T += ["#", c] + T += ['#', '$'] + return T + + T = preProcess(s) + center, right = 0, 0 + for i in xrange(1, len(T) - 1): + i_mirror = 2 * center - i + if right > i: + P[i] = min(right - i, P[i_mirror]) + else: + P[i] = 0 + while T[i + 1 + P[i]] == T[i - 1 - P[i]]: + P[i] += 1 + if i + P[i] > right: + center, right = i, i + P[i] + + prefix, suffix = collections.defaultdict(list), collections.defaultdict(list) + for i, word in enumerate(words): + P = [0] * (2 * len(word) + 3) + manacher(word, P) + for j in xrange(len(P)): + if j - P[j] == 1: + prefix[word[(j + P[j]) / 2:]].append(i) + if j + P[j] == len(P) - 2: + suffix[word[:(j - P[j]) / 2]].append(i) + res = [] + for i, word in enumerate(words): + for j in prefix[word[::-1]]: + if j != i: + res.append([i, j]) + for j in suffix[word[::-1]]: + if len(word) != len(words[j]): + res.append([j, i]) + return res + + +# Time: O(n * k^2), n is the number of the words, k is the max length of the words. +# Space: O(n * k) +# Trie solution. +class TrieNode: + def __init__(self): + self.word_idx = -1 + self.leaves = {} + + def insert(self, word, i): + cur = self + for c in word: + if not c in cur.leaves: + cur.leaves[c] = TrieNode() + cur = cur.leaves[c] + cur.word_idx = i + + def find(self, s, idx, res): + cur = self + for i in reversed(xrange(len(s))): + if s[i] in cur.leaves: + cur = cur.leaves[s[i]] + if cur.word_idx not in (-1, idx) and \ + self.is_palindrome(s, i - 1): + res.append([cur.word_idx, idx]) + else: + break + + def is_palindrome(self, s, j): + i = 0 + while i <= j: + if s[i] != s[j]: + return False + i += 1 + j -= 1 + return True + +class Solution_MLE(object): + def palindromePairs(self, words): + """ + :type words: List[str] + :rtype: List[List[int]] + """ + res = [] + trie = TrieNode() + for i in xrange(len(words)): + trie.insert(words[i], i) + + for i in xrange(len(words)): + trie.find(words[i], i, res) + + return res diff --git a/Python/palindrome-partitioning-ii.py b/Python/palindrome-partitioning-ii.py index c2f3d98aa..173eeab03 100644 --- a/Python/palindrome-partitioning-ii.py +++ b/Python/palindrome-partitioning-ii.py @@ -1,10 +1,11 @@ +from __future__ import print_function # Time: O(n^2) # Space: O(n^2) # # Given a string s, partition s such that every substring of the partition is a palindrome. -# +# # Return the minimum cuts needed for a palindrome partitioning of s. -# +# # For example, given s = "aab", # Return 1 since the palindrome partitioning ["aa","b"] could be produced using 1 cut. # @@ -15,14 +16,14 @@ class Solution: def minCut(self, s): lookup = [[False for j in xrange(len(s))] for i in xrange(len(s))] mincut = [len(s) - 1 - i for i in xrange(len(s) + 1)] - + for i in reversed(xrange(len(s))): for j in xrange(i, len(s)): if s[i] == s[j] and (j - i < 2 or lookup[i + 1][j - 1]): lookup[i][j] = True mincut[i] = min(mincut[i], mincut[j + 1] + 1) - + return mincut[0] if __name__ == "__main__": - print Solution().minCut("aab") \ No newline at end of file + print(Solution().minCut("aab")) \ No newline at end of file diff --git a/Python/palindrome-partitioning.py b/Python/palindrome-partitioning.py index fbba4054f..640c6a208 100644 --- a/Python/palindrome-partitioning.py +++ b/Python/palindrome-partitioning.py @@ -1,13 +1,14 @@ +from __future__ import print_function # Time: O(n^2 ~ 2^n) # Space: O(n^2) # # Given a string s, partition s such that every substring of the partition is a palindrome. -# +# # Return all possible palindrome partitioning of s. -# +# # For example, given s = "aab", # Return -# +# # [ # ["aa","b"], # ["a","a","b"] @@ -21,12 +22,12 @@ class Solution: # @return a list of lists of string def partition(self, s): n = len(s) - + is_palindrome = [[0 for j in xrange(n)] for i in xrange(n)] for i in reversed(xrange(0, n)): for j in xrange(i, n): is_palindrome[i][j] = s[i] == s[j] and ((j - i < 2 ) or is_palindrome[i + 1][j - 1]) - + sub_partition = [[] for i in xrange(n)] for i in reversed(xrange(n)): for j in xrange(i, n): @@ -36,7 +37,7 @@ def partition(self, s): sub_partition[i].append([s[i:j + 1]] + p) else: sub_partition[i].append([s[i:j + 1]]) - + return sub_partition[0] # Time: O(2^n) @@ -49,7 +50,7 @@ def partition(self, s): result = [] self.partitionRecu(result, [], s, 0) return result - + def partitionRecu(self, result, cur, s, i): if i == len(s): result.append(list(cur)) @@ -59,7 +60,7 @@ def partitionRecu(self, result, cur, s, i): cur.append(s[i: j + 1]) self.partitionRecu(result, cur, s, j + 1) cur.pop() - + def isPalindrome(self, s): for i in xrange(len(s) / 2): if s[i] != s[-(i + 1)]: @@ -67,4 +68,4 @@ def isPalindrome(self, s): return True if __name__ == "__main__": - print Solution().partition("aab") + print(Solution().partition("aab")) diff --git a/Python/palindrome-permutation-ii.py b/Python/palindrome-permutation-ii.py new file mode 100644 index 000000000..c74444ba0 --- /dev/null +++ b/Python/palindrome-permutation-ii.py @@ -0,0 +1,48 @@ +# Time: O(n * n!) +# Space: O(n) + +import collections +import itertools + + +class Solution(object): + def generatePalindromes(self, s): + """ + :type s: str + :rtype: List[str] + """ + cnt = collections.Counter(s) + mid = ''.join(k for k, v in cnt.iteritems() if v % 2) + chars = ''.join(k * (v / 2) for k, v in cnt.iteritems()) + return self.permuteUnique(mid, chars) if len(mid) < 2 else [] + + def permuteUnique(self, mid, nums): + result = [] + used = [False] * len(nums) + self.permuteUniqueRecu(mid, result, used, [], nums) + return result + + def permuteUniqueRecu(self, mid, result, used, cur, nums): + if len(cur) == len(nums): + half_palindrome = ''.join(cur) + result.append(half_palindrome + mid + half_palindrome[::-1]) + return + for i in xrange(len(nums)): + if not used[i] and not (i > 0 and nums[i-1] == nums[i] and used[i-1]): + used[i] = True + cur.append(nums[i]) + self.permuteUniqueRecu(mid, result, used, cur, nums) + cur.pop() + used[i] = False + +class Solution2(object): + def generatePalindromes(self, s): + """ + :type s: str + :rtype: List[str] + """ + cnt = collections.Counter(s) + mid = tuple(k for k, v in cnt.iteritems() if v % 2) + chars = ''.join(k * (v / 2) for k, v in cnt.iteritems()) + return [''.join(half_palindrome + mid + half_palindrome[::-1]) \ + for half_palindrome in set(itertools.permutations(chars))] if len(mid) < 2 else [] diff --git a/Python/palindrome-permutation.py b/Python/palindrome-permutation.py new file mode 100644 index 000000000..1932a00ee --- /dev/null +++ b/Python/palindrome-permutation.py @@ -0,0 +1,13 @@ +# Time: O(n) +# Space: O(1) + +import collections + + +class Solution(object): + def canPermutePalindrome(self, s): + """ + :type s: str + :rtype: bool + """ + return sum(v % 2 for v in collections.Counter(s).values()) < 2 diff --git a/Python/palindromic-substrings.py b/Python/palindromic-substrings.py new file mode 100644 index 000000000..5f2668515 --- /dev/null +++ b/Python/palindromic-substrings.py @@ -0,0 +1,39 @@ +# Time: O(n) +# Space: O(n) + +# Given a string, your task is to count how many palindromic substrings in this string. +# +# The substrings with different start indexes or end indexes are counted as +# different substrings even they consist of same characters. +# +# Example 1: +# Input: "abc" +# Output: 3 +# Explanation: Three palindromic strings: "a", "b", "c". +# Example 2: +# Input: "aaa" +# Output: 6 +# Explanation: Six palindromic strings: "a", "a", "a", "aa", "aa", "aaa". +# Note: +# The input string length won't exceed 1000. + +class Solution(object): + def countSubstrings(self, s): + """ + :type s: str + :rtype: int + """ + def manacher(s): + s = '^#' + '#'.join(s) + '#$' + P = [0] * len(s) + C, R = 0, 0 + for i in xrange(1, len(s) - 1): + i_mirror = 2*C-i + if R > i: + P[i] = min(R-i, P[i_mirror]) + while s[i+1+P[i]] == s[i-1-P[i]]: + P[i] += 1 + if i+P[i] > R: + C, R = i, i+P[i] + return P + return sum((max_len+1)/2 for max_len in manacher(s)) diff --git a/Python/parse-lisp-expression.py b/Python/parse-lisp-expression.py new file mode 100644 index 000000000..24ec81409 --- /dev/null +++ b/Python/parse-lisp-expression.py @@ -0,0 +1,110 @@ +# Time: O(n^2) +# Space: O(n^2) + +# You are given a string expression representing a Lisp-like expression to return the integer value of. +# +# The syntax for these expressions is given as follows. +# +# An expression is either an integer, a let-expression, +# an add-expression, a mult-expression, or an assigned variable. +# Expressions always evaluate to a single integer. +# (An integer could be positive or negative.) +# A let-expression takes the form (let v1 e1 v2 e2 ... vn en expr), +# where let is always the string "let", then there are 1 or more pairs of alternating variables and expressions, +# meaning that the first variable v1 is assigned the value of the expression e1, +# the second variable v2 is assigned the value of the expression e2, +# and so on sequentially; and then the value of this let-expression is the value of the expression expr. +# An add-expression takes the form (add e1 e2) where add is always the string "add", +# there are always two expressions e1, e2, +# and this expression evaluates to the addition of the evaluation of e1 and the evaluation of e2. +# A mult-expression takes the form (mult e1 e2) where mult is always the string "mult", +# there are always two expressions e1, e2, +# and this expression evaluates to the multiplication of the evaluation of e1 and the evaluation of e2. +# For the purposes of this question, we will use a smaller subset of variable names. +# A variable starts with a lowercase letter, then zero or more lowercase letters or digits. +# Additionally for your convenience, +# the names "add", "let", or "mult" are protected and will never be used as variable names. +# Finally, there is the concept of scope. +# When an expression of a variable name is evaluated, +# within the context of that evaluation, +# the innermost scope (in terms of parentheses) is checked first for the value of that variable, +# and then outer scopes are checked sequentially. It is guaranteed that every expression is legal. +# Please see the examples for more details on scope. +# +# Evaluation Examples: +# Input: (add 1 2) +# Output: 3 +# +# Input: (mult 3 (add 2 3)) +# Output: 15 +# +# Input: (let x 2 (mult x 5)) +# Output: 10 +# +# Input: (let x 2 (mult x (let x 3 y 4 (add x y)))) +# Output: 14 +# Explanation: In the expression (add x y), when checking for the value of the variable x, +# we check from the innermost scope to the outermost in the context of the variable we are trying to evaluate. +# Since x = 3 is found first, the value of x is 3. +# +# Input: (let x 3 x 2 x) +# Output: 2 +# Explanation: Assignment in let statements is processed sequentially. +# +# Input: (let x 1 y 2 x (add x y) (add x y)) +# Output: 5 +# Explanation: The first (add x y) evaluates as 3, and is assigned to x. +# The second (add x y) evaluates as 3+2 = 5. +# +# Input: (let x 2 (add (let x 3 (let x 4 x)) x)) +# Output: 6 +# Explanation: Even though (let x 4 x) has a deeper scope, it is outside the context +# of the final x in the add-expression. That final x will equal 2. +# +# Input: (let a1 3 b2 (add a1 1) b2) +# Output 4 +# Explanation: Variable names can contain digits after the first character. +# +# Note: +# - The given string expression is well formatted: +# There are no leading or trailing spaces, +# there is only a single space separating different components of the string, +# and no space between adjacent parentheses. +# The expression is guaranteed to be legal and evaluate to an integer. +# - The length of expression is at most 2000. (It is also non-empty, as that would not be a legal expression.) +# - The answer and all intermediate calculations of that answer are guaranteed to fit in a 32-bit integer. + +class Solution(object): + def evaluate(self, expression): + """ + :type expression: str + :rtype: int + """ + def getval(lookup, x): + return lookup.get(x, x) + + def evaluate(tokens, lookup): + if tokens[0] in ('add', 'mult'): + a, b = map(int, map(lambda x: getval(lookup, x), tokens[1:])) + return str(a+b if tokens[0] == 'add' else a*b) + for i in xrange(1, len(tokens)-1, 2): + if tokens[i+1]: + lookup[tokens[i]] = getval(lookup, tokens[i+1]) + return getval(lookup, tokens[-1]) + + tokens, lookup, stk = [''], {}, [] + for c in expression: + if c == '(': + if tokens[0] == 'let': + evaluate(tokens, lookup) + stk.append((tokens, dict(lookup))) + tokens = [''] + elif c == ' ': + tokens.append('') + elif c == ')': + val = evaluate(tokens, lookup) + tokens, lookup = stk.pop() + tokens[-1] += val + else: + tokens[-1] += c + return int(tokens[0]) diff --git a/Python/partition-equal-subset-sum.py b/Python/partition-equal-subset-sum.py new file mode 100644 index 000000000..0cbad38d8 --- /dev/null +++ b/Python/partition-equal-subset-sum.py @@ -0,0 +1,42 @@ +# Time: O(n * s), s is the sum of nums +# Space: O(s) + +# Given a non-empty array containing only positive integers, +# find if the array can be partitioned into two subsets +# such that the sum of elements in both subsets is equal. +# +# Note: +# Both the array size and each of the array element will not exceed 100. +# +# Example 1: +# +# Input: [1, 5, 11, 5] +# +# Output: true +# +# Explanation: The array can be partitioned as [1, 5, 5] and [11]. +# Example 2: +# +# Input: [1, 2, 3, 5] +# +# Output: false +# +# Explanation: The array cannot be partitioned into equal sum subsets. + +class Solution(object): + def canPartition(self, nums): + """ + :type nums: List[int] + :rtype: bool + """ + s = sum(nums) + if s % 2: + return False + + dp = [False] * (s/2 + 1) + dp[0] = True + for num in nums: + for i in reversed(xrange(1, len(dp))): + if num <= i: + dp[i] = dp[i] or dp[i - num] + return dp[-1] diff --git a/Python/partition-labels.py b/Python/partition-labels.py new file mode 100644 index 000000000..db4e03ec2 --- /dev/null +++ b/Python/partition-labels.py @@ -0,0 +1,18 @@ +# Time: O(n) +# Space: O(n) + +class Solution(object): + def partitionLabels(self, S): + """ + :type S: str + :rtype: List[int] + """ + lookup = {c: i for i, c in enumerate(S)} + first, last = 0, 0 + result = [] + for i, c in enumerate(S): + last = max(last, lookup[c]) + if i == last: + result.append(i-first+1) + first = i+1 + return result diff --git a/Python/partition-list.py b/Python/partition-list.py index ec5b0a3c1..48d7b74c5 100644 --- a/Python/partition-list.py +++ b/Python/partition-list.py @@ -1,10 +1,11 @@ +from __future__ import print_function # Time: O(n) # Space: O(1) # # Given a linked list and a value x, partition it such that all nodes less than x come before nodes greater than or equal to x. -# +# # You should preserve the original relative order of the nodes in each of the two partitions. -# +# # For example, # Given 1->4->3->2->5->2 and x = 3, # return 1->2->2->4->3->5. @@ -15,7 +16,7 @@ class ListNode: def __init__(self, x): self.val = x self.next = None - + def __repr__(self): if self: return "{} -> {}".format(self.val, repr(self.next)) @@ -27,7 +28,7 @@ class Solution: def partition(self, head, x): dummySmaller, dummyGreater = ListNode(-1), ListNode(-1) smaller, greater = dummySmaller, dummyGreater - + while head: if head.val < x: smaller.next = head @@ -36,10 +37,10 @@ def partition(self, head, x): greater.next = head greater = greater.next head = head.next - + smaller.next = dummyGreater.next greater.next = None - + return dummySmaller.next if __name__ == "__main__": @@ -49,6 +50,5 @@ def partition(self, head, x): head.next.next.next = ListNode(2) head.next.next.next.next = ListNode(5) head.next.next.next.next.next = ListNode(2) - print Solution().partition(head, 3) + print(Solution().partition(head, 3)) - \ No newline at end of file diff --git a/Python/partition-to-k-equal-sum-subsets.py b/Python/partition-to-k-equal-sum-subsets.py new file mode 100644 index 000000000..6dd49fab1 --- /dev/null +++ b/Python/partition-to-k-equal-sum-subsets.py @@ -0,0 +1,68 @@ +# Time: O(n*2^n) +# Space: O(2^n) + +# Given an array of integers nums and a positive integer k, +# find whether it's possible to divide this array into k non-empty subsets whose sums are all equal. +# +# Example 1: +# Input: nums = [4, 3, 2, 3, 5, 2, 1], k = 4 +# Output: True +# Explanation: It's possible to divide it into 4 subsets (5), (1, 4), (2,3), (2,3) with equal sums. +# Note: +# +# 1 <= k <= len(nums) <= 16. +# 0 < nums[i] < 10000. + +# Memoization solution. +class Solution(object): + def canPartitionKSubsets(self, nums, k): + """ + :type nums: List[int] + :type k: int + :rtype: bool + """ + def dfs(nums, target, used, todo, lookup): + if lookup[used] is None: + targ = (todo-1)%target + 1 + lookup[used] = any(dfs(nums, target, used | (1<>i) & 1) == 0 and num <= targ) + return lookup[used] + + total = sum(nums) + if total%k or max(nums) > total//k: + return False + lookup = [None] * (1 << len(nums)) + lookup[-1] = True + return dfs(nums, total//k, 0, total, lookup) + + +# Time: O(k^(n-k) * k!) +# Space: O(n) +# DFS solution with pruning. +class Solution2(object): + def canPartitionKSubsets(self, nums, k): + """ + :type nums: List[int] + :type k: int + :rtype: bool + """ + def dfs(nums, target, i, subset_sums): + if i == len(nums): + return True + for k in xrange(len(subset_sums)): + if subset_sums[k]+nums[i] > target: + continue + subset_sums[k] += nums[i] + if dfs(nums, target, i+1, subset_sums): + return True + subset_sums[k] -= nums[i] + if not subset_sums[k]: break + return False + + total = sum(nums) + if total%k != 0 or max(nums) > total//k: + return False + nums.sort(reverse=True) + subset_sums = [0] * k + return dfs(nums, total//k, 0, subset_sums) diff --git a/Python/pascals-triangle-ii.py b/Python/pascals-triangle-ii.py index f8aaa06d1..0b9eaf12d 100644 --- a/Python/pascals-triangle-ii.py +++ b/Python/pascals-triangle-ii.py @@ -1,24 +1,17 @@ +from __future__ import print_function # Time: O(n^2) -# Space: O(n) -# +# Space: O(1) + # Given an index k, return the kth row of the Pascal's triangle. -# +# # For example, given k = 3, # Return [1,3,3,1]. -# +# # Note: # Could you optimize your algorithm to use only O(k) extra space? # class Solution: - # @return a list of integers - def getRow(self, rowIndex): - result = [1] - for i in range(1, rowIndex + 1): - result = [1] + [result[j - 1] + result[j] for j in range(1, i)] + [1] - return result - -class Solution2: # @return a list of integers def getRow(self, rowIndex): result = [0] * (rowIndex + 1) @@ -28,5 +21,47 @@ def getRow(self, rowIndex): old, result[j] = result[j], old + result[j] return result + def getRow2(self, rowIndex): + """ + :type rowIndex: int + :rtype: List[int] + """ + row = [1] + for _ in range(rowIndex): + row = [x + y for x, y in zip([0] + row, row + [0])] + return row + + def getRow3(self, rowIndex): + """ + :type rowIndex: int + :rtype: List[int] + """ + if rowIndex == 0: return [1] + res = [1, 1] + + def add(nums): + res = nums[:1] + for i, j in enumerate(nums): + if i < len(nums) - 1: + res += [nums[i] + nums[i + 1]] + res += nums[:1] + return res + + while res[1] < rowIndex: + res = add(res) + return res + + +# Time: O(n^2) +# Space: O(n) +class Solution2: + # @return a list of integers + def getRow(self, rowIndex): + result = [1] + for i in range(1, rowIndex + 1): + result = [1] + [result[j - 1] + result[j] for j in xrange(1, i)] + [1] + return result + + if __name__ == "__main__": - print Solution().getRow(3) \ No newline at end of file + print(Solution().getRow(3)) diff --git a/Python/pascals-triangle.py b/Python/pascals-triangle.py index 96bbf51f7..a4d915625 100644 --- a/Python/pascals-triangle.py +++ b/Python/pascals-triangle.py @@ -1,11 +1,12 @@ +from __future__ import print_function # Time: O(n^2) -# Space: O(n) +# Space: O(1) # # Given numRows, generate the first numRows of Pascal's triangle. -# +# # For example, given numRows = 5, # Return -# +# # [ # [1], # [1,1], @@ -28,5 +29,33 @@ def generate(self, numRows): result[i].append(result[i - 1][j - 1] + result[i - 1][j]) return result + def generate2(self, numRows): + if not numRows: return [] + res = [[1]] + for i in range(1, numRows): + res += [map(lambda x, y: x + y, res[-1] + [0], [0] + res[-1])] + return res[:numRows] + + def generate3(self, numRows): + """ + :type numRows: int + :rtype: List[List[int]] + """ + if numRows == 0: return [] + if numRows == 1: return [[1]] + res = [[1], [1, 1]] + + def add(nums): + res = nums[:1] + for i, j in enumerate(nums): + if i < len(nums) - 1: + res += [nums[i] + nums[i + 1]] + res += nums[:1] + return res + + while len(res) < numRows: + res.extend([add(res[-1])]) + return res + if __name__ == "__main__": - print Solution().generate(5) + print(Solution().generate(5)) diff --git a/Python/patching-array.py b/Python/patching-array.py new file mode 100644 index 000000000..8b9a681af --- /dev/null +++ b/Python/patching-array.py @@ -0,0 +1,48 @@ +# Time: O(s + logn), s is the number of elements in the array +# Space: O(1) + +# Given a sorted positive integer array nums and +# an integer n, add/patch elements to the array +# such that any number in range [1, n] inclusive +# can be formed by the sum of some elements in the +# array. Return the minimum number of patches required. +# +# Example 1: +# nums = [1, 3], n = 6 +# Return 1. +# +# Combinations of nums are [1], [3], [1,3], which form +# possible sums of: 1, 3, 4. +# Now if we add/patch 2 to nums, the combinations are: +# [1], [2], [3], [1,3], [2,3], [1,2,3]. +# Possible sums are 1, 2, 3, 4, 5, 6, which now covers +# the range [1, 6]. +# So we only need 1 patch. +# +# Example 2: +# nums = [1, 5, 10], n = 20 +# Return 2. +# The two patches can be [2, 4]. +# +# Example 3: +# nums = [1, 2, 2], n = 5 +# Return 0. + + +class Solution(object): + def minPatches(self, nums, n): + """ + :type nums: List[int] + :type n: int + :rtype: int + """ + patch, miss, i = 0, 1, 0 + while miss <= n: + if i < len(nums) and nums[i] <= miss: + miss += nums[i] + i += 1 + else: + miss += miss + patch += 1 + + return patch diff --git a/Python/path-sum-ii.py b/Python/path-sum-ii.py index a6e835dcb..c92886f74 100644 --- a/Python/path-sum-ii.py +++ b/Python/path-sum-ii.py @@ -1,8 +1,9 @@ +from __future__ import print_function # Time: O(n) -# Space: O(logn) +# Space: O(h), h is height of binary tree # # Given a binary tree and a sum, find all root-to-leaf paths where each path's sum equals the given sum. -# +# # For example: # Given the below binary tree and sum = 22, # 5 @@ -33,22 +34,22 @@ class Solution: def pathSum(self, root, sum): return self.pathSumRecu([], [], root, sum) - + def pathSumRecu(self, result, cur, root, sum): if root is None: return result - + if root.left is None and root.right is None and root.val == sum: result.append(cur + [root.val]) return result - + cur.append(root.val) self.pathSumRecu(result, cur, root.left, sum - root.val) self.pathSumRecu(result, cur,root.right, sum - root.val) cur.pop() return result - + if __name__ == "__main__": root = TreeNode(5) - print Solution().pathSum(root, 5) \ No newline at end of file + print(Solution().pathSum(root, 5)) diff --git a/Python/path-sum-iii.py b/Python/path-sum-iii.py new file mode 100644 index 000000000..a3b8f6cc2 --- /dev/null +++ b/Python/path-sum-iii.py @@ -0,0 +1,89 @@ +# Time: O(n) +# Space: O(h) + +# You are given a binary tree in which each node contains an integer value. +# +# Find the number of paths that sum to a given value. +# +# The path does not need to start or end at the root or a leaf, +# but it must go downwards (traveling only from parent nodes to child nodes). +# +# The tree has no more than 1,000 nodes and the values are in the range -1,000,000 to 1,000,000. +# +# Example: +# +# root = [10,5,-3,3,2,null,11,3,-2,null,1], sum = 8 +# +# 10 +# / \ +# 5 -3 +# / \ \ +# 3 2 11 +# / \ \ +# 3 -2 1 +# +# Return 3. The paths that sum to 8 are: +# +# 1. 5 -> 3 +# 2. 5 -> 2 -> 1 +# 3. -3 -> 11 + +# Definition for a binary tree node. +# class TreeNode(object): +# def __init__(self, x): +# self.val = x +# self.left = None +# self.right = None + +import collections + + +class Solution(object): + def pathSum(self, root, sum): + """ + :type root: TreeNode + :type sum: int + :rtype: int + """ + def pathSumHelper(root, curr, sum, lookup): + if root is None: + return 0 + curr += root.val + result = lookup[curr-sum] if curr-sum in lookup else 0 + lookup[curr] += 1 + result += pathSumHelper(root.left, curr, sum, lookup) + \ + pathSumHelper(root.right, curr, sum, lookup) + lookup[curr] -= 1 + if lookup[curr] == 0: + del lookup[curr] + return result + + lookup = collections.defaultdict(int) + lookup[0] = 1 + return pathSumHelper(root, 0, sum, lookup) + + +# Time: O(n^2) +# Space: O(h) +class Solution2(object): + def pathSum(self, root, sum): + """ + :type root: TreeNode + :type sum: int + :rtype: int + """ + def pathSumHelper(root, prev, sum): + if root is None: + return 0 + + curr = prev + root.val; + return int(curr == sum) + \ + pathSumHelper(root.left, curr, sum) + \ + pathSumHelper(root.right, curr, sum) + + if root is None: + return 0 + + return pathSumHelper(root, 0, sum) + \ + self.pathSum(root.left, sum) + \ + self.pathSum(root.right, sum) diff --git a/Python/path-sum-iv.py b/Python/path-sum-iv.py new file mode 100644 index 000000000..6ee097bfa --- /dev/null +++ b/Python/path-sum-iv.py @@ -0,0 +1,40 @@ +# Time: O(n) +# Space: O(p), p is the number of paths + +import collections + + +class Solution(object): + def pathSum(self, nums): + """ + :type nums: List[int] + :rtype: int + """ + class Node(object): + def __init__(self, num): + self.level = num/100 - 1 + self.i = (num%100)/10 - 1 + self.val = num%10 + self.leaf = True + + def isParent(self, other): + return self.level == other.level-1 and \ + self.i == other.i/2 + + if not nums: + return 0 + result = 0 + q = collections.deque() + dummy = Node(10) + parent = dummy + for num in nums: + child = Node(num) + while not parent.isParent(child): + result += parent.val if parent.leaf else 0 + parent = q.popleft() + parent.leaf = False + child.val += parent.val + q.append(child) + while q: + result += q.pop().val + return result diff --git a/Python/path-sum.py b/Python/path-sum.py index 0f686ec7f..7f9a11fb6 100644 --- a/Python/path-sum.py +++ b/Python/path-sum.py @@ -1,9 +1,10 @@ +from __future__ import print_function # Time: O(n) -# Space: O(logn) +# Space: O(h), h is height of binary tree # -# Given a binary tree and a sum, determine if the tree has a root-to-leaf path +# Given a binary tree and a sum, determine if the tree has a root-to-leaf path # such that adding up all the values along the path equals the given sum. -# +# # For example: # Given the below binary tree and sum = 22, # 5 @@ -22,7 +23,7 @@ def __init__(self, x): self.val = x self.left = None self.right = None - + class Solution: # @param root, a tree node # @param sum, an integer @@ -30,16 +31,16 @@ class Solution: def hasPathSum(self, root, sum): if root is None: return False - + if root.left is None and root.right is None and root.val == sum: return True - + return self.hasPathSum(root.left, sum - root.val) or self.hasPathSum(root.right, sum - root.val) - + if __name__ == "__main__": root = TreeNode(5) root.left = TreeNode(4) root.right = TreeNode(8) root.left.left = TreeNode(11) root.left.left.right = TreeNode(2) - print Solution().hasPathSum(root, 22) \ No newline at end of file + print(Solution().hasPathSum(root, 22)) diff --git a/Python/peak-index-in-a-mountain-array.py b/Python/peak-index-in-a-mountain-array.py new file mode 100644 index 000000000..388f3d153 --- /dev/null +++ b/Python/peak-index-in-a-mountain-array.py @@ -0,0 +1,41 @@ +# Time: O(logn) +# Space: O(1) + +# Let's call an array A a mountain if the following properties hold: +# +# A.length >= 3 +# There exists some 0 < i < A.length - 1 +# such that A[0] < A[1] < ... A[i-1] < A[i] > A[i+1] > ... > A[A.length - 1] +# Given an array that is definitely a mountain, +# return any i such that +# A[0] < A[1] < ... A[i-1] < A[i] > A[i+1] > ... > A[A.length - 1]. +# +# Example 1: +# +# Input: [0,1,0] +# Output: 1 +# Example 2: +# +# Input: [0,2,1,0] +# Output: 1 +# Note: +# +# 3 <= A.length <= 10000 +# 0 <= A[i] <= 10^6 +# A is a mountain, as defined above. + + +class Solution(object): + def peakIndexInMountainArray(self, A): + """ + :type A: List[int] + :rtype: int + """ + left, right = 0, len(A) + while left < right: + mid = left + (right-left)//2 + if A[mid] > A[mid+1]: + right = mid + else: + left = mid+1 + return left diff --git a/Python/peeking-iterator.py b/Python/peeking-iterator.py new file mode 100644 index 000000000..f59311e0d --- /dev/null +++ b/Python/peeking-iterator.py @@ -0,0 +1,83 @@ +# Time: O(1) per peek(), next(), hasNext() +# Space: O(1) + +# Given an Iterator class interface with methods: next() and hasNext(), +# design and implement a PeekingIterator that support the peek() operation -- +# it essentially peek() at the element that will be returned by the next call to next(). +# +# Here is an example. Assume that the iterator is initialized to the beginning of +# the list: [1, 2, 3]. +# +# Call next() gets you 1, the first element in the list. +# +# Now you call peek() and it returns 2, the next element. Calling next() after that +# still return 2. +# +# You call next() the final time and it returns 3, the last element. Calling hasNext() +# after that should return false. +# + +# Below is the interface for Iterator, which is already defined for you. +# +# class Iterator(object): +# def __init__(self, nums): +# """ +# Initializes an iterator object to the beginning of a list. +# :type nums: List[int] +# """ +# +# def hasNext(self): +# """ +# Returns true if the iteration has more elements. +# :rtype: bool +# """ +# +# def next(self): +# """ +# Returns the next element in the iteration. +# :rtype: int +# """ + +class PeekingIterator(object): + def __init__(self, iterator): + """ + Initialize your data structure here. + :type iterator: Iterator + """ + self.iterator = iterator + self.val_ = None + self.has_next_ = iterator.hasNext() + self.has_peeked_ = False + + + def peek(self): + """ + Returns the next element in the iteration without advancing the iterator. + :rtype: int + """ + if not self.has_peeked_: + self.has_peeked_ = True + self.val_ = self.iterator.next() + return self.val_; + + def next(self): + """ + :rtype: int + """ + self.val_ = self.peek() + self.has_peeked_ = False + self.has_next_ = self.iterator.hasNext() + return self.val_; + + def hasNext(self): + """ + :rtype: bool + """ + return self.has_next_ + + +# Your PeekingIterator object will be instantiated and called as such: +# iter = PeekingIterator(Iterator(nums)) +# while iter.hasNext(): +# val = iter.peek() # Get the next element but not advance the iterator. +# iter.next() # Should return the same value as [val]. diff --git a/Python/perfect-number.py b/Python/perfect-number.py new file mode 100644 index 000000000..d8d9f30cc --- /dev/null +++ b/Python/perfect-number.py @@ -0,0 +1,28 @@ +# Time: O(sqrt(n)) +# Space: O(1) + +# We define the Perfect Number is a positive integer that is equal +# to the sum of all its positive divisors except itself. +# +# Now, given an integer n, write a function that returns true +# when it is a perfect number and false when it is not. +# Example: +# Input: 28 +# Output: True +# Explanation: 28 = 1 + 2 + 4 + 7 + 14 +# Note: The input number n will not exceed 100,000,000. (1e8) + +class Solution(object): + def checkPerfectNumber(self, num): + """ + :type num: int + :rtype: bool + """ + if num <= 0: + return False + + sqrt_num = int(num ** 0.5) + total = sum(i+num//i for i in xrange(1, sqrt_num+1) if num%i == 0) + if sqrt_num ** 2 == num: + total -= sqrt_num + return total - num == num diff --git a/Python/perfect-rectangle.py b/Python/perfect-rectangle.py new file mode 100644 index 000000000..3cd7e513b --- /dev/null +++ b/Python/perfect-rectangle.py @@ -0,0 +1,81 @@ +# Time: O(n) +# Space: O(n) + +# Given N axis-aligned rectangles where N > 0, +# determine if they all together form an exact cover of a rectangular region. +# +# Each rectangle is represented as a bottom-left point and a top-right point. +# For example, a unit square is represented as [1,1,2,2]. +# (coordinate of bottom-left point is (1, 1) and top-right point is (2, 2)). +# +# Example 1: +# +# rectangles = [ +# [1,1,3,3], +# [3,1,4,2], +# [3,2,4,4], +# [1,3,2,4], +# [2,3,3,4] +# ] +# +# Return true. All 5 rectangles together form an exact cover of a rectangular region. +# +# Example 2: +# +# rectangles = [ +# [1,1,2,3], +# [1,3,2,4], +# [3,1,4,2], +# [3,2,4,4] +# ] +# +# Return false. Because there is a gap between the two rectangular regions. +# +# Example 3: +# +# rectangles = [ +# [1,1,3,3], +# [3,1,4,2], +# [1,3,2,4], +# [3,2,4,4] +# ] +# +# Return false. Because there is a gap in the top center. +# +# Example 4: +# +# rectangles = [ +# [1,1,3,3], +# [3,1,4,2], +# [1,3,2,4], +# [2,2,4,4] +# ] +# +# Return false. Because two of the rectangles overlap with each other. + +from collections import defaultdict + +class Solution(object): + def isRectangleCover(self, rectangles): + """ + :type rectangles: List[List[int]] + :rtype: bool + """ + left = min(rec[0] for rec in rectangles) + bottom = min(rec[1] for rec in rectangles) + right = max(rec[2] for rec in rectangles) + top = max(rec[3] for rec in rectangles) + + points = defaultdict(int) + for l, b, r, t in rectangles: + for p, q in zip(((l, b), (r, b), (l, t), (r, t)), (1, 2, 4, 8)): + if points[p] & q: + return False + points[p] |= q + + for px, py in points: + if left < px < right or bottom < py < top: + if points[(px, py)] not in (3, 5, 10, 12, 15): + return False + + return True diff --git a/Python/perfect-squares.py b/Python/perfect-squares.py new file mode 100644 index 000000000..2201bc73b --- /dev/null +++ b/Python/perfect-squares.py @@ -0,0 +1,21 @@ +# Time: O(n * sqrt(n)) +# Space: O(n) +# +# Given a positive integer n, find the least number of perfect +# square numbers (for example, 1, 4, 9, 16, ...) which sum to n. +# +# For example, given n = 12, return 3 because 12 = 4 + 4 + 4; +# given n = 13, return 2 because 13 = 4 + 9. +# + +class Solution(object): + _num = [0] + def numSquares(self, n): + """ + :type n: int + :rtype: int + """ + num = self._num + while len(num) <= n: + num += min(num[-i*i] for i in xrange(1, int(len(num)**0.5+1))) + 1, + return num[n] diff --git a/Python/permutation-in-string.py b/Python/permutation-in-string.py new file mode 100644 index 000000000..338244159 --- /dev/null +++ b/Python/permutation-in-string.py @@ -0,0 +1,42 @@ +# Time: O(n) +# Space: O(1) + +# Given two strings s1 and s2, write a function to return true +# if s2 contains the permutation of s1. In other words, +# one of the first string's permutations is the substring of the second string. +# +# Example 1: +# Input:s1 = "ab" s2 = "eidbaooo" +# Output:True +# Explanation: s2 contains one permutation of s1 ("ba"). +# Example 2: +# Input:s1= "ab" s2 = "eidboaoo" +# Output: False +# Note: +# The input strings only contain lower case letters. +# The length of both given strings is in range [1, 10,000]. + +import collections + + +class Solution(object): + def checkInclusion(self, s1, s2): + """ + :type s1: str + :type s2: str + :rtype: bool + """ + counts = collections.Counter(s1) + l = len(s1) + for i in xrange(len(s2)): + if counts[s2[i]] > 0: + l -= 1 + counts[s2[i]] -= 1 + if l == 0: + return True + start = i + 1 - len(s1) + if start >= 0: + counts[s2[start]] += 1 + if counts[s2[start]] > 0: + l += 1 + return False diff --git a/Python/permutation-sequence.py b/Python/permutation-sequence.py index 2252f26ad..488f38d61 100644 --- a/Python/permutation-sequence.py +++ b/Python/permutation-sequence.py @@ -1,11 +1,12 @@ -# Time: O(n) -# Space: O(1) -# +from __future__ import print_function +# Time: O(n^2) +# Space: O(n) + # The set [1,2,3,...,n] contains a total of n! unique permutations. -# +# # By listing and labeling all of the permutations in order, # We get the following sequence (ie, for n = 3): -# +# # "123" # "132" # "213" @@ -13,16 +14,19 @@ # "312" # "321" # Given n and k, return the kth permutation sequence. -# -# Note: Given n will be between 1 and 9 inclusive. # +# Note: Given n will be between 1 and 9 inclusive. import math # Cantor ordering solution -class Solution: - # @return a string +class Solution(object): def getPermutation(self, n, k): + """ + :type n: int + :type k: int + :rtype: str + """ seq, k, fact = "", k - 1, math.factorial(n - 1) perm = [i for i in xrange(1, n + 1)] for i in reversed(xrange(n)): @@ -33,6 +37,7 @@ def getPermutation(self, n, k): k %= fact fact /= i return seq - + + if __name__ == "__main__": - print Solution().getPermutation(3, 2) + print(Solution().getPermutation(3, 2)) diff --git a/Python/permutations-ii.py b/Python/permutations-ii.py index 1f473145a..d6ee2f737 100644 --- a/Python/permutations-ii.py +++ b/Python/permutations-ii.py @@ -1,20 +1,45 @@ -# Time: O(n!) +from __future__ import print_function +# Time: O(n * n!) # Space: O(n) # # Given a collection of numbers that might contain duplicates, return all possible unique permutations. -# +# # For example, # [1,1,2] have the following unique permutations: # [1,1,2], [1,2,1], and [2,1,1]. # +class Solution(object): + def permuteUnique(self, nums): + """ + :type nums: List[int] + :rtype: List[List[int]] + """ + nums.sort() + result = [] + used = [False] * len(nums) + self.permuteUniqueRecu(result, used, [], nums) + return result + + def permuteUniqueRecu(self, result, used, cur, nums): + if len(cur) == len(nums): + result.append(cur + []) + return + for i in xrange(len(nums)): + if used[i] or (i > 0 and nums[i-1] == nums[i] and not used[i-1]): + continue + used[i] = True + cur.append(nums[i]) + self.permuteUniqueRecu(result, used, cur, nums) + cur.pop() + used[i] = False -class Solution: +class Solution2: # @param num, a list of integer # @return a list of lists of integers def permuteUnique(self, nums): solutions = [[]] - + for num in nums: next = [] for solution in solutions: @@ -22,13 +47,13 @@ def permuteUnique(self, nums): candidate = solution[:i] + [num] + solution[i:] if candidate not in next: next.append(candidate) - - solutions = next - + + solutions = next + return solutions if __name__ == "__main__": - print Solution().permuteUnique([1, 1, 2]) - print Solution().permuteUnique([1, -1, 1, 2, -1, 2, 2, -1]) + print(Solution().permuteUnique([1, 1, 2])) + print(Solution().permuteUnique([1, -1, 1, 2, -1, 2, 2, -1])) diff --git a/Python/permutations.py b/Python/permutations.py index f224894c8..5e3887f62 100644 --- a/Python/permutations.py +++ b/Python/permutations.py @@ -1,8 +1,9 @@ -# Time: O(n!) +from __future__ import print_function +# Time: O(n * n!) # Space: O(n) # # Given a collection of numbers, return all possible permutations. -# +# # For example, # [1,2,3] have the following permutations: # [1,2,3], [1,3,2], [2,1,3], [2,3,1], [3,1,2], and [3,2,1]. @@ -17,10 +18,10 @@ def permute(self, num): used = [False] * len(num) self.permuteRecu(result, used, [], num) return result - + def permuteRecu(self, result, used, cur, num): if len(cur) == len(num): - result.append(cur + []) + result.append(cur[:]) return for i in xrange(len(num)): if not used[i]: @@ -31,5 +32,5 @@ def permuteRecu(self, result, used, cur, num): used[i] = False if __name__ == "__main__": - print Solution().permute([1, 2, 3]) + print(Solution().permute([1, 2, 3])) diff --git a/Python/plus-one-linked-list.py b/Python/plus-one-linked-list.py new file mode 100644 index 000000000..faa54d5ee --- /dev/null +++ b/Python/plus-one-linked-list.py @@ -0,0 +1,67 @@ +# Time: O(n) +# Space: O(1) + +# Definition for singly-linked list. +class ListNode(object): + def __init__(self, x): + self.val = x + self.next = None + + +# Two pointers solution. +class Solution(object): + def plusOne(self, head): + """ + :type head: ListNode + :rtype: ListNode + """ + if not head: + return None + + dummy = ListNode(0) + dummy.next = head + + left, right = dummy, head + while right.next: + if right.val != 9: + left = right + right = right.next + + if right.val != 9: + right.val += 1 + else: + left.val += 1 + right = left.next + while right: + right.val = 0 + right = right.next + + return dummy if dummy.val else dummy.next + + +# Time: O(n) +# Space: O(1) +class Solution2(object): + def plusOne(self, head): + """ + :type head: ListNode + :rtype: ListNode + """ + def reverseList(head): + dummy = ListNode(0) + curr = head + while curr: + dummy.next, curr.next, curr = curr, dummy.next, curr.next + return dummy.next + + rev_head = reverseList(head) + curr, carry = rev_head, 1 + while curr and carry: + curr.val += carry + carry = curr.val / 10 + curr.val %= 10 + if carry and curr.next is None: + curr.next = ListNode(0) + curr = curr.next + + return reverseList(rev_head) diff --git a/Python/plus-one.py b/Python/plus-one.py index e704900ef..335dcaf53 100644 --- a/Python/plus-one.py +++ b/Python/plus-one.py @@ -1,25 +1,46 @@ +from __future__ import print_function # Time: O(n) # Space: O(1) -# -# Given a non-negative number represented as an array of digits, plus one to the number. -# + +# Given a non-negative integer represented as a non-empty array of digits, plus one to the integer. +# You may assume the integer do not contain any leading zero, except the number 0 itself. # The digits are stored such that the most significant digit is at the head of the list. -# -class Solution: - # @param digits, a list of integer digits - # @return a list of integer digits +# in-place solution +class Solution(object): def plusOne(self, digits): - carry = 1 + """ + :type digits: List[int] + :rtype: List[int] + """ for i in reversed(xrange(len(digits))): - digits[i] += carry - carry = digits[i] / 10 - digits[i] %= 10 - - if carry: - digits = [1] + digits - + if digits[i] == 9: + digits[i] = 0 + else: + digits[i] += 1 + return digits + digits[0] = 1 + digits.append(0) return digits + +# Time: O(n) +# Space: O(n) +class Solution2(object): + def plusOne(self, digits): + """ + :type digits: List[int] + :rtype: List[int] + """ + result = digits[::-1] + carry = 1 + for i in xrange(len(result)): + result[i] += carry + carry, result[i] = divmod(result[i], 10) + if carry: + result.append(carry) + return result[::-1] + + if __name__ == "__main__": - print Solution().plusOne([9, 9, 9, 9]) \ No newline at end of file + print(Solution().plusOne([9, 9, 9, 9])) diff --git a/Python/poor-pigs.py b/Python/poor-pigs.py new file mode 100644 index 000000000..5c33be03a --- /dev/null +++ b/Python/poor-pigs.py @@ -0,0 +1,29 @@ +# Time: O(1) +# Space: O(1) + +# There are 1000 buckets, one and only one of them contains poison, +# the rest are filled with water. They all look the same. +# If a pig drinks that poison it will die within 15 minutes. +# What is the minimum amount of pigs you need to figure out +# which bucket contains the poison within one hour. +# +# Answer this question, and write an algorithm for the follow-up general case. +# +# Follow-up: +# +# If there are n buckets and a pig drinking poison will die within m minutes, +# how many pigs (x) you need to figure out the "poison" bucket within p minutes? +# There is exact one bucket with poison. + +import math + + +class Solution(object): + def poorPigs(self, buckets, minutesToDie, minutesToTest): + """ + :type buckets: int + :type minutesToDie: int + :type minutesToTest: int + :rtype: int + """ + return int(math.ceil(math.log(buckets) / math.log(minutesToTest / minutesToDie + 1))) diff --git a/Python/populating-next-right-pointers-in-each-node-ii.py b/Python/populating-next-right-pointers-in-each-node-ii.py index e4f206e2f..d0d4049cb 100644 --- a/Python/populating-next-right-pointers-in-each-node-ii.py +++ b/Python/populating-next-right-pointers-in-each-node-ii.py @@ -1,12 +1,13 @@ +from __future__ import print_function # Time: O(n) # Space: O(1) # # Follow up for problem "Populating Next Right Pointers in Each Node". -# +# # What if the given tree could be any binary tree? Would your previous solution still work? -# +# # Note: -# +# # You may only use constant extra space. # For example, # Given the following binary tree, @@ -30,7 +31,7 @@ def __init__(self, x): self.left = None self.right = None self.next = None - + def __repr__(self): if self is None: return "Nil" @@ -50,27 +51,26 @@ def connect(self, root): next_head = cur.left elif cur.right: next_head = cur.right - + if cur.left: if prev: prev.next = cur.left prev = cur.left - + if cur.right: if prev: prev.next = cur.right prev = cur.right - + cur = cur.next head = next_head - - + + if __name__ == "__main__": root, root.left, root.right = TreeNode(1), TreeNode(2), TreeNode(3) root.left.left, root.left.right, root.right.right = TreeNode(4), TreeNode(5), TreeNode(7) Solution().connect(root) - print root - print root.left - print root.left.left - \ No newline at end of file + print(root) + print(root.left) + print(root.left.left) diff --git a/Python/populating-next-right-pointers-in-each-node.py b/Python/populating-next-right-pointers-in-each-node.py index 5223f28fb..a23163646 100644 --- a/Python/populating-next-right-pointers-in-each-node.py +++ b/Python/populating-next-right-pointers-in-each-node.py @@ -1,19 +1,20 @@ +from __future__ import print_function # Time: O(n) -# Space: O(logn) +# Space: O(1) # # Given a binary tree -# +# # struct TreeLinkNode { # TreeLinkNode *left; # TreeLinkNode *right; # TreeLinkNode *next; # } # Populate each next pointer to point to its next right node. If there is no next right node, the next pointer should be set to NULL. -# +# # Initially, all next pointers are set to NULL. -# +# # Note: -# +# # You may only use constant extra space. # You may assume that it is a perfect binary tree (ie, all leaves are at the same level, and every parent has two children). # For example, @@ -37,7 +38,7 @@ def __init__(self, x): self.left = None self.right = None self.next = None - + def __repr__(self): if self is None: return "Nil" @@ -45,6 +46,23 @@ def __repr__(self): return "{} -> {}".format(self.val, repr(self.next)) class Solution: + # @param root, a tree node + # @return nothing + def connect(self, root): + head = root + while head: + prev, cur, next_head = None, head, None + while cur and cur.left: + cur.left.next = cur.right + if cur.next: + cur.right.next = cur.next.left + cur = cur.next + head = head.left + +# Time: O(n) +# Space: O(logn) +# recusion +class Solution2: # @param root, a tree node # @return nothing def connect(self, root): @@ -61,7 +79,7 @@ def connect(self, root): root, root.left, root.right = TreeNode(1), TreeNode(2), TreeNode(3) root.left.left, root.left.right, root.right.left, root.right.right = TreeNode(4), TreeNode(5), TreeNode(6), TreeNode(7) Solution().connect(root) - print root - print root.left - print root.left.left - \ No newline at end of file + print(root) + print(root.left) + print(root.left.left) + diff --git a/Python/positions-of-large-groups.py b/Python/positions-of-large-groups.py new file mode 100644 index 000000000..3b01e950f --- /dev/null +++ b/Python/positions-of-large-groups.py @@ -0,0 +1,52 @@ +# Time: O(n) +# Space: O(1) + +# In a string S of lowercase letters, +# these letters form consecutive groups of the same character. +# +# For example, a string like S = "abbxxxxzyy" has +# the groups "a", "bb", "xxxx", "z" and "yy". +# +# Call a group large if it has 3 or more characters. +# We would like the starting and ending positions of every large group. +# +# The final answer should be in lexicographic order. +# +# Example 1: +# +# Input: "abbxxxxzzy" +# Output: [[3,6]] +# Explanation: "xxxx" is the single large group with starting 3 +# and ending positions 6. +# Example 2: +# +# Input: "abc" +# Output: [] +# Explanation: We have "a","b" and "c" but no large group. +# Example 3: +# +# Input: "abcdddeeeeaabbbcd" +# Output: [[3,5],[6,9],[12,14]] +# +# Note: 1 <= S.length <= 1000 + +try: + xrange # Python 2 +except NameError: + xrange = range # Python 3 + + +class Solution(object): + def largeGroupPositions(self, S): + """ + :type S: str + :rtype: List[List[int]] + """ + result = [] + i = 0 + for j in xrange(len(S)): + if j == len(S)-1 or S[j] != S[j+1]: + if j-i+1 >= 3: + result.append([i, j]) + i = j+1 + return result diff --git a/Python/pour-water.py b/Python/pour-water.py new file mode 100644 index 000000000..2b13679e8 --- /dev/null +++ b/Python/pour-water.py @@ -0,0 +1,141 @@ +# Time: O(v * n) +# Space: O(1) + +# We are given an elevation map, heights[i] representing the height of the terrain at that index. +# The width at each index is 1. After V units of water fall at index K, how much water is at each index? +# +# Water first drops at index K and rests on top of the highest terrain or water at that index. +# Then, it flows according to the following rules: +# +# If the droplet would eventually fall by moving left, then move left. +# Otherwise, if the droplet would eventually fall by moving right, then move right. +# Otherwise, rise at it's current position. +# Here, "eventually fall" means that the droplet will eventually be at a lower level +# if it moves in that direction. +# Also, "level" means the height of the terrain plus any water in that column. +# We can assume there's infinitely high terrain on the two sides out of bounds of the array. +# Also, there could not be partial water being spread out evenly on more than 1 grid block - +# each unit of water has to be in exactly one block. +# +# Example 1: +# Input: heights = [2,1,1,2,1,2,2], V = 4, K = 3 +# Output: [2,2,2,3,2,2,2] +# Explanation: +# # # +# # # +# ## # ### +# ######### +# 0123456 <- index +# +# The first drop of water lands at index K = 3: +# +# # # +# # w # +# ## # ### +# ######### +# 0123456 +# +# When moving left or right, the water can only move to the same level or a lower level. +# (By level, we mean the total height of the terrain plus any water in that column.) +# Since moving left will eventually make it fall, it moves left. +# (A droplet "made to fall" means go to a lower height than it was at previously.) +# +# # # +# # # +# ## w# ### +# ######### +# 0123456 +# +# Since moving left will not make it fall, it stays in place. The next droplet falls: +# +# # # +# # w # +# ## w# ### +# ######### +# 0123456 +# +# Since the new droplet moving left will eventually make it fall, it moves left. +# Notice that the droplet still preferred to move left, +# even though it could move right (and moving right makes it fall quicker.) +# +# # # +# # w # +# ## w# ### +# ######### +# 0123456 +# +# # # +# # # +# ##ww# ### +# ######### +# 0123456 +# +# After those steps, the third droplet falls. +# Since moving left would not eventually make it fall, it tries to move right. +# Since moving right would eventually make it fall, it moves right. +# +# # # +# # w # +# ##ww# ### +# ######### +# 0123456 +# +# # # +# # # +# ##ww#w### +# ######### +# 0123456 +# +# Finally, the fourth droplet falls. +# Since moving left would not eventually make it fall, it tries to move right. +# Since moving right would not eventually make it fall, it stays in place: +# +# # # +# # w # +# ##ww#w### +# ######### +# 0123456 +# +# The final answer is [2,2,2,3,2,2,2]: +# +# # +# ####### +# ####### +# 0123456 +# +# Example 2: +# Input: heights = [1,2,3,4], V = 2, K = 2 +# Output: [2,3,3,4] +# Explanation: +# The last droplet settles at index 1, +# since moving further left would not cause it to eventually fall to a lower height. +# +# Example 3: +# Input: heights = [3,1,3], V = 5, K = 1 +# Output: [4,4,4] +# +# Note: +# - heights will have length in [1, 100] and contain integers in [0, 99]. +# - V will be in range [0, 2000]. +# - K will be in range [0, heights.length - 1]. + +class Solution(object): + def pourWater(self, heights, V, K): + """ + :type heights: List[int] + :type V: int + :type K: int + :rtype: List[int] + """ + for _ in xrange(V): + best = K + for d in (-1, 1): + i = K + while 0 <= i+d < len(heights) and \ + heights[i+d] <= heights[i]: + if heights[i+d] < heights[i]: best = i+d + i += d + if best != K: + break + heights[best] += 1 + return heights diff --git a/Python/power-of-four.py b/Python/power-of-four.py new file mode 100644 index 000000000..74ca8217a --- /dev/null +++ b/Python/power-of-four.py @@ -0,0 +1,41 @@ +# Time: O(1) +# Space: O(1) + +# Given an integer (signed 32 bits), write a function to check whether it is a power of 4. +# +# Example: +# Given num = 16, return true. Given num = 5, return false. +# +# Follow up: Could you solve it without loops/recursion? + +class Solution(object): + def isPowerOfFour(self, num): + """ + :type num: int + :rtype: bool + """ + return num > 0 and (num & (num - 1)) == 0 and \ + ((num & 0b01010101010101010101010101010101) == num) + + +# Time: O(1) +# Space: O(1) +class Solution2(object): + def isPowerOfFour(self, num): + """ + :type num: int + :rtype: bool + """ + while num and not (num & 0b11): + num >>= 2 + return (num == 1) + + +class Solution3(object): + def isPowerOfFour(self, num): + """ + :type num: int + :rtype: bool + """ + num = bin(num) + return True if num[2:].startswith('1') and len(num[2:]) == num.count('0') and num.count('0') % 2 and '-' not in num else False diff --git a/Python/power-of-three.py b/Python/power-of-three.py new file mode 100644 index 000000000..e81d1873e --- /dev/null +++ b/Python/power-of-three.py @@ -0,0 +1,27 @@ +# Time: O(1) +# Space: O(1) + +# Given an integer, write a function to determine +# if it is a power of three. +# +# Follow up: +# Could you do it without using any loop / recursion? +import math + + +class Solution(object): + def __init__(self): + self.__max_log3 = int(math.log(0x7fffffff) / math.log(3)) + self.__max_pow3 = 3 ** self.__max_log3 + + def isPowerOfThree(self, n): + """ + :type n: int + :rtype: bool + """ + return n > 0 and self.__max_pow3 % n == 0 + + +class Solution2(object): + def isPowerOfThree(self, n): + return n > 0 and (math.log10(n)/math.log10(3)).is_integer() diff --git a/Python/power-of-two.py b/Python/power-of-two.py new file mode 100644 index 000000000..55e5bbf92 --- /dev/null +++ b/Python/power-of-two.py @@ -0,0 +1,18 @@ +# Time: O(1) +# Space: O(1) +# +# Given an integer, write a function to determine if it is a power of two. + + +class Solution: + # @param {integer} n + # @return {boolean} + def isPowerOfTwo(self, n): + return n > 0 and (n & (n - 1)) == 0 + + +class Solution2: + # @param {integer} n + # @return {boolean} + def isPowerOfTwo(self, n): + return n > 0 and (n & ~-n) == 0 diff --git a/Python/powx-n.py b/Python/powx-n.py index 02672df29..59711eb4a 100644 --- a/Python/powx-n.py +++ b/Python/powx-n.py @@ -1,28 +1,49 @@ -# Time: O(logn) -# Space: O(logn) -# +from __future__ import print_function +# Time: O(logn) = O(1) +# Space: O(1) + # Implement pow(x, n). -# -class Solution: - # @param x, a float - # @param n, a integer - # @return a float - def pow(self, x, n): - if n < 0: - return 1 / self.powRecu(x, -n) - - return self.powRecu(x, n) - - def powRecu(self, x, n): +# Iterative solution. +class Solution(object): + def myPow(self, x, n): + """ + :type x: float + :type n: int + :rtype: float + """ + result = 1 + abs_n = abs(n) + while abs_n: + if abs_n & 1: + result *= x + abs_n >>= 1 + x *= x + + return 1 / result if n < 0 else result + + +# Time: O(logn) +# Space: O(logn) +# Recursive solution. +class Solution2(object): + def myPow(self, x, n): + """ + :type x: float + :type n: int + :rtype: float + """ + if n < 0 and n != -n: + return 1.0 / self.myPow(x, -n) if n == 0: - return 1.0 - + return 1 + v = self.myPow(x, n / 2) if n % 2 == 0: - return self.powRecu(x * x, n / 2) + return v * v else: - return x * self.powRecu(x * x, n / 2) + return v * v * x + if __name__ == "__main__": - print Solution().pow(3, 5) - print Solution().pow(3, -5) \ No newline at end of file + print(Solution().pow(3, 5)) + print(Solution().pow(3, -5)) diff --git a/Python/predict-the-winner.py b/Python/predict-the-winner.py new file mode 100644 index 000000000..6c871ef7a --- /dev/null +++ b/Python/predict-the-winner.py @@ -0,0 +1,48 @@ +# Time: O(n^2) +# Space: O(n) + +# Given an array of scores that are non-negative integers. +# Player 1 picks one of the numbers from either end of the array +# followed by the player 2 and then player 1 and so on. +# Each time a player picks a number, that number will not be available for the next player. +# This continues until all the scores have been chosen. The player with the maximum score wins. +# +# Given an array of scores, predict whether player 1 is the winner. +# You can assume each player plays to maximize his score. +# +# Example 1: +# Input: [1, 5, 2] +# Output: False +# Explanation: Initially, player 1 can choose between 1 and 2. +# If he chooses 2 (or 1), then player 2 can choose from 1 (or 2) and 5. +# If player 2 chooses 5, then player 1 will be left with 1 (or 2). +# So, final score of player 1 is 1 + 2 = 3, and player 2 is 5. +# Hence, player 1 will never be the winner and you need to return False. +# Example 2: +# Input: [1, 5, 233, 7] +# Output: True +# Explanation: Player 1 first chooses 1. Then player 2 have to choose between 5 and 7. +# No matter which number player 2 choose, player 1 can choose 233. +# Finally, player 1 has more score (234) than player 2 (12), so you need to return True representing player1 can win. +# Note: +# 1 <= length of the array <= 20. +# Any scores in the given array are non-negative integers and will not exceed 10,000,000. +# If the scores of both players are equal, then player 1 is still the winner. + +class Solution(object): + def PredictTheWinner(self, nums): + """ + :type nums: List[int] + :rtype: bool + """ + if len(nums) % 2 == 0 or len(nums) == 1: + return True + + dp = [0] * len(nums); + for i in reversed(xrange(len(nums))): + dp[i] = nums[i] + for j in xrange(i+1, len(nums)): + dp[j] = max(nums[i] - dp[j], nums[j] - dp[j - 1]) + + return dp[-1] >= 0 + diff --git a/Python/prefix-and-suffix-search.py b/Python/prefix-and-suffix-search.py new file mode 100644 index 000000000..8c2eb8b23 --- /dev/null +++ b/Python/prefix-and-suffix-search.py @@ -0,0 +1,123 @@ +# Time: ctor: O(w * l^2), w is the number of words, l is the word length on average +# search: O(p + s) , p is the length of the prefix, s is the length of the suffix, +# Space: O(t), t is the number of trie nodes + +# Given many words, words[i] has weight i. +# +# Design a class WordFilter that supports one function, +# WordFilter.f(String prefix, String suffix). +# It will return the word with given prefix and suffix with maximum weight. +# If no word exists, return -1. +# +# Examples: +# Input: +# WordFilter(["apple"]) +# WordFilter.f("a", "e") // returns 0 +# WordFilter.f("b", "") // returns -1 +# Note: +# words has length in range [1, 15000]. +# For each test case, up to words.length queries WordFilter.f may be made. +# words[i] has length in range [1, 10]. +# prefix, suffix have lengths in range [0, 10]. +# words[i] and prefix, suffix queries consist of lowercase letters only. + +import collections + + +class WordFilter(object): + + def __init__(self, words): + """ + :type words: List[str] + """ + _trie = lambda: collections.defaultdict(_trie) + self.__trie = _trie() + + for weight, word in enumerate(words): + word += '#' + for i in xrange(len(word)): + cur = self.__trie + cur["_weight"] = weight + for j in xrange(i, 2*len(word)-1): + cur = cur[word[j%len(word)]] + cur["_weight"] = weight + + def f(self, prefix, suffix): + """ + :type prefix: str + :type suffix: str + :rtype: int + """ + cur = self.__trie + for letter in suffix + '#' + prefix: + if letter not in cur: + return -1 + cur = cur[letter] + return cur["_weight"] + + +# Time: ctor: O(w * l), w is the number of words, l is the word length on average +# search: O(p + s + max(m, n)), p is the length of the prefix, s is the length of the suffix, +# m is the number of the prefix match, n is the number of the suffix match +# Space: O(w * l) +class Trie(object): + + def __init__(self): + _trie = lambda: collections.defaultdict(_trie) + self.__trie = _trie() + + def insert(self, word, i): + def add_word(cur, i): + if "_words" not in cur: + cur["_words"] = [] + cur["_words"].append(i) + + cur = self.__trie + add_word(cur, i) + for c in word: + cur = cur[c] + add_word(cur, i) + + def find(self, word): + cur = self.__trie + for c in word: + if c not in cur: + return [] + cur = cur[c] + return cur["_words"] + + +class WordFilter2(object): + + def __init__(self, words): + """ + :type words: List[str] + """ + self.__prefix_trie = Trie() + self.__suffix_trie = Trie() + for i in reversed(xrange(len(words))): + self.__prefix_trie.insert(words[i], i) + self.__suffix_trie.insert(words[i][::-1], i) + + def f(self, prefix, suffix): + """ + :type prefix: str + :type suffix: str + :rtype: int + """ + prefix_match = self.__prefix_trie.find(prefix) + suffix_match = self.__suffix_trie.find(suffix[::-1]) + i, j = 0, 0 + while i != len(prefix_match) and j != len(suffix_match): + if prefix_match[i] == suffix_match[j]: + return prefix_match[i] + elif prefix_match[i] > suffix_match[j]: + i += 1 + else: + j += 1 + return -1 + + +# Your WordFilter object will be instantiated and called as such: +# obj = WordFilter(words) +# param_1 = obj.f(prefix,suffix) diff --git a/Python/preimage-size-of-factorial-zeroes-function.py b/Python/preimage-size-of-factorial-zeroes-function.py new file mode 100644 index 000000000..2d9849e88 --- /dev/null +++ b/Python/preimage-size-of-factorial-zeroes-function.py @@ -0,0 +1,44 @@ +# Time: O((logn)^2) +# Space: O(1) + +# Let f(x) be the number of zeroes at the end of x!. +# (Recall that x! = 1 * 2 * 3 * ... * x, and by convention, 0! = 1.) +# +# For example, f(3) = 0 because 3! = 6 has no zeroes at the end, +# while f(11) = 2 because 11! = 39916800 has 2 zeroes at the end. +# Given K, find how many non-negative integers x have the property that f(x) = K. +# +# Example 1: +# Input: K = 0 +# Output: 5 +# Explanation: 0!, 1!, 2!, 3!, and 4! end with K = 0 zeroes. +# +# Example 2: +# Input: K = 5 +# Output: 0 +# Explanation: There is no x such that x! ends in K = 5 zeroes. +# Note: +# - K will be an integer in the range [0, 10^9]. + +class Solution(object): + def preimageSizeFZF(self, K): + """ + :type K: int + :rtype: int + """ + def count_of_factorial_primes(n, p): + cnt = 0 + while n > 0: + cnt += n//p + n //= p + return cnt + + p = 5 + left, right = 0, p*K + while left <= right: + mid = left + (right-left)//2 + if count_of_factorial_primes(mid, p) >= K: + right = mid-1 + else: + left = mid+1 + return p if count_of_factorial_primes(left, p) == K else 0 diff --git a/Python/prime-number-of-set-bits-in-binary-representation.py b/Python/prime-number-of-set-bits-in-binary-representation.py new file mode 100644 index 000000000..f843f9714 --- /dev/null +++ b/Python/prime-number-of-set-bits-in-binary-representation.py @@ -0,0 +1,20 @@ +# Time: O(log(R - L)) = O(1) +# Space: O(1) + +class Solution(object): + def countPrimeSetBits(self, L, R): + """ + :type L: int + :type R: int + :rtype: int + """ + def bitCount(n): + result = 0 + while n: + n &= n-1 + result += 1 + return result + + primes = {2, 3, 5, 7, 11, 13, 17, 19} + return sum(bitCount(i) in primes + for i in xrange(L, R+1)) diff --git a/Python/prime-palindrome.py b/Python/prime-palindrome.py new file mode 100644 index 000000000..c93690413 --- /dev/null +++ b/Python/prime-palindrome.py @@ -0,0 +1,54 @@ +# Time: O(n^(3/2)) +# Space: O(logn) + +# Find the smallest prime palindrome greater than or equal to N. +# Recall that a number is prime if it's only divisors are 1 and itself, +# and it is greater than 1. +# +# For example, 2,3,5,7,11 and 13 are primes. +# +# Recall that a number is a palindrome if it reads the same from +# left to right as it does from right to left. +# +# For example, 12321 is a palindrome. +# +# Example 1: +# +# Input: 6 +# Output: 7 +# Example 2: +# +# Input: 8 +# Output: 11 +# Example 3: +# +# Input: 13 +# Output: 101 +# +# Note: +# - 1 <= N <= 10^8 +# - The answer is guaranteed to exist and be less than 2 * 10^8. + +try: + xrange # Python 2 +except NameError: + xrange = range # Python 3 + + +class Solution(object): + def primePalindrome(self, N): + """ + :type N: int + :rtype: int + """ + def is_prime(n): + if n < 2 or n % 2 == 0: + return n == 2 + return all(n % d for d in xrange(3, int(n**.5) + 1, 2)) + + if 8 <= N <= 11: + return 11 + for i in xrange(10**(len(str(N))//2), 10**5): + j = int(str(i) + str(i)[-2::-1]) + if j >= N and is_prime(j): + return j diff --git a/Python/print-binary-tree.py b/Python/print-binary-tree.py new file mode 100644 index 000000000..9d7fd5460 --- /dev/null +++ b/Python/print-binary-tree.py @@ -0,0 +1,87 @@ +# Time: O(h * 2^h) +# Space: O(h * 2^h) + +# Print a binary tree in an m*n 2D string array following these rules: +# +# The row number m should be equal to the height of the given binary tree. +# The column number n should always be an odd number. +# The root node's value (in string format) should be put in the exactly middle of the first row it can be put. +# The column and the row where the root node belongs will separate the rest space into two parts +# (left-bottom part and right-bottom part). You should print the left subtree in the left-bottom part and +# print the right subtree in the right-bottom part. The left-bottom part and the right-bottom part should have the same size. +# Even if one subtree is none while the other is not, you don't need to print anything for the none subtree +# but still need to leave the space as large as that for the other subtree. However, if two subtrees are none, +# then you don't need to leave space for both of them. +# Each unused space should contain an empty string "". +# Print the subtrees following the same rules. +# Example 1: +# Input: +# 1 +# / +# 2 +# Output: +# [["", "1", ""], +# ["2", "", ""]] +# Example 2: +# Input: +# 1 +# / \ +# 2 3 +# \ +# 4 +# Output: +# [["", "", "", "1", "", "", ""], +# ["", "2", "", "", "", "3", ""], +# ["", "", "4", "", "", "", ""]] +# Example 3: +# Input: +# 1 +# / \ +# 2 5 +# / +# 3 +# / +# 4 +# Output: +# +# [["", "", "", "", "", "", "", "1", "", "", "", "", "", "", ""] +# ["", "", "", "2", "", "", "", "", "", "", "", "5", "", "", ""] +# ["", "3", "", "", "", "", "", "", "", "", "", "", "", "", ""] +# ["4", "", "", "", "", "", "", "", "", "", "", "", "", "", ""]] +# Note: The height of binary tree is in the range of [1, 10]. +# +# Definition for a binary tree node. +# class TreeNode(object): +# def __init__(self, x): +# self.val = x +# self.left = None +# self.right = None + +class Solution(object): + def printTree(self, root): + """ + :type root: TreeNode + :rtype: List[List[str]] + """ + def getWidth(root): + if not root: + return 0 + return 2 * max(getWidth(root.left), getWidth(root.right)) + 1 + + def getHeight(root): + if not root: + return 0 + return max(getHeight(root.left), getHeight(root.right)) + 1 + + def preorderTraversal(root, level, left, right, result): + if not root: + return + mid = left + (right-left)/2 + result[level][mid] = str(root.val) + preorderTraversal(root.left, level+1, left, mid-1, result) + preorderTraversal(root.right, level+1, mid+1, right, result) + + h, w = getHeight(root), getWidth(root) + result = [[""] * w for _ in xrange(h)] + preorderTraversal(root, 0, 0, w-1, result) + return result diff --git a/Python/product-of-array-except-self.py b/Python/product-of-array-except-self.py new file mode 100644 index 000000000..02b3e1a83 --- /dev/null +++ b/Python/product-of-array-except-self.py @@ -0,0 +1,35 @@ +# Time: O(n) +# Space: O(1) +# +# Given an array of n integers where n > 1, nums, +# return an array output such that output[i] is equal to +# the product of all the elements of nums except nums[i]. +# +# Solve it without division and in O(n). +# +# For example, given [1,2,3,4], return [24,12,8,6]. +# +# +# Follow up: +# Could you solve it with constant space complexity? +# (Note: The output array does not count as extra space +# for the purpose of space complexity analysis.) +# + +class Solution: + # @param {integer[]} nums + # @return {integer[]} + def productExceptSelf(self, nums): + if not nums: + return [] + + left_product = [1 for _ in xrange(len(nums))] + for i in xrange(1, len(nums)): + left_product[i] = left_product[i - 1] * nums[i - 1] + + right_product = 1 + for i in xrange(len(nums) - 2, -1, -1): + right_product *= nums[i + 1] + left_product[i] = left_product[i] * right_product + + return left_product diff --git a/Python/profitable-schemes.py b/Python/profitable-schemes.py new file mode 100644 index 000000000..07d7b0e18 --- /dev/null +++ b/Python/profitable-schemes.py @@ -0,0 +1,57 @@ +# Time: O(n * p * g) +# Space: O(p * g) + +# There are G people in a gang, and a list of various crimes they could commit. +# +# The i-th crime generates a profit[i] and requires group[i] gang members to participate. +# +# If a gang member participates in one crime, +# that member can't participate in another crime. +# +# Let's call a profitable scheme any subset of these crimes that +# generates at least P profit, +# and the total number of gang members participating in that subset of crimes is at most G. +# +# How many schemes can be chosen? Since the answer may be very large, return it modulo 10^9 + 7. +# +# Example 1: +# +# Input: G = 5, P = 3, group = [2,2], profit = [2,3] +# Output: 2 +# Explanation: +# To make a profit of at least 3, the gang could either commit crimes 0 and 1, or just crime 1. +# In total, there are 2 schemes. +# Example 2: +# +# Input: G = 10, P = 5, group = [2,3,5], profit = [6,7,8] +# Output: 7 +# Explanation: +# To make a profit of at least 5, the gang could commit any crimes, as long as they commit one. +# There are 7 possible schemes: (0), (1), (2), (0,1), (0,2), (1,2), and (0,1,2). +# +# Note: +# - 1 <= G <= 100 +# - 0 <= P <= 100 +# - 1 <= group[i] <= 100 +# - 0 <= profit[i] <= 100 +# - 1 <= group.length = profit.length <= 100 + +import itertools + + +class Solution(object): + def profitableSchemes(self, G, P, group, profit): + """ + :type G: int + :type P: int + :type group: List[int] + :type profit: List[int] + :rtype: int + """ + dp = [[0 for _ in xrange(G+1)] for _ in xrange(P+1)] + dp[0][0] = 1 + for p, g in itertools.izip(profit, group): + for i in reversed(xrange(P+1)): + for j in reversed(xrange(G-g+1)): + dp[min(i+p, P)][j+g] += dp[i][j] + return sum(dp[P]) % (10**9 + 7) diff --git a/Python/push-dominoes.py b/Python/push-dominoes.py new file mode 100644 index 000000000..fee075b24 --- /dev/null +++ b/Python/push-dominoes.py @@ -0,0 +1,80 @@ +# Time: O(n) +# Space: O(n) + +# There are N dominoes in a line, and we place each domino vertically upright. +# +# In the beginning, +# we simultaneously push some of the dominoes either to the left or +# to the right. +# +# After each second, +# each domino that is falling to the left pushes the adjacent domino +# on the left. +# +# Similarly, the dominoes falling to the right push their adjacent dominoes +# standing on the right. +# +# When a vertical domino has dominoes falling on it from both sides, +# it stays still due to the balance of the forces. +# +# For the purposes of this question, +# we will consider that a falling domino expends no additional force to a +# falling or already fallen domino. +# +# Given a string "S" representing the initial state. S[i] = 'L', +# if the i-th domino has been pushed to the left; S[i] = 'R', +# if the i-th domino has been pushed to the right; S[i] = '.', +# if the i-th domino has not been pushed. +# +# Return a string representing the final state. +# +# Example 1: +# +# Input: ".L.R...LR..L.." +# Output: "LL.RR.LLRRLL.." +# Example 2: +# +# Input: "RR.L" +# Output: "RR.L" +# Explanation: The first domino expends no additional force +# on the second domino. +# Note: +# - 0 <= N <= 10^5 +# - String dominoes contains only 'L', 'R' and '.' + +try: + xrange # Python 2 +except NameError: + xrange = range # Python 3 + + +class Solution(object): + def pushDominoes(self, dominoes): + """ + :type dominoes: str + :rtype: str + """ + force = [0]*len(dominoes) + + f = 0 + for i in xrange(len(dominoes)): + if dominoes[i] == 'R': + f = len(dominoes) + elif dominoes[i] == 'L': + f = 0 + else: + f = max(f-1, 0) + force[i] += f + + f = 0 + for i in reversed(xrange(len(dominoes))): + if dominoes[i] == 'L': + f = len(dominoes) + elif dominoes[i] == 'R': + f = 0 + else: + f = max(f-1, 0) + force[i] -= f + + return "".join('.' if f == 0 else 'R' if f > 0 else 'L' + for f in force) diff --git a/Python/pyramid-transition-matrix.py b/Python/pyramid-transition-matrix.py new file mode 100644 index 000000000..6c3073b3d --- /dev/null +++ b/Python/pyramid-transition-matrix.py @@ -0,0 +1,73 @@ +# Time: O((a^(b+1)-a)/(a-1)) = O(a^b) , a is the size of allowed, +# b is the length of bottom +# Space: O((a^(b+1)-a)/(a-1)) = O(a^b) + +# We are stacking blocks to form a pyramid. Each block has a color which is a one letter string, like `'Z'`. +# +# For every block of color `C` we place not in the bottom row, +# we are placing it on top of a left block of color `A` and right block of color `B`. +# We are allowed to place the block there only if `(A, B, C)` is an allowed triple. +# +# We start with a bottom row of bottom, represented as a single string. +# We also start with a list of allowed triples allowed. +# Each allowed triple is represented as a string of length 3. +# +# Return true if we can build the pyramid all the way to the top, otherwise false. +# +# Example 1: +# Input: bottom = "XYZ", allowed = ["XYD", "YZE", "DEA", "FFF"] +# Output: true +# Explanation: +# We can stack the pyramid like this: +# A +# / \ +# D E +# / \ / \ +# X Y Z +# +# This works because ('X', 'Y', 'D'), ('Y', 'Z', 'E'), and ('D', 'E', 'A') are allowed triples. +# Example 1: +# Input: bottom = "XXYX", allowed = ["XXX", "XXY", "XYX", "XYY", "YXZ"] +# Output: false +# Explanation: +# We can't stack the pyramid to the top. +# Note that there could be allowed triples (A, B, C) and (A, B, D) with C != D. +# +# Note: +# bottom will be a string with length in range [2, 100]. +# allowed will have length in range [0, 350]. +# Letters in all strings will be chosen from the set {'A', 'B', 'C', 'D', 'E', 'F', 'G'}. + +# dfs solution +class Solution(object): + def pyramidTransition(self, bottom, allowed): + """ + :type bottom: str + :type allowed: List[str] + :rtype: bool + """ + def pyramidTransitionHelper(bottom, edges, lookup): + def dfs(bottom, edges, new_bottom, idx, lookup): + if idx == len(bottom)-1: + return pyramidTransitionHelper("".join(new_bottom), edges, lookup) + for i in edges[ord(bottom[idx])-ord('A')][ord(bottom[idx+1])-ord('A')]: + new_bottom[idx] = chr(i+ord('A')); + if dfs(bottom, edges, new_bottom, idx+1, lookup): + return True + return False + + if len(bottom) == 1: + return True + if bottom in lookup: + return False + lookup.add(bottom) + for i in xrange(len(bottom)-1): + if not edges[ord(bottom[i])-ord('A')][ord(bottom[i+1])-ord('A')]: + return False + new_bottom = ['A']*(len(bottom)-1) + return dfs(bottom, edges, new_bottom, 0, lookup) + + edges = [[[] for _ in xrange(7)] for _ in xrange(7)] + for s in allowed: + edges[ord(s[0])-ord('A')][ord(s[1])-ord('A')].append(ord(s[2])-ord('A')) + return pyramidTransitionHelper(bottom, edges, set()) diff --git a/Python/quad-tree-intersection.py b/Python/quad-tree-intersection.py new file mode 100644 index 000000000..291e96b0a --- /dev/null +++ b/Python/quad-tree-intersection.py @@ -0,0 +1,102 @@ +# Time: O(n) +# Space: O(h) + +# A quadtree is a tree data in which each internal node has exactly four children: +# topLeft, topRight, bottomLeft and bottomRight. +# Quad trees are often used to partition a two-dimensional space by +# recursively subdividing it into four quadrants or regions. +# +# We want to store True/False information in our quad tree. +# The quad tree is used to represent a N * N boolean grid. For each node, +# it will be subdivided into four children nodes until the values in the region +# it represents are all the same. Each node has another two boolean attributes : +# isLeaf and val. isLeaf is true if and only if the node is a leaf node. +# The val attribute for a leaf node contains the value of the region it represents. +# +# For example, below are two quad trees A and B: +# +# A: +# +-------+-------+ T: true +# | | | F: false +# | T | T | +# | | | +# +-------+-------+ +# | | | +# | F | F | +# | | | +# +-------+-------+ +# topLeft: T +# topRight: T +# bottomLeft: F +# bottomRight: F +# +# B: +# +-------+---+---+ +# | | F | F | +# | T +---+---+ +# | | T | T | +# +-------+---+---+ +# | | | +# | T | F | +# | | | +# +-------+-------+ +# topLeft: T +# topRight: +# topLeft: F +# topRight: F +# bottomLeft: T +# bottomRight: T +# bottomLeft: T +# bottomRight: F +# +# Your task is to implement a function that will take two quadtrees and +# return a quadtree that represents the logical OR (or union) of the two trees. +# +# A: B: C (A or B): +# +-------+-------+ +-------+---+---+ +-------+-------+ +# | | | | | F | F | | | | +# | T | T | | T +---+---+ | T | T | +# | | | | | T | T | | | | +# +-------+-------+ +-------+---+---+ +-------+-------+ +# | | | | | | | | | +# | F | F | | T | F | | T | F | +# | | | | | | | | | +# +-------+-------+ +-------+-------+ +-------+-------+ +# +# Note: +# - N is guaranteened to be a power of 2. +# - If you want to know more about the quad tree, you can refer to its wiki. +# - The logic OR operation is define as this: "A or B" is true if A is true, +# or if B is true, or if both A and B are true. + +# Definition for a QuadTree node. +class Node(object): + def __init__(self, val, isLeaf, topLeft, topRight, bottomLeft, bottomRight): + self.val = val + self.isLeaf = isLeaf + self.topLeft = topLeft + self.topRight = topRight + self.bottomLeft = bottomLeft + self.bottomRight = bottomRight + + +class Solution(object): + def intersect(self, quadTree1, quadTree2): + """ + :type quadTree1: Node + :type quadTree2: Node + :rtype: Node + """ + if quadTree1.isLeaf: + return quadTree1 if quadTree1.val else quadTree2 + elif quadTree2.isLeaf: + return quadTree2 if quadTree2.val else quadTree1 + topLeftNode = self.intersect(quadTree1.topLeft, quadTree2.topLeft) + topRightNode = self.intersect(quadTree1.topRight, quadTree2.topRight) + bottomLeftNode = self.intersect(quadTree1.bottomLeft, quadTree2.bottomLeft) + bottomRightNode = self.intersect(quadTree1.bottomRight, quadTree2.bottomRight) + if topLeftNode.isLeaf and topRightNode.isLeaf and \ + bottomLeftNode.isLeaf and bottomRightNode.isLeaf and \ + topLeftNode.val == topRightNode.val == bottomLeftNode.val == bottomRightNode.val: + return Node(topLeftNode.val, True, None, None, None, None) + return Node(True, False, topLeftNode, topRightNode, bottomLeftNode, bottomRightNode) diff --git a/Python/queue-reconstruction-by-height.py b/Python/queue-reconstruction-by-height.py new file mode 100644 index 000000000..844da51c4 --- /dev/null +++ b/Python/queue-reconstruction-by-height.py @@ -0,0 +1,58 @@ +# Time: O(n * sqrt(n)) +# Space: O(n) + +# Suppose you have a random list of people standing in a queue. +# Each person is described by a pair of integers (h, k), +# where h is the height of the person and k is the number of people +# in front of this person who have a height greater than or equal to h. +# Write an algorithm to reconstruct the queue. +# +# Note: +# The number of people is less than 1,100. +# +# Example +# +# Input: +# [[7,0], [4,4], [7,1], [5,0], [6,1], [5,2]] +# +# Output: +# [[5,0], [7,0], [5,2], [6,1], [4,4], [7,1]] + +class Solution(object): + def reconstructQueue(self, people): + """ + :type people: List[List[int]] + :rtype: List[List[int]] + """ + people.sort(key=lambda h_k: (-h_k[0], h_k[1])) + + blocks = [[]] + for p in people: + index = p[1] + + for i, block in enumerate(blocks): + if index <= len(block): + break + index -= len(block) + block.insert(index, p) + + if len(block) * len(block) > len(people): + blocks.insert(i+1, block[len(block)/2:]) + del block[len(block)/2:] + + return [p for block in blocks for p in block] + + +# Time: O(n^2) +# Space: O(n) +class Solution2(object): + def reconstructQueue(self, people): + """ + :type people: List[List[int]] + :rtype: List[List[int]] + """ + people.sort(key=lambda h_k1: (-h_k1[0], h_k1[1])) + result = [] + for p in people: + result.insert(p[1], p) + return result diff --git a/Python/rabbits-in-forest.py b/Python/rabbits-in-forest.py new file mode 100644 index 000000000..bd92a73b9 --- /dev/null +++ b/Python/rabbits-in-forest.py @@ -0,0 +1,40 @@ +# Time: O(n) +# Space: O(n) + +# In a forest, each rabbit has some color. +# Some subset of rabbits (possibly all of them) tell you how many other rabbits have the same color as them. +# Those answers are placed in an array. +# +# Return the minimum number of rabbits that could be in the forest. +# +# Examples: +# Input: answers = [1, 1, 2] +# Output: 5 +# Explanation: +# The two rabbits that answered "1" could both be the same color, say red. +# The rabbit than answered "2" can't be red or the answers would be inconsistent. +# Say the rabbit that answered "2" was blue. +# Then there should be 2 other blue rabbits in the forest that didn't answer into the array. +# The smallest possible number of rabbits in the forest is therefore 5: 3 that answered plus 2 that didn't. +# +# Input: answers = [10, 10, 10] +# Output: 11 +# +# Input: answers = [] +# Output: 0 +# +# Note: +# - answers will have length at most 1000. +# - Each answers[i] will be an integer in the range [0, 999]. + +import collections + + +class Solution(object): + def numRabbits(self, answers): + """ + :type answers: List[int] + :rtype: int + """ + count = collections.Counter(answers) + return sum((((k+1)+v-1)//(k+1))*(k+1) for k, v in count.iteritems()) diff --git a/Python/race-car.py b/Python/race-car.py new file mode 100644 index 000000000..b59aa946e --- /dev/null +++ b/Python/race-car.py @@ -0,0 +1,74 @@ +# Time : O(nlogn), n is the value of the target +# Space: O(n) + +# Your car starts at position 0 and speed +1 on an infinite number line. +# (Your car can go into negative positions.) +# +# Your car drives automatically according to a sequence of +# instructions A (accelerate) and R (reverse). +# +# When you get an instruction "A", your car does the following: +# position += speed, speed *= 2. +# +# When you get an instruction "R", your car does the following: +# if your speed is positive then speed = -1 , otherwise speed = 1. +# (Your position stays the same.) +# +# For example, after commands "AAR", your car goes to +# positions 0->1->3->3, and your speed goes to 1->2->4->-1. +# +# Now for some target position, say the length of the shortest +# sequence of instructions to get there. +# +# Example 1: +# Input: +# target = 3 +# Output: 2 +# Explanation: +# The shortest instruction sequence is "AA". +# Your position goes from 0->1->3. +# Example 2: +# Input: +# target = 6 +# Output: 5 +# Explanation: +# The shortest instruction sequence is "AAARA". +# Your position goes from 0->1->3->7->7->6. +# +# Note: +# - 1 <= target <= 10000. + +try: + xrange # Python 2 +except NameError: + xrange = range # Python 3 + + +class Solution(object): + def racecar(self, target): + dp = [0] * (target+1) + for i in xrange(1, target+1): + # 2^(k-1) <= i < 2^k + k = i.bit_length() + + # case 1. drive exactly i at best + # seq(i) = A^k + if i == 2**k-1: + dp[i] = k + continue + + # case 2. drive cross i at 2^k-1, and turn back to i + # seq(i) = A^k -> R -> seq(2^k-1 - i) + dp[i] = k+1 + dp[2**k-1 - i] + + # case 3. drive less then 2^k-1, and turn back some distance, + # and turn back again to make the direction is the same + # seq(i) = shortest(seq(i), A^(k-1) -> R -> A^j -> R -> + # seq(i - (2^(k-1)-1) + (2^j-1)), + # where 0 <= j < k-1) + # => dp[i] = min(dp[i], (k-1) + 1 + j + 1 + + # dp[i - (2**(k-1)-1) + (2**j-1)]) + for j in xrange(k-1): + dp[i] = min(dp[i], k+j+1 + dp[i - 2**(k-1) + 2**j]) + + return dp[-1] diff --git a/Python/random-flip-matrix.py b/Python/random-flip-matrix.py new file mode 100644 index 000000000..9b2163c00 --- /dev/null +++ b/Python/random-flip-matrix.py @@ -0,0 +1,78 @@ +# Time: ctor: O(1) +# flip: O(1) +# reset: O(min(f, r * c)) +# Space: O(min(f, r * c)) + +# You are given the number of rows n_rows and +# number of columns n_cols of a 2D binary matrix +# where all values are initially 0. +# Write a function flip which chooses a 0 value uniformly at random, +# changes it to 1, and then returns the position [row.id, col.id] of +# that value. Also, write a function reset which sets all values back to 0. +# Try to minimize the number of calls to system's Math.random() +# and optimize the time and space complexity. +# +# Note: +# - 1 <= n_rows, n_cols <= 10000 +# - 0 <= row.id < n_rows and 0 <= col.id < n_cols +# - flip will not be called when the matrix has no 0 values left. +# - the total number of calls to flip and reset will not exceed 1000. +# Example 1: +# +# Input: +# ["Solution","flip","flip","flip","flip"] +# [[2,3],[],[],[],[]] +# Output: [null,[0,1],[1,2],[1,0],[1,1]] +# Example 2: +# +# Input: +# ["Solution","flip","flip","reset","flip"] +# [[1,2],[],[],[],[]] +# Output: [null,[0,0],[0,1],null,[0,0]] +# Explanation of Input Syntax: +# +# The input is two lists: +# the subroutines called and their arguments. +# Solution's constructor has two arguments, n_rows and n_cols. +# flip and reset have no arguments. +# Arguments are always wrapped with a list, even if there aren't any. + +import random + + +class Solution(object): + + def __init__(self, n_rows, n_cols): + """ + :type n_rows: int + :type n_cols: int + """ + self.__n_rows = n_rows + self.__n_cols = n_cols + self.__n = n_rows*n_cols + self.__lookup = {} + + + def flip(self): + """ + :rtype: List[int] + """ + self.__n -= 1 + target = random.randint(0, self.__n) + x = self.__lookup.get(target, target) + self.__lookup[target] = self.__lookup.get(self.__n, self.__n) + return divmod(x, self.__n_cols) + + + def reset(self): + """ + :rtype: void + """ + self.__n = self.__n_rows*self.__n_cols + self.__lookup = {} + + +# Your Solution object will be instantiated and called as such: +# obj = Solution(n_rows, n_cols) +# param_1 = obj.flip() +# obj.reset() diff --git a/Python/random-pick-index.py b/Python/random-pick-index.py new file mode 100644 index 000000000..b812c932e --- /dev/null +++ b/Python/random-pick-index.py @@ -0,0 +1,53 @@ +# Time: O(n) +# Space: O(1) + +# Given an array of integers with possible duplicates, +# randomly output the index of a given target number. +# You can assume that the given target number must exist in the array. +# +# Note: +# The array size can be very large. +# Solution that uses too much extra space will not pass the judge. +# +# Example: +# +# int[] nums = new int[] {1,2,3,3,3}; +# Solution solution = new Solution(nums); +# +# // pick(3) should return either index 2, 3, or 4 randomly. +# Each index should have equal probability of returning. +# solution.pick(3); +# +# // pick(1) should return 0. Since in the array only nums[0] is equal to 1. +# solution.pick(1); + +from random import randint + +class Solution(object): + + def __init__(self, nums): + """ + + :type nums: List[int] + :type numsSize: int + """ + self.__nums = nums + + def pick(self, target): + """ + :type target: int + :rtype: int + """ + reservoir = -1 + n = 0 + for i in xrange(len(self.__nums)): + if self.__nums[i] != target: + continue + reservoir = i if randint(1, n+1) == 1 else reservoir + n += 1 + return reservoir + + +# Your Solution object will be instantiated and called as such: +# obj = Solution(nums) +# param_1 = obj.pick(target) diff --git a/Python/random-pick-with-blacklist.py b/Python/random-pick-with-blacklist.py new file mode 100644 index 000000000..f1a270548 --- /dev/null +++ b/Python/random-pick-with-blacklist.py @@ -0,0 +1,108 @@ +# Time: ctor: O(b) +# pick: O(1) +# Space: O(b) + +# Given a blacklist B containing unique integers from [0, N), +# write a function to return a uniform random integer from +# [0, N) which is NOT in B. +# +# Optimize it such that it minimizes the call to system’s Math.random(). +# +# Note: +# +# 1 <= N <= 1000000000 +# 0 <= B.length < min(100000, N) +# [0, N) does NOT include N. See interval notation. +# Example 1: +# +# Input: +# ["Solution","pick","pick","pick"] +# [[1,[]],[],[],[]] +# Output: [null,0,0,0] +# Example 2: +# +# Input: +# ["Solution","pick","pick","pick"] +# [[2,[]],[],[],[]] +# Output: [null,1,1,1] +# Example 3: +# +# Input: +# ["Solution","pick","pick","pick"] +# [[3,[1]],[],[],[]] +# Output: [null,0,0,2] +# Example 4: +# +# Input: +# ["Solution","pick","pick","pick"] +# [[4,[2]],[],[],[]] +# Output: [null,1,3,1] +# Explanation of Input Syntax: +# +# The input is two lists: the subroutines called and +# their arguments. Solution's constructor has two arguments, +# N and the blacklist B. +# pick has no arguments. +# Arguments are always wrapped with a list, +# even if there aren't any. + +import random + + +class Solution(object): + + def __init__(self, N, blacklist): + """ + :type N: int + :type blacklist: List[int] + """ + self.__n = N-len(blacklist) + self.__lookup = {} + white = iter(set(range(self.__n, N))-set(blacklist)) + for black in blacklist: + if black < self.__n: + self.__lookup[black] = next(white) + + + def pick(self): + """ + :rtype: int + """ + index = random.randint(0, self.__n-1) + return self.__lookup[index] if index in self.__lookup else index + + +# Time: ctor: O(blogb) +# pick: O(logb) +# Space: O(b) +import random + +class Solution2(object): + + def __init__(self, N, blacklist): + """ + :type N: int + :type blacklist: List[int] + """ + self.__n = N-len(blacklist) + blacklist.sort() + self.__blacklist = blacklist + + def pick(self): + """ + :rtype: int + """ + index = random.randint(0, self.__n-1) + left, right = 0, len(self.__blacklist)-1 + while left <= right: + mid = left+(right-left)//2 + if index+mid < self.__blacklist[mid]: + right = mid-1 + else: + left = mid+1 + return index+left + + +# Your Solution object will be instantiated and called as such: +# obj = Solution(N, blacklist) +# param_1 = obj.pick() diff --git a/Python/random-pick-with-weight.py b/Python/random-pick-with-weight.py new file mode 100644 index 000000000..3b6a818f4 --- /dev/null +++ b/Python/random-pick-with-weight.py @@ -0,0 +1,56 @@ +# Time: ctor: O(n) +# pickIndex: O(logn) +# Space: O(n) + +# Given an array w of positive integers, +# where w[i] describes the weight of index i, +# write a function pickIndex which randomly picks an index in proportion to its weight. +# +# Note: +# +# 1 <= w.length <= 10000 +# 1 <= w[i] <= 10^5 +# pickIndex will be called at most 10000 times. +# Example 1: +# +# Input: +# ["Solution","pickIndex"] +# [[[1]],[]] +# Output: [null,0] +# Example 2: +# +# Input: +# ["Solution","pickIndex","pickIndex","pickIndex","pickIndex","pickIndex"] +# [[[1,3]],[],[],[],[],[]] +# Output: [null,0,1,1,1,0] +# Explanation of Input Syntax: +# +# The input is two lists: the subroutines called and their arguments. +# Solution's constructor has one argument, the array w. pickIndex has no arguments. +# Arguments are always wrapped with a list, even if there aren't any. + +import random +import bisect + + +class Solution(object): + + def __init__(self, w): + """ + :type w: List[int] + """ + self.__prefix_sum = list(w) + for i in xrange(1, len(w)): + self.__prefix_sum[i] += self.__prefix_sum[i-1] + + def pickIndex(self): + """ + :rtype: int + """ + target = random.randint(0, self.__prefix_sum[-1]-1) + return bisect.bisect_right(self.__prefix_sum, target) + + +# Your Solution object will be instantiated and called as such: +# obj = Solution(w) +# param_1 = obj.pickIndex() diff --git a/Python/random-point-in-non-overlapping-rectangles.py b/Python/random-point-in-non-overlapping-rectangles.py new file mode 100644 index 000000000..0e0c5d4b2 --- /dev/null +++ b/Python/random-point-in-non-overlapping-rectangles.py @@ -0,0 +1,70 @@ +# Time: ctor: O(n) +# pick: O(logn) +# Space: O(n) + +# Given a list of non-overlapping axis-aligned rectangles rects, +# write a function pick which randomly and uniformily picks +# an integer point in the space covered by the rectangles. +# +# Note: +# - An integer point is a point that has integer coordinates. +# - A point on the perimeter of a rectangle is included in the space covered by the rectangles. +# - ith rectangle = rects[i] = [x1,y1,x2,y2], +# where [x1, y1] are the integer coordinates of the bottom-left corner, +# and [x2, y2] are the integer coordinates of the top-right corner. +# - length and width of each rectangle does not exceed 2000. +# - 1 <= rects.length <= 100 +# - pick return a point as an array of integer coordinates [p_x, p_y] +# - pick is called at most 10000 times. +# +# Example 1: +# +# Input: +# ["Solution","pick","pick","pick"] +# [[[[1,1,5,5]]],[],[],[]] +# Output: +# [null,[4,1],[4,1],[3,3]] +# Example 2: +# +# Input: +# ["Solution","pick","pick","pick","pick","pick"] +# [[[[-2,-2,-1,-1],[1,0,3,0]]],[],[],[],[],[]] +# Output: +# [null,[-1,-2],[2,0],[-2,-1],[3,0],[-2,-2]] +# Explanation of Input Syntax: +# +# The input is two lists: the subroutines called and their arguments. +# Solution's constructor has one argument, +# the array of rectangles rects. pick has no arguments. +# Arguments are always wrapped with a list, even if there aren't any. + +import random +import bisect + + +class Solution(object): + + def __init__(self, rects): + """ + :type rects: List[List[int]] + """ + self.__rects = list(rects) + self.__prefix_sum = map(lambda x : (x[2]-x[0]+1)*(x[3]-x[1]+1), rects) + for i in xrange(1, len(self.__prefix_sum)): + self.__prefix_sum[i] += self.__prefix_sum[i-1] + + def pick(self): + """ + :rtype: List[int] + """ + target = random.randint(0, self.__prefix_sum[-1]-1) + left = bisect.bisect_right(self.__prefix_sum, target) + rect = self.__rects[left] + width, height = rect[2]-rect[0]+1, rect[3]-rect[1]+1 + base = self.__prefix_sum[left]-width*height + return [rect[0]+(target-base)%width, rect[1]+(target-base)//width] + + +# Your Solution object will be instantiated and called as such: +# obj = Solution(rects) +# param_1 = obj.pick() diff --git a/Python/range-addition-ii.py b/Python/range-addition-ii.py new file mode 100644 index 000000000..641bff3db --- /dev/null +++ b/Python/range-addition-ii.py @@ -0,0 +1,51 @@ +# Time: O(p), p is the number of ops +# Space: O(1) + +# Given an m * n matrix M initialized with all 0's and several update operations. +# +# Operations are represented by a 2D array, +# and each operation is represented by an array with two positive integers a and b, +# which means M[i][j] should be added by one for all 0 <= i < a and 0 <= j < b. +# +# You need to count and return the number of maximum integers +# in the matrix after performing all the operations. +# +# Example 1: +# Input: +# m = 3, n = 3 +# operations = [[2,2],[3,3]] +# Output: 4 +# Explanation: +# Initially, M = +# [[0, 0, 0], +# [0, 0, 0], +# [0, 0, 0]] +# +# After performing [2,2], M = +# [[1, 1, 0], +# [1, 1, 0], +# [0, 0, 0]] +# +# After performing [3,3], M = +# [[2, 2, 1], +# [2, 2, 1], +# [1, 1, 1]] +# +# So the maximum integer in M is 2, and there are four of it in M. So return 4. +# Note: +# The range of m and n is [1,40000]. +# The range of a is [1,m], and the range of b is [1,n]. +# The range of operations size won't exceed 10,000. + +class Solution(object): + def maxCount(self, m, n, ops): + """ + :type m: int + :type n: int + :type ops: List[List[int]] + :rtype: int + """ + for op in ops: + m = min(m, op[0]) + n = min(n, op[1]) + return m*n diff --git a/Python/range-addition.py b/Python/range-addition.py new file mode 100644 index 000000000..bc9c6fcaf --- /dev/null +++ b/Python/range-addition.py @@ -0,0 +1,20 @@ +# Time: O(k + n) +# Space: O(1) + +class Solution(object): + def getModifiedArray(self, length, updates): + """ + :type length: int + :type updates: List[List[int]] + :rtype: List[int] + """ + result = [0] * length + for update in updates: + result[update[0]] += update[2] + if update[1]+1 < length: + result[update[1]+1] -= update[2] + + for i in xrange(1, length): + result[i] += result[i-1] + + return result diff --git a/Python/range-module.py b/Python/range-module.py new file mode 100644 index 000000000..f5a2adbb0 --- /dev/null +++ b/Python/range-module.py @@ -0,0 +1,98 @@ +# Time: addRange: O(n) +# removeRange: O(n) +# queryRange: O(logn) +# Space: O(n) + +# A Range Module is a module that tracks ranges of numbers. +# Your task is to design and implement the following interfaces in an efficient manner. +# - addRange(int left, int right) Adds the half-open interval [left, right), +# tracking every real number in that interval. +# Adding an interval that partially overlaps with currently tracked numbers should +# add any numbers in the interval [left, right) that are not already tracked. +# - queryRange(int left, int right) Returns true if and only if +# every real number in the interval [left, right) is currently being tracked. +# - removeRange(int left, int right) Stops tracking every real number currently being tracked +# in the interval [left, right). +# +# Example 1: +# addRange(10, 20): null +# removeRange(14, 16): null +# queryRange(10, 14): true (Every number in [10, 14) is being tracked) +# queryRange(13, 15): false (Numbers like 14, 14.03, 14.17 in [13, 15) are not being tracked) +# queryRange(16, 17): true (The number 16 in [16, 17) is still being tracked, despite the remove operation) +# +# Note: +# - A half open interval [left, right) denotes all real numbers left <= x < right. +# - 0 < left < right < 10^9 in all calls to addRange, queryRange, removeRange. +# - The total number of calls to addRange in a single test case is at most 1000. +# - The total number of calls to queryRange in a single test case is at most 5000. +# - The total number of calls to removeRange in a single test case is at most 1000. + +import bisect + + +class RangeModule(object): + + def __init__(self): + self.__intervals = [] + + def addRange(self, left, right): + """ + :type left: int + :type right: int + :rtype: void + """ + tmp = [] + i = 0 + for interval in self.__intervals: + if right < interval[0]: + tmp.append((left, right)) + break + elif interval[1] < left: + tmp.append(interval); + else: + left = min(left, interval[0]) + right = max(right, interval[1]) + i += 1 + if i == len(self.__intervals): + tmp.append((left, right)) + while i < len(self.__intervals): + tmp.append(self.__intervals[i]) + i += 1 + self.__intervals = tmp + + def queryRange(self, left, right): + """ + :type left: int + :type right: int + :rtype: bool + """ + i = bisect.bisect_left(self.__intervals, (left, float("inf"))) + if i: i -= 1 + return bool(self.__intervals) and \ + self.__intervals[i][0] <= left and \ + right <= self.__intervals[i][1] + + def removeRange(self, left, right): + """ + :type left: int + :type right: int + :rtype: void + """ + tmp = [] + for interval in self.__intervals: + if interval[1] <= left or interval[0] >= right: + tmp.append(interval) + else: + if interval[0] < left: + tmp.append((interval[0], left)) + if right < interval[1]: + tmp.append((right, interval[1])) + self.__intervals = tmp + + +# Your RangeModule object will be instantiated and called as such: +# obj = RangeModule() +# obj.addRange(left,right) +# param_2 = obj.queryRange(left,right) +# obj.removeRange(left,right) diff --git a/Python/range-sum-query-2d-immutable.py b/Python/range-sum-query-2d-immutable.py new file mode 100644 index 000000000..91e444fd6 --- /dev/null +++ b/Python/range-sum-query-2d-immutable.py @@ -0,0 +1,65 @@ +# Time: ctor: O(m * n), +# lookup: O(1) +# Space: O(m * n) +# +# Given a 2D matrix matrix, find the sum of the elements inside +# the rectangle defined by its upper left corner (row1, col1) +# and lower right corner (row2, col2). +# +# Range Sum Query 2D +# The above rectangle (with the red border) is defined by +# (row1, col1) = (2, 1) and (row2, col2) = (4, 3), +# which contains sum = 8. +# +# Example: +# Given matrix = [ +# [3, 0, 1, 4, 2], +# [5, 6, 3, 2, 1], +# [1, 2, 0, 1, 5], +# [4, 1, 0, 1, 7], +# [1, 0, 3, 0, 5] +# ] +# +# sumRegion(2, 1, 4, 3) -> 8 +# sumRegion(1, 1, 2, 2) -> 11 +# sumRegion(1, 2, 2, 4) -> 12 +# Note: +# You may assume that the matrix does not change. +# There are many calls to sumRegion function. +# You may assume that row1 <= row2 and col1 <= col2. + +class NumMatrix(object): + def __init__(self, matrix): + """ + initialize your data structure here. + :type matrix: List[List[int]] + """ + if not matrix: + return + + m, n = len(matrix), len(matrix[0]) + self.__sums = [[0 for _ in xrange(n+1)] for _ in xrange(m+1)] + for i in xrange(1, m+1): + for j in xrange(1, n+1): + self.__sums[i][j] = self.__sums[i][j-1] + matrix[i-1][j-1] + for j in xrange(1, n+1): + for i in xrange(1, m+1): + self.__sums[i][j] += self.__sums[i-1][j] + + def sumRegion(self, row1, col1, row2, col2): + """ + sum of elements matrix[(row1,col1)..(row2,col2)], inclusive. + :type row1: int + :type col1: int + :type row2: int + :type col2: int + :rtype: int + """ + return self.__sums[row2+1][col2+1] - self.__sums[row2+1][col1] - \ + self.__sums[row1][col2+1] + self.__sums[row1][col1] + + +# Your NumMatrix object will be instantiated and called as such: +# numMatrix = NumMatrix(matrix) +# numMatrix.sumRegion(0, 1, 2, 3) +# numMatrix.sumRegion(1, 2, 3, 4) diff --git a/Python/range-sum-query-2d-mutable.py b/Python/range-sum-query-2d-mutable.py new file mode 100644 index 000000000..ac8963f41 --- /dev/null +++ b/Python/range-sum-query-2d-mutable.py @@ -0,0 +1,81 @@ +# Time: ctor: O(m * n) +# update: O(logm * logn) +# query: O(logm * logn) +# Space: O(m * n) + +# Binary Indexed Tree (BIT) solution. +class NumMatrix(object): + def __init__(self, matrix): + """ + initialize your data structure here. + :type matrix: List[List[int]] + """ + if not matrix: + return + self.__matrix = matrix + self.__bit = [[0] * (len(self.__matrix[0]) + 1) \ + for _ in xrange(len(self.__matrix) + 1)] + for i in xrange(1, len(self.__bit)): + for j in xrange(1, len(self.__bit[0])): + self.__bit[i][j] = matrix[i-1][j-1] + self.__bit[i-1][j] + \ + self.__bit[i][j-1] - self.__bit[i-1][j-1] + for i in reversed(xrange(1, len(self.__bit))): + for j in reversed(xrange(1, len(self.__bit[0]))): + last_i, last_j = i - (i & -i), j - (j & -j) + self.__bit[i][j] = self.__bit[i][j] - self.__bit[i][last_j] - \ + self.__bit[last_i][j] + self.__bit[last_i][last_j] + + def update(self, row, col, val): + """ + update the element at matrix[row,col] to val. + :type row: int + :type col: int + :type val: int + :rtype: void + """ + if val - self.__matrix[row][col]: + self.__add(row, col, val - self.__matrix[row][col]) + self.__matrix[row][col] = val + + def sumRegion(self, row1, col1, row2, col2): + """ + sum of elements matrix[(row1,col1)..(row2,col2)], inclusive. + :type row1: int + :type col1: int + :type row2: int + :type col2: int + :rtype: int + """ + return self.__sum(row2, col2) - self.__sum(row2, col1 - 1) - \ + self.__sum(row1 - 1, col2) + self.__sum(row1 - 1, col1 - 1) + + def __sum(self, row, col): + row += 1 + col += 1 + ret = 0 + i = row + while i > 0: + j = col + while j > 0: + ret += self.__bit[i][j] + j -= (j & -j) + i -= (i & -i) + return ret + + def __add(self, row, col, val): + row += 1 + col += 1 + i = row + while i <= len(self.__matrix): + j = col + while j <= len(self.__matrix[0]): + self.__bit[i][j] += val + j += (j & -j) + i += (i & -i) + + +# Your NumMatrix object will be instantiated and called as such: +# numMatrix = NumMatrix(matrix) +# numMatrix.sumRegion(0, 1, 2, 3) +# numMatrix.update(1, 1, 10) +# numMatrix.sumRegion(1, 2, 3, 4) diff --git a/Python/range-sum-query-immutable.py b/Python/range-sum-query-immutable.py new file mode 100644 index 000000000..b3b503b53 --- /dev/null +++ b/Python/range-sum-query-immutable.py @@ -0,0 +1,41 @@ +# Time: ctor: O(n), +# lookup: O(1) +# Space: O(n) +# +#Given an integer array nums, find the sum of the elements between indices i and j (i ≤ j), inclusive. +# +# Example: +# Given nums = [-2, 0, 3, -5, 2, -1] +# +# sumRange(0, 2) -> 1 +# sumRange(2, 5) -> -1 +# sumRange(0, 5) -> -3 +# Note: +# You may assume that the array does not change. +# There are many calls to sumRange function. +# + +class NumArray(object): + def __init__(self, nums): + """ + initialize your data structure here. + :type nums: List[int] + """ + self.accu = [0] + for num in nums: + self.accu.append(self.accu[-1] + num), + + def sumRange(self, i, j): + """ + sum of elements nums[i..j], inclusive. + :type i: int + :type j: int + :rtype: int + """ + return self.accu[j + 1] - self.accu[i] + + +# Your NumArray object will be instantiated and called as such: +# numArray = NumArray(nums) +# numArray.sumRange(0, 1) +# numArray.sumRange(1, 2) diff --git a/Python/range-sum-query-mutable.py b/Python/range-sum-query-mutable.py new file mode 100644 index 000000000..5e8ed8136 --- /dev/null +++ b/Python/range-sum-query-mutable.py @@ -0,0 +1,166 @@ +# Time: ctor: O(n), +# update: O(logn), +# query: O(logn) +# Space: O(n) + +# Given an integer array nums, find the sum of +# the elements between indices i and j (i <= j), inclusive. +# +# The update(i, val) function modifies nums by +# updating the element at index i to val. +# Example: +# Given nums = [1, 3, 5] +# +# sumRange(0, 2) -> 9 +# update(1, 2) +# sumRange(0, 2) -> 8 +# Note: +# The array is only modifiable by the update function. +# You may assume the number of calls to update +# and sumRange function is distributed evenly. + +# Binary Indexed Tree (BIT) solution. +class NumArray(object): + def __init__(self, nums): + """ + initialize your data structure here. + :type nums: List[int] + """ + if not nums: + return + self.__nums = nums + self.__bit = [0] * (len(self.__nums) + 1) + for i in xrange(1, len(self.__bit)): + self.__bit[i] = nums[i-1] + self.__bit[i-1] + + for i in reversed(xrange(1, len(self.__bit))): + last_i = i - (i & -i) + self.__bit[i] -= self.__bit[last_i] + + def update(self, i, val): + """ + :type i: int + :type val: int + :rtype: int + """ + if val - self.__nums[i]: + self.__add(i, val - self.__nums[i]) + self.__nums[i] = val + + def sumRange(self, i, j): + """ + sum of elements nums[i..j], inclusive. + :type i: int + :type j: int + :rtype: int + """ + return self.__sum(j) - self.__sum(i-1) + + def __sum(self, i): + i += 1 + ret = 0 + while i > 0: + ret += self.__bit[i] + i -= (i & -i) + return ret + + def __add(self, i, val): + i += 1 + while i <= len(self.__nums): + self.__bit[i] += val + i += (i & -i) + + +# Time: ctor: O(n), +# update: O(logn), +# query: O(logn) +# Space: O(n) +# Segment Tree solutoin. +class NumArray2(object): + def __init__(self, nums): + """ + initialize your data structure here. + :type nums: List[int] + """ + # Build segment tree. + self.__nums = nums + def buildHelper(nums, start, end): + if start > end: + return None + + # The root's start and end is given by build method. + root = self._SegmentTreeNode(start, end, 0) + + # If start equals to end, there will be no children for this node. + if start == end: + root.sum = nums[start] + return root + + # Left child: start=nums.left, end=(nums.left + nums.right) / 2. + root.left = buildHelper(nums, start, (start + end) / 2) + + # Right child: start=(nums.left + nums.right) / 2 + 1, end=nums.right. + root.right = buildHelper(nums, (start + end) / 2 + 1, end) + + # Update sum. + root.sum = (root.left.sum if root.left else 0) + \ + (root.right.sum if root.right else 0) + return root + + self.__root = buildHelper(nums, 0, len(nums) - 1) + + def update(self, i, val): + """ + :type i: int + :type val: int + :rtype: int + """ + def updateHelper(root, i, val): + # Out of range. + if not root or root.start > i or root.end < i: + return + + # Change the node's value with [i] to the new given value. + if root.start == i and root.end == i: + root.sum = val + return + + updateHelper(root.left, i, val) + updateHelper(root.right, i, val) + + # Update sum. + root.sum = (root.left.sum if root.left else 0) + \ + (root.right.sum if root.right else 0) + if self.__nums[i] != val: + self.__nums[i] = val + updateHelper(self.__root, i, val) + + def sumRange(self, i, j): + """ + sum of elements nums[i..j], inclusive. + :type i: int + :type j: int + :rtype: int + """ + def sumRangeHelper(root, start, end): + # Out of range. + if not root or root.start > end or root.end < start: + return 0 + # Current segment is totally within range [start, end] + if root.start >= start and root.end <= end: + return root.sum + return sumRangeHelper(root.left, start, end) + \ + sumRangeHelper(root.right, start, end) + + return sumRangeHelper(self.__root, i, j) + + class _SegmentTreeNode: + def __init__(self, i, j, s): + self.start, self.end, self.sum = i, j, s + + +# Your NumArray object will be instantiated and called as such: +# numArray = NumArray(nums) +# numArray.sumRange(0, 1) +# numArray.update(1, 10) +# numArray.sumRange(1, 2) diff --git a/Python/ransom-note.py b/Python/ransom-note.py new file mode 100644 index 000000000..0d175fdd4 --- /dev/null +++ b/Python/ransom-note.py @@ -0,0 +1,53 @@ +# Time: O(n) +# Space: O(1) + +# Given an arbitrary ransom note string and another string containing letters +# from all the magazines, write a function that will return true if +# the ransom note can be constructed from the magazines ; +# otherwise, it will return false. +# +# Each letter in the magazine string can only be used once in your ransom note. +# +# Note: +# You may assume that both strings contain only lowercase letters. +# +# canConstruct("a", "b") -> false +# canConstruct("aa", "ab") -> false +# canConstruct("aa", "aab") -> true + +class Solution(object): + def canConstruct(self, ransomNote, magazine): + """ + :type ransomNote: str + :type magazine: str + :rtype: bool + """ + counts = [0] * 26 + letters = 0 + + for c in ransomNote: + if counts[ord(c) - ord('a')] == 0: + letters += 1 + counts[ord(c) - ord('a')] += 1 + + for c in magazine: + counts[ord(c) - ord('a')] -= 1 + if counts[ord(c) - ord('a')] == 0: + letters -= 1 + if letters == 0: + break + + return letters == 0 + +# Time: O(n) +# Space: O(1) +import collections + +class Solution2(object): + def canConstruct(self, ransomNote, magazine): + """ + :type ransomNote: str + :type magazine: str + :rtype: bool + """ + return not collections.Counter(ransomNote) - collections.Counter(magazine) diff --git a/Python/reach-a-number.py b/Python/reach-a-number.py new file mode 100644 index 000000000..ff4344ee7 --- /dev/null +++ b/Python/reach-a-number.py @@ -0,0 +1,57 @@ +# Time: O(logn) +# Space: O(1) + +# You are standing at position 0 on an infinite number line. There is a goal at position target. +# +# On each move, you can either go left or right. +# During the n-th move (starting from 1), you take n steps. +# +# Return the minimum number of steps required to reach the destination. +# +# Example 1: +# Input: target = 3 +# Output: 2 +# Explanation: +# On the first move we step from 0 to 1. +# On the second step we step from 1 to 3. +# +# Example 2: +# Input: target = 2 +# Output: 3 +# Explanation: +# On the first move we step from 0 to 1. +# On the second move we step from 1 to -1. +# On the third move we step from -1 to 2. +# +# Note: +# - target will be a non-zero integer in the range [-10^9, 10^9]. + +import math + + +class Solution(object): + def reachNumber(self, target): + """ + :type target: int + :rtype: int + """ + target = abs(target) + k = int(math.ceil((-1+math.sqrt(1+8*target))/2)) + target -= k*(k+1)/2 + return k if target%2 == 0 else k+1+k%2 + + +# Time: O(sqrt(n)) +# Space: O(1) +class Solution2(object): + def reachNumber(self, target): + """ + :type target: int + :rtype: int + """ + target = abs(target) + k = 0 + while target > 0: + k += 1 + target -= k + return k if target%2 == 0 else k+1+k%2 diff --git a/Python/reaching-points.py b/Python/reaching-points.py new file mode 100644 index 000000000..cd8a18bba --- /dev/null +++ b/Python/reaching-points.py @@ -0,0 +1,46 @@ +# Time: O(log(max(m, n))) +# Space: O(1) + +# A move consists of taking a point (x, y) and transforming it to either (x, x+y) or (x+y, y). +# +# Given a starting point (sx, sy) and a target point (tx, ty), +# return True if and only if a sequence of moves exists to transform the point (sx, sy) to (tx, ty). +# Otherwise, return False. +# +# Examples: +# Input: sx = 1, sy = 1, tx = 3, ty = 5 +# Output: True +# Explanation: +# One series of moves that transforms the starting point to the target is: +# (1, 1) -> (1, 2) +# (1, 2) -> (3, 2) +# (3, 2) -> (3, 5) +# +# Input: sx = 1, sy = 1, tx = 2, ty = 2 +# Output: False +# +# Input: sx = 1, sy = 1, tx = 1, ty = 1 +# Output: True +# +# Note: +# - sx, sy, tx, ty will all be integers in the range [1, 10^9]. + +class Solution(object): + def reachingPoints(self, sx, sy, tx, ty): + """ + :type sx: int + :type sy: int + :type tx: int + :type ty: int + :rtype: bool + """ + while tx >= sx and ty >= sy: + if tx < ty: + sx, sy = sy, sx + tx, ty = ty, tx + if ty > sy: + tx %= ty + else: + return (tx - sx) % ty == 0 + + return False diff --git a/Python/read-n-characters-given-read4-ii-call-multiple-times.py b/Python/read-n-characters-given-read4-ii-call-multiple-times.py index 91532e01c..d5e474d15 100644 --- a/Python/read-n-characters-given-read4-ii-call-multiple-times.py +++ b/Python/read-n-characters-given-read4-ii-call-multiple-times.py @@ -1,12 +1,13 @@ +from __future__ import print_function # Time: O(n) # Space: O(1) # # The API: int read4(char *buf) reads 4 characters at a time from a file. -# +# # The return value is the actual number of characters read. For example, it returns 3 if there is only 3 characters left in the file. -# +# # By using the read4 API, implement the function int read(char *buf, int n) that reads n characters from the file. -# +# # Note: # The read function may be called multiple times. # @@ -20,43 +21,49 @@ def read4(buf): while i < len(file_content) and i < 4: buf[i] = file_content[i] i += 1 - + if len(file_content) > 4: file_content = file_content[4:] else: file_content = "" return i - -class Solution: + +# The read4 API is already defined for you. +# @param buf, a list of characters +# @return an integer +# def read4(buf): + +class Solution(object): def __init__(self): - self.buffer_size, self.offset = 0, 0 - self.buffer = [None for _ in xrange(4)] - - # @param buf, Destination buffer (a list of characters) - # @param n, Maximum number of characters to read (an integer) - # @return The number of characters read (an integer) + self.__buf4 = [''] * 4 + self.__i4 = 0 + self.__n4 = 0 + def read(self, buf, n): - read_bytes = 0 - eof = False - while not eof and read_bytes < n: - if self.buffer_size == 0: - size = read4(self.buffer) + """ + :type buf: Destination buffer (List[str]) + :type n: Maximum number of characters to read (int) + :rtype: The number of characters read (int) + """ + i = 0 + while i < n: + if self.__i4 < self.__n4: # Any characters in buf4. + buf[i] = self.__buf4[self.__i4] + i += 1 + self.__i4 += 1 else: - size = self.buffer_size - if self.buffer_size == 0 and size < 4: - eof = True - bytes = min(n - read_bytes, size) - for i in xrange(bytes): - buf[read_bytes + i] = self.buffer[self.offset + i] - self.offset = (self.offset + bytes) % 4 - self.buffer_size = size - bytes - read_bytes += bytes - return read_bytes + self.__n4 = read4(self.__buf4) # Read more characters. + if self.__n4: + self.__i4 = 0 + else: # Buffer has been empty. + break + + return i if __name__ == "__main__": global file_content sol = Solution() buf = ['' for _ in xrange(100)] file_content = "ab" - print buf[:sol.read(buf, 1)] - print buf[:sol.read(buf, 2)] + print(buf[:sol.read(buf, 1)]) + print(buf[:sol.read(buf, 2)]) diff --git a/Python/read-n-characters-given-read4.py b/Python/read-n-characters-given-read4.py index 912eb1c25..21f4f9cb0 100644 --- a/Python/read-n-characters-given-read4.py +++ b/Python/read-n-characters-given-read4.py @@ -1,12 +1,13 @@ +from __future__ import print_function # Time: O(n) # Space: O(1) # # The API: int read4(char *buf) reads 4 characters at a time from a file. -# +# # The return value is the actual number of characters read. For example, it returns 3 if there is only 3 characters left in the file. -# +# # By using the read4 API, implement the function int read(char *buf, int n) that reads n characters from the file. -# +# # Note: # The read function will only be called once for each test case. # @@ -20,35 +21,36 @@ def read4(buf): while i < len(file_content) and i < 4: buf[i] = file_content[i] i += 1 - + if len(file_content) > 4: file_content = file_content[4:] else: file_content = "" return i - -class Solution: - # @param buf, Destination buffer (a list of characters) - # @param n, Maximum number of characters to read (an integer) - # @return The number of characters read (an integer) + +class Solution(object): def read(self, buf, n): + """ + :type buf: Destination buffer (List[str]) + :type n: Maximum number of characters to read (int) + :rtype: The number of characters read (int) + """ read_bytes = 0 - eof = False - buffer = ['' for _ in xrange(4)] - while not eof and read_bytes < n: + buffer = [''] * 4 + for i in xrange(n / 4 + 1): size = read4(buffer) - if size < 4: - eof = True - bytes = min(n - read_bytes, size) - for i in xrange(bytes): - buf[read_bytes + i] = buffer[i] - read_bytes += bytes + if size: + size = min(size, n-read_bytes) + buf[read_bytes:read_bytes+size] = buffer[:size] + read_bytes += size + else: + break return read_bytes if __name__ == "__main__": global file_content buf = ['' for _ in xrange(100)] file_content = "a" - print buf[:Solution().read(buf, 9)] + print(buf[:Solution().read(buf, 9)]) file_content = "abcdefghijklmnop" - print buf[:Solution().read(buf, 9)] \ No newline at end of file + print(buf[:Solution().read(buf, 9)]) diff --git a/Python/rearrange-string-k-distance-apart.py b/Python/rearrange-string-k-distance-apart.py new file mode 100644 index 000000000..98610862c --- /dev/null +++ b/Python/rearrange-string-k-distance-apart.py @@ -0,0 +1,71 @@ +# Time: O(n) +# Space: O(n) + +class Solution(object): + def rearrangeString(self, str, k): + """ + :type str: str + :type k: int + :rtype: str + """ + cnts = [0] * 26; + for c in str: + cnts[ord(c) - ord('a')] += 1 + + sorted_cnts = [] + for i in xrange(26): + sorted_cnts.append((cnts[i], chr(i + ord('a')))) + sorted_cnts.sort(reverse=True) + + max_cnt = sorted_cnts[0][0] + blocks = [[] for _ in xrange(max_cnt)] + i = 0 + for cnt in sorted_cnts: + for _ in xrange(cnt[0]): + blocks[i].append(cnt[1]) + i = (i + 1) % max(cnt[0], max_cnt - 1) + + for i in xrange(max_cnt-1): + if len(blocks[i]) < k: + return "" + + return "".join(map(lambda x : "".join(x), blocks)) + + +# Time: O(nlogc), c is the count of unique characters. +# Space: O(c) +from collections import defaultdict +from heapq import heappush, heappop +class Solution2(object): + def rearrangeString(self, str, k): + """ + :type str: str + :type k: int + :rtype: str + """ + if k == 0: + return str + + cnts = defaultdict(int) + for c in str: + cnts[c] += 1 + + heap = [] + for c, cnt in cnts.iteritems(): + heappush(heap, [-cnt, c]) + + result = [] + while heap: + used_cnt_chars = [] + for _ in xrange(min(k, len(str) - len(result))): + if not heap: + return "" + cnt_char = heappop(heap) + result.append(cnt_char[1]) + cnt_char[0] += 1 + if cnt_char[0] < 0: + used_cnt_chars.append(cnt_char) + for cnt_char in used_cnt_chars: + heappush(heap, cnt_char) + + return "".join(result) diff --git a/Python/reconstruct-itinerary.py b/Python/reconstruct-itinerary.py new file mode 100644 index 000000000..b1864972f --- /dev/null +++ b/Python/reconstruct-itinerary.py @@ -0,0 +1,59 @@ +# Time: O(t! / (n1! * n2! * ... nk!)), t is the total number of tickets, +# ni is the number of the ticket which from is city i, +# k is the total number of cities. +# Space: O(t) + +# Given a list of airline tickets represented by pairs of departure +# and arrival airports [from, to], reconstruct the itinerary in order. +# All of the tickets belong to a man who departs from JFK. +# Thus, the itinerary must begin with JFK. +# +# Note: +# If there are multiple valid itineraries, you should return the itinerary +# that has the smallest lexical order when read as a single string. +# For example, the itinerary ["JFK", "LGA"] has a smaller lexical +# order than ["JFK", "LGB"]. +# All airports are represented by three capital letters (IATA code). +# You may assume all tickets may form at least one valid itinerary. +# Example 1: +# tickets = [["MUC", "LHR"], ["JFK", "MUC"], ["SFO", "SJC"], ["LHR", "SFO"]] +# Return ["JFK", "MUC", "LHR", "SFO", "SJC"]. +# Example 2: +# tickets = [["JFK","SFO"],["JFK","ATL"],["SFO","ATL"],["ATL","JFK"],["ATL","SFO"]] +# Return ["JFK","ATL","JFK","SFO","ATL","SFO"]. +# Another possible reconstruction is ["JFK","SFO","ATL","JFK","ATL","SFO"]. +# But it is larger in lexical order. + +import collections + + +class Solution(object): + def findItinerary(self, tickets): + """ + :type tickets: List[List[str]] + :rtype: List[str] + """ + def route_helper(origin, ticket_cnt, graph, ans): + if ticket_cnt == 0: + return True + + for i, (dest, valid) in enumerate(graph[origin]): + if valid: + graph[origin][i][1] = False + ans.append(dest) + if route_helper(dest, ticket_cnt - 1, graph, ans): + return ans + ans.pop() + graph[origin][i][1] = True + return False + + graph = collections.defaultdict(list) + for ticket in tickets: + graph[ticket[0]].append([ticket[1], True]) + for k in graph.keys(): + graph[k].sort() + + origin = "JFK" + ans = [origin] + route_helper(origin, len(tickets), graph, ans) + return ans diff --git a/Python/reconstruct-original-digits-from-english.py b/Python/reconstruct-original-digits-from-english.py new file mode 100644 index 000000000..eb67abeab --- /dev/null +++ b/Python/reconstruct-original-digits-from-english.py @@ -0,0 +1,49 @@ +# Time: O(n) +# Space: O(1) + +# Given a non-empty string containing an out-of-order English representation +# of digits 0-9, output the digits in ascending order. +# +# Note: +# Input contains only lowercase English letters. +# Input is guaranteed to be valid and can be transformed to its original digits. +# That means invalid inputs such as "abc" or "zerone" are not permitted. +# Input length is less than 50,000. +# Example 1: +# Input: "owoztneoer" +# +# Output: "012" +# Example 2: +# Input: "fviefuro" +# +# Output: "45" + +from collections import Counter + +class Solution(object): + def originalDigits(self, s): + """ + :type s: str + :rtype: str + """ + # The count of each char in each number string. + cnts = [Counter(_) for _ in ["zero", "one", "two", "three", \ + "four", "five", "six", "seven", \ + "eight", "nine"]] + + # The order for greedy method. + order = [0, 2, 4, 6, 8, 1, 3, 5, 7, 9] + + # The unique char in the order. + unique_chars = ['z', 'o', 'w', 't', 'u', \ + 'f', 'x', 's', 'g', 'n'] + + cnt = Counter(list(s)) + res = [] + for i in order: + while cnt[unique_chars[i]] > 0: + cnt -= cnts[i] + res.append(i) + res.sort() + + return "".join(map(str, res)) diff --git a/Python/recover-binary-search-tree.py b/Python/recover-binary-search-tree.py index 0cc9ddda1..478a0ebf8 100644 --- a/Python/recover-binary-search-tree.py +++ b/Python/recover-binary-search-tree.py @@ -1,10 +1,11 @@ +from __future__ import print_function # Time: O(n) # Space: O(1) # # Two elements of a binary search tree (BST) are swapped by mistake. -# +# # Recover the tree without changing its structure. -# +# # Note: # A solution using O(n) space is pretty straight forward. Could you devise a constant space solution? # @@ -14,8 +15,8 @@ class TreeNode: def __init__(self, x): self.val = x self.left = None - self.right = None - + self.right = None + def __repr__(self): if self: serial = [] @@ -23,21 +24,21 @@ def __repr__(self): while queue: cur = queue[0] - + if cur: serial.append(cur.val) queue.append(cur.left) queue.append(cur.right) else: serial.append("#") - + queue = queue[1:] - + while serial[-1] == "#": serial.pop() - + return repr(serial) - + else: return None @@ -46,13 +47,13 @@ class Solution: # @return a tree node def recoverTree(self, root): return self.MorrisTraversal(root) - + def MorrisTraversal(self, root): if root is None: return broken = [None, None] pre, cur = None, root - + while cur: if cur.left is None: self.detectBroken(broken, pre, cur) @@ -62,7 +63,7 @@ def MorrisTraversal(self, root): node = cur.left while node.right and node.right != cur: node = node.right - + if node.right is None: node.right =cur cur = cur.left @@ -71,20 +72,19 @@ def MorrisTraversal(self, root): node.right = None pre = cur cur = cur.right - + broken[0].val, broken[1].val = broken[1].val, broken[0].val - + return root - + def detectBroken(self, broken, pre, cur): if pre and pre.val > cur.val: if broken[0] is None: broken[0] = pre broken[1] = cur - + if __name__ == "__main__": root = TreeNode(0) root.left = TreeNode(1) - print root - print Solution().recoverTree(root) - \ No newline at end of file + print(root) + print(Solution().recoverTree(root)) diff --git a/Python/rectangle-area-ii.py b/Python/rectangle-area-ii.py new file mode 100644 index 000000000..b91dc6cc1 --- /dev/null +++ b/Python/rectangle-area-ii.py @@ -0,0 +1,91 @@ +# Time: O(nlogn) +# Space: O(n) + +# We are given a list of (axis-aligned) rectangles. +# Each rectangle[i] = [x1, y1, x2, y2] , +# where (x1, y1) are the coordinates of the bottom-left corner, +# and (x2, y2) are the coordinates of the top-right corner of +# the ith rectangle. +# +# Find the total area covered by all rectangles in the plane. +# Since the answer may be too large, return it modulo 10^9 + 7. +# +# Example 1: +# +# Input: [[0,0,2,2],[1,0,2,3],[1,0,3,1]] +# Output: 6 +# Explanation: As illustrated in the picture. +# Example 2: +# +# Input: [[0,0,1000000000,1000000000]] +# Output: 49 +# Explanation: The answer is 10^18 modulo (10^9 + 7), +# which is (10^9)^2 = (-7)^2 = 49. +# +# Note: +# - 1 <= rectangles.length <= 200 +# - rectanges[i].length = 4 +# - 0 <= rectangles[i][j] <= 10^9 +# - The total area covered by all rectangles will never exceed 2^63 - 1 and +# thus will fit in a 64-bit signed integer. + + +class SegmentTreeNode(object): + def __init__(self, start, end): + self.start, self.end = start, end + self.total = self.count = 0 + self._left = self._right = None + + def mid(self): + return (self.start+self.end) // 2 + + def left(self): + self._left = self._left or SegmentTreeNode(self.start, self.mid()) + return self._left + + def right(self): + self._right = self._right or SegmentTreeNode(self.mid(), self.end) + return self._right + + def update(self, X, i, j, val): + if i >= j: + return 0 + if self.start == i and self.end == j: + self.count += val + else: + self.left().update(X, i, min(self.mid(), j), val) + self.right().update(X, max(self.mid(), i), j, val) + if self.count > 0: + self.total = X[self.end]-X[self.start] + else: + self.total = self.left().total + self.right().total + return self.total + + +class Solution(object): + def rectangleArea(self, rectangles): + """ + :type rectangles: List[List[int]] + :rtype: int + """ + OPEN, CLOSE = 1, -1 + events = [] + X = set() + for x1, y1, x2, y2 in rectangles: + events.append((y1, OPEN, x1, x2)) + events.append((y2, CLOSE, x1, x2)) + X.add(x1) + X.add(x2) + events.sort() + X = sorted(X) + Xi = {x: i for i, x in enumerate(X)} + + st = SegmentTreeNode(0, len(X)-1) + result = 0 + cur_x_sum = 0 + cur_y = events[0][0] + for y, typ, x1, x2 in events: + result += cur_x_sum * (y-cur_y) + cur_x_sum = st.update(X, Xi[x1], Xi[x2], typ) + cur_y = y + return result % (10**9+7) diff --git a/Python/rectangle-area.py b/Python/rectangle-area.py new file mode 100644 index 000000000..52b879654 --- /dev/null +++ b/Python/rectangle-area.py @@ -0,0 +1,28 @@ +# Time: O(1) +# Space: O(1) +# +# Find the total area covered by two rectilinear rectangles in a 2D plane. +# +# Each rectangle is defined by its bottom left corner +# and top right corner as shown in the figure. +# +# Rectangle Area +# Assume that the total area is never beyond the maximum +# possible value of int. +# + +class Solution: + # @param {integer} A + # @param {integer} B + # @param {integer} C + # @param {integer} D + # @param {integer} E + # @param {integer} F + # @param {integer} G + # @param {integer} H + # @return {integer} + def computeArea(self, A, B, C, D, E, F, G, H): + return (D - B) * (C - A) + \ + (G - E) * (H - F) - \ + max(0, (min(C, G) - max(A, E))) * \ + max(0, (min(D, H) - max(B, F))) diff --git a/Python/rectangle-overlap.py b/Python/rectangle-overlap.py new file mode 100644 index 000000000..bc42db74f --- /dev/null +++ b/Python/rectangle-overlap.py @@ -0,0 +1,40 @@ +# Time: O(1) +# Space: O(1) + +# A rectangle is represented as a list [x1, y1, x2, y2], +# where (x1, y1) are the coordinates of its bottom-left corner, +# and (x2, y2) are the coordinates of its top-right corner. +# +# Two rectangles overlap if +# the area of their intersection is positive. +# To be clear, two rectangles that only +# touch at the corner or edges do not overlap. +# +# Given two rectangles, return whether they overlap. +# +# Example 1: +# +# Input: rec1 = [0,0,2,2], rec2 = [1,1,3,3] +# Output: true +# Example 2: +# +# Input: rec1 = [0,0,1,1], rec2 = [1,0,2,1] +# Output: false +# Notes: +# +# Both rectangles rec1 and rec2 are lists of 4 integers. +# All coordinates in rectangles will be between -10^9 and 10^9. + + +class Solution(object): + def isRectangleOverlap(self, rec1, rec2): + """ + :type rec1: List[int] + :type rec2: List[int] + :rtype: bool + """ + def intersect(p_left, p_right, q_left, q_right): + return max(p_left, q_left) < min(p_right, q_right) + + return (intersect(rec1[0], rec1[2], rec2[0], rec2[2]) and + intersect(rec1[1], rec1[3], rec2[1], rec2[3])) diff --git a/Python/redundant-connection-ii.py b/Python/redundant-connection-ii.py new file mode 100644 index 000000000..79e613c68 --- /dev/null +++ b/Python/redundant-connection-ii.py @@ -0,0 +1,70 @@ +# Time: O(nlog*n) ~= O(n), n is the length of the positions +# Space: O(n) + +# In this problem, a rooted tree is a directed graph such that, +# there is exactly one node (the root) for +# which all other nodes are descendants of this node, plus every node has exactly one parent, +# except for the root node which has no parents. +# +# The given input is a directed graph that started as a rooted tree with N nodes +# (with distinct values 1, 2, ..., N), with one additional directed edge added. +# The added edge has two different vertices chosen from 1 to N, and was not an edge that already existed. +# +# The resulting graph is given as a 2D-array of edges. +# Each element of edges is a pair [u, v] that represents a directed edge connecting nodes u and v, +# where u is a parent of child v. +# +# Return an edge that can be removed so that the resulting graph is a rooted tree of N nodes. +# If there are multiple answers, return the answer that occurs last in the given 2D-array. +# +# Example 1: +# Input: [[1,2], [1,3], [2,3]] +# Output: [2,3] +# Explanation: The given directed graph will be like this: +# 1 +# / \ +# v v +# 2-->3 +# Example 2: +# Input: [[1,2], [2,3], [3,4], [4,1], [1,5]] +# Output: [4,1] +# Explanation: The given directed graph will be like this: +# 5 <- 1 -> 2 +# ^ | +# | v +# 4 <- 3 +# Note: +# The size of the input 2D-array will be between 3 and 1000. +# Every integer represented in the 2D-array will be between 1 and N, where N is the size of the input array. + +class UnionFind(object): + def __init__(self, n): + self.set = range(n) + self.count = n + + def find_set(self, x): + if self.set[x] != x: + self.set[x] = self.find_set(self.set[x]) # path compression. + return self.set[x] + + def union_set(self, x, y): + x_root, y_root = map(self.find_set, (x, y)) + if x_root == y_root or \ + y != y_root: # already has a father + return False + self.set[y_root] = x_root + self.count -= 1 + return True + + +class Solution(object): + def findRedundantDirectedConnection(self, edges): + """ + :type edges: List[List[int]] + :rtype: List[int] + """ + union_find = UnionFind(len(edges)+1) + for edge in edges: + if not union_find.union_set(*edge): + return edge + return [] diff --git a/Python/redundant-connection.py b/Python/redundant-connection.py new file mode 100644 index 000000000..c490e4743 --- /dev/null +++ b/Python/redundant-connection.py @@ -0,0 +1,62 @@ +# Time: O(nlog*n) ~= O(n), n is the length of the positions +# Space: O(n) + +# We are given a "tree" in the form of a 2D-array, with distinct values for each node. +# +# In the given 2D-array, each element pair [u, v] represents that v is a child of u in the tree. +# +# We can remove exactly one redundant pair in this "tree" to make the result a (rooted) tree. +# +# You need to find and output such a pair. If there are multiple answers for this question, +# output the one appearing last in the 2D-array. There is always at least one answer. +# +# Example 1: +# Input: [[1,2], [1,3], [2,3]] +# Output: [2,3] +# Explanation: Original tree will be like this: +# 1 +# / \ +# 2 - 3 +# +# Example 2: +# Input: [[1,2], [1,3], [3,1]] +# Output: [3,1] +# Explanation: Original tree will be like this: +# 1 +# / \\ +# 2 3 +# Note: +# The size of the input 2D-array will be between 3 and 1000. +# Every integer represented in the 2D-array will be between 1 and N, where N is the size of the input array. + +class UnionFind(object): + def __init__(self, n): + self.set = range(n) + self.count = n + + def find_set(self, x): + if self.set[x] != x: + self.set[x] = self.find_set(self.set[x]) # path compression. + return self.set[x] + + def union_set(self, x, y): + x_root, y_root = map(self.find_set, (x, y)) + if x_root == y_root: + return False + self.set[min(x_root, y_root)] = max(x_root, y_root) + self.count -= 1 + return True + + +class Solution(object): + def findRedundantConnection(self, edges): + """ + :type edges: List[List[int]] + :rtype: List[int] + """ + union_find = UnionFind(len(edges)+1) + for edge in edges: + if not union_find.union_set(*edge): + return edge + return [] + diff --git a/Python/regular-expression-matching.py b/Python/regular-expression-matching.py index f6c6d2746..8c8fb812e 100644 --- a/Python/regular-expression-matching.py +++ b/Python/regular-expression-matching.py @@ -1,16 +1,17 @@ +from __future__ import print_function # Time: O(m * n) # Space: O(n) # # Implement regular expression matching with support for '.' and '*'. -# +# # '.' Matches any single character. # '*' Matches zero or more of the preceding element. -# +# # The matching should cover the entire input string (not partial). -# +# # The function prototype should be: # bool isMatch(const char *s, const char *p) -# +# # Some examples: # isMatch("aa","a") -> false # isMatch("aa","aa") -> true @@ -27,12 +28,12 @@ class Solution: def isMatch(self, s, p): k = 3 result = [[False for j in xrange(len(p) + 1)] for i in xrange(k)] - + result[0][0] = True for i in xrange(2, len(p) + 1): if p[i-1] == '*': result[0][i] = result[0][i-2] - + for i in xrange(1,len(s) + 1): if i > 1: result[0][0] = False @@ -40,8 +41,8 @@ def isMatch(self, s, p): if p[j-1] != '*': result[i % k][j] = result[(i-1) % k][j-1] and (s[i-1] == p[j-1] or p[j-1] == '.') else: - result[i % k][j] = result[i % k][j-2] or (result[(i-1) % k][j] and (s[i-1] == p[j-2] or p[j-2]=='.')) - + result[i % k][j] = result[i % k][j-2] or (result[(i-1) % k][j] and (s[i-1] == p[j-2] or p[j-2] == '.')) + return result[len(s) % k][len(p)] # dp @@ -51,30 +52,64 @@ class Solution2: # @return a boolean def isMatch(self, s, p): result = [[False for j in xrange(len(p) + 1)] for i in xrange(len(s) + 1)] - + result[0][0] = True for i in xrange(2, len(p) + 1): if p[i-1] == '*': result[0][i] = result[0][i-2] - + for i in xrange(1,len(s) + 1): for j in xrange(1, len(p) + 1): if p[j-1] != '*': result[i][j] = result[i-1][j-1] and (s[i-1] == p[j-1] or p[j-1] == '.') else: - result[i][j] = result[i][j-2] or (result[i-1][j] and (s[i-1] == p[j-2] or p[j-2]=='.')) - + result[i][j] = result[i][j-2] or (result[i-1][j] and (s[i-1] == p[j-2] or p[j-2] == '.')) + return result[len(s)][len(p)] -# recursive +# iteration class Solution3: # @return a boolean def isMatch(self, s, p): - if len(p) == 0: - return len(s) == 0 - + p_ptr, s_ptr, last_s_ptr, last_p_ptr = 0, 0, -1, -1 + last_ptr = [] + while s_ptr < len(s): + if p_ptr < len(p) and (p_ptr == len(p) - 1 or p[p_ptr + 1] != '*') and \ + (s_ptr < len(s) and (p[p_ptr] == s[s_ptr] or p[p_ptr] == '.')): + s_ptr += 1 + p_ptr += 1 + elif p_ptr < len(p) - 1 and (p_ptr != len(p) - 1 and p[p_ptr + 1] == '*'): + p_ptr += 2 + last_ptr.append([s_ptr, p_ptr]) + elif last_ptr: + [last_s_ptr, last_p_ptr] = last_ptr.pop() + while last_ptr and p[last_p_ptr - 2] != s[last_s_ptr] and p[last_p_ptr - 2] != '.': + [last_s_ptr, last_p_ptr] = last_ptr.pop() + + if p[last_p_ptr - 2] == s[last_s_ptr] or p[last_p_ptr - 2] == '.': + last_s_ptr += 1 + s_ptr = last_s_ptr + p_ptr = last_p_ptr + last_ptr.append([s_ptr, p_ptr]) + else: + return False + else: + return False + + while p_ptr < len(p) - 1 and p[p_ptr] == '.' and p[p_ptr + 1] == '*': + p_ptr += 2 + + return p_ptr == len(p) + +# recursive +class Solution4: + # @return a boolean + def isMatch(self, s, p): + if not p: + return not s + if len(p) == 1 or p[1] != '*': - if len(s) == 0 or (p[0] == s[0] or p[0] == '.'): + if len(s) > 0 and (p[0] == s[0] or p[0] == '.'): return self.isMatch(s[1:], p[1:]) else: return False @@ -86,12 +121,12 @@ def isMatch(self, s, p): return self.isMatch(s, p[2:]) if __name__ == "__main__": - print Solution().isMatch("abcd","d*") - print Solution().isMatch("aaaaaaaaaaaaab", "a*a*a*a*a*a*a*a*a*a*c") - print Solution().isMatch("aa","a") - print Solution().isMatch("aa","aa") - print Solution().isMatch("aaa","aa") - print Solution().isMatch("aa", "a*") - print Solution().isMatch("aa", ".*") - print Solution().isMatch("ab", ".*") - print Solution().isMatch("aab", "c*a*b") + print(Solution3().isMatch("abab", "a*b*")) + print(Solution().isMatch("aaaaaaaaaaaaab", "a*a*a*a*a*a*a*a*a*a*c")) + print(Solution().isMatch("aa","a")) + print(Solution().isMatch("aa","aa")) + print(Solution().isMatch("aaa","aa")) + print(Solution().isMatch("aa", "a*")) + print(Solution().isMatch("aa", ".*")) + print(Solution().isMatch("ab", ".*")) + print(Solution().isMatch("aab", "c*a*b")) diff --git a/Python/relative-ranks.py b/Python/relative-ranks.py new file mode 100644 index 000000000..35b12842f --- /dev/null +++ b/Python/relative-ranks.py @@ -0,0 +1,27 @@ +# Time: O(nlogn) +# Space: O(n) + +# Given scores of N athletes, find their relative ranks and +# the people with the top three highest scores, who will be +# awarded medals: "Gold Medal", "Silver Medal" and "Bronze Medal". +# +# Example 1: +# Input: [5, 4, 3, 2, 1] +# Output: ["Gold Medal", "Silver Medal", "Bronze Medal", "4", "5"] +# Explanation: The first three athletes got the top three highest scores, +# so they got "Gold Medal", "Silver Medal" and "Bronze Medal". +# For the left two athletes, you just need to output +# their relative ranks according to their scores. +# Note: +# N is a positive integer and won't exceed 10,000. +# All the scores of athletes are guaranteed to be unique. + +class Solution(object): + def findRelativeRanks(self, nums): + """ + :type nums: List[int] + :rtype: List[str] + """ + sorted_nums = sorted(nums)[::-1] + ranks = ["Gold Medal", "Silver Medal", "Bronze Medal"] + map(str, range(4, len(nums) + 1)) + return map(dict(zip(sorted_nums, ranks)).get, nums) diff --git a/Python/remove-9.py b/Python/remove-9.py new file mode 100644 index 000000000..234f57714 --- /dev/null +++ b/Python/remove-9.py @@ -0,0 +1,15 @@ +# Time: O(logn) +# Space: O(1) + +class Solution(object): + def newInteger(self, n): + """ + :type n: int + :rtype: int + """ + result, base = 0, 1 + while n > 0: + result += (n%9) * base + n /= 9 + base *= 10 + return result diff --git a/Python/remove-boxes.py b/Python/remove-boxes.py new file mode 100644 index 000000000..e1012647e --- /dev/null +++ b/Python/remove-boxes.py @@ -0,0 +1,46 @@ +# Time: O(n^3) ~ O(n^4) +# Space: O(n^3) + +# Given several boxes with different colors represented by different positive numbers. +# You may experience several rounds to remove boxes until there is no box left. +# Each time you can choose some continuous boxes with the same color (composed of k boxes, k >= 1), +# remove them and get k*k points. +# Find the maximum points you can get. +# +# Example 1: +# Input: +# +# [1, 3, 2, 2, 2, 3, 4, 3, 1] +# Output: +# 23 +# Explanation: +# [1, 3, 2, 2, 2, 3, 4, 3, 1] +# ----> [1, 3, 3, 4, 3, 1] (3*3=9 points) +# ----> [1, 3, 3, 3, 1] (1*1=1 points) +# ----> [1, 1] (3*3=9 points) +# ----> [] (2*2=4 points) +# Note: The number of boxes n would not exceed 100. + +class Solution(object): + def removeBoxes(self, boxes): + """ + :type boxes: List[int] + :rtype: int + """ + def dfs(boxes, l, r, k, lookup): + if l > r: return 0 + if lookup[l][r][k]: return lookup[l][r][k] + + ll, kk = l, k + while l < r and boxes[l+1] == boxes[l]: + l += 1 + k += 1 + result = dfs(boxes, l+1, r, 0, lookup) + (k+1) ** 2 + for i in xrange(l+1, r+1): + if boxes[i] == boxes[l]: + result = max(result, dfs(boxes, l+1, i-1, 0, lookup) + dfs(boxes, i, r, k+1, lookup)) + lookup[ll][r][kk] = result + return result + + lookup = [[[0]*len(boxes) for _ in xrange(len(boxes)) ] for _ in xrange(len(boxes)) ] + return dfs(boxes, 0, len(boxes)-1, 0, lookup) diff --git a/Python/remove-comments.py b/Python/remove-comments.py new file mode 100644 index 000000000..57895225a --- /dev/null +++ b/Python/remove-comments.py @@ -0,0 +1,108 @@ +# Time: O(n), n is the length of the source +# Space: O(k), k is the max length of a line + +# Given a C++ program, remove comments from it. +# The program source is an array where source[i] is the i-th line of the source code. +# This represents the result of splitting the original source code string by the newline character \n. +# +# In C++, there are two types of comments, line comments, and block comments. +# +# The string // denotes a line comment, which represents that it and +# rest of the characters to the right of it in the same line should be ignored. +# +# The string /* denotes a block comment, +# which represents that all characters until the next (non-overlapping) occurrence of */ +# should be ignored. (Here, occurrences happen in reading order: line by line from left to right.) +# To be clear, the string /*/ does not yet end the block comment, as the ending would be overlapping the beginning. +# +# The first effective comment takes precedence over others: +# if the string // occurs in a block comment, it is ignored. +# Similarly, if the string /* occurs in a line or block comment, it is also ignored. +# +# If a certain line of code is empty after removing comments, +# you must not output that line: each string in the answer list will be non-empty. +# +# There will be no control characters, single quote, or double quote characters. +# For example, source = "string s = "/* Not a comment. */";" will not be a test case. +# (Also, nothing else such as defines or macros will interfere with the comments.) +# +# It is guaranteed that every open block comment will eventually be closed, +# so /* outside of a line or block comment always starts a new comment. +# +# Finally, implicit newline characters can be deleted by block comments. Please see the examples below for details. +# +# After removing the comments from the source code, return the source code in the same format. +# +# Example 1: +# Input: +# source = ["/*Test program */", "int main()", "{ ", " // variable declaration ", "int a, b, c;", "/* This is a test", " multiline ", " comment for ", " testing */", "a = b + c;", "}"] +# +# The line by line code is visualized as below: +# /*Test program */ +# int main() +# { +# // variable declaration +# int a, b, c; +# /* This is a test +# multiline +# comment for +# testing */ +# a = b + c; +# } +# +# Output: ["int main()","{ "," ","int a, b, c;","a = b + c;","}"] +# +# The line by line code is visualized as below: +# int main() +# { +# int a, b, c; +# a = b + c; +# } +# Explanation: +# The string +# /* +# denotes a block comment, including line 1 and lines 6-9. The string +# // +# denotes line 4 as comments. +# +# Example 2: +# Input: +# source = ["a/*comment", "line", "more_comment*/b"] +# Output: ["ab"] +# Explanation: The original source string is "a/*comment\nline\nmore_comment*/b", +# where we have bolded the newline characters. +# After deletion, the implicit newline characters are deleted, +# leaving the string "ab", which when delimited by newline characters becomes ["ab"]. +# +# Note: +# - The length of source is in the range [1, 100]. +# - The length of source[i] is in the range [0, 80]. +# - Every open block comment is eventually closed. +# - There are no single-quote, double-quote, or control characters in the source code. + +class Solution(object): + def removeComments(self, source): + """ + :type source: List[str] + :rtype: List[str] + """ + in_block = False + result, newline = [], [] + for line in source: + i = 0 + while i < len(line): + if not in_block and i+1 < len(line) and line[i:i+2] == '/*': + in_block = True + i += 1 + elif in_block and i+1 < len(line) and line[i:i+2] == '*/': + in_block = False + i += 1 + elif not in_block and i+1 < len(line) and line[i:i+2] == '//': + break + elif not in_block: + newline.append(line[i]) + i += 1 + if newline and not in_block: + result.append("".join(newline)) + newline = [] + return result diff --git a/Python/remove-duplicate-letters.py b/Python/remove-duplicate-letters.py new file mode 100644 index 000000000..665537749 --- /dev/null +++ b/Python/remove-duplicate-letters.py @@ -0,0 +1,38 @@ +# Time: O(n) +# Space: O(k), k is size of the alphabet + +# Given a string which contains only lowercase letters, +# remove duplicate letters so that every letter appear +# once and only once. You must make sure your result is +# the smallest in lexicographical order among all +# possible results. +# +# Example: +# Given "bcabc" +# Return "abc" +# +# Given "cbacdcbc" +# Return "acdb" + +import collections + + +class Solution(object): + def removeDuplicateLetters(self, s): + """ + :type s: str + :rtype: str + """ + remaining = collections.defaultdict(int) + for c in s: + remaining[c] += 1 + + in_stack, stk = set(), [] + for c in s: + if c not in in_stack: + while stk and stk[-1] > c and remaining[stk[-1]]: + in_stack.remove(stk.pop()) + stk += c + in_stack.add(c) + remaining[c] -= 1 + return "".join(stk) diff --git a/Python/remove-duplicates-from-sorted-array-ii.py b/Python/remove-duplicates-from-sorted-array-ii.py index 527416e7f..80fb570d1 100644 --- a/Python/remove-duplicates-from-sorted-array-ii.py +++ b/Python/remove-duplicates-from-sorted-array-ii.py @@ -1,12 +1,13 @@ +from __future__ import print_function # Time: O(n) # Space: O(1) # # Follow up for "Remove Duplicates": # What if duplicates are allowed at most twice? -# +# # For example, # Given sorted array A = [1,1,1,2,2,3], -# +# # Your function should return length = 5, and A is now [1,1,2,2,3]. # @@ -14,9 +15,9 @@ class Solution: # @param a list of integers # @return an integer def removeDuplicates(self, A): - if len(A) == 0: + if not A: return 0 - + last, i, same = 0, 1, False while i < len(A): if A[last] != A[i] or not same: @@ -24,8 +25,8 @@ def removeDuplicates(self, A): last += 1 A[last] = A[i] i += 1 - + return last + 1 if __name__ == "__main__": - print Solution().removeDuplicates([1, 1, 1, 2, 2, 3]) \ No newline at end of file + print(Solution().removeDuplicates([1, 1, 1, 2, 2, 3])) diff --git a/Python/remove-duplicates-from-sorted-array.py b/Python/remove-duplicates-from-sorted-array.py index 473e645fc..c5d8cff4f 100644 --- a/Python/remove-duplicates-from-sorted-array.py +++ b/Python/remove-duplicates-from-sorted-array.py @@ -2,30 +2,26 @@ # Space: O(1) # # Given a sorted array, remove the duplicates in place such that each element appear only once and return the new length. -# +# # Do not allocate extra space for another array, you must do this in place with constant memory. -# +# # For example, # Given input array A = [1,1,2], -# +# # Your function should return length = 2, and A is now [1,2]. # -class Solution: + +class Solution(object): # @param a list of integers # @return an integer def removeDuplicates(self, A): - if len(A) == 0: + if not A: return 0 - - last, i = 0, 1 - while i < len(A): + + last = 0 + for i in xrange(len(A)): if A[last] != A[i]: last += 1 A[last] = A[i] - i += 1 - return last + 1 - -if __name__ == "__main__": - print Solution().removeDuplicates([1, 1, 2]) \ No newline at end of file diff --git a/Python/remove-duplicates-from-sorted-list-ii.py b/Python/remove-duplicates-from-sorted-list-ii.py index 5c8f44e14..c0088c186 100644 --- a/Python/remove-duplicates-from-sorted-list-ii.py +++ b/Python/remove-duplicates-from-sorted-list-ii.py @@ -1,9 +1,10 @@ +from __future__ import print_function # Time: O(n) # Space: O(1) # # Given a sorted linked list, delete all nodes that have duplicate numbers, # leaving only distinct numbers from the original list. -# +# # For example, # Given 1->2->3->3->4->4->5, return 1->2->5. # Given 1->1->1->2->3, return 2->3. @@ -14,33 +15,36 @@ class ListNode: def __init__(self, x): self.val = x self.next = None - + def __repr__(self): if self is None: return "Nil" else: return "{} -> {}".format(self.val, repr(self.next)) -class Solution: - # @param head, a ListNode - # @return a ListNode +class Solution(object): def deleteDuplicates(self, head): + """ + :type head: ListNode + :rtype: ListNode + """ dummy = ListNode(0) - dummy.next = head - current = dummy - while current.next: - next = current.next - while next.next and next.next.val == next.val: - next = next.next - if current.next is not next: - current.next = next.next + pre, cur = dummy, head + while cur: + if cur.next and cur.next.val == cur.val: + val = cur.val; + while cur and cur.val == val: + cur = cur.next + pre.next = cur else: - current = current.next + pre.next = cur + pre = cur + cur = cur.next return dummy.next - + if __name__ == "__main__": head, head.next, head.next.next = ListNode(1), ListNode(2), ListNode(3) head.next.next.next, head.next.next.next.next = ListNode(3), ListNode(4) head.next.next.next.next.next, head.next.next.next.next.next.next = ListNode(4), ListNode(5) - print Solution().deleteDuplicates(head) - \ No newline at end of file + print(Solution().deleteDuplicates(head)) + diff --git a/Python/remove-duplicates-from-sorted-list.py b/Python/remove-duplicates-from-sorted-list.py index 88e0fd370..dc29c5e2c 100644 --- a/Python/remove-duplicates-from-sorted-list.py +++ b/Python/remove-duplicates-from-sorted-list.py @@ -1,40 +1,53 @@ +from __future__ import print_function # Time: O(n) # Space: O(1) # # Given a sorted linked list, delete all duplicates such that each element appear only once. -# +# # For example, # Given 1->1->2, return 1->2. # Given 1->1->2->3->3, return 1->2->3. # # Definition for singly-linked list. -class ListNode: + + +class ListNode(object): def __init__(self, x): self.val = x self.next = None - - def __repr__(self): - if self is None: - return "Nil" - else: - return "{} -> {}".format(self.val, repr(self.next)) - -class Solution: - # @param head, a ListNode - # @return a ListNode + + +class Solution(object): def deleteDuplicates(self, head): - current = head - while current and current.next: - next = current.next - if current.val == next.val: - current.next = current.next.next + """ + :type head: ListNode + :rtype: ListNode + """ + cur = head + while cur: + runner = cur.next + while runner and runner.val == cur.val: + runner = runner.next + cur.next = runner + cur = runner + return head + + def deleteDuplicates2(self, head): + """ + :type head: ListNode + :rtype: ListNode + """ + if not head: return head + if head.next: + if head.val == head.next.val: + head = self.deleteDuplicates(head.next) else: - current = next + head.next = self.deleteDuplicates(head.next) return head + if __name__ == "__main__": head, head.next, head.next.next = ListNode(1), ListNode(1), ListNode(2) head.next.next.next, head.next.next.next.next = ListNode(3), ListNode(3) - print Solution().deleteDuplicates(head) - \ No newline at end of file + print(Solution().deleteDuplicates(head)) diff --git a/Python/remove-element.py b/Python/remove-element.py index a24b77318..0f6a3b18c 100644 --- a/Python/remove-element.py +++ b/Python/remove-element.py @@ -1,8 +1,9 @@ +from __future__ import print_function # Time: O(n) # Space: O(1) # # Given an array and a value, remove all instances of that value in place and return the new length. -# +# # The order of elements can be changed. It doesn't matter what you leave beyond the new length. # @@ -19,6 +20,6 @@ def removeElement(self, A, elem): else: i += 1 return last + 1 - + if __name__ == "__main__": - print Solution().removeElement([1, 2, 3, 4, 5, 2, 2], 2) \ No newline at end of file + print(Solution().removeElement([1, 2, 3, 4, 5, 2, 2], 2)) \ No newline at end of file diff --git a/Python/remove-invalid-parentheses.py b/Python/remove-invalid-parentheses.py new file mode 100644 index 000000000..348e4291e --- /dev/null +++ b/Python/remove-invalid-parentheses.py @@ -0,0 +1,73 @@ +# Time: O(C(n, c)), try out all possible substrings with the minimum c deletion. +# Space: O(c), the depth is at most c, and it costs n at each depth +# +# Remove the minimum number of invalid parentheses in order to +# make the input string valid. Return all possible results. +# +# Note: The input string may contain letters other than the +# parentheses ( and ). +# +# Examples: +# "()())()" -> ["()()()", "(())()"] +# "(a)())()" -> ["(a)()()", "(a())()"] +# ")(" -> [""] +# + +# DFS solution. +class Solution(object): + def removeInvalidParentheses(self, s): + """ + :type s: str + :rtype: List[str] + """ + # Calculate the minimum left and right parantheses to remove + def findMinRemove(s): + left_removed, right_removed = 0, 0 + for c in s: + if c == '(': + left_removed += 1 + elif c == ')': + if not left_removed: + right_removed += 1 + else: + left_removed -= 1 + return (left_removed, right_removed) + + # Check whether s is valid or not. + def isValid(s): + sum = 0 + for c in s: + if c == '(': + sum += 1 + elif c == ')': + sum -= 1 + if sum < 0: + return False + return sum == 0 + + def removeInvalidParenthesesHelper(start, left_removed, right_removed): + if left_removed == 0 and right_removed == 0: + tmp = "" + for i, c in enumerate(s): + if i not in removed: + tmp += c + if isValid(tmp): + res.append(tmp) + return + + for i in xrange(start, len(s)): + if right_removed == 0 and left_removed > 0 and s[i] == '(': + if i == start or s[i] != s[i - 1]: # Skip duplicated. + removed[i] = True + removeInvalidParenthesesHelper(i + 1, left_removed - 1, right_removed) + del removed[i] + elif right_removed > 0 and s[i] == ')': + if i == start or s[i] != s[i - 1]: # Skip duplicated. + removed[i] = True + removeInvalidParenthesesHelper(i + 1, left_removed, right_removed - 1); + del removed[i] + + res, removed = [], {} + (left_removed, right_removed) = findMinRemove(s) + removeInvalidParenthesesHelper(0, left_removed, right_removed) + return res diff --git a/Python/remove-k-digits.py b/Python/remove-k-digits.py new file mode 100644 index 000000000..b9b9ee749 --- /dev/null +++ b/Python/remove-k-digits.py @@ -0,0 +1,40 @@ +# Time: O(n) +# Space: O(n) + +# Given a non-negative integer num represented as a string, +# remove k digits from the number so that the new number is the smallest possible. +# +# Note: +# The length of num is less than 10^5 and will be >= k. +# The given num does not contain any leading zero. +# Example 1: +# +# Input: num = "1432219", k = 3 +# Output: "1219" +# Explanation: Remove the three digits 4, 3, and 2 to form the new number 1219 which is the smallest. +# Example 2: +# +# Input: num = "10200", k = 1 +# Output: "200" +# Explanation: Remove the leading 1 and the number is 200. +# Note that the output must not contain leading zeroes. +# Example 3: +# +# Input: num = "10", k = 2 +# Output: "0" +# Explanation: Remove all the digits from the number and it is left with nothing which is 0. + +class Solution(object): + def removeKdigits(self, num, k): + """ + :type num: str + :type k: int + :rtype: str + """ + result = [] + for d in num: + while k and result and result[-1] > d: + result.pop() + k -= 1 + result.append(d) + return ''.join(result).lstrip('0')[:-k or None] or '0' diff --git a/Python/remove-linked-list-elements.py b/Python/remove-linked-list-elements.py new file mode 100644 index 000000000..9e6226968 --- /dev/null +++ b/Python/remove-linked-list-elements.py @@ -0,0 +1,36 @@ +# Time: O(n) +# Space: O(1) +# +# Remove all elements from a linked list of integers that have value val. +# +# Example +# Given: 1 --> 2 --> 6 --> 3 --> 4 --> 5 --> 6, val = 6 +# Return: 1 --> 2 --> 3 --> 4 --> 5 +# +# Definition for singly-linked list. +class ListNode: + def __init__(self, x): + self.val = x + self.next = None + + +class Solution: + # @param {ListNode} head + # @param {integer} val + # @return {ListNode} + def removeElements(self, head, val): + dummy = ListNode(float("-inf")) + dummy.next = head + prev, curr = dummy, dummy.next + + while curr: + if curr.val == val: + prev.next = curr.next + else: + prev = curr + + curr = curr.next + + return dummy.next + + diff --git a/Python/remove-nth-node-from-end-of-list.py b/Python/remove-nth-node-from-end-of-list.py index ea655cd08..a79c34902 100644 --- a/Python/remove-nth-node-from-end-of-list.py +++ b/Python/remove-nth-node-from-end-of-list.py @@ -1,12 +1,13 @@ +from __future__ import print_function # Time: O(n) # Space: O(1) # # Given a linked list, remove the nth node from the end of list and return its head. -# +# # For example, -# +# # Given linked list: 1->2->3->4->5, and n = 2. -# +# # After removing the second node from the end, the linked list becomes 1->2->3->5. # Note: # Given n will always be valid. @@ -24,22 +25,22 @@ def __repr__(self): return "Nil" else: return "{} -> {}".format(self.val, repr(self.next)) - + class Solution: # @return a ListNode def removeNthFromEnd(self, head, n): dummy = ListNode(-1) dummy.next = head slow, fast = dummy, dummy - + for i in xrange(n): fast = fast.next - + while fast.next: slow, fast = slow.next, fast.next - + slow.next = slow.next.next - + return dummy.next if __name__ == "__main__": @@ -48,5 +49,5 @@ def removeNthFromEnd(self, head, n): head.next.next = ListNode(3) head.next.next.next = ListNode(4) head.next.next.next.next = ListNode(5) - - print Solution().removeNthFromEnd(head, 2) \ No newline at end of file + + print(Solution().removeNthFromEnd(head, 2)) \ No newline at end of file diff --git a/Python/reorder-list.py b/Python/reorder-list.py index a80c0fede..6db5922c1 100644 --- a/Python/reorder-list.py +++ b/Python/reorder-list.py @@ -1,11 +1,12 @@ +from __future__ import print_function # Time: O(n) # Space: O(1) # # Given a singly linked list L: L0->L1->...->Ln-1->Ln, # reorder it to: L0->Ln->L1->Ln-1->L2->Ln-2->... -# +# # You must do this in-place without altering the nodes' values. -# +# # For example, # Given {1,2,3,4}, reorder it to {1,4,2,3}. # @@ -15,26 +16,26 @@ class ListNode: def __init__(self, x): self.val = x self.next = None - + def __repr__(self): if self: return "{} -> {}".format(self.val, repr(self.next)) - + class Solution: # @param head, a ListNode # @return nothing def reorderList(self, head): if head == None or head.next == None: return head - + fast, slow, prev = head, head, None while fast != None and fast.next != None: fast, slow, prev = fast.next.next, slow.next, slow current, prev.next, prev = slow, None, None - + while current != None: current.next, prev, current = prev, current, current.next - + l1, l2 = head, prev dummy = ListNode(0) current = dummy @@ -42,7 +43,7 @@ def reorderList(self, head): while l1 != None and l2 != None: current.next, current, l1 = l1, l1, l1.next current.next, current, l2 = l2, l2, l2.next - + return dummy.next if __name__ == "__main__": @@ -51,4 +52,4 @@ def reorderList(self, head): head.next.next = ListNode(3) head.next.next.next = ListNode(4) head.next.next.next.next = ListNode(5) - print Solution().reorderList(head) \ No newline at end of file + print(Solution().reorderList(head)) \ No newline at end of file diff --git a/Python/reordered-power-of-2.py b/Python/reordered-power-of-2.py new file mode 100644 index 000000000..ed4e0200a --- /dev/null +++ b/Python/reordered-power-of-2.py @@ -0,0 +1,46 @@ +# Time: O((logn)^2) = O(1) due to n is a 32-bit number +# Space: O(logn) = O(1) + +# Starting with a positive integer N, +# we reorder the digits in any order (including the original order) +# such that the leading digit is not zero. +# +# Return true if and only if we can do this in a way +# such that the resulting number is a power of 2. +# +# Example 1: +# +# Input: 1 +# Output: true +# Example 2: +# +# Input: 10 +# Output: false +# Example 3: +# +# Input: 16 +# Output: true +# Example 4: +# +# Input: 24 +# Output: false +# Example 5: +# +# Input: 46 +# Output: true +# +# Note: +# - 1 <= N <= 10^9 + +import collections + + +class Solution(object): + def reorderedPowerOf2(self, N): + """ + :type N: int + :rtype: bool + """ + count = collections.Counter(str(N)) + return any(count == collections.Counter(str(1 << i)) + for i in xrange(31)) diff --git a/Python/reorganize-string.py b/Python/reorganize-string.py new file mode 100644 index 000000000..719c1aeac --- /dev/null +++ b/Python/reorganize-string.py @@ -0,0 +1,46 @@ +# Time: O(nloga) = O(n), a is the size of alphabet +# Space: O(a) = O(1) + +# Given a string S, check if the letters can be rearranged +# so that two characters that are adjacent to each other are not the same. +# +# If possible, output any possible result. If not possible, return the empty string. +# +# Example 1: +# +# Input: S = "aab" +# Output: "aba" +# Example 2: +# +# Input: S = "aaab" +# Output: "" +# +# Note: +# - S will consist of lowercase letters and have length in range [1, 500]. + +import collections +import heapq + + +class Solution(object): + def reorganizeString(self, S): + """ + :type S: str + :rtype: str + """ + counts = collections.Counter(S) + if any(v > (len(S)+1)/2 for k, v in counts.iteritems()): + return "" + + result = [] + max_heap = [] + for k, v in counts.iteritems(): + heapq.heappush(max_heap, (-v, k)) + while len(max_heap) > 1: + count1, c1 = heapq.heappop(max_heap) + count2, c2 = heapq.heappop(max_heap) + if not result or c1 != result[-1]: + result.extend([c1, c2]) + if count1+1: heapq.heappush(max_heap, (count1+1, c1)) + if count2+1: heapq.heappush(max_heap, (count2+1, c2)) + return "".join(result) + (max_heap[0][1] if max_heap else '') diff --git a/Python/repeated-dna-sequences.py b/Python/repeated-dna-sequences.py new file mode 100644 index 000000000..bb15174c3 --- /dev/null +++ b/Python/repeated-dna-sequences.py @@ -0,0 +1,51 @@ +from __future__ import print_function +# Time: O(n) +# Space: O(n) + +# All DNA is composed of a series of nucleotides abbreviated as A, C, G, and T, +# for example: "ACGAATTCCG". When studying DNA, it is sometimes useful to identify repeated sequences within the DNA. +# +# Write a function to find all the 10-letter-long sequences (substrings) that occur more than once in a DNA molecule. +# +# For example, +# +# Given s = "AAAAACCCCCAAAAACCCCCCAAAAAGGGTTT", +# +# Return: +# ["AAAAACCCCC", "CCCCCAAAAA"]. + +import collections + +class Solution(object): + def findRepeatedDnaSequences(self, s): + """ + :type s: str + :rtype: List[str] + """ + dict, rolling_hash, res = {}, 0, [] + + for i in xrange(len(s)): + rolling_hash = ((rolling_hash << 3) & 0x3fffffff) | (ord(s[i]) & 7) + if rolling_hash not in dict: + dict[rolling_hash] = True + elif dict[rolling_hash]: + res.append(s[i - 9: i + 1]) + dict[rolling_hash] = False + return res + + def findRepeatedDnaSequences2(self, s): + """ + :type s: str + :rtype: List[str] + """ + l, r = [], [] + if len(s) < 10: return [] + for i in range(len(s) - 9): + l.extend([s[i:i + 10]]) + return [k for k, v in collections.Counter(l).items() if v > 1] + + +if __name__ == "__main__": + print(Solution().findRepeatedDnaSequences("AAAAAAAAAA")) + print(Solution().findRepeatedDnaSequences("")) + print(Solution().findRepeatedDnaSequences("AAAAACCCCCAAAAACCCCCCAAAAAGGGTTT")) diff --git a/Python/repeated-string-match.py b/Python/repeated-string-match.py new file mode 100644 index 000000000..7b17def8c --- /dev/null +++ b/Python/repeated-string-match.py @@ -0,0 +1,53 @@ +# Time: O(n + m) +# Space: O(1) + +# Given two strings A and B, find the minimum number of times A has to be repeated +# such that B is a substring of it. If no such solution, return -1. +# +# For example, with A = "abcd" and B = "cdabcdab". +# +# Return 3, because by repeating A three times (“abcdabcdabcd”), B is a substring of it; +# and B is not a substring of A repeated two times ("abcdabcd"). +# +# Note: +# The length of A and B will be between 1 and 10000. + +# Rabin-Karp Algorithm (rolling hash) +class Solution(object): + def repeatedStringMatch(self, A, B): + """ + :type A: str + :type B: str + :rtype: int + """ + def check(index): + return all(A[(i+index) % len(A)] == c + for i, c in enumerate(B)) + + M, p = 10**9+7, 113 + p_inv = pow(p, M-2, M) + q = (len(B)+len(A)-1) // len(A) + + b_hash, power = 0, 1 + for c in B: + b_hash += power * ord(c) + b_hash %= M + power = (power*p) % M + + a_hash, power = 0, 1 + for i in xrange(len(B)): + a_hash += power * ord(A[i%len(A)]) + a_hash %= M + power = (power*p) % M + + if a_hash == b_hash and check(0): return q + + power = (power*p_inv) % M + for i in xrange(len(B), (q+1)*len(A)): + a_hash = (a_hash-ord(A[(i-len(B))%len(A)])) * p_inv + a_hash += power * ord(A[i%len(A)]) + a_hash %= M + if a_hash == b_hash and check(i-len(B)+1): + return q if i < q*len(A) else q+1 + + return -1 diff --git a/Python/repeated-substring-pattern.py b/Python/repeated-substring-pattern.py new file mode 100644 index 000000000..8a158f903 --- /dev/null +++ b/Python/repeated-substring-pattern.py @@ -0,0 +1,60 @@ +from __future__ import print_function +# Time: O(n) +# Space: O(n) + +# Given a non-empty string check if it can be constructed by taking a substring of it +# and appending multiple copies of the substring together. +# You may assume the given string consists of lowercase English letters only and its length will not exceed 10000. +# +# Example 1: +# Input: "abab" +# +# Output: True +# +# Explanation: It's the substring "ab" twice. +# Example 2: +# Input: "aba" +# +# Output: False +# Example 3: +# Input: "abcabcabcabc" +# +# Output: True +# +# Explanation: It's the substring "abc" four times. (And the substring "abcabc" twice.) + +# KMP solution. + + +class Solution(object): + def repeatedSubstringPattern(self, str): + """ + :type str: str + :rtype: bool + """ + def getPrefix(pattern): + prefix = [-1] * len(pattern) + j = -1 + for i in xrange(1, len(pattern)): + while j > -1 and pattern[j + 1] != pattern[i]: + j = prefix[j] + if pattern[j + 1] == pattern[i]: + j += 1 + prefix[i] = j + return prefix + + prefix = getPrefix(str) + return prefix[-1] != -1 and \ + (prefix[-1] + 1) % (len(str) - prefix[-1] - 1) == 0 + + def repeatedSubstringPattern2(self, str): + """ + :type str: str + :rtype: bool + """ + if not str: + return False + + ss = (str + str)[1:-1] + print(ss) + return ss.find(str) != -1 diff --git a/Python/replace-words.py b/Python/replace-words.py new file mode 100644 index 000000000..662f8ec0f --- /dev/null +++ b/Python/replace-words.py @@ -0,0 +1,50 @@ +# Time: O(n) +# Space: O(t), t is the number of nodes in trie + +# In English, we have a concept called root, which can be followed by +# some other words to form another longer word - let's call this word successor. +# For example, the root an, followed by other, which can form another word another. +# +# Now, given a dictionary consisting of many roots and a sentence. +# You need to replace all the successor in the sentence with the root forming it. +# If a successor has many roots can form it, replace it with the root with the shortest length. +# +# You need to output the sentence after the replacement. +# +# Example 1: +# Input: dict = ["cat", "bat", "rat"] +# sentence = "the cattle was rattled by the battery" +# Output: "the cat was rat by the bat" +# Note: +# The input will only have lower-case letters. +# 1 <= dict words number <= 1000 +# 1 <= sentence words number <= 1000 +# 1 <= root length <= 100 +# 1 <= sentence words length <= 1000 + +import collections + + +class Solution(object): + def replaceWords(self, dictionary, sentence): + """ + :type dictionary: List[str] + :type sentence: str + :rtype: str + """ + _trie = lambda: collections.defaultdict(_trie) + trie = _trie() + for word in dictionary: + reduce(dict.__getitem__, word, trie).setdefault("_end") + + def replace(word): + curr = trie + for i, c in enumerate(word): + if c not in curr: + break + curr = curr[c] + if "_end" in curr: + return word[:i+1] + return word + + return " ".join(map(replace, sentence.split())) diff --git a/Python/reshape-the-matrix.py b/Python/reshape-the-matrix.py new file mode 100644 index 000000000..6c5eba08e --- /dev/null +++ b/Python/reshape-the-matrix.py @@ -0,0 +1,63 @@ +# Time: O(m * n) +# Space: O(m * n) + +# In MATLAB, there is a very useful function called 'reshape', +# which can reshape a matrix into a new one with different size but keep its original data. +# +# You're given a matrix represented by a two-dimensional array, +# and two positive integers r and c representing the row number +# and column number of the wanted reshaped matrix, respectively. +# +# The reshaped matrix need to be filled with +# all the elements of the original matrix in the same row-traversing order as they were. +# +# If the 'reshape' operation with given parameters is possible and legal, +# output the new reshaped matrix; Otherwise, output the original matrix. +# +# Example 1: +# Input: +# nums = +# [[1,2], +# [3,4]] +# r = 1, c = 4 +# Output: +# [[1,2,3,4]] +# Explanation: +# The row-traversing of nums is [1,2,3,4]. The new reshaped matrix is a 1 * 4 matrix, +# fill it row by row by using the previous list. +# +# Example 2: +# Input: +# nums = +# [[1,2], +# [3,4]] +# r = 2, c = 4 +# Output: +# [[1,2], +# [3,4]] +# Explanation: +# There is no way to reshape a 2 * 2 matrix to a 2 * 4 matrix. So output the original matrix. +# +# Note: +# The height and width of the given matrix is in range [1, 100]. +# The given r and c are all positive. + +class Solution(object): + def matrixReshape(self, nums, r, c): + """ + :type nums: List[List[int]] + :type r: int + :type c: int + :rtype: List[List[int]] + """ + if not nums or \ + r*c != len(nums) * len(nums[0]): + return nums + + result = [[0 for _ in xrange(c)] for _ in xrange(r)] + count = 0 + for i in xrange(len(nums)): + for j in xrange(len(nums[0])): + result[count/c][count%c] = nums[i][j] + count += 1 + return result diff --git a/Python/restore-ip-addresses.py b/Python/restore-ip-addresses.py index 63e6b9a71..90d126060 100644 --- a/Python/restore-ip-addresses.py +++ b/Python/restore-ip-addresses.py @@ -1,11 +1,12 @@ +from __future__ import print_function # Time: O(n^m) = O(3^4) # Space: O(n * m) = O(3 * 4) # # Given a string containing only digits, restore it by returning all possible valid IP address combinations. -# +# # For example: # Given "25525511135", -# +# # return ["255.255.11.135", "255.255.111.35"]. (Order does not matter) # @@ -16,12 +17,12 @@ def restoreIpAddresses(self, s): result = [] self.restoreIpAddressesRecur(result, s, 0, "", 0) return result - + def restoreIpAddressesRecur(self, result, s, start, current, dots): # pruning to improve performance if (4 - dots) * 3 < len(s) - start or (4 - dots) > len(s) - start: return - + if start == len(s) and dots == 4: result.append(current[:-1]) else: @@ -30,11 +31,11 @@ def restoreIpAddressesRecur(self, result, s, start, current, dots): current += s[start:i + 1] + '.' self.restoreIpAddressesRecur(result, s, i + 1, current, dots + 1) current = current[:-(i - start + 2)] - + def isValid(self, s): - if len(s) == 0 or (s[0] == "0" and s != "0"): + if len(s) == 0 or (s[0] == '0' and s != "0"): return False return int(s) < 256 - + if __name__ == "__main__": - print Solution().restoreIpAddresses("0000") + print(Solution().restoreIpAddresses("0000")) diff --git a/Python/reverse-bits.py b/Python/reverse-bits.py new file mode 100644 index 000000000..9d33d712b --- /dev/null +++ b/Python/reverse-bits.py @@ -0,0 +1,35 @@ +from __future__ import print_function +# Time : O(logn) = O(32) +# Space: O(1) +# +# Reverse bits of a given 32 bits unsigned integer. +# +# For example, given input 43261596 (represented in binary as +# 00000010100101000001111010011100), return 964176192 (represented in binary +# as 00111001011110000010100101000000). +# +# Follow up: +# If this function is called many times, how would you optimize it? +# + +class Solution: + # @param n, an integer + # @return an integer + def reverseBits(self, n): + result = 0 + for i in xrange(32): + result <<= 1 + result |= n & 1 + n >>= 1 + return result + + def reverseBits2(self, n): + string = bin(n) + if '-' in string: + string = string[:3] + string[3:].zfill(32)[::-1] + else: + string = string[:2] + string[2:].zfill(32)[::-1] + return int(string, 2) + +if __name__ == '__main__': + print(Solution().reverseBits(1)) diff --git a/Python/reverse-integer.py b/Python/reverse-integer.py index 69692d017..7c8ae7f46 100644 --- a/Python/reverse-integer.py +++ b/Python/reverse-integer.py @@ -1,37 +1,63 @@ -# Time: O(logn) +from __future__ import print_function +# Time: O(logn) = O(1) # Space: O(1) # # Reverse digits of an integer. -# +# # Example1: x = 123, return 321 # Example2: x = -123, return -321 -# +# # click to show spoilers. -# +# # Have you thought about this? # Here are some good questions to ask before coding. Bonus points for you if you have already thought through this! -# +# # If the integer's last digit is 0, what should the output be? ie, cases such as 10, 100. -# -# Did you notice that the reversed integer might overflow? Assume the input is a 32-bit integer, +# +# Did you notice that the reversed integer might overflow? Assume the input is a 32-bit integer, # then the reverse of 1000000003 overflows. How should you handle such cases? -# -# Throw an exception? Good, but what if throwing an exception is not an option? -# You would then have to re-design the function (ie, add an extra parameter). # +# Throw an exception? Good, but what if throwing an exception is not an option? +# You would then have to re-design the function (ie, add an extra parameter). + -class Solution: - # @return an integer +class Solution(object): def reverse(self, x): - ans = 0 - if x >= 0: - while x: - ans = ans * 10 + x % 10 - x /= 10 - return ans - else: + """ + :type x: int + :rtype: int + """ + if x < 0: return -self.reverse(-x) - + + result = 0 + while x: + result = result * 10 + x % 10 + x //= 10 + return result if result <= 0x7fffffff else 0 # Handle overflow. + + def reverse2(self, x): + """ + :type x: int + :rtype: int + """ + if x < 0: + x = int(str(x)[::-1][-1] + str(x)[::-1][:-1]) + else: + x = int(str(x)[::-1]) + x = 0 if abs(x) > 0x7FFFFFFF else x + return x + + def reverse3(self, x): + """ + :type x: int + :rtype: int + """ + s = cmp(x, 0) + r = int(repr(s * x)[::-1]) + return s * r * (r < 2 ** 31) + + if __name__ == "__main__": - print Solution().reverse(123) - print Solution().reverse(-321) \ No newline at end of file + print(Solution().reverse(123)) + print(Solution().reverse(-321)) diff --git a/Python/reverse-linked-list-ii.py b/Python/reverse-linked-list-ii.py index 4314d1b6f..42fabb533 100644 --- a/Python/reverse-linked-list-ii.py +++ b/Python/reverse-linked-list-ii.py @@ -1,13 +1,14 @@ +from __future__ import print_function # Time: O(n) # Space: O(1) # # Reverse a linked list from position m to n. Do it in-place and in one-pass. -# +# # For example: # Given 1->2->3->4->5->NULL, m = 2 and n = 4, -# +# # return 1->4->3->2->5->NULL. -# +# # Note: # Given m, n satisfy the following condition: # 1 <= m <= n <= length of list. @@ -18,7 +19,7 @@ class ListNode: def __init__(self, x): self.val = x self.next = None - + def __repr__(self): if self: return "{} -> {}".format(self.val, repr(self.next)) @@ -30,17 +31,17 @@ class Solution: def reverseBetween(self, head, m, n): diff, dummy, cur = n - m + 1, ListNode(-1), head dummy.next = head - + last_unswapped = dummy while cur and m > 1: cur, last_unswapped, m = cur.next, cur, m - 1 - + prev, first_swapped = last_unswapped, cur while cur and diff > 0: cur.next, prev, cur, diff = prev, cur, cur.next, diff - 1 - + last_unswapped.next, first_swapped.next = prev, cur - + return dummy.next if __name__ == "__main__": @@ -49,5 +50,4 @@ def reverseBetween(self, head, m, n): head.next.next = ListNode(3) head.next.next.next = ListNode(4) head.next.next.next.next = ListNode(5) - print Solution().reverseBetween(head, 2, 4) - \ No newline at end of file + print(Solution().reverseBetween(head, 2, 4)) diff --git a/Python/reverse-linked-list.py b/Python/reverse-linked-list.py new file mode 100644 index 000000000..ae62a1b29 --- /dev/null +++ b/Python/reverse-linked-list.py @@ -0,0 +1,62 @@ +from __future__ import print_function +# Time: O(n) +# Space: O(1) +# +# Reverse a singly linked list. +# +# click to show more hints. +# +# Hint: +# A linked list can be reversed either iteratively or recursively. Could you implement both? +# + +# Definition for singly-linked list. +class ListNode: + def __init__(self, x): + self.val = x + self.next = None + + def __repr__(self): + if self: + return "{} -> {}".format(self.val, repr(self.next)) + +# Iterative solution. +class Solution: + # @param {ListNode} head + # @return {ListNode} + def reverseList(self, head): + dummy = ListNode(float("-inf")) + while head: + dummy.next, head.next, head = head, dummy.next, head.next + return dummy.next + +# Time: O(n) +# Space: O(n) +# Recursive solution. +class Solution2: + # @param {ListNode} head + # @return {ListNode} + def reverseList(self, head): + [begin, end] = self.reverseListRecu(head) + return begin + + def reverseListRecu(self, head): + if not head: + return [None, None] + + [begin, end] = self.reverseListRecu(head.next) + + if end: + end.next = head + head.next = None + return [begin, head] + else: + return [head, head] + +if __name__ == "__main__": + head = ListNode(1) + head.next = ListNode(2) + head.next.next = ListNode(3) + head.next.next.next = ListNode(4) + head.next.next.next.next = ListNode(5) + print(Solution2().reverseList(head)) \ No newline at end of file diff --git a/Python/reverse-nodes-in-k-group.py b/Python/reverse-nodes-in-k-group.py index 359fce169..373c7bf94 100644 --- a/Python/reverse-nodes-in-k-group.py +++ b/Python/reverse-nodes-in-k-group.py @@ -1,19 +1,20 @@ +from __future__ import print_function # Time: O(n) # Space: O(1) # # Given a linked list, reverse the nodes of a linked list k at a time and return its modified list. -# +# # If the number of nodes is not a multiple of k then left-out nodes in the end should remain as it is. -# +# # You may not alter the values in the nodes, only nodes itself may be changed. -# +# # Only constant memory is allowed. -# +# # For example, # Given this linked list: 1->2->3->4->5 -# +# # For k = 2, you should return: 2->1->4->3->5 -# +# # For k = 3, you should return: 3->2->1->4->5 # @@ -21,8 +22,8 @@ class ListNode: def __init__(self, x): self.val = x - self.next = None - + self.next = None + def __repr__(self): if self: return "{} -> {}".format(self.val, repr(self.next)) @@ -34,27 +35,27 @@ class Solution: def reverseKGroup(self, head, k): dummy = ListNode(-1) dummy.next = head - + cur, cur_dummy = head, dummy length = 0 - + while cur: next_cur = cur.next length = (length + 1) % k - + if length == 0: next_dummy = cur_dummy.next self.reverse(cur_dummy, cur.next) cur_dummy = next_dummy - + cur = next_cur - + return dummy.next - + def reverse(self, begin, end): first = begin.next cur = first.next - + while cur != end: first.next = cur.next cur.next = begin.next @@ -67,4 +68,4 @@ def reverse(self, begin, end): head.next.next = ListNode(3) head.next.next.next = ListNode(4) head.next.next.next.next = ListNode(5) - print Solution().reverseKGroup(head, 2) \ No newline at end of file + print(Solution().reverseKGroup(head, 2)) \ No newline at end of file diff --git a/Python/reverse-pairs.py b/Python/reverse-pairs.py new file mode 100644 index 000000000..17a430162 --- /dev/null +++ b/Python/reverse-pairs.py @@ -0,0 +1,53 @@ +# Time: O(nlogn) +# Space: O(n) + +# Given an array nums, we call (i, j) an important reverse pair if i < j and nums[i] > 2*nums[j]. +# +# You need to return the number of important reverse pairs in the given array. +# +# Example1: +# +# Input: [1,3,2,3,1] +# Output: 2 +# Example2: +# +# Input: [2,4,3,5,1] +# Output: 3 +# Note: +# The length of the given array will not exceed 50,000. +# All the numbers in the input array are in the range of 32-bit integer. +# Show Company Tags +# Show Tags +# Hide Similar Problems + +class Solution(object): + def reversePairs(self, nums): + """ + :type nums: List[int] + :rtype: int + """ + def merge(nums, start, mid, end): + r = mid + 1 + tmp = [] + for i in xrange(start, mid + 1): + while r <= end and nums[i] > nums[r]: + tmp.append(nums[r]) + r += 1 + tmp.append(nums[i]) + nums[start:start+len(tmp)] = tmp + + def countAndMergeSort(nums, start, end): + if end - start <= 0: + return 0 + + mid = start + (end - start) / 2 + count = countAndMergeSort(nums, start, mid) + countAndMergeSort(nums, mid + 1, end) + r = mid + 1 + for i in xrange(start, mid + 1): + while r <= end and nums[i] > nums[r] * 2: + r += 1 + count += r - (mid + 1) + merge(nums, start, mid, end) + return count + + return countAndMergeSort(nums, 0, len(nums) - 1) diff --git a/Python/reverse-string-ii.py b/Python/reverse-string-ii.py new file mode 100644 index 000000000..e9958cae9 --- /dev/null +++ b/Python/reverse-string-ii.py @@ -0,0 +1,26 @@ +# Time: O(n) +# Space: O(1) + +# Given a string and an integer k, you need to reverse the first k characters +# for every 2k characters counting from the start of the string. +# If there are less than k characters left, reverse all of them. +# If there are less than 2k but greater than or equal to k characters, +# then reverse the first k characters and left the other as original. +# Example: +# Input: s = "abcdefg", k = 2 +# Output: "bacdfeg" +# Restrictions: +# The string consists of lower English letters only. +# Length of the given string and k will in the range [1, 10000] + +class Solution(object): + def reverseStr(self, s, k): + """ + :type s: str + :type k: int + :rtype: str + """ + s = list(s) + for i in xrange(0, len(s), 2*k): + s[i:i+k] = reversed(s[i:i+k]) + return "".join(s) diff --git a/Python/reverse-string.py b/Python/reverse-string.py new file mode 100644 index 000000000..be5e9ce83 --- /dev/null +++ b/Python/reverse-string.py @@ -0,0 +1,33 @@ +# Time: O(n) +# Space: O(n) + +# Write a function that takes a string as input and +# returns the string reversed. +# +# Example: +# Given s = "hello", return "olleh". + +class Solution(object): + def reverseString(self, s): + """ + :type s: str + :rtype: str + """ + string = list(s) + i, j = 0, len(string) - 1 + while i < j: + string[i], string[j] = string[j], string[i] + i += 1 + j -= 1 + return "".join(string) + + +# Time: O(n) +# Space: O(n) +class Solution2(object): + def reverseString(self, s): + """ + :type s: str + :rtype: str + """ + return s[::-1] diff --git a/Python/reverse-vowels-of-a-string.py b/Python/reverse-vowels-of-a-string.py new file mode 100644 index 000000000..359c71f8a --- /dev/null +++ b/Python/reverse-vowels-of-a-string.py @@ -0,0 +1,31 @@ +# Time: O(n) +# Space: O(1) + +# Write a function that takes a string as input +# and reverse only the vowels of a string. +# +# Example 1: +# Given s = "hello", return "holle". +# +# Example 2: +# Given s = "leetcode", return "leotcede". + +class Solution(object): + def reverseVowels(self, s): + """ + :type s: str + :rtype: str + """ + vowels = "aeiou" + string = list(s) + i, j = 0, len(s) - 1 + while i < j: + if string[i].lower() not in vowels: + i += 1 + elif string[j].lower() not in vowels: + j -= 1 + else: + string[i], string[j] = string[j], string[i] + i += 1 + j -= 1 + return "".join(string) diff --git a/Python/reverse-words-in-a-string-ii.py b/Python/reverse-words-in-a-string-ii.py new file mode 100644 index 000000000..c30b947e3 --- /dev/null +++ b/Python/reverse-words-in-a-string-ii.py @@ -0,0 +1,39 @@ +from __future__ import print_function +# Time: O(n) +# Space:O(1) +# +# Given an input string, reverse the string word by word. +# A word is defined as a sequence of non-space characters. +# +# The input string does not contain leading or trailing spaces +# and the words are always separated by a single space. +# +# For example, +# Given s = "the sky is blue", +# return "blue is sky the". +# +# Could you do it in-place without allocating extra space? +# + +class Solution(object): + def reverseWords(self, s): + """ + :type s: a list of 1 length strings (List[str]) + :rtype: nothing + """ + def reverse(s, begin, end): + for i in xrange((end - begin) / 2): + s[begin + i], s[end - 1 - i] = s[end - 1 - i], s[begin + i] + + reverse(s, 0, len(s)) + i = 0 + for j in xrange(len(s) + 1): + if j == len(s) or s[j] == ' ': + reverse(s, i, j) + i = j + 1 + + +if __name__ == '__main__': + s = ['h','e','l','l','o', ' ', 'w', 'o', 'r', 'l', 'd'] + Solution().reverseWords(s) + print(s) diff --git a/Python/reverse-words-in-a-string-iii.py b/Python/reverse-words-in-a-string-iii.py new file mode 100644 index 000000000..973b55d0b --- /dev/null +++ b/Python/reverse-words-in-a-string-iii.py @@ -0,0 +1,34 @@ +# Time: O(n) +# Space: O(1) + +# Given a string, you need to reverse the order of characters in each word within a sentence +# while still preserving whitespace and initial word order. +# +# Example 1: +# Input: "Let's take LeetCode contest" +# Output: "s'teL ekat edoCteeL tsetnoc" +# Note: In the string, each word is separated by single space and +# there will not be any extra space in the string. + +class Solution(object): + def reverseWords(self, s): + """ + :type s: str + :rtype: str + """ + def reverse(s, begin, end): + for i in xrange((end - begin) // 2): + s[begin + i], s[end - 1 - i] = s[end - 1 - i], s[begin + i] + + s, i = list(s), 0 + for j in xrange(len(s) + 1): + if j == len(s) or s[j] == ' ': + reverse(s, i, j) + i = j + 1 + return "".join(s) + + +class Solution2(object): + def reverseWords(self, s): + reversed_words = [word[::-1] for word in s.split(' ')] + return ' '.join(reversed_words) diff --git a/Python/reverse-words-in-a-string.py b/Python/reverse-words-in-a-string.py index 4d4bddc90..0db120660 100644 --- a/Python/reverse-words-in-a-string.py +++ b/Python/reverse-words-in-a-string.py @@ -1,14 +1,15 @@ +from __future__ import print_function # Time: O(n) # Space: O(n) # # Given an input string, reverse the string word by word. -# +# # For example, # Given s = "the sky is blue", # return "blue is sky the". -# +# # click to show clarification. -# +# # Clarification: # What constitutes a word? # A sequence of non-space characters constitutes a word. @@ -25,4 +26,4 @@ def reverseWords(self, s): return ' '.join(reversed(s.split())) if __name__ == '__main__': - print Solution().reverseWords('hello world') + print(Solution().reverseWords('hello world')) diff --git a/Python/robot-room-cleaner.py b/Python/robot-room-cleaner.py new file mode 100644 index 000000000..c772c58e2 --- /dev/null +++ b/Python/robot-room-cleaner.py @@ -0,0 +1,66 @@ +# Time: O(n), n is the number of cells +# Space: O(n) + +# """ +# This is the robot's control interface. +# You should not implement it, or speculate about its implementation +# """ +#class Robot(object): +# def move(self): +# """ +# Returns true if the cell in front is open and robot moves into the cell. +# Returns false if the cell in front is blocked and robot stays in the current cell. +# :rtype bool +# """ +# +# def turnLeft(self): +# """ +# Robot will stay in the same cell after calling turnLeft/turnRight. +# Each turn will be 90 degrees. +# :rtype void +# """ +# +# def turnRight(self): +# """ +# Robot will stay in the same cell after calling turnLeft/turnRight. +# Each turn will be 90 degrees. +# :rtype void +# """ +# +# def clean(self): +# """ +# Clean the current cell. +# :rtype void +# """ + +class Solution(object): + def cleanRoom(self, robot): + """ + :type robot: Robot + :rtype: None + """ + directions = [(0, 1), (1, 0), (0, -1), (-1, 0)] + + def goBack(robot): + robot.turnLeft() + robot.turnLeft() + robot.move() + robot.turnRight() + robot.turnRight() + + def dfs(pos, robot, d, lookup): + if pos in lookup: + return + lookup.add(pos) + + robot.clean() + for _ in directions: + if robot.move(): + dfs((pos[0]+directions[d][0], + pos[1]+directions[d][1]), + robot, d, lookup) + goBack(robot) + robot.turnRight() + d = (d+1) % len(directions) + + dfs((0, 0), robot, 0, set()) diff --git a/Python/roman-to-integer.py b/Python/roman-to-integer.py index 2b858fea1..0a977205d 100644 --- a/Python/roman-to-integer.py +++ b/Python/roman-to-integer.py @@ -1,8 +1,9 @@ +from __future__ import print_function # Time: O(n) # Space: O(1) # # Given a roman numeral, convert it to an integer. -# +# # Input is guaranteed to be within the xrange from 1 to 3999. # @@ -19,5 +20,5 @@ def romanToInt(self, s): return decimal if __name__ == "__main__": - print Solution().romanToInt("IIVX") - print Solution().romanToInt("MMMCMXCIX") + print(Solution().romanToInt("IIVX")) + print(Solution().romanToInt("MMMCMXCIX")) diff --git a/Python/rotate-array.py b/Python/rotate-array.py new file mode 100644 index 000000000..d2822f479 --- /dev/null +++ b/Python/rotate-array.py @@ -0,0 +1,117 @@ +from __future__ import print_function +# Time: O(n) +# Space: O(1) + +# Rotate an array of n elements to the right by k steps. +# +# For example, with n = 7 and k = 3, the array [1,2,3,4,5,6,7] is rotated to [5,6,7,1,2,3,4]. +# +# Note: +# Try to come up as many solutions as you can, there are at least 3 different ways to solve this problem. + +class Solution(object): + """ + :type nums: List[int] + :type k: int + :rtype: void Do not return anything, modify nums in-place instead. + """ + + def rotate(self, nums, k): + k %= len(nums) + self.reverse(nums, 0, len(nums)) + self.reverse(nums, 0, k) + self.reverse(nums, k, len(nums)) + + def reverse(self, nums, start, end): + while start < end: + nums[start], nums[end - 1] = nums[end - 1], nums[start] + start += 1 + end -= 1 + + +# Time: O(n) +# Space: O(1) +from fractions import gcd + + +class Solution2(object): + """ + :type nums: List[int] + :type k: int + :rtype: void Do not return anything, modify nums in-place instead. + """ + + def rotate(self, nums, k): + def apply_cycle_permutation(k, offset, cycle_len, nums): + tmp = nums[offset] + for i in xrange(1, cycle_len): + nums[(offset + i * k) % len(nums)], tmp = tmp, nums[(offset + i * k) % len(nums)] + nums[offset] = tmp + + k %= len(nums) + num_cycles = gcd(len(nums), k) + cycle_len = len(nums) / num_cycles + for i in xrange(num_cycles): + apply_cycle_permutation(k, i, cycle_len, nums) + + +# Time: O(n) +# Space: O(1) +class Solution3(object): + """ + :type nums: List[int] + :type k: int + :rtype: void Do not return anything, modify nums in-place instead. + """ + + def rotate(self, nums, k): + count = 0 + start = 0 + while count < len(nums): + curr = start + prev = nums[curr] + while True: + idx = (curr + k) % len(nums) + nums[idx], prev = prev, nums[idx] + curr = idx + count += 1 + if start == curr: + break + start += 1 + + +# Time: O(n) +# Space: O(n) +class Solution4(object): + """ + :type nums: List[int] + :type k: int + :rtype: void Do not return anything, modify nums in-place instead. + """ + def rotate(self, nums, k): + """ + :type nums: List[int] + :type k: int + :rtype: void Do not return anything, modify nums in-place instead. + """ + nums[:] = nums[len(nums) - k:] + nums[:len(nums) - k] + + +# Time: O(k * n) +# Space: O(1) +class Solution5(object): + """ + :type nums: List[int] + :type k: int + :rtype: void Do not return anything, modify nums in-place instead. + """ + def rotate(self, nums, k): + while k > 0: + nums.insert(0, nums.pop()) + k -= 1 + + +if __name__ == '__main__': + nums = [1, 2, 3, 4, 5, 6, 7] + Solution().rotate(nums, 3) + print(nums) diff --git a/Python/rotate-function.py b/Python/rotate-function.py new file mode 100644 index 000000000..654d6aa9d --- /dev/null +++ b/Python/rotate-function.py @@ -0,0 +1,42 @@ +# Time: O(n) +# Space: O(1) + +# Given an array of integers A and let n to be its length. +# +# Assume Bk to be an array obtained by rotating the array A +# k positions clock-wise, we define a "rotation function" F on A as follow: +# +# F(k) = 0 * Bk[0] + 1 * Bk[1] + ... + (n-1) * Bk[n-1]. +# +# Calculate the maximum value of F(0), F(1), ..., F(n-1). +# +# Note: +# n is guaranteed to be less than 105. +# +# Example: +# +# A = [4, 3, 2, 6] +# +# F(0) = (0 * 4) + (1 * 3) + (2 * 2) + (3 * 6) = 0 + 3 + 4 + 18 = 25 +# F(1) = (0 * 6) + (1 * 4) + (2 * 3) + (3 * 2) = 0 + 4 + 6 + 6 = 16 +# F(2) = (0 * 2) + (1 * 6) + (2 * 4) + (3 * 3) = 0 + 6 + 8 + 9 = 23 +# F(3) = (0 * 3) + (1 * 2) + (2 * 6) + (3 * 4) = 0 + 2 + 12 + 12 = 26 +# +# So the maximum value of F(0), F(1), F(2), F(3) is F(3) = 26. + +class Solution(object): + def maxRotateFunction(self, A): + """ + :type A: List[int] + :rtype: int + """ + s = sum(A) + fi = 0 + for i in xrange(len(A)): + fi += i * A[i] + + result = fi + for i in xrange(1, len(A)+1): + fi += s - len(A) * A[-i] + result = max(result, fi) + return result diff --git a/Python/rotate-image.py b/Python/rotate-image.py index c434dffa5..25b0f9dba 100644 --- a/Python/rotate-image.py +++ b/Python/rotate-image.py @@ -1,10 +1,11 @@ +from __future__ import print_function # Time: O(n^2) # Space: O(1) # # You are given an n x n 2D matrix representing an image. -# +# # Rotate the image by 90 degrees (clockwise). -# +# # Follow up: # Could you do this in-place? # @@ -16,19 +17,19 @@ class Solution: # @return a list of lists of integers def rotate(self, matrix): n = len(matrix) - + # anti-diagonal mirror for i in xrange(n): for j in xrange(n - i): - matrix[i][j], matrix[n - 1 - j][n - 1 -i] = matrix[n - 1 - j][n - 1 -i], matrix[i][j] - + matrix[i][j], matrix[n-1-j][n-1-i] = matrix[n-1-j][n-1-i], matrix[i][j] + # horizontal mirror for i in xrange(n / 2): for j in xrange(n): - matrix[i][j], matrix[n - 1 - i][j] = matrix[n - 1 - i][j], matrix[i][j] - + matrix[i][j], matrix[n-1-i][j] = matrix[n-1-i][j], matrix[i][j] + return matrix - + # Time: O(n^2) # Space: O(n^2) class Solution2: @@ -36,7 +37,7 @@ class Solution2: # @return a list of lists of integers def rotate(self, matrix): return [list(reversed(x)) for x in zip(*matrix)] - + if __name__ == "__main__": matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] - print Solution().rotate(matrix) + print(Solution().rotate(matrix)) diff --git a/Python/rotate-list.py b/Python/rotate-list.py index 7d33a6c84..717ab0bdc 100644 --- a/Python/rotate-list.py +++ b/Python/rotate-list.py @@ -1,8 +1,9 @@ +from __future__ import print_function # Time: O(n) # Space: O(1) # # Given a list, rotate the list to the right by k places, where k is non-negative. -# +# # For example: # Given 1->2->3->4->5->NULL and k = 2, # return 4->5->1->2->3->NULL. @@ -13,40 +14,40 @@ class ListNode: def __init__(self, x): self.val = x self.next = None - + def __repr__(self): if self: return "{} -> {}".format(self.val, repr(self.next)) - -class Solution: - # @param head, a ListNode - # @param k, an integer - # @return a ListNode + +class Solution(object): def rotateRight(self, head, k): - if head is None: + """ + :type head: ListNode + :type k: int + :rtype: ListNode + """ + if not head or not head.next: return head - - cur, len = head, 1 + + n, cur = 1, head while cur.next: cur = cur.next - len += 1 + n += 1 cur.next = head - - cur = head - shift = len - k % len - 1 - while shift > 0: + + cur, tail = head, cur + for _ in xrange(n - k % n): + tail = cur cur = cur.next - shift -= 1 - - result = cur.next - cur.next = None - - return result - + tail.next = None + + return cur + + if __name__ == "__main__": head = ListNode(1) head.next = ListNode(2) head.next.next = ListNode(3) head.next.next.next = ListNode(4) head.next.next.next.next = ListNode(5) - print Solution().rotateRight(head, 2) \ No newline at end of file + print(Solution().rotateRight(head, 2)) diff --git a/Python/rotate-string.py b/Python/rotate-string.py new file mode 100644 index 000000000..3a8f5151a --- /dev/null +++ b/Python/rotate-string.py @@ -0,0 +1,116 @@ +# Time: O(n) +# Space: O(1) + +# We are given two strings, A and B. +# +# A shift on A consists of taking string A and moving the leftmost character to the rightmost position. +# For example, if A = 'abcde', then it will be 'bcdea' after one shift on A. Return True +# if and only if A can become B after some number of shifts on A. +# +# Example 1: +# Input: A = 'abcde', B = 'cdeab' +# Output: true +# +# Example 2: +# Input: A = 'abcde', B = 'abced' +# Output: false +# +# Note: +# - A and B will have length at most 100. + +# Rabin-Karp Algorithm (rolling hash) +class Solution(object): + def rotateString(self, A, B): + """ + :type A: str + :type B: str + :rtype: bool + """ + def check(index): + return all(A[(i+index) % len(A)] == c + for i, c in enumerate(B)) + + if len(A) != len(B): + return False + + M, p = 10**9+7, 113 + p_inv = pow(p, M-2, M) + + b_hash, power = 0, 1 + for c in B: + b_hash += power * ord(c) + b_hash %= M + power = (power*p) % M + + a_hash, power = 0, 1 + for i in xrange(len(B)): + a_hash += power * ord(A[i%len(A)]) + a_hash %= M + power = (power*p) % M + + if a_hash == b_hash and check(0): return True + + power = (power*p_inv) % M + for i in xrange(len(B), 2*len(A)): + a_hash = (a_hash-ord(A[(i-len(B))%len(A)])) * p_inv + a_hash += power * ord(A[i%len(A)]) + a_hash %= M + if a_hash == b_hash and check(i-len(B)+1): + return True + + return False + + +# Time: O(n) +# Space: O(n) +# KMP algorithm +class Solution2(object): + def rotateString(self, A, B): + """ + :type A: str + :type B: str + :rtype: bool + """ + def strStr(haystack, needle): + def KMP(text, pattern): + prefix = getPrefix(pattern) + j = -1 + for i in xrange(len(text)): + while j > -1 and pattern[j + 1] != text[i]: + j = prefix[j] + if pattern[j + 1] == text[i]: + j += 1 + if j == len(pattern) - 1: + return i - j + return -1 + + def getPrefix(pattern): + prefix = [-1] * len(pattern) + j = -1 + for i in xrange(1, len(pattern)): + while j > -1 and pattern[j + 1] != pattern[i]: + j = prefix[j] + if pattern[j + 1] == pattern[i]: + j += 1 + prefix[i] = j + return prefix + + if not needle: + return 0 + return KMP(haystack, needle) + + if len(A) != len(B): + return False + return strStr(A*2, B) != -1 + + +# Time: O(n^2) +# Space: O(n) +class Solution3(object): + def rotateString(self, A, B): + """ + :type A: str + :type B: str + :rtype: bool + """ + return len(A) == len(B) and B in A*2 diff --git a/Python/rotated-digits.py b/Python/rotated-digits.py new file mode 100644 index 000000000..98223aa44 --- /dev/null +++ b/Python/rotated-digits.py @@ -0,0 +1,88 @@ +# Time: O(logn) +# Space: O(logn) + +# X is a good number if after rotating each digit individually by 180 degrees, +# we get a valid number that is different from X. +# A number is valid if each digit remains a digit after rotation. +# 0, 1, and 8 rotate to themselves; 2 and 5 rotate to each other; +# 6 and 9 rotate to each other, and the rest of the numbers do not rotate to any other number. +# +# Now given a positive number N, how many numbers X from 1 to N are good? +# +# Example: +# Input: 10 +# Output: 4 +# Explanation: +# There are four good numbers in the range [1, 10] : 2, 5, 6, 9. +# Note that 1 and 10 are not good numbers, since they remain unchanged after rotating. +# +# Note: +# - N will be in range [1, 10000]. + +# memoization (top-down dp) +class Solution(object): + def rotatedDigits(self, N): + """ + :type N: int + :rtype: int + """ + A = map(int, str(N)) + invalid, diff = set([3, 4, 7]), set([2, 5, 6, 9]) + def dp(A, i, is_prefix_equal, is_good, lookup): + if i == len(A): return int(is_good) + if (i, is_prefix_equal, is_good) not in lookup: + result = 0 + for d in xrange(A[i]+1 if is_prefix_equal else 10): + if d in invalid: continue + result += dp(A, i+1, + is_prefix_equal and d == A[i], + is_good or d in diff, + lookup) + lookup[i, is_prefix_equal, is_good] = result + return lookup[i, is_prefix_equal, is_good] + + lookup = {} + return dp(A, 0, True, False, lookup) + + +# Time: O(n) +# Space: O(n) +class Solution2(object): + def rotatedDigits(self, N): + """ + :type N: int + :rtype: int + """ + INVALID, SAME, DIFF = 0, 1, 2 + same, diff = [0, 1, 8], [2, 5, 6, 9] + dp = [0] * (N+1) + dp[0] = SAME + for i in xrange(N//10+1): + if dp[i] != INVALID: + for j in same: + if i*10+j <= N: + dp[i*10+j] = max(SAME, dp[i]) + for j in diff: + if i*10+j <= N: + dp[i*10+j] = DIFF + return dp.count(DIFF) + + +# Time: O(nlogn) = O(n), because O(logn) = O(32) by this input +# Space: O(logn) = O(1) +class Solution3(object): + def rotatedDigits(self, N): + """ + :type N: int + :rtype: int + """ + invalid, diff = set(['3', '4', '7']), set(['2', '5', '6', '9']) + result = 0 + for i in xrange(N+1): + lookup = set(list(str(i))) + if invalid & lookup: + continue + if diff & lookup: + result += 1 + return result + diff --git a/Python/russian-doll-envelopes.py b/Python/russian-doll-envelopes.py new file mode 100644 index 000000000..50685c92b --- /dev/null +++ b/Python/russian-doll-envelopes.py @@ -0,0 +1,42 @@ +# Time: O(nlogn + nlogk) = O(nlogn), k is the length of the result. +# Space: O(1) + +# You have a number of envelopes with widths and heights given +# as a pair of integers (w, h). One envelope can fit into another +# if and only if both the width and height of one envelope is greater +# than the width and height of the other envelope. +# +# What is the maximum number of envelopes can you Russian doll? +# (put one inside other) +# +# Example: +# Given envelopes = [[5,4],[6,4],[6,7],[2,3]], the maximum number +# of envelopes you can Russian doll is 3 ([2,3] => [5,4] => [6,7]). + +class Solution(object): + def maxEnvelopes(self, envelopes): + """ + :type envelopes: List[List[int]] + :rtype: int + """ + def insert(target): + left, right = 0, len(result) - 1 + while left <= right: + mid = left + (right - left) / 2 + if result[mid] >= target: + right = mid - 1 + else: + left = mid + 1 + if left == len(result): + result.append(target) + else: + result[left] = target + + result = [] + + envelopes.sort(lambda x, y: y[1] - x[1] if x[0] == y[0] else \ + x[0] - y[0]) + for envelope in envelopes: + insert(envelope[1]) + + return len(result) diff --git a/Python/same-tree.py b/Python/same-tree.py index b73333130..d89114b14 100644 --- a/Python/same-tree.py +++ b/Python/same-tree.py @@ -1,8 +1,9 @@ +from __future__ import print_function # Time: O(n) -# Space: O(logn) +# Space: O(h), h is height of binary tree # # Given two binary trees, write a function to check if they are equal or not. -# +# # Two binary trees are considered equal if they are structurally identical and the nodes have the same value. # @@ -20,13 +21,13 @@ class Solution: def isSameTree(self, p, q): if p is None and q is None: return True - + if p is not None and q is not None: return p.val == q.val and self.isSameTree(p.left, q.left) and self.isSameTree(p.right, q.right) - + return False - + if __name__ == "__main__": root1, root1.left, root1.right = TreeNode(1), TreeNode(2), TreeNode(3) root2, root2.left, root2.right = TreeNode(1), TreeNode(2), TreeNode(3) - print Solution().isSameTree(root1, root2) \ No newline at end of file + print(Solution().isSameTree(root1, root2)) diff --git a/Python/score-after-flipping-matrix.py b/Python/score-after-flipping-matrix.py new file mode 100644 index 000000000..9e832f8c1 --- /dev/null +++ b/Python/score-after-flipping-matrix.py @@ -0,0 +1,48 @@ +# Time: O(r * c) +# Space: O(1) + +# We have a two dimensional matrix A where each value is 0 or 1. +# +# A move consists of choosing any row or column, +# and toggling each value in that row or column: +# changing all 0s to 1s, and all 1s to 0s. +# +# After making any number of moves, every row of this matrix +# is interpreted as a binary number, and the score of the matrix +# is the sum of these numbers. +# +# Return the highest possible score. +# +# Example 1: +# +# Input: [[0,0,1,1],[1,0,1,0],[1,1,0,0]] +# Output: 39 +# Explanation: +# Toggled to [[1,1,1,1],[1,0,0,1],[1,1,1,1]]. +# 0b1111 + 0b1001 + 0b1111 = 15 + 9 + 15 = 39 +# +# Note: +# - 1 <= A.length <= 20 +# - 1 <= A[0].length <= 20 +# - A[i][j] is 0 or 1. + +try: + xrange # Python 2 +except NameError: + xrange = range # Python 3 + + +class Solution(object): + def matrixScore(self, A): + """ + :type A: List[List[int]] + :rtype: int + """ + R, C = len(A), len(A[0]) + result = 0 + for c in xrange(C): + col = 0 + for r in xrange(R): + col += A[r][c] ^ A[r][0] + result += max(col, R-col) * 2**(C-1-c) + return result diff --git a/Python/score-of-parentheses.py b/Python/score-of-parentheses.py new file mode 100644 index 000000000..6eecf01b1 --- /dev/null +++ b/Python/score-of-parentheses.py @@ -0,0 +1,70 @@ +# Time: O(n) +# Space: O(1) + +# Given a balanced parentheses string S, +# compute the score of the string based on the following rule: +# +# () has score 1 +# AB has score A + B, where A and B are balanced parentheses strings. +# (A) has score 2 * A, where A is a balanced parentheses string. +# +# Example 1: +# +# Input: "()" +# Output: 1 +# Example 2: +# +# Input: "(())" +# Output: 2 +# Example 3: +# +# Input: "()()" +# Output: 2 +# Example 4: +# +# Input: "(()(()))" +# Output: 6 +# +# Note: +# - S is a balanced parentheses string, containing only ( and ). +# - 2 <= S.length <= 50 + +try: + xrange # Python 2 +except NameError: + xrange = range # Python 3 + + +class Solution(object): + def scoreOfParentheses(self, S): + """ + :type S: str + :rtype: int + """ + result, depth = 0, 0 + for i in xrange(len(S)): + if S[i] == '(': + depth += 1 + else: + depth -= 1 + if S[i-1] == '(': + result += 2**depth + return result + + +# Time: O(n) +# Space: O(h) +class Solution2(object): + def scoreOfParentheses(self, S): + """ + :type S: str + :rtype: int + """ + stack = [0] + for c in S: + if c == '(': + stack.append(0) + else: + last = stack.pop() + stack[-1] += max(1, 2*last) + return stack[0] diff --git a/Python/scramble-string.py b/Python/scramble-string.py index f75325daf..ad0b9e8e6 100644 --- a/Python/scramble-string.py +++ b/Python/scramble-string.py @@ -1,10 +1,11 @@ +from __future__ import print_function # Time: O(n^4) # Space: O(n^3) # # Given a string s1, we may represent it as a binary tree by partitioning it to two non-empty substrings recursively. -# +# # Below is one possible representation of s1 = "great": -# +# # great # / \ # gr eat @@ -13,9 +14,9 @@ # / \ # a t # To scramble the string, we may choose any non-leaf node and swap its two children. -# +# # For example, if we choose the node "gr" and swap its two children, it produces a scrambled string "rgeat". -# +# # rgeat # / \ # rg eat @@ -24,9 +25,9 @@ # / \ # a t # We say that "rgeat" is a scrambled string of "great". -# +# # Similarly, if we continue to swap the children of nodes "eat" and "at", it produces a scrambled string "rgtae". -# +# # rgtae # / \ # rg tae @@ -35,7 +36,7 @@ # / \ # t a # We say that "rgtae" is a scrambled string of "great". -# +# # Given two strings s1 and s2 of the same length, determine if s2 is a scrambled string of s1. # @@ -47,14 +48,14 @@ class Solution: def isScramble(self, s1, s2): if not s1 or not s2 or len(s1) != len(s2): return False - if len(s1) == 0: + if s1 == s2: return True result = [[[False for j in xrange(len(s2))] for i in xrange(len(s1))] for n in xrange(len(s1) + 1)] for i in xrange(len(s1)): for j in xrange(len(s2)): if s1[i] == s2[j]: result[1][i][j] = True - + for n in xrange(2, len(s1) + 1): for i in xrange(len(s1) - n + 1): for j in xrange(len(s2) - n + 1): @@ -63,8 +64,8 @@ def isScramble(self, s1, s2): result[k][i][j + n - k] and result[n - k][i + k][j]: result[n][i][j] = True break - + return result[n][0][0] if __name__ == "__main__": - print Solution().isScramble("rgtae", "great") \ No newline at end of file + print(Solution().isScramble("rgtae", "great")) diff --git a/Python/search-a-2d-matrix-ii.py b/Python/search-a-2d-matrix-ii.py new file mode 100644 index 000000000..e61290905 --- /dev/null +++ b/Python/search-a-2d-matrix-ii.py @@ -0,0 +1,47 @@ +# Time: O(m + n) +# Space: O(1) +# +# Write an efficient algorithm that searches for a value in an m x n matrix. +# This matrix has the following properties: +# +# Integers in each row are sorted in ascending from left to right. +# Integers in each column are sorted in ascending from top to bottom. +# For example, +# +# Consider the following matrix: +# +# [ +# [1, 4, 7, 11, 15], +# [2, 5, 8, 12, 19], +# [3, 6, 9, 16, 22], +# [10, 13, 14, 17, 24], +# [18, 21, 23, 26, 30] +# ] +# Given target = 5, return true. +# +# Given target = 20, return false. +# + +class Solution: + # @param {integer[][]} matrix + # @param {integer} target + # @return {boolean} + def searchMatrix(self, matrix, target): + m = len(matrix) + if m == 0: + return False + + n = len(matrix[0]) + if n == 0: + return False + + i, j = 0, n - 1 + while i < m and j >= 0: + if matrix[i][j] == target: + return True + elif matrix[i][j] > target: + j -= 1 + else: + i += 1 + + return False diff --git a/Python/search-a-2d-matrix.py b/Python/search-a-2d-matrix.py index 712bc4dc1..dec6cbbf2 100644 --- a/Python/search-a-2d-matrix.py +++ b/Python/search-a-2d-matrix.py @@ -1,14 +1,15 @@ +from __future__ import print_function # Time: O(logm + logn) # Space: O(1) # # Write an efficient algorithm that searches for a value in an m x n matrix. This matrix has the following properties: -# +# # Integers in each row are sorted from left to right. # The first integer of each row is greater than the last integer of the previous row. # For example, -# +# # Consider the following matrix: -# +# # [ # [1, 3, 5, 7], # [10, 11, 16, 20], @@ -17,26 +18,28 @@ # Given target = 3, return true. # -class Solution: - # @param matrix, a list of lists of integers - # @param target, an integer - # @return a boolean +class Solution(object): def searchMatrix(self, matrix, target): - m = len(matrix) - n = len(matrix[0]) - i, j = 0, m * n - - while i < j: - mid = i + (j - i) / 2 - val = matrix[mid / n][mid % n] - if val == target: - return True - elif val < target: - i = mid + 1 + """ + :type matrix: List[List[int]] + :type target: int + :rtype: bool + """ + if not matrix: + return False + + m, n = len(matrix), len(matrix[0]) + left, right = 0, m * n + while left < right: + mid = left + (right - left) / 2 + if matrix[mid / n][mid % n] >= target: + right = mid else: - j = mid - return False + left = mid + 1 + + return left < m * n and matrix[left / n][left % n] == target + if __name__ == "__main__": matrix = [[1, 3, 5, 7], [10, 11, 16, 20], [23, 30, 34, 50]] - print Solution().searchMatrix(matrix, 3) \ No newline at end of file + print(Solution().searchMatrix(matrix, 3)) diff --git a/Python/search-for-a-range.py b/Python/search-for-a-range.py index 505961eed..81a53c10f 100644 --- a/Python/search-for-a-range.py +++ b/Python/search-for-a-range.py @@ -1,38 +1,64 @@ +from __future__ import print_function # Time: O(logn) # Space: O(1) # # Given a sorted array of integers, find the starting and ending position of a given target value. -# +# # Your algorithm's runtime complexity must be in the order of O(log n). -# +# # If the target is not found in the array, return [-1, -1]. -# +# # For example, # Given [5, 7, 7, 8, 8, 10] and target value 8, # return [3, 4]. # -class Solution: - # @param A, a list of integers - # @param target, an integer to be searched - # @return a list of length 2, [index1, index2] - def searchRange(self, A, target): - left = self.binarySearch(lambda x, y: x > y, A, target) - if left >= len(A) or A[left] != target: +class Solution(object): + def searchRange(self, nums, target): + """ + :type nums: List[int] + :type target: int + :rtype: List[int] + """ + # Find the first idx where nums[idx] >= target + left = self.binarySearch(lambda x, y: x >= y, nums, target) + if left >= len(nums) or nums[left] != target: return [-1, -1] - right = self.binarySearch(lambda x, y: x >= y, A, target) + # Find the first idx where nums[idx] > target + right = self.binarySearch(lambda x, y: x > y, nums, target) return [left, right - 1] - - def binarySearch(self, compare, A, target): - start, end = 0, len(A) - while start < end: - mid = start + (end - start) / 2 - if compare(target, A[mid]): - start = mid + 1 + + def binarySearch(self, compare, nums, target): + left, right = 0, len(nums) + while left < right: + mid = left + (right - left) / 2 + if compare(nums[mid], target): + right = mid else: - end = mid - return start + left = mid + 1 + return left + + def binarySearch2(self, compare, nums, target): + left, right = 0, len(nums) - 1 + while left <= right: + mid = left + (right - left) / 2 + if compare(nums[mid], target): + right = mid - 1 + else: + left = mid + 1 + return left + + def binarySearch3(self, compare, nums, target): + left, right = -1, len(nums) + while left + 1 < right: + mid = left + (right - left) / 2 + if compare(nums[mid], target): + right = mid + else: + left = mid + return left if left != -1 and compare(nums[left], target) else right + if __name__ == "__main__": - print Solution().searchRange([2, 2], 3) - print Solution().searchRange([5, 7, 7, 8, 8, 10], 8) \ No newline at end of file + print(Solution().searchRange([2, 2], 3)) + print(Solution().searchRange([5, 7, 7, 8, 8, 10], 8)) diff --git a/Python/search-in-a-binary-search-tree.py b/Python/search-in-a-binary-search-tree.py new file mode 100644 index 000000000..755970f77 --- /dev/null +++ b/Python/search-in-a-binary-search-tree.py @@ -0,0 +1,48 @@ +# Time: O(h) +# Space: O(1) + +# Given the root node of a binary search tree (BST) and a value. +# You need to find the node in the BST that the node's value equals the given value. +# Return the subtree rooted with that node. +# If such node doesn't exist, you should return NULL. +# +# For example, +# +# Given the tree: +# 4 +# / \ +# 2 7 +# / \ +# 1 3 +# +# And the value to search: 2 +# You should return this subtree: +# +# 2 +# / \ +# 1 3 +# In the example above, +# if we want to search the value 5, +# since there is no node with value 5, we should return NULL. + +# Definition for a binary tree node. +class TreeNode(object): + def __init__(self, x): + self.val = x + self.left = None + self.right = None + + +class Solution(object): + def searchBST(self, root, val): + """ + :type root: TreeNode + :type val: int + :rtype: TreeNode + """ + while root and val != root.val: + if val < root.val: + root = root.left + else: + root = root.right + return root diff --git a/Python/search-in-a-sorted-array-of-unknown-size.py b/Python/search-in-a-sorted-array-of-unknown-size.py new file mode 100644 index 000000000..9300a9e22 --- /dev/null +++ b/Python/search-in-a-sorted-array-of-unknown-size.py @@ -0,0 +1,21 @@ +# Time: O(logn) +# Space: O(1) + +class Solution(object): + def search(self, reader, target): + """ + :type reader: ArrayReader + :type target: int + :rtype: int + """ + left, right = 0, 19999 + while left <= right: + mid = left + (right-left)//2 + response = reader.get(mid) + if response > target: + right = mid-1 + elif response < target: + left = mid+1 + else: + return mid + return -1 diff --git a/Python/search-in-rotated-sorted-array-ii.py b/Python/search-in-rotated-sorted-array-ii.py index cd623c3c9..f3215fa5e 100644 --- a/Python/search-in-rotated-sorted-array-ii.py +++ b/Python/search-in-rotated-sorted-array-ii.py @@ -1,44 +1,41 @@ +from __future__ import print_function # Time: O(logn) # Space: O(1) # # Follow up for "Search in Rotated Sorted Array": # What if duplicates are allowed? -# +# # Would this affect the run-time complexity? How and why? -# +# # Write a function to determine if a given target is in the array. # -class Solution: - # @param A a list of integers - # @param target an integer - # @return a boolean - def search(self, A, target): - low, high = 0, len(A) - - while low < high: - mid = low + (high - low) / 2 - - if A[mid] == target: +class Solution(object): + def search(self, nums, target): + """ + :type nums: List[int] + :type target: int + :rtype: int + """ + left, right = 0, len(nums) - 1 + + while left <= right: + mid = left + (right - left) / 2 + + if nums[mid] == target: return True - - if A[low] < A[mid]: - if A[low] <= target and target < A[mid]: - high = mid - else: - low = mid + 1 - elif A[low] > A[mid]: - if A[mid] < target and target <= A[high - 1]: - low = mid + 1 - else: - high = mid + elif nums[mid] == nums[left]: + left += 1 + elif (nums[mid] > nums[left] and nums[left] <= target < nums[mid]) or \ + (nums[mid] < nums[left] and not (nums[mid] < target <= nums[right])): + right = mid - 1 else: - low += 1 - + left = mid + 1 + return False - + if __name__ == "__main__": - print Solution().search([3, 5, 1], 3) - print Solution().search([2, 2, 3, 3, 4, 1], 1) - print Solution().search([4, 4, 5, 6, 7, 0, 1, 2], 5) \ No newline at end of file + print(Solution().search([3, 5, 1], 3)) + print(Solution().search([2, 2, 3, 3, 4, 1], 1)) + print(Solution().search([4, 4, 5, 6, 7, 0, 1, 2], 5)) diff --git a/Python/search-in-rotated-sorted-array.py b/Python/search-in-rotated-sorted-array.py index fc35c8073..45b30445c 100644 --- a/Python/search-in-rotated-sorted-array.py +++ b/Python/search-in-rotated-sorted-array.py @@ -1,43 +1,40 @@ +from __future__ import print_function # Time: O(logn) # Space: O(1) # # Suppose a sorted array is rotated at some pivot unknown to you beforehand. -# +# # (i.e., 0 1 2 4 5 6 7 might become 4 5 6 7 0 1 2). -# +# # You are given a target value to search. If found in the array return its index, otherwise return -1. -# +# # You may assume no duplicate exists in the array. # -class Solution: - # @param A, a list of integers - # @param target, an integer to be searched - # @return an integer - def search(self, A, target): - low, high = 0, len(A) - - while low < high: - mid = low + (high - low) / 2 - - if A[mid] == target: +class Solution(object): + def search(self, nums, target): + """ + :type nums: List[int] + :type target: int + :rtype: int + """ + left, right = 0, len(nums) - 1 + + while left <= right: + mid = left + (right - left) / 2 + + if nums[mid] == target: return mid - - if A[low] <= A[mid]: - if A[low] <= target and target < A[mid]: - high = mid - else: - low = mid + 1 + elif (nums[mid] >= nums[left] and nums[left] <= target < nums[mid]) or \ + (nums[mid] < nums[left] and not (nums[mid] < target <= nums[right])): + right = mid - 1 else: - if A[mid] < target and target <= A[high - 1]: - low = mid + 1 - else: - high = mid - + left = mid + 1 + return -1 - + if __name__ == "__main__": - print Solution().search([3, 5, 1], 3) - print Solution().search([1], 1) - print Solution().search([4, 5, 6, 7, 0, 1, 2], 5) \ No newline at end of file + print(Solution().search([3, 5, 1], 3)) + print(Solution().search([1], 1)) + print(Solution().search([4, 5, 6, 7, 0, 1, 2], 5)) diff --git a/Python/search-insert-position.py b/Python/search-insert-position.py index be71509c7..4c31609c2 100644 --- a/Python/search-insert-position.py +++ b/Python/search-insert-position.py @@ -1,12 +1,13 @@ +from __future__ import print_function # Time: O(logn) # Space: O(1) # # Given a sorted array and a target value, return the index if the target is found. -# +# # If not, return the index where it would be if it were inserted in order. -# +# # You may assume no duplicates in the array. -# +# # Here are few examples. # [1,3,5,6], 5 -> 2 # [1,3,5,6], 2 -> 1 @@ -14,26 +15,26 @@ # [1,3,5,6], 0 -> 0 # -class Solution: - # @param A, a list of integers - # @param target, an integer to be inserted - # @return integer - def searchInsert(self, A, target): - low, high = 0, len(A) - 1 - - while low <= high: - mid = low + (high - low) / 2 - if A[mid] == target: - return mid - elif A[mid] < target: - low = mid + 1 +class Solution(object): + def searchInsert(self, nums, target): + """ + :type nums: List[int] + :type target: int + :rtype: int + """ + left, right = 0, len(nums) - 1 + while left <= right: + mid = left + (right - left) / 2 + if nums[mid] >= target: + right = mid - 1 else: - high = mid - 1 - - return low + left = mid + 1 + + return left + if __name__ == "__main__": - print Solution().searchInsert([1, 3, 5, 6], 5) - print Solution().searchInsert([1, 3, 5, 6], 2) - print Solution().searchInsert([1, 3, 5, 6], 7) - print Solution().searchInsert([1, 3, 5, 6], 0) \ No newline at end of file + print(Solution().searchInsert([1, 3, 5, 6], 5)) + print(Solution().searchInsert([1, 3, 5, 6], 2)) + print(Solution().searchInsert([1, 3, 5, 6], 7)) + print(Solution().searchInsert([1, 3, 5, 6], 0)) diff --git a/Python/second-minimum-node-in-a-binary-tree.py b/Python/second-minimum-node-in-a-binary-tree.py new file mode 100644 index 000000000..e300f12e5 --- /dev/null +++ b/Python/second-minimum-node-in-a-binary-tree.py @@ -0,0 +1,63 @@ +# Time: O(n) +# Space: O(h) + +# Given a non-empty special binary tree consisting of nodes with the non-negative value, +# where each node in this tree has exactly two or zero sub-node. If the node has two sub-nodes, +# then this node's value is the smaller value among its two sub-nodes. +# +# Given such a binary tree, you need to output the second minimum value in the set made of +# all the nodes' value in the whole tree. +# +# If no such second minimum value exists, output -1 instead. +# +# Example 1: +# Input: +# 2 +# / \ +# 2 5 +# / \ +# 5 7 +# +# Output: 5 +# Explanation: The smallest value is 2, the second smallest value is 5. +# Example 2: +# Input: +# 2 +# / \ +# 2 2 +# +# Output: -1 +# Explanation: The smallest value is 2, but there isn't any second smallest value. + +# Definition for a binary tree node. +# class TreeNode(object): +# def __init__(self, x): +# self.val = x +# self.left = None +# self.right = None + +import heapq + + +class Solution(object): + def findSecondMinimumValue(self, root): + """ + :type root: TreeNode + :rtype: int + """ + def findSecondMinimumValueHelper(root, max_heap, lookup): + if not root: + return + if root.val not in lookup: + heapq.heappush(max_heap, -root.val) + lookup.add(root.val) + if len(max_heap) > 2: + lookup.remove(-heapq.heappop(max_heap)) + findSecondMinimumValueHelper(root.left, max_heap, lookup) + findSecondMinimumValueHelper(root.right, max_heap, lookup) + + max_heap, lookup = [], set() + findSecondMinimumValueHelper(root, max_heap, lookup) + if len(max_heap) < 2: + return -1 + return -max_heap[0] diff --git a/Python/self-crossing.py b/Python/self-crossing.py new file mode 100644 index 000000000..76fd0d7d2 --- /dev/null +++ b/Python/self-crossing.py @@ -0,0 +1,70 @@ +# Time: O(n) +# Space: O(1) + +# You are given an array x of n positive numbers. +# You start at point (0,0) and moves x[0] metres to the north, +# then x[1] metres to the west, x[2] metres to the south, +# x[3] metres to the east and so on. In other words, +# after each move your direction changes counter-clockwise. +# +# Write a one-pass algorithm with O(1) extra space to determine, +# if your path crosses itself, or not. +# +# Example 1: +# Given x = [2, 1, 1, 2], +# ┌───┐ +# │ │ +# └───┼──> +# │ +# +# Return true (self crossing) +# Example 2: +# Given x = [1, 2, 3, 4], +# ┌──────┐ +# │ │ +# │ +# │ +# └────────────> +# +# Return false (not self crossing) +# Example 3: +# Given x = [1, 1, 1, 1], +# ┌───┐ +# │ │ +# └───┼> +# +# Return true (self crossing) + +class Solution(object): + def isSelfCrossing(self, x): + """ + :type x: List[int] + :rtype: bool + """ + if len(x) >= 5 and x[3] == x[1] and x[4] + x[0] >= x[2]: + # Crossing in a loop: + # 2 + # 3 ┌────┐ + # └─══>┘1 + # 4 0 (overlapped) + return True + + for i in xrange(3, len(x)): + if x[i] >= x[i - 2] and x[i - 3] >= x[i - 1]: + # Case 1: + # i-2 + # i-1┌─┐ + # └─┼─>i + # i-3 + return True + elif i >= 5 and x[i - 4] <= x[i - 2] and x[i] + x[i - 4] >= x[i - 2] and \ + x[i - 1] <= x[i - 3] and x[i - 5] + x[i - 1] >= x[i - 3]: + # Case 2: + # i-4 + # ┌──┐ + # │i<┼─┐ + # i-3│ i-5│i-1 + # └────┘ + # i-2 + return True + return False diff --git a/Python/self-dividing-numbers.py b/Python/self-dividing-numbers.py new file mode 100644 index 000000000..d3428690d --- /dev/null +++ b/Python/self-dividing-numbers.py @@ -0,0 +1,41 @@ +# Time: O(nlogr) = O(n) +# Space: O(logr) = O(1) + +# A self-dividing number is a number that is divisible by every digit it contains. +# +# For example, 128 is a self-dividing number because 128 % 1 == 0, +# 128 % 2 == 0, and 128 % 8 == 0. +# +# Also, a self-dividing number is not allowed to contain the digit zero. +# +# Given a lower and upper number bound, output a list of every possible self dividing number, +# including the bounds if possible. +# +# Example 1: +# Input: +# left = 1, right = 22 +# Output: [1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 15, 22] +# +# Note: +# - The boundaries of each input argument are 1 <= left <= right <= 10000. + +class Solution(object): + def selfDividingNumbers(self, left, right): + """ + :type left: int + :type right: int + :rtype: List[int] + """ + def isDividingNumber(num): + n = num + while n > 0: + if (n%10) == 0 or (num%(n%10)) != 0: + return False + n /= 10 + return True + + result = [] + for num in xrange(left, right+1): + if isDividingNumber(num): + result.append(num) + return result diff --git a/Python/sentence-screen-fitting.py b/Python/sentence-screen-fitting.py new file mode 100644 index 000000000..eb5f98046 --- /dev/null +++ b/Python/sentence-screen-fitting.py @@ -0,0 +1,32 @@ +# Time: O(r + n * c) +# Space: O(n) + +class Solution(object): + def wordsTyping(self, sentence, rows, cols): + """ + :type sentence: List[str] + :type rows: int + :type cols: int + :rtype: int + """ + def words_fit(sentence, start, cols): + if len(sentence[start]) > cols: + return 0 + + s, count = len(sentence[start]), 1 + i = (start + 1) % len(sentence) + while s + 1 + len(sentence[i]) <= cols: + s += 1 + len(sentence[i]) + count += 1 + i = (i + 1) % len(sentence) + return count + + wc = [0] * len(sentence) + for i in xrange(len(sentence)): + wc[i] = words_fit(sentence, i, cols) + + words, start = 0, 0 + for i in xrange(rows): + words += wc[start] + start = (start + wc[start]) % len(sentence) + return words / len(sentence) diff --git a/Python/sentence-similarity-ii.py b/Python/sentence-similarity-ii.py new file mode 100644 index 000000000..312e04df2 --- /dev/null +++ b/Python/sentence-similarity-ii.py @@ -0,0 +1,73 @@ +# Time: O(n + p) +# Space: O(p) + +# Given two sentences words1, words2 (each represented as an array of strings), +# and a list of similar word pairs pairs, determine if two sentences are similar. +# +# For example, words1 = ["great", "acting", "skills"] and +# words2 = ["fine", "drama", "talent"] are similar, +# if the similar word pairs are pairs = +# [["great", "good"], ["fine", "good"], ["acting","drama"], ["skills","talent"]]. +# +# Note that the similarity relation is transitive. +# For example, if "great" and "good" are similar, +# and "fine" and "good" are similar, then "great" and "fine" are similar. +# +# Similarity is also symmetric. +# For example, "great" and "fine" being similar is the same as "fine" and "great" being similar. +# +# Also, a word is always similar with itself. +# For example, the sentences words1 = ["great"], +# words2 = ["great"], pairs = [] are similar, even though there are no specified similar word pairs. +# +# Finally, sentences can only be similar if they have the same number of words. +# So a sentence like words1 = ["great"] can never be similar to words2 = ["doubleplus","good"]. +# +# Note: +# - The length of words1 and words2 will not exceed 1000. +# - The length of pairs will not exceed 2000. +# - The length of each pairs[i] will be 2. +# - The length of each words[i] and pairs[i][j] will be in the range [1, 20]. + +import itertools + + +class UnionFind(object): + def __init__(self, n): + self.set = range(n) + + def find_set(self, x): + if self.set[x] != x: + self.set[x] = self.find_set(self.set[x]) # path compression. + return self.set[x] + + def union_set(self, x, y): + x_root, y_root = map(self.find_set, (x, y)) + if x_root == y_root: + return False + self.set[min(x_root, y_root)] = max(x_root, y_root) + return True + + +class Solution(object): + def areSentencesSimilarTwo(self, words1, words2, pairs): + """ + :type words1: List[str] + :type words2: List[str] + :type pairs: List[List[str]] + :rtype: bool + """ + if len(words1) != len(words2): return False + + lookup = {} + union_find = UnionFind(2 * len(pairs)) + for pair in pairs: + for p in pair: + if p not in lookup: + lookup[p] = len(lookup) + union_find.union_set(lookup[pair[0]], lookup[pair[1]]) + + return all(w1 == w2 or + w1 in lookup and w2 in lookup and + union_find.find_set(lookup[w1]) == union_find.find_set(lookup[w2]) + for w1, w2 in itertools.izip(words1, words2)) diff --git a/Python/sentence-similarity.py b/Python/sentence-similarity.py new file mode 100644 index 000000000..2cad00bba --- /dev/null +++ b/Python/sentence-similarity.py @@ -0,0 +1,44 @@ +# Time: O(n + p) +# Space: O(p) + +# Given two sentences words1, words2 (each represented as an array of strings), +# and a list of similar word pairs pairs, determine if two sentences are similar. +# +# For example, "great acting skills" and "fine drama talent" are similar, +# if the similar word pairs are pairs = [["great", "fine"], ["acting","drama"], ["skills","talent"]]. +# +# Note that the similarity relation is not transitive. +# For example, if "great" and "fine" are similar, and "fine" and "good" are similar, +# "great" and "good" are not necessarily similar. +# +# However, similarity is symmetric. +# For example, "great" and "fine" being similar is the same as "fine" and "great" being similar. +# +# Also, a word is always similar with itself. +# For example, the sentences words1 = ["great"], words2 = ["great"], pairs = [] are similar, +# even though there are no specified similar word pairs. +# +# Finally, sentences can only be similar if they have the same number of words. +# So a sentence like words1 = ["great"] can never be similar to words2 = ["doubleplus","good"]. +# +# Note: +# - The length of words1 and words2 will not exceed 1000. +# - The length of pairs will not exceed 2000. +# - The length of each pairs[i] will be 2. +# - The length of each words[i] and pairs[i][j] will be in the range [1, 20]. + +import itertools + + +class Solution(object): + def areSentencesSimilar(self, words1, words2, pairs): + """ + :type words1: List[str] + :type words2: List[str] + :type pairs: List[List[str]] + :rtype: bool + """ + if len(words1) != len(words2): return False + lookup = set(map(tuple, pairs)) + return all(w1 == w2 or (w1, w2) in lookup or (w2, w1) in lookup \ + for w1, w2 in itertools.izip(words1, words2)) diff --git a/Python/sequence-reconstruction.py b/Python/sequence-reconstruction.py new file mode 100644 index 000000000..5de09aed0 --- /dev/null +++ b/Python/sequence-reconstruction.py @@ -0,0 +1,84 @@ +# Time: O(n * s), n is the size of org, s is the size of seqs +# Space: O(n) + +import collections + + +class Solution(object): + def sequenceReconstruction(self, org, seqs): + """ + :type org: List[int] + :type seqs: List[List[int]] + :rtype: bool + """ + if not seqs: + return False + pos = [0] * (len(org) + 1) + for i in xrange(len(org)): + pos[org[i]] = i + + is_matched = [False] * (len(org) + 1) + cnt_to_match = len(org) - 1 + for seq in seqs: + for i in xrange(len(seq)): + if not 0 < seq[i] <= len(org): + return False + if i == 0: + continue + if pos[seq[i-1]] >= pos[seq[i]]: + return False + if is_matched[seq[i-1]] == False and pos[seq[i-1]] + 1 == pos[seq[i]]: + is_matched[seq[i-1]] = True + cnt_to_match -= 1 + + return cnt_to_match == 0 + + +# Time: O(|V| + |E|) +# Space: O(|E|) +class Solution2(object): + def sequenceReconstruction(self, org, seqs): + """ + :type org: List[int] + :type seqs: List[List[int]] + :rtype: bool + """ + graph = collections.defaultdict(set) + indegree = collections.defaultdict(int) + integer_set = set() + for seq in seqs: + for i in seq: + integer_set.add(i) + if len(seq) == 1: + if seq[0] not in indegree: + indegree[seq[0]] = 0 + continue + for i in xrange(len(seq)-1): + if seq[i] not in indegree: + indegree[seq[i]] = 0 + if seq[i+1] not in graph[seq[i]]: + graph[seq[i]].add(seq[i+1]) + indegree[seq[i+1]] += 1 + + cnt_of_zero_indegree = 0 + res = [] + q = [] + for i in indegree: + if indegree[i] == 0: + cnt_of_zero_indegree += 1 + if cnt_of_zero_indegree > 1: + return False + q.append(i) + + while q: + i = q.pop() + res.append(i) + cnt_of_zero_indegree = 0 + for j in graph[i]: + indegree[j] -= 1 + if indegree[j] == 0: + cnt_of_zero_indegree += 1 + if cnt_of_zero_indegree > 1: + return False + q.append(j) + return res == org and len(org) == len(integer_set) diff --git a/Python/serialize-and-deserialize-binary-tree.py b/Python/serialize-and-deserialize-binary-tree.py new file mode 100644 index 000000000..89c2ddfa0 --- /dev/null +++ b/Python/serialize-and-deserialize-binary-tree.py @@ -0,0 +1,87 @@ +# Time: O(n) +# Space: O(h) + +# Serialization is the process of converting a data structure or +# object into a sequence of bits so that it can be stored in a file +# or memory buffer, or transmitted across a network connection link +# to be reconstructed later in the same or another computer environment. +# +# Design an algorithm to serialize and deserialize a binary tree. +# There is no restriction on how your serialization/deserialization +# algorithm should work. You just need to ensure that a binary tree can +# be serialized to a string and this string can be deserialized to the +# original tree structure. +# +# For example, you may serialize the following tree +# +# 1 +# / \ +# 2 3 +# / \ +# 4 5 +# as "[1,2,3,null,null,4,5]", just the same as how LeetCode OJ serializes +# a binary tree. You do not necessarily need to follow this format, so +# please be creative and come up with different approaches yourself. +# Note: Do not use class member/global/static variables to store states. +# Your serialize and deserialize algorithms should be stateless. +# + +# Definition for a binary tree node. +class TreeNode(object): + def __init__(self, x): + self.val = x + self.left = None + self.right = None + + +class Codec: + + def serialize(self, root): + """Encodes a tree to a single string. + + :type root: TreeNode + :rtype: str + """ + def serializeHelper(node): + if not node: + vals.append('#') + else: + vals.append(str(node.val)) + serializeHelper(node.left) + serializeHelper(node.right) + vals = [] + serializeHelper(root) + return ' '.join(vals) + + + def deserialize(self, data): + """Decodes your encoded data to tree. + + :type data: str + :rtype: TreeNode + """ + def deserializeHelper(): + val = next(vals) + if val == '#': + return None + else: + node = TreeNode(int(val)) + node.left = deserializeHelper() + node.right = deserializeHelper() + return node + def isplit(source, sep): + sepsize = len(sep) + start = 0 + while True: + idx = source.find(sep, start) + if idx == -1: + yield source[start:] + return + yield source[start:idx] + start = idx + sepsize + vals = iter(isplit(data, ' ')) + return deserializeHelper() + +# Your Codec object will be instantiated and called as such: +# codec = Codec() +# codec.deserialize(codec.serialize(root)) diff --git a/Python/serialize-and-deserialize-bst.py b/Python/serialize-and-deserialize-bst.py new file mode 100644 index 000000000..cf993194f --- /dev/null +++ b/Python/serialize-and-deserialize-bst.py @@ -0,0 +1,77 @@ +# Time: O(n) +# Space: O(h) + +# Serialization is the process of converting a data structure or +# object into a sequence of bits so that it can be stored in a file or +# memory buffer, or transmitted across a network connection link to be +# reconstructed later in the same or another computer environment. +# +# Design an algorithm to serialize and deserialize a binary search tree. +# There is no restriction on how your serialization/deserialization algorithm should work. +# You just need to ensure that a binary search tree can be serialized to a string and +# this string can be deserialized to the original tree structure. +# +# The encoded string should be as compact as possible. +# +# Note: Do not use class member/global/static variables to store states. +# Your serialize and deserialize algorithms should be stateless. + +# Definition for a binary tree node. + +import collections + + +class TreeNode(object): + def __init__(self, x): + self.val = x + self.left = None + self.right = None + + +class Codec: + + def serialize(self, root): + """Encodes a tree to a single string. + + :type root: TreeNode + :rtype: str + """ + def serializeHelper(node, vals): + if node: + vals.append(node.val) + serializeHelper(node.left, vals) + serializeHelper(node.right, vals) + + vals = [] + serializeHelper(root, vals) + + return ' '.join(map(str, vals)) + + + def deserialize(self, data): + """Decodes your encoded data to tree. + + :type data: str + :rtype: TreeNode + """ + def deserializeHelper(minVal, maxVal, vals): + if not vals: + return None + + if minVal < vals[0] < maxVal: + val = vals.popleft() + node = TreeNode(val) + node.left = deserializeHelper(minVal, val, vals) + node.right = deserializeHelper(val, maxVal, vals) + return node + else: + return None + + vals = collections.deque([int(val) for val in data.split()]) + + return deserializeHelper(float('-inf'), float('inf'), vals) + + +# Your Codec object will be instantiated and called as such: +# codec = Codec() +# codec.deserialize(codec.serialize(root)) diff --git a/Python/serialize-and-deserialize-n-ary-tree.py b/Python/serialize-and-deserialize-n-ary-tree.py new file mode 100644 index 000000000..e1bc83f1c --- /dev/null +++ b/Python/serialize-and-deserialize-n-ary-tree.py @@ -0,0 +1,68 @@ +# Time: O(n) +# Space: O(h) + +# Definition for a Node. +class Node(object): + def __init__(self, val, children): + self.val = val + self.children = children + + +class Codec: + + def serialize(self, root): + """Encodes a tree to a single string. + + :type root: Node + :rtype: str + """ + def dfs(node, vals): + if not node: + return + vals.append(str(node.val)) + for child in node.children: + dfs(child, vals) + vals.append("#") + + vals = [] + dfs(root, vals) + return " ".join(vals) + + + def deserialize(self, data): + """Decodes your encoded data to tree. + + :type data: str + :rtype: Node + """ + def isplit(source, sep): + sepsize = len(sep) + start = 0 + while True: + idx = source.find(sep, start) + if idx == -1: + yield source[start:] + return + yield source[start:idx] + start = idx + sepsize + + def dfs(vals): + val = next(vals) + if val == "#": + return None + root = Node(int(val), []) + child = dfs(vals) + while child: + root.children.append(child) + child = dfs(vals) + return root + + if not data: + return None + + return dfs(iter(isplit(data, ' '))) + + +# Your Codec object will be instantiated and called as such: +# codec = Codec() +# codec.deserialize(codec.serialize(root)) diff --git a/Python/set-intersection-size-at-least-two.py b/Python/set-intersection-size-at-least-two.py new file mode 100644 index 000000000..94cf0517d --- /dev/null +++ b/Python/set-intersection-size-at-least-two.py @@ -0,0 +1,46 @@ +# Time: O(nlogn) +# Space: O(n) + +# An integer interval [a, b] (for integers a < b) is a set of all consecutive integers from a to b, +# including a and b. +# +# Find the minimum size of a set S such that for every integer interval A in intervals, +# the intersection of S with A has size at least 2. +# +# Example 1: +# Input: intervals = [[1, 3], [1, 4], [2, 5], [3, 5]] +# Output: 3 +# Explanation: +# Consider the set S = {2, 3, 4}. For each interval, there are at least 2 elements from S in the interval. +# Also, there isn't a smaller size set that fulfills the above condition. +# Thus, we output the size of this set, which is 3. +# +# Example 2: +# Input: intervals = [[1, 2], [2, 3], [2, 4], [4, 5]] +# Output: 5 +# Explanation: +# An example of a minimum sized set is {1, 2, 3, 4, 5}. +# +# Note: +# intervals will have length in range [1, 3000]. +# intervals[i] will have length 2, representing some integer interval. +# intervals[i][j] will be an integer in [0, 10^8]. + +# greedy solution +class Solution(object): + def intersectionSizeTwo(self, intervals): + """ + :type intervals: List[List[int]] + :rtype: int + """ + intervals.sort(key = lambda s_e: (s_e[0], -s_e[1])) + cnts = [2] * len(intervals) + result = 0 + while intervals: + (start, _), cnt = intervals.pop(), cnts.pop() + for s in xrange(start, start+cnt): + for i in xrange(len(intervals)): + if cnts[i] and s <= intervals[i][1]: + cnts[i] -= 1 + result += cnt + return result diff --git a/Python/set-matrix-zeroes.py b/Python/set-matrix-zeroes.py index ea770632c..af8226320 100644 --- a/Python/set-matrix-zeroes.py +++ b/Python/set-matrix-zeroes.py @@ -1,8 +1,10 @@ +from __future__ import print_function +from functools import reduce # Time: O(m * n) # Space: O(1) # # Given a m x n matrix, if an element is 0, set its entire row and column to 0. Do it in place. -# +# # Follow up: # Did you use extra space? # A straight forward solution using O(mn) space is probably a bad idea. @@ -16,21 +18,21 @@ class Solution: def setZeroes(self, matrix): first_col = reduce(lambda acc, i: acc or matrix[i][0] == 0, xrange(len(matrix)), False) first_row = reduce(lambda acc, j: acc or matrix[0][j] == 0, xrange(len(matrix[0])), False) - + for i in xrange(1, len(matrix)): for j in xrange(1, len(matrix[0])): if matrix[i][j] == 0: matrix[i][0], matrix[0][j] = 0, 0 - + for i in xrange(1, len(matrix)): for j in xrange(1, len(matrix[0])): if matrix[i][0] == 0 or matrix[0][j] == 0: matrix[i][j] = 0 - + if first_col: for i in xrange(len(matrix)): matrix[i][0] = 0 - + if first_row: for j in xrange(len(matrix[0])): matrix[0][j] = 0 @@ -41,4 +43,4 @@ def setZeroes(self, matrix): , [1, 1, 1, 0] , [1, 1, 1, 1]] Solution().setZeroes(matrix) - print matrix \ No newline at end of file + print(matrix) \ No newline at end of file diff --git a/Python/set-mismatch.py b/Python/set-mismatch.py new file mode 100644 index 000000000..d9a24acda --- /dev/null +++ b/Python/set-mismatch.py @@ -0,0 +1,72 @@ +# Time: O(n) +# Space: O(1) + +# The set S originally contains numbers from 1 to n. +# But unfortunately, due to the data error, one of the numbers +# in the set got duplicated to another number in the set, which results +# in repetition of one number and loss of another number. +# +# Given an array nums representing the data status of this set after the error. +# Your task is to firstly find the number occurs twice and then find the number +# that is missing. Return them in the form of an array. +# +# Example 1: +# Input: nums = [1,2,2,4] +# Output: [2,3] +# Note: +# The given array size will in the range [2, 10000]. +# The given array's numbers won't have any order. + +class Solution(object): + def findErrorNums(self, nums): + """ + :type nums: List[int] + :rtype: List[int] + """ + x_xor_y = 0 + for i in xrange(len(nums)): + x_xor_y ^= nums[i] ^ (i+1) + bit = x_xor_y & ~(x_xor_y-1) + result = [0] * 2 + for i, num in enumerate(nums): + result[bool(num & bit)] ^= num + result[bool((i+1) & bit)] ^= i+1 + if result[0] not in nums: + result[0], result[1] = result[1], result[0] + return result + + +# Time: O(n) +# Space: O(1) +class Solution2(object): + def findErrorNums(self, nums): + """ + :type nums: List[int] + :rtype: List[int] + """ + result = [0] * 2 + for i in nums: + if nums[abs(i)-1] < 0: + result[0] = abs(i) + else: + nums[abs(i)-1] *= -1 + for i in xrange(len(nums)): + if nums[i] > 0: + result[1] = i+1 + else: + nums[i] *= -1 + return result + + +# Time: O(n) +# Space: O(1) +class Solution3(object): + def findErrorNums(self, nums): + """ + :type nums: List[int] + :rtype: List[int] + """ + N = len(nums) + x_minus_y = sum(nums) - N*(N+1)//2 + x_plus_y = (sum(x*x for x in nums) - N*(N+1)*(2*N+1)/6) // x_minus_y + return (x_plus_y+x_minus_y) // 2, (x_plus_y-x_minus_y) // 2 diff --git a/Python/shifting-letters.py b/Python/shifting-letters.py new file mode 100644 index 000000000..ae87d6ddb --- /dev/null +++ b/Python/shifting-letters.py @@ -0,0 +1,43 @@ +# Time: O(n) +# Space: O(1) + +# We have a string S of lowercase letters, and an integer array shifts. +# +# Call the shift of a letter, the next letter in the alphabet, +# (wrapping around so that 'z' becomes 'a'). +# +# For example, shift('a') = 'b', shift('t') = 'u', and shift('z') = 'a'. +# +# Now for each shifts[i] = x, +# we want to shift the first i+1 letters of S, x times. +# +# Return the final string after all such shifts to S are applied. +# +# Example 1: +# +# Input: S = "abc", shifts = [3,5,9] +# Output: "rpl" +# Explanation: +# We start with "abc". +# After shifting the first 1 letters of S by 3, we have "dbc". +# After shifting the first 2 letters of S by 5, we have "igc". +# After shifting the first 3 letters of S by 9, we have "rpl", the answer. +# Note: +# - 1 <= S.length = shifts.length <= 20000 +# - 0 <= shifts[i] <= 10 ^ 9 + + +class Solution(object): + def shiftingLetters(self, S, shifts): + """ + :type S: str + :type shifts: List[int] + :rtype: str + """ + result = [] + times = sum(shifts) % 26 + for i, c in enumerate(S): + index = ord(c) - ord('a') + result.append(chr(ord('a') + (index+times) % 26)) + times = (times-shifts[i]) % 26 + return "".join(result) diff --git a/Python/shopping-offers.py b/Python/shopping-offers.py new file mode 100644 index 000000000..743d8d0a3 --- /dev/null +++ b/Python/shopping-offers.py @@ -0,0 +1,60 @@ +# Time: O(n * 2^n) +# Space: O(n) + +# In LeetCode Store, there are some kinds of items to sell. Each item has a price. +# +# However, there are some special offers, and a special offer consists of one or +# more different kinds of items with a sale price. +# +# You are given the each item's price, a set of special offers, +# and the number we need to buy for each item. The job is to output the lowest price you have to pay +# for exactly certain items as given, where you could make optimal use of the special offers. +# +# Each special offer is represented in the form of an array, +# the last number represents the price you need to pay for this special offer, +# other numbers represents how many specific items you could get if you buy this offer. +# +# You could use any of special offers as many times as you want. +# +# Example 1: +# Input: [2,5], [[3,0,5],[1,2,10]], [3,2] +# Output: 14 +# Explanation: +# There are two kinds of items, A and B. Their prices are $2 and $5 respectively. +# In special offer 1, you can pay $5 for 3A and 0B +# In special offer 2, you can pay $10 for 1A and 2B. +# You need to buy 3A and 2B, so you may pay $10 for 1A and 2B (special offer #2), and $4 for 2A. +# Example 2: +# Input: [2,3,4], [[1,1,0,4],[2,2,1,9]], [1,2,1] +# Output: 11 +# Explanation: +# The price of A is $2, and $3 for B, $4 for C. +# You may pay $4 for 1A and 1B, and $9 for 2A ,2B and 1C. +# You need to buy 1A ,2B and 1C, so you may pay $4 for 1A and 1B (special offer #1), and $3 for 1B, $4 for 1C. +# You cannot add more items, though only $9 for 2A ,2B and 1C. +# Note: +# There are at most 6 kinds of items, 100 special offers. +# For each item, you need to buy at most 6 of them. +# You are not allowed to buy more items than you want, even if that would lower the overall price. + +class Solution(object): + def shoppingOffers(self, price, special, needs): + """ + :type price: List[int] + :type special: List[List[int]] + :type needs: List[int] + :rtype: int + """ + def shoppingOffersHelper(price, special, needs, i): + if i == len(special): + return sum(map(lambda x, y: x*y, price, needs)) + result = shoppingOffersHelper(price, special, needs, i+1) + for j in xrange(len(needs)): + needs[j] -= special[i][j] + if all(need >= 0 for need in needs): + result = min(result, special[i][-1] + shoppingOffersHelper(price, special, needs, i)) + for j in xrange(len(needs)): + needs[j] += special[i][j] + return result + + return shoppingOffersHelper(price, special, needs, 0) diff --git a/Python/short-encoding-of-words.py b/Python/short-encoding-of-words.py new file mode 100644 index 000000000..89d8fc3c6 --- /dev/null +++ b/Python/short-encoding-of-words.py @@ -0,0 +1,48 @@ +# Time: O(n), n is the total sum of the lengths of words +# Space: O(t), t is the number of nodes in trie + +# Given a list of words, we may encode it by writing a reference +# string S and a list of indexes A. +# +# For example, if the list of words is ["time", "me", "bell"], +# we can write it as S = "time#bell#" +# and indexes = [0, 2, 5]. +# +# Then for each index, we will recover the word by reading from +# the reference string from that +# index until we reach a "#" character. +# +# What is the length of the shortest reference string S possible +# that encodes the given words? +# +# Example: +# +# Input: words = ["time", "me", "bell"] +# Output: 10 +# Explanation: S = "time#bell#" and indexes = [0, 2, 5]. +# +# Note: +# 1. 1 <= words.length <= 2000. +# 2. 1 <= words[i].length <= 7. +# 3. Each word has only lowercase letters. + +import collections +import functools + + +class Solution(object): + def minimumLengthEncoding(self, words): + """ + :type words: List[str] + :rtype: int + """ + words = list(set(words)) + _trie = lambda: collections.defaultdict(_trie) + trie = _trie() + + nodes = [functools.reduce(dict.__getitem__, word[::-1], trie) + for word in words] + + return sum(len(word) + 1 + for i, word in enumerate(words) + if len(nodes[i]) == 0) diff --git a/Python/shortest-completing-word.py b/Python/shortest-completing-word.py new file mode 100644 index 000000000..46890b5c3 --- /dev/null +++ b/Python/shortest-completing-word.py @@ -0,0 +1,56 @@ +# Time: O(n) +# Space: O(1) + +# Find the minimum length word from a given dictionary words, +# which has all the letters from the string licensePlate. +# Such a word is said to complete the given string licensePlate +# +# Here, for letters we ignore case. +# For example, "P" on the licensePlate still matches "p" on the word. +# +# It is guaranteed an answer exists. +# If there are multiple answers, return the one that occurs first in the array. +# +# The license plate might have the same letter occurring multiple times. +# For example, given a licensePlate of "PP", +# the word "pair" does not complete the licensePlate, but the word "supper" does. +# +# Example 1: +# Input: licensePlate = "1s3 PSt", words = ["step", "steps", "stripe", "stepple"] +# Output: "steps" +# Explanation: The smallest length word that contains the letters "S", "P", "S", and "T". +# Note that the answer is not "step", because the letter "s" must occur in the word twice. +# Also note that we ignored case for the purposes of comparing whether a letter exists in the word. +# Example 2: +# Input: licensePlate = "1s3 456", words = ["looks", "pest", "stew", "show"] +# Output: "pest" +# Explanation: There are 3 smallest length words that contains the letters "s". +# We return the one that occurred first. +# Note: +# - licensePlate will be a string with length in range [1, 7]. +# - licensePlate will contain digits, spaces, or letters (uppercase or lowercase). +# - words will have a length in the range [10, 1000]. +# - Every words[i] will consist of lowercase letters, and have length in range [1, 15]. + +import collections + + +class Solution(object): + def shortestCompletingWord(self, licensePlate, words): + """ + :type licensePlate: str + :type words: List[str] + :rtype: str + """ + def contains(counter1, w2): + c2 = collections.Counter(w2.lower()) + c2.subtract(counter1) + return all(map(lambda x: x >= 0, c2.values())) + + result = None + counter = collections.Counter(c.lower() for c in licensePlate if c.isalpha()) + for word in words: + if (result is None or (len(word) < len(result))) and \ + contains(counter, word): + result = word + return result diff --git a/Python/shortest-distance-from-all-buildings.py b/Python/shortest-distance-from-all-buildings.py new file mode 100644 index 000000000..19a0b0082 --- /dev/null +++ b/Python/shortest-distance-from-all-buildings.py @@ -0,0 +1,46 @@ +# Time: O(k * m * n), k is the number of the buildings +# Space: O(m * n) + +class Solution(object): + def shortestDistance(self, grid): + """ + :type grid: List[List[int]] + :rtype: int + """ + def bfs(grid, dists, cnts, x, y): + dist, m, n = 0, len(grid), len(grid[0]) + visited = [[False for _ in xrange(n)] for _ in xrange(m)] + + pre_level = [(x, y)] + visited[x][y] = True + while pre_level: + dist += 1 + cur_level = [] + for i, j in pre_level: + for dir in [(-1, 0), (1, 0), (0, -1), (0, 1)]: + I, J = i+dir[0], j+dir[1] + if 0 <= I < m and 0 <= J < n and grid[I][J] == 0 and not visited[I][J]: + cnts[I][J] += 1 + dists[I][J] += dist + cur_level.append((I, J)) + visited[I][J] = True + + pre_level = cur_level + + + m, n, cnt = len(grid), len(grid[0]), 0 + dists = [[0 for _ in xrange(n)] for _ in xrange(m)] + cnts = [[0 for _ in xrange(n)] for _ in xrange(m)] + for i in xrange(m): + for j in xrange(n): + if grid[i][j] == 1: + cnt += 1 + bfs(grid, dists, cnts, i, j) + + shortest = float("inf") + for i in xrange(m): + for j in xrange(n): + if dists[i][j] < shortest and cnts[i][j] == cnt: + shortest = dists[i][j] + + return shortest if shortest != float("inf") else -1 diff --git a/Python/shortest-distance-to-a-character.py b/Python/shortest-distance-to-a-character.py new file mode 100644 index 000000000..80099f9f9 --- /dev/null +++ b/Python/shortest-distance-to-a-character.py @@ -0,0 +1,40 @@ +# Time: O(n) +# Space: O(1) + +# Given a string S and a character C, +# return an array of integers representing the shortest distance +# from the character C in the string. +# +# Example 1: +# +# Input: S = "loveleetcode", C = 'e' +# Output: [3, 2, 1, 0, 1, 0, 0, 1, 2, 2, 1, 0] +# +# Note: +# - S string length is in [1, 10000]. +# - C is a single character, and guaranteed to be in string S. +# - All letters in S and C are lowercase. + +import itertools + +try: + xrange # Python 2 +except NameError: + xrange = range # Python 3 + + +class Solution(object): + def shortestToChar(self, S, C): + """ + :type S: str + :type C: str + :rtype: List[int] + """ + result = [len(S)] * len(S) + prev = -len(S) + for i in itertools.chain(xrange(len(S)), + reversed(xrange(len(S)))): + if S[i] == C: + prev = i + result[i] = min(result[i], abs(i-prev)) + return result diff --git a/Python/shortest-palindrome.py b/Python/shortest-palindrome.py new file mode 100644 index 000000000..e267e597e --- /dev/null +++ b/Python/shortest-palindrome.py @@ -0,0 +1,82 @@ +# Time: O(n) +# Space: O(n) +# +# Given a string S, you are allowed to convert it to a palindrome +# by adding characters in front of it. Find and return the shortest +# palindrome you can find by performing this transformation. +# +# For example: +# +# Given "aacecaaa", return "aaacecaaa". +# +# Given "abcd", return "dcbabcd". +# + +# KMP Algorithm +class Solution(object): + def shortestPalindrome(self, s): + """ + :type s: str + :rtype: str + """ + def getPrefix(pattern): + prefix = [-1] * len(pattern) + j = -1 + for i in xrange(1, len(pattern)): + while j > -1 and pattern[j+1] != pattern[i]: + j = prefix[j] + if pattern[j+1] == pattern[i]: + j += 1 + prefix[i] = j + return prefix + + if not s: + return s + + A = s + s[::-1] + prefix = getPrefix(A) + i = prefix[-1] + while i >= len(s): + i = prefix[i] + return s[i+1:][::-1] + s + + +# Time: O(n) +# Space: O(n) +# Manacher's Algorithm +class Solution2(object): + def shortestPalindrome(self, s): + """ + :type s: str + :rtype: str + """ + def preProcess(s): + if not s: + return ['^', '$'] + string = ['^'] + for c in s: + string += ['#', c] + string += ['#', '$'] + return string + + string = preProcess(s) + palindrome = [0] * len(string) + center, right = 0, 0 + for i in xrange(1, len(string) - 1): + i_mirror = 2 * center - i + if right > i: + palindrome[i] = min(right - i, palindrome[i_mirror]) + else: + palindrome[i] = 0 + + while string[i + 1 + palindrome[i]] == string[i - 1 - palindrome[i]]: + palindrome[i] += 1 + + if i + palindrome[i] > right: + center, right = i, i + palindrome[i] + + max_len = 0 + for i in xrange(1, len(string) - 1): + if i - palindrome[i] == 1: + max_len = palindrome[i] + return s[len(s)-1:max_len-1:-1] + s diff --git a/Python/shortest-path-to-get-all-keys.py b/Python/shortest-path-to-get-all-keys.py new file mode 100644 index 000000000..b4663d534 --- /dev/null +++ b/Python/shortest-path-to-get-all-keys.py @@ -0,0 +1,109 @@ +# Time: O(k*r*c + |E|log|V|) = O(k*r*c + (k*|V|)*log|V|) +# = O(k*r*c + (k*(k*2^k))*log(k*2^k)) +# = O(k*r*c + (k*(k*2^k))*(logk + k*log2)) +# = O(k*r*c + (k*(k*2^k))*k) +# = O(k*r*c + k^3*2^k) +# Space: O(|V|) = O(k*2^k) + +# We are given a 2-dimensional grid. "." is an empty cell, +# "#" is a wall, "@" is the starting point, ("a", "b", ...) are keys, +# and ("A", "B", ...) are locks. +# +# We start at the starting point, and one move consists of walking one space +# in one of the 4 cardinal directions. We cannot walk outside the grid, +# or walk into a wall. If we walk over a key, we pick it up. +# We can't walk over a lock unless we have the corresponding key. +# +# For some 1 <= K <= 6, there is exactly one lowercase and one uppercase +# letter of the first K letters of the English alphabet in the grid. +# This means that there is exactly one key for each lock, and one lock for +# each key; +# and also that the letters used to represent the keys and locks were chosen +# in the same order as the English alphabet. +# +# Return the lowest number of moves to acquire all keys. If it's impossible, +# return -1. +# +# Example 1: +# +# Input: ["@.a.#","###.#","b.A.B"] +# Output: 8 +# Example 2: +# +# Input: ["@..aA","..B#.","....b"] +# Output: 6 +# +# Note: +# - 1 <= grid.length <= 30 +# - 1 <= grid[0].length <= 30 +# - grid[i][j] contains only '.', '#', '@', 'a'-'f' and 'A'-'F' +# - The number of keys is in [1, 6]. Each key has a different letter and +# opens exactly one lock. + +import collections +import heapq + +try: + xrange # Python 2 +except NameError: + xrange = range # Python 3 + + +class Solution(object): + def shortestPathAllKeys(self, grid): + """ + :type grid: List[str] + :rtype: int + """ + directions = [(0, -1), (0, 1), (-1, 0), (1, 0)] + + def bfs(grid, source, locations): + r, c = locations[source] + lookup = [[False]*(len(grid[0])) for _ in xrange(len(grid))] + lookup[r][c] = True + q = collections.deque([(r, c, 0)]) + dist = {} + while q: + r, c, d = q.popleft() + if source != grid[r][c] != '.': + dist[grid[r][c]] = d + continue + for direction in directions: + cr, cc = r+direction[0], c+direction[1] + if not ((0 <= cr < len(grid)) and + (0 <= cc < len(grid[cr]))): + continue + if grid[cr][cc] != '#' and not lookup[cr][cc]: + lookup[cr][cc] = True + q.append((cr, cc, d+1)) + return dist + + locations = {place: (r, c) + for r, row in enumerate(grid) + for c, place in enumerate(row) + if place not in '.#'} + dists = {place: bfs(grid, place, locations) for place in locations} + + # Dijkstra's algorithm + min_heap = [(0, '@', 0)] + best = collections.defaultdict(lambda: collections.defaultdict( + lambda: float("inf"))) + best['@'][0] = 0 + target_state = 2**sum(place.islower() for place in locations)-1 + while min_heap: + cur_d, place, state = heapq.heappop(min_heap) + if best[place][state] < cur_d: + continue + if state == target_state: + return cur_d + for dest, d in dists[place].iteritems(): + next_state = state + if dest.islower(): + next_state |= (1 << (ord(dest)-ord('a'))) + elif dest.isupper(): + if not (state & (1 << (ord(dest)-ord('A')))): + continue + if cur_d+d < best[dest][next_state]: + best[dest][next_state] = cur_d+d + heapq.heappush(min_heap, (cur_d+d, dest, next_state)) + return -1 diff --git a/Python/shortest-path-visiting-all-nodes.py b/Python/shortest-path-visiting-all-nodes.py new file mode 100644 index 000000000..1d3484f8d --- /dev/null +++ b/Python/shortest-path-visiting-all-nodes.py @@ -0,0 +1,57 @@ +# Time: O(n * 2^n) +# Space: O(n * 2^n) + +# An undirected, connected graph of N nodes (labeled 0, 1, 2, ..., N-1) +# is given as graph. +# +# graph.length = N, and j != i is in the list graph[i] exactly once, +# if and only if nodes i and j are connected. +# +# Return the length of the shortest path that visits every node. +# You may start and stop at any node, you may revisit nodes multiple times, +# and you may reuse edges. +# +# Example 1: +# +# Input: [[1,2,3],[0],[0],[0]] +# Output: 4 +# Explanation: One possible path is [1,0,2,0,3] +# Example 2: +# +# Input: [[1],[0,2,4],[1,3,4],[2],[1,2]] +# Output: 4 +# Explanation: One possible path is [0,1,4,2,3] +# +# Note: +# - 1 <= graph.length <= 12 +# - 0 <= graph[i].length < graph.length + +import collections + +try: + xrange # Python 2 +except NameError: + xrange = range # Python 3 + + +class Solution(object): + def shortestPathLength(self, graph): + """ + :type graph: List[List[int]] + :rtype: int + """ + dp = [[float("inf")]*(len(graph)) + for _ in xrange(1 << len(graph))] + q = collections.deque() + for i in xrange(len(graph)): + dp[1 << i][i] = 0 + q.append((1 << i, i)) + while q: + state, node = q.popleft() + steps = dp[state][node] + for nei in graph[node]: + new_state = state | (1 << nei) + if dp[new_state][nei] == float("inf"): + dp[new_state][nei] = steps+1 + q.append((new_state, nei)) + return min(dp[-1]) diff --git a/Python/shortest-subarray-with-sum-at-least-k.py b/Python/shortest-subarray-with-sum-at-least-k.py new file mode 100644 index 000000000..db63a9dce --- /dev/null +++ b/Python/shortest-subarray-with-sum-at-least-k.py @@ -0,0 +1,55 @@ +# Time: O(n) +# Space: O(n) + +# Return the length of the shortest, non-empty, +# contiguous subarray of A with sum at least K. +# If there is no non-empty subarray with sum at least K, return -1. +# +# Example 1: +# +# Input: A = [1], K = 1 +# Output: 1 +# Example 2: +# +# Input: A = [1,2], K = 4 +# Output: -1 +# Example 3: +# +# Input: A = [2,-1,2], K = 3 +# Output: 3 +# +# Note: +# - 1 <= A.length <= 50000 +# - -10 ^ 5 <= A[i] <= 10 ^ 5 +# - 1 <= K <= 10 ^ 9 + +try: + xrange # Python 2 +except NameError: + xrange = range # Python 3 + +import collections + + +class Solution(object): + def shortestSubarray(self, A, K): + """ + :type A: List[int] + :type K: int + :rtype: int + """ + accumulated_sum = [0]*(len(A)+1) + for i in xrange(len(A)): + accumulated_sum[i+1] = accumulated_sum[i]+A[i] + + result = float("inf") + mono_increasing_q = collections.deque() + for i, curr in enumerate(accumulated_sum): + while mono_increasing_q and curr <= \ + accumulated_sum[mono_increasing_q[-1]]: + mono_increasing_q.pop() + while mono_increasing_q and \ + curr-accumulated_sum[mono_increasing_q[0]] >= K: + result = min(result, i-mono_increasing_q.popleft()) + mono_increasing_q.append(i) + return result if result != float("inf") else -1 diff --git a/Python/shortest-unsorted-continuous-subarray.py b/Python/shortest-unsorted-continuous-subarray.py new file mode 100644 index 000000000..637b692a0 --- /dev/null +++ b/Python/shortest-unsorted-continuous-subarray.py @@ -0,0 +1,53 @@ +# Time: O(n) +# Space: O(1) + +# Given an integer array, you need to find one continuous subarray that +# if you only sort this subarray in ascending order, +# then the whole array will be sorted in ascending order, too. +# +# You need to find the shortest such subarray and output its length. +# +# Example 1: +# Input: [2, 6, 4, 8, 10, 9, 15] +# Output: 5 +# Explanation: You need to sort [6, 4, 8, 10, 9] in ascending order +# to make the whole array sorted in ascending order. +# +# Note: +# Then length of the input array is in range [1, 10,000]. +# The input array may contain duplicates, so ascending order here means <=. + +class Solution(object): + def findUnsortedSubarray(self, nums): + """ + :type nums: List[int] + :rtype: int + """ + n = len(nums) + left, right = -1, -2 + min_from_right, max_from_left = nums[-1], nums[0] + for i in xrange(1, n): + max_from_left = max(max_from_left, nums[i]) + min_from_right = min(min_from_right, nums[n-1-i]) + if nums[i] < max_from_left: right = i + if nums[n-1-i] > min_from_right: left = n-1-i + + +# Time: O(nlogn) +# Space: O(n) +class Solution2(object): + def findUnsortedSubarray(self, nums): + """ + :type nums: List[int] + :rtype: int + """ + a = sorted(nums) #sort the list + left, right = 0, len(nums) -1 #define left and right pointer + while (nums[left] == a[left] or nums[right] == a[right]): + if right - left <= 1: + return 0 + if nums[left] == a[left]: + left += 1 + if nums[right] == a[right]: + right -= 1 + return right - left + 1 diff --git a/Python/shortest-word-distance-ii.py b/Python/shortest-word-distance-ii.py new file mode 100644 index 000000000..21807b273 --- /dev/null +++ b/Python/shortest-word-distance-ii.py @@ -0,0 +1,31 @@ +# Time: init: O(n), lookup: O(a + b), a, b is occurences of word1, word2 +# Space: O(n) + +import collections + + +class WordDistance: + # initialize your data structure here. + # @param {string[]} words + def __init__(self, words): + self.wordIndex = collections.defaultdict(list) + for i in xrange(len(words)): + self.wordIndex[words[i]].append(i) + + # @param {string} word1 + # @param {string} word2 + # @return {integer} + # Adds a word into the data structure. + def shortest(self, word1, word2): + indexes1 = self.wordIndex[word1] + indexes2 = self.wordIndex[word2] + + i, j, dist = 0, 0, float("inf") + while i < len(indexes1) and j < len(indexes2): + dist = min(dist, abs(indexes1[i] - indexes2[j])) + if indexes1[i] < indexes2[j]: + i += 1 + else: + j += 1 + + return dist diff --git a/Python/shortest-word-distance-iii.py b/Python/shortest-word-distance-iii.py new file mode 100644 index 000000000..021aea8f7 --- /dev/null +++ b/Python/shortest-word-distance-iii.py @@ -0,0 +1,25 @@ +# Time: O(n) +# Space: O(1) + +class Solution: + # @param {string[]} words + # @param {string} word1 + # @param {string} word2 + # @return {integer} + def shortestWordDistance(self, words, word1, word2): + dist = float("inf") + is_same = (word1 == word2) + i, index1, index2 = 0, None, None + while i < len(words): + if words[i] == word1: + if is_same and index1 is not None: + dist = min(dist, abs(index1 - i)) + index1 = i + elif words[i] == word2: + index2 = i + + if index1 is not None and index2 is not None: + dist = min(dist, abs(index1 - index2)) + i += 1 + + return dist diff --git a/Python/shortest-word-distance.py b/Python/shortest-word-distance.py new file mode 100644 index 000000000..c57352eb7 --- /dev/null +++ b/Python/shortest-word-distance.py @@ -0,0 +1,22 @@ +# Time: O(n) +# Space: O(1) + +class Solution: + # @param {string[]} words + # @param {string} word1 + # @param {string} word2 + # @return {integer} + def shortestDistance(self, words, word1, word2): + dist = float("inf") + i, index1, index2 = 0, None, None + while i < len(words): + if words[i] == word1: + index1 = i + elif words[i] == word2: + index2 = i + + if index1 is not None and index2 is not None: + dist = min(dist, abs(index1 - index2)) + i += 1 + + return dist diff --git a/Python/shuffle-an-array.py b/Python/shuffle-an-array.py new file mode 100644 index 000000000..0bafe4e79 --- /dev/null +++ b/Python/shuffle-an-array.py @@ -0,0 +1,59 @@ +# Time: O(n) +# Space: O(n) + +# Shuffle a set of numbers without duplicates. +# +# Example: +# +# // Init an array with set 1, 2, and 3. +# int[] nums = {1,2,3}; +# Solution solution = new Solution(nums); +# +# // Shuffle the array [1,2,3] and return its result. +# Any permutation of [1,2,3] must equally likely to be returned. +# solution.shuffle(); +# +# // Resets the array back to its original configuration [1,2,3]. +# solution.reset(); +# +# // Returns the random shuffling of array [1,2,3]. +# solution.shuffle(); + +import random + + +class Solution(object): + + def __init__(self, nums): + """ + + :type nums: List[int] + :type size: int + """ + self.__nums = nums + + + def reset(self): + """ + Resets the array to its original configuration and return it. + :rtype: List[int] + """ + return self.__nums + + + def shuffle(self): + """ + Returns a random shuffling of the array. + :rtype: List[int] + """ + nums = list(self.__nums) + for i in xrange(len(nums)): + j = random.randint(i, len(nums)-1) + nums[i], nums[j] = nums[j], nums[i] + return nums + + +# Your Solution object will be instantiated and called as such: +# obj = Solution(nums) +# param_1 = obj.reset() +# param_2 = obj.shuffle() diff --git a/Python/similar-rgb-color.py b/Python/similar-rgb-color.py new file mode 100644 index 000000000..caefa472b --- /dev/null +++ b/Python/similar-rgb-color.py @@ -0,0 +1,18 @@ +# Time: O(1) +# Space: O(1) + +class Solution(object): + def similarRGB(self, color): + """ + :type color: str + :rtype: str + """ + def rounding(color): + q, r = divmod(int(color, 16), 17) + if r > 8: q += 1 + return '{:02x}'.format(17*q) + + return '#' + \ + rounding(color[1:3]) + \ + rounding(color[3:5]) + \ + rounding(color[5:7]) diff --git a/Python/similar-string-groups.py b/Python/similar-string-groups.py new file mode 100644 index 000000000..06c9a9664 --- /dev/null +++ b/Python/similar-string-groups.py @@ -0,0 +1,100 @@ +# Time: O(n^2 * l) ~ O(n * l^4) +# Space: O(n) ~ O(n * l^3) + +# Two strings X and Y are similar if we can swap two letters +# (in different positions) of X, so that it equals Y. +# +# For example, "tars" and "rats" are similar (swapping at positions 0 and 2), +# and "rats" and "arts" are similar, but "star" is not similar to +# "tars", "rats", or "arts". +# +# Together, these form two connected groups by similarity: +# {"tars", "rats", "arts"} and {"star"}. +# Notice that "tars" and "arts" are in the same group +# even though they are not similar. +# Formally, each group is such that a word is in the group +# if and only if it is similar +# to at least one other word in the group. +# +# We are given a list A of unique strings. +# Every string in A is an anagram of every other string in A. +# How many groups are there? +# +# Example 1: +# +# Input: ["tars","rats","arts","star"] +# Output: 2 +# +# Note: +# - A.length <= 2000 +# - A[i].length <= 1000 +# - A.length * A[i].length <= 20000 +# - All words in A consist of lowercase letters only. +# - All words in A have the same length and are anagrams of each other. +# - The judging time limit has been increased for this question. + +import collections +import itertools + +try: + xrange # Python 2 +except NameError: + xrange = range # Python 3 + + +class UnionFind(object): + def __init__(self, n): + self.set = range(n) + self.__size = n + + def find_set(self, x): + if self.set[x] != x: + self.set[x] = self.find_set(self.set[x]) # path compression. + return self.set[x] + + def union_set(self, x, y): + x_root, y_root = map(self.find_set, (x, y)) + if x_root == y_root: + return False + self.set[min(x_root, y_root)] = max(x_root, y_root) + self.__size -= 1 + return True + + def size(self): + return self.__size + + +class Solution(object): + def numSimilarGroups(self, A): + def isSimilar(a, b): + diff = 0 + for x, y in itertools.izip(a, b): + if x != y: + diff += 1 + if diff > 2: + return False + return diff == 2 + + N, L = len(A), len(A[0]) + union_find = UnionFind(N) + if N < L*L: + for (i1, word1), (i2, word2) in \ + itertools.combinations(enumerate(A), 2): + if isSimilar(word1, word2): + union_find.union_set(i1, i2) + else: + buckets = collections.defaultdict(list) + lookup = set() + for i in xrange(len(A)): + word = list(A[i]) + if A[i] not in lookup: + buckets[A[i]].append(i) + lookup.add(A[i]) + for j1, j2 in itertools.combinations(xrange(L), 2): + word[j1], word[j2] = word[j2], word[j1] + buckets["".join(word)].append(i) + word[j1], word[j2] = word[j2], word[j1] + for word in A: # Time: O(n * l^4) + for i1, i2 in itertools.combinations(buckets[word], 2): + union_find.union_set(i1, i2) + return union_find.size() diff --git a/Python/simplify-path.py b/Python/simplify-path.py index b55e73a11..6d06133fa 100644 --- a/Python/simplify-path.py +++ b/Python/simplify-path.py @@ -1,13 +1,14 @@ +from __future__ import print_function # Time: O(n) # Space: O(n) # # Given an absolute path for a file (Unix-style), simplify it. -# +# # For example, # path = "/home/", => "/home" # path = "/a/./b/../../c/", => "/c" # click to show corner cases. -# +# # Corner Cases: # Did you consider the case where path = "/../"? # In this case, you should return "/". @@ -28,5 +29,5 @@ def simplifyPath(self, path): return "/" + "/".join(stack) if __name__ == "__main__": - print Solution().simplifyPath("/../") - print Solution().simplifyPath("/home//foo/") \ No newline at end of file + print(Solution().simplifyPath("/../")) + print(Solution().simplifyPath("/home//foo/")) \ No newline at end of file diff --git a/Python/single-element-in-a-sorted-array.py b/Python/single-element-in-a-sorted-array.py new file mode 100644 index 000000000..ef54a8bef --- /dev/null +++ b/Python/single-element-in-a-sorted-array.py @@ -0,0 +1,31 @@ +# Time: O(logn) +# Space: O(1) + +# Given a sorted array consisting of only integers +# where every element appears twice except for one element +# which appears once. Find this single element that appears only once. +# +# Example 1: +# Input: [1,1,2,3,3,4,4,8,8] +# Output: 2 +# Example 2: +# Input: [3,3,7,7,10,11,11] +# Output: 10 +# Note: Your solution should run in O(log n) time and O(1) space. + +class Solution(object): + def singleNonDuplicate(self, nums): + """ + :type nums: List[int] + :rtype: int + """ + left, right = 0, len(nums)-1 + while left <= right: + mid = left + (right - left) / 2 + if not (mid%2 == 0 and mid+1 < len(nums) and \ + nums[mid] == nums[mid+1]) and \ + not (mid%2 == 1 and nums[mid] == nums[mid-1]): + right = mid-1 + else: + left = mid+1 + return nums[left] diff --git a/Python/single-number-ii.py b/Python/single-number-ii.py index e725ccf8a..6e1e91f1f 100644 --- a/Python/single-number-ii.py +++ b/Python/single-number-ii.py @@ -1,13 +1,24 @@ +from __future__ import print_function # Time: O(n) # Space: O(1) -# +# # Given an array of integers, every element appears three times except for one. Find that single one. -# +# # Note: # Your algorithm should have a linear runtime complexity. Could you implement it without using extra memory? -# +import collections + + +class Solution(object): + # @param A, a list of integer + # @return an integer + def singleNumber(self, A): + one, two = 0, 0 + for x in A: + one, two = (~x & one) | (x & ~one & ~two), (~x & two) | (x & one) + return one -class Solution: +class Solution2(object): # @param A, a list of integer # @return an integer def singleNumber(self, A): @@ -20,5 +31,35 @@ def singleNumber(self, A): two &= ~carry return one + +class Solution3(object): + def singleNumber(self, nums): + """ + :type nums: List[int] + :rtype: int + """ + return (collections.Counter(list(set(nums)) * 3) - collections.Counter(nums)).keys()[0] + + +class Solution4(object): + def singleNumber(self, nums): + """ + :type nums: List[int] + :rtype: int + """ + return (sum(set(nums)) * 3 - sum(nums)) / 2 + + +# every element appears 4 times except for one with 2 times +class SolutionEX(object): + # @param A, a list of integer + # @return an integer + # [1, 1, 1, 1, 2, 2, 2, 2, 3, 3] + def singleNumber(self, A): + one, two, three = 0, 0, 0 + for x in A: + one, two, three = (~x & one) | (x & ~one & ~two & ~three), (~x & two) | (x & one), (~x & three) | (x & two) + return two + if __name__ == "__main__": - print Solution().singleNumber([1, 1, 1, 2, 2, 2, 3]) \ No newline at end of file + print(Solution().singleNumber([1, 1, 1, 2, 2, 2, 3])) diff --git a/Python/single-number-iii.py b/Python/single-number-iii.py new file mode 100644 index 000000000..49d0e53ba --- /dev/null +++ b/Python/single-number-iii.py @@ -0,0 +1,57 @@ +# Time: O(n) +# Space: O(1) +# +# Given an array of numbers nums, in which exactly two +# elements appear only once and all the other elements +# appear exactly twice. Find the two elements that appear only once. +# +# For example: +# +# Given nums = [1, 2, 1, 3, 2, 5], return [3, 5]. +# +# Note: +# The order of the result is not important. So in the +# above example, [5, 3] is also correct. +# Your algorithm should run in linear runtime complexity. +# Could you implement it using only constant space complexity? +import operator +import collections + + +class Solution: + # @param {integer[]} nums + # @return {integer[]} + def singleNumber(self, nums): + x_xor_y = reduce(operator.xor, nums) + bit = x_xor_y & -x_xor_y + result = [0, 0] + for i in nums: + result[bool(i & bit)] ^= i + return result + + +class Solution2: + # @param {integer[]} nums + # @return {integer[]} + def singleNumber(self, nums): + x_xor_y = 0 + for i in nums: + x_xor_y ^= i + + bit = x_xor_y & ~(x_xor_y - 1) + + x = 0 + for i in nums: + if i & bit: + x ^= i + + return [x, x ^ x_xor_y] + + +class Solution3(object): + def singleNumber(self, nums): + """ + :type nums: List[int] + :rtype: List[int] + """ + return [x[0] for x in sorted(collections.Counter(nums).items(), key=lambda i: i[1], reverse=False)[:2]] diff --git a/Python/single-number.py b/Python/single-number.py index adcb1cba6..e48062ebd 100644 --- a/Python/single-number.py +++ b/Python/single-number.py @@ -1,19 +1,24 @@ +from __future__ import print_function # Time: O(n) # Space: O(1) # # Given an array of integers, every element appears twice except for one. Find that single one. -# +# # Note: # Your algorithm should have a linear runtime complexity. Could you implement it without using extra memory? # import operator +from functools import reduce + class Solution: - # @param A, a list of integer - # @return an integer + """ + :type nums: List[int] + :rtype: int + """ def singleNumber(self, A): return reduce(operator.xor, A) if __name__ == '__main__': - print Solution().singleNumber([1, 1, 2, 2, 3]) \ No newline at end of file + print(Solution().singleNumber([1, 1, 2, 2, 3])) diff --git a/Python/sliding-puzzle.py b/Python/sliding-puzzle.py new file mode 100644 index 000000000..04d104fed --- /dev/null +++ b/Python/sliding-puzzle.py @@ -0,0 +1,146 @@ +# Time: O((m * n) * (m * n)!) +# Space: O((m * n) * (m * n)!) + +# On a 2x3 board, there are 5 tiles represented by the integers 1 through 5, +# and an empty square represented by 0. +# +# A move consists of choosing 0 and a 4-directionally adjacent number and swapping it. +# +# The state of the board is solved if and only if the board is [[1,2,3],[4,5,0]]. +# +# Given a puzzle board, return the least number of moves required +# so that the state of the board is solved. If it is impossible +# for the state of the board to be solved, return -1. +# +# Examples: +# +# Input: board = [[1,2,3],[4,0,5]] +# Output: 1 +# Explanation: Swap the 0 and the 5 in one move. +# Input: board = [[1,2,3],[5,4,0]] +# Output: -1 +# Explanation: No number of moves will make the board solved. +# Input: board = [[4,1,2],[5,0,3]] +# Output: 5 +# Explanation: 5 is the smallest number of moves that solves the board. +# An example path: +# After move 0: [[4,1,2],[5,0,3]] +# After move 1: [[4,1,2],[0,5,3]] +# After move 2: [[0,1,2],[4,5,3]] +# After move 3: [[1,0,2],[4,5,3]] +# After move 4: [[1,2,0],[4,5,3]] +# After move 5: [[1,2,3],[4,5,0]] +# Input: board = [[3,2,4],[1,5,0]] +# Output: 14 +# +# Note: +# - board will be a 2 x 3 array as described above. +# - board[i][j] will be a permutation of [0, 1, 2, 3, 4, 5]. + +import heapq +import itertools + + +# A* Search Algorithm +class Solution(object): + def slidingPuzzle(self, board): + """ + :type board: List[List[int]] + :rtype: int + """ + def dot(p1, p2): + return p1[0]*p2[0]+p1[1]*p2[1] + + def heuristic_estimate(board, R, C, expected): + result = 0 + for i in xrange(R): + for j in xrange(C): + val = board[C*i + j] + if val == 0: continue + r, c = expected[val] + result += abs(r-i) + abs(c-j) + return result + + R, C = len(board), len(board[0]) + begin = tuple(itertools.chain(*board)) + end = tuple(range(1, R*C) + [0]) + expected = {(C*i+j+1) % (R*C) : (i, j) + for i in xrange(R) for j in xrange(C)} + + min_steps = heuristic_estimate(begin, R, C, expected) + closer, detour = [(begin.index(0), begin)], [] + lookup = set() + while True: + if not closer: + if not detour: + return -1 + min_steps += 2 + closer, detour = detour, closer + zero, board = closer.pop() + if board == end: + return min_steps + if board not in lookup: + lookup.add(board) + r, c = divmod(zero, C) + for direction in ((-1, 0), (1, 0), (0, -1), (0, 1)): + i, j = r+direction[0], c+direction[1] + if 0 <= i < R and 0 <= j < C: + new_zero = i*C+j + tmp = list(board) + tmp[zero], tmp[new_zero] = tmp[new_zero], tmp[zero] + new_board = tuple(tmp) + r2, c2 = expected[board[new_zero]] + r1, c1 = divmod(zero, C) + r0, c0 = divmod(new_zero, C) + is_closer = dot((r1-r0, c1-c0), (r2-r0, c2-c0)) > 0 + (closer if is_closer else detour).append((new_zero, new_board)) + return min_steps + + +# Time: O((m * n) * (m * n)! * log((m * n)!)) +# Space: O((m * n) * (m * n)!) +# A* Search Algorithm +class Solution2(object): + def slidingPuzzle(self, board): + """ + :type board: List[List[int]] + :rtype: int + """ + def heuristic_estimate(board, R, C, expected): + result = 0 + for i in xrange(R): + for j in xrange(C): + val = board[C*i + j] + if val == 0: continue + r, c = expected[val] + result += abs(r-i) + abs(c-j) + return result + + R, C = len(board), len(board[0]) + begin = tuple(itertools.chain(*board)) + end = tuple(range(1, R*C) + [0]) + end_wrong = tuple(range(1, R*C-2) + [R*C-1, R*C-2, 0]) + expected = {(C*i+j+1) % (R*C) : (i, j) + for i in xrange(R) for j in xrange(C)} + + min_heap = [(0, 0, begin.index(0), begin)] + lookup = {begin: 0} + while min_heap: + f, g, zero, board = heapq.heappop(min_heap) + if board == end: return g + if board == end_wrong: return -1 + if f > lookup[board]: continue + + r, c = divmod(zero, C) + for direction in ((-1, 0), (1, 0), (0, -1), (0, 1)): + i, j = r+direction[0], c+direction[1] + if 0 <= i < R and 0 <= j < C: + new_zero = C*i+j + tmp = list(board) + tmp[zero], tmp[new_zero] = tmp[new_zero], tmp[zero] + new_board = tuple(tmp) + f = g+1+heuristic_estimate(new_board, R, C, expected) + if f < lookup.get(new_board, float("inf")): + lookup[new_board] = f + heapq.heappush(min_heap, (f, g+1, new_zero, new_board)) + return -1 diff --git a/Python/sliding-window-maximum.py b/Python/sliding-window-maximum.py new file mode 100644 index 000000000..c55ce43b1 --- /dev/null +++ b/Python/sliding-window-maximum.py @@ -0,0 +1,49 @@ +# Time: O(n) +# Space: O(k) + +# Given an array nums, there is a sliding window of size k +# which is moving from the very left of the array to the +# very right. You can only see the k numbers in the window. +# Each time the sliding window moves right by one position. +# +# For example, +# Given nums = [1,3,-1,-3,5,3,6,7], and k = 3. +# +# Window position Max +# --------------- ----- +# [1 3 -1] -3 5 3 6 7 3 +# 1 [3 -1 -3] 5 3 6 7 3 +# 1 3 [-1 -3 5] 3 6 7 5 +# 1 3 -1 [-3 5 3] 6 7 5 +# 1 3 -1 -3 [5 3 6] 7 6 +# 1 3 -1 -3 5 [3 6 7] 7 +# Therefore, return the max sliding window as [3,3,5,5,6,7]. +# +# Note: +# You may assume k is always valid, ie: 1 <= k <= input array's size for non-empty array. +# +# Follow up: +# Could you solve it in linear time? + +from collections import deque + +class Solution(object): + def maxSlidingWindow(self, nums, k): + """ + :type nums: List[int] + :type k: int + :rtype: List[int] + """ + dq = deque() + max_numbers = [] + + for i in xrange(len(nums)): + while dq and nums[i] >= nums[dq[-1]]: + dq.pop() + dq.append(i) + if i >= k and dq and dq[0] == i - k: + dq.popleft() + if i >= k - 1: + max_numbers.append(nums[dq[0]]) + + return max_numbers diff --git a/Python/smallest-good-base.py b/Python/smallest-good-base.py new file mode 100644 index 000000000..ca093562b --- /dev/null +++ b/Python/smallest-good-base.py @@ -0,0 +1,39 @@ +# Time: O(logn * log(logn)) +# Space: O(1) + +# For an integer n, we call k>=2 a good base of n, if all digits of n base k are 1. +# +# Now given a string representing n, you should return the smallest good base of n in string format. +# +# Example 1: +# Input: "13" +# Output: "3" +# Explanation: 13 base 3 is 111. +# Example 2: +# Input: "4681" +# Output: "8" +# Explanation: 4681 base 8 is 11111. +# Example 3: +# Input: "1000000000000000000" +# Output: "999999999999999999" +# Explanation: 1000000000000000000 base 999999999999999999 is 11. +# Note: +# The range of n is [3, 10^18]. +# The string representing n is always valid and will not have leading zeros. + +import math + + +class Solution(object): + def smallestGoodBase(self, n): + """ + :type n: str + :rtype: str + """ + num = int(n) + max_len = int(math.log(num,2)) + for l in xrange(max_len, 1, -1): + b = int(num ** (l**-1)) + if (b**(l+1)-1) // (b-1) == num: + return str(b) + return str(num-1) diff --git a/Python/smallest-range.py b/Python/smallest-range.py new file mode 100644 index 000000000..8b5e76c90 --- /dev/null +++ b/Python/smallest-range.py @@ -0,0 +1,50 @@ +# Time: O(nlogk) +# Space: O(k) + +# You have k lists of sorted integers in ascending order. +# Find the smallest range that includes at least one number from each of the k lists. +# +# We define the range [a,b] is smaller than range [c,d] if b-a < d-c or a < c if b-a == d-c. +# +# Example 1: +# Input:[[4,10,15,24,26], [0,9,12,20], [5,18,22,30]] +# Output: [20,24] +# Explanation: +# List 1: [4, 10, 15, 24,26], 24 is in range [20,24]. +# List 2: [0, 9, 12, 20], 20 is in range [20,24]. +# List 3: [5, 18, 22, 30], 22 is in range [20,24]. +# Note: +# The given list may contain duplicates, so ascending order means >= here. +# 1 <= k <= 3500 +# -10^5 <= value of elements <= 10^5. +# For Java users, please note that the input type has been changed to List>. +# And after you reset the code template, you'll see this point. + +import heapq + + +class Solution(object): + def smallestRange(self, nums): + """ + :type nums: List[List[int]] + :rtype: List[int] + """ + left, right = float("inf"), float("-inf") + min_heap = [] + for row in nums: + left = min(left, row[0]) + right = max(right, row[0]) + it = iter(row) + heapq.heappush(min_heap, (next(it, None), it)) + + result = (left, right) + while min_heap: + (val, it) = heapq.heappop(min_heap) + val = next(it, None) + if val is None: + break + heapq.heappush(min_heap, (val, it)) + left, right = min_heap[0][0], max(right, val); + if right - left < result[1] - result[0]: + result = (left, right) + return result diff --git a/Python/smallest-rectangle-enclosing-black-pixels.py b/Python/smallest-rectangle-enclosing-black-pixels.py new file mode 100644 index 000000000..5e54d555d --- /dev/null +++ b/Python/smallest-rectangle-enclosing-black-pixels.py @@ -0,0 +1,34 @@ +# Time: O(nlogn) +# Space: O(1) + +import bisect +import itertools + + +class Solution(object): + def minArea(self, image, x, y): + """ + :type image: List[List[str]] + :type x: int + :type y: int + :rtype: int + """ + def binarySearch(left, right, find, image, has_one): + while left <= right: # O(logn) times + mid = left + (right - left) / 2 + if find(image, has_one, mid): # Time: O(n) + right = mid - 1 + else: + left = mid + 1 + return left + + + searchColumns = lambda image, has_one, mid: any([int(row[mid]) for row in image]) == has_one + left = binarySearch(0, y - 1, searchColumns, image, True) + right = binarySearch(y + 1, len(image[0]) - 1, searchColumns, image, False) + + searchRows = lambda image, has_one, mid: any(itertools.imap(int, image[mid])) == has_one + top = binarySearch(0, x - 1, searchRows, image, True) + bottom = binarySearch(x + 1, len(image) - 1, searchRows, image, False) + + return (right - left) * (bottom - top) diff --git a/Python/smallest-rotation-with-highest-score.py b/Python/smallest-rotation-with-highest-score.py new file mode 100644 index 000000000..c37570ea0 --- /dev/null +++ b/Python/smallest-rotation-with-highest-score.py @@ -0,0 +1,52 @@ +# Time: O(n) +# Space: O(n) + +# Given an array A, we may rotate it by a non-negative integer K +# so that the array becomes A[K], A[K+1], A{K+2], ... A[A.length - 1], A[0], A[1], ..., A[K-1]. +# Afterward, any entries that are less than or equal to their index are worth 1 point. +# +# For example, if we have [2, 4, 1, 3, 0], and we rotate by K = 2, +# it becomes [1, 3, 0, 2, 4]. +# This is worth 3 points because 1 > 0 [no points], 3 > 1 [no points], +# 0 <= 2 [one point], 2 <= 3 [one point], 4 <= 4 [one point]. +# +# Over all possible rotations, +# return the rotation index K that corresponds to the highest score we could receive. +# If there are multiple answers, return the smallest such index K. +# +# Example 1: +# Input: [2, 3, 1, 4, 0] +# Output: 3 +# Explanation: +# Scores for each K are listed below: +# K = 0, A = [2,3,1,4,0], score 2 +# K = 1, A = [3,1,4,0,2], score 3 +# K = 2, A = [1,4,0,2,3], score 3 +# K = 3, A = [4,0,2,3,1], score 4 +# K = 4, A = [0,2,3,1,4], score 3 +# So we should choose K = 3, which has the highest score. +# +# Example 2: +# Input: [1, 3, 0, 2, 4] +# Output: 0 +# Explanation: A will always have 3 points no matter how it shifts. +# So we will choose the smallest K, which is 0. +# +# Note: +# - A will have length at most 20000. +# - A[i] will be in the range [0, A.length]. + +class Solution(object): + def bestRotation(self, A): + """ + :type A: List[int] + :rtype: int + """ + N = len(A) + change = [1] * N + for i in xrange(N): + change[(i-A[i]+1)%N] -= 1 + for i in xrange(1, N): + change[i] += change[i-1] + return change.index(max(change)) + diff --git a/Python/smallest-subtree-with-all-the-deepest-nodes.py b/Python/smallest-subtree-with-all-the-deepest-nodes.py new file mode 100644 index 000000000..e0957e805 --- /dev/null +++ b/Python/smallest-subtree-with-all-the-deepest-nodes.py @@ -0,0 +1,59 @@ +# Time: O(n) +# Space: O(h) + +# Given a binary tree rooted at root, the depth of each node is +# the shortest distance to the root. +# A node is deepest if it has the largest depth possible among +# any node in the entire tree. +# The subtree of a node is that node, plus the set of all descendants +# of that node. +# Return the node with the largest depth such that it contains +# all the deepest nodes in it's subtree. +# +# Example 1: +# +# Input: [3,5,1,6,2,0,8,null,null,7,4] +# Output: [2,7,4] +# Explanation: +# +# We return the node with value 2, colored in yellow in the diagram. +# The nodes colored in blue are the deepest nodes of the tree. +# The input "[3, 5, 1, 6, 2, 0, 8, null, null, 7, 4]" is +# a serialization of the given tree. +# The output "[2, 7, 4]" is a serialization of the subtree +# rooted at the node with value 2. +# Both the input and output have TreeNode type. +# +# Note: +# - The number of nodes in the tree will be between 1 and 500. +# - The values of each node are unique. +# +# Definition for a binary tree node. +# class TreeNode(object): +# def __init__(self, x): +# self.val = x +# self.left = None +# self.right = None + +import collections + + +class Solution(object): + def subtreeWithAllDeepest(self, root): + """ + :type root: TreeNode + :rtype: TreeNode + """ + Result = collections.namedtuple("Result", ("node", "depth")) + + def dfs(node): + if not node: + return Result(None, 0) + left, right = dfs(node.left), dfs(node.right) + if left.depth > right.depth: + return Result(left.node, left.depth+1) + if left.depth < right.depth: + return Result(right.node, right.depth+1) + return Result(node, left.depth+1) + + return dfs(root).node diff --git a/Python/solve-the-equation.py b/Python/solve-the-equation.py new file mode 100644 index 000000000..21ce493de --- /dev/null +++ b/Python/solve-the-equation.py @@ -0,0 +1,46 @@ +# Time: O(n) +# Space: O(n) + +# Solve a given equation and return the value of x in the form of string "x=#value". +# The equation contains only '+', '-' operation, the variable x and its coefficient. +# +# If there is no solution for the equation, return "No solution". +# +# If there are infinite solutions for the equation, return "Infinite solutions". +# +# If there is exactly one solution for the equation, we ensure that the value of x is an integer. +# +# Example 1: +# Input: "x+5-3+x=6+x-2" +# Output: "x=2" +# Example 2: +# Input: "x=x" +# Output: "Infinite solutions" +# Example 3: +# Input: "2x=x" +# Output: "x=0" +# Example 4: +# Input: "2x+3x-6x=x+2" +# Output: "x=-1" +# Example 5: +# Input: "x=x+2" +# Output: "No solution" + +import re + + +class Solution(object): + def solveEquation(self, equation): + """ + :type equation: str + :rtype: str + """ + a, b, side = 0, 0, 1 + for eq, sign, num, isx in re.findall('(=)|([-+]?)(\d*)(x?)', equation): + if eq: + side = -1 + elif isx: + a += side * int(sign + '1') * int(num or 1) + elif num: + b -= side * int(sign + num) + return 'x=%d' % (b / a) if a else 'No solution' if b else 'Infinite solutions' diff --git a/Python/sort-characters-by-frequency.py b/Python/sort-characters-by-frequency.py new file mode 100644 index 000000000..3bfbde6d7 --- /dev/null +++ b/Python/sort-characters-by-frequency.py @@ -0,0 +1,58 @@ +# Time: O(n) +# Space: O(n) + +# Input: +# "tree" +# +# Output: +# "eert" +# +# Explanation: +# 'e' appears twice while 'r' and 't' both appear once. +# So 'e' must appear before both 'r' and 't'. Therefore "eetr" is also a valid answer. +# Example 2: +# +# Input: +# "cccaaa" +# +# Output: +# "cccaaa" +# +# Explanation: +# Both 'c' and 'a' appear three times, so "aaaccc" is also a valid answer. +# Note that "cacaca" is incorrect, as the same characters must be together. +# Example 3: +# +# Input: +# "Aabb" +# +# Output: +# "bbAa" +# +# Explanation: +# "bbaA" is also a valid answer, but "Aabb" is incorrect. +# Note that 'A' and 'a' are treated as two different characters. + +import collections + + +class Solution(object): + def frequencySort(self, s): + """ + :type s: str + :rtype: str + """ + freq = collections.defaultdict(int) + for c in s: + freq[c] += 1 + + counts = [""] * (len(s)+1) + for c in freq: + counts[freq[c]] += c + + result = "" + for count in reversed(xrange(len(counts)-1)): + for c in counts[count]: + result += c * count + + return result diff --git a/Python/sort-colors.py b/Python/sort-colors.py index d6bef9682..dc3e519af 100644 --- a/Python/sort-colors.py +++ b/Python/sort-colors.py @@ -1,41 +1,48 @@ +from __future__ import print_function # Time: O(n) # Space: O(1) # -# Given an array with n objects colored red, white or blue, sort them so that objects of the same color are adjacent, +# Given an array with n objects colored red, white or blue, sort them so that objects of the same color are adjacent, # with the colors in the order red, white and blue. -# +# # Here, we will use the integers 0, 1, and 2 to represent the color red, white, and blue respectively. -# +# # Note: # You are not suppose to use the library's sort function for this problem. -# +# # click to show follow up. -# +# # Follow up: # A rather straight forward solution is a two-pass algorithm using counting sort. -# First, iterate the array counting number of 0's, 1's, and 2's, +# First, iterate the array counting number of 0's, 1's, and 2's, # then overwrite array with total number of 0's, then 1's and followed by 2's. -# +# # Could you come up with an one-pass algorithm using only constant space? # -class Solution: - # @param A a list of integers - # @return nothing, sort in place - def sortColors(self, A): - i, last_zero, first_two = 0, -1, len(A) - - while i < first_two: - if A[i] == 0: - last_zero += 1 - A[last_zero], A[i] = A[i], A[last_zero] - elif A[i] == 2: - first_two -= 1 - A[first_two], A[i] = A[i], A[first_two] - i -= 1 - i += 1 +class Solution(object): + def sortColors(self, nums): + """ + :type nums: List[int] + :rtype: void Do not return anything, modify nums in-place instead. + """ + def triPartition(nums, target): + i, j, n = 0, 0, len(nums) - 1 + + while j <= n: + if nums[j] < target: + nums[i], nums[j] = nums[j], nums[i] + i += 1 + j += 1 + elif nums[j] > target: + nums[j], nums[n] = nums[n], nums[j] + n -= 1 + else: + j += 1 + + triPartition(nums, 1) if __name__ == "__main__": A = [2, 1, 1, 0, 0, 2] Solution().sortColors(A) - print A \ No newline at end of file + print(A) diff --git a/Python/sort-list.py b/Python/sort-list.py index bd56db074..7a2749efc 100644 --- a/Python/sort-list.py +++ b/Python/sort-list.py @@ -1,5 +1,6 @@ +from __future__ import print_function # Time: O(nlogn) -# Space: O(1) +# Space: O(logn) for stack call # # Sort a linked list in O(n log n) time using constant space complexity. # @@ -9,43 +10,43 @@ class ListNode: def __init__(self, x): self.val = x self.next = None - + def __repr__(self): if self: return "{} -> {}".format(self.val, repr(self.next)) class Solution: # @param head, a ListNode - # @return a ListNode + # @return a ListNode def sortList(self, head): if head == None or head.next == None: return head - + fast, slow, prev = head, head, None while fast != None and fast.next != None: prev, fast, slow = slow, fast.next.next, slow.next prev.next = None - + sorted_l1 = self.sortList(head) sorted_l2 = self.sortList(slow) - + return self.mergeTwoLists(sorted_l1, sorted_l2) - + def mergeTwoLists(self, l1, l2): dummy = ListNode(0) - + cur = dummy while l1 != None and l2 != None: if l1.val <= l2.val: cur.next, cur, l1 = l1, l1, l1.next else: cur.next, cur, l2 = l2, l2, l2.next - + if l1 != None: cur.next = l1 if l2 != None: cur.next = l2 - + return dummy.next if __name__ == "__main__": @@ -53,4 +54,4 @@ def mergeTwoLists(self, l1, l2): head.next = ListNode(4) head.next.next = ListNode(1) head.next.next.next= ListNode(2) - print Solution().sortList(head) \ No newline at end of file + print(Solution().sortList(head)) diff --git a/Python/sort-transformed-array.py b/Python/sort-transformed-array.py new file mode 100644 index 000000000..01eb27de1 --- /dev/null +++ b/Python/sort-transformed-array.py @@ -0,0 +1,29 @@ +# Time: O(n) +# Space: O(1) + +class Solution(object): + def sortTransformedArray(self, nums, a, b, c): + """ + :type nums: List[int] + :type a: int + :type b: int + :type c: int + :rtype: List[int] + """ + f = lambda x, a, b, c : a * x * x + b * x + c + + result = [] + if not nums: + return result + + left, right = 0, len(nums) - 1 + d = -1 if a > 0 else 1 + while left <= right: + if d * f(nums[left], a, b, c) < d * f(nums[right], a, b, c): + result.append(f(nums[left], a, b, c)) + left += 1 + else: + result.append(f(nums[right], a, b, c)) + right -= 1 + + return result[::d] diff --git a/Python/soup-servings.py b/Python/soup-servings.py new file mode 100644 index 000000000..97a108aa7 --- /dev/null +++ b/Python/soup-servings.py @@ -0,0 +1,62 @@ +# Time: O(1) +# Space: O(1) + +# There are two types of soup: type A and type B. +# Initially we have N ml of each type of soup. There are four kinds of operations: +# 1. Serve 100 ml of soup A and 0 ml of soup B +# 2. Serve 75 ml of soup A and 25 ml of soup B +# 3. Serve 50 ml of soup A and 50 ml of soup B +# 4. Serve 25 ml of soup A and 75 ml of soup B +# +# When we serve some soup, we give it to someone and we no longer have it. +# Each turn, we will choose from the four operations with equal probability 0.25. +# If the remaining volume of soup is not enough to complete the operation, +# we will serve as much as we can. +# We stop once we no longer have some quantity of both types of soup. +# +# Note that we do not have the operation where all 100 ml's of soup B are used first. +# +# Return the probability that soup A will be empty first, +# plus half the probability that A and B become empty at the same time. +# +# Example: +# Input: N = 50 +# Output: 0.625 +# Explanation: +# If we choose the first two operations, A will become empty first. +# For the third operation, A and B will become empty at the same time. +# For the fourth operation, B will become empty first. +# So the total probability of A becoming empty first plus half the probability +# that A and B become empty at the same time, is 0.25 * (1 + 1 + 0.5 + 0) = 0.625. +# +# Notes: +# 0 <= N <= 10^9. +# Answers within 10^-6 of the true value will be accepted as correct. + + +class Solution(object): + def soupServings(self, N): + """ + :type N: int + :rtype: float + """ + def dp(a, b, lookup): + if (a, b) in lookup: + return lookup[a, b] + if a <= 0 and b <= 0: + return 0.5 + if a <= 0: + return 1.0 + if b <= 0: + return 0.0 + lookup[a, b] = 0.25 * (dp(a-4, b, lookup) + + dp(a-3, b-1, lookup) + + dp(a-2, b-2, lookup) + + dp(a-1, b-3, lookup)) + return lookup[a, b] + + if N >= 4800: + return 1.0 + lookup = {} + N = (N+24)//25 + return dp(N, N, lookup) diff --git a/Python/sparse-matrix-multiplication.py b/Python/sparse-matrix-multiplication.py new file mode 100644 index 000000000..30dc340eb --- /dev/null +++ b/Python/sparse-matrix-multiplication.py @@ -0,0 +1,18 @@ +# Time: O(m * n * l), A is m x n matrix, B is n x l matrix +# Space: O(m * l) + +class Solution(object): + def multiply(self, A, B): + """ + :type A: List[List[int]] + :type B: List[List[int]] + :rtype: List[List[int]] + """ + m, n, l = len(A), len(A[0]), len(B[0]) + res = [[0 for _ in xrange(l)] for _ in xrange(m)] + for i in xrange(m): + for k in xrange(n): + if A[i][k]: + for j in xrange(l): + res[i][j] += A[i][k] * B[k][j] + return res diff --git a/Python/special-binary-string.py b/Python/special-binary-string.py new file mode 100644 index 000000000..ab8a7ced5 --- /dev/null +++ b/Python/special-binary-string.py @@ -0,0 +1,42 @@ +# Time: f(n) = k * f(n/k) + n/k * klogk <= O(logn * nlogk) <= O(n^2) +# n is the length of S, k is the max number of special strings in each depth +# Space: O(n) + +# Special binary strings are binary strings with the following two properties: +# +# The number of 0's is equal to the number of 1's. +# Every prefix of the binary string has at least as many 1's as 0's. +# Given a special string S, a move consists of choosing two consecutive, non-empty, +# special substrings of S, and swapping them. +# (Two strings are consecutive if the last character of the first string is +# exactly one index before the first character of the second string.) +# +# At the end of any number of moves, what is the lexicographically largest resulting string possible? +# +# Example 1: +# Input: S = "11011000" +# Output: "11100100" +# +# Explanation: +# The strings "10" [occuring at S[1]] and "1100" [at S[3]] are swapped. +# This is the lexicographically largest string possible after some number of swaps. +# +# Note: +# - S has length at most 50. +# - S is guaranteed to be a special binary string as defined above. + +class Solution(object): + def makeLargestSpecial(self, S): + """ + :type S: str + :rtype: str + """ + result = [] + anchor = count = 0 + for i, v in enumerate(S): + count += 1 if v == '1' else -1 + if count == 0: + result.append("1{}0".format(self.makeLargestSpecial(S[anchor+1:i]))) + anchor = i+1 + result.sort(reverse = True) + return "".join(result) diff --git a/Python/spiral-matrix-ii.py b/Python/spiral-matrix-ii.py index 554c9f7a8..5244cb820 100644 --- a/Python/spiral-matrix-ii.py +++ b/Python/spiral-matrix-ii.py @@ -1,11 +1,12 @@ +from __future__ import print_function # Time: O(n^2) # Space: O(1) # # Given an integer n, generate a square matrix filled with elements from 1 to n2 in spiral order. -# +# # For example, # Given n = 3, -# +# # You should return the following matrix: # [ # [ 1, 2, 3 ], @@ -17,10 +18,10 @@ class Solution: # @return a list of lists of integer def generateMatrix(self, n): - matrix = [[0 for i in range(n)] for i in range(n)] - + matrix = [[0 for _ in xrange(n)] for _ in xrange(n)] + left, right, top, bottom, num = 0, n - 1, 0, n - 1, 1 - + while left <= right and top <= bottom: for j in xrange(left, right + 1): matrix[top][j] = num @@ -37,10 +38,10 @@ def generateMatrix(self, n): matrix[i][left] = num num += 1 left, right, top, bottom = left + 1, right - 1, top + 1, bottom - 1 - + return matrix if __name__ == "__main__": - print Solution().generateMatrix(3) - print Solution().generateMatrix(8) \ No newline at end of file + print(Solution().generateMatrix(3)) + print(Solution().generateMatrix(8)) diff --git a/Python/spiral-matrix.py b/Python/spiral-matrix.py index 5071ccc54..78e4df195 100644 --- a/Python/spiral-matrix.py +++ b/Python/spiral-matrix.py @@ -1,11 +1,12 @@ +from __future__ import print_function # Time: O(m * n) # Space: O(1) # # Given a matrix of m x n elements (m rows, n columns), return all elements of the matrix in spiral order. -# +# # For example, # Given the following matrix: -# +# # [ # [ 1, 2, 3 ], # [ 4, 5, 6 ], @@ -21,9 +22,9 @@ def spiralOrder(self, matrix): result = [] if matrix == []: return result - + left, right, top, bottom = 0, len(matrix[0]) - 1, 0, len(matrix) - 1 - + while left <= right and top <= bottom: for j in xrange(left, right + 1): result.append(matrix[top][j]) @@ -36,12 +37,12 @@ def spiralOrder(self, matrix): if left < right: result.append(matrix[i][left]) left, right, top, bottom = left + 1, right - 1, top + 1, bottom - 1 - + return result if __name__ == "__main__": - print Solution().spiralOrder([[ 1, 2, 3 ], + print(Solution().spiralOrder([[ 1, 2, 3 ], [ 4, 5, 6 ], - [ 7, 8, 9 ]]) - print Solution().spiralOrder([[2,3]]) \ No newline at end of file + [ 7, 8, 9 ]])) + print(Solution().spiralOrder([[2,3]])) \ No newline at end of file diff --git a/Python/split-array-into-consecutive-subsequences.py b/Python/split-array-into-consecutive-subsequences.py new file mode 100644 index 000000000..2e6b113d5 --- /dev/null +++ b/Python/split-array-into-consecutive-subsequences.py @@ -0,0 +1,55 @@ +# Time: O(n) +# Space: O(1) + +# You are given an integer array sorted in ascending order (may contain duplicates), +# you need to split them into several subsequences, +# where each subsequences consist of at least 3 consecutive integers. Return whether you can make such a split. +# +# Example 1: +# Input: [1,2,3,3,4,5] +# Output: True +# Explanation: +# You can split them into two consecutive subsequences : +# 1, 2, 3 +# 3, 4, 5 +# Example 2: +# Input: [1,2,3,3,4,4,5,5] +# Output: True +# Explanation: +# You can split them into two consecutive subsequences : +# 1, 2, 3, 4, 5 +# 3, 4, 5 +# Example 3: +# Input: [1,2,3,4,4,5] +# Output: False +# Note: +# The length of the input is in range of [1, 10000] + +class Solution(object): + def isPossible(self, nums): + """ + :type nums: List[int] + :rtype: bool + """ + pre, cur = float("-inf"), 0 + cnt1, cnt2, cnt3 = 0, 0, 0 + i = 0 + while i < len(nums): + cnt = 0 + cur = nums[i] + while i < len(nums) and cur == nums[i]: + cnt += 1 + i += 1 + + if cur != pre + 1: + if cnt1 != 0 or cnt2 != 0: + return False + cnt1, cnt2, cnt3 = cnt, 0, 0 + else: + if cnt < cnt1 + cnt2: + return False + cnt1, cnt2, cnt3 = max(0, cnt - (cnt1 + cnt2 + cnt3)), \ + cnt1, \ + cnt2 + min(cnt3, cnt - (cnt1 + cnt2)) + pre = cur + return cnt1 == 0 and cnt2 == 0 diff --git a/Python/split-array-into-fibonacci-sequence.py b/Python/split-array-into-fibonacci-sequence.py new file mode 100644 index 000000000..c88b53308 --- /dev/null +++ b/Python/split-array-into-fibonacci-sequence.py @@ -0,0 +1,95 @@ +# Time: O(n^3) +# Space: O(n) + +# Given a string S of digits, such as S = "123456579", +# we can split it into a Fibonacci-like sequence [123, 456, 579]. +# +# Formally, a Fibonacci-like sequence is a list F of non-negative +# integers such that: +# +# 0 <= F[i] <= 2^31 - 1, +# (that is, each integer fits a 32-bit signed integer type); +# F.length >= 3; +# and F[i] + F[i+1] = F[i+2] for all 0 <= i < F.length - 2. +# Also, note that when splitting the string into pieces, +# each piece must not have extra leading zeroes, +# except if the piece is the number 0 itself. +# +# Return any Fibonacci-like sequence split from S, +# or return [] if it cannot be done. +# +# Example 1: +# +# Input: "123456579" +# Output: [123,456,579] +# Example 2: +# +# Input: "11235813" +# Output: [1,1,2,3,5,8,13] +# Example 3: +# +# Input: "112358130" +# Output: [] +# Explanation: The task is impossible. +# Example 4: +# +# Input: "0123" +# Output: [] +# Explanation: Leading zeroes are not allowed, so "01", "2", "3" is not valid. +# Example 5: +# +# Input: "1101111" +# Output: [110, 1, 111] +# Explanation: The output [11, 0, 11, 11] would also be accepted. +# +# Note: +# - 1 <= S.length <= 200 +# - S contains only digits. + +try: + xrange # Python 2 +except NameError: + xrange = range # Python 3 + + +class Solution(object): + def splitIntoFibonacci(self, S): + """ + :type S: str + :rtype: List[int] + """ + def startswith(S, k, x): + y = 0 + for i in xrange(k, len(S)): + y = 10*y + int(S[i]) + if y == x: + return i-k+1 + elif y > x: + break + return 0 + + MAX_INT = 2**31-1 + a = 0 + for i in xrange(len(S)-2): + a = 10*a + int(S[i]) + b = 0 + for j in xrange(i+1, len(S)-1): + b = 10*b + int(S[j]) + fib = [a, b] + k = j+1 + while k < len(S): + if fib[-2] > MAX_INT-fib[-1]: + break + c = fib[-2]+fib[-1] + length = startswith(S, k, c) + if length == 0: + break + fib.append(c) + k += length + else: + return fib + if b == 0: + break + if a == 0: + break + return [] diff --git a/Python/split-array-largest-sum.py b/Python/split-array-largest-sum.py new file mode 100644 index 000000000..5c67d4692 --- /dev/null +++ b/Python/split-array-largest-sum.py @@ -0,0 +1,52 @@ +# Time: O(nlogs), s is the sum of nums +# Space: O(1) + +# Given an array which consists of non-negative integers and an integer m, +# you can split the array into m non-empty continuous subarrays. +# Write an algorithm to minimize the largest sum among these m subarrays. +# +# Note: +# Given m satisfies the following constraint: 1 <= m <= length(nums) <= 14,000. +# +# Examples: +# +# Input: +# nums = [7,2,5,10,8] +# m = 2 +# +# Output: +# 18 +# +# Explanation: +# There are four ways to split nums into two subarrays. +# The best way is to split it into [7,2,5] and [10,8], +# where the largest sum among the two subarrays is only 18. + +class Solution(object): + def splitArray(self, nums, m): + """ + :type nums: List[int] + :type m: int + :rtype: int + """ + def canSplit(nums, m, s): + cnt, curr_sum = 1, 0 + for num in nums: + curr_sum += num + if curr_sum > s: + curr_sum = num + cnt += 1 + return cnt <= m + + left, right = 0, 0 + for num in nums: + left = max(left, num) + right += num + + while left <= right: + mid = left + (right - left) / 2; + if canSplit(nums, m, mid): + right = mid - 1 + else: + left = mid + 1 + return left diff --git a/Python/split-array-with-equal-sum.py b/Python/split-array-with-equal-sum.py new file mode 100644 index 000000000..825e79418 --- /dev/null +++ b/Python/split-array-with-equal-sum.py @@ -0,0 +1,26 @@ +# Time: O(n^2) +# Space: O(n) + +class Solution(object): + def splitArray(self, nums): + """ + :type nums: List[int] + :rtype: bool + """ + if len(nums) < 7: + return False + + accumulated_sum = [0] * len(nums) + accumulated_sum[0] = nums[0] + for i in xrange(1, len(nums)): + accumulated_sum[i] = accumulated_sum[i-1] + nums[i] + for j in xrange(3, len(nums)-3): + lookup = set() + for i in xrange(1, j-1): + if accumulated_sum[i-1] == accumulated_sum[j-1] - accumulated_sum[i]: + lookup.add(accumulated_sum[i-1]) + for k in xrange(j+2, len(nums)-1): + if accumulated_sum[-1] - accumulated_sum[k] == accumulated_sum[k-1] - accumulated_sum[j] and \ + accumulated_sum[k - 1] - accumulated_sum[j] in lookup: + return True + return False diff --git a/Python/split-array-with-same-average.py b/Python/split-array-with-same-average.py new file mode 100644 index 000000000..f4a83ec9f --- /dev/null +++ b/Python/split-array-with-same-average.py @@ -0,0 +1,44 @@ +# Time: O(n^4) +# Space: O(n^3) + +# In a given integer array A, we must move every element of A to +# either list B or list C. (B and C initially start empty.) +# +# Return true if and only if after such a move, it is possible that +# the average value of B is equal to the average value of C, and B and C are both non-empty. +# +# Example : +# Input: +# [1,2,3,4,5,6,7,8] +# Output: true +# Explanation: We can split the array into [1,4,5,8] and [2,3,6,7], and both of them have the average of 4.5. +# +# Note: +# - The length of A will be in the range [1, 30]. +# - A[i] will be in the range of [0, 10000]. + +class Solution(object): + def splitArraySameAverage(self, A): + """ + :type A: List[int] + :rtype: bool + """ + def possible(total, n): + for i in xrange(1, n//2+1): + if total*i%n == 0: + return True + return False + n, s = len(A), sum(A) + if not possible(n, s): + return False + + sums = [set() for _ in xrange(n//2+1)]; + sums[0].add(0) + for num in A: # O(n) times + for i in reversed(xrange(1, n//2+1)): # O(n) times + for prev in sums[i-1]: # O(1) + O(2) + ... O(n/2) = O(n^2) times + sums[i].add(prev+num) + for i in xrange(1, n//2+1): + if s*i%n == 0 and s*i//n in sums[i]: + return True + return False diff --git a/Python/split-bst.py b/Python/split-bst.py new file mode 100644 index 000000000..ced0963bc --- /dev/null +++ b/Python/split-bst.py @@ -0,0 +1,27 @@ +# Time: O(n) +# Space: O(h) + +# Definition for a binary tree node. +# class TreeNode(object): +# def __init__(self, x): +# self.val = x +# self.left = None +# self.right = None + +class Solution(object): + def splitBST(self, root, V): + """ + :type root: TreeNode + :type V: int + :rtype: List[TreeNode] + """ + if not root: + return None, None + elif root.val <= V: + result = self.splitBST(root.right, V) + root.right = result[0] + return root, result[1] + else: + result = self.splitBST(root.left, V) + root.left = result[1] + return result[0], root diff --git a/Python/split-concatenated-strings.py b/Python/split-concatenated-strings.py new file mode 100644 index 000000000..4ad1d593f --- /dev/null +++ b/Python/split-concatenated-strings.py @@ -0,0 +1,47 @@ +# Time: O(n^2) +# Space: O(n) + +# Given a list of strings, you could concatenate these strings together into a loop, +# where for each string you could choose to reverse it or not. +# Among all the possible loops, you need to find the lexicographically +# biggest string after cutting the loop, which will make the looped string into a regular one. +# +# Specifically, to find the lexicographically biggest string, you need to experience two phases: +# +# Concatenate all the strings into a loop, where you can reverse some strings or +# not and connect them in the same order as given. +# Cut and make one breakpoint in any place of the loop, which will make the looped string +# into a regular one starting from the character at the cutpoint. +# And your job is to find the lexicographically biggest one among all the possible regular strings. +# +# Example: +# Input: "abc", "xyz" +# Output: "zyxcba" +# Explanation: You can get the looped string "-abcxyz-", "-abczyx-", "-cbaxyz-", "-cbazyx-", +# where '-' represents the looped status. +# The answer string came from the fourth looped one, +# where you could cut from the middle character 'a' and get "zyxcba". +# Note: +# The input strings will only contain lowercase letters. +# The total length of all the strings will not over 1,000. + +class Solution(object): + def splitLoopedString(self, strs): + """ + :type strs: List[str] + :rtype: str + """ + tmp = [] + for s in strs: + tmp += max(s, s[::-1]) + s = "".join(tmp) + + result, st = "a", 0 + for i in xrange(len(strs)): + body = "".join([s[st + len(strs[i]):], s[0:st]]) + for p in strs[i], strs[i][::-1]: + for j in xrange(len(strs[i])): + if p[j] >= result[0]: + result = max(result, "".join([p[j:], body, p[:j]])) + st += len(strs[i]) + return result diff --git a/Python/split-linked-list-in-parts.py b/Python/split-linked-list-in-parts.py new file mode 100644 index 000000000..4189510f1 --- /dev/null +++ b/Python/split-linked-list-in-parts.py @@ -0,0 +1,71 @@ +# Time: O(n + k) +# Space: O(1) + +# Given a (singly) linked list with head node root, +# write a function to split the linked list into k consecutive linked list "parts". +# +# The length of each part should be as equal as possible: +# no two parts should have a size differing by more than 1. +# This may lead to some parts being null. +# +# The parts should be in order of occurrence in the input list, +# and parts occurring earlier should always have a size greater than or equal parts occurring later. +# +# Return a List of ListNode's representing the linked list parts that are formed. +# +# Examples 1->2->3->4, k = 5 // 5 equal parts [ [1], [2], [3], [4], null ] +# Example 1: +# Input: +# root = [1, 2, 3], k = 5 +# Output: [[1],[2],[3],[],[]] +# Explanation: +# The input and each element of the output are ListNodes, not arrays. +# For example, the input root has root.val = 1, root.next.val = 2, \root.next.next.val = 3, +# and root.next.next.next = null. +# The first element output[0] has output[0].val = 1, output[0].next = null. +# The last element output[4] is null, but it's string representation as a ListNode is []. +# +# Example 2: +# Input: +# root = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], k = 3 +# Output: [[1, 2, 3, 4], [5, 6, 7], [8, 9, 10]] +# Explanation: +# The input has been split into consecutive parts with size difference at most 1, +# and earlier parts are a larger size than the later parts. +# +# Note: +# - The length of root will be in the range [0, 1000]. +# - Each value of a node in the input will be an integer in the range [0, 999]. +# - k will be an integer in the range [1, 50]. +# +# Definition for singly-linked list. +# class ListNode(object): +# def __init__(self, x): +# self.val = x +# self.next = None + +class Solution(object): + def splitListToParts(self, root, k): + """ + :type root: ListNode + :type k: int + :rtype: List[ListNode] + """ + n = 0 + curr = root + while curr: + curr = curr.next + n += 1 + width, remainder = divmod(n, k) + + result = [] + curr = root + for i in xrange(k): + head = curr + for j in xrange(width-1+int(i < remainder)): + if curr: + curr = curr.next + if curr: + curr.next, curr = None, curr.next + result.append(head) + return result diff --git a/Python/sqrtx.py b/Python/sqrtx.py index aab364874..fce6acc1b 100644 --- a/Python/sqrtx.py +++ b/Python/sqrtx.py @@ -1,27 +1,31 @@ +from __future__ import print_function # Time: O(logn) # Space: O(1) -# + # Implement int sqrt(int x). -# -# Compute and return the square root of x. # +# Compute and return the square root of x. -class Solution: - # @param x, an integer - # @return an integer - def sqrt(self, x): +class Solution(object): + def mySqrt(self, x): + """ + :type x: int + :rtype: int + """ if x < 2: return x - - low, high = 1, x / 2 - while high >= low: - mid = low + (high - low) / 2 - if x / mid < mid: - high = mid - 1 + + left, right = 1, x // 2 + while left <= right: + mid = left + (right - left) // 2 + if mid > x / mid: + right = mid - 1 else: - low = mid + 1 - return high + left = mid + 1 + + return left - 1 + if __name__ == "__main__": - print Solution().sqrt(10) - \ No newline at end of file + print(Solution().mySqrt(10)) + diff --git a/Python/squirrel-simulation.py b/Python/squirrel-simulation.py new file mode 100644 index 000000000..f0057eec2 --- /dev/null +++ b/Python/squirrel-simulation.py @@ -0,0 +1,22 @@ +# Time: O(n) +# Space: O(1) + +class Solution(object): + def minDistance(self, height, width, tree, squirrel, nuts): + """ + :type height: int + :type width: int + :type tree: List[int] + :type squirrel: List[int] + :type nuts: List[List[int]] + :rtype: int + """ + def distance(a, b): + return abs(a[0] - b[0]) + abs(a[1] - b[1]) + + result = 0 + d = float("inf") + for nut in nuts: + result += (distance(nut, tree) * 2) + d = min(d, distance(nut, squirrel) - distance(nut, tree)) + return result + d diff --git a/Python/stickers-to-spell-word.py b/Python/stickers-to-spell-word.py new file mode 100644 index 000000000..180f9889b --- /dev/null +++ b/Python/stickers-to-spell-word.py @@ -0,0 +1,78 @@ +# Time: O(T * S^T) +# Space: O(T * S^T) + +# We are given N different types of stickers. Each sticker has a lowercase English word on it. +# +# You would like to spell out the given target string by cutting individual letters +# from your collection of stickers and rearranging them. +# +# You can use each sticker more than once if you want, and you have infinite quantities of each sticker. +# +# What is the minimum number of stickers that you need to spell out the target? +# If the task is impossible, return -1. +# +# Example 1: +# +# Input: +# ["with", "example", "science"], "thehat" +# +# Output: +# 3 +# +# Explanation: +# We can use 2 "with" stickers, and 1 "example" sticker. +# After cutting and rearrange the letters of those stickers, we can form the target "thehat". +# Also, this is the minimum number of stickers necessary to form the target string. +# +# Example 2: +# +# Input: +# ["notice", "possible"], "basicbasic" +# +# Output: +# -1 +# +# Explanation: +# We can't form the target "basicbasic" from cutting letters from the given stickers. +# +# Note: +# - stickers has length in the range [1, 50]. +# - stickers consists of lowercase English words (without apostrophes). +# - target has length in the range [1, 15], and consists of lowercase English letters. +# - In all test cases, all words were chosen randomly from the 1000 most common US English words, +# and the target was chosen as a concatenation of two random words. +# - The time limit may be more challenging than usual. +# It is expected that a 50 sticker test case can be solved within 35ms on average. + +import collections + + +class Solution(object): + def minStickers(self, stickers, target): + """ + :type stickers: List[str] + :type target: str + :rtype: int + """ + def minStickersHelper(sticker_counts, target, dp): + if "".join(target) in dp: + return dp["".join(target)] + target_count = collections.Counter(target) + result = float("inf") + for sticker_count in sticker_counts: + if sticker_count[target[0]] == 0: + continue + new_target = [] + for k in target_count.keys(): + if target_count[k] > sticker_count[k]: + new_target += [k]*(target_count[k] - sticker_count[k]) + if len(new_target) != len(target): + num = minStickersHelper(sticker_counts, new_target, dp) + if num != -1: + result = min(result, 1+num) + dp["".join(target)] = -1 if result == float("inf") else result + return dp["".join(target)] + + sticker_counts = map(collections.Counter, stickers) + dp = { "":0 } + return minStickersHelper(sticker_counts, target, dp) diff --git a/Python/stone-game.py b/Python/stone-game.py new file mode 100644 index 000000000..8deca7ab1 --- /dev/null +++ b/Python/stone-game.py @@ -0,0 +1,52 @@ +# Time: O(n^2) +# Space: O(n) + +# Alex and Lee play a game with piles of stones. +# There are an even number of piles arranged in a row, +# and each pile has a positive integer number of stones piles[i]. +# +# The objective of the game is to end with the most stones. +# The total number of stones is odd, so there are no ties. +# +# Alex and Lee take turns, with Alex starting first. +# Each turn, a player takes the entire pile of stones from +# either the beginning or the end of the row. +# This continues until there are no more piles left, +# at which point the person with the most stones wins. +# +# Assuming Alex and Lee play optimally, +# return True if and only if Alex wins the game. +# +# Example 1: +# +# Input: [5,3,4,5] +# Output: true +# Explanation: +# Alex starts first, and can only take the first 5 or the last 5. +# Say he takes the first 5, so that the row becomes [3, 4, 5]. +# If Lee takes 3, then the board is [4, 5], and Alex takes 5 to win with 10 points. +# If Lee takes the last 5, then the board is [3, 4], and Alex takes 4 to win with 9 points. +# This demonstrated that taking the first 5 was a winning move for Alex, so we return true. +# +# Note: +# - 2 <= piles.length <= 500 +# - piles.length is even. +# - 1 <= piles[i] <= 500 +# - sum(piles) is odd. + +# The solution is the same as https://leetcode.com/problems/predict-the-winner/description/ +class Solution(object): + def stoneGame(self, piles): + """ + :type piles: List[int] + :rtype: bool + """ + if len(piles) % 2 == 0 or len(piles) == 1: + return True + + dp = [0] * len(piles); + for i in reversed(xrange(len(piles))): + dp[i] = piles[i] + for j in xrange(i+1, len(piles)): + dp[j] = max(piles[i] - dp[j], piles[j] - dp[j - 1]) + return dp[-1] >= 0 diff --git a/Python/strange-printer.py b/Python/strange-printer.py new file mode 100644 index 000000000..811933aaf --- /dev/null +++ b/Python/strange-printer.py @@ -0,0 +1,43 @@ +# Time: O(n^3) +# Space: O(n^2) + +# There is a strange printer with the following two special requirements: +# +# The printer can only print a sequence of the same character each time. +# At each turn, the printer can print new characters starting from +# and ending at any places, and will cover the original existing characters. +# +# Given a string consists of lower English letters only, +# your job is to count the minimum number of turns the printer needed in order to print it. +# +# Example 1: +# Input: "aaabbb" +# Output: 2 +# Explanation: Print "aaa" first and then print "bbb". +# Example 2: +# Input: "aba" +# Output: 2 +# Explanation: Print "aaa" first and then print "b" from +# the second place of the string, which will cover the existing character 'a'. +# +# Hint: Length of the given string will not exceed 100. + +class Solution(object): + def strangePrinter(self, s): + """ + :type s: str + :rtype: int + """ + def dp(s, i, j, lookup): + if i > j: + return 0 + if (i, j) not in lookup: + lookup[(i, j)] = dp(s, i, j-1, lookup) + 1 + for k in xrange(i, j): + if s[k] == s[j]: + lookup[(i, j)] = min(lookup[(i, j)], \ + dp(s, i, k, lookup) + dp(s, k+1, j-1, lookup)) + return lookup[(i, j)] + + lookup = {} + return dp(s, 0, len(s)-1, lookup) diff --git a/Python/string-compression.py b/Python/string-compression.py new file mode 100644 index 000000000..cf57ce2e9 --- /dev/null +++ b/Python/string-compression.py @@ -0,0 +1,67 @@ +# Time: O(n) +# Space: O(1) + +# Given an array of characters, compress it in-place. +# The length after compression must always be smaller than or equal to the original array. +# Every element of the array should be a character (not int) of length 1. +# After you are done modifying the input array in-place, return the new length of the array. +# +# Follow up: +# Could you solve it using only O(1) extra space? +# +# Example 1: +# Input: +# ["a","a","b","b","c","c","c"] +# +# Output: +# Return 6, and the first 6 characters of the input array should be: ["a","2","b","2","c","3"] +# +# Explanation: +# "aa" is replaced by "a2". "bb" is replaced by "b2". "ccc" is replaced by "c3". +# Example 2: +# Input: +# ["a"] +# +# Output: +# Return 1, and the first 1 characters of the input array should be: ["a"] +# +# Explanation: +# Nothing is replaced. +# Example 3: +# Input: +# ["a","b","b","b","b","b","b","b","b","b","b","b","b"] +# +# Output: +# Return 4, and the first 4 characters of the input array should be: ["a","b","1","2"]. +# +# Explanation: +# Since the character "a" does not repeat, it is not compressed. "bbbbbbbbbbbb" is replaced by "b12". +# Notice each digit has it's own entry in the array. +# Note: +# All characters have an ASCII value in [35, 126]. +# 1 <= len(chars) <= 1000. + +class Solution(object): + def compress(self, chars): + """ + :type chars: List[str] + :rtype: int + """ + anchor, write = 0, 0 + for read, c in enumerate(chars): + if read+1 == len(chars) or chars[read+1] != c: + chars[write] = chars[anchor] + write += 1 + if read > anchor: + n, left = read-anchor+1, write + while n > 0: + chars[write] = chr(n%10+ord('0')) + write += 1 + n /= 10 + right = write-1 + while left < right: + chars[left], chars[right] = chars[right], chars[left] + left += 1 + right -= 1 + anchor = read+1 + return write diff --git a/Python/string-to-integer-atoi.py b/Python/string-to-integer-atoi.py index b10431638..ae716b3ec 100644 --- a/Python/string-to-integer-atoi.py +++ b/Python/string-to-integer-atoi.py @@ -2,44 +2,50 @@ # Space: O(1) # # Implement atoi to convert a string to an integer. -# -# Hint: Carefully consider all possible input cases. If you want a challenge, please do not see below +# +# Hint: Carefully consider all possible input cases. If you want a challenge, please do not see below # and ask yourself what are the possible input cases. -# +# # Notes: It is intended for this problem to be specified vaguely (ie, no given input specs). # You are responsible to gather all the input requirements up front. -# +# # spoilers alert... click to show requirements for atoi. -# +# # Requirements for atoi: -# The function first discards as many whitespace characters as necessary -# until the first non-whitespace character is found. Then, starting from this character, +# The function first discards as many whitespace characters as necessary +# until the first non-whitespace character is found. Then, starting from this character, # takes an optional initial plus or minus sign followed by as many numerical digits as possible, and interprets them as a numerical value. -# -# The string can contain additional characters after those that +# +# The string can contain additional characters after those that # form the integral number, which are ignored and have no effect on the behavior of this function. -# -# If the first sequence of non-whitespace characters in str is not a valid integral number, +# +# If the first sequence of non-whitespace characters in str is not a valid integral number, # or if no such sequence exists because either str is empty or it contains only whitespace characters, no conversion is performed. -# -# If no valid conversion could be performed, a zero value is returned. +# +# If no valid conversion could be performed, a zero value is returned. # If the correct value is out of the range of representable values, INT_MAX (2147483647) or INT_MIN (-2147483648) is returned. # -class Solution: - # @return an integer - def atoi(self, str): +class Solution(object): + def myAtoi(self, str): + """ + :type str: str + :rtype: int + """ INT_MAX = 2147483647 INT_MIN = -2147483648 result = 0 - - if len(str) == 0: + + if not str: return result - + i = 0 - while i < len(str) and str[i] == " ": + while i < len(str) and str[i].isspace(): i += 1 - + + if len(str) == i: + return result + sign = 1 if str[i] == "+": i += 1 @@ -47,22 +53,10 @@ def atoi(self, str): sign = -1 i += 1 - while i < len(str) and str[i] >= '0' and str[i] <= '9': - if result > INT_MAX / 10 or (result == INT_MAX / 10 and ord(str[i]) - ord('0') > INT_MAX % 10): - if sign > 0: - return INT_MAX - else: - return INT_MIN - - result = result * 10 + ord(str[i]) - ord('0') + while i < len(str) and '0' <= str[i] <= '9': + if result > (INT_MAX - int(str[i])) / 10: + return INT_MAX if sign > 0 else INT_MIN + result = result * 10 + int(str[i]) i += 1 - - return sign * result -if __name__ == "__main__": - print Solution().atoi("") - print Solution().atoi("-1") - print Solution().atoi("2147483647") - print Solution().atoi("2147483648") - print Solution().atoi("-2147483648") - print Solution().atoi("-2147483649") \ No newline at end of file + return sign * result diff --git a/Python/strobogrammatic-number-ii.py b/Python/strobogrammatic-number-ii.py new file mode 100644 index 000000000..a914402b2 --- /dev/null +++ b/Python/strobogrammatic-number-ii.py @@ -0,0 +1,24 @@ +# Time: O(n^2 * 5^(n/2)) +# Space: O(n) + +class Solution: + lookup = {'0':'0', '1':'1', '6':'9', '8':'8', '9':'6'} + + # @param {integer} n + # @return {string[]} + def findStrobogrammatic(self, n): + return self.findStrobogrammaticRecu(n, n) + + def findStrobogrammaticRecu(self, n, k): + if k == 0: + return [''] + elif k == 1: + return ['0', '1', '8'] + + result = [] + for num in self.findStrobogrammaticRecu(n, k - 2): + for key, val in self.lookup.iteritems(): + if n != k or key != '0': + result.append(key + num + val) + + return result diff --git a/Python/strobogrammatic-number-iii.py b/Python/strobogrammatic-number-iii.py new file mode 100644 index 000000000..23bf9b7b1 --- /dev/null +++ b/Python/strobogrammatic-number-iii.py @@ -0,0 +1,74 @@ +# Time: O(5^(n/2)) +# Space: O(n) + +class Solution: + lookup = {'0':'0', '1':'1', '6':'9', '8':'8', '9':'6'} + cache = {} + + # @param {string} low + # @param {string} high + # @return {integer} + def strobogrammaticInRange(self, low, high): + count = self.countStrobogrammaticUntil(high, False) - \ + self.countStrobogrammaticUntil(low, False) + \ + self.isStrobogrammatic(low) + return count if count >= 0 else 0 + + def countStrobogrammaticUntil(self, num, can_start_with_0): + if can_start_with_0 and num in self.cache: + return self.cache[num] + + count = 0 + if len(num) == 1: + for c in ['0', '1', '8']: + if num[0] >= c: + count += 1 + self.cache[num] = count + return count + + for key, val in self.lookup.iteritems(): + if can_start_with_0 or key != '0': + if num[0] > key: + if len(num) == 2: # num is like "21" + count += 1 + else: # num is like "201" + count += self.countStrobogrammaticUntil('9' * (len(num) - 2), True) + elif num[0] == key: + if len(num) == 2: # num is like 12". + if num[-1] >= val: + count += 1 + else: + if num[-1] >= val: # num is like "102". + count += self.countStrobogrammaticUntil(self.getMid(num), True); + elif (self.getMid(num) != '0' * (len(num) - 2)): # num is like "110". + count += self.countStrobogrammaticUntil(self.getMid(num), True) - \ + self.isStrobogrammatic(self.getMid(num)) + + if not can_start_with_0: # Sum up each length. + for i in xrange(len(num) - 1, 0, -1): + count += self.countStrobogrammaticByLength(i) + else: + self.cache[num] = count + + return count + + def getMid(self, num): + return num[1:len(num) - 1] + + def countStrobogrammaticByLength(self, n): + if n == 1: + return 3 + elif n == 2: + return 4 + elif n == 3: + return 4 * 3 + else: + return 5 * self.countStrobogrammaticByLength(n - 2) + + def isStrobogrammatic(self, num): + n = len(num) + for i in xrange((n+1) / 2): + if num[n-1-i] not in self.lookup or \ + num[i] != self.lookup[num[n-1-i]]: + return False + return True diff --git a/Python/strobogrammatic-number.py b/Python/strobogrammatic-number.py new file mode 100644 index 000000000..289c531d7 --- /dev/null +++ b/Python/strobogrammatic-number.py @@ -0,0 +1,15 @@ +# Time: O(n) +# Space: O(1) + +class Solution: + lookup = {'0':'0', '1':'1', '6':'9', '8':'8', '9':'6'} + + # @param {string} num + # @return {boolean} + def isStrobogrammatic(self, num): + n = len(num) + for i in xrange((n+1) / 2): + if num[n-1-i] not in self.lookup or \ + num[i] != self.lookup[num[n-1-i]]: + return False + return True diff --git a/Python/strong-password-checker.py b/Python/strong-password-checker.py new file mode 100644 index 000000000..2776c8dfa --- /dev/null +++ b/Python/strong-password-checker.py @@ -0,0 +1,61 @@ +# Time: O(n) +# Space: O(1) + +# A password is considered strong if below conditions are all met: +# +# It has at least 6 characters and at most 20 characters. +# It must contain at least one lowercase letter, at least one uppercase letter, +# and at least one digit. +# It must NOT contain three repeating characters in a row ("...aaa..." is weak, +# but "...aa...a..." is strong, assuming other conditions are met). +# Write a function strongPasswordChecker(s), that takes a string s as input, +# and return the MINIMUM change required to make s a strong password. If s is already strong, return 0. +# +# Insertion, deletion or replace of any one character are all considered as one change. + +class Solution(object): + def strongPasswordChecker(self, s): + """ + :type s: str + :rtype: int + """ + missing_type_cnt = 3 + if any('a' <= c <= 'z' for c in s): + missing_type_cnt -= 1 + if any('A' <= c <= 'Z' for c in s): + missing_type_cnt -= 1 + if any(c.isdigit() for c in s): + missing_type_cnt -= 1 + + total_change_cnt = 0 + one_change_cnt, two_change_cnt, three_change_cnt = 0, 0, 0 + i = 2 + while i < len(s): + if s[i] == s[i-1] == s[i-2]: + length = 2 + while i < len(s) and s[i] == s[i-1]: + length += 1 + i += 1 + + total_change_cnt += length / 3 + if length % 3 == 0: + one_change_cnt += 1 + elif length % 3 == 1: + two_change_cnt += 1 + else: + three_change_cnt += 1 + else: + i += 1 + + if len(s) < 6: + return max(missing_type_cnt, 6 - len(s)) + elif len(s) <= 20: + return max(missing_type_cnt, total_change_cnt) + else: + delete_cnt = len(s) - 20 + + total_change_cnt -= min(delete_cnt, one_change_cnt * 1) / 1 + total_change_cnt -= min(max(delete_cnt - one_change_cnt, 0), two_change_cnt * 2) / 2 + total_change_cnt -= min(max(delete_cnt - one_change_cnt - 2 * two_change_cnt, 0), three_change_cnt * 3) / 3 + + return delete_cnt + max(missing_type_cnt, total_change_cnt) diff --git a/Python/student-attendance-record-i.py b/Python/student-attendance-record-i.py new file mode 100644 index 000000000..e63511eae --- /dev/null +++ b/Python/student-attendance-record-i.py @@ -0,0 +1,37 @@ +# Time: O(n) +# Space: O(1) + +# You are given a string representing an attendance record for a student. +# The record only contains the following three characters: +# +# 'A' : Absent. +# 'L' : Late. +# 'P' : Present. +# A student could be rewarded if his attendance record +# doesn't contain more than one 'A' (absent) or more than two continuous 'L' (late). +# +# You need to return whether the student could be rewarded according to his attendance record. +# +# Example 1: +# Input: "PPALLP" +# Output: True +# Example 2: +# Input: "PPALLL" +# Output: False + +class Solution(object): + def checkRecord(self, s): + """ + :type s: str + :rtype: bool + """ + count_A = 0 + for i in xrange(len(s)): + if s[i] == 'A': + count_A += 1 + if count_A == 2: + return False + if i < len(s) - 2 and s[i] == s[i+1] == s[i+2] == 'L': + return False + return True + diff --git a/Python/student-attendance-record-ii.py b/Python/student-attendance-record-ii.py new file mode 100644 index 000000000..c52793b45 --- /dev/null +++ b/Python/student-attendance-record-ii.py @@ -0,0 +1,35 @@ +# Time: O(n) +# Space: O(1) + +# Given a positive integer n, return the number of all possible attendance records with length n, +# which will be regarded as rewardable. The answer may be very large, return it after mod 10^9 + 7. +# +# A student attendance record is a string that only contains the following three characters: +# +# 'A' : Absent. +# 'L' : Late. +# 'P' : Present. +# A record is regarded as rewardable if it doesn't +# contain more than one 'A' (absent) or more than two continuous 'L' (late). +# +# Example 1: +# Input: n = 2 +# Output: 8 +# Explanation: +# There are 8 records with length 2 will be regarded as rewardable: +# "PP" , "AP", "PA", "LP", "PL", "AL", "LA", "LL" +# Only "AA" won't be regarded as rewardable owing to more than one absent times. +# Note: The value of n won't exceed 100,000. + +class Solution(object): + def checkRecord(self, n): + """ + :type n: int + :rtype: int + """ + M = 1000000007 + a0l0, a0l1, a0l2, a1l0, a1l1, a1l2 = 1, 0, 0, 0, 0, 0 + for i in xrange(n+1): + a0l2, a0l1, a0l0 = a0l1, a0l0, (a0l0 + a0l1 + a0l2) % M + a1l2, a1l1, a1l0 = a1l1, a1l0, (a0l0 + a1l0 + a1l1 + a1l2) % M; + return a1l0 diff --git a/Python/subarray-product-less-than-k.py b/Python/subarray-product-less-than-k.py new file mode 100644 index 000000000..9d947ca72 --- /dev/null +++ b/Python/subarray-product-less-than-k.py @@ -0,0 +1,38 @@ +# Time: O(n) +# Space: O(1) + +# Your are given an array of positive integers nums. +# +# Count and print the number of (contiguous) subarrays where the product of all the elements +# in the subarray is less than k. +# +# Example 1: +# Input: nums = [10, 5, 2, 6], k = 100 +# Output: 8 +# Explanation: The 8 subarrays that have product less than 100 are: +# [10], [5], [2], [6], [10, 5], [5, 2], [2, 6], [5, 2, 6]. +# Note that [10, 5, 2] is not included as the product of 100 is not strictly less than k. +# +# Note: +# 0 < nums.length <= 50000. +# 0 < nums[i] < 1000. +# 0 <= k < 10^6. + +# Sliding window solution. +class Solution(object): + def numSubarrayProductLessThanK(self, nums, k): + """ + :type nums: List[int] + :type k: int + :rtype: int + """ + if k <= 1: return 0 + result, start, prod = 0, 0, 1 + for i, num in enumerate(nums): + prod *= num + while prod >= k: + prod /= nums[start] + start += 1 + result += i-start+1 + return result + diff --git a/Python/subarray-sum-equals-k.py b/Python/subarray-sum-equals-k.py new file mode 100644 index 000000000..96a14da77 --- /dev/null +++ b/Python/subarray-sum-equals-k.py @@ -0,0 +1,33 @@ +# Time: O(n) +# Space: O(n) + +# Given an array of integers and an integer k, +# you need to find the total number of continuous subarrays whose sum equals to k. +# +# Example 1: +# Input:nums = [1,1,1], k = 2 +# Output: 2 +# +# Note: +# The length of the array is in range [1, 20,000]. +# The range of numbers in the array is [-1000, 1000] and the range of the integer k is [-1e7, 1e7]. + +import collections + + +class Solution(object): + def subarraySum(self, nums, k): + """ + :type nums: List[int] + :type k: int + :rtype: int + """ + result = 0 + accumulated_sum = 0 + lookup = collections.defaultdict(int) + lookup[0] += 1 + for num in nums: + accumulated_sum += num + result += lookup[accumulated_sum - k] + lookup[accumulated_sum] += 1 + return result diff --git a/Python/subdomain-visit-count.py b/Python/subdomain-visit-count.py new file mode 100644 index 000000000..5f5cbbd89 --- /dev/null +++ b/Python/subdomain-visit-count.py @@ -0,0 +1,84 @@ +# Time: O(n), is the length of cpdomains (assuming the length of cpdomains[i] is fixed) +# Space: O(n) + +# A website domain like "discuss.leetcode.com" consists of various subdomains. +# At the top level, we have "com", at the next level, we have "leetcode.com", +# and at the lowest level, "discuss.leetcode.com". +# When we visit a domain like "discuss.leetcode.com", +# we will also visit the parent domains "leetcode.com" and "com" implicitly. +# +# Now, call a "count-paired domain" to be a count +# (representing the number of visits this domain received), +# followed by a space, followed by the address. +# An example of a count-paired domain might be "9001 discuss.leetcode.com". +# +# We are given a list cpdomains of count-paired domains. +# We would like a list of count-paired domains, +# (in the same format as the input, and in any order), +# that explicitly counts the number of visits to each subdomain. +# +# Example 1: +# Input: +# ["9001 discuss.leetcode.com"] +# Output: +# ["9001 discuss.leetcode.com", "9001 leetcode.com", "9001 com"] +# Explanation: +# We only have one website domain: "discuss.leetcode.com". +# As discussed above, the subdomain "leetcode.com" and +# "com" will also be visited. So they will all be visited 9001 times. +# +# Example 2: +# Input: +# ["900 google.mail.com", "50 yahoo.com", "1 intel.mail.com", "5 wiki.org"] +# Output: +# ["901 mail.com","50 yahoo.com","900 google.mail.com","5 wiki.org","5 org","1 intel.mail.com","951 com"] +# Explanation: +# We will visit "google.mail.com" 900 times, "yahoo.com" 50 times, +# "intel.mail.com" once and "wiki.org" 5 times. +# For the subdomains, we will visit "mail.com" 900 + 1 = 901 times, +# "com" 900 + 50 + 1 = 951 times, and "org" 5 times. +# +# Notes: +# - The length of cpdomains will not exceed 100. +# - The length of each domain name will not exceed 100. +# - Each address will have either 1 or 2 "." characters. +# - The input count in any count-paired domain will not exceed 10000. + +from collections import defaultdict + + +try: + xrange # Python 2 +except NameError: + xrange = range # Python 3 + + +try: + defaultdict.iteritems +except AttributeError: + # Python 3 + def iteritems(d): + return iter(d.items()) +else: + # Python 2 + def iteritems(d): + return d.iteritems() + + +class Solution: + def subdomainVisits(self, cpdomains): + """ + :type cpdomains: List[str] + :rtype: List[str] + """ + result = defaultdict(int) + for domain in cpdomains: + count, domain = domain.split() + count = int(count) + frags = domain.split('.') + curr = [] + for i in reversed(xrange(len(frags))): + curr.append(frags[i]) + result[".".join(reversed(curr))] += count + + return ["{} {}".format(count, domain) for domain, count in iteritems(result)] diff --git a/Python/subsets-ii.py b/Python/subsets-ii.py index a22bedb27..3c7199d83 100644 --- a/Python/subsets-ii.py +++ b/Python/subsets-ii.py @@ -1,14 +1,15 @@ -# Time: O(2^n) +from __future__ import print_function +# Time: O(n * 2^n) # Space: O(1) -# + # Given a collection of integers that might contain duplicates, S, return all possible subsets. -# +# # Note: # Elements in a subset must be in non-descending order. # The solution set must not contain duplicate subsets. # For example, # If S = [1,2,2], a solution is: -# +# # [ # [2], # [1], @@ -17,41 +18,71 @@ # [1,2], # [] # ] -# -class Solution: - # @param num, a list of integer - # @return a list of lists of integer - def subsetsWithDup(self, S): +class Solution(object): + def subsetsWithDup(self, nums): + """ + :type nums: List[int] + :rtype: List[List[int]] + """ + nums.sort() + result = [[]] + previous_size = 0 + for i in xrange(len(nums)): + size = len(result) + for j in xrange(size): + # Only union non-duplicate element or new union set. + if i == 0 or nums[i] != nums[i - 1] or j >= previous_size: + result.append(list(result[j])) + result[-1].append(nums[i]) + previous_size = size + return result + + +# Time: O(n * 2^n) ~ O((n * 2^n)^2) +# Space: O(1) +class Solution2(object): + def subsetsWithDup(self, nums): + """ + :type nums: List[int] + :rtype: List[List[int]] + """ result = [] - i, count = 0, 1 << len(S) - S = sorted(S) - + i, count = 0, 1 << len(nums) + nums.sort() + while i < count: cur = [] - for j in xrange(len(S)): + for j in xrange(len(nums)): if i & 1 << j: - cur.append(S[j]) + cur.append(nums[j]) if cur not in result: result.append(cur) i += 1 - + return result -class Solution2: - # @param num, a list of integer - # @return a list of lists of integer - def subsetsWithDup(self, S): + +# Time: O(n * 2^n) ~ O((n * 2^n)^2) +# Space: O(1) +class Solution3(object): + def subsetsWithDup(self, nums): + """ + :type nums: List[int] + :rtype: List[List[int]] + """ result = [] - self.subsetsWithDupRecu(result, [], sorted(S)) + self.subsetsWithDupRecu(result, [], sorted(nums)) return result - - def subsetsWithDupRecu(self, result, cur, S): - if len(S) == 0 and cur not in result: - result.append(cur) - elif S: - self.subsetsWithDupRecu(result, cur, S[1:]) - self.subsetsWithDupRecu(result, cur + [S[0]], S[1:]) - + + def subsetsWithDupRecu(self, result, cur, nums): + if not nums: + if cur not in result: + result.append(cur) + else: + self.subsetsWithDupRecu(result, cur, nums[1:]) + self.subsetsWithDupRecu(result, cur + [nums[0]], nums[1:]) + + if __name__ == "__main__": - print Solution().subsetsWithDup([1, 2, 2]) \ No newline at end of file + print(Solution().subsetsWithDup([1, 2, 2])) diff --git a/Python/subsets.py b/Python/subsets.py index 5143a2d62..952f4ad48 100644 --- a/Python/subsets.py +++ b/Python/subsets.py @@ -1,14 +1,15 @@ -# Time: O(2^n) +from __future__ import print_function +# Time: O(n * 2^n) # Space: O(1) -# + # Given a set of distinct integers, S, return all possible subsets. -# +# # Note: # Elements in a subset must be in non-descending order. # The solution set must not contain duplicate subsets. # For example, # If S = [1,2,3], a solution is: -# +# # [ # [3], # [1], @@ -19,37 +20,62 @@ # [1,2], # [] # ] -# -class Solution: - # @param S, a list of integer - # @return a list of lists of integer - def subsets(self, S): +class Solution(object): + def subsets(self, nums): + """ + :type nums: List[int] + :rtype: List[List[int]] + """ + nums.sort() + result = [[]] + for i in xrange(len(nums)): + size = len(result) + for j in xrange(size): + result.append(list(result[j])) + result[-1].append(nums[i]) + return result + + +# Time: O(n * 2^n) +# Space: O(1) +class Solution2(object): + def subsets(self, nums): + """ + :type nums: List[int] + :rtype: List[List[int]] + """ result = [] - i, count = 0, 1 << len(S) - S = sorted(S) - + i, count = 0, 1 << len(nums) + nums.sort() + while i < count: cur = [] - for j in xrange(len(S)): + for j in xrange(len(nums)): if i & 1 << j: - cur.append(S[j]) + cur.append(nums[j]) result.append(cur) i += 1 - + return result -class Solution2: - # @param S, a list of integer - # @return a list of lists of integer - def subsets(self, S): - return self.subsetsRecu([], sorted(S)) - - def subsetsRecu(self, cur, S): - if len(S) == 0: + +# Time: O(n * 2^n) +# Space: O(1) +class Solution3(object): + def subsets(self, nums): + """ + :type nums: List[int] + :rtype: List[List[int]] + """ + return self.subsetsRecu([], sorted(nums)) + + def subsetsRecu(self, cur, nums): + if not nums: return [cur] - - return self.subsetsRecu(cur, S[1:]) + self.subsetsRecu(cur + [S[0]], S[1:]) - + + return self.subsetsRecu(cur, nums[1:]) + self.subsetsRecu(cur + [nums[0]], nums[1:]) + + if __name__ == "__main__": - print Solution().subsets([1, 2, 3]) \ No newline at end of file + print(Solution().subsets([1, 2, 3])) diff --git a/Python/substring-with-concatenation-of-all-words.py b/Python/substring-with-concatenation-of-all-words.py index a6aeb0d27..4d12bdbf9 100644 --- a/Python/substring-with-concatenation-of-all-words.py +++ b/Python/substring-with-concatenation-of-all-words.py @@ -1,48 +1,99 @@ -# Time: O(m * n * k), where m is string length, n is dictionary size, k is word length - +from __future__ import print_function +# Time: O((m + n) * k), where m is string length, n is dictionary size, k is word length # Space: O(n * k) + +# You are given a string, s, and a list of words, words, +# that are all of the same length. Find all starting indices of substring(s) +# in s that is a concatenation of each word in words exactly once and +# without any intervening characters. # -# You are given a string, S, and a list of words, L, that are all of the same length. -# Find all starting indices of substring(s) in S that is a concatenation of each word -# in L exactly once and without any intervening characters. -# # For example, given: -# S: "barfoothefoobarman" -# L: ["foo", "bar"] -# +# s: "barfoothefoobarman" +# words: ["foo", "bar"] +# # You should return the indices: [0,9]. # (order does not matter). -# -class Solution: - # @param S, a string - # @param L, a list of string - # @return a list of integer - def findSubstring(self, S, L): - result, words, word_num, word_len = [], {}, len(L), len(L[0]) - for i in L: - if i not in words: - words[i] = 1 - else: - words[i] += 1 - - for i in xrange(len(S) + 1 - word_len * word_num): - cur, j = {}, 0 - while j < word_num: - word = S[i + j * word_len:i + j * word_len + word_len] - if word not in words: - break - if word not in cur: - cur[word] = 1 +# Sliding window solution + +import collections + + +class Solution(object): + def findSubstring(self, s, words): + """ + :type s: str + :type words: List[str] + :rtype: List[int] + """ + result, m, n, k = [], len(s), len(words), len(words[0]) + if m < n*k: + return result + + lookup = collections.defaultdict(int) + for i in words: + lookup[i] += 1 # Space: O(n * k) + + for i in xrange(k): # Time: O(k) + left, count = i, 0 + tmp = collections.defaultdict(int) + for j in xrange(i, m-k+1, k): # Time: O(m / k) + s1 = s[j:j+k]; # Time: O(k) + if s1 in lookup: + tmp[s1] += 1 + if tmp[s1] <= lookup[s1]: + count += 1 + else: + while tmp[s1] > lookup[s1]: + s2 = s[left:left+k] + tmp[s2] -= 1 + if tmp[s2] < lookup[s2]: + count -= 1 + left += k + if count == n: + result.append(left) + tmp[s[left:left+k]] -= 1 + count -= 1 + left += k else: - cur[word] += 1 - if cur[word] > words[word]: + tmp = collections.defaultdict(int) + count = 0 + left = j+k + return result + + +# Time: O(m * n * k), where m is string length, n is dictionary size, k is word length +# Space: O(n * k) +class Solution2(object): + def findSubstring(self, s, words): + """ + :type s: str + :type words: List[str] + :rtype: List[int] + """ + result, m, n, k = [], len(s), len(words), len(words[0]) + if m < n*k: + return result + + lookup = collections.defaultdict(int) + for i in words: + lookup[i] += 1 # Space: O(n * k) + + for i in xrange(m+1-k*n): # Time: O(m) + cur, j = collections.defaultdict(int), 0 + while j < n: # Time: O(n) + word = s[i+j*k:i+j*k+k] # Time: O(k) + if word not in lookup: + break + cur[word] += 1 + if cur[word] > lookup[word]: break j += 1 - if j == word_num: + if j == n: result.append(i) - + return result + if __name__ == "__main__": - print Solution().findSubstring("barfoothefoobarman", ["foo", "bar"]) + print(Solution().findSubstring("barfoothefoobarman", ["foo", "bar"])) diff --git a/Python/subtree-of-another-tree.py b/Python/subtree-of-another-tree.py new file mode 100644 index 000000000..423941776 --- /dev/null +++ b/Python/subtree-of-another-tree.py @@ -0,0 +1,68 @@ +# Time: O(m * n), m is the number of nodes of s, n is the number of nodes of t +# Space: O(h), h is the height of s + +# Given two non-empty binary trees s and t, +# check whether tree t has exactly the same structure and +# node values with a subtree of s. +# A subtree of s is a tree consists of a node in s and all of this node's descendants. +# The tree s could also be considered as a subtree of itself. +# +# Example 1: +# Given tree s: +# +# 3 +# / \ +# 4 5 +# / \ +# 1 2 +# Given tree t: +# 4 +# / \ +# 1 2 +# Return true, because t has the same structure and node values with a subtree of s. +# Example 2: +# Given tree s: +# +# 3 +# / \ +# 4 5 +# / \ +# 1 2 +# / +# 0 +# Given tree t: +# 4 +# / \ +# 1 2 +# Return false. + +# Definition for a binary tree node. +# class TreeNode(object): +# def __init__(self, x): +# self.val = x +# self.left = None +# self.right = None + +class Solution(object): + def isSubtree(self, s, t): + """ + :type s: TreeNode + :type t: TreeNode + :rtype: bool + """ + def isSame(x, y): + if not x and not y: + return True + if not x or not y: + return False + return x.val == y.val and \ + isSame(x.left, y.left) and \ + isSame(x.right, y.right) + + def preOrderTraverse(s, t): + return s != None and \ + (isSame(s, t) or \ + preOrderTraverse(s.left, t) or \ + preOrderTraverse(s.right, t)) + + return preOrderTraverse(s, t) diff --git a/Python/sudoku-solver.py b/Python/sudoku-solver.py index ed997c11f..39835b421 100644 --- a/Python/sudoku-solver.py +++ b/Python/sudoku-solver.py @@ -2,9 +2,9 @@ # Space: (1) # # Write a program to solve a Sudoku puzzle by filling the empty cells. -# +# # Empty cells are indicated by the character '.'. -# +# # You may assume that there will be only one unique solution. # @@ -13,46 +13,33 @@ class Solution: # Solve the Sudoku by modifying the input board in-place. # Do not return any value. def solveSudoku(self, board): - for i in xrange(len(board)): - for j in xrange(len(board[0])): - if(board[i][j] == '.'): - for k in xrange(9): - board[i][j] = chr(ord('1') + k) - if self.isValid(board, i, j) and self.solveSudoku(board): - return True - board[i][j] = '.' + def isValid(board, x, y): + for i in xrange(9): + if i != x and board[i][y] == board[x][y]: return False - return True - - def isValid(self, board, x, y): - for i in xrange(9): - if i != x and board[i][y] == board[x][y]: - return False - - for j in xrange(9): - if j != y and board[x][j] == board[x][y]: - return False - - i = 3 * (x / 3) - while i < 3 * (x / 3 + 1): - j = 3 * (y / 3) - while j < 3 * (y / 3 + 1): - if (i != x or j != y) and board[i][j] == board[x][y]: + for j in xrange(9): + if j != y and board[x][j] == board[x][y]: return False - j += 1 - i += 1 - - return True + i = 3 * (x / 3) + while i < 3 * (x / 3 + 1): + j = 3 * (y / 3) + while j < 3 * (y / 3 + 1): + if (i != x or j != y) and board[i][j] == board[x][y]: + return False + j += 1 + i += 1 + return True + def solver(board): + for i in xrange(len(board)): + for j in xrange(len(board[0])): + if(board[i][j] == '.'): + for k in xrange(9): + board[i][j] = chr(ord('1') + k) + if isValid(board, i, j) and solver(board): + return True + board[i][j] = '.' + return False + return True -if __name__ == "__main__": - board = [['5', '3', '.', '.', '7', '.', '.', '.', '.'], - ['6', '.', '.', '1', '9', '5', '.', '.', '.'], - ['.', '9', '8', '.', '.', '.', '.', '6', '.'], - ['8', '.', '.', '.', '6', '.', '.', '.', '3'], - ['4', '.', '.', '8', '.', '3', '.', '.', '1'], - ['7', '.', '.', '.', '2', '.', '.', '.', '6'], - ['.', '6', '.', '.', '.', '.', '2', '8', '.'], - ['.', '.', '.', '4', '1', '9', '.', '.', '5'], - ['.', '.', '.', '.', '8', '.', '.', '7', '9']] - print Solution().solveSudoku(board) + solver(board) diff --git a/Python/sum-of-distances-in-tree.py b/Python/sum-of-distances-in-tree.py new file mode 100644 index 000000000..8e96d8874 --- /dev/null +++ b/Python/sum-of-distances-in-tree.py @@ -0,0 +1,61 @@ +# Time: O(n) +# Space: O(n) + +# An undirected, connected tree with N nodes +# labelled 0...N-1 and N-1 edges are given. +# +# The ith edge connects nodes edges[i][0] and edges[i][1] together. +# +# Return a list ans, where ans[i] is the sum of the distances +# between node i and all other nodes. +# +# Example 1: +# +# Input: N = 6, edges = [[0,1],[0,2],[2,3],[2,4],[2,5]] +# Output: [8,12,6,10,10,10] +# Explanation: +# Here is a diagram of the given tree: +# 0 +# / \ +# 1 2 +# /|\ +# 3 4 5 +# We can see that dist(0,1) + dist(0,2) + dist(0,3) + dist(0,4) + dist(0,5) +# equals 1 + 1 + 2 + 2 + 2 = 8. Hence, answer[0] = 8, and so on. +# Note: 1 <= N <= 10000 + +import collections + + +class Solution(object): + def sumOfDistancesInTree(self, N, edges): + """ + :type N: int + :type edges: List[List[int]] + :rtype: List[int] + """ + def dfs(graph, node, parent, count, result): + for nei in graph[node]: + if nei != parent: + dfs(graph, nei, node, count, result) + count[node] += count[nei] + result[node] += result[nei]+count[nei] + + def dfs2(graph, node, parent, count, result): + for nei in graph[node]: + if nei != parent: + result[nei] = result[node]-count[nei] + \ + len(count)-count[nei] + dfs2(graph, nei, node, count, result) + + graph = collections.defaultdict(list) + for u, v in edges: + graph[u].append(v) + graph[v].append(u) + + count = [1] * N + result = [0] * N + + dfs(graph, 0, None, count, result) + dfs2(graph, 0, None, count, result) + return result diff --git a/Python/sum-of-left-leaves.py b/Python/sum-of-left-leaves.py new file mode 100644 index 000000000..fb0787415 --- /dev/null +++ b/Python/sum-of-left-leaves.py @@ -0,0 +1,38 @@ +# Time: O(n) +# Space: O(h) + +# Find the sum of all left leaves in a given binary tree. +# +# Example: +# +# 3 +# / \ +# 9 20 +# / \ +# 15 7 +# +# There are two left leaves in the binary tree, +# with values 9 and 15 respectively. Return 24. + +# Definition for a binary tree node. +# class TreeNode(object): +# def __init__(self, x): +# self.val = x +# self.left = None +# self.right = None + +class Solution(object): + def sumOfLeftLeaves(self, root): + """ + :type root: TreeNode + :rtype: int + """ + def sumOfLeftLeavesHelper(root, is_left): + if not root: + return 0 + if not root.left and not root.right: + return root.val if is_left else 0 + return sumOfLeftLeavesHelper(root.left, True) + \ + sumOfLeftLeavesHelper(root.right, False) + + return sumOfLeftLeavesHelper(root, False) diff --git a/Python/sum-of-square-numbers.py b/Python/sum-of-square-numbers.py new file mode 100644 index 000000000..2688be8d1 --- /dev/null +++ b/Python/sum-of-square-numbers.py @@ -0,0 +1,28 @@ +# Time: O(sqrt(c) * logc) +# Space: O(1) + +# Given a non-negative integer c, your task is to decide +# whether there're two integers a and b such that a2 + b2 = c. +# +# Example 1: +# Input: 5 +# Output: True +# Explanation: 1 * 1 + 2 * 2 = 5 +# Example 2: +# Input: 3 +# Output: False + +import math + + +class Solution(object): + def judgeSquareSum(self, c): + """ + :type c: int + :rtype: bool + """ + for a in xrange(int(math.sqrt(c))+1): + b = int(math.sqrt(c-a**2)) + if a**2 + b**2 == c: + return True + return False diff --git a/Python/sum-of-two-integers.py b/Python/sum-of-two-integers.py new file mode 100644 index 000000000..1990ef06b --- /dev/null +++ b/Python/sum-of-two-integers.py @@ -0,0 +1,77 @@ +# Time: O(1) +# Space: O(1) + +# Calculate the sum of two integers a and b, +# but you are not allowed to use the operator + and -. +# +# Example: +# Given a = 1 and b = 2, return 3. + + +class Solution(object): + def getSum(self, a, b): + """ + :type a: int + :type b: int + :rtype: int + """ + bit_length = 32 + neg_bit, mask = (1 << bit_length) >> 1, ~(~0 << bit_length) + + a = (a | ~mask) if (a & neg_bit) else (a & mask) + b = (b | ~mask) if (b & neg_bit) else (b & mask) + + while b: + carry = a & b + a ^= b + a = (a | ~mask) if (a & neg_bit) else (a & mask) + b = carry << 1 + b = (b | ~mask) if (b & neg_bit) else (b & mask) + + return a + + def getSum2(self, a, b): + """ + :type a: int + :type b: int + :rtype: int + """ + # 32 bits integer max + MAX = 0x7FFFFFFF + # 32 bits interger min + MIN = 0x80000000 + # mask to get last 32 bits + mask = 0xFFFFFFFF + while b: + # ^ get different bits and & gets double 1s, << moves carry + a, b = (a ^ b) & mask, ((a & b) << 1) & mask + # if a is negative, get a's 32 bits complement positive first + # then get 32-bit positive's Python complement negative + return a if a <= MAX else ~(a ^ mask) + + def minus(self, a, b): + b = self.getSum(~b, 1) + return self.getSum(a, b) + + def multiply(self, a, b): + isNeg = (a > 0) ^ (b > 0) + x = a if a > 0 else self.getSum(~a, 1) + y = b if b > 0 else self.getSum(~b, 1) + ans = 0 + while y & 0x01: + ans = self.getSum(ans, x) + y >>= 1 + x <<= 1 + return self.getSum(~ans, 1) if isNeg else ans + + def divide(self, a, b): + isNeg = (a > 0) ^ (b > 0) + x = a if a > 0 else self.getSum(~a, 1) + y = b if b > 0 else self.getSum(~b, 1) + ans = 0 + for i in range(31, -1, -1): + if (x >> i) >= y: + x = self.minus(x, y << i) + ans = self.getSum(ans, 1 << i) + return self.getSum(~ans, 1) if isNeg else ans + diff --git a/Python/sum-root-to-leaf-numbers.py b/Python/sum-root-to-leaf-numbers.py index 763a9faba..2c1a71365 100644 --- a/Python/sum-root-to-leaf-numbers.py +++ b/Python/sum-root-to-leaf-numbers.py @@ -1,20 +1,21 @@ +from __future__ import print_function # Time: O(n) -# Space: O(logn) +# Space: O(h), h is height of binary tree # # Given a binary tree containing digits from 0-9 only, each root-to-leaf path could represent a number. -# +# # An example is the root-to-leaf path 1->2->3 which represents the number 123. -# +# # Find the total sum of all root-to-leaf numbers. -# +# # For example, -# +# # 1 # / \ # 2 3 # The root-to-leaf path 1->2 represents the number 12. # The root-to-leaf path 1->3 represents the number 13. -# +# # Return the sum = 12 + 13 = 25. # @@ -30,18 +31,18 @@ class Solution: # @return an integer def sumNumbers(self, root): return self.sumNumbersRecu(root, 0) - + def sumNumbersRecu(self, root, num): if root is None: return 0 - + if root.left is None and root.right is None: return num * 10 + root.val - + return self.sumNumbersRecu(root.left, num * 10 + root.val) + self.sumNumbersRecu(root.right, num * 10 + root.val) if __name__ == "__main__": root = TreeNode(1) root.left = TreeNode(2) root.right = TreeNode(3) - print Solution().sumNumbers(root) \ No newline at end of file + print(Solution().sumNumbers(root)) diff --git a/Python/summary-ranges.py b/Python/summary-ranges.py new file mode 100644 index 000000000..b8bded7ee --- /dev/null +++ b/Python/summary-ranges.py @@ -0,0 +1,46 @@ +# Time: O(n) +# Space: O(1) +# +# Given a sorted integer array without duplicates, +# return the summary of its ranges. +# +# For example, given [0,1,2,4,5,7], +# return ["0->2","4->5","7"]. +# + +import bisect +import collections +import itertools +import re + + +class Solution: + # @param {integer[]} nums + # @return {string[]} + def summaryRanges(self, nums): + ranges = [] + if not nums: + return ranges + + start, end = nums[0], nums[0] + for i in xrange(1, len(nums) + 1): + if i < len(nums) and nums[i] == end + 1: + end = nums[i] + else: + interval = str(start) + if start != end: + interval += "->" + str(end) + ranges.append(interval) + if i < len(nums): + start = end = nums[i] + + return ranges + +# Time: O(n) +# Space: O(n) +class Solution2: + # @param {integer[]} nums + # @return {string[]} + def summaryRanges(self, nums): + return [re.sub('->.*>', '->', '->'.join(repr(n) for _, n in g)) + for _, g in itertools.groupby(enumerate(nums), lambda i_n: i_n[1]-i_n[0])] diff --git a/Python/super-pow.py b/Python/super-pow.py new file mode 100644 index 000000000..70caf17c8 --- /dev/null +++ b/Python/super-pow.py @@ -0,0 +1,40 @@ +# Time: O(n), n is the size of b. +# Space: O(1) + +# Your task is to calculate a^b mod 1337 where a is a positive integer +# and b is an extremely large positive integer given in the form of an array. +# +# Example1: +# +# a = 2 +# b = [3] +# +# Result: 8 +# Example2: +# +# a = 2 +# b = [1,0] +# +# Result: 1024 + +class Solution(object): + def superPow(self, a, b): + """ + :type a: int + :type b: List[int] + :rtype: int + """ + def myPow(a, n, b): + result = 1 + x = a % b + while n: + if n & 1: + result = result * x % b + n >>= 1 + x = x * x % b + return result % b + + result = 1 + for digit in b: + result = myPow(result, 10, 1337) * myPow(a, digit, 1337) % 1337 + return result diff --git a/Python/super-ugly-number.py b/Python/super-ugly-number.py new file mode 100644 index 000000000..4ac12281d --- /dev/null +++ b/Python/super-ugly-number.py @@ -0,0 +1,142 @@ +# Time: O(n * k) +# Space: O(n + k) + +# Write a program to find the nth super ugly number. +# +# Super ugly numbers are positive numbers whose all +# prime factors are in the given prime list primes of size k. +# For example, [1, 2, 4, 7, 8, 13, 14, 16, 19, 26, 28, 32] +# is the sequence of the first 12 super ugly numbers given +# primes = [2, 7, 13, 19] of size 4. +# +# Note: +# (1) 1 is a super ugly number for any given primes. +# (2) The given numbers in primes are in ascending order. +# (3) 0 < k <= 100, 0 < n <= 106, 0 < primes[i] < 1000. + +import heapq + + +# Heap solution. (620ms) +class Solution(object): + def nthSuperUglyNumber(self, n, primes): + """ + :type n: int + :type primes: List[int] + :rtype: int + """ + heap, uglies, idx, ugly_by_last_prime = [], [0] * n, [0] * len(primes), [0] * n + uglies[0] = 1 + + for k, p in enumerate(primes): + heapq.heappush(heap, (p, k)) + + for i in xrange(1, n): + uglies[i], k = heapq.heappop(heap) + ugly_by_last_prime[i] = k + idx[k] += 1 + while ugly_by_last_prime[idx[k]] > k: + idx[k] += 1 + heapq.heappush(heap, (primes[k] * uglies[idx[k]], k)) + + return uglies[-1] + +# Time: O(n * k) +# Space: O(n + k) +# Hash solution. (932ms) +class Solution2(object): + def nthSuperUglyNumber(self, n, primes): + """ + :type n: int + :type primes: List[int] + :rtype: int + """ + uglies, idx, heap, ugly_set = [0] * n, [0] * len(primes), [], set([1]) + uglies[0] = 1 + + for k, p in enumerate(primes): + heapq.heappush(heap, (p, k)) + ugly_set.add(p) + + for i in xrange(1, n): + uglies[i], k = heapq.heappop(heap) + while (primes[k] * uglies[idx[k]]) in ugly_set: + idx[k] += 1 + heapq.heappush(heap, (primes[k] * uglies[idx[k]], k)) + ugly_set.add(primes[k] * uglies[idx[k]]) + + return uglies[-1] + +# Time: O(n * logk) ~ O(n * klogk) +# Space: O(n + k) +class Solution3(object): + def nthSuperUglyNumber(self, n, primes): + """ + :type n: int + :type primes: List[int] + :rtype: int + """ + uglies, idx, heap = [1], [0] * len(primes), [] + for k, p in enumerate(primes): + heapq.heappush(heap, (p, k)) + + for i in xrange(1, n): + min_val, k = heap[0] + uglies += [min_val] + + while heap[0][0] == min_val: # worst time: O(klogk) + min_val, k = heapq.heappop(heap) + idx[k] += 1 + heapq.heappush(heap, (primes[k] * uglies[idx[k]], k)) + + return uglies[-1] + +# Time: O(n * k) +# Space: O(n + k) +# TLE due to the last test case, but it passess and performs the best in C++. +class Solution4(object): + def nthSuperUglyNumber(self, n, primes): + """ + :type n: int + :type primes: List[int] + :rtype: int + """ + uglies = [0] * n + uglies[0] = 1 + ugly_by_prime = list(primes) + idx = [0] * len(primes) + + for i in xrange(1, n): + uglies[i] = min(ugly_by_prime) + for k in xrange(len(primes)): + if uglies[i] == ugly_by_prime[k]: + idx[k] += 1 + ugly_by_prime[k] = primes[k] * uglies[idx[k]] + + return uglies[-1] + +# Time: O(n * logk) ~ O(n * klogk) +# Space: O(k^2) +# TLE due to the last test case, but it passess and performs well in C++. +class Solution5(object): + def nthSuperUglyNumber(self, n, primes): + """ + :type n: int + :type primes: List[int] + :rtype: int + """ + ugly_number = 0 + + heap = [] + heapq.heappush(heap, 1) + for p in primes: + heapq.heappush(heap, p) + for _ in xrange(n): + ugly_number = heapq.heappop(heap) + for i in xrange(len(primes)): + if ugly_number % primes[i] == 0: + for j in xrange(i + 1): + heapq.heappush(heap, ugly_number * primes[j]) + break + + return ugly_number diff --git a/Python/super-washing-machines.py b/Python/super-washing-machines.py new file mode 100644 index 000000000..202cba8db --- /dev/null +++ b/Python/super-washing-machines.py @@ -0,0 +1,61 @@ +# Time: O(n) +# Space: O(1) + +# You have n super washing machines on a line. +# Initially, each washing machine has some dresses or is empty. +# +# For each move, you could choose any m (1 <= m <= n) washing machines, +# and pass one dress of each washing machine to one of +# its adjacent washing machines at the same time . +# +# Given an integer array representing the number of dresses +# in each washing machine from left to right on the line, +# you should find the minimum number of moves to make +# all the washing machines have the same number of dresses. +# If it is not possible to do it, return -1. +# +# Example1 +# +# Input: [1,0,5] +# +# Output: 3 +# +# Explanation: +# 1st move: 1 0 <-- 5 => 1 1 4 +# 2nd move: 1 <-- 1 <-- 4 => 2 1 3 +# 3rd move: 2 1 <-- 3 => 2 2 2 +# Example2 +# +# Input: [0,3,0] +# +# Output: 2 +# +# Explanation: +# 1st move: 0 <-- 3 0 => 1 2 0 +# 2nd move: 1 2 --> 0 => 1 1 1 +# Example3 +# +# Input: [0,2,0] +# +# Output: -1 +# +# Explanation: +# It's impossible to make all the three washing machines have the same number of dresses. +# Note: +# The range of n is [1, 10000]. +# The range of dresses number in a super washing machine is [0, 1e5]. + +class Solution(object): + def findMinMoves(self, machines): + """ + :type machines: List[int] + :rtype: int + """ + total = sum(machines) + if total % len(machines): return -1 + + result, target, curr = 0, total / len(machines), 0 + for n in machines: + curr += n - target + result = max(result, max(n - target, abs(curr))) + return result diff --git a/Python/surrounded-region.py b/Python/surrounded-regions.py similarity index 69% rename from Python/surrounded-region.py rename to Python/surrounded-regions.py index a16bbee0b..6178ac2d5 100644 --- a/Python/surrounded-region.py +++ b/Python/surrounded-regions.py @@ -1,51 +1,56 @@ +from __future__ import print_function # Time: O(m * n) # Space: O(m + n) # # Given a 2D board containing 'X' and 'O', capture all regions surrounded by 'X'. -# +# # A region is captured by flipping all 'O's into 'X's in that surrounded region. -# +# # For example, # X X X X # X O O X # X X O X # X O X X # After running your function, the board should be: -# +# # X X X X # X X X X # X X X X # X O X X # +import collections + + class Solution: # @param board, a 2D array # Capture all regions by modifying the input board in-place. # Do not return any value. def solve(self, board): - if len(board) == 0: + if not board: return - current = [] - + q = collections.deque([]) + for i in xrange(len(board)): - current.append((i, 0)) - current.append((i, len(board[0]) - 1)) - - for i in xrange(len(board[0])): - current.append((0, i)) - current.append((len(board) - 1, i)) - - while current: - i, j = current.pop() + q.append((i, 0)) + q.append((i, len(board[0]) - 1)) + + for j in xrange(len(board[0])): + q.append((0, j)) + q.append((len(board) - 1, j)) + + while q: + i, j = q.popleft() if board[i][j] in ['O', 'V']: board[i][j] = 'V' for x, y in [(i + 1, j), (i - 1, j), (i, j + 1), (i, j - 1)]: - if 0 <= x < len(board) and 0 <= y < len(board[0]) and board[x][y] == 'O': + if 0 <= x < len(board) and 0 <= y < len(board[0]) and \ + board[x][y] == 'O': board[x][y] = 'V' - current.append((x, y)) - + q.append((x, y)) + for i in xrange(len(board)): - for j in range(len(board[0])): + for j in xrange(len(board[0])): if board[i][j] != 'V': board[i][j] = 'X' else: @@ -57,4 +62,4 @@ def solve(self, board): ['X', 'X', 'O', 'X'], ['X', 'O', 'X', 'X']] Solution().solve(board) - print board \ No newline at end of file + print(board) diff --git a/Python/swap-adjacent-in-lr-string.py b/Python/swap-adjacent-in-lr-string.py new file mode 100644 index 000000000..1a2129283 --- /dev/null +++ b/Python/swap-adjacent-in-lr-string.py @@ -0,0 +1,49 @@ +# Time: O(n) +# Space: O(1) + +# In a string composed of 'L', 'R', and 'X' characters, like "RXXLRXRXL", +# a move consists of either replacing one occurrence of "XL" with "LX", +# or replacing one occurrence of "RX" with "XR". +# Given the starting string start and the ending string end, +# return True if and only if there exists a sequence of moves to transform one string to the other. +# +# Example: +# Input: start = "RXXLRXRXL", end = "XRLXXRRLX" +# Output: True +# +# Explanation: +# We can transform start to end following these steps: +# RXXLRXRXL -> +# XRXLRXRXL -> +# XRLXRXRXL -> +# XRLXXRRXL -> +# XRLXXRRLX +# +# Note: +# - 1 <= len(start) = len(end) <= 10000. +# - Both start and end will only consist of characters in {'L', 'R', 'X'}. + +class Solution(object): + def canTransform(self, start, end): + """ + :type start: str + :type end: str + :rtype: bool + """ + N = len(start) + i, j = 0, 0 + while i < N and j < N: + while i < N and start[i] == 'X': + i += 1 + while j < N and end[j] == 'X': + j += 1 + if (i < N) != (j < N): + return False + elif i < N and j < N: + if start[i] != end[j] or \ + (start[i] == 'L' and i < j) or \ + (start[i] == 'R' and i > j): + return False + i += 1 + j += 1 + return True diff --git a/Python/swap-nodes-in-pairs.py b/Python/swap-nodes-in-pairs.py index c11df1225..73ca8fc63 100644 --- a/Python/swap-nodes-in-pairs.py +++ b/Python/swap-nodes-in-pairs.py @@ -1,11 +1,12 @@ +from __future__ import print_function # Time: O(n) # Space: O(1) # # Given a linked list, swap every two adjacent nodes and return its head. -# +# # For example, # Given 1->2->3->4, you should return the list as 2->1->4->3. -# +# # Your algorithm should use only constant space. # You may not modify the values in the list, only nodes itself can be changed. # @@ -15,7 +16,7 @@ class ListNode: def __init__(self, x): self.val = x self.next = None - + def __repr__(self): if self: return "{} -> {}".format(self.val, self.next) @@ -38,5 +39,4 @@ def swapPairs(self, head): if __name__ == "__main__": head = ListNode(1) head.next, head.next.next, head.next.next.next = ListNode(2), ListNode(3), ListNode(4) - print Solution().swapPairs(head) - \ No newline at end of file + print(Solution().swapPairs(head)) diff --git a/Python/swim-in-rising-water.py b/Python/swim-in-rising-water.py new file mode 100644 index 000000000..f2034acac --- /dev/null +++ b/Python/swim-in-rising-water.py @@ -0,0 +1,82 @@ +# Time: O(n^2) +# Space: O(n^2) + +# On an N x N grid, each square grid[i][j] represents the elevation at that point (i,j). +# +# Now rain starts to fall. At time t, the depth of the water everywhere is t. +# You can swim from a square to another 4-directionally adjacent square +# if and only if the elevation of both squares individually are at most t. +# You can swim infinite distance in zero time. Of course, +# you must stay within the boundaries of the grid during your swim. +# +# You start at the top left square (0, 0). +# What is the least time until you can reach the bottom right square (N-1, N-1)? +# +# Example 1: +# Input: [[0,2],[1,3]] +# Output: 3 +# Explanation: +# At time 0, you are in grid location (0, 0). +# You cannot go anywhere else because 4-directionally adjacent neighbors have a higher elevation than t = 0. +# +# You cannot reach point (1, 1) until time 3. +# When the depth of water is 3, we can swim anywhere inside the grid. +# Example 2: +# +# Input: [[0,1,2,3,4],[24,23,22,21,5],[12,13,14,15,16],[11,17,18,19,20],[10,9,8,7,6]] +# Output: 16 +# Explanation: +# 0 1 2 3 4 +# 24 23 22 21 5 +# 12 13 14 15 16 +# 11 17 18 19 20 +# 10 9 8 7 6 +# +# The final route is marked in bold. +# We need to wait until time 16 so that (0, 0) and (4, 4) are connected. +# +# Note: +# - 2 <= N <= 50. +# - grid[i][j] is a permutation of [0, ..., N*N - 1]. + +class UnionFind(object): + def __init__(self, n): + self.set = range(n) + + def find_set(self, x): + if self.set[x] != x: + self.set[x] = self.find_set(self.set[x]) # path compression. + return self.set[x] + + def union_set(self, x, y): + x_root, y_root = map(self.find_set, (x, y)) + if x_root == y_root: + return False + self.set[min(x_root, y_root)] = max(x_root, y_root) + return True + + +class Solution(object): + def swimInWater(self, grid): + """ + :type grid: List[List[int]] + :rtype: int + """ + n = len(grid) + positions = [None] * (n**2) + for i in xrange(n): + for j in xrange(n): + positions[grid[i][j]] = (i, j) + directions = ((-1, 0), (1, 0), (0, -1), (0, 1)) + + union_find = UnionFind(n**2) + for elevation in xrange(n**2): + i, j = positions[elevation] + for direction in directions: + x, y = i+direction[0], j+direction[1] + if 0 <= x < n and 0 <= y < n and grid[x][y] <= elevation: + union_find.union_set(i*n+j, x*n+y) + if union_find.find_set(0) == union_find.find_set(n**2-1): + return elevation + return n**2-1 + diff --git a/Python/symmetric-tree.py b/Python/symmetric-tree.py index 6b2a1c991..1b361f336 100644 --- a/Python/symmetric-tree.py +++ b/Python/symmetric-tree.py @@ -1,9 +1,10 @@ +from __future__ import print_function # Time: O(n) -# Space: O(logn) +# Space: O(h), h is height of binary tree # Given a binary tree, check whether it is a mirror of itself (ie, symmetric around its center). -# +# # For example, this binary tree is symmetric: -# +# # 1 # / \ # 2 2 @@ -36,24 +37,24 @@ def isSymmetric(self, root): stack = [] stack.append(root.left) stack.append(root.right) - + while stack: p, q = stack.pop(), stack.pop() - + if p is None and q is None: continue - + if p is None or q is None or p.val != q.val: return False - + stack.append(p.left) stack.append(q.right) - + stack.append(p.right) stack.append(q.left) - + return True - + # Recursive solution class Solution2: # @param root, a tree node @@ -61,9 +62,9 @@ class Solution2: def isSymmetric(self, root): if root is None: return True - + return self.isSymmetricRecu(root.left, root.right) - + def isSymmetricRecu(self, left, right): if left is None and right is None: return True @@ -76,5 +77,5 @@ def isSymmetricRecu(self, left, right): root.left, root.right = TreeNode(2), TreeNode(2) root.left.left, root.right.right = TreeNode(3), TreeNode(3) root.left.right, root.right.left = TreeNode(4), TreeNode(4) - print Solution().isSymmetric(root) - \ No newline at end of file + print(Solution().isSymmetric(root)) + diff --git a/Python/tag-validator.py b/Python/tag-validator.py new file mode 100644 index 000000000..7409ec413 --- /dev/null +++ b/Python/tag-validator.py @@ -0,0 +1,133 @@ +# Time: O(n) +# Space: O(n) + +# Given a string representing a code snippet, +# you need to implement a tag validator to parse the code and return whether it is valid. +# A code snippet is valid if all the following rules hold: +# +# 1. The code must be wrapped in a valid closed tag. Otherwise, the code is invalid. +# 2. A closed tag (not necessarily valid) has exactly the following format : +# TAG_CONTENT. Among them, is the start tag, +# and is the end tag. The TAG_NAME in start and end tags should be the same. +# A closed tag is valid if and only if the TAG_NAME and TAG_CONTENT are valid. +# 3. A valid TAG_NAME only contain upper-case letters, and has length in range [1,9]. +# Otherwise, the TAG_NAME is invalid. +# 4. A valid TAG_CONTENT may contain other valid closed tags, +# cdata and any characters (see note1) EXCEPT unmatched <, +# unmatched start and end tag, and unmatched or closed tags with invalid TAG_NAME. +# Otherwise, the TAG_CONTENT is invalid. +# 5. A start tag is unmatched if no end tag exists with the same TAG_NAME, +# and vice versa. However, you also need to consider the issue of unbalanced when tags are nested. +# 6. A < is unmatched if you cannot find a subsequent >. +# And when you find a < or should be +# parsed as TAG_NAME (not necessarily valid). +# 7. The cdata has the following format : . +# The range of CDATA_CONTENT is defined as the characters between . +# 8. CDATA_CONTENT may contain any characters. +# The function of cdata is to forbid the validator to parse CDATA_CONTENT, +# so even it has some characters that can be parsed as tag (no matter valid or invalid), +# you should treat it as regular characters. +# +# Valid Code Examples: +# Input: "

" +# Output: True +# Explanation: +# The code is wrapped in a closed tag :
and
. +# The TAG_NAME is valid, the TAG_CONTENT consists of some characters and cdata. +# Although CDATA_CONTENT has unmatched start tag with invalid TAG_NAME, +# it should be considered as plain text, not parsed as tag. +# So TAG_CONTENT is valid, and then the code is valid. Thus return true. +# +# Input: "
>> ![cdata[]] ]>]]>]]>>]
" +# Output: True +# Explanation: +# We first separate the code into : start_tag|tag_content|end_tag. +# start_tag -> "
" +# end_tag -> "
" +# tag_content could also be separated into : text1|cdata|text2. +# text1 -> ">> ![cdata[]] " +# cdata -> "]>]]>", where the CDATA_CONTENT is "
]>" +# text2 -> "]]>>]" +# +# The reason why start_tag is NOT "
>>" is because of the rule 6. +# The reason why cdata is NOT "]>]]>]]>" is because of the rule 7. +# Invalid Code Examples: +# Input: " " +# Output: False +# Explanation: Unbalanced. If "" is closed, then "" must be unmatched, and vice versa. +# +# Input: "
div tag is not closed
" +# Output: False +# +# Input: "
unmatched <
" +# Output: False +# +# Input: "
closed tags with invalid tag name 123
" +# Output: False +# +# Input: "
unmatched tags with invalid tag name and
" +# Output: False +# +# Input: "
unmatched start tag and unmatched end tag
" +# Output: False +# Note: +# For simplicity, you could assume the input code (including the any characters mentioned above) +# only contain letters, digits, '<','>','/','!','[',']' and ' '. + +class Solution(object): + def isValid(self, code): + """ + :type code: str + :rtype: bool + """ + def validText(s, i): + j = i + i = s.find("<", i) + return i != j, i + + def validCData(s, i): + if s.find("", i) + if j == -1: + return False, i + return True, j+3 + + def parseTagName(s, i): + if s[i] != '<': + return "", i + j = s.find('>', i) + if j == -1 or not (1 <= (j-1-i) <= 9): + return "", i + tag = s[i+1:j] + for c in tag: + if not (ord('A') <= ord(c) <= ord('Z')): + return "", i + return tag, j+1 + + def parseContent(s, i): + while i < len(s): + result, i = validText(s, i) + if result: + continue + result, i = validCData(s, i) + if result: + continue + result, i = validTag(s, i) + if result: + continue + break + return i + + def validTag(s, i): + tag, j = parseTagName(s, i) + if not tag: + return False, i + j = parseContent(s, j) + k = j + len(tag) + 2 + if k >= len(s) or s[j:k+1] != "": + return False, i + return True, k+1 + + result, i = validTag(code, 0) + return result and i == len(code) diff --git a/Python/target-sum.py b/Python/target-sum.py new file mode 100644 index 000000000..cb01825df --- /dev/null +++ b/Python/target-sum.py @@ -0,0 +1,49 @@ +# Time: O(n * S) +# Space: O(S) + +# You are given a list of non-negative integers, a1, a2, ..., an, +# and a target, S. Now you have 2 symbols + and -. +# For each integer, you should choose one from + and - as its new symbol. +# +# Find out how many ways to assign symbols to make sum of integers equal to target S. +# +# Example 1: +# Input: nums is [1, 1, 1, 1, 1], S is 3. +# Output: 5 +# Explanation: +# +# -1+1+1+1+1 = 3 +# +1-1+1+1+1 = 3 +# +1+1-1+1+1 = 3 +# +1+1+1-1+1 = 3 +# +1+1+1+1-1 = 3 +# +# There are 5 ways to assign symbols to make the sum of nums be target 3. +# Note: +# The length of the given array is positive and will not exceed 20. +# The sum of elements in the given array will not exceed 1000. +# Your output answer is guaranteed to be fitted in a 32-bit integer. + +import collections + + +class Solution(object): + def findTargetSumWays(self, nums, S): + """ + :type nums: List[int] + :type S: int + :rtype: int + """ + def subsetSum(nums, S): + dp = collections.defaultdict(int) + dp[0] = 1 + for n in nums: + for i in reversed(xrange(n, S+1)): + if i-n in dp: + dp[i] += dp[i-n] + return dp[S] + + total = sum(nums) + if total < S or (S + total) % 2: return 0 + P = (S + total) // 2 + return subsetSum(nums, P) diff --git a/Python/task-scheduler.py b/Python/task-scheduler.py new file mode 100644 index 000000000..1e2bb7813 --- /dev/null +++ b/Python/task-scheduler.py @@ -0,0 +1,45 @@ +# Time: O(n) +# Space: O(26) = O(1) + +# Given a char array representing tasks CPU need to do. +# It contains capital letters A to Z where different letters represent +# different tasks.Tasks could be done without original order. +# Each task could be done in one interval. +# For each interval, CPU could finish one task or just be idle. +# +# However, there is a non-negative cooling interval n that +# means between two same tasks, there must be at least n intervals that +# CPU are doing different tasks or just be idle. +# +# You need to return the least number of intervals the CPU +# will take to finish all the given tasks. +# +# Example 1: +# Input: tasks = ['A','A','A','B','B','B'], n = 2 +# Output: 8 +# Explanation: A -> B -> idle -> A -> B -> idle -> A -> B. +# Note: +# The number of tasks is in the range [1, 10000]. +# The integer n is in the range [0, 100]. + +import collections + + +class Solution(object): + def leastInterval(self, tasks, n): + """ + :type tasks: List[str] + :type n: int + :rtype: int + """ + count = collections.defaultdict(int) + max_count = 0 + for task in tasks: + count[task] += 1 + max_count = max(max_count, count[task]) + + result = (max_count-1) * (n+1) + for count in count.values(): + if count == max_count: + result += 1 + return max(result, len(tasks)) diff --git a/Python/teemo-attacking.py b/Python/teemo-attacking.py new file mode 100644 index 000000000..918242f76 --- /dev/null +++ b/Python/teemo-attacking.py @@ -0,0 +1,42 @@ +# Time: O(n) +# Space: O(1) + +# In LLP world, there is a hero called Teemo and his attacking can make his enemy Ashe be in poisoned condition. +# Now, given the Teemo's attacking ascending time series towards Ashe and +# the poisoning time duration per Teemo's attacking, you need to output the total time that Ashe is in poisoned condition. +# +# You may assume that Teemo attacks at the very beginning of a specific time point, +# and makes Ashe be in poisoned condition immediately. +# +# Example 1: +# Input: [1,4], 2 +# Output: 4 +# Explanation: At time point 1, Teemo starts attacking Ashe and makes Ashe be poisoned immediately. +# This poisoned status will last 2 seconds until the end of time point 2. +# And at time point 4, Teemo attacks Ashe again, and causes Ashe to be in poisoned status for another 2 seconds. +# So you finally need to output 4. +# Example 2: +# Input: [1,2], 2 +# Output: 3 +# Explanation: At time point 1, Teemo starts attacking Ashe and makes Ashe be poisoned. +# This poisoned status will last 2 seconds until the end of time point 2. +# However, at the beginning of time point 2, Teemo attacks Ashe again who is already in poisoned status. +# Since the poisoned status won't add up together, though the second poisoning attack will still work at time point 2, +# it will stop at the end of time point 3. +# So you finally need to output 3. +# Note: +# You may assume the length of given time series array won't exceed 10000. +# You may assume the numbers in the Teemo's attacking time series and his poisoning time +# duration per attacking are non-negative integers, which won't exceed 10,000,000. + +class Solution(object): + def findPoisonedDuration(self, timeSeries, duration): + """ + :type timeSeries: List[int] + :type duration: int + :rtype: int + """ + result = duration * len(timeSeries) + for i in xrange(1, len(timeSeries)): + result -= max(0, duration - (timeSeries[i] - timeSeries[i-1])) + return result diff --git a/Python/ternary-expression-parser.py b/Python/ternary-expression-parser.py new file mode 100644 index 000000000..3f6e9b14b --- /dev/null +++ b/Python/ternary-expression-parser.py @@ -0,0 +1,29 @@ +# Time: O(n) +# Space: O(1) + +class Solution(object): + def parseTernary(self, expression): + """ + :type expression: str + :rtype: str + """ + if not expression: + return "" + + stack = [] + for c in expression[::-1]: + if stack and stack[-1] == '?': + stack.pop() # pop '?' + first = stack.pop() + stack.pop() # pop ':' + second = stack.pop() + + if c == 'T': + stack.append(first) + else: + stack.append(second) + else: + stack.append(c) + + + return str(stack[-1]) diff --git a/Python/text-justification.py b/Python/text-justification.py index c1a18d1e9..bc9c9b4da 100644 --- a/Python/text-justification.py +++ b/Python/text-justification.py @@ -1,20 +1,25 @@ +from __future__ import print_function # Time: O(n) -# Space: O(1) +# Space: O(k), k is maxWidth. # -# Given an array of words and a length L, format the text such that each line has exactly L characters and is fully (left and right) justified. -# -# You should pack your words in a greedy approach; that is, pack as many words as you can in each line. Pad extra spaces ' ' +# Given an array of words and a length L, format the text such that +# each line has exactly L characters and is fully (left and right) justified. +# +# You should pack your words in a greedy approach; that is, pack +# as many words as you can in each line. Pad extra spaces ' ' # when necessary so that each line has exactly L characters. -# -# Extra spaces between words should be distributed as evenly as possible. -# If the number of spaces on a line do not divide evenly between words, the empty slots on the left will be assigned more spaces than the slots on the right. -# -# For the last line of text, it should be left justified and no extra space is inserted between words. -# +# +# Extra spaces between words should be distributed as evenly as possible. +# If the number of spaces on a line do not divide evenly between words, +# the empty slots on the left will be assigned more spaces than the slots on the right. +# +# For the last line of text, it should be left justified and no extra space +# is inserted between words. +# # For example, # words: ["This", "is", "an", "example", "of", "text", "justification."] # L: 16. -# +# # Return the formatted lines as: # [ # "This is an", @@ -23,52 +28,44 @@ # ] # Note: Each word is guaranteed not to exceed L in length. -class Solution: - # @param words, a list of strings - # @param L, an integer - # @return a list of strings - def fullJustify(self, words, L): - result = [] - - i = 0 - while i < len(words): - # count words in one line - size, begin = 0, i - while i < len(words): - if size == 0: - newsize = len(words[i]) - else: - newsize = size + len(words[i]) + 1 - if newsize <= L: - size = newsize - else: - break - i += 1 - - # count space number - spaceCount = L - size - if i - begin - 1 > 0 and i < len(words): - everyCount = spaceCount / (i - begin - 1) - spaceCount %= i - begin - 1 - else: - everyCount = 0 - - # add space - j = begin - while j < i: - if j == begin: - s = words[j] - else: - s += ' ' * (everyCount + 1) - if spaceCount > 0 and i < len(words): - s += ' ' - spaceCount -= 1 - s += words[j] - j += 1 - s += ' ' * spaceCount - result.append(s) - - return result +class Solution(object): + def fullJustify(self, words, maxWidth): + """ + :type words: List[str] + :type maxWidth: int + :rtype: List[str] + """ + def addSpaces(i, spaceCnt, maxWidth, is_last): + if i < spaceCnt: + # For the last line of text, it should be left justified, + # and no extra space is inserted between words. + return 1 if is_last else (maxWidth // spaceCnt) + int(i < maxWidth % spaceCnt) + return 0 + + def connect(words, maxWidth, begin, end, length, is_last): + s = [] # The extra space O(k) is spent here. + n = end - begin + for i in xrange(n): + s += words[begin + i], + s += ' ' * addSpaces(i, n - 1, maxWidth - length, is_last), + # For only one word in a line. + line = "".join(s) + if len(line) < maxWidth: + line += ' ' * (maxWidth - len(line)) + return line + + res = [] + begin, length = 0, 0 + for i in xrange(len(words)): + if length + len(words[i]) + (i - begin) > maxWidth: + res += connect(words, maxWidth, begin, i, length, False), + begin, length = i, 0 + length += len(words[i]) + + # Last line. + res += connect(words, maxWidth, begin, len(words), length, True), + return res + if __name__ == "__main__": - print Solution().fullJustify(["This", "is", "an", "example", "of", "text", "justification."], 16) + print(Solution().fullJustify(["This", "is", "an", "example", "of", "text", "justification."], 16)) diff --git a/Python/the-maze-ii.py b/Python/the-maze-ii.py new file mode 100644 index 000000000..e651891d9 --- /dev/null +++ b/Python/the-maze-ii.py @@ -0,0 +1,39 @@ +# Time: O(max(r, c) * wlogw) +# Space: O(w) + +import heapq + + +class Solution(object): + def shortestDistance(self, maze, start, destination): + """ + :type maze: List[List[int]] + :type start: List[int] + :type destination: List[int] + :rtype: int + """ + start, destination = tuple(start), tuple(destination) + + def neighbors(maze, node): + for dir in [(-1, 0), (0, 1), (0, -1), (1, 0)]: + cur_node, dist = list(node), 0 + while 0 <= cur_node[0]+dir[0] < len(maze) and \ + 0 <= cur_node[1]+dir[1] < len(maze[0]) and \ + not maze[cur_node[0]+dir[0]][cur_node[1]+dir[1]]: + cur_node[0] += dir[0] + cur_node[1] += dir[1] + dist += 1 + yield dist, tuple(cur_node) + + heap = [(0, start)] + visited = set() + while heap: + dist, node = heapq.heappop(heap) + if node in visited: continue + if node == destination: + return dist + visited.add(node) + for neighbor_dist, neighbor in neighbors(maze, node): + heapq.heappush(heap, (dist+neighbor_dist, neighbor)) + + return -1 diff --git a/Python/the-maze-iii.py b/Python/the-maze-iii.py new file mode 100644 index 000000000..ddcc087fe --- /dev/null +++ b/Python/the-maze-iii.py @@ -0,0 +1,41 @@ +# Time: O(max(r, c) * wlogw) +# Space: O(w^2) + +import heapq + + +class Solution(object): + def findShortestWay(self, maze, ball, hole): + """ + :type maze: List[List[int]] + :type ball: List[int] + :type hole: List[int] + :rtype: str + """ + ball, hole = tuple(ball), tuple(hole) + dirs = {'u' : (-1, 0), 'r' : (0, 1), 'l' : (0, -1), 'd': (1, 0)} + + def neighbors(maze, node): + for dir, vec in dirs.iteritems(): + cur_node, dist = list(node), 0 + while 0 <= cur_node[0]+vec[0] < len(maze) and \ + 0 <= cur_node[1]+vec[1] < len(maze[0]) and \ + not maze[cur_node[0]+vec[0]][cur_node[1]+vec[1]]: + cur_node[0] += vec[0] + cur_node[1] += vec[1] + dist += 1 + if tuple(cur_node) == hole: + break + yield tuple(cur_node), dir, dist + + heap = [(0, '', ball)] + visited = set() + while heap: + dist, path, node = heapq.heappop(heap) + if node in visited: continue + if node == hole: return path + visited.add(node) + for neighbor, dir, neighbor_dist in neighbors(maze, node): + heapq.heappush(heap, (dist+neighbor_dist, path+dir, neighbor)) + + return "impossible" diff --git a/Python/the-maze.py b/Python/the-maze.py new file mode 100644 index 000000000..0016d08c0 --- /dev/null +++ b/Python/the-maze.py @@ -0,0 +1,39 @@ +# Time: O(max(r, c) * w) +# Space: O(w) + +import collections + + +class Solution(object): + def hasPath(self, maze, start, destination): + """ + :type maze: List[List[int]] + :type start: List[int] + :type destination: List[int] + :rtype: bool + """ + start, destination = tuple(start), tuple(destination) + + def neighbors(maze, node): + for dir in [(-1, 0), (0, 1), (0, -1), (1, 0)]: + cur_node, dist = list(node), 0 + while 0 <= cur_node[0]+dir[0] < len(maze) and \ + 0 <= cur_node[1]+dir[1] < len(maze[0]) and \ + not maze[cur_node[0]+dir[0]][cur_node[1]+dir[1]]: + cur_node[0] += dir[0] + cur_node[1] += dir[1] + dist += 1 + yield dist, tuple(cur_node) + + queue = collections.deque([(0, start)]) + visited = set() + while queue: + dist, node = queue.popleft() + if node in visited: continue + if node == destination: + return True + visited.add(node) + for neighbor_dist, neighbor in neighbors(maze, node): + queue.append((dist+neighbor_dist, neighbor)) + + return False diff --git a/Python/the-skyline-problem.py b/Python/the-skyline-problem.py new file mode 100644 index 000000000..e032ac3e6 --- /dev/null +++ b/Python/the-skyline-problem.py @@ -0,0 +1,115 @@ +# Time: O(nlogn) +# Space: O(n) +# +# A city's skyline is the outer contour of the silhouette formed +# by all the buildings in that city when viewed from a distance. +# Now suppose you are given the locations and height of all the +# buildings as shown on a cityscape photo (Figure A), write a +# program to output the skyline formed by these buildings +# collectively (Figure B). +# +# The geometric information of each building is represented by a +# triplet of integers [Li, Ri, Hi], where Li and Ri are the x +# coordinates of the left and right edge of the ith building, +# respectively, and Hi is its height. It is guaranteed that 0 <= Li, +# Ri <= INT_MAX, 0 < Hi <= INT_MAX, and Ri - Li > 0. You may assume +# all buildings are perfect rectangles grounded on an absolutely +# flat surface at height 0. +# +# Notes: +# +# The number of buildings in any input list is guaranteed to be +# in the range [0, 10000]. +# The input list is already sorted in ascending order by the +# left x position Li. +# The output list must be sorted by the x position. +# There must be no consecutive horizontal lines of equal height +# in the output skyline. +# For instance, [...[2 3], [4 5], [7 5], [11 5], [12 7]...] is +# not acceptable; +# the three lines of height 5 should be merged into one +# in the final output as such: [...[2 3], [4 5], [12 7], ...] +# + +# Divide and conquer solution. +start, end, height = 0, 1, 2 +class Solution: + # @param {integer[][]} buildings + # @return {integer[][]} + def getSkyline(self, buildings): + intervals = self.ComputeSkylineInInterval(buildings, 0, len(buildings)) + + res = [] + last_end = -1 + for interval in intervals: + if last_end != -1 and last_end < interval[start]: + res.append([last_end, 0]) + res.append([interval[start], interval[height]]) + last_end = interval[end] + if last_end != -1: + res.append([last_end, 0]) + + return res + + # Divide and Conquer. + def ComputeSkylineInInterval(self, buildings, left_endpoint, right_endpoint): + if right_endpoint - left_endpoint <= 1: + return buildings[left_endpoint:right_endpoint] + mid = left_endpoint + ((right_endpoint - left_endpoint) / 2) + left_skyline = self.ComputeSkylineInInterval(buildings, left_endpoint, mid) + right_skyline = self.ComputeSkylineInInterval(buildings, mid, right_endpoint) + return self.MergeSkylines(left_skyline, right_skyline) + + # Merge Sort. + def MergeSkylines(self, left_skyline, right_skyline): + i, j = 0, 0 + merged = [] + + while i < len(left_skyline) and j < len(right_skyline): + if left_skyline[i][end] < right_skyline[j][start]: + merged.append(left_skyline[i]) + i += 1 + elif right_skyline[j][end] < left_skyline[i][start]: + merged.append(right_skyline[j]) + j += 1 + elif left_skyline[i][start] <= right_skyline[j][start]: + i, j = self.MergeIntersectSkylines(merged, left_skyline[i], i,\ + right_skyline[j], j) + else: # left_skyline[i][start] > right_skyline[j][start]. + j, i = self.MergeIntersectSkylines(merged, right_skyline[j], j, \ + left_skyline[i], i) + + # Insert the remaining skylines. + merged += left_skyline[i:] + merged += right_skyline[j:] + return merged + + # a[start] <= b[start] + def MergeIntersectSkylines(self, merged, a, a_idx, b, b_idx): + if a[end] <= b[end]: + if a[height] > b[height]: # |aaa| + if b[end] != a[end]: # |abb|b + b[start] = a[end] + merged.append(a) + a_idx += 1 + else: # aaa + b_idx += 1 # abb + elif a[height] == b[height]: # abb + b[start] = a[start] # abb + a_idx += 1 + else: # a[height] < b[height]. + if a[start] != b[start]: # bb + merged.append([a[start], b[start], a[height]]) # |a|bb + a_idx += 1 + else: # a[end] > b[end]. + if a[height] >= b[height]: # aaaa + b_idx += 1 # abba + else: + # |bb| + # |a||bb|a + if a[start] != b[start]: + merged.append([a[start], b[start], a[height]]) + a[start] = b[end] + merged.append(b) + b_idx += 1 + return a_idx, b_idx diff --git a/Python/third-maximum-number.py b/Python/third-maximum-number.py new file mode 100644 index 000000000..ec9e2152f --- /dev/null +++ b/Python/third-maximum-number.py @@ -0,0 +1,30 @@ +# Time: O(n) +# Space: O(1) + +# Given an array of integers, return the 3rd Maximum Number in this array, +# if it doesn't exist, return the Maximum Number. +# The time complexity must be O(n) or less. + +class Solution(object): + def thirdMax(self, nums): + """ + :type nums: List[int] + :rtype: int + """ + count = 0 + top = [float("-inf")] * 3 + for num in nums: + if num > top[0]: + top[0], top[1], top[2] = num, top[0], top[1] + count += 1 + elif num != top[0] and num > top[1]: + top[1], top[2] = num, top[1] + count += 1 + elif num != top[0] and num != top[1] and num >= top[2]: + top[2] = num + count += 1 + + if count < 3: + return top[0] + + return top[2] diff --git a/Python/to-lower-case.py b/Python/to-lower-case.py new file mode 100644 index 000000000..7e2cee950 --- /dev/null +++ b/Python/to-lower-case.py @@ -0,0 +1,14 @@ +# Time: O(n) +# Space: O(1) + +# Implement function ToLowerCase() that has a string parameter str, +# and returns the same string in lowercase. + +class Solution(object): + def toLowerCase(self, str): + """ + :type str: str + :rtype: str + """ + return "".join([chr(ord('a')+ord(c)-ord('A')) + if 'A' <= c <= 'Z' else c for c in str]) diff --git a/Python/toeplitz-matrix.py b/Python/toeplitz-matrix.py new file mode 100644 index 000000000..fbabbb154 --- /dev/null +++ b/Python/toeplitz-matrix.py @@ -0,0 +1,55 @@ +# Time: O(m * n) +# Space: O(1) + +# A matrix is Toeplitz if every diagonal from top-left to bottom-right has the same element. +# Now given an M x N matrix, return True if and only if the matrix is Toeplitz. +# +# Example 1: +# +# Input: matrix = [[1,2,3,4],[5,1,2,3],[9,5,1,2]] +# Output: True +# Explanation: +# 1234 +# 5123 +# 9512 +# +# In the above grid, the diagonals are +# "[9]", "[5, 5]", "[1, 1, 1]", "[2, 2, 2]", "[3, 3]", "[4]", +# and in each diagonal all elements are the same, so the answer is True. +# +# Example 2: +# +# Input: matrix = [[1,2],[2,2]] +# Output: False +# Explanation: +# The diagonal "[1, 2]" has different elements. +# +# Note: +# - matrix will be a 2D array of integers. +# - matrix will have a number of rows and columns in range [1, 20]. +# - matrix[i][j] will be integers in range [0, 99]. + +class Solution(object): + def isToeplitzMatrix(self, matrix): + """ + :type matrix: List[List[int]] + :rtype: bool + """ + return all(i == 0 or j == 0 or matrix[i-1][j-1] == val + for i, row in enumerate(matrix) + for j, val in enumerate(row)) + + +class Solution2(object): + def isToeplitzMatrix(self, matrix): + """ + :type matrix: List[List[int]] + :rtype: bool + """ + for row_index, row in enumerate(matrix): + for digit_index, digit in enumerate(row): + if not row_index or not digit_index: + continue + if matrix[row_index - 1][digit_index - 1] != digit: + return False + return True diff --git a/Python/top-k-frequent-elements.py b/Python/top-k-frequent-elements.py new file mode 100644 index 000000000..4789fa8f2 --- /dev/null +++ b/Python/top-k-frequent-elements.py @@ -0,0 +1,103 @@ +# Time: O(n) +# Space: O(n) + +# Given a non-empty array of integers, +# return the k most frequent elements. +# +# For example, +# Given [1,1,1,2,2,3] and k = 2, return [1,2]. +# +# Note: +# You may assume k is always valid, +# 1 <= k <= number of unique elements. +# Your algorithm's time complexity must be better +# than O(n log n), where n is the array's size. + +# Bucket Sort Solution + +import collections + +try: + xrange # Python 2 +except NameError: + xrange = range # Python 3 + + +class Solution(object): + def topKFrequent(self, nums, k): + """ + :type nums: List[int] + :type k: int + :rtype: List[int] + """ + counts = collections.Counter(nums) + buckets = [[] for _ in xrange(len(nums)+1)] + for i, count in counts.iteritems(): + buckets[count].append(i) + + result = [] + for i in reversed(xrange(len(buckets))): + for j in xrange(len(buckets[i])): + result.append(buckets[i][j]) + if len(result) == k: + return result + return result + + +# Time: O(n) ~ O(n^2), O(n) on average. +# Space: O(n) +# Quick Select Solution +from random import randint +class Solution2(object): + def topKFrequent(self, nums, k): + """ + :type nums: List[int] + :type k: int + :rtype: List[int] + """ + counts = collections.Counter(nums) + p = [] + for key, val in counts.iteritems(): + p.append((-val, key)) + self.kthElement(p, k) + + result = [] + for i in xrange(k): + result.append(p[i][1]) + return result + + def kthElement(self, nums, k): + def PartitionAroundPivot(left, right, pivot_idx, nums): + pivot_value = nums[pivot_idx] + new_pivot_idx = left + nums[pivot_idx], nums[right] = nums[right], nums[pivot_idx] + for i in xrange(left, right): + if nums[i] < pivot_value: + nums[i], nums[new_pivot_idx] = nums[new_pivot_idx], nums[i] + new_pivot_idx += 1 + + nums[right], nums[new_pivot_idx] = nums[new_pivot_idx], nums[right] + return new_pivot_idx + + left, right = 0, len(nums) - 1 + while left <= right: + pivot_idx = randint(left, right) + new_pivot_idx = PartitionAroundPivot(left, right, pivot_idx, nums) + if new_pivot_idx == k - 1: + return + elif new_pivot_idx > k - 1: + right = new_pivot_idx - 1 + else: # new_pivot_idx < k - 1. + left = new_pivot_idx + 1 + + +# Time: O(nlogk) +# Space: O(n) +class Solution3(object): + def topKFrequent(self, nums, k): + """ + :type nums: List[int] + :type k: int + :rtype: List[int] + """ + return [key for key, _ in collections.Counter(nums).most_common(k)] diff --git a/Python/top-k-frequent-words.py b/Python/top-k-frequent-words.py new file mode 100644 index 000000000..cf1ea8b3d --- /dev/null +++ b/Python/top-k-frequent-words.py @@ -0,0 +1,132 @@ +# Time: O(n + klogk) on average +# Space: O(n) + +# Given a non-empty list of words, return the k most frequent elements. +# +# Your answer should be sorted by frequency from highest to lowest. +# If two words have the same frequency, then the word with the lower alphabetical order comes first. +# +# Example 1: +# Input: ["i", "love", "leetcode", "i", "love", "coding"], k = 2 +# Output: ["i", "love"] +# Explanation: "i" and "love" are the two most frequent words. +# Note that "i" comes before "love" due to a lower alphabetical order. +# Example 2: +# Input: ["the", "day", "is", "sunny", "the", "the", "the", "sunny", "is", "is"], k = 4 +# Output: ["the", "is", "sunny", "day"] +# Explanation: "the", "is", "sunny" and "day" are the four most frequent words, +# with the number of occurrence being 4, 3, 2 and 1 respectively. +# Note: +# You may assume k is always valid, 1 ≤ k ≤ number of unique elements. +# Input words contain only lowercase letters. +# +# Follow up: +# Try to solve it in O(n log k) time and O(n) extra space. +# Can you solve it in O(n) time with only O(k) extra space? + +# Quick Select Solution + +import collections +import heapq +from random import randint + + +class Solution(object): + def topKFrequent(self, words, k): + """ + :type words: List[str] + :type k: int + :rtype: List[str] + """ + counts = collections.Counter(words) + p = [] + for key, val in counts.iteritems(): + p.append((-val, key)) + self.kthElement(p, k) + + result = [] + sorted_p = sorted(p[:k]) + for i in xrange(k): + result.append(sorted_p[i][1]) + return result + + def kthElement(self, nums, k): # O(n) on average + def PartitionAroundPivot(left, right, pivot_idx, nums): + pivot_value = nums[pivot_idx] + new_pivot_idx = left + nums[pivot_idx], nums[right] = nums[right], nums[pivot_idx] + for i in xrange(left, right): + if nums[i] < pivot_value: + nums[i], nums[new_pivot_idx] = nums[new_pivot_idx], nums[i] + new_pivot_idx += 1 + + nums[right], nums[new_pivot_idx] = nums[new_pivot_idx], nums[right] + return new_pivot_idx + + left, right = 0, len(nums) - 1 + while left <= right: + pivot_idx = randint(left, right) + new_pivot_idx = PartitionAroundPivot(left, right, pivot_idx, nums) + if new_pivot_idx == k - 1: + return + elif new_pivot_idx > k - 1: + right = new_pivot_idx - 1 + else: # new_pivot_idx < k - 1. + left = new_pivot_idx + 1 + + +# Time: O(nlogk) +# Space: O(n) +# Heap Solution +class Solution2(object): + def topKFrequent(self, words, k): + """ + :type words: List[str] + :type k: int + :rtype: List[str] + """ + class MinHeapObj(object): + def __init__(self,val): + self.val = val + def __lt__(self,other): + return self.val[1] > other.val[1] if self.val[0] == other.val[0] else \ + self.val < other.val + def __eq__(self,other): + return self.val == other.val + def __str__(self): + return str(self.val) + + counts = collections.Counter(words) + min_heap = [] + for word, count in counts.iteritems(): + heapq.heappush(min_heap, MinHeapObj((count, word))) + if len(min_heap) == k+1: + heapq.heappop(min_heap) + result = [] + while min_heap: + result.append(heapq.heappop(min_heap).val[1]) + return result[::-1] + + +# Time: O(n + klogk) ~ O(n + nlogn) +# Space: O(n) +# Bucket Sort Solution +class Solution3(object): + def topKFrequent(self, words, k): + """ + :type words: List[str] + :type k: int + :rtype: List[str] + """ + counts = collections.Counter(words) + buckets = [[] for _ in xrange(len(words)+1)] + for word, count in counts.iteritems(): + buckets[count].append(word) + pairs = [] + for i in reversed(xrange(len(words))): + for word in buckets[i]: + pairs.append((-i, word)) + if len(pairs) >= k: + break + pairs.sort() + return [pair[1] for pair in pairs[:k]] diff --git a/Python/total-hamming-distance.py b/Python/total-hamming-distance.py new file mode 100644 index 000000000..411be8f44 --- /dev/null +++ b/Python/total-hamming-distance.py @@ -0,0 +1,34 @@ +# Time: O(n) +# Space: O(1) + +# The Hamming distance between two integers is the number of positions +# at which the corresponding bits are different. +# +# Now your job is to find the total Hamming distance between all pairs of the given numbers. +# +# Example: +# Input: 4, 14, 2 +# +# Output: 6 +# +# Explanation: In binary representation, the 4 is 0100, 14 is 1110, and 2 is 0010 (just +# showing the four bits relevant in this case). So the answer will be: +# HammingDistance(4, 14) + HammingDistance(4, 2) + HammingDistance(14, 2) = 2 + 2 + 2 = 6. +# Note: +# Elements of the given array are in the range of 0 to 10^9 +# Length of the array will not exceed 10^4. + +class Solution(object): + def totalHammingDistance(self, nums): + """ + :type nums: List[int] + :rtype: int + """ + result = 0 + for i in xrange(32): + counts = [0] * 2 + for num in nums: + counts[(num >> i) & 1] += 1 + result += counts[0] * counts[1] + return result + diff --git a/Python/transform-to-chessboard.py b/Python/transform-to-chessboard.py new file mode 100644 index 000000000..236bbe106 --- /dev/null +++ b/Python/transform-to-chessboard.py @@ -0,0 +1,66 @@ +# Time: O(n^2) +# Space: O(n^2), used by Counter, this could be reduced to O(n) by skipping invalid input + +# An N x N board contains only 0s and 1s. In each move, you can swap any 2 rows with each other, +# or any 2 columns with each other. +# +# What is the minimum number of moves to transform the board into a "chessboard" - +# a board where no 0s and no 1s are 4-directionally adjacent? If the task is impossible, return -1. +# +# Examples: +# Input: board = [[0,1,1,0],[0,1,1,0],[1,0,0,1],[1,0,0,1]] +# Output: 2 +# Explanation: +# One potential sequence of moves is shown below, from left to right: +# +# 0110 1010 1010 +# 0110 --> 1010 --> 0101 +# 1001 0101 1010 +# 1001 0101 0101 +# +# The first move swaps the first and second column. +# The second move swaps the second and third row. +# +# Input: board = [[0, 1], [1, 0]] +# Output: 0 +# Explanation: +# Also note that the board with 0 in the top left corner, +# 01 +# 10 +# +# is also a valid chessboard. +# +# Input: board = [[1, 0], [1, 0]] +# Output: -1 +# Explanation: +# No matter what sequence of moves you make, you cannot end with a valid chessboard. +# +# Note: +# - board will have the same number of rows and columns, a number in the range [2, 30]. +# - board[i][j] will be only 0s or 1s. + +import collections +import itertools + + +class Solution(object): + def movesToChessboard(self, board): + """ + :type board: List[List[int]] + :rtype: int + """ + N = len(board) + result = 0 + for count in (collections.Counter(map(tuple, board)), \ + collections.Counter(itertools.izip(*board))): + if len(count) != 2 or \ + sorted(count.values()) != [N/2, (N+1)/2]: + return -1 + + seq1, seq2 = count + if any(x == y for x, y in itertools.izip(seq1, seq2)): + return -1 + begins = [int(seq1.count(1) * 2 > N)] if N%2 else [0, 1] + result += min(sum(int(i%2 != v) for i, v in enumerate(seq1, begin)) \ + for begin in begins) / 2 + return result diff --git a/Python/transpose-matrix.py b/Python/transpose-matrix.py new file mode 100644 index 000000000..3d00e1fde --- /dev/null +++ b/Python/transpose-matrix.py @@ -0,0 +1,49 @@ +# Time: O(r * c) +# Space: O(1) + +# Given a matrix A, return the transpose of A. +# +# The transpose of a matrix is the matrix flipped over it's main diagonal, +# switching the row and column indices of the matrix. +# +# Example 1: +# +# Input: [[1,2,3],[4,5,6],[7,8,9]] +# Output: [[1,4,7],[2,5,8],[3,6,9]] +# Example 2: +# +# Input: [[1,2,3],[4,5,6]] +# Output: [[1,4],[2,5],[3,6]] +# +# Note: +# - 1 <= A.length <= 1000 +# - 1 <= A[0].length <= 1000 + +try: + xrange # Python 2 +except NameError: + xrange = range # Python 3 + + +class Solution(object): + def transpose(self, A): + """ + :type A: List[List[int]] + :rtype: List[List[int]] + """ + result = [[None] * len(A) for _ in xrange(len(A[0]))] + for r, row in enumerate(A): + for c, val in enumerate(row): + result[c][r] = val + return result + + +# Time: O(r * c) +# Space: O(1) +class Solution2(object): + def transpose(self, A): + """ + :type A: List[List[int]] + :rtype: List[List[int]] + """ + return zip(*A) diff --git a/Python/trapping-rain-water-ii.py b/Python/trapping-rain-water-ii.py new file mode 100644 index 000000000..8408de5f1 --- /dev/null +++ b/Python/trapping-rain-water-ii.py @@ -0,0 +1,60 @@ +# Time: O(m * n * log(m + n)) ~ O(m * n * log(m * n)) +# Space: O(m * n) + +# Given an m x n matrix of positive integers representing the height of each unit cell in +# a 2D elevation map, compute the volume of water it is able to trap after raining. +# +# Note: +# Both m and n are less than 110. The height of each unit cell is greater than 0 and is less than 20,000. +# +# Example: +# +# Given the following 3x6 height map: +# [ +# [1,4,3,1,3,2], +# [3,2,1,3,2,4], +# [2,3,3,2,3,1] +# ] +# +# Return 4. + +from heapq import heappush, heappop + +class Solution(object): + def trapRainWater(self, heightMap): + """ + :type heightMap: List[List[int]] + :rtype: int + """ + m = len(heightMap) + if not m: + return 0 + n = len(heightMap[0]) + if not n: + return 0 + + is_visited = [[False for i in xrange(n)] for j in xrange(m)] + + heap = [] + for i in xrange(m): + heappush(heap, [heightMap[i][0], i, 0]) + is_visited[i][0] = True + heappush(heap, [heightMap[i][n-1], i, n-1]) + is_visited[i][n-1] = True + for j in xrange(n): + heappush(heap, [heightMap[0][j], 0, j]) + is_visited[0][j] = True + heappush(heap, [heightMap[m-1][j], m-1, j]) + is_visited[m-1][j] = True + + trap = 0 + while heap: + height, i, j = heappop(heap) + for (dx, dy) in [(1,0), (-1,0), (0,1), (0,-1)]: + x, y = i+dx, j+dy + if 0 <= x < m and 0 <= y < n and not is_visited[x][y]: + trap += max(0, height - heightMap[x][y]) + heappush(heap, [max(height, heightMap[x][y]), x, y]) + is_visited[x][y] = True + + return trap diff --git a/Python/trapping-rain-water.py b/Python/trapping-rain-water.py index c2d030382..09b40444e 100644 --- a/Python/trapping-rain-water.py +++ b/Python/trapping-rain-water.py @@ -1,13 +1,14 @@ +from __future__ import print_function # Time: O(n) # Space: O(1) # # Given n non-negative integers representing an elevation map where the width of each bar is 1, # compute how much water it is able to trap after raining. -# -# For example, +# +# For example, # Given [0,1,0,2,1,0,1,3,2,1,2,1], return 6. -# -# The above elevation map is represented by array [0,1,0,2,1,0,1,3,2,1,2,1]. +# +# The above elevation map is represented by array [0,1,0,2,1,0,1,3,2,1,2,1]. # In this case, 6 units of rain water (blue section) are being trapped. # @@ -20,21 +21,21 @@ def trap(self, A): for i in xrange(len(A)): if A[top] < A[i]: top = i - + second_top = 0 for i in xrange(top): if A[second_top] < A[i]: second_top = i result += A[second_top] - A[i] - + second_top = len(A) - 1 for i in reversed(xrange(top, len(A))): if A[second_top] < A[i]: second_top = i result += A[second_top] - A[i] - + return result - + # Time: O(n) # Space: O(n) class Solution2: @@ -43,20 +44,20 @@ class Solution2: def trap(self, A): result = 0 stack = [] - + for i in xrange(len(A)): mid_height = 0 while stack: [pos, height] = stack.pop() result += (min(height, A[i]) - mid_height) * (i - pos - 1) mid_height = height - + if A[i] < height: stack.append([pos, height]) break stack.append([i, A[i]]) - + return result - + if __name__ == "__main__": - print Solution().trap([0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1]) + print(Solution().trap([0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1])) diff --git a/Python/triangle.py b/Python/triangle.py index 40cf9c627..836c42f4d 100644 --- a/Python/triangle.py +++ b/Python/triangle.py @@ -1,8 +1,10 @@ +from __future__ import print_function +from functools import reduce # Time: O(m * n) # Space: O(n) # # Given a triangle, find the minimum path sum from top to bottom. Each step you may move to adjacent numbers on the row below. -# +# # For example, given the following triangle # [ # [2], @@ -11,7 +13,7 @@ # [4,1,8,3] # ] # The minimum path sum from top to bottom is 11 (i.e., 2 + 3 + 5 + 1 = 11). -# +# # Note: # Bonus point if you are able to do this using only O(n) extra space, where n is the total number of rows in the triangle. # @@ -20,9 +22,9 @@ class Solution: # @param triangle, a list of lists of integers # @return an integer def minimumTotal(self, triangle): - if len(triangle) == 0: + if not triangle: return 0 - + cur = triangle[0] + [float("inf")] for i in xrange(1, len(triangle)): next = [] @@ -30,9 +32,9 @@ def minimumTotal(self, triangle): for j in xrange(1, i + 1): next.append(triangle[i][j] + min(cur[j - 1], cur[j])) cur = next + [float("inf")] - + return reduce(min, cur) if __name__ == "__main__": - print Solution().minimumTotal([[-1], [2, 3], [1, -1, -3]]) - \ No newline at end of file + print(Solution().minimumTotal([[-1], [2, 3], [1, -1, -3]])) + diff --git a/Python/trim-a-binary-search-tree.py b/Python/trim-a-binary-search-tree.py new file mode 100644 index 000000000..e0f1c4c07 --- /dev/null +++ b/Python/trim-a-binary-search-tree.py @@ -0,0 +1,65 @@ +# Time: O(n) +# Space: O(h) + +# Given a binary search tree and the lowest and highest boundaries as L and R, +# trim the tree so that all its elements lies in [L, R] (R >= L). +# You might need to change the root of the tree, so the result should +# return the new root of the trimmed binary search tree. +# +# Example 1: +# Input: +# 1 +# / \ +# 0 2 +# +# L = 1 +# R = 2 +# +# Output: +# 1 +# \ +# 2 +# Example 2: +# Input: +# 3 +# / \ +# 0 4 +# \ +# 2 +# / +# 1 +# +# L = 1 +# R = 3 +# +# Output: +# 3 +# / +# 2 +# / +# 1 + +# Definition for a binary tree node. +# class TreeNode(object): +# def __init__(self, x): +# self.val = x +# self.left = None +# self.right = None + +class Solution(object): + def trimBST(self, root, L, R): + """ + :type root: TreeNode + :type L: int + :type R: int + :rtype: TreeNode + """ + if not root: + return None + if root.val < L: + return self.trimBST(root.right, L, R) + if root.val > R: + return self.trimBST(root.left, L, R) + root.left, root.right = self.trimBST(root.left, L, R), self.trimBST(root.right, L, R) + return root + diff --git a/Python/two-sum-ii-input-array-is-sorted.py b/Python/two-sum-ii-input-array-is-sorted.py index 247af6397..356476b00 100644 --- a/Python/two-sum-ii-input-array-is-sorted.py +++ b/Python/two-sum-ii-input-array-is-sorted.py @@ -1,15 +1,16 @@ +from __future__ import print_function # Time: O(n) # Space: O(1) # -# Given an array of integers that is already sorted in ascending order, +# Given an array of integers that is already sorted in ascending order, # find two numbers such that they add up to a specific target number. -# -# The function twoSum should return indices of the two numbers such that -# they add up to the target, where index1 must be less than index2. +# +# The function twoSum should return indices of the two numbers such that +# they add up to the target, where index1 must be less than index2. # Please note that your returned answers (both index1 and index2) are not zero-based. -# +# # You may assume that each input would have exactly one solution. -# +# # Input: numbers={2, 7, 11, 15}, target=9 # Output: index1=1, index2=2 # @@ -17,7 +18,7 @@ class Solution: def twoSum(self, nums, target): start, end = 0, len(nums) - 1 - + while start != end: sum = nums[start] + nums[end] if sum > target: @@ -28,4 +29,4 @@ def twoSum(self, nums, target): return [start + 1, end + 1] if __name__ == "__main__": - print Solution().twoSum([2, 7, 11, 15], 9) + print(Solution().twoSum([2, 7, 11, 15], 9)) diff --git a/Python/two-sum-iii-data-structure-design.py b/Python/two-sum-iii-data-structure-design.py index 0bcaf917e..55d1ae7aa 100644 --- a/Python/two-sum-iii-data-structure-design.py +++ b/Python/two-sum-iii-data-structure-design.py @@ -1,47 +1,56 @@ +from __future__ import print_function # Time: O(n) # Space: O(n) -# + # Design and implement a TwoSum class. It should support the following operations: add and find. -# +# # add - Add the number to an internal data structure. # find - Find if there exists any pair of numbers which sum is equal to the value. -# +# # For example, # add(1); add(3); add(5); # find(4) -> true # find(7) -> false -# -class TwoSum: +from collections import defaultdict + +class TwoSum(object): - # initialize your data structure here def __init__(self): - self.lookup = {} - + """ + initialize your data structure here + """ + self.lookup = defaultdict(int) + + - # @return nothing def add(self, number): - if number in self.lookup: - self.lookup[number] += 1 - else: - self.lookup[number] = 1 + """ + Add the number to an internal data structure. + :rtype: nothing + """ + self.lookup[number] += 1 + - # @param value, an integer - # @return a Boolean def find(self, value): + """ + Find if there exists any pair of numbers which sum is equal to the value. + :type value: int + :rtype: bool + """ for key in self.lookup: num = value - key if num in self.lookup and (num != key or self.lookup[key] > 1): return True return False + if __name__ == "__main__": Sol = TwoSum() - + for i in (1, 3, 5): Sol.add(i) - + for i in (4, 7): - print Sol.find(i) - - \ No newline at end of file + print(Sol.find(i)) + diff --git a/Python/two-sum-iv-input-is-a-bst.py b/Python/two-sum-iv-input-is-a-bst.py new file mode 100644 index 000000000..47a3b44d7 --- /dev/null +++ b/Python/two-sum-iv-input-is-a-bst.py @@ -0,0 +1,77 @@ +# Time: O(n) +# Space: O(h) + +# Given a Binary Search Tree and a target number, +# return true if there exist two elements in the BST such that their sum is equal to the given target. +# +# Example 1: +# Input: +# 5 +# / \ +# 3 6 +# / \ \ +# 2 4 7 +# +# Target = 9 +# +# Output: True +# Example 2: +# Input: +# 5 +# / \ +# 3 6 +# / \ \ +# 2 4 7 +# +# Target = 28 +# +# Output: False + +# Definition for a binary tree node. +# class TreeNode(object): +# def __init__(self, x): +# self.val = x +# self.left = None +# self.right = None + +class Solution(object): + def findTarget(self, root, k): + """ + :type root: TreeNode + :type k: int + :rtype: bool + """ + class BSTIterator(object): + def __init__(self, root, forward): + self.__node = root + self.__forward = forward + self.__s = [] + self.__cur = None + self.next() + + def val(self): + return self.__cur + + def next(self): + while self.__node or self.__s: + if self.__node: + self.__s.append(self.__node) + self.__node = self.__node.left if self.__forward else self.__node.right + else: + self.__node = self.__s.pop() + self.__cur = self.__node.val + self.__node = self.__node.right if self.__forward else self.__node.left + break + + + if not root: + return False + left, right = BSTIterator(root, True), BSTIterator(root, False) + while left.val() < right.val(): + if left.val() + right.val() == k: + return True + elif left.val() + right.val() < k: + left.next() + else: + right.next() + return False diff --git a/Python/two-sum.py b/Python/two-sum.py index e89be65fe..0e17bbc0b 100644 --- a/Python/two-sum.py +++ b/Python/two-sum.py @@ -1,25 +1,45 @@ # Time: O(n) # Space: O(n) + +# Given an array of integers, return indices of the two numbers +# such that they add up to a specific target. # -# Given an array of integers, find two numbers such that -# they add up to a specific target number. -# The function twoSum should return indices of the two numbers such that -# they add up to the target, -# where index1 must be less than index2. Please note that -# your returned answers (both index1 and index2) are not zero-based. # You may assume that each input would have exactly one solution. # -# Input: numbers={2, 7, 11, 15}, target=9 -# Output: index1=1, index2=2 +# Example: +# Given nums = [2, 7, 11, 15], target = 9, # +# Because nums[0] + nums[1] = 2 + 7 = 9, +# return [0, 1]. + -class Solution: +class Solution(object): def twoSum(self, nums, target): + """ + :type nums: List[int] + :type target: int + :rtype: List[int] + """ lookup = {} for i, num in enumerate(nums): if target - num in lookup: - return (lookup[target - num] + 1, i + 1) + return [lookup[target - num], i] lookup[num] = i + def twoSum2(self, nums, target): + """ + :type nums: List[int] + :type target: int + :rtype: List[int] + """ + for i in nums: + j = target - i + tmp_nums_start_index = nums.index(i) + 1 + tmp_nums = nums[tmp_nums_start_index:] + if j in tmp_nums: + return [nums.index(i), tmp_nums_start_index + tmp_nums.index(j)] + + if __name__ == '__main__': - print "index1=%d, index2=%d" % Solution().twoSum((2, 7, 11, 15), 9) \ No newline at end of file + print(Solution().twoSum((2, 7, 11, 15), 9)) + print(Solution().twoSum2((2, 7, 11, 15), 9)) diff --git a/Python/ugly-number-ii.py b/Python/ugly-number-ii.py new file mode 100644 index 000000000..f2eff1e9b --- /dev/null +++ b/Python/ugly-number-ii.py @@ -0,0 +1,73 @@ +# Time: O(n) +# Space: O(1) +# +# Write a program to find the n-th ugly number. +# +# Ugly numbers are positive numbers whose prime factors +# only include 2, 3, 5. For example, +# 1, 2, 3, 4, 5, 6, 8, 9, 10, 12 is the sequence of the +# first 10 ugly numbers. +# +# Note that 1 is typically treated as an ugly number. +# +# Hint: +# +# The naive approach is to call isUgly for every number +# until you reach the nth one. Most numbers are not ugly. +# Try to focus your effort on generating only the ugly ones. +# + +import heapq + +class Solution: + # @param {integer} n + # @return {integer} + def nthUglyNumber(self, n): + ugly_number = 0 + + heap = [] + heapq.heappush(heap, 1) + for _ in xrange(n): + ugly_number = heapq.heappop(heap) + if ugly_number % 2 == 0: + heapq.heappush(heap, ugly_number * 2) + elif ugly_number % 3 == 0: + heapq.heappush(heap, ugly_number * 2) + heapq.heappush(heap, ugly_number * 3) + else: + heapq.heappush(heap, ugly_number * 2) + heapq.heappush(heap, ugly_number * 3) + heapq.heappush(heap, ugly_number * 5) + + return ugly_number + + def nthUglyNumber2(self, n): + ugly = [1] + i2 = i3 = i5 = 0 + while len(ugly) < n: + while ugly[i2] * 2 <= ugly[-1]: i2 += 1 + while ugly[i3] * 3 <= ugly[-1]: i3 += 1 + while ugly[i5] * 5 <= ugly[-1]: i5 += 1 + ugly.append(min(ugly[i2] * 2, ugly[i3] * 3, ugly[i5] * 5)) + return ugly[-1] + + def nthUglyNumber3(self, n): + q2, q3, q5 = [2], [3], [5] + ugly = 1 + for u in heapq.merge(q2, q3, q5): + if n == 1: + return ugly + if u > ugly: + ugly = u + n -= 1 + q2 += 2 * u, + q3 += 3 * u, + q5 += 5 * u, + + +class Solution2: + ugly = sorted(2**a * 3**b * 5**c + for a in range(32) for b in range(20) for c in range(14)) + + def nthUglyNumber(self, n): + return self.ugly[n-1] diff --git a/Python/ugly-number.py b/Python/ugly-number.py new file mode 100644 index 000000000..c4185bda9 --- /dev/null +++ b/Python/ugly-number.py @@ -0,0 +1,21 @@ +# Time: O(logn) = O(1) +# Space: O(1) +# +# Write a program to check whether a given number is an ugly number. +# +# Ugly numbers are positive numbers whose prime factors only include +# 2, 3, 5. For example, 6, 8 are ugly while 14 is not ugly since it +# includes another prime factor 7. +# +# Note that 1 is typically treated as an ugly number. +# +class Solution: + # @param {integer} num + # @return {boolean} + def isUgly(self, num): + if num == 0: + return False + for i in [2, 3, 5]: + while num % i == 0: + num /= i + return num == 1 diff --git a/Python/unique-binary-search-trees-ii.py b/Python/unique-binary-search-trees-ii.py index 1c7fee7ff..ddda4ba7f 100644 --- a/Python/unique-binary-search-trees-ii.py +++ b/Python/unique-binary-search-trees-ii.py @@ -1,11 +1,12 @@ +from __future__ import print_function # Time: O(4^n / n^(3/2)) ~= Catalan numbers # Space: O(4^n / n^(3/2)) ~= Catalan numbers # # Given n, generate all structurally unique BST's (binary search trees) that store values 1...n. -# +# # For example, # Given n = 3, your program should return all 5 unique BST's shown below. -# +# # 1 3 3 2 1 # \ / / / \ \ # 3 2 1 1 3 2 @@ -19,7 +20,7 @@ def __init__(self, x): self.val = x self.left = None self.right = None - + def __repr__(self): if self: serial = [] @@ -27,21 +28,21 @@ def __repr__(self): while queue: cur = queue[0] - + if cur: serial.append(cur.val) queue.append(cur.left) queue.append(cur.right) else: serial.append("#") - + queue = queue[1:] - + while serial[-1] == "#": serial.pop() - + return repr(serial) - + else: return None @@ -49,7 +50,7 @@ class Solution: # @return a list of tree node def generateTrees(self, n): return self.generateTreesRecu(1, n) - + def generateTreesRecu(self, low, high): result = [] if low > high: @@ -66,4 +67,4 @@ def generateTreesRecu(self, low, high): return result if __name__ == "__main__": - print Solution().generateTrees(3) \ No newline at end of file + print(Solution().generateTrees(3)) \ No newline at end of file diff --git a/Python/unique-binary-search-trees.py b/Python/unique-binary-search-trees.py index cac4cb41d..bad28ae88 100644 --- a/Python/unique-binary-search-trees.py +++ b/Python/unique-binary-search-trees.py @@ -1,11 +1,12 @@ -# Time: O(n^2) -# Space: O(n) +from __future__ import print_function +# Time: O(n) +# Space: O(1) # # Given n, how many structurally unique BST's (binary search trees) that store values 1...n? -# +# # For example, # Given n = 3, there are a total of 5 unique BST's. -# +# # 1 3 3 2 1 # \ / / / \ \ # 3 2 1 1 3 2 @@ -13,7 +14,29 @@ # 2 1 2 3 # -class Solution: +# Math solution. +class Solution(object): + def numTrees(self, n): + """ + :type n: int + :rtype: int + """ + if n == 0: + return 1 + + def combination(n, k): + count = 1 + # C(n, k) = (n) / 1 * (n - 1) / 2 ... * (n - k + 1) / k + for i in xrange(1, k + 1): + count = count * (n - i + 1) / i; + return count + + return combination(2 * n, n) - combination(2 * n, n - 1) + +# Time: O(n^2) +# Space: O(n) +# DP solution. +class Solution2: # @return an integer def numTrees(self, n): counts = [1, 1] @@ -23,6 +46,6 @@ def numTrees(self, n): count += counts[j] * counts[i - j - 1] counts.append(count) return counts[-1] - + if __name__ == "__main__": - print Solution().numTrees(3) + print(Solution().numTrees(3)) diff --git a/Python/unique-letter-string.py b/Python/unique-letter-string.py new file mode 100644 index 000000000..55dd8c180 --- /dev/null +++ b/Python/unique-letter-string.py @@ -0,0 +1,55 @@ +# Time: O(n) +# Space: O(1) + +# A character is unique in string S if it occurs exactly once in it. +# +# For example, in string S = "LETTER", +# the only unique characters are "L" and "R". +# +# Let's define UNIQ(S) as the number of unique characters in string S. +# +# For example, UNIQ("LETTER") = 2. +# +# Given a string S, calculate the sum of UNIQ(substring) over +# all non-empty substrings of S. +# +# If there are two or more equal substrings at different positions in S, +# we consider them different. +# +# Since the answer can be very large, retrun the answer modulo 10 ^ 9 + 7. +# +# Example 1: +# +# Input: "ABC" +# Output: 10 +# Explanation: All possible substrings are: "A","B","C","AB","BC" and "ABC". +# Evey substring is composed with only unique letters. +# Sum of lengths of all substring is 1 + 1 + 1 + 2 + 2 + 3 = 10 +# Example 2: +# +# Input: "ABA" +# Output: 8 +# Explanation: The same as example 1, except uni("ABA") = 1. +# +# Note: 0 <= S.length <= 10000. + +import string + + +class Solution(object): + def uniqueLetterString(self, S): + """ + :type S: str + :rtype: int + """ + M = 10**9 + 7 + index = {c: [-1, -1] for c in string.ascii_uppercase} + result = 0 + for i, c in enumerate(S): + k, j = index[c] + result += (i-j) * (j-k) + index[c] = [j, i] + for c in index: + k, j = index[c] + result += (len(S)-j) * (j-k) + return result % M diff --git a/Python/unique-morse-code-words.py b/Python/unique-morse-code-words.py new file mode 100644 index 000000000..8729470dd --- /dev/null +++ b/Python/unique-morse-code-words.py @@ -0,0 +1,51 @@ +# Time: O(n), n is the sume of all word lengths +# Space: O(n) + +# International Morse Code defines a standard encoding where each letter +# is mapped to a series of dots and dashes, as follows: "a" maps to ".-", "b" maps +# to "-...", "c" maps to "-.-.", and so on. +# +# For convenience, the full table for the 26 letters of the English alphabet is given below: +# +# [".-","-...","-.-.","-..",".","..-.","--.", +# "....","..",".---","-.-",".-..","--","-.", +# "---",".--.","--.-",".-.","...","-","..-", +# "...-",".--","-..-","-.--","--.."] +# Now, given a list of words, each word can be written as a concatenation of +# the Morse code of each letter. For example, "cab" can be written as "-.-.-....-", +# (which is the concatenation "-.-." + "-..." + ".-"). +# We'll call such a concatenation, the transformation of a word. +# +# Return the number of different transformations among all words we have. +# +# Example: +# Input: words = ["gin", "zen", "gig", "msg"] +# Output: 2 +# Explanation: +# The transformation of each word is: +# "gin" -> "--...-." +# "zen" -> "--...-." +# "gig" -> "--...--." +# "msg" -> "--...--." +# +# There are 2 different transformations, "--...-." and "--...--.". +# +# Note: +# - The length of words will be at most 100. +# - Each words[i] will have length in range [1, 12]. +# - words[i] will only consist of lowercase letters. + +class Solution(object): + def uniqueMorseRepresentations(self, words): + """ + :type words: List[str] + :rtype: int + """ + MORSE = [".-", "-...", "-.-.", "-..", ".", "..-.", "--.", + "....", "..", ".---", "-.-", ".-..", "--", "-.", + "---", ".--.", "--.-", ".-.", "...", "-", "..-", + "...-", ".--", "-..-", "-.--", "--.."] + + lookup = {"".join(MORSE[ord(c) - ord('a')] for c in word) \ + for word in words} + return len(lookup) diff --git a/Python/unique-paths-ii.py b/Python/unique-paths-ii.py index 8848514ca..4ed5a8108 100644 --- a/Python/unique-paths-ii.py +++ b/Python/unique-paths-ii.py @@ -1,22 +1,23 @@ +from __future__ import print_function # Time: O(m * n) # Space: O(m + n) # # Follow up for "Unique Paths": -# +# # Now consider if some obstacles are added to the grids. How many unique paths would there be? -# +# # An obstacle and empty space is marked as 1 and 0 respectively in the grid. -# +# # For example, # There is one obstacle in the middle of a 3x3 grid as illustrated below. -# +# # [ # [0,0,0], # [0,1,0], # [0,0,0] # ] # The total number of unique paths is 2. -# +# # Note: m and n will be at most 100. # @@ -24,29 +25,23 @@ class Solution: # @param obstacleGrid, a list of lists of integers # @return an integer def uniquePathsWithObstacles(self, obstacleGrid): + """ + :type obstacleGrid: List[List[int]] + :rtype: int + """ m, n = len(obstacleGrid), len(obstacleGrid[0]) - ways = [0] * n - - if obstacleGrid[0][0] == 0: - ways[0] = 1 - - for j in xrange(1, n): - if obstacleGrid[0][j] == 1: - ways[j] = 0 - else: - ways[j] = ways[j - 1] - - for i in xrange(1, m): + + ways = [0]*n + ways[0] = 1 + for i in xrange(m): if obstacleGrid[i][0] == 1: ways[0] = 0 - - for j in xrange(1, n): + for j in xrange(n): if obstacleGrid[i][j] == 1: ways[j] = 0 - else: - ways[j] += ways[j - 1] - - return ways[n - 1] + elif j>0: + ways[j] += ways[j-1] + return ways[-1] if __name__ == "__main__": obstacleGrid = [ @@ -54,4 +49,4 @@ def uniquePathsWithObstacles(self, obstacleGrid): [0,1,0], [0,0,0] ] - print Solution().uniquePathsWithObstacles(obstacleGrid) \ No newline at end of file + print(Solution().uniquePathsWithObstacles(obstacleGrid)) diff --git a/Python/unique-paths.py b/Python/unique-paths.py index c4f234a76..efe228e3c 100644 --- a/Python/unique-paths.py +++ b/Python/unique-paths.py @@ -1,13 +1,14 @@ +from __future__ import print_function # Time: O(m * n) # Space: O(m + n) # # A robot is located at the top-left corner of a m x n grid (marked 'Start' in the diagram below). -# +# # The robot can only move either down or right at any point in time. # The robot is trying to reach the bottom-right corner of the grid (marked 'Finish' in the diagram below). -# +# # How many possible unique paths are there? -# +# # Note: m and n will be at most 100. # @@ -17,13 +18,12 @@ def uniquePaths(self, m, n): if m < n: return self.uniquePaths(n, m) ways = [1] * n - + for i in xrange(1, m): for j in xrange(1, n): ways[j] += ways[j - 1] - + return ways[n - 1] if __name__ == "__main__": - print Solution().uniquePaths(1, 2) - \ No newline at end of file + print(Solution().uniquePaths(1, 2)) diff --git a/Python/unique-substrings-in-wraparound-string.py b/Python/unique-substrings-in-wraparound-string.py new file mode 100644 index 000000000..33d6c3347 --- /dev/null +++ b/Python/unique-substrings-in-wraparound-string.py @@ -0,0 +1,45 @@ +# Time: O(n) +# Space: O(1) + +# Consider the string s to be the infinite wraparound string of +# "abcdefghijklmnopqrstuvwxyz", so s will look like this: +# "...zabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcd....". +# +# Now we have another string p. Your job is to find out +# how many unique non-empty substrings of p are present in s. +# In particular, your input is the string p and you need to output +# the number of different non-empty substrings of p in the string s. +# +# Note: p consists of only lowercase English letters and the size of p might be over 10000. +# +# Example 1: +# Input: "a" +# Output: 1 +# +# Explanation: Only the substring "a" of string "a" is in the string s. +# Example 2: +# Input: "cac" +# Output: 2 +# Explanation: There are two substrings "a", "c" of string "cac" in the string s. +# Example 3: +# Input: "zab" +# Output: 6 +# Explanation: There are six substrings "z", "a", "b", "za", "ab", "zab" of string "zab" in the string s. + +class Solution(object): + def findSubstringInWraproundString(self, p): + """ + :type p: str + :rtype: int + """ + letters = [0] * 26 + result, length = 0, 0 + for i in xrange(len(p)): + curr = ord(p[i]) - ord('a') + if i > 0 and ord(p[i-1]) != (curr-1)%26 + ord('a'): + length = 0 + length += 1 + if length > letters[curr]: + result += length - letters[curr] + letters[curr] = length + return result diff --git a/Python/unique-word-abbreviation.py b/Python/unique-word-abbreviation.py new file mode 100644 index 000000000..6d90c05df --- /dev/null +++ b/Python/unique-word-abbreviation.py @@ -0,0 +1,39 @@ +# Time: ctor: O(n), n is number of words in the dictionary. +# lookup: O(1) +# Space: O(k), k is number of unique words. + +import collections + + +class ValidWordAbbr(object): + def __init__(self, dictionary): + """ + initialize your data structure here. + :type dictionary: List[str] + """ + self.lookup_ = collections.defaultdict(set) + for word in dictionary: + abbr = self.abbreviation(word) + self.lookup_[abbr].add(word) + + + def isUnique(self, word): + """ + check if a word is unique. + :type word: str + :rtype: bool + """ + abbr = self.abbreviation(word) + return self.lookup_[abbr] <= {word} + + + def abbreviation(self, word): + if len(word) <= 2: + return word + return word[0] + str(len(word)-2) + word[-1] + + +# Your ValidWordAbbr object will be instantiated and called as such: +# vwa = ValidWordAbbr(dictionary) +# vwa.isUnique("word") +# vwa.isUnique("anotherWord") diff --git a/Python/utf-8-validation.py b/Python/utf-8-validation.py new file mode 100644 index 000000000..6a53dd5ac --- /dev/null +++ b/Python/utf-8-validation.py @@ -0,0 +1,61 @@ +# Time: O(n) +# Space: O(1) + +# A character in UTF8 can be from 1 to 4 bytes long, subjected to the following rules: +# +# For 1-byte character, the first bit is a 0, followed by its unicode code. +# For n-bytes character, the first n-bits are all one's, the n+1 bit is 0, +# followed by n-1 bytes with most significant 2 bits being 10. +# This is how the UTF-8 encoding would work: +# +# Char. number range | UTF-8 octet sequence +# (hexadecimal) | (binary) +# --------------------+--------------------------------------------- +# 0000 0000-0000 007F | 0xxxxxxx +# 0000 0080-0000 07FF | 110xxxxx 10xxxxxx +# 0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx +# 0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx +# Given an array of integers representing the data, return whether it is a valid utf-8 encoding. +# +# Note: +# The input is an array of integers. +# Only the least significant 8 bits of each integer is used to store the data. +# This means each integer represents only 1 byte of data. +# +# Example 1: +# +# data = [197, 130, 1], which represents the octet sequence: 11000101 10000010 00000001. +# +# Return true. +# It is a valid utf-8 encoding for a 2-bytes character followed by a 1-byte character. +# Example 2: +# +# data = [235, 140, 4], which represented the octet sequence: 11101011 10001100 00000100. +# +# Return false. +# The first 3 bits are all one's and the 4th bit is 0 means it is a 3-bytes character. +# The next byte is a continuation byte which starts with 10 and that's correct. +# But the second continuation byte does not start with 10, so it is invalid. + +class Solution(object): + def validUtf8(self, data): + """ + :type data: List[int] + :rtype: bool + """ + count = 0 + for c in data: + if count == 0: + if (c >> 5) == 0b110: + count = 1 + elif (c >> 4) == 0b1110: + count = 2 + elif (c >> 3) == 0b11110: + count = 3 + elif (c >> 7): + return False + else: + if (c >> 6) != 0b10: + return False + count -= 1 + return count == 0 diff --git a/Python/valid-anagram.py b/Python/valid-anagram.py new file mode 100644 index 000000000..040bdc853 --- /dev/null +++ b/Python/valid-anagram.py @@ -0,0 +1,67 @@ +# Time: O(n) +# Space: O(1) +# +# Given two strings s and t, write a function to +# determine if t is an anagram of s. +# +# For example, +# s = "anagram", t = "nagaram", return true. +# s = "rat", t = "car", return false. +# +# Note: +# You may assume the string contains only lowercase alphabets. +# + +import collections +import string + + +class Solution: + # @param {string} s + # @param {string} t + # @return {boolean} + def isAnagram(self, s, t): + if len(s) != len(t): + return False + + count = {} + + for c in s: + if c.lower() in count: + count[c.lower()] += 1 + else: + count[c.lower()] = 1 + + for c in t: + if c.lower() in count: + count[c.lower()] -= 1 + else: + count[c.lower()] = -1 + if count[c.lower()] < 0: + return False + + return True + + def isAnagram2(self, s, t): + return all([s.count(c)==t.count(c) for c in string.ascii_lowercase]) + + def isAnagram3(self, s, t): + if len(s) != len(t): + return False + count = collections.defaultdict(int) + for c in s: + count[c] += 1 + for c in t: + count[c] -= 1 + if count[c] < 0: + return False + return True + +# Time: O(nlogn) +# Space: O(n) +class Solution2: + # @param {string} s + # @param {string} t + # @return {boolean} + def isAnagram(self, s, t): + return sorted(s) == sorted(t) diff --git a/Python/valid-number.py b/Python/valid-number.py index c45fbaab7..1630ec0c6 100644 --- a/Python/valid-number.py +++ b/Python/valid-number.py @@ -1,8 +1,9 @@ +from __future__ import print_function # Time: O(n) # Space: O(1) # # Validate if a given string is numeric. -# +# # Some examples: # "0" => true # " 0.1 " => true @@ -21,12 +22,15 @@ class InputType: DOT = 4 EXPONENT = 5 -# regular expression: "^\s*[\+\-]?((\d+(\.\d*)?)|\.\d+)([eE][+-]?\d+)?\s*$" + +# regular expression: "^\s*[\+-]?((\d+(\.\d*)?)|\.\d+)([eE][\+-]?\d+)?\s*$" # automata: http://images.cnitblog.com/i/627993/201405/012016243309923.png -class Solution: - # @param s, a string - # @return a boolean +class Solution(object): def isNumber(self, s): + """ + :type s: str + :rtype: bool + """ transition_table = [[-1, 0, 3, 1, 2, -1], # next states for state 0 [-1, 8, -1, 1, 4, 5], # next states for state 1 [-1, -1, -1, 4, -1, -1], # next states for state 2 @@ -36,7 +40,7 @@ def isNumber(self, s): [-1, -1, -1, 7, -1, -1], # next states for state 6 [-1, 8, -1, 7, -1, -1], # next states for state 7 [-1, 8, -1, -1, -1, -1]] # next states for state 8 - + state = 0 for char in s: inputType = InputType.INVALID @@ -50,24 +54,28 @@ def isNumber(self, s): inputType = InputType.DOT elif char == 'e' or char == 'E': inputType = InputType.EXPONENT; - + state = transition_table[state][inputType]; - + if state == -1: return False; - + return state == 1 or state == 4 or state == 7 or state == 8 -class Solution2: - # @param s, a string - # @return a boolean + +class Solution2(object): def isNumber(self, s): + """ + :type s: str + :rtype: bool + """ import re - return bool(re.match("^\s*[\+\-]?((\d+(\.\d*)?)|\.\d+)([eE][+-]?\d+)?\s*$", s)) - + return bool(re.match("^\s*[\+-]?((\d+(\.\d*)?)|\.\d+)([eE][\+-]?\d+)?\s*$", s)) + + if __name__ == "__main__": - print Solution().isNumber(" 0.1 ") - print Solution().isNumber("abc") - print Solution().isNumber("1 a") - print Solution().isNumber("2e10") - \ No newline at end of file + print(Solution().isNumber(" 0.1 ")) + print(Solution().isNumber("abc")) + print(Solution().isNumber("1 a")) + print(Solution().isNumber("2e10")) + diff --git a/Python/valid-palindrome-ii.py b/Python/valid-palindrome-ii.py new file mode 100644 index 000000000..3cb4d2d00 --- /dev/null +++ b/Python/valid-palindrome-ii.py @@ -0,0 +1,35 @@ +# Time: O(n) +# Soace: O(1) + +# Given a non-empty string s, you may delete at most one character. +# Judge whether you can make it a palindrome. +# +# Example 1: +# Input: "aba" +# Output: True +# Example 2: +# Input: "abca" +# Output: True +# Explanation: You could delete the character 'c'. +# Note: +# The string will only contain lowercase characters a-z. The maximum length of the string is 50000. + +class Solution(object): + def validPalindrome(self, s): + """ + :type s: str + :rtype: bool + """ + def validPalindrome(s, left, right): + while left < right: + if s[left] != s[right]: + return False + left, right = left+1, right-1 + return True + + left, right = 0, len(s)-1 + while left < right: + if s[left] != s[right]: + return validPalindrome(s, left, right-1) or validPalindrome(s, left+1, right) + left, right = left+1, right-1 + return True diff --git a/Python/valid-palindrome.py b/Python/valid-palindrome.py index b9abbfc9b..d3db6552e 100644 --- a/Python/valid-palindrome.py +++ b/Python/valid-palindrome.py @@ -1,15 +1,16 @@ +from __future__ import print_function # Time: O(n) # Space: O(1) # # Given a string, determine if it is a palindrome, considering only alphanumeric characters and ignoring cases. -# +# # For example, # "A man, a plan, a canal: Panama" is a palindrome. # "race a car" is not a palindrome. -# +# # Note: # Have you consider that the string might be empty? This is a good question to ask during an interview. -# +# # For the purpose of this problem, we define empty string as valid palindrome. # @@ -19,9 +20,9 @@ class Solution: def isPalindrome(self, s): i, j = 0, len(s) - 1 while i < j: - while i < j and not (s[i].isalpha() or s[i].isdigit()): + while i < j and not s[i].isalnum(): i += 1 - while i < j and not (s[j].isalpha() or s[j].isdigit()): + while i < j and not s[j].isalnum(): j -= 1 if s[i].lower() != s[j].lower(): return False @@ -29,4 +30,4 @@ def isPalindrome(self, s): return True if __name__ == "__main__": - print Solution().isPalindrome("A man, a plan, a canal: Panama") \ No newline at end of file + print(Solution().isPalindrome("A man, a plan, a canal: Panama")) diff --git a/Python/valid-parentheses.py b/Python/valid-parentheses.py index d62ef17e2..0569e43e9 100644 --- a/Python/valid-parentheses.py +++ b/Python/valid-parentheses.py @@ -1,10 +1,11 @@ +from __future__ import print_function # Time: O(n) # Space: O(n) # # Given a string containing just the characters '(', ')', '{', '}', '[' and ']', # determine if the input string is valid. -# -# The brackets must close in the correct order, "()" and "()[]{}" +# +# The brackets must close in the correct order, "()" and "()[]{}" # are all valid but "(]" and "([)]" are not. # @@ -18,7 +19,7 @@ def isValid(self, s): elif len(stack) == 0 or lookup[stack.pop()] != parenthese: return False return len(stack) == 0 - + if __name__ == "__main__": - print Solution().isValid("()[]{}") - print Solution().isValid("()[{]}") \ No newline at end of file + print(Solution().isValid("()[]{}")) + print(Solution().isValid("()[{]}")) \ No newline at end of file diff --git a/Python/valid-parenthesis-string.py b/Python/valid-parenthesis-string.py new file mode 100644 index 000000000..25c9700c6 --- /dev/null +++ b/Python/valid-parenthesis-string.py @@ -0,0 +1,36 @@ +# Time: O(n) +# Space: O(1) + +# Given a string containing only three types of characters: '(', ')' and '*', +# write a function to check whether this string is valid. We define the validity of a string by these rules: +# 1. Any left parenthesis '(' must have a corresponding right parenthesis ')'. +# 2. Any right parenthesis ')' must have a corresponding left parenthesis '('. +# 3. Left parenthesis '(' must go before the corresponding right parenthesis ')'. +# 4. '*' could be treated as a single right parenthesis ')' or a single left parenthesis '(' or an empty string. +# 5. An empty string is also valid. +# +# Example 1: +# Input: "()" +# Output: True +# Example 2: +# Input: "(*)" +# Output: True +# Example 3: +# Input: "(*))" +# Output: True +# Note: +# The string size will be in the range [1, 100]. + +class Solution(object): + def checkValidString(self, s): + """ + :type s: str + :rtype: bool + """ + lower, upper = 0, 0 # keep lower bound and upper bound of '(' counts + for c in s: + lower += 1 if c == '(' else -1 + upper -= 1 if c == ')' else -1 + if upper < 0: break + lower = max(lower, 0) + return lower == 0 # range of '(' count is valid diff --git a/Python/valid-perfect-square.py b/Python/valid-perfect-square.py new file mode 100644 index 000000000..1b77255fa --- /dev/null +++ b/Python/valid-perfect-square.py @@ -0,0 +1,31 @@ +# Time: O(logn) +# Space: O(1) + +# Given a positive integer num, write a function +# which returns True if num is a perfect square else False. +# +# Note: Do not use any built-in library function such as sqrt. +# +# Example 1: +# +# Input: 16 +# Returns: True +# Example 2: +# +# Input: 14 +# Returns: False + +class Solution(object): + def isPerfectSquare(self, num): + """ + :type num: int + :rtype: bool + """ + left, right = 1, num + while left <= right: + mid = left + (right - left) / 2 + if mid >= num / mid: + right = mid - 1 + else: + left = mid + 1 + return left == num / left and num % left == 0 diff --git a/Python/valid-square.py b/Python/valid-square.py new file mode 100644 index 000000000..a3855abbc --- /dev/null +++ b/Python/valid-square.py @@ -0,0 +1,34 @@ +# Time: O(1) +# Space: O(1) + +# Given the coordinates of four points in 2D space, +# return whether the four points could construct a square. +# +# The coordinate (x,y) of a point is represented by an integer array with two integers. +# +# Example: +# Input: p1 = [0,0], p2 = [1,1], p3 = [1,0], p4 = [0,1] +# Output: True +# Note: +# +# All the input integers are in the range [-10000, 10000]. +# A valid square has four equal sides with positive length +# and four equal angles (90-degree angles). +# Input points have no order. + +class Solution(object): + def validSquare(self, p1, p2, p3, p4): + """ + :type p1: List[int] + :type p2: List[int] + :type p3: List[int] + :type p4: List[int] + :rtype: bool + """ + def dist(p1, p2): + return (p1[0] - p2[0]) ** 2 + (p1[1] - p2[1]) ** 2 + + lookup = set([dist(p1, p2), dist(p1, p3),\ + dist(p1, p4), dist(p2, p3),\ + dist(p2, p4), dist(p3, p4)]) + return 0 not in lookup and len(lookup) == 2 diff --git a/Python/valid-sudoku.py b/Python/valid-sudoku.py index 7de8b05c5..243aa4683 100644 --- a/Python/valid-sudoku.py +++ b/Python/valid-sudoku.py @@ -1,34 +1,41 @@ -# Time: O(n^2) -# Space: O(n) +from __future__ import print_function +# Time: O(9^2) +# Space: O(9) + +# Determine if a Sudoku is valid, +# according to: Sudoku Puzzles - The Rules. +# +# The Sudoku board could be partially filled, +# where empty cells are filled with the character '.'. # -# Determine if a Sudoku is valid, according to: Sudoku Puzzles - The Rules. -# -# The Sudoku board could be partially filled, where empty cells are filled with the character '.'. -# -# # A partially filled sudoku which is valid. -# -# Note: -# A valid Sudoku board (partially filled) is not necessarily solvable. Only the filled cells need to be validated. # +# Note: +# A valid Sudoku board (partially filled) is not necessarily solvable. +# Only the filled cells need to be validated. -class Solution: - # @param board, a 9x9 2D array - # @return a boolean +class Solution(object): def isValidSudoku(self, board): + """ + :type board: List[List[str]] + :rtype: bool + """ for i in xrange(9): - if not self.isValidList([board[i][j] for j in xrange(9)]) or not self.isValidList([board[j][i] for j in xrange(9)]): + if not self.isValidList([board[i][j] for j in xrange(9)]) or \ + not self.isValidList([board[j][i] for j in xrange(9)]): return False for i in xrange(3): for j in xrange(3): - if not self.isValidList([board[m][n] for n in xrange(3 * j, 3 * j + 3) for m in xrange(3 * i, 3 * i + 3)]): + if not self.isValidList([board[m][n] for n in xrange(3 * j, 3 * j + 3) \ + for m in xrange(3 * i, 3 * i + 3)]): return False return True - + def isValidList(self, xs): xs = filter(lambda x: x != '.', xs) return len(set(xs)) == len(xs) + if __name__ == "__main__": board = [[1, '.', '.', '.', '.', '.', '.', '.', '.'], ['.', 2, '.', '.', '.', '.', '.', '.', '.'], @@ -39,4 +46,4 @@ def isValidList(self, xs): ['.', '.', '.', '.', '.', '.', 7, '.', '.'], ['.', '.', '.', '.', '.', '.', '.', 8, '.'], ['.', '.', '.', '.', '.', '.', '.', '.', 9]] - print Solution().isValidSudoku(board) + print(Solution().isValidSudoku(board)) diff --git a/Python/valid-tic-tac-toe-state.py b/Python/valid-tic-tac-toe-state.py new file mode 100644 index 000000000..7d11d867d --- /dev/null +++ b/Python/valid-tic-tac-toe-state.py @@ -0,0 +1,64 @@ +# Time: O(1) +# Space: O(1) + +# A Tic-Tac-Toe board is given as a string array board. Return True +# if and only if it is possible to reach this board position +# during the course of a valid tic-tac-toe game. +# +# The board is a 3 x 3 array, and consists of characters " ", "X", +# and "O". The " " character represents an empty square. +# +# Here are the rules of Tic-Tac-Toe: +# - Players take turns placing characters into empty squares (" "). +# - The first player always places "X" characters, while the second player always places "O" characters. +# - "X" and "O" characters are always placed into empty squares, never filled ones. +# - The game ends when there are 3 of the same (non-empty) character filling any row, column, or diagonal. +# - The game also ends if all squares are non-empty. +# - No more moves can be played if the game is over. +# +# Example 1: +# Input: board = ["O ", " ", " "] +# Output: false +# Explanation: The first player always plays "X". +# +# Example 2: +# Input: board = ["XOX", " X ", " "] +# Output: false +# Explanation: Players take turns making moves. +# +# Example 3: +# Input: board = ["XXX", " ", "OOO"] +# Output: false +# +# Example 4: +# Input: board = ["XOX", "O O", "XOX"] +# Output: true +# +# Note: +# - board is a length-3 array of strings, where each string board[i] has length 3. +# - Each board[i][j] is a character in the set {" ", "X", "O"}. + +class Solution(object): + def validTicTacToe(self, board): + """ + :type board: List[str] + :rtype: bool + """ + def win(board, player): + for i in xrange(3): + if all(board[i][j] == player for j in xrange(3)): + return True + if all(board[j][i] == player for j in xrange(3)): + return True + + return (player == board[1][1] == board[0][0] == board[2][2] or \ + player == board[1][1] == board[0][2] == board[2][0]) + + FIRST, SECOND = ('X', 'O') + x_count = sum(row.count(FIRST) for row in board) + o_count = sum(row.count(SECOND) for row in board) + if o_count not in {x_count-1, x_count}: return False + if win(board, FIRST) and x_count-1 != o_count: return False + if win(board, SECOND) and x_count != o_count: return False + + return True diff --git a/Python/valid-triangle-number.py b/Python/valid-triangle-number.py new file mode 100644 index 000000000..573a27fb2 --- /dev/null +++ b/Python/valid-triangle-number.py @@ -0,0 +1,37 @@ +# Time: O(n^2) +# Space: O(1) + +# Given an array consists of non-negative integers, +# your task is to count the number of triplets chosen +# from the array that can make triangles +# if we take them as side lengths of a triangle. +# +# Example 1: +# Input: [2,2,3,4] +# Output: 3 +# Explanation: +# Valid combinations are: +# 2,3,4 (using the first 2) +# 2,3,4 (using the second 2) +# 2,2,3 +# Note: +# The length of the given array won't exceed 1000. +# The integers in the given array are in the range of [0, 1000]. + +class Solution(object): + def triangleNumber(self, nums): + """ + :type nums: List[int] + :rtype: int + """ + result = 0 + nums.sort() + for i in xrange(len(nums)-2): + if nums[i] == 0: + continue + k = i+2 + for j in xrange(i+1, len(nums)-1): + while k < len(nums) and nums[i] + nums[j] > nums[k]: + k += 1 + result += k-j-1 + return result diff --git a/Python/valid-word-abbreviation.py b/Python/valid-word-abbreviation.py new file mode 100644 index 000000000..e14d474e7 --- /dev/null +++ b/Python/valid-word-abbreviation.py @@ -0,0 +1,28 @@ +# Time: O(n) +# Space: O(1) + +class Solution(object): + def validWordAbbreviation(self, word, abbr): + """ + :type word: str + :type abbr: str + :rtype: bool + """ + i , digit = 0, 0 + for c in abbr: + if c.isdigit(): + if digit == 0 and c == '0': + return False + digit *= 10 + digit += int(c) + else: + if digit: + i += digit + digit = 0 + if i >= len(word) or word[i] != c: + return False + i += 1 + if digit: + i += digit + + return i == len(word) diff --git a/Python/valid-word-square.py b/Python/valid-word-square.py new file mode 100644 index 000000000..8f9fe8c4a --- /dev/null +++ b/Python/valid-word-square.py @@ -0,0 +1,15 @@ +# Time: O(m * n) +# Space: O(1) + +class Solution(object): + def validWordSquare(self, words): + """ + :type words: List[str] + :rtype: bool + """ + for i in xrange(len(words)): + for j in xrange(len(words[i])): + if j >= len(words) or i >= len(words[j]) or \ + words[j][i] != words[i][j]: + return False + return True diff --git a/Python/validate-binary-search-tree.py b/Python/validate-binary-search-tree.py index 9af8db35d..2ef06fb87 100644 --- a/Python/validate-binary-search-tree.py +++ b/Python/validate-binary-search-tree.py @@ -1,10 +1,11 @@ +from __future__ import print_function # Time: O(n) -# Space: O(logn) -# +# Space: O(1) +# # Given a binary tree, determine if it is a valid binary search tree (BST). -# +# # Assume a BST is defined as follows: -# +# # The left subtree of a node contains only nodes with keys less than the node's key. # The right subtree of a node contains only nodes with keys greater than the node's key. # Both the left and right subtrees must also be binary search trees. @@ -17,22 +18,55 @@ def __init__(self, x): self.left = None self.right = None +# Morris Traversal Solution class Solution: + # @param root, a tree node + # @return a list of integers + def isValidBST(self, root): + prev, cur = None, root + while cur: + if cur.left is None: + if prev and prev.val >= cur.val: + return False + prev = cur + cur = cur.right + else: + node = cur.left + while node.right and node.right != cur: + node = node.right + + if node.right is None: + node.right = cur + cur = cur.left + else: + if prev and prev.val >= cur.val: + return False + node.right = None + prev = cur + cur = cur.right + + return True + + +# Time: O(n) +# Space: O(h) +class Solution2: # @param root, a tree node # @return a boolean def isValidBST(self, root): return self.isValidBSTRecu(root, float("-inf"), float("inf")) - + def isValidBSTRecu(self, root, low, high): if root is None: return True - + return low < root.val and root.val < high \ and self.isValidBSTRecu(root.left, low, root.val) \ and self.isValidBSTRecu(root.right, root.val, high) - + + if __name__ == "__main__": root = TreeNode(2) root.left = TreeNode(1) root.right = TreeNode(3) - print Solution().isValidBST(root) \ No newline at end of file + print(Solution().isValidBST(root)) diff --git a/Python/validate-ip-address.py b/Python/validate-ip-address.py new file mode 100644 index 000000000..6a46a8a44 --- /dev/null +++ b/Python/validate-ip-address.py @@ -0,0 +1,73 @@ +# Time: O(1) +# Space: O(1) + +# In this problem, your job to write a function to check whether a input string +# is a valid IPv4 address or IPv6 address or neither. +# +# IPv4 addresses are canonically represented in dot-decimal notation, +# which consists of four decimal numbers, each ranging from 0 to 255, separated by dots ("."), e.g.,172.16.254.1; +# +# Besides, you need to keep in mind that leading zeros in the IPv4 is illegal. +# For example, the address 172.16.254.01 is illegal. +# +# IPv6 addresses are represented as eight groups of four hexadecimal digits, +# each group representing 16 bits. The groups are separated by colons (":"). +# For example, the address 2001:0db8:85a3:0000:0000:8a2e:0370:7334 is a legal one. +# Also, we could omit some leading zeros among four hexadecimal digits and +# some low-case characters in the address to upper-case ones, +# so 2001:db8:85a3:0:0:8A2E:0370:7334 is also a valid IPv6 address(Omit leading zeros and using upper cases). +# +# However, we don't replace a consecutive group of zero value with a single empty group +# using two consecutive colons (::) to pursue simplicity. +# For example, 2001:0db8:85a3::8A2E:0370:7334 is an invalid IPv6 address. +# +# Besides, you need to keep in mind that extra leading zeros in the IPv6 is also illegal. +# For example, the address 02001:0db8:85a3:0000:0000:8a2e:0370:7334 is also illegal. +# +# Note: You could assume there is no extra space in the test cases and +# there may some special characters in the input string. +# +# Example 1: +# Input: "172.16.254.1" +# +# Output: "IPv4" +# +# Explanation: This is a valid IPv4 address, return "IPv4". +# Example 2: +# Input: "2001:0db8:85a3:0:0:8A2E:0370:7334" +# +# Output: "IPv6" +# +# Explanation: This is a valid IPv6 address, return "IPv6". +# Example 3: +# Input: "256.256.256.256" +# +# Output: "Neither" +# +# Explanation: This is neither a IPv4 address nor a IPv6 address. + +import string + + +class Solution(object): + def validIPAddress(self, IP): + """ + :type IP: str + :rtype: str + """ + blocks = IP.split('.') + if len(blocks) == 4: + for i in xrange(len(blocks)): + if not blocks[i].isdigit() or not 0 <= int(blocks[i]) < 256 or \ + (blocks[i][0] == '0' and len(blocks[i]) > 1): + return "Neither" + return "IPv4" + + blocks = IP.split(':') + if len(blocks) == 8: + for i in xrange(len(blocks)): + if not (1 <= len(blocks[i]) <= 4) or \ + not all(c in string.hexdigits for c in blocks[i]): + return "Neither" + return "IPv6" + return "Neither" diff --git a/Python/verify-preorder-sequence-in-binary-search-tree.py b/Python/verify-preorder-sequence-in-binary-search-tree.py new file mode 100644 index 000000000..8da8deeb2 --- /dev/null +++ b/Python/verify-preorder-sequence-in-binary-search-tree.py @@ -0,0 +1,34 @@ +# Time: O(n) +# Space: O(1) + +class Solution: + # @param {integer[]} preorder + # @return {boolean} + def verifyPreorder(self, preorder): + low, i = float("-inf"), -1 + for p in preorder: + if p < low: + return False + while i >= 0 and p > preorder[i]: + low = preorder[i] + i -= 1 + i += 1 + preorder[i] = p + return True + +# Time: O(n) +# Space: O(h) +class Solution2: + # @param {integer[]} preorder + # @return {boolean} + def verifyPreorder(self, preorder): + low = float("-inf") + path = [] + for p in preorder: + if p < low: + return False + while path and p > path[-1]: + low = path[-1] + path.pop() + path.append(p) + return True diff --git a/Python/verify-preorder-serialization-of-a-binary-tree.py b/Python/verify-preorder-serialization-of-a-binary-tree.py new file mode 100644 index 000000000..c084b07ca --- /dev/null +++ b/Python/verify-preorder-serialization-of-a-binary-tree.py @@ -0,0 +1,66 @@ +# Time: O(n) +# Space: O(1) + +# One way to serialize a binary tree is to use pre-oder traversal. +# When we encounter a non-null node, we record the node's value. +# If it is a null node, we record using a sentinel value such as #. +# +# _9_ +# / \ +# 3 2 +# / \ / \ +# 4 1 # 6 +# / \ / \ / \ +# # # # # # # +# For example, the above binary tree can be serialized to the string +# "9,3,4,#,#,1,#,#,2,#,6,#,#", where # represents a null node. +# +# Given a string of comma separated values, verify whether it is a +# correct preorder traversal serialization of a binary tree. +# Find an algorithm without reconstructing the tree. +# +# Each comma separated value in the string must be either an integer +# or a character '#' representing null pointer. +# +# You may assume that the input format is always valid, for example +# it could never contain two consecutive commas such as "1,,3". +# +# Example 1: +# "9,3,4,#,#,1,#,#,2,#,6,#,#" +# Return true +# +# Example 2: +# "1,#" +# Return false +# +# Example 3: +# "9,#,#,1" +# Return false + +class Solution(object): + def isValidSerialization(self, preorder): + """ + :type preorder: str + :rtype: bool + """ + def split_iter(s, tok): + start = 0 + for i in xrange(len(s)): + if s[i] == tok: + yield s[start:i] + start = i + 1 + yield s[start:] + + if not preorder: + return False + + depth, cnt = 0, preorder.count(',') + 1 + for tok in split_iter(preorder, ','): + cnt -= 1 + if tok == "#": + depth -= 1; + if depth < 0: + break + else: + depth += 1 + return cnt == 0 and depth < 0 diff --git a/Python/walking-robot-simulation.py b/Python/walking-robot-simulation.py new file mode 100644 index 000000000..9cb1c60b1 --- /dev/null +++ b/Python/walking-robot-simulation.py @@ -0,0 +1,61 @@ +# Time: O(n + k) +# Space: O(k) + +# A robot on an infinite grid starts at point (0, 0) and faces north. +# The robot can receive one of three possible types of commands: +# - -2: turn left 90 degrees +# - -1: turn right 90 degrees +# - 1 <= x <= 9: move forward x units +# +# Some of the grid squares are obstacles. +# +# The i-th obstacle is at grid point (obstacles[i][0], obstacles[i][1]) +# +# If the robot would try to move onto them, +# the robot stays on the previous grid square instead +# (but still continues following the rest of the route.) +# +# Return the square of the maximum Euclidean distance that +# the robot will be from the origin. +# +# Example 1: +# +# Input: commands = [4,-1,3], obstacles = [] +# Output: 25 +# Explanation: robot will go to (3, 4) +# Example 2: +# +# Input: commands = [4,-1,4,-2,4], obstacles = [[2,4]] +# Output: 65 +# Explanation: robot will be stuck at (1, 4) before turning left and going to (1, 8) +# +# Note: +# - 0 <= commands.length <= 10000 +# - 0 <= obstacles.length <= 10000 +# - -30000 <= obstacle[i][0] <= 30000 +# - -30000 <= obstacle[i][1] <= 30000 +# - The answer is guaranteed to be less than 2 ^ 31. + +class Solution(object): + def robotSim(self, commands, obstacles): + """ + :type commands: List[int] + :type obstacles: List[List[int]] + :rtype: int + """ + directions = [(0, 1), (1, 0), (0, -1), (-1, 0)] + x, y, i = 0, 0, 0 + lookup = set(map(tuple, obstacles)) + result = 0 + for cmd in commands: + if cmd == -2: + i = (i-1) % 4 + elif cmd == -1: + i = (i+1) % 4 + else: + for k in xrange(cmd): + if (x+directions[i][0], y+directions[i][1]) not in lookup: + x += directions[i][0] + y += directions[i][1] + result = max(result, x*x + y*y) + return result diff --git a/Python/walls-and-gates.py b/Python/walls-and-gates.py new file mode 100644 index 000000000..7d287cfcc --- /dev/null +++ b/Python/walls-and-gates.py @@ -0,0 +1,21 @@ +# Time: O(m * n) +# Space: O(g) + +from collections import deque + +class Solution(object): + def wallsAndGates(self, rooms): + """ + :type rooms: List[List[int]] + :rtype: void Do not return anything, modify rooms in-place instead. + """ + INF = 2147483647 + q = deque([(i, j) for i, row in enumerate(rooms) for j, r in enumerate(row) if not r]) + while q: + (i, j) = q.popleft() + for I, J in (i+1, j), (i-1, j), (i, j+1), (i, j-1): + if 0 <= I < len(rooms) and 0 <= J < len(rooms[0]) and \ + rooms[I][J] == INF: + rooms[I][J] = rooms[i][j] + 1 + q.append((I, J)) + diff --git a/Python/water-and-jug-problem.py b/Python/water-and-jug-problem.py new file mode 100644 index 000000000..df6f3601d --- /dev/null +++ b/Python/water-and-jug-problem.py @@ -0,0 +1,42 @@ +# Time: O(logn), n is the max of (x, y) +# Space: O(1) + +# You are given two jugs with capacities x and y litres. +# There is an infinite amount of water supply available. +# You need to determine whether it is possible to +# measure exactly z litres using these two jugs. +# +# Operations allowed: +# +# Fill any of the jugs completely. +# Empty any of the jugs. +# Pour water from one jug into another till +# the other jug is completely full or +# the first jug itself is empty. +# Example 1: +# +# Input: x = 2, y = 6, z = 4 +# Output: True +# Example 2: +# +# Input: x = 2, y = 6, z = 5 +# Output: False + +# Bézout's identity (also called Bézout's lemma) +class Solution(object): + def canMeasureWater(self, x, y, z): + """ + :type x: int + :type y: int + :type z: int + :rtype: bool + """ + def gcd(a, b): + while b: + a, b = b, a%b + return a + + # The problem is to solve: + # - check z <= x + y + # - check if there is any (a, b) integers s.t. ax + by = z + return z == 0 or ((z <= x + y) and (z % gcd(x, y) == 0)) diff --git a/Python/wiggle-sort-ii.py b/Python/wiggle-sort-ii.py new file mode 100644 index 000000000..58efd5258 --- /dev/null +++ b/Python/wiggle-sort-ii.py @@ -0,0 +1,79 @@ +# Time: O(nlogn) +# Space: O(n) + +# Given an unsorted array nums, reorder it such that nums[0] < nums[1] > nums[2] < nums[3].... +# +# Example: +# (1) Given nums = [1, 5, 1, 1, 6, 4], one possible answer is [1, 4, 1, 5, 1, 6]. +# (2) Given nums = [1, 3, 2, 2, 3, 1], one possible answer is [2, 3, 1, 3, 1, 2]. +# +# Note: +# You may assume all input has valid answer. +# +# Follow Up: +# Can you do it in O(n) time and/or in-place with O(1) extra space? + +# Sorting and reoder solution. (92ms) +class Solution(object): + def wiggleSort(self, nums): + """ + :type nums: List[int] + :rtype: void Do not return anything, modify nums in-place instead. + """ + nums.sort() + med = (len(nums) - 1) / 2 + nums[::2], nums[1::2] = nums[med::-1], nums[:med:-1] + +# Time: O(n) ~ O(n^2) +# Space: O(1) +# Tri Partition (aka Dutch National Flag Problem) with virtual index solution. (TLE) +from random import randint +class Solution2(object): + def wiggleSort(self, nums): + """ + :type nums: List[int] + :rtype: void Do not return anything, modify nums in-place instead. + """ + def findKthLargest(nums, k): + left, right = 0, len(nums) - 1 + while left <= right: + pivot_idx = randint(left, right) + new_pivot_idx = partitionAroundPivot(left, right, pivot_idx, nums) + if new_pivot_idx == k - 1: + return nums[new_pivot_idx] + elif new_pivot_idx > k - 1: + right = new_pivot_idx - 1 + else: # new_pivot_idx < k - 1. + left = new_pivot_idx + 1 + + def partitionAroundPivot(left, right, pivot_idx, nums): + pivot_value = nums[pivot_idx] + new_pivot_idx = left + nums[pivot_idx], nums[right] = nums[right], nums[pivot_idx] + for i in xrange(left, right): + if nums[i] > pivot_value: + nums[i], nums[new_pivot_idx] = nums[new_pivot_idx], nums[i] + new_pivot_idx += 1 + nums[right], nums[new_pivot_idx] = nums[new_pivot_idx], nums[right] + return new_pivot_idx + + def reversedTriPartitionWithVI(nums, val): + def idx(i, N): + return (1 + 2 * (i)) % N + + N = len(nums) / 2 * 2 + 1 + i, j, n = 0, 0, len(nums) - 1 + while j <= n: + if nums[idx(j, N)] > val: + nums[idx(i, N)], nums[idx(j, N)] = nums[idx(j, N)], nums[idx(i, N)] + i += 1 + j += 1 + elif nums[idx(j, N)] < val: + nums[idx(j, N)], nums[idx(n, N)] = nums[idx(n, N)], nums[idx(j, N)] + n -= 1 + else: + j += 1 + + mid = (len(nums) - 1) / 2 + findKthLargest(nums, mid + 1) + reversedTriPartitionWithVI(nums, nums[mid]) diff --git a/Python/wiggle-sort.py b/Python/wiggle-sort.py new file mode 100644 index 000000000..fd3b0283f --- /dev/null +++ b/Python/wiggle-sort.py @@ -0,0 +1,14 @@ +# Time: O(n) +# Space: O(1) + +class Solution(object): + def wiggleSort(self, nums): + """ + :type nums: List[int] + :rtype: void Do not return anything, modify nums in-place instead. + """ + for i in xrange(1, len(nums)): + if ((i % 2) and nums[i - 1] > nums[i]) or \ + (not (i % 2) and nums[i - 1] < nums[i]): + # Swap unordered elements. + nums[i - 1], nums[i] = nums[i], nums[i - 1] diff --git a/Python/wiggle-subsequence.py b/Python/wiggle-subsequence.py new file mode 100644 index 000000000..abb8e6148 --- /dev/null +++ b/Python/wiggle-subsequence.py @@ -0,0 +1,58 @@ +# Time: O(n) +# Space: O(1) + +# A sequence of numbers is called a wiggle sequence +# if the differences between successive numbers strictly +# alternate between positive and negative. +# The first difference (if one exists) may be either positive +# or negative. A sequence with fewer than two elements +# is trivially a wiggle sequence. +# +# For example, [1,7,4,9,2,5] is a wiggle sequence because +# the differences (6,-3,5,-7,3) are alternately positive +# and negative. In contrast, [1,4,7,2,5] and [1,7,4,5,5] are +# not wiggle sequences, the first because its first two differences +# are positive and the second because its last difference is zero. +# +# Given a sequence of integers, return the length of +# the longest subsequence that is a wiggle sequence. +# A subsequence is obtained by deleting some number of elements +# (eventually, also zero) from the original sequence, leaving +# the remaining elements in their original order. +# +# Examples: +# Input: [1,7,4,9,2,5] +# Output: 6 +# The entire sequence is a wiggle sequence. +# +# Input: [1,17,5,10,13,15,10,5,16,8] +# Output: 7 +# There are several subsequences that achieve this length. One is [1,17,10,13,10,16,8]. +# +# Input: [1,2,3,4,5,6,7,8,9] +# Output: 2 +# +# Follow up: +# Can you do it in O(n) time? + + +class Solution(object): + def wiggleMaxLength(self, nums): + """ + :type nums: List[int] + :rtype: int + """ + if len(nums) < 2: + return len(nums) + + length, up = 1, None + + for i in xrange(1, len(nums)): + if nums[i - 1] < nums[i] and (up is None or up is False): + length += 1 + up = True + elif nums[i - 1] > nums[i] and (up is None or up is True): + length += 1 + up = False + + return length diff --git a/Python/wildcard-matching.py b/Python/wildcard-matching.py index ebd9f1c49..9d62f3271 100644 --- a/Python/wildcard-matching.py +++ b/Python/wildcard-matching.py @@ -1,16 +1,17 @@ +from __future__ import print_function # Time: O(m + n) # Space: O(1) # # Implement wildcard pattern matching with support for '?' and '*'. -# +# # '?' Matches any single character. # '*' Matches any sequence of characters (including the empty sequence). -# +# # The matching should cover the entire input string (not partial). -# +# # The function prototype should be: # bool isMatch(const char *s, const char *p) -# +# # Some examples: # isMatch("aa","a") -> false # isMatch("aa","aa") -> true @@ -42,13 +43,15 @@ def isMatch(self, s, p): p_ptr = last_p_ptr else: return False - + while p_ptr < len(p) and p[p_ptr] == '*': p_ptr += 1 - + return p_ptr == len(p) - -# dp + +# dp with rolling window +# Time: O(m * n) +# Space: O(m + n) class Solution2: # @return a boolean def isMatch(self, s, p): @@ -66,7 +69,7 @@ def isMatch(self, s, p): result[i % k][j] = result[(i-1) % k][j-1] and (s[i-1] == p[j-1] or p[j-1] == '?') else: result[i % k][j] = result[i % k][j-1] or result[(i-1) % k][j] - + return result[len(s) % k][len(p)] # dp @@ -88,19 +91,19 @@ def isMatch(self, s, p): result[i][j] = result[i-1][j-1] and (s[i-1] == p[j-1] or p[j-1] == '?') else: result[i][j] = result[i][j-1] or result[i-1][j] - + return result[len(s)][len(p)] -# recursive, slowest +# recursive, slowest, TLE class Solution4: # @return a boolean def isMatch(self, s, p): - if len(p) == 0: - return len(s) == 0 - + if not p or not s: + return not s and not p + if p[0] != '*': - if len(s) == 0 or (p[0] == s[0] or p[0] == '?'): + if p[0] == s[0] or p[0] == '?': return self.isMatch(s[1:], p[1:]) else: return False @@ -112,12 +115,12 @@ def isMatch(self, s, p): return self.isMatch(s, p[1:]) if __name__ == "__main__": - print Solution().isMatch("aaaabaaaab","a*b*b") - print Solution().isMatch("aaaaaaaaaaaaab", "a*a*a*a*a*a*a*a*a*a*c") - print Solution().isMatch("aa","a") - print Solution().isMatch("aa","aa") - print Solution().isMatch("aaa","aa") - print Solution().isMatch("aa", "a*") - print Solution().isMatch("aa", "?*") - print Solution().isMatch("ab", "?*") - print Solution().isMatch("aab", "c*a*b") \ No newline at end of file + print(Solution().isMatch("aaaabaaaab","a*b*b")) + print(Solution().isMatch("aaaaaaaaaaaaab", "a*a*a*a*a*a*a*a*a*a*c")) + print(Solution().isMatch("aa","a")) + print(Solution().isMatch("aa","aa")) + print(Solution().isMatch("aaa","aa")) + print(Solution().isMatch("aa", "a*")) + print(Solution().isMatch("aa", "?*")) + print(Solution().isMatch("ab", "?*")) + print(Solution().isMatch("aab", "c*a*b")) diff --git a/Python/word-abbreviation.py b/Python/word-abbreviation.py new file mode 100644 index 000000000..87f1290ad --- /dev/null +++ b/Python/word-abbreviation.py @@ -0,0 +1,38 @@ +# Time: O(n * l) ~ O(n^2 * l^2) +# Space: O(n * l) + +import collections + + +class Solution(object): + def wordsAbbreviation(self, dict): + """ + :type dict: List[str] + :rtype: List[str] + """ + def isUnique(prefix, words): + return sum(word.startswith(prefix) for word in words) == 1 + + def toAbbr(prefix, word): + abbr = prefix + str(len(word) - 1 - len(prefix)) + word[-1] + return abbr if len(abbr) < len(word) else word + + abbr_to_word = collections.defaultdict(set) + word_to_abbr = {} + + for word in dict: + prefix = word[:1] + abbr_to_word[toAbbr(prefix, word)].add(word) + + for abbr, conflicts in abbr_to_word.iteritems(): + if len(conflicts) > 1: + for word in conflicts: + for i in xrange(2, len(word)): + prefix = word[:i] + if isUnique(prefix, conflicts): + word_to_abbr[word] = toAbbr(prefix, word) + break + else: + word_to_abbr[conflicts.pop()] = abbr + + return [word_to_abbr[word] for word in dict] diff --git a/Python/word-break-ii.py b/Python/word-break-ii.py index f0908b2d0..804778b3e 100644 --- a/Python/word-break-ii.py +++ b/Python/word-break-ii.py @@ -1,48 +1,57 @@ -# Time: O(2^n) -# Space: O(n) +from __future__ import print_function +# Time: O(n * l^2 + n * r), l is the max length of the words, +# r is the number of the results. +# Space: O(n^2) # -# Given a string s and a dictionary of words dict, +# Given a string s and a dictionary of words dict, # add spaces in s to construct a sentence where each word is a valid dictionary word. -# +# # Return all such possible sentences. -# +# # For example, given # s = "catsanddog", # dict = ["cat", "cats", "and", "sand", "dog"]. -# +# # A solution is ["cats and dog", "cat sand dog"]. # -class Solution: - # @param s, a string - # @param dict, a set of string - # @return a list of strings - def wordBreak(self, s, dict): +class Solution(object): + def wordBreak(self, s, wordDict): + """ + :type s: str + :type wordDict: Set[str] + :rtype: List[str] + """ n = len(s) - possible = [False for _ in xrange(n)] + + max_len = 0 + for string in wordDict: + max_len = max(max_len, len(string)) + + can_break = [False for _ in xrange(n + 1)] valid = [[False] * n for _ in xrange(n)] - for i in xrange(n): - if s[:i+1] in dict: - possible[i] = True - valid[0][i] = True - for j in xrange(i): - if possible[j] and s[j+1:i+1] in dict: - valid[j+1][i] = True - possible[i] = True + can_break[0] = True + for i in xrange(1, n + 1): + for l in xrange(1, min(i, max_len) + 1): + if can_break[i-l] and s[i-l:i] in wordDict: + valid[i-l][i-1] = True + can_break[i] = True + result = [] - if possible[n-1]: - self.genPath(s, valid, 0, [], result) + if can_break[-1]: + self.wordBreakHelper(s, valid, 0, [], result) return result - - def genPath(self, s, valid, start, path, result): + + def wordBreakHelper(self, s, valid, start, path, result): if start == len(s): result.append(" ".join(path)) return for i in xrange(start, len(s)): if valid[start][i]: path += [s[start:i+1]] - self.genPath(s, valid, i + 1, path, result) + self.wordBreakHelper(s, valid, i + 1, path, result) path.pop() + if __name__ == "__main__": - print Solution().wordBreak("catsanddog", ["cat", "cats", "and", "sand", "dog"]) \ No newline at end of file + print(Solution().wordBreak("catsanddog", ["cat", "cats", "and", "sand", "dog"])) diff --git a/Python/word-break.py b/Python/word-break.py index bd39ed955..0bbc5c266 100644 --- a/Python/word-break.py +++ b/Python/word-break.py @@ -1,32 +1,39 @@ -# Time: O(n^2) +from __future__ import print_function +# Time: O(n * l^2) # Space: O(n) -# -# Given a string s and a dictionary of words dict, + +# Given a string s and a dictionary of words dict, # determine if s can be segmented into a space-separated sequence of one or more dictionary words. -# +# # For example, given # s = "leetcode", # dict = ["leet", "code"]. -# -# Return true because "leetcode" can be segmented as "leet code". # +# Return true because "leetcode" can be segmented as "leet code". -class Solution: - # @param s, a string - # @param dict, a set of string - # @return a boolean - def wordBreak(self, s, dict): +class Solution(object): + def wordBreak(self, s, wordDict): + """ + :type s: str + :type wordDict: Set[str] + :rtype: bool + """ n = len(s) - possible = [False for _ in xrange(n)] - for i in xrange(n): - if s[:i+1] in dict: - possible[i] = True - for j in xrange(i): - if possible[j] and s[j+1:i+1] in dict: - possible[i] = True + + max_len = 0 + for string in wordDict: + max_len = max(max_len, len(string)) + + can_break = [False for _ in xrange(n + 1)] + can_break[0] = True + for i in xrange(1, n + 1): + for l in xrange(1, min(i, max_len) + 1): + if can_break[i-l] and s[i-l:i] in wordDict: + can_break[i] = True break - - return possible[n-1] - + + return can_break[-1] + + if __name__ == "__main__": - print Solution().wordBreak("leetcode", ["leet", "code"]) \ No newline at end of file + print(Solution().wordBreak("leetcode", ["leet", "code"])) diff --git a/Python/word-ladder-ii.py b/Python/word-ladder-ii.py index 501fd94fa..e6475bfe6 100644 --- a/Python/word-ladder-ii.py +++ b/Python/word-ladder-ii.py @@ -1,12 +1,13 @@ -# Time: O((25n)^n) -# Space: O((25n)^n) +from __future__ import print_function +# Time: O(n * d), n is length of string, d is size of dictionary +# Space: O(d) # # Given two words (start and end), and a dictionary, find all shortest transformation sequence(s) from start to end, such that: -# +# # Only one letter can be changed at a time # Each intermediate word must exist in the dictionary # For example, -# +# # Given: # start = "hit" # end = "cog" @@ -30,14 +31,14 @@ class Solution: def findLadders(self, start, end, dict): dict.add(start) dict.add(end) - - result, cur, visited, found, trace = [], [start], set([start]), False, {word: [] for word in dict} + + result, cur, visited, found, trace = [], [start], set([start]), False, {word: [] for word in dict} while cur and not found: for word in cur: visited.add(word) - - next = set([]) + + next = set() for word in cur: for i in xrange(len(word)): for j in 'abcdefghijklmnopqrstuvwxyz': @@ -48,18 +49,18 @@ def findLadders(self, start, end, dict): next.add(candidate) trace[candidate].append(word) cur = next - + if found: self.backtrack(result, trace, [], end) - + return result - + def backtrack(self, result, trace, path, word): - if len(trace[word]) == 0: + if not trace[word]: result.append([word] + path) else: for prev in trace[word]: self.backtrack(result, trace, [word] + path, prev) - + if __name__ == "__main__": - print Solution().findLadders("hit", "cog", set(["hot","dot","dog","lot","log"])) + print(Solution().findLadders("hit", "cog", set(["hot","dot","dog","lot","log"]))) diff --git a/Python/word-ladder.py b/Python/word-ladder.py index 8ed2f6c97..db057c541 100644 --- a/Python/word-ladder.py +++ b/Python/word-ladder.py @@ -1,19 +1,20 @@ -# Time: O((25n)^n) -# Space: O((25n)^n) +from __future__ import print_function +# Time: O(n * d), n is length of string, d is size of dictionary +# Space: O(d) # # Given two words (start and end), and a dictionary, find the length of shortest transformation sequence from start to end, such that: -# +# # Only one letter can be changed at a time # Each intermediate word must exist in the dictionary # For example, -# +# # Given: # start = "hit" # end = "cog" # dict = ["hot","dot","dog","lot","log"] # As one shortest transformation is "hit" -> "hot" -> "dot" -> "dog" -> "cog", # return its length 5. -# +# # Note: # Return 0 if there is no such transformation sequence. # All words have the same length. @@ -21,30 +22,34 @@ # # BFS -class Solution: - # @param start, a string - # @param end, a string - # @param dict, a set of string - # @return an integer - def ladderLength(self, start, end, dict): - distance, cur, visited = 0, [start], set([start]) - dict.add(end) - +class Solution(object): + def ladderLength(self, beginWord, endWord, wordList): + """ + :type beginWord: str + :type endWord: str + :type wordList: List[str] + :rtype: int + """ + distance, cur, visited, lookup = 0, [beginWord], set([beginWord]), set(wordList) + while cur: - next = [] + next_queue = [] + for word in cur: - if word == end: + if word == endWord: return distance + 1 for i in xrange(len(word)): for j in 'abcdefghijklmnopqrstuvwxyz': candidate = word[:i] + j + word[i + 1:] - if candidate not in visited and candidate in dict: - next.append(candidate) + if candidate not in visited and candidate in lookup: + next_queue.append(candidate) visited.add(candidate) distance += 1 - cur = next - + cur = next_queue + return 0 - + + if __name__ == "__main__": - print Solution().ladderLength("hit", "cog", set(["hot","dot","dog","lot","log"])) + print(Solution().ladderLength("hit", "cog", set(["hot", "dot", "dog", "lot", "log"]))) + print(Solution().ladderLength("hit", "cog", set(["hot", "dot", "dog", "lot", "log", "cog"]))) diff --git a/Python/word-pattern-ii.py b/Python/word-pattern-ii.py new file mode 100644 index 000000000..774795858 --- /dev/null +++ b/Python/word-pattern-ii.py @@ -0,0 +1,39 @@ +# Time: O(n * C(n - 1, c - 1)), n is length of str, c is unique count of pattern, +# there are H(n - c, c - 1) = C(n - 1, c - 1) possible splits of string, +# and each one costs O(n) to check if it matches the word pattern. +# Space: O(n + c) + +class Solution(object): + def wordPatternMatch(self, pattern, str): + """ + :type pattern: str + :type str: str + :rtype: bool + """ + w2p, p2w = {}, {} + return self.match(pattern, str, 0, 0, w2p, p2w) + + + def match(self, pattern, str, i, j, w2p, p2w): + is_match = False + if i == len(pattern) and j == len(str): + is_match = True + elif i < len(pattern) and j < len(str): + p = pattern[i] + if p in p2w: + w = p2w[p] + if w == str[j:j+len(w)]: # Match pattern. + is_match = self.match(pattern, str, i + 1, j + len(w), w2p, p2w) + # Else return false. + else: + for k in xrange(j, len(str)): # Try any possible word + w = str[j:k+1] + if w not in w2p: + # Build mapping. Space: O(n + c) + w2p[w], p2w[p] = p, w; + is_match = self.match(pattern, str, i + 1, k + 1, w2p, p2w) + w2p.pop(w), p2w.pop(p); + if is_match: + break + return is_match + diff --git a/Python/word-pattern.py b/Python/word-pattern.py new file mode 100644 index 000000000..ef0db71b9 --- /dev/null +++ b/Python/word-pattern.py @@ -0,0 +1,82 @@ +# Time: O(n) +# Space: O(c), c is unique count of pattern + +# Given a pattern and a string str, find if str follows the same pattern. +# +# Examples: +# 1. pattern = "abba", str = "dog cat cat dog" should return true. +# 2. pattern = "abba", str = "dog cat cat fish" should return false. +# 3. pattern = "aaaa", str = "dog cat cat dog" should return false. +# 4. pattern = "abba", str = "dog dog dog dog" should return false. +# +# Notes: +# 1. Both pattern and str contains only lowercase alphabetical letters. +# 2. Both pattern and str do not have leading or trailing spaces. +# 3. Each word in str is separated by a single space. +# 4. Each letter in pattern must map to a word with length that is at least 1. + +from itertools import izip # Generator version of zip. + +class Solution(object): + def wordPattern(self, pattern, str): + """ + :type pattern: str + :type str: str + :rtype: bool + """ + if len(pattern) != self.wordCount(str): + return False + + w2p, p2w = {}, {} + for p, w in izip(pattern, self.wordGenerator(str)): + if w not in w2p and p not in p2w: + # Build mapping. Space: O(c) + w2p[w] = p + p2w[p] = w + elif w not in w2p or w2p[w] != p: + # Contradict mapping. + return False + return True + + def wordCount(self, str): + cnt = 1 if str else 0 + for c in str: + if c == ' ': + cnt += 1 + return cnt + + # Generate a word at a time without saving all the words. + def wordGenerator(self, str): + w = "" + for c in str: + if c == ' ': + yield w + w = "" + else: + w += c + yield w + + +# Time: O(n) +# Space: O(n) +class Solution2(object): + def wordPattern(self, pattern, str): + """ + :type pattern: str + :type str: str + :rtype: bool + """ + words = str.split() # Space: O(n) + if len(pattern) != len(words): + return False + + w2p, p2w = {}, {} + for p, w in izip(pattern, words): + if w not in w2p and p not in p2w: + # Build mapping. Space: O(c) + w2p[w] = p + p2w[p] = w + elif w not in w2p or w2p[w] != p: + # Contradict mapping. + return False + return True diff --git a/Python/word-search-ii.py b/Python/word-search-ii.py new file mode 100644 index 000000000..c317e6b2d --- /dev/null +++ b/Python/word-search-ii.py @@ -0,0 +1,77 @@ +# Time: O(m * n * l) +# Space: O(l) +# +# Given a 2D board and a list of words from the dictionary, find all words in the board. +# +# Each word must be constructed from letters of sequentially adjacent cell, where "adjacent" cells +# are those horizontally or vertically neighboring. The same letter cell may not be used more than once in a word. +# +# For example, +# Given words = ["oath","pea","eat","rain"] and board = +# +# [ +# ['o','a','a','n'], +# ['e','t','a','e'], +# ['i','h','k','r'], +# ['i','f','l','v'] +# ] +# Return ["eat","oath"]. +# Note: +# You may assume that all inputs are consist of lowercase letters a-z. +# + +class TrieNode(object): + # Initialize your data structure here. + def __init__(self): + self.is_string = False + self.leaves = {} + + # Inserts a word into the trie. + def insert(self, word): + cur = self + for c in word: + if not c in cur.leaves: + cur.leaves[c] = TrieNode() + cur = cur.leaves[c] + cur.is_string = True + + +class Solution(object): + def findWords(self, board, words): + """ + :type board: List[List[str]] + :type words: List[str] + :rtype: List[str] + """ + visited = [[False for j in xrange(len(board[0]))] for i in xrange(len(board))] + result = {} + trie = TrieNode() + for word in words: + trie.insert(word) + + for i in xrange(len(board)): + for j in xrange(len(board[0])): + if self.findWordsRecu(board, trie, 0, i, j, visited, [], result): + return True + + return result.keys() + + def findWordsRecu(self, board, trie, cur, i, j, visited, cur_word, result): + if not trie or i < 0 or i >= len(board) or j < 0 or j >= len(board[0]) or visited[i][j]: + return + + if board[i][j] not in trie.leaves: + return + + cur_word.append(board[i][j]) + next_node = trie.leaves[board[i][j]] + if next_node.is_string: + result["".join(cur_word)] = True + + visited[i][j] = True + self.findWordsRecu(board, next_node, cur + 1, i + 1, j, visited, cur_word, result) + self.findWordsRecu(board, next_node, cur + 1, i - 1, j, visited, cur_word, result) + self.findWordsRecu(board, next_node, cur + 1, i, j + 1, visited, cur_word, result) + self.findWordsRecu(board, next_node, cur + 1, i, j - 1, visited, cur_word, result) + visited[i][j] = False + cur_word.pop() diff --git a/Python/word-search.py b/Python/word-search.py index 434f5dbda..4dfe9763a 100644 --- a/Python/word-search.py +++ b/Python/word-search.py @@ -1,15 +1,16 @@ -# Time: O(m * n * 3^p) -# Space: O(m * n + p) +from __future__ import print_function +# Time: O(m * n * l) +# Space: O(l) # # Given a 2D board and a word, find if the word exists in the grid. -# -# The word can be constructed from letters of sequentially adjacent cell, -# where "adjacent" cells are those horizontally or vertically neighboring. +# +# The word can be constructed from letters of sequentially adjacent cell, +# where "adjacent" cells are those horizontally or vertically neighboring. # The same letter cell may not be used more than once. -# +# # For example, # Given board = -# +# # [ # "ABCE", # "SFCS", @@ -26,36 +27,36 @@ class Solution: # @return a boolean def exist(self, board, word): visited = [[False for j in xrange(len(board[0]))] for i in xrange(len(board))] - + for i in xrange(len(board)): for j in xrange(len(board[0])): if self.existRecu(board, word, 0, i, j, visited): return True - + return False - + def existRecu(self, board, word, cur, i, j, visited): if cur == len(word): return True - + if i < 0 or i >= len(board) or j < 0 or j >= len(board[0]) or visited[i][j] or board[i][j] != word[cur]: return False - + visited[i][j] = True result = self.existRecu(board, word, cur + 1, i + 1, j, visited) or\ self.existRecu(board, word, cur + 1, i - 1, j, visited) or\ self.existRecu(board, word, cur + 1, i, j + 1, visited) or\ - self.existRecu(board, word, cur + 1, i, j - 1, visited) + self.existRecu(board, word, cur + 1, i, j - 1, visited) visited[i][j] = False - + return result - + if __name__ == "__main__": board = [ "ABCE", "SFCS", "ADEE" ] - print Solution().exist(board, "ABCCED") - print Solution().exist(board, "SFCS") - print Solution().exist(board, "ABCB") + print(Solution().exist(board, "ABCCED")) + print(Solution().exist(board, "SFCS")) + print(Solution().exist(board, "ABCB")) diff --git a/Python/word-squares.py b/Python/word-squares.py new file mode 100644 index 000000000..21d6bbf43 --- /dev/null +++ b/Python/word-squares.py @@ -0,0 +1,51 @@ +# Time: O(n^2 * n!) +# Space: O(n^2) + +class TrieNode(object): + def __init__(self): + self.indices = [] + self.children = [None] * 26 + + def insert(self, words, i): + cur = self + for c in words[i]: + if not cur.children[ord(c)-ord('a')]: + cur.children[ord(c)-ord('a')] = TrieNode() + cur = cur.children[ord(c)-ord('a')] + cur.indices.append(i) + + +class Solution(object): + def wordSquares(self, words): + """ + :type words: List[str] + :rtype: List[List[str]] + """ + result = [] + + trie = TrieNode() + for i in xrange(len(words)): + trie.insert(words, i) + + curr = [] + for s in words: + curr.append(s) + self.wordSquaresHelper(words, trie, curr, result) + curr.pop() + + return result + + def wordSquaresHelper(self, words, trie, curr, result): + if len(curr) >= len(words[0]): + return result.append(list(curr)) + + node = trie + for s in curr: + node = node.children[ord(s[len(curr)]) - ord('a')] + if not node: + return + + for i in node.indices: + curr.append(words[i]) + self.wordSquaresHelper(words, trie, curr, result) + curr.pop() diff --git a/Python/zigzag-conversion.py b/Python/zigzag-conversion.py index ebe4be0da..aec60f9b0 100644 --- a/Python/zigzag-conversion.py +++ b/Python/zigzag-conversion.py @@ -1,33 +1,36 @@ +from __future__ import print_function # Time: O(n) # Space: O(1) # -# The string "PAYPALISHIRING" is written in a zigzag pattern on a given number of rows like this: +# The string "PAYPALISHIRING" is written in a zigzag pattern on a given number of rows like this: # (you may want to display this pattern in a fixed font for better legibility) -# +# # P A H N # A P L S I I G # Y I R # And then read line by line: "PAHNAPLSIIGYIR" # Write the code that will take a string and make this conversion given a number of rows: -# +# # string convert(string text, int nRows); # convert("PAYPALISHIRING", 3) should return "PAHNAPLSIIGYIR". # -class Solution: - # @return a string - def convert(self, s, nRows): - step, zigzag = 2 * nRows - 2, "" - if s == None or len(s) == 0 or nRows <= 0: - return "" - if nRows == 1: +class Solution(object): + def convert(self, s, numRows): + """ + :type s: str + :type numRows: int + :rtype: str + """ + if numRows == 1: return s - for i in xrange(nRows): + step, zigzag = 2 * numRows - 2, "" + for i in xrange(numRows): for j in xrange(i, len(s), step): zigzag += s[j] - if i > 0 and i < nRows - 1 and j + step - 2 * i < len(s): + if 0 < i < numRows - 1 and j + step - 2 * i < len(s): zigzag += s[j + step - 2 * i] return zigzag if __name__ == "__main__": - print Solution().convert("PAYPALISHIRING", 3) \ No newline at end of file + print(Solution().convert("PAYPALISHIRING", 3)) diff --git a/Python/zigzag-iterator.py b/Python/zigzag-iterator.py new file mode 100644 index 000000000..56fc22313 --- /dev/null +++ b/Python/zigzag-iterator.py @@ -0,0 +1,34 @@ +# Time: O(n) +# Space: O(k) + +import collections + + +class ZigzagIterator(object): + + def __init__(self, v1, v2): + """ + Initialize your q structure here. + :type v1: List[int] + :type v2: List[int] + """ + self.q = collections.deque([(len(v), iter(v)) for v in (v1, v2) if v]) + + def next(self): + """ + :rtype: int + """ + len, iter = self.q.popleft() + if len > 1: + self.q.append((len-1, iter)) + return next(iter) + + def hasNext(self): + """ + :rtype: bool + """ + return bool(self.q) + +# Your ZigzagIterator object will be instantiated and called as such: +# i, v = ZigzagIterator(v1, v2), [] +# while i.hasNext(): v.append(i.next()) diff --git a/Python/zuma-game.py b/Python/zuma-game.py new file mode 100644 index 000000000..99d25c947 --- /dev/null +++ b/Python/zuma-game.py @@ -0,0 +1,107 @@ +# Time: O(b * b! * h!) +# Space: O(b * b! * h!) + +# Think about Zuma Game. You have a row of balls on the table, +# colored red(R), yellow(Y), blue(B), green(G), and white(W). +# You also have several balls in your hand. +# +# Each time, you may choose a ball in your hand, and insert it into the row +# (including the leftmost place and rightmost place). +# Then, if there is a group of 3 or more balls in the same color touching, +# remove these balls. Keep doing this until no more balls can be removed. +# +# Find the minimal balls you have to insert to remove all the balls on the table. +# If you cannot remove all the balls, output -1. +# +# Examples: +# +# Input: "WRRBBW", "RB" +# Output: -1 +# Explanation: WRRBBW -> WRR[R]BBW -> WBBW -> WBB[B]W -> WW +# +# Input: "WWRRBBWW", "WRBRW" +# Output: 2 +# Explanation: WWRRBBWW -> WWRR[R]BBWW -> WWBBWW -> WWBB[B]WW -> WWWW -> empty +# +# Input:"G", "GGGGG" +# Output: 2 +# Explanation: G -> G[G] -> GG[G] -> empty +# +# Input: "RBYYBBRRB", "YRBGB" +# Output: 3 +# Explanation: RBYYBBRRB -> RBYY[Y]BBRRB -> RBBBRRB -> RRRB -> B -> B[B] -> BB[B] -> empty +# +# Note: +# You may assume that the initial row of balls on the table won’t have any 3 or +# more consecutive balls with the same color. +# The number of balls on the table won't exceed 20, and the string represents these balls +# is called "board" in the input. +# The number of balls in your hand won't exceed 5, and the string represents these balls +# is called "hand" in the input. +# Both input strings will be non-empty and only contain characters 'R','Y','B','G','W'. + +import collections + + +class Solution(object): + def findMinStep(self, board, hand): + """ + :type board: str + :type hand: str + :rtype: int + """ + def shrink(s): # Time: O(n), Space: O(n) + stack = [] + start = 0 + for i in xrange(len(s)+1): + if i == len(s) or s[i] != s[start]: + if stack and stack[-1][0] == s[start]: + stack[-1][1] += i - start + if stack[-1][1] >= 3: + stack.pop() + elif s and i - start < 3: + stack += [s[start], i - start], + start = i + result = [] + for p in stack: + result += [p[0]] * p[1] + return result + + def find(board, c, j): + for i in xrange(j, len(board)): + if board[i] == c: + return i + return -1 + + def findMinStepHelper(board, hand, lookup): + if not board: return 0 + if not hand: return float("inf") + if tuple(hand) in lookup[tuple(board)]: return lookup[tuple(board)][tuple(hand)] + + result = float("inf") + for i in xrange(len(hand)): + j = 0 + while j < len(board): + k = find(board, hand[i], j) + if k == -1: + break + + if k < len(board) - 1 and board[k] == board[k+1]: + next_board = shrink(board[0:k] + board[k+2:]) + next_hand = hand[0:i] + hand[i+1:] + result = min(result, findMinStepHelper(next_board, next_hand, lookup) + 1) + k += 1 + elif i > 0 and hand[i] == hand[i-1]: + next_board = shrink(board[0:k] + board[k+1:]) + next_hand = hand[0:i-1] + hand[i+1:] + result = min(result, findMinStepHelper(next_board, next_hand, lookup) + 2) + j = k+1 + + lookup[tuple(board)][tuple(hand)] = result + return result + + lookup = collections.defaultdict(dict) + board, hand = list(board), list(hand) + hand.sort() + result = findMinStepHelper(board, hand, lookup) + return -1 if result == float("inf") else result diff --git a/README.md b/README.md index c7c3ea16e..575a7599a 100644 --- a/README.md +++ b/README.md @@ -1,734 +1,947 @@ -LeetCode -======== +# [LeetCode](https://leetcode.com/problemset/algorithms/) ![Language](https://img.shields.io/badge/language-Python%20%2F%20C++%2011-orange.svg) [![License](https://img.shields.io/badge/license-MIT-blue.svg)](./LICENSE.md) [![SayThanks](https://img.shields.io/badge/say-thanks-ff69b4.svg)](https://saythanks.io/to/kamyu104) ![Travis](https://travis-ci.org/kamyu104/LeetCode.svg?branch=master) -Up to date (2015-01-13), there are total `180` problems on [LeetCode Online Judge](https://oj.leetcode.com/). -The number of problems is increasing recently. -Here is the classification of all `180` problems. +The number of LeetCode questions is increasing every week. +For more questions and solutions, you can see my [LintCode](https://github.com/kamyu104/LintCode) repository. I'll keep updating for full summary and better solutions. Stay tuned for updates. +(Notes: "📖" means you need to subscribe to [LeetCode premium membership](https://leetcode.com/subscribe/) for the access to premium questions.) ---- -Algorithm -==== +## Algorithms * [Bit Manipulation](https://github.com/kamyu104/LeetCode#bit-manipulation) * [Array](https://github.com/kamyu104/LeetCode#array) * [String](https://github.com/kamyu104/LeetCode#string) * [Linked List](https://github.com/kamyu104/LeetCode#linked-list) * [Stack](https://github.com/kamyu104/LeetCode#stack) +* [Queue](https://github.com/kamyu104/LeetCode#queue) * [Heap](https://github.com/kamyu104/LeetCode#heap) * [Tree](https://github.com/kamyu104/LeetCode#tree) * [Hash Table](https://github.com/kamyu104/LeetCode#hash-table) -* [Data Structure](https://github.com/kamyu104/LeetCode#data-structure) * [Math](https://github.com/kamyu104/LeetCode#math) -* [Two Pointer](https://github.com/kamyu104/LeetCode#two-pointer) +* [Two Pointers](https://github.com/kamyu104/LeetCode#two-pointers) * [Sort](https://github.com/kamyu104/LeetCode#sort) -* [Brute Force Search](https://github.com/kamyu104/LeetCode#brute-force-search) -* [Divide and Conquer](https://github.com/kamyu104/LeetCode#divide-and-conquer) +* [Recursion](https://github.com/kamyu104/LeetCode#recursion) * [Binary Search](https://github.com/kamyu104/LeetCode#binary-search) +* [Binary Search Tree](https://github.com/kamyu104/LeetCode#binary-search-tree) * [Breadth-First Search](https://github.com/kamyu104/LeetCode#breadth-first-search) * [Depth-First Search](https://github.com/kamyu104/LeetCode#depth-first-search) -* [Dynamic Programming](https://github.com/kamyu104/LeetCode#dynamic-programming) * [Backtracking](https://github.com/kamyu104/LeetCode#backtracking) +* [Dynamic Programming](https://github.com/kamyu104/LeetCode#dynamic-programming) * [Greedy](https://github.com/kamyu104/LeetCode#greedy) +* [Graph](https://github.com/kamyu104/LeetCode#graph) +* [Geometry](https://github.com/kamyu104/LeetCode#geometry) +* [Simulation](https://github.com/kamyu104/LeetCode#simulation) +* [Design](https://github.com/kamyu104/LeetCode#design) -Database -=== +## Database * [SQL](https://github.com/kamyu104/LeetCode#sql) ---- - -##Bit Manipulation -Problem | Solution | Time | Space | Difficulty | Notes ---------------- | --------------- | --------------- | --------------- | -------------- | ----- -[Single Number] | [single-number.py] | _O(n)_ | _O(1)_ | Medium | -[Single Number II] | [single-number-ii.py] | _O(n)_ | _O(1)_ | Medium | - -[Single Number]: https://oj.leetcode.com/problems/single-number/ -[single-number.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/single-number.py -[Single Number II]: https://oj.leetcode.com/problems/single-number-ii/ -[single-number-ii.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/single-number-ii.py - ---- - -##Array - -Problem | Solution | Time | Space | Difficulty | Notes ---------------- | --------------- | --------------- | --------------- | -------------- | ----- -[3 Sum] | [3sum.py] | _O(n^2)_ | _O(1)_ | Medium | -[3 Sum Closest] | [3sum-closest.py]| _O(n^2)_ | _O(1)_ | Medium | -[Best Time to Buy and Sell Stock]| [best-time-to-buy-and-sell-stock.py] | _O(n)_ | _O(1)_ | Medium | -[First Missing Positive]| [first-missing-positive.py] | _O(n)_ | _O(1)_ | Hard | Tricky -[Longest Consecutive Sequence]| [longest-consecutive-sequence.py] | _O(n)_ | _O(n)_ | Hard | Tricky -[Majority Element] | [majority-element.py] | _O(n)_ | _O(1)_ | Easy | -[Missing Ranges]| [missing-ranges.py] | _O(n)_ | _O(1)_ | Medium | -[Next Permutation]| [next-permutation.py] | _O(n)_ | _O(1)_ | Medium | Tricky -[Pascal's Triangle]| [pascals-triangle.py] | _O(n^2)_ | _O(n)_ | Easy | -[Pascal's Triangle II]| [pascals-triangle-ii.py] | _O(n^2)_ | _O(n)_ | Easy | -[Plus One] | [plus-one.py] | _O(n)_ | _O(1)_ | Easy | -[Read N Characters Given Read4] | [read-n-characters-given-read4.py] | _O(n)_ | _O(1)_ | Easy | -[Read N Characters Given Read4 II - Call multiple times] | [read-n-characters-given-read4-ii-call-multiple-times.py] | _O(n)_ | _O(1)_ | Hard | -[Remove Duplicates from Sorted Array]| [remove-duplicates-from-sorted-array.py] | _O(n)_ | _O(1)_ | Easy | -[Remove Duplicates from Sorted Array II]| [remove-duplicates-from-sorted-array-ii.py] | _O(n)_ | _O(1)_ | Medium | -[Remove Element] | [remove-element.py] | _O(n)_ | _O(1)_ | Easy | -[Rotate Image] | [rotate-image.py] | _O(n^2)_ | _O(1)_ | Medium | -[Set Matrix Zeroes] | [set-matrix-zeroes.py] | _O(m * n)_ | _O(1)_ | Medium | -[Spiral Matrix] | [spiral-matrix.py] | _O(m * n)_ | _O(1)_ | Medium | -[Spiral Matrix II] | [spiral-matrix-ii.py] | _O(m * n)_ | _O(1)_ | Medium | - - -[3 Sum]: https://oj.leetcode.com/problems/3sum/ -[3sum.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/3sum.py -[3 Sum Closest]: https://oj.leetcode.com/problems/3sum-closest/ -[3sum-closest.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/3sum-closest.py -[Best Time to Buy and Sell Stock]:https://oj.leetcode.com/problems/best-time-to-buy-and-sell-stock/ -[best-time-to-buy-and-sell-stock.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/best-time-to-buy-and-sell-stock.py -[First Missing Positive]:https://oj.leetcode.com/problems/first-missing-positive/ -[first-missing-positive.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/first-missing-positive.py -[Longest Consecutive Sequence]:https://oj.leetcode.com/problems/longest-consecutive-sequence/ -[longest-consecutive-sequence.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/longest-consecutive-sequence.py -[Majority Element]: https://oj.leetcode.com/problems/majority-element/ -[majority-element.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/majority-element.py -[Missing Ranges]:https://oj.leetcode.com/problems/missing-ranges/ -[missing-ranges.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/missing-ranges.py -[Next Permutation]:https://oj.leetcode.com/problems/next-permutation/ -[next-permutation.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/next-permutation.py -[Pascal's Triangle]:https://oj.leetcode.com/problems/pascals-triangle/ -[pascals-triangle.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/pascals-triangle.py -[Pascal's Triangle II]:https://oj.leetcode.com/problems/pascals-triangle-ii/ -[pascals-triangle-ii.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/pascals-triangle-ii.py -[Plus One]:https://oj.leetcode.com/problems/plus-one/ -[plus-one.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/plus-one.py -[Read N Characters Given Read4]:https://oj.leetcode.com/problems/read-n-characters-given-read4/ -[read-n-characters-given-read4.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/read-n-characters-given-read4.py -[Read N Characters Given Read4 II - Call multiple times]:https://oj.leetcode.com/problems/read-n-characters-given-read4-ii-call-multiple-times/ -[read-n-characters-given-read4-ii-call-multiple-times.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/read-n-characters-given-read4-ii-call-multiple-times.py -[Remove Duplicates from Sorted Array]:https://oj.leetcode.com/problems/remove-duplicates-from-sorted-array/ -[remove-duplicates-from-sorted-array.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/remove-duplicates-from-sorted-array.py -[Remove Duplicates from Sorted Array II]:https://oj.leetcode.com/problems/remove-duplicates-from-sorted-array-ii/ -[remove-duplicates-from-sorted-array-ii.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/remove-duplicates-from-sorted-array-ii.py -[Remove Element]:https://oj.leetcode.com/problems/remove-element/ -[remove-element.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/remove-element.py -[Rotate Image]:https://oj.leetcode.com/problems/rotate-image/ -[rotate-image.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/rotate-image.py -[Set Matrix Zeroes]:https://oj.leetcode.com/problems/set-matrix-zeroes/ -[set-matrix-zeroes.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/set-matrix-zeroes.py -[Spiral Matrix]:https://oj.leetcode.com/problems/spiral-matrix/ -[spiral-matrix.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/spiral-matrix.py -[Spiral Matrix II]:https://oj.leetcode.com/problems/spiral-matrix-ii/ -[spiral-matrix-ii.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/spiral-matrix-ii.py - - ---- - -##String -Problem | Solution | Time | Space | Difficulty | Notes ---------------- | --------------- | --------------- | --------------- | -------------- | ----- -[Add Binary] | [add-binary.py] | _O(n)_ | _O(1)_ | Easy | -[Anagrams] | [anagrams.py] | _O(n)_ | _O(n)_ | Medium | -[Compare Version Numbers] | [compare-version-numbers.py] | _O(n)_ | _O(1)_ | Easy | -[Count and Say] | [count-and-say.py]| _O(n^2)_ | _O(n)_ | Easy | -[Implement strStr()] | [implement-strstr.py] | _O(n + m)_ | _O(m)_ | Easy | `KMP Algorithm` -[Length of Last Word] | [length-of-last-word.py] | _O(n)_ | _O(1)_ | Easy | -[Longest Common Prefix] | [longest-common-prefix.py] | _O(n1 + n2 + ...)_ | _O(1)_ | Easy | -[Longest Palindromic Substring] | [longest-palindromic-substring.py] | _O(n)_ | _O(n)_ | Medium | `Manacher's Algorithm` -[Multiply Strings] | [multiply-strings.py] | _O(m * n)_ | _O(m + n)_ | Medium | -[One Edit Distance] | [one-edit-distance.py] | _O(m + n)_ | _O(1)_ | Medium | -[Reverse Words in a String] | [reverse-words-in-a-string.py] | _O(n)_ | _O(n)_ | Medium | -[String to Integer (atoi)] | [string-to-integer-atoi.py] | _O(n)_ | _O(1)_ | Easy | -[Text Justification] | [text-justification.py] | _O(n)_ | _O(1)_ | Hard | -[Valid Palindrome] | [valid-palindrome.py] | _O(n)_ | _O(1)_ | Easy | -[ZigZag Conversion] | [zigzag-conversion.py] | _O(n)_ | _O(1)_ | Easy | - -[Add Binary]:https://oj.leetcode.com/problems/add-binary/ -[add-binary.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/add-binary.py -[Anagrams]:https://oj.leetcode.com/problems/anagrams/ -[anagrams.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/anagrams.py -[Count and Say]:https://oj.leetcode.com/problems/count-and-say/ -[count-and-say.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/count-and-say.py -[Compare Version Numbers]:https://oj.leetcode.com/problems/compare-version-numbers/ -[compare-version-numbers.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/compare-version-numbers.py -[Implement strStr()]:https://oj.leetcode.com/problems/implement-strstr/ -[implement-strstr.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/implement-strstr.py -[Length of Last Word]:https://oj.leetcode.com/problems/length-of-last-word/ -[length-of-last-word.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/length-of-last-word.py -[Longest Common Prefix]:https://oj.leetcode.com/problems/longest-common-prefix/ -[longest-common-prefix.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/longest-common-prefix.py -[Longest Palindromic Substring]:https://oj.leetcode.com/problems/longest-palindromic-substring/ -[longest-palindromic-substring.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/longest-palindromic-substring.py -[Multiply Strings]:https://oj.leetcode.com/problems/multiply-strings/ -[multiply-strings.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/multiply-strings.py - -[One Edit Distance]:https://oj.leetcode.com/problems/one-edit-distance/ -[one-edit-distance.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/one-edit-distance.py -[Reverse Words in a String]:https://oj.leetcode.com/problems/reverse-words-in-a-string/ -[reverse-words-in-a-string.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/reverse-words-in-a-string.py -[String to Integer (atoi)]:https://oj.leetcode.com/problems/string-to-integer-atoi/ -[string-to-integer-atoi.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/string-to-integer-atoi.py -[Text Justification]:https://oj.leetcode.com/problems/text-justification/ -[text-justification.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/text-justification.py -[Valid Palindrome]:https://oj.leetcode.com/problems/valid-palindrome/ -[valid-palindrome.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/valid-palindrome.py -[ZigZag Conversion]:https://oj.leetcode.com/problems/zigzag-conversion/ -[zigzag-conversion.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/zigzag-conversion.py - ---- - -##Linked List -Problem | Solution | Time | Space | Difficulty | Notes ---------------- | --------------- | --------------- | --------------- | -------------- | ----- -[Add Two Numbers] | [add-two-numbers.py] | _O(n)_ | _O(1)_ | Medium | -[Copy List with Random Pointer] | [copy-list-with-random-pointer.py] | _O(n)_ | _O(1)_ | Hard | -[Intersection of Two Linked Lists]| [intersection-of-two-linked-lists.py] | _O(m + n)_ | _O(1)_ | Easy | -[Remove Duplicates from Sorted List]| [remove-duplicates-from-sorted-list.py] | _O(n)_ | _O(1)_ | Easy | -[Remove Duplicates from Sorted List II]| [remove-duplicates-from-sorted-list-ii.py] | _O(n)_ | _O(1)_ | Medium | -[Reverse Linked List II]| [reverse-linked-list-ii.py] | _O(n)_ | _O(1)_ | Medium | -[Reverse Nodes in k-Group]| [reverse-nodes-in-k-group.py] | _O(n)_ | _O(1)_ | Hard | -[Rotate List]| [rotate-list.py] | _O(n)_ | _O(1)_ | Medium | -[Swap Nodes in Pairs]| [swap-nodes-in-pairs.py] | _O(n)_ | _O(1)_ | Medium | - -[Add Two Numbers]:https://oj.leetcode.com/problems/add-two-numbers/ -[add-two-numbers.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/add-two-numbers.py -[Copy List with Random Pointer]:https://oj.leetcode.com/problems/copy-list-with-random-pointer/ -[copy-list-with-random-pointer.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/copy-list-with-random-pointer.py -[Intersection of Two Linked Lists]:https://oj.leetcode.com/problems/intersection-of-two-linked-lists/ -[intersection-of-two-linked-lists.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/intersection-of-two-linked-lists.py -[Remove Duplicates from Sorted List]:https://oj.leetcode.com/problems/remove-duplicates-from-sorted-list/ -[remove-duplicates-from-sorted-list.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/remove-duplicates-from-sorted-list.py -[Remove Duplicates from Sorted List II]:https://oj.leetcode.com/problems/remove-duplicates-from-sorted-list-ii/ -[remove-duplicates-from-sorted-list-ii.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/remove-duplicates-from-sorted-list-ii.py -[Reverse Linked List II]:https://oj.leetcode.com/problems/reverse-linked-list-ii/ -[reverse-linked-list-ii.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/reverse-linked-list-ii.py -[Reverse Nodes in k-Group]:https://oj.leetcode.com/problems/reverse-nodes-in-k-group/ -[reverse-nodes-in-k-group.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/reverse-nodes-in-k-group.py -[Rotate List]:https://oj.leetcode.com/problems/rotate-list/ -[rotate-list.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/rotate-list.py -[Swap Nodes in Pairs]:https://oj.leetcode.com/problems/swap-nodes-in-pairs/ -[swap-nodes-in-pairs.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/swap-nodes-in-pairs.py - ---- - -##Stack -Problem | Solution | Time | Space | Difficulty | Notes ---------------- | --------------- | --------------- | --------------- | -------------- | ----- -[Binary Search Tree Iterator] | [binary-search-tree-iterator.py] | _O(1)_| _O(logn)_| Medium -[Evaluate Reverse Polish Notation]| [evaluate-reverse-polish-notation.py]| _O(n)_| _O(n)_| Medium | -[Longest Valid Parentheses]| [longest-valid-parentheses.py] | _O(n)_ | _O(1)_ | Hard | -[Min Stack] | [min-stack.py] | _O(n)_ | _O(1)_ | Easy | -[Simplify Path]| [simplify-path.py] | _O(n)_ | _O(n)_ | Medium | -[Symmetric Tree]| [symmetric-tree.py] | _O(n)_ | _O(logn)_ | Easy | -[Valid Parentheses]| [valid-parentheses.py] | _O(n)_ | _O(n)_ | Easy | - -[Binary Search Tree Iterator]:https://oj.leetcode.com/problems/binary-search-tree-iterator/ -[binary-search-tree-iterator.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/binary-search-tree-iterator.py -[Evaluate Reverse Polish Notation]:https://oj.leetcode.com/problems/evaluate-reverse-polish-notation/ -[evaluate-reverse-polish-notation.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/evaluate-reverse-polish-notation.py -[Longest Valid Parentheses]:https://oj.leetcode.com/problems/longest-valid-parentheses/ -[longest-valid-parentheses.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/longest-valid-parentheses.py -[Min Stack]:https://oj.leetcode.com/problems/min-stack/ -[min-stack.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/min-stack.py -[Simplify Path]:https://oj.leetcode.com/problems/simplify-path/ -[simplify-path.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/simplify-path.py -[Symmetric Tree]:https://oj.leetcode.com/problems/symmetric-tree/ -[symmetric-tree.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/symmetric-tree.py -[Valid Parentheses]:https://oj.leetcode.com/problems/valid-parentheses/ -[valid-parentheses.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/valid-parentheses.py - ---- - -##Heap -Problem | Solution | Time | Space | Difficulty | Notes ---------------- | --------------- | --------------- | --------------- | -------------- | ----- -[Merge k Sorted Lists] | [merge-k-sorted-lists.py] | _O(nlogk)_| _O(1)_| Hard | - -[Merge k Sorted Lists]:https://oj.leetcode.com/problems/merge-k-sorted-lists/ -[merge-k-sorted-lists.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/merge-k-sorted-lists.py - ---- - -##Tree -Problem | Solution | Time | Space | Difficulty | Notes ---------------- | --------------- | --------------- | --------------- | -------------- | ----- -[Binary Tree Preorder Traversal] | [binary-tree-preorder-traversal.py] | _O(n)_| _O(1)_| Medium | `Morris Traversal` -[Binary Tree Inorder Traversal] | [binary-tree-inorder-traversal.py] | _O(n)_| _O(1)_| Medium | `Morris Traversal` -[Binary Tree Postorder Traversal]| [binary-tree-postorder-traversal.py] | _O(n)_| _O(1)_| Hard | `Morris Traversal` -[Recover Binary Search Tree]| [recover-binary-search-tree.py] | _O(n)_| _O(1)_| Hard | `Morris Traversal` - -[Binary Tree Preorder Traversal]:https://oj.leetcode.com/problems/binary-tree-preorder-traversal/ -[binary-tree-preorder-traversal.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/binary-tree-preorder-traversal.py -[Binary Tree Inorder Traversal]:https://oj.leetcode.com/problems/binary-tree-inorder-traversal/ -[binary-tree-inorder-traversal.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/binary-tree-inorder-traversal.py -[Binary Tree Postorder Traversal]:https://oj.leetcode.com/problems/binary-tree-postorder-traversal/ -[binary-tree-postorder-traversal.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/binary-tree-postorder-traversal.py -[Recover Binary Search Tree]:https://oj.leetcode.com/problems/recover-binary-search-tree/ -[recover-binary-search-tree.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/recover-binary-search-tree.py - ---- - -##Hash Table -Problem | Solution | Time | Space | Difficulty | Notes ---------------- | --------------- | --------------- | --------------- | -------------- | ----- -[4 Sum] |[4sum.py] | _O(n^2)_ ~ _O(n^4)_ | _O(n^2)_ | Medium | -[Longest Substring with At Most Two Distinct Characters]| [longest-substring-with-at-most-two-distinct-characters.py] | _O(n^2)_ | _O(1)_ | Hard | -[Longest Substring Without Repeating Characters] | [longest-substring-without-repeating-characters.py] | _O(n)_ | _O(1)_ | Medium | -[Max Points on a Line] | [max-points-on-a-line.py] | _O(n^2)_ | _O(n)_ | Hard | -[Minimum Window Substring] | [minimum-window-substring.py] | _O(n^2)_ | _O(n)_ | Hard | -[Substring with Concatenation of All Words] | [substring-with-concatenation-of-all-words.py] | _O(m * n * k)_ | _O(n * k)_ | Hard | -[Two Sum] | [two-sum.py] | _O(n)_ | _O(n)_ | Medium | -[Two Sum III - Data structure design] | [two-sum-iii-data-structure-design.py] | _O(n)_ | _O(n)_ | Easy | -[Valid Sudoku] | [valid-sudoku.py] | _O(n)_ | _O(n)_ | Easy | - -[4 Sum]: https://oj.leetcode.com/problems/4sum/ -[4sum.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/4sum.py -[Longest Substring with At Most Two Distinct Characters]:https://oj.leetcode.com/problems/longest-substring-with-at-most-two-distinct-characters/ -[longest-substring-with-at-most-two-distinct-characters.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/longest-substring-with-at-most-two-distinct-characters.py -[Longest Substring Without Repeating Characters]:https://oj.leetcode.com/problems/longest-substring-without-repeating-characters/ -[longest-substring-without-repeating-characters.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/longest-substring-without-repeating-characters.py -[Max Points on a Line]:https://oj.leetcode.com/problems/max-points-on-a-line/ -[max-points-on-a-line.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/max-points-on-a-line.py -[Minimum Window Substring]:https://oj.leetcode.com/problems/minimum-window-substring/ -[minimum-window-substring.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/minimum-window-substring.py -[Substring with Concatenation of All Words]:https://oj.leetcode.com/problems/substring-with-concatenation-of-all-words/ -[substring-with-concatenation-of-all-words.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/substring-with-concatenation-of-all-words.py -[Two Sum]:https://oj.leetcode.com/problems/two-sum/ -[two-sum.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/two-sum.py -[Two Sum III - Data structure design]:https://oj.leetcode.com/problems/two-sum-iii-data-structure-design/ -[two-sum-iii-data-structure-design.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/two-sum-iii-data-structure-design.py -[Valid Sudoku]:https://oj.leetcode.com/problems/valid-sudoku/ -[valid-sudoku.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/valid-sudoku.py - ---- - -##Data Structure -Problem | Solution | Time | Space | Difficulty | Notes ---------------- | --------------- | --------------- | --------------- | -------------- | ----- -[LRU Cache] | [lru-cache.py] | _O(1)_ | _O(n)_ | Hard | - - -[LRU Cache]:https://oj.leetcode.com/problems/lru-cache/ -[lru-cache.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/lru-cache.py - ---- - -##Math -Problem | Solution | Time | Space | Difficulty | Notes ---------------- | --------------- | --------------- | --------------- | -------------- | ----- -[Divide Two Integers] | [divide-two-integers.py] | _O(logn)_ | _O(1)_ | Medium | -[Excel Sheet Column Title] | [excel-sheet-column-title.py] | _O(logn)_ | _O(1)_ | Easy | -[Excel Sheet Column Number] | [excel-sheet-column-number.py] | _O(n)_ | _O(1)_ | Easy | -[Factorial Trailing Zeroes] | [factorial-trailing-zeroes.py] | _O(logn)_ | _O(1)_ | Easy | -[Fraction to Recurring Decimal] | [fraction-to-recurring-decimal.py] | _O(logn)_ | _O(1)_ | Medium | -[Gray Code] | [gray-code.py] | _O(2^n)_ | _O(1)_ | Medium | -[Integer to Roman] | [integer-to-roman.py] | _O(n)_ | _O(1)_ | Medium | -[Palindrome Number] | [palindrome-number.py] | _O(1)_ | _O(1)_ | Easy | -[Permutation Sequence] | [permutation-sequence.py] | _O(n)_ | _O(1)_ | Medium | `Cantor Ordering` -[Reverse Integer] | [reverse-integer.py] | _O(logn)_ | _O(1)_ | Easy | -[Roman to Integer] | [roman-to-integer.py] | _O(n)_ | _O(1)_ | Easy | -[Valid Number] | [valid-number.py] | _O(n)_ | _O(1)_ | Hard | `Automata` - -[Divide Two Integers]:https://oj.leetcode.com/problems/divide-two-integers/ -[divide-two-integers.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/divide-two-integers.py -[Excel Sheet Column Title]:https://oj.leetcode.com/problems/excel-sheet-column-title/ -[excel-sheet-column-title.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/excel-sheet-column-title.py -[Excel Sheet Column Number]:https://oj.leetcode.com/problems/excel-sheet-column-number/ -[excel-sheet-column-number.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/excel-sheet-column-number.py -[Factorial Trailing Zeroes]:https://oj.leetcode.com/problems/factorial-trailing-zeroes/ -[factorial-trailing-zeroes.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/factorial-trailing-zeroes.py -[Fraction to Recurring Decimal]:https://oj.leetcode.com/problems/fraction-to-recurring-decimal/ -[fraction-to-recurring-decimal.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/fraction-to-recurring-decimal.py -[Gray Code]:https://oj.leetcode.com/problems/gray-code/ -[gray-code.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/gray-code.py -[Integer to Roman]:https://oj.leetcode.com/problems/integer-to-roman/ -[integer-to-roman.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/integer-to-roman.py -[Palindrome Number]:https://oj.leetcode.com/problems/palindrome-number/ -[palindrome-number.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/palindrome-number.py -[Permutation Sequence]:https://oj.leetcode.com/problems/permutation-sequence/ -[permutation-sequence.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/permutation-sequence.py -[Reverse Integer]:https://oj.leetcode.com/problems/reverse-integer/ -[reverse-integer.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/reverse-integer.py -[Roman to Integer]:https://oj.leetcode.com/problems/roman-to-integer/ -[roman-to-integer.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/roman-to-integer.py -[Valid Number]:https://oj.leetcode.com/problems/valid-number/ -[valid-number.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/valid-number.py - - ---- - -##Sort -Problem | Solution | Time | Space | Difficulty | Notes ---------------- | --------------- | --------------- | --------------- | -------------- | ----- -[Insert Interval]| [insert-interval.py] | _O(n)_ | _O(1)_ | Hard | -[Insertion Sort List]|[insertion-sort-list.py] | _O(n^2)_ | _O(1)_ | Medium | -[Largest Number] | [largest-number.py] | _O(n^2)_ | _O(n)_ | Medium | -[Maximum Gap] | [maximum-gap.py]| _O(n)_ | _O(n)_ | Hard | Tricky -[Merge Intervals]| [merge-intervals.py] | _O(n^2)_ | _O(1)_ | Hard | -[Merge Sorted Array]| [merge-sorted-array.py] | _O(n)_ | _O(1)_ | Easy | -[Merge Two Sorted Lists]| [merge-two-sorted-lists.py] | _O(n)_ | _O(1)_ | Easy | -[Sort Colors] | [sort-colors.py] | _O(n)_ | _O(1)_ | Medium | -[Sort List] | [sort-list.py] | _O(nlogn)_ | _O(1)_ | Medium | - -[Insert Interval]:https://oj.leetcode.com/problems/insert-interval/ -[insert-interval.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/insert-interval.py -[Insertion Sort List]:https://oj.leetcode.com/problems/insertion-sort-list/ -[insertion-sort-list.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/insertion-sort-list.py -[Largest Number]:https://oj.leetcode.com/problems/largest-number/ -[largest-number.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/largest-number.py -[Maximum Gap]:https://oj.leetcode.com/problems/maximum-gap/ -[maximum-gap.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/maximum-gap.py -[Merge Intervals]:https://oj.leetcode.com/problems/merge-intervals/ -[merge-intervals.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/merge-intervals.py -[Merge Sorted Array]:https://oj.leetcode.com/problems/merge-sorted-array/ -[merge-sorted-array.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/merge-sorted-array.py -[Merge Two Sorted Lists]:https://oj.leetcode.com/problems/merge-two-sorted-lists/ -[merge-two-sorted-lists.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/merge-two-sorted-lists.py -[Sort Colors]:https://oj.leetcode.com/problems/sort-colors/ -[sort-colors.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/sort-colors.py -[Sort List]:https://oj.leetcode.com/problems/sort-list/ -[sort-list.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/sort-list.py - ---- - -##Two Pointer -Problem | Solution | Time | Space | Difficulty | Notes ---------------- | --------------- | --------------- | --------------- | -------------- | ----- -[Linked List Cycle]| [linked-list-cycle.py] | _O(n)_ | _O(1)_ | Medium | -[Linked List Cycle II]| [linked-list-cycle-ii.py] | _O(n)_ | _O(1)_ | Medium | -[Partition List]| [partition-list.py] | _O(n)_ | _O(1)_ | Medium | -[Remove Nth Node From End of List]| [remove-nth-node-from-end-of-list.py] | _O(n)_ | _O(1)_ | Easy | -[Reorder List]| [reorder-list.py] | _O(n)_ | _O(1)_ | Medium | -[Two Sum II - Input array is sorted] | [two-sum-ii-input-array-is-sorted.py] | _O(n)_ | _O(1)_ | Medium | - -[Linked List Cycle]:https://oj.leetcode.com/problems/linked-list-cycle/ -[linked-list-cycle.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/linked-list-cycle.py -[Linked List Cycle II]:https://oj.leetcode.com/problems/linked-list-cycle-ii/ -[linked-list-cycle-ii.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/linked-list-cycle-ii.py -[Partition List]:https://oj.leetcode.com/problems/partition-list/ -[partition-list.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/partition-list.py -[Remove Nth Node From End of List]:https://oj.leetcode.com/problems/remove-nth-node-from-end-of-list/ -[remove-nth-node-from-end-of-list.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/remove-nth-node-from-end-of-list.py -[Reorder List]:https://oj.leetcode.com/problems/reorder-list/ -[reorder-list.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/reorder-list.py -[Two Sum II - Input array is sorted]:https://oj.leetcode.com/problems/two-sum-ii-input-array-is-sorted/ -[two-sum-ii-input-array-is-sorted.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/two-sum-ii-input-array-is-sorted.py - ---- - -##Brute Force Search -Problem | Solution | Time | Space | Difficulty | Notes ---------------- | --------------- | --------------- | --------------- | -------------- | ----- -[Letter Combinations of a Phone Number]| [letter-combinations-of-a-phone-number.py] | _O(4^n)_ | _O(1)_ | Medium | -[Permutations]| [permutations.py] | _O(n!)_ | _O(n)_ | Medium | -[Permutations II]| [permutations-ii.py] | _O(n!)_ | _O(n)_ | Hard | -[Subsets] | [subsets.py] | _O(2^n)_ | _O(1)_ | Medium | -[Subsets II] | [subsets-ii.py] | _O(2^n)_ | _O(1)_ | Medium | - -[Letter Combinations of a Phone Number]:https://oj.leetcode.com/problems/letter-combinations-of-a-phone-number/ -[letter-combinations-of-a-phone-number.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/letter-combinations-of-a-phone-number.py -[Permutations]:https://oj.leetcode.com/problems/permutations/ -[permutations.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/permutations.py -[Permutations II]:https://oj.leetcode.com/problems/permutations-ii/ -[permutations-ii.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/permutations-ii.py -[Subsets]:https://oj.leetcode.com/problems/subsets/ -[subsets.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/subsets.py -[Subsets II]:https://oj.leetcode.com/problems/subsets-ii/ -[subsets-ii.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/subsets-ii.py - ---- - -##Divide and Conquer -Problem | Solution | Time | Space | Difficulty | Notes ---------------- | --------------- | --------------- | --------------- | -------------- | ----- -[Balanced Binary Tree] | [balanced-binary-tree.py] | _O(n)_| _O(logn)_| Easy | -[Binary Tree Maximum Path Sum]| [binary-tree-maximum-path-sum.py] | _O(n)_| _O(logn)_| Hard | -[Binary Tree Upside Down] | [binary-tree-upside-down.py] | _O(n)_ | _O(1)_ | Medium | -[Construct Binary Tree from Inorder and Postorder Traversal] | [construct-binary-tree-from-inorder-and-postorder-traversal.py] | _O(n)_ | _O(n)_ | Medium | -[Construct Binary Tree from Preorder and Inorder Traversal] | [construct-binary-tree-from-preorder-and-inorder-traversal.py] | _O(n)_ | _O(n)_ | Medium -[Convert Sorted Array to Binary Search Tree] | [convert-sorted-array-to-binary-search-tree.py] | _O(n)_ | _O(logn)_ | Medium | -[Convert Sorted List to Binary Search Tree] | [convert-sorted-list-to-binary-search-tree.py] | _O(n)_ | _O(logn)_ | Medium | -[Flatten Binary Tree to Linked List]|[flatten-binary-tree-to-linked-list.py]| _O(n)_ | _O(logn)_ | Medium | -[Maximum Depth of Binary Tree]|[maximum-depth-of-binary-tree.py]| _O(n)_ | _O(logn)_ | Easy | -[Minimum Depth of Binary Tree]|[minimum-depth-of-binary-tree.py]| _O(n)_ | _O(logn)_ | Easy | -[Populating Next Right Pointers in Each Node]|[populating-next-right-pointers-in-each-node.py]| _O(n)_ | _O(logn)_ | Medium | -[Same Tree] |[same-tree.py] | _O(n)_ | _O(logn)_ | Easy | -[Sum Root to Leaf Numbers] | [sum-root-to-leaf-numbers.py] | _O(n)_ | _O(logn)_ | Medium | -[Unique Binary Search Trees II] | [unique-binary-search-trees-ii.py] | _O(4^n / n^(3/2)_ | _O(4^n / n^(3/2)_ | Medium | -[Validate Binary Search Tree]|[validate-binary-search-tree.py]| _O(n)_ | _O(logn)_ | Medium | - -[Balanced Binary Tree]:https://oj.leetcode.com/problems/balanced-binary-tree/ -[balanced-binary-tree.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/balanced-binary-tree.py -[Binary Tree Maximum Path Sum]:https://oj.leetcode.com/problems/binary-tree-maximum-path-sum/ -[binary-tree-maximum-path-sum.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/binary-tree-maximum-path-sum.py -[Binary Tree Upside Down]:https://oj.leetcode.com/problems/binary-tree-upside-down/ -[binary-tree-upside-down.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/binary-tree-upside-down.py -[Construct Binary Tree from Inorder and Postorder Traversal]:https://oj.leetcode.com/problems/construct-binary-tree-from-inorder-and-postorder-traversal/ -[construct-binary-tree-from-inorder-and-postorder-traversal.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/construct-binary-tree-from-inorder-and-postorder-traversal.py -[Construct Binary Tree from Preorder and Inorder Traversal]:https://oj.leetcode.com/problems/construct-binary-tree-from-preorder-and-inorder-traversal/ -[construct-binary-tree-from-preorder-and-inorder-traversal.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/construct-binary-tree-from-preorder-and-inorder-traversal.py -[Convert Sorted Array to Binary Search Tree]:https://oj.leetcode.com/problems/convert-sorted-array-to-binary-search-tree/ -[convert-sorted-array-to-binary-search-tree.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/convert-sorted-array-to-binary-search-tree.py -[Convert Sorted List to Binary Search Tree]:https://oj.leetcode.com/problems/convert-sorted-list-to-binary-search-tree/ -[convert-sorted-list-to-binary-search-tree.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/convert-sorted-list-to-binary-search-tree.py -[Flatten Binary Tree to Linked List]:https://oj.leetcode.com/problems/flatten-binary-tree-to-linked-list/ -[flatten-binary-tree-to-linked-list.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/flatten-binary-tree-to-linked-list.py -[Maximum Depth of Binary Tree]:https://oj.leetcode.com/problems/maximum-depth-of-binary-tree/ -[maximum-depth-of-binary-tree.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/maximum-depth-of-binary-tree.py -[Minimum Depth of Binary Tree]:https://oj.leetcode.com/problems/minimum-depth-of-binary-tree/ -[minimum-depth-of-binary-tree.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/minimum-depth-of-binary-tree.py -[Populating Next Right Pointers in Each Node]:https://oj.leetcode.com/problems/populating-next-right-pointers-in-each-node/ -[populating-next-right-pointers-in-each-node.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/populating-next-right-pointers-in-each-node.py -[Same Tree]:https://oj.leetcode.com/problems/same-tree/ -[same-tree.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/same-tree.py -[Sum Root to Leaf Numbers]:https://oj.leetcode.com/problems/sum-root-to-leaf-numbers/ -[sum-root-to-leaf-numbers.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/sum-root-to-leaf-numbers.py -[Unique Binary Search Trees II]:https://oj.leetcode.com/problems/unique-binary-search-trees-ii/ -[unique-binary-search-trees-ii.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/unique-binary-search-trees-ii.py -[Validate Binary Search Tree]:https://oj.leetcode.com/problems/validate-binary-search-tree.py/ -[validate-binary-search-tree.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/validate-binary-search-tree.py - - ---- - -##Binary Search - -Problem | Solution | Time | Space | Difficulty | Notes ---------------- | --------------- | --------------- | --------------- | -------------- | ----- -[Find Minimum in Rotated Sorted Array] | [find-minimum-in-rotated-sorted-array.py] | _O(logn)_ | _O(1)_ | Medium | -[Find Minimum in Rotated Sorted Array II] | [find-minimum-in-rotated-sorted-array-ii.py] | _O(logn)_ ~ _O(n)_ | _O(1)_ | Hard | -[Find Peak Element] | [find-peak-element.py] | _O(logn)_ | _O(1)_ | Medium | -[Median of Two Sorted Arrays] | [median-of-two-sorted-arrays.py] | _O(log(m + n)_ | _O(log(m + n)_ | Hard | -[Pow(x, n)] | [powx-n.py] | _O(logn)_ | _O(logn)_ | Medium | -[Search a 2D Matrix] | [search-a-2d-matrix.py] | _O(log m + logn)_ | _O(1)_ | Medium | -[Search for a Range] | [search-for-a-range.py] | _O(logn)_ | _O(1)_ | Medium | -[Search in Rotated Sorted Array] | [search-in-rotated-sorted-array.py] | _O(logn)_ | _O(1)_ | Hard | -[Search in Rotated Sorted Array II] | [search-in-rotated-sorted-array-ii.py] | _O(logn)_ | _O(1)_ | Medium | -[Search Insert Position] | [search-insert-position.py] | _O(logn)_ | _O(1)_ | Medium | -[Sqrt(x)] | [sqrtx.py] | _O(logn)_ | _O(1)_ | Medium | - -[Find Minimum in Rotated Sorted Array]:https://oj.leetcode.com/problems/find-minimum-in-rotated-sorted-array/ -[find-minimum-in-rotated-sorted-array.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/find-minimum-in-rotated-sorted-array.py -[Find Minimum in Rotated Sorted Array II]:https://oj.leetcode.com/problems/find-minimum-in-rotated-sorted-array-ii/ -[find-minimum-in-rotated-sorted-array-ii.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/find-minimum-in-rotated-sorted-array-ii.py -[Find Peak Element]:https://oj.leetcode.com/problems/find-peak-element/ -[find-peak-element.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/find-peak-element.py -[Median of Two Sorted Arrays]:https://oj.leetcode.com/problems/median-of-two-sorted-arrays/ -[median-of-two-sorted-arrays.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/median-of-two-sorted-arrays.py -[Pow(x, n)]:https://oj.leetcode.com/problems/powx-n/ -[powx-n.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/powx-n.py -[Search a 2D Matrix]:https://oj.leetcode.com/problems/search-a-2d-matrix/ -[search-a-2d-matrix.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/search-a-2d-matrix.py -[Search for a Range]:https://oj.leetcode.com/problems/search-for-a-range/ -[search-for-a-range.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/search-for-a-range.py -[Search in Rotated Sorted Array]:https://oj.leetcode.com/problems/search-in-rotated-sorted-array/ -[search-in-rotated-sorted-array.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/search-in-rotated-sorted-array.py -[Search in Rotated Sorted Array II]:https://oj.leetcode.com/problems/search-in-rotated-sorted-array-ii/ -[search-in-rotated-sorted-array-ii.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/search-in-rotated-sorted-array-ii.py -[Search Insert Position]:https://oj.leetcode.com/problems/search-insert-position/ -[search-insert-position.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/search-insert-position.py -[Sqrt(x)]:https://oj.leetcode.com/problems/sqrtx/ -[sqrtx.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/sqrtx.py - ---- - -##Breadth-First Search -Problem | Solution | Time | Space | Difficulty | Notes ---------------- | --------------- | --------------- | --------------- | -------------- | ----- -[Binary Tree Level Order Traversal]| [binary-tree-level-order-traversal.py] | _O(n)_| _O(n)_| Easy | -[Binary Tree Level Order Traversal II]| [binary-tree-level-order-traversal-ii.py] | _O(n)_| _O(n)_| Easy | -[Binary Tree Zigzag Level Order Traversal]| [binary-tree-zigzag-level-order-traversal.py] | _O(n)_| _O(n)_| Medium | -[Clone Graph]| [clone-graph.py] | _O(n)_ | _O(n)_ | Medium | -[Populating Next Right Pointers in Each Node II]|[populating-next-right-pointers-in-each-node-ii.py]| _O(n)_ | _O(1)_ | Hard | -[Surrounded Regions]|[surrounded-regions.py]| _O(m * n)_ | _O(m + n)_ | Medium | -[Word Ladder] |[word-ladder.py] | _O((25n)^n)_ | _O((25n)^n)_ | Medium | - -[Binary Tree Level Order Traversal]:https://oj.leetcode.com/problems/binary-tree-level-order-traversal/ -[binary-tree-level-order-traversal.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/binary-tree-level-order-traversal.py -[Binary Tree Level Order Traversal II]:https://oj.leetcode.com/problems/binary-tree-level-order-traversal-ii/ -[binary-tree-level-order-traversal-ii.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/binary-tree-level-order-traversal-ii.py -[Binary Tree Zigzag Level Order Traversal]:https://oj.leetcode.com/problems/binary-tree-zigzag-level-order-traversal/ -[binary-tree-zigzag-level-order-traversal.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/binary-tree-zigzag-level-order-traversal.py -[Clone Graph]:https://oj.leetcode.com/problems/clone-graph/ -[clone-graph.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/clone-graph.py -[Populating Next Right Pointers in Each Node II]:https://oj.leetcode.com/problems/populating-next-right-pointers-in-each-node-ii/ -[populating-next-right-pointers-in-each-node-ii.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/populating-next-right-pointers-in-each-node-ii.py -[Surrounded Regions]:https://oj.leetcode.com/problems/surrounded-regions/ -[surrounded-regions.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/surrounded-regions.py -[Word Ladder]:https://oj.leetcode.com/problems/word-ladder/ -[word-ladder.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/word-ladder.py - ---- - -##Depth-First Search -Problem | Solution | Time | Space | Difficulty | Notes ---------------- | --------------- | --------------- | --------------- | -------------- | ----- -[Combination Sum]| [combination-sum.py] | _O(n^m)_ | _O(m)_ | Medium | -[Combination Sum II]| [combination-sum-ii.py]| _O(n! / m!(n-m)!)_| _O(m)_ | Medium | -[Combinations] | [combinations.py] | _O(n!)_ | _O(n)_ | Medium | -[Generate Parentheses]| [generate-parentheses.py]| _O(4^n / n^(3/2)_ | _O(n)_ | Medium | -[N-Queens] | [n-queens.py] | _O(n!)_ | _O(n)_ | Hard | -[N-Queens-II] | [n-queens-ii.py] | _O(n!)_ | _O(n)_ | Hard | -[Palindrome Partitioning] | [palindrome-partitioning.py] | _O(n^2)_ ~ _O(2^n)_ | _O(n^2)_ | Medium | -[Path Sum] | [path-sum.py] | _O(n)_ | _O(logn)_ | Easy | -[Path Sum II] | [path-sum-ii.py] | _O(n)_ | _O(logn)_ | Medium | -[Restore IP Addresses] | [restore-ip-addresses.py] | _O(n^m)_ ~ _O(3^4)_ | _O(n * m)_ ~ _O(3 * 4)_ | Medium | -[Sudoku Solver] | [sudoku-solver.py] | _O((9!)^9)_ | _O(1)_ | Hard | -[Word Search] | [word-search.py] | _O(m * n * 3^p)_ | _O(m * n * p)_ | Medium | - -[Combination Sum]:https://oj.leetcode.com/problems/combination-sum/ -[combination-sum.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/combination-sum.py -[Combination Sum II]:https://oj.leetcode.com/problems/combination-sum-ii/ -[combination-sum-ii.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/combination-sum-ii.py -[Combinations]:https://oj.leetcode.com/problems/combinations/ -[combinations.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/combinations.py -[Generate Parentheses]:https://oj.leetcode.com/problems/generate-parentheses/ -[generate-parentheses.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/generate-parentheses.py -[N-Queens]:https://oj.leetcode.com/problems/n-queens/ -[n-queens.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/n-queens.py -[N-Queens-II]:https://oj.leetcode.com/problems/n-queens-ii/ -[n-queens-ii.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/n-queens-ii.py -[Palindrome Partitioning]:https://oj.leetcode.com/problems/palindrome-partitioning/ -[palindrome-partitioning.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/palindrome-partitioning.py -[Path Sum]:https://oj.leetcode.com/problems/path-sum/ -[path-sum.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/path-sum.py -[Path Sum II]:https://oj.leetcode.com/problems/path-sum-ii/ -[path-sum-ii.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/path-sum-ii.py -[Restore IP Addresses]:https://oj.leetcode.com/problems/restore-ip-addresses/ -[restore-ip-addresses.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/restore-ip-addresses.py -[Sudoku Solver]:https://oj.leetcode.com/problems/sudoku-solver/ -[sudoku-solver.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/sudoku-solver.py -[Word Search]:https://oj.leetcode.com/problems/word-search/ -[word-search.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/word-search.py - ---- - -##Dynamic Programming -Problem | Solution | Time | Space | Difficulty | Notes ---------------- | --------------- | --------------- | --------------- | -------------- | ----- -[Best Time to Buy and Sell Stock III]| [best-time-to-buy-and-sell-stock-iii.py] | _O(n)_ | _O(1)_ | Hard | -[Climbing Stairs]| [climbing-stairs.py] | _O(n)_ | _O(1)_ | Easy | -[Decode Ways] | [decode-ways.py]| _O(n)_ | _O(1)_ | Medium | -[Distinct Subsequences]|[distinct-subsequences.py]| _O(n^2)_ | _O(n)_ | Hard | -[Dungeon Game] | [dungeon-game.py]| _O(m * n)_ | _O(m + n)_ | Hard | -[Edit Distance]|[edit-distance.py]| _O(m * n)_ | _O(m + n)_ | Hard | -[Interleaving String]|[interleaving-string.py]| _O(m * n)_ | _O(m + n)_ | Hard | -[Maximal Rectangle]|[maximal-rectangle.py]| _O(n^2)_ | _O(n)_ | Hard | -[Maximum Product Subarray]|[maximum-product-subarray.py]| _O(n)_ | _O(1)_ | Medium | -[Maximum Subarray]|[maximum-subarray.py]| _O(n)_ | _O(1)_ | Medium | -[Minimum Path Sum]|[minimum-path-sum.py]| _O(m * n)_ | _O(m + n)_ | Medium | -[Palindrome Partitioning II] | [palindrome-partitioning-ii.py] | _O(n^2)_ | _O(n^2)_ | Hard | -[Regular Expression Matching] | [regular-expression-matching.py] | _O(m * n)_ | _O(n)_ | Hard | -[Scramble String] | [scramble-string.py] | _O(n^4)_ | _O(n^3)_ | Hard | -[Triangle] | [triangle.py] | _O(m * n)_ | _O(n)_ | Medium | -[Unique Binary Search Trees] | [unique-binary-search-trees.py] | _O(n^2)_ | _O(n)_ | Medium | -[Unique Paths] | [unique-paths.py]| _O(m * n)_ | _O(m + n)_ | Medium | -[Unique Paths II] | [unique-paths-ii.py] | _O(m * n)_ | _O(m + n)_ | Medium | -[Word Break] | [word-break.py] | _O(n^2)_ | _O(n)_ | Medium | -[Word Break II] | [word-break-ii.py] | _O(n^2)_ | _O(n)_ | Hard | - -[Best Time to Buy and Sell Stock III]:https://oj.leetcode.com/problems/best-time-to-buy-and-sell-stock-iii/ -[best-time-to-buy-and-sell-stock-iii.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/best-time-to-buy-and-sell-stock-iii.py -[Climbing Stairs]:https://oj.leetcode.com/problems/climbing-stairs/ -[climbing-stairs.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/climbing-stairs.py -[Decode Ways]:https://oj.leetcode.com/problems/decode-ways/ -[decode-ways.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/decode-ways.py -[Distinct Subsequences]:https://oj.leetcode.com/problems/distinct-subsequences/ -[distinct-subsequences.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/distinct-subsequences.py -[Dungeon Game]:https://oj.leetcode.com/problems/dungeon-game/ -[dungeon-game.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/dungeon-game.py -[Edit Distance]:https://oj.leetcode.com/problems/edit-distance/ -[edit-distance.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/edit-distance.py -[Interleaving String]:https://oj.leetcode.com/problems/interleaving-string/ -[interleaving-string.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/interleaving-string.py -[Maximal Rectangle]:https://oj.leetcode.com/problems/maximal-rectangle/ -[maximal-rectangle.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/maximal-rectangle.py -[Maximum Product Subarray]:https://oj.leetcode.com/problems/maximum-product-subarray/ -[maximum-product-subarray.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/maximum-product-subarray.py -[Maximum Subarray]:https://oj.leetcode.com/problems/maximum-subarray/ -[maximum-subarray.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/maximum-subarray.py -[Minimum Path Sum]:https://oj.leetcode.com/problems/minimum-path-sum/ -[minimum-path-sum.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/minimum-path-sum.py -[Palindrome Partitioning II]:https://oj.leetcode.com/problems/palindrome-partitioning-ii/ -[palindrome-partitioning-ii.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/palindrome-partitioning-ii.py -[Regular Expression Matching]:https://oj.leetcode.com/problems/regular-expression-matching/ -[regular-expression-matching.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/regular-expression-matching.py -[Scramble String]:https://oj.leetcode.com/problems/scramble-string/ -[scramble-string.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/scramble-string.py -[Triangle]:https://oj.leetcode.com/problems/triangle/ -[triangle.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/triangle.py -[Unique Binary Search Trees]:https://oj.leetcode.com/problems/unique-binary-search-trees/ -[unique-binary-search-trees.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/unique-binary-search-trees.py -[Unique Paths]:https://oj.leetcode.com/problems/unique-paths/ -[unique-paths.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/unique-paths.py -[Unique Paths II]:https://oj.leetcode.com/problems/unique-paths-ii/ -[unique-paths-ii.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/unique-paths-ii.py -[Word Break]:https://oj.leetcode.com/problems/word-break/ -[word-break.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/word-break.py -[Word Break II]:https://oj.leetcode.com/problems/word-break-ii/ -[word-break-ii.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/word-break-ii.py - ---- - -##Backtracking -Problem | Solution | Time | Space | Difficulty | Notes ---------------- | --------------- | --------------- | --------------- | -------------- | ----- -[Word Ladder II] |[word-ladder-ii.py] | _O((25n)^n)_ | _O((25n)^n)_ | Hard | - -[Word Ladder II]:https://oj.leetcode.com/problems/word-ladder-ii/ -[word-ladder-ii.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/word-ladder-ii.py - ---- - -##Greedy -Problem | Solution | Time | Space | Difficulty | Notes ---------------- | --------------- | --------------- | --------------- | -------------- | ----- -[Best Time to Buy and Sell Stock II]| [best-time-to-buy-and-sell-stock-ii.py] | _O(n)_ | _O(1)_ | Medium | -[Candy]| [candy.py] | _O(n)_ | _O(n)_ | Hard | -[Container With Most Water]| [container-with-most-water.py] | _O(n)_ | _O(1)_ | Medium | -[Gas Station]| [gas-station.py] | _O(n)_ | _O(1)_ | Medium | -[Jump Game] | [jump-game.py] | _O(n)_ | _O(1)_ | Medium | -[Jump Game II] | [jump-game-ii.py] | _O(n^2)_ | _O(1)_ | Hard | -[Largest Rectangle in Histogram] | [largest-rectangle-in-histogram.py] | _O(n)_ | _O(n)_ | Hard | Tricky -[Trapping Rain Water] | [trapping-rain-water.py] | _O(n)_ | _O(1)_ | Hard | Tricky -[Wildcard Matching] | [wildcard-matching.py] | _O(m + n)_ | _O(1)_ | Hard | Tricky - -[Best Time to Buy and Sell Stock II]:https://oj.leetcode.com/problems/best-time-to-buy-and-sell-stock-ii/ -[best-time-to-buy-and-sell-stock-ii.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/best-time-to-buy-and-sell-stock-ii.py -[Candy]:https://oj.leetcode.com/problems/candy/ -[candy.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/candy.py -[Container With Most Water]:https://oj.leetcode.com/problems/container-with-most-water/ -[container-with-most-water.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/container-with-most-water.py -[Gas Station]:https://oj.leetcode.com/problems/gas-station/ -[gas-station.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/gas-station.py -[Jump Game]:https://oj.leetcode.com/problems/jump-game/ -[jump-game.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/jump-game.py -[Jump Game II]:https://oj.leetcode.com/problems/jump-game-ii/ -[jump-game-ii.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/jump-game-ii.py -[Largest Rectangle in Histogram]:https://oj.leetcode.com/problems/largest-rectangle-in-histogram/ -[largest-rectangle-in-histogram.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/largest-rectangle-in-histogram.py -[Trapping Rain Water]:https://oj.leetcode.com/problems/trapping-rain-water/ -[trapping-rain-water.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/trapping-rain-water.py -[Wildcard Matching]:https://oj.leetcode.com/problems/wildcard-matching/ -[wildcard-matching.py]:https://github.com/kamyu104/LeetCode/blob/master/Python/wildcard-matching.py - ---- - -##SQL -Problem | Solution | Time | Space | Difficulty | Notes ---------------- | --------------- | --------------- | --------------- | -------------- | ----- -[Combine Two Tables] | [combine-two-tables.sql] | _O(m + n)_ | _O(m + n)_ | Easy | -[Consecutive Numbers] | [consecutive-numbers.sql] | _O(n)_ | _O(n)_ | Medium | -[Nth Highest Salary] | [nth-highest-salary.sql] | _O(n^2)_ | _O(n)_ | Medium | -[Rank Scores] | [rank-scores.sql] | _O(n^2)_ | _O(n)_ | Medium | -[Second Highest Salary] | [second-highest-salary.sql] | _O(n)_ | _O(1)_ | Easy | - -[Combine Two Tables]:https://oj.leetcode.com/problems/combine-two-tables/ -[combine-two-tables.sql]:https://github.com/kamyu104/LeetCode/blob/master/MySQL/combine-two-tables.sql -[Consecutive Numbers]:https://oj.leetcode.com/problems/consecutive-numbers/ -[consecutive-numbers.sql]:https://github.com/kamyu104/LeetCode/blob/master/MySQL/consecutive-numbers.sql -[Nth Highest Salary]:https://oj.leetcode.com/problems/nth-highest-salary/ -[nth-highest-salary.sql]:https://github.com/kamyu104/LeetCode/blob/master/MySQL/nth-highest-salary.sql -[Rank Scores]:https://oj.leetcode.com/problems/rank-scores/ -[rank-scores.sql]:https://github.com/kamyu104/LeetCode/blob/master/MySQL/rank-scores.sql -[Second Highest Salary]:https://oj.leetcode.com/problems/second-highest-salary/ -[second-highest-salary.sql]:https://github.com/kamyu104/LeetCode/blob/master/MySQL/second-highest-salary.sql +## Shell + +* [Shell Script](https://github.com/kamyu104/LeetCode#shell-script) + +## Reference + +* C++ + * [STL Time Complexity (Detailed)](http://www.cplusplus.com/reference/stl/) + * [STL Time Complexity (Summary)](http://john-ahlgren.blogspot.com/2013/10/stl-container-performance.html) + * [Data Structure and Algorithms Cheat Sheet](https://github.com/gibsjose/cpp-cheat-sheet/blob/master/Data%20Structures%20and%20Algorithms.md) +* Python + * [Time Complexity](https://wiki.python.org/moin/TimeComplexity) + +## Bit Manipulation +| # | Title | Solution | Time | Space | Difficulty | Tag | Note| +|-----|---------------- | --------------- | --------------- | --------------- | ------------- |--------------|-----| +136 | [Single Number](https://leetcode.com/problems/single-number/) | [C++](./C++/single-number.cpp) [Python](./Python/single-number.py) | _O(n)_ | _O(1)_ | Easy ||| +137 | [Single Number II](https://leetcode.com/problems/single-number-ii/) | [C++](./C++/single-number-ii.cpp) [Python](./Python/single-number-ii.py) | _O(n)_ | _O(1)_ | Medium ||| +190 | [Reverse Bits](https://leetcode.com/problems/reverse-bits/) | [C++](./C++/reverse-bits.cpp) [Python](./Python/reverse-bits.py) | _O(1)_ | _O(1)_ | Easy ||| +191 |[Number of 1 Bits](https://leetcode.com/problems/number-of-1-bits/) | [C++](./C++/number-of-1-bits.cpp) [Python](./Python/number-of-1-bits.py) | _O(1)_ | _O(1)_ | Easy ||| +201 | [Bitwise AND of Numbers Range](https://leetcode.com/problems/bitwise-and-of-numbers-range/) | [C++](./C++/bitwise-and-of-numbers-range.cpp) [Python](./Python/bitwise-and-of-numbers-range.py) | _O(1)_ | _O(1)_ | Medium || +231 | [Power of Two](https://leetcode.com/problems/power-of-two/) | [C++](./C++/power-of-two.cpp) [Python](./Python/power-of-two.py) | _O(1)_ | _O(1)_ | Easy | LintCode | +260 | [Single Number III](https://leetcode.com/problems/single-number-iii/) | [C++](./C++/single-number-iii.cpp) [Python](./Python/single-number-iii.py) | _O(n)_ | _O(1)_ | Medium || +268| [Missing Number](https://leetcode.com/problems/missing-number/) | [C++](./C++/missing-number.cpp) [Python](./Python/missing-number.py) | _O(n)_ | _O(1)_ | Medium | LintCode || +318| [Maximum Product of Word Lengths](https://leetcode.com/problems/maximum-product-of-word-lengths/) | [C++](./C++/maximum-product-of-word-lengths.cpp) [Python](./Python/maximum-product-of-word-lengths.py) | _O(n)_ ~ _O(n^2)_ | _O(n)_ | Medium || Bit Manipulation, Counting Sort, Pruning| +342 | [Power of Four](https://leetcode.com/problems/power-of-four/) | [C++](./C++/power-of-four.cpp) [Python](./Python/power-of-four.py) | _O(1)_ | _O(1)_ | Easy | | +371 | [Sum of Two Integers](https://leetcode.com/problems/sum-of-two-integers/) | [C++](./C++/sum-of-two-integers.cpp) [Python](./Python/sum-of-two-integers.py) | _O(1)_ | _O(1)_ | Easy | LintCode | +389 | [Find the Difference](https://leetcode.com/problems/find-the-difference/) | [C++](./C++/find-the-difference.cpp) [Python](./Python/find-the-difference.py) | _O(n)_ | _O(1)_ | Easy | | +393 | [UTF-8 Validation](https://leetcode.com/problems/utf-8-validation/) | [C++](./C++/utf-8-validation.cpp) [Python](./Python/utf-8-validation.py) | _O(n)_ | _O(1)_ | Medium | | +401 | [Binary Watch](https://leetcode.com/problems/binary-watch/) | [C++](./C++/binary-watch.cpp) [Python](./Python/binary-watch.py) | _O(1)_ | _O(1)_ | Easy | | +411 | [Minimum Unique Word Abbreviation](https://leetcode.com/problems/minimum-unique-word-abbreviation/) | [C++](./C++/minimum-unique-word-abbreviation.cpp) [Python](./Python/minimum-unique-word-abbreviation.py) | _O(2^n)_ | _O(n)_ | Hard | 📖 | +421 | [Maximum XOR of Two Numbers in an Array](https://leetcode.com/problems/maximum-xor-of-two-numbers-in-an-array/) | [C++](./C++/maximum-xor-of-two-numbers-in-an-array.cpp) [Python](./Python/maximum-xor-of-two-numbers-in-an-array.py) | _O(n)_ | _O(1)_ | Medium || +461 | [Hamming Distance](https://leetcode.com/problems/hamming-distance/) | [C++](./C++/hamming-distance.cpp) [Python](./Python/hamming-distance.py) | _O(1)_ | _O(1)_ | Easy || +462 | [Minimum Moves to Equal Array Elements II](https://leetcode.com/problems/minimum-moves-to-equal-array-elements-ii/) | [C++](./C++/minimum-moves-to-equal-array-elements-ii.cpp) [Python](./Python/minimum-moves-to-equal-array-elements-ii.py) | _O(n)_ on average | _O(1)_ | Medium || +477 | [Total Hamming Distance](https://leetcode.com/problems/total-hamming-distance/) | [C++](./C++/total-hamming-distance.cpp) [Python](./Python/total-hamming-distance.py) | _O(n)_ | _O(1)_ | Medium || +645 | [Set Mismatch](https://leetcode.com/problems/set-mismatch/) | [C++](./C++/set-mismatch.cpp) [Python](./Python/set-mismatch.py) | _O(n)_ | _O(1)_ | Easy || +693 | [Binary Number with Alternating Bits](https://leetcode.com/problems/binary-number-with-alternating-bits/) | [C++](./C++/binary-number-with-alternating-bits.cpp) [Python](./Python/binary-number-with-alternating-bits.py) | _O(1)_ | _O(1)_ | Easy || +762 | [Prime Number of Set Bits in Binary Representation](https://leetcode.com/problems/prime-number-of-set-bits-in-binary-representation/) | [C++](./C++/prime-number-of-set-bits-in-binary-representation.cpp) [Python](./Python/prime-number-of-set-bits-in-binary-representation.py) | _O(1)_ | _O(1)_ | Easy || +868 | [Binary Gap](https://leetcode.com/problems/binary-gap/) | [C++](./C++/binary-gap.cpp) [Python](./Python/binary-gap.py) | _O(1)_ | _O(1)_ | Easy || + +## Array +| # | Title | Solution | Time | Space | Difficulty | Tag | Note| +|-----|---------------- | --------------- | --------------- | --------------- | ------------- |--------------|-----| +015 | [3 Sum](https://leetcode.com/problems/3sum/) | [C++](./C++/3sum.cpp) [Python](./Python/3sum.py) | _O(n^2)_ | _O(1)_ | Medium || Two Pointers +016 | [3 Sum Closest](https://leetcode.com/problems/3sum-closest/) | [C++](./C++/3sum-closest.cpp) [Python](./Python/3sum-closest.py) | _O(n^2)_ | _O(1)_ | Medium || Two Pointers +018| [4 Sum](https://leetcode.com/problems/4sum/) | [C++](./C++/4sum.cpp) [Python](./Python/4sum.py) | _O(n^3)_ | _O(1)_ | Medium || Two Pointers +026 | [Remove Duplicates from Sorted Array](https://leetcode.com/problems/remove-duplicates-from-sorted-array/)| [C++](./C++/remove-duplicates-from-sorted-array.cpp) [Python](./Python/remove-duplicates-from-sorted-array.py) | _O(n)_ | _O(1)_ | Easy || Two Pointers +027 | [Remove Element](https://leetcode.com/problems/remove-element/) | [C++](./C++/remove-element.cpp) [Python](./Python/remove-element.py) | _O(n)_ | _O(1)_ | Easy || +031 | [Next Permutation](https://leetcode.com/problems/next-permutation/)| [C++](./C++/next-permutation.cpp) [Python](./Python/next-permutation.py) | _O(n)_ | _O(1)_ | Medium || Tricky +041 | [First Missing Positive](https://leetcode.com/problems/first-missing-positive/)| [C++](./C++/first-missing-positive.cpp) [Python](./Python/first-missing-positive.py) | _O(n)_ | _O(1)_ | Hard || Tricky +048 | [Rotate Image](https://leetcode.com/problems/rotate-image/) | [C++](./C++/rotate-image.cpp) [Python](./Python/rotate-image.py) | _O(n^2)_ | _O(1)_ | Medium || +054 | [Spiral Matrix](https://leetcode.com/problems/spiral-matrix/) | [C++](./C++/spiral-matrix.cpp) [Python](./Python/spiral-matrix.py) | _O(m * n)_ | _O(1)_ | Medium || +059 | [Spiral Matrix II](https://leetcode.com/problems/spiral-matrix-ii/) | [C++](./C++/spiral-matrix-ii.cpp) [Python](./Python/spiral-matrix-ii.py) | _O(n^2)_ | _O(1)_ | Medium || +066 | [Plus One](https://leetcode.com/problems/plus-one/) | [C++](./C++/plus-one.cpp) [Python](./Python/plus-one.py) | _O(n)_ | _O(1)_ | Easy || +073 | [Set Matrix Zeroes](https://leetcode.com/problems/set-matrix-zeroes/) | [C++](./C++/set-matrix-zeroes.cpp) [Python](./Python/set-matrix-zeroes.py) | _O(m * n)_ | _O(1)_ | Medium || +080 | [Remove Duplicates from Sorted Array II](https://leetcode.com/problems/remove-duplicates-from-sorted-array-ii/)| [C++](./C++/remove-duplicates-from-sorted-array-ii.cpp) [Python](./Python/remove-duplicates-from-sorted-array-ii.py) | _O(n)_ | _O(1)_ | Medium || Two Pointers +118 | [Pascal's Triangle](https://leetcode.com/problems/pascals-triangle/)| [C++](./C++/pascals-triangle.cpp) [Python](./Python/pascals-triangle.py) | _O(n^2)_ | _O(1)_ | Easy || +119 | [Pascal's Triangle II](https://leetcode.com/problems/pascals-triangle-ii/)| [C++](./C++/pascals-triangle-ii.cpp) [Python](./Python/pascals-triangle-ii.py) | _O(n^2)_ | _O(1)_ | Easy || +121 | [Best Time to Buy and Sell Stock](https://leetcode.com/problems/best-time-to-buy-and-sell-stock/)| [C++](./C++/best-time-to-buy-and-sell-stock.cpp) [Python](./Python/best-time-to-buy-and-sell-stock.py) | _O(n)_ | _O(1)_ | Easy || +128 | [Longest Consecutive Sequence](https://leetcode.com/problems/longest-consecutive-sequence/)| [C++](./C++/longest-consecutive-sequence.cpp) [Python](./Python/longest-consecutive-sequence.py) | _O(n)_ | _O(n)_ | Hard || Tricky +157 | [Read N Characters Given Read4](https://leetcode.com/problems/read-n-characters-given-read4/) | [C++](./C++/read-n-characters-given-read4.cpp) [Python](./Python/read-n-characters-given-read4.py) | _O(n)_ | _O(1)_ | Easy |📖| +158 | [Read N Characters Given Read4 II - Call multiple times](https://leetcode.com/problems/read-n-characters-given-read4-ii-call-multiple-times/) | [C++](./C++/read-n-characters-given-read4-ii-call-multiple-times.cpp) [Python](./Python/read-n-characters-given-read4-ii-call-multiple-times.py) | _O(n)_ | _O(1)_ | Hard |📖| +163 | [Missing Ranges](https://leetcode.com/problems/missing-ranges/)| [C++](./C++/missing-ranges.cpp) [Python](./Python/missing-ranges.py) | _O(n)_ | _O(1)_ | Medium | 📖 | +169 | [Majority Element](https://leetcode.com/problems/majority-element/) | [C++](./C++/majority-element.cpp) [Python](./Python/majority-element.py) | _O(n)_ | _O(1)_ | Easy | +189 | [Rotate Array](https://leetcode.com/problems/rotate-array/) | [C++](./C++/rotate-array.cpp) [Python](./Python/rotate-array.py) | _O(n)_ | _O(1)_ | Easy || +209 | [Minimum Size Subarray Sum](https://leetcode.com/problems/minimum-size-subarray-sum/) | [C++](./C++/minimum-size-subarray-sum.cpp) [Python](./Python/minimum-size-subarray-sum.py) | _O(n)_ | _O(1)_ | Medium | | Binary Search +215 | [Kth Largest Element in an Array](https://leetcode.com/problems/kth-largest-element-in-an-array/) | [C++](./C++/kth-largest-element-in-an-array.cpp) [Python](./Python/kth-largest-element-in-an-array.py)| _O(n)_ ~ _O(n^2)_ | _O(1)_ | Medium | EPI| +228 | [Summary Ranges](https://leetcode.com/problems/summary-ranges/) | [C++](./C++/summary-ranges.cpp) [Python](./Python/summary-ranges.py)| _O(n)_ | _O(1)_ | Medium | | +229 | [Majority Element II](https://leetcode.com/problems/majority-element-ii/) | [C++](./C++/majority-element-ii.cpp) [Python](./Python/majority-element-ii.py) | _O(n)_ | _O(1)_ | Medium | | +238 | [Product of Array Except Self](https://leetcode.com/problems/product-of-array-except-self/) | [C++](./C++/product-of-array-except-self.cpp) [Python](./Python/product-of-array-except-self.py) | _O(n)_ | _O(1)_ | Medium | LintCode | +240 | [Search a 2D Matrix II](https://leetcode.com/problems/search-a-2d-matrix-ii/) | [C++](./C++/search-a-2d-matrix-ii.cpp) [Python](./Python/search-a-2d-matrix-ii.py) | _O(m + n)_ | _O(1)_ | Medium | EPI, LintCode | +243| [Shortest Word Distance](https://leetcode.com/problems/shortest-word-distance/) | [C++](./C++/shortest-word-distance.cpp) [Python](./Python/shortest-word-distance.py) | _O(n)_ | _O(1)_ | Easy |📖|| +245| [Shortest Word Distance III](https://leetcode.com/problems/shortest-word-distance-iii/) | [C++](./C++/shortest-word-distance-iii.cpp) [Python](./Python/shortest-word-distance-iii.py) | _O(n)_ | _O(1)_ | Medium |📖|| +251| [Flatten 2D Vector](https://leetcode.com/problems/flatten-2d-vector/) | [C++](./C++/flatten-2d-vector.cpp) [Python](./Python/flatten-2d-vector.py) | _O(1)_ | _O(1)_ | Medium |📖|| +277| [Find the Celebrity](https://leetcode.com/problems/find-the-celebrity/) | [C++](./C++/find-the-celebrity.cpp) [Python](./Python/find-the-celebrity.py) | _O(n)_ | _O(1)_ | Medium |📖, EPI || +289| [Game of Life](https://leetcode.com/problems/game-of-life/) | [C++](./C++/game-of-life.cpp) [Python](./Python/game-of-life.py) | _O(m * n)_ | _O(1)_ | Medium ||| +293| [Flip Game](https://leetcode.com/problems/flip-game/) | [C++](./C++/flip-game.cpp) [Python](./Python/flip-game.py) | _O(n * (c+1))_ | _O(1)_ | Easy |📖|| +296| [Best Meeting Point](https://leetcode.com/problems/best-meeting-point/) | [C++](./C++/best-meeting-point.cpp) [Python](./Python/best-meeting-point.py) | _O(m * n)_ | _O(m + n)_ | Hard |📖|| +311| [Sparse Matrix Multiplication](https://leetcode.com/problems/sparse-matrix-multiplication/) | [C++](./C++/sparse-matrix-multiplication.cpp) [Python](./Python/sparse-matrix-multiplication.py) | _O(m * n * l)_ | _O(m * l)_ | Medium |📖|| +334| [Increasing Triplet Subsequence](https://leetcode.com/problems/increasing-triplet-subsequence/) | [C++](./C++/increasing-triplet-subsequence.cpp) [Python](./Python/increasing-triplet-subsequence.py) | _O(n)_ | _O(1)_ | Medium ||| +370| [Range Addition](https://leetcode.com/problems/range-addition/) | [C++](./C++/range-addition.cpp) [Python](./Python/range-addition.py) | _O(k + n)_ | _O(1)_ | Medium |📖|| +384| [Shuffle an Array](https://leetcode.com/problems/shuffle-an-array/) | [C++](./C++/shuffle-an-array.cpp) [Python](./Python/shuffle-an-array.py) | _O(n)_ | _O(n)_ | Medium | EPI || +396| [Rotate Function](https://leetcode.com/problems/rotate-function/) | [C++](./C++/rotate-function.cpp) [Python](./Python/rotate-function.py) | _O(n)_ | _O(1)_ | Easy ||| +412| [Fizz Buzz](https://leetcode.com/problems/fizz-buzz/) | [C++](./C++/fizz-buzz.cpp) [Python](./Python/fizz-buzz.py) | _O(n)_ | _O(1)_ | Easy ||| +414| [Third Maximum Number](https://leetcode.com/problems/third-maximum-number/) | [C++](./C++/third-maximum-number.cpp) [Python](./Python/third-maximum-number.py) | _O(n)_ | _O(1)_ | Easy ||| +419| [Battleships in a Board](https://leetcode.com/problems/battleships-in-a-board/) | [C++](./C++/battleships-in-a-board.cpp) [Python](./Python/battleships-in-a-board.py) | _O(m * n)_ | _O(1)_ | Medium ||| +422| [Valid Word Square](https://leetcode.com/problems/valid-word-square/) | [C++](./C++/valid-word-square.cpp) [Python](./Python/valid-word-square.py) | _O(m * n)_ | _O(1)_ | Easy |📖|| +442| [Find All Duplicates in an Array](https://leetcode.com/problems/find-all-duplicates-in-an-array/) | [C++](./C++/find-all-duplicates-in-an-array.cpp) [Python](./Python/find-all-duplicates-in-an-array.py) | _O(n)_ | _O(1)_ | Medium ||| +448| [Find All Numbers Disappeared in an Array](https://leetcode.com/problems/find-all-numbers-disappeared-in-an-array/) | [C++](./C++/find-all-numbers-disappeared-in-an-array.cpp) [Python](./Python/find-all-numbers-disappeared-in-an-array.py) | _O(n)_ | _O(1)_ | Easy ||| +531 | [Lonely Pixel I](https://leetcode.com/problems/lonely-pixel-i/) | [C++](./C++/lonely-pixel-i.cpp) [Python](./Python/lonely-pixel-i.py) | _O(m * n)_ | _O(m + n)_ | Medium |📖|| +533 | [Lonely Pixel II](https://leetcode.com/problems/lonely-pixel-ii/) | [C++](./C++/lonely-pixel-ii.cpp) [Python](./Python/lonely-pixel-ii.py) | _O(m * n)_ | _O(m * n)_ | Medium |📖|| +565 | [Array Nesting](https://leetcode.com/problems/array-nesting/) | [C++](./C++/array-nesting.cpp) [Python](./Python/array-nesting.py) | _O(n)_ | _O(1)_ | Medium ||| +566 | [Reshape the Matrix](https://leetcode.com/problems/reshape-the-matrix/) | [C++](./C++/reshape-the-matrix.cpp) [Python](./Python/reshape-the-matrix.py) | _O(m * n)_ | _O(m * n)_ | Easy ||| +581 | [Shortest Unsorted Continuous Subarray](https://leetcode.com/problems/shortest-unsorted-continuous-subarray/) | [C++](./C++/shortest-unsorted-continuous-subarray.cpp) [Python](./Python/shortest-unsorted-continuous-subarray.py) | _O(n)_ | _O(1)_ | Easy ||| +605 | [Can Place Flowers](https://leetcode.com/problems/can-place-flowers/) | [C++](./C++/can-place-flowers.cpp) [Python](./Python/can-place-flowers.py) | _O(n)_ | _O(1)_ | Easy ||| +624 | [Maximum Distance in Arrays](https://leetcode.com/problems/maximum-distance-in-arrays/) | [C++](./C++/maximum-distance-in-arrays.cpp) [Python](./Python/maximum-distance-in-arrays.py) | _O(n)_ | _O(1)_ | Easy | 📖 | +643 | [Maximum Average Subarray I](https://leetcode.com/problems/maximum-average-subarray-i/) | [C++](./C++/maximum-average-subarray-i.cpp) [Python](./Python/maximum-average-subarray-i.py) | _O(n)_ | _O(1)_ | Easy || Math +644 | [Maximum Average Subarray II](https://leetcode.com/problems/maximum-average-subarray-ii/) | [C++](./C++/maximum-average-subarray-ii.cpp) [Python](./Python/maximum-average-subarray-ii.py) | _O(n)_ | _O(n)_ | Hard | 📖 | Math +661| [Image Smoother](https://leetcode.com/problems/image-smoother/) | [C++](./C++/image-smoother.cpp) [Python](./Python/image-smoother.py) | _O(m * n)_ | _O(1)_ | Easy ||| +665| [Non-decreasing Array](https://leetcode.com/problems/non-decreasing-array/) | [C++](./C++/non-decreasing-array.cpp) [Python](./Python/non-decreasing-array.py) | _O(n)_ | _O(1)_ | Easy ||| +667| [Beautiful Arrangement II](https://leetcode.com/problems/beautiful-arrangement-ii/) | [C++](./C++/beautiful-arrangement-ii.cpp) [Python](./Python/beautiful-arrangement-ii.py) | _O(n)_ | _O(1)_ | Medium ||| +670| [Maximum Swap](https://leetcode.com/problems/maximum-swap/) | [C++](./C++/maximum-swap.cpp) [Python](./Python/maximum-swap.py) | _O(logn)_ | _O(logn)_ | Medium ||| +674 | [Longest Continuous Increasing Subsequence](https://leetcode.com/problems/longest-continuous-increasing-subsequence/) | [C++](./C++/longest-continuous-increasing-subsequence.cpp) [Python](./Python/longest-continuous-increasing-subsequence.py) | _O(n)_ | _O(1)_ | Easy || +683 | [K Empty Slots](https://leetcode.com/problems/k-empty-slots/) | [C++](./C++/k-empty-slots.cpp) [Python](./Python/k-empty-slots.py) | _O(n)_ | _O(n)_ | Hard || +697| [Degree of an Array](https://leetcode.com/problems/degree-of-an-array/) | [C++](./C++/degree-of-an-array.cpp) [Python](./Python/degree-of-an-array.py) | _O(n)_ | _O(n)_ | Easy || +713 | [Subarray Product Less Than K](https://leetcode.com/problems/subarray-product-less-than-k/) | [C++](./C++/subarray-product-less-than-k.cpp) [Python](./Python/subarray-product-less-than-k.py) | _O(n)_ | _O(1)_ | Medium || +717 | [1-bit and 2-bit Characters](https://leetcode.com/problems/1-bit-and-2-bit-characters/) | [C++](./C++/1-bit-and-2-bit-characters.cpp) [Python](./Python/1-bit-and-2-bit-characters.py) | _O(n)_ | _O(1)_ | Easy || Greedy +723 | [Candy Crush](https://leetcode.com/problems/candy-crush/) | [C++](./C++/candy-crush.cpp) [Python](./Python/candy-crush.py) | _O((R * C)^2)_ | _O(1)_ | Medium || +724 | [Find Pivot Index](https://leetcode.com/problems/find-pivot-index/) | [C++](./C++/find-pivot-index.cpp) [Python](./Python/find-pivot-index.py) | _O(n)_ | _O(1)_ | Easy || +729 | [My Calendar I](https://leetcode.com/problems/my-calendar-i/) | [C++](./C++/my-calendar-i.cpp) [Python](./Python/my-calendar-i.py) | _O(nlogn)_ | _O(n)_ | Medium || +731 | [My Calendar II](https://leetcode.com/problems/my-calendar-ii/) | [C++](./C++/my-calendar-ii.cpp) [Python](./Python/my-calendar-ii.py) | _O(n^2)_ | _O(n)_ | Medium || +732 | [My Calendar III](https://leetcode.com/problems/my-calendar-iii/) | [C++](./C++/my-calendar-iii.cpp) [Python](./Python/my-calendar-iii.py) | _O(n^2)_ | _O(n)_ | Hard || +747 | [Largest Number At Least Twice of Others](https://leetcode.com/problems/largest-number-at-least-twice-of-others/) | [C++](./C++/largest-number-at-least-twice-of-others.cpp) [Python](./Python/largest-number-at-least-twice-of-others.py) | _O(n)_ | _O(1)_ | Easy || +755 | [Pour Water](https://leetcode.com/problems/pour-water/) | [C++](./C++/pour-water.cpp) [Python](./Python/pour-water.py) | _O(v * n)_ | _O(1)_ | Medium || +766 | [Toeplitz Matrix](https://leetcode.com/problems/toeplitz-matrix/) | [C++](./C++/toeplitz-matrix.cpp) [Python](./Python/toeplitz-matrix.py) | _O(m * n)_ | _O(1)_ | Easy || +768 | [Max Chunks To Make Sorted II](https://leetcode.com/problems/max-chunks-to-make-sorted-ii/) | [C++](./C++/max-chunks-to-make-sorted-ii.cpp) [Python](./Python/max-chunks-to-make-sorted-ii.py) | _O(nlogn)_ | _O(n)_ | Hard || +769 | [Max Chunks To Make Sorted](https://leetcode.com/problems/max-chunks-to-make-sorted/) | [C++](./C++/max-chunks-to-make-sorted.cpp) [Python](./Python/max-chunks-to-make-sorted.py) | _O(n)_ | _O(1)_ | Medium || +778 | [Swim in Rising Water](https://leetcode.com/problems/swim-in-rising-water/) | [C++](./C++/swim-in-rising-water.cpp) [Python](./Python/swim-in-rising-water.py) | _O(n^2)_ | _O(n^2)_ | Hard || Union Find +792 | [Number of Matching Subsequences](https://leetcode.com/problems/number-of-matching-subsequences/) | [C++](./C++/number-of-matching-subsequences.cpp) [Python](./Python/number-of-matching-subsequences.py) | _O(n + w)_ | _O(1)_ | Medium || +794 | [Valid Tic-Tac-Toe State](https://leetcode.com/problems/valid-tic-tac-toe-state/) | [C++](./C++/valid-tic-tac-toe-state.cpp) [Python](./Python/valid-tic-tac-toe-state.py) | _O(1)_ | _O(1)_ | Medium || +795 | [Number of Subarrays with Bounded Maximum](https://leetcode.com/problems/number-of-subarrays-with-bounded-maximum/) | [C++](./C++/number-of-subarrays-with-bounded-maximum.cpp) [Python](./Python/number-of-subarrays-with-bounded-maximum.py) | _O(n)_ | _O(1)_ | Medium || +803 | [Bricks Falling When Hit](https://leetcode.com/problems/bricks-falling-when-hit/) | [C++](./C++/bricks-falling-when-hit.cpp) [Python](./Python/bricks-falling-when-hit.py) | _O(r * c)_ | _O(r * c)_ | Hard || Union Find +807 | [Max Increase to Keep City Skyline](https://leetcode.com/problems/max-increase-to-keep-city-skyline/) | [C++](./C++/max-increase-to-keep-city-skyline.cpp) [Python](./Python/max-increase-to-keep-city-skyline.py) | _O(n^2)_ | _O(n)_ | Medium || +821 | [Shortest Distance to a Character](https://leetcode.com/problems/shortest-distance-to-a-character/) | [C++](./C++/shortest-distance-to-a-character.cpp) [Python](./Python/shortest-distance-to-a-character.py) | _O(n)_ | _O(1)_ | Easy || +830 | [Positions of Large Groups](https://leetcode.com/problems/positions-of-large-groups/) | [C++](./C++/positions-of-large-groups.cpp) [Python](./Python/positions-of-large-groups.py) | _O(n)_ | _O(1)_ | Easy || +832 | [Flipping an Image](https://leetcode.com/problems/flipping-an-image/) | [C++](./C++/flipping-an-image.cpp) [Python](./Python/flipping-an-image.py) | _O(n^2)_ | _O(1)_ | Easy || +835 | [Image Overlap](https://leetcode.com/problems/image-overlap/) | [C++](./C++/image-overlap.cpp) [Python](./Python/image-overlap.py) | _O(n^4)_ | _O(n^2)_ | Medium || +840 | [Magic Squares In Grid](https://leetcode.com/problems/magic-squares-in-grid/) | [C++](./C++/magic-squares-in-grid.cpp) [Python](./Python/magic-squares-in-grid.py) | _O(m * n)_ | _O(1)_ | Easy || +842 | [Split Array into Fibonacci Sequence](https://leetcode.com/problems/split-array-into-fibonacci-sequence/) | [C++](./C++/split-array-into-fibonacci-sequence.cpp) [Python](./Python/split-array-into-fibonacci-sequence.py) | _O(n^3)_ | _O(n)_ | Medium || +845 | [Longest Mountain in Array](https://leetcode.com/problems/longest-mountain-in-array/) | [C++](./C++/longest-mountain-in-array.cpp) [Python](./Python/longest-mountain-in-array.py) | _O(n)_ | _O(1)_ | Medium || +849 | [Maximize Distance to Closest Person](https://leetcode.com/problems/maximize-distance-to-closest-person/) | [C++](./C++/maximize-distance-to-closest-person.cpp) [Python](./Python/maximize-distance-to-closest-person.py) | _O(n)_ | _O(1)_ | Easy || +860 | [Lemonade Change](https://leetcode.com/problems/lemonade-change/) | [C++](./C++/lemonade-change.cpp) [Python](./Python/lemonade-change.py) | _O(n)_ | _O(1)_ | Easy || +868 | [Transpose Matrix](https://leetcode.com/problems/transpose-matrix/) | [C++](./C++/transpose-matrix.cpp) [Python](./Python/transpose-matrix.py) | _O(r * c)_ | _O(1)_ | Easy || + +## String +| # | Title | Solution | Time | Space | Difficulty | Tag | Note| +|-----|---------------- | --------------- | --------------- | --------------- | ------------- |--------------|-----| +005| [Longest Palindromic Substring](https://leetcode.com/problems/longest-palindromic-substring/) | [C++](./C++/longest-palindromic-substring.cpp) [Python](./Python/longest-palindromic-substring.py) | _O(n)_ | _O(n)_ | Medium || `Manacher's Algorithm` +006| [ZigZag Conversion](https://leetcode.com/problems/zigzag-conversion/) | [C++](./C++/zigzag-conversion.cpp) [Python](./Python/zigzag-conversion.py) | _O(n)_ | _O(1)_ | Easy || +008| [String to Integer (atoi)](https://leetcode.com/problems/string-to-integer-atoi/) | [C++](./C++/string-to-integer-atoi.cpp) [Python](./Python/string-to-integer-atoi.py) | _O(n)_ | _O(1)_ | Easy || +014| [Longest Common Prefix](https://leetcode.com/problems/longest-common-prefix/) | [C++](./C++/longest-common-prefix.cpp) [Python](./Python/longest-common-prefix.py) | _O(n * k)_ | _O(1)_ | Easy || +028| [Implement strStr()](https://leetcode.com/problems/implement-strstr/) | [C++](./C++/implement-strstr.cpp) [Python](./Python/implement-strstr.py) | _O(n + k)_ | _O(k)_ | Easy || `KMP Algorithm` +038| [Count and Say](https://leetcode.com/problems/count-and-say/) | [C++](./C++/count-and-say.cpp) [Python](./Python/count-and-say.py)| _O(n * 2^n)_ | _O(2^n)_ | Easy || +043| [Multiply Strings](https://leetcode.com/problems/multiply-strings/) | [C++](./C++/multiply-strings.cpp) [Python](./Python/multiply-strings.py) | _O(m * n)_ | _O(m + n)_ | Medium || +058| [Length of Last Word](https://leetcode.com/problems/length-of-last-word/) | [C++](./C++/length-of-last-word.cpp) [Python](./Python/length-of-last-word.py) | _O(n)_ | _O(1)_ | Easy || +067| [Add Binary](https://leetcode.com/problems/add-binary/) | [C++](./C++/add-binary.cpp) [Python](./Python/add-binary.py) | _O(n)_ | _O(1)_ | Easy || +068| [Text Justification](https://leetcode.com/problems/text-justification/) | [C++](./C++/text-justification.cpp) [Python](./Python/text-justification.py) | _O(n)_ | _O(1)_ | Hard || +125| [Valid Palindrome](https://leetcode.com/problems/valid-palindrome/) | [C++](./C++/valid-palindrome.cpp) [Python](./Python/valid-palindrome.py) | _O(n)_ | _O(1)_ | Easy || +151| [Reverse Words in a String](https://leetcode.com/problems/reverse-words-in-a-string/) | [C++](./C++/reverse-words-in-a-string.cpp) [Python](./Python/reverse-words-in-a-string.py) | _O(n)_ | _O(1)_ | Medium || +161| [One Edit Distance](https://leetcode.com/problems/one-edit-distance/) | [C++](./C++/one-edit-distance.cpp) [Python](./Python/one-edit-distance.py) | _O(m + n)_ | _O(1)_ | Medium |📖 | +165| [Compare Version Numbers](https://leetcode.com/problems/compare-version-numbers/) | [C++](./C++/compare-version-numbers.cpp) [Python](./Python/compare-version-numbers.py) | _O(n)_ | _O(1)_ | Easy || +186| [Reverse Words in a String II](https://leetcode.com/problems/reverse-words-in-a-string-ii/) |[C++](./C++/reverse-words-in-a-string-ii.cpp) [Python](./Python/reverse-words-in-a-string-ii.py) | _O(n)_ | _O(1)_ | Medium | 📖 | +214| [Shortest Palindrome](https://leetcode.com/problems/shortest-palindrome/) | [C++](./C++/shortest-palindrome.cpp) [Python](./Python/shortest-palindrome.py) | _O(n)_ | _O(n)_ | Hard || `KMP Algorithm` `Manacher's Algorithm` +242| [Valid Anagram](https://leetcode.com/problems/valid-anagram/)| [C++](./C++/valid-anagram.cpp) [Python](./Python/valid-anagram.py) | _O(n)_ | _O(1)_ | Easy | LintCode | +271| [Encode and Decode Strings](https://leetcode.com/problems/encode-and-decode-strings/) | [C++](./C++/encode-and-decode-strings.cpp) [Python](./Python/encode-and-decode-strings.py) | _O(n)_ | _O(1)_ | Medium | 📖 | +273| [Integer to English Words](https://leetcode.com/problems/integer-to-english-words/) | [C++](./C++/integer-to-english-words.cpp) [Python](./Python/integer-to-english-words.py) | _O(1)_ | _O(1)_ | Hard | | +306| [Addictive Number](https://leetcode.com/problems/additive-number/) | [C++](./C++/additive-number.cpp) [Python](./Python/additive-number.py) | _O(n^3)_ | _O(n)_ | Medium | | +383| [Ransom Note](https://leetcode.com/problems/ransom-note/) | [C++](./C++/ransom-note.cpp) [Python](./Python/ransom-note.py) | _O(n)_ | _O(1)_ | Easy | EPI | +405| [Convert a Number to Hexadecimal](https://leetcode.com/problems/convert-a-number-to-hexadecimal/) | [C++](./C++/convert-a-number-to-hexadecimal.cpp) [Python](./Python/convert-a-number-to-hexadecimal.py) | _O(n)_ | _O(1)_ | Easy | | +408| [Valid Word Abbreviation](https://leetcode.com/problems/valid-word-abbreviation/) | [C++](./C++/valid-word-abbreviation.cpp) [Python](./Python/valid-word-abbreviation.py) | _O(n)_ | _O(1)_ | Easy | 📖 | +415| [Add Strings](https://leetcode.com/problems/add-strings/) | [C++](./C++/add-strings.cpp) [Python](./Python/add-strings.py) | _O(n)_ | _O(1)_ | Easy | | +420| [Strong Password Checker](https://leetcode.com/problems/strong-password-checker/) | [C++](./C++/strong-password-checker.cpp) [Python](./Python/strong-password-checker.py) | _O(n)_ | _O(1)_ | Hard | | +434| [Number of Segments in a String](https://leetcode.com/problems/number-of-segments-in-a-string/) | [C++](./C++/number-of-segments-in-a-string.cpp) [Python](./Python/number-of-segments-in-a-string.py) | _O(n)_ | _O(1)_ | Easy | | +443| [String Compression](https://leetcode.com/problems/string-compression/) | [C++](./C++/string-compression.cpp) [Python](./Python/string-compression.py) | _O(n)_ | _O(1)_ | Easy | | +459| [Repeated Substring Pattern](https://leetcode.com/problems/repeated-substring-pattern/) | [C++](./C++/repeated-substring-pattern.cpp) [Python](./Python/repeated-substring-pattern.py) | _O(n)_ | _O(n)_ | Easy || `KMP Algorithm` | +468| [Validate IP Address](https://leetcode.com/problems/validate-ip-address/) | [C++](./C++/validate-ip-address.cpp) [Python](./Python/validate-ip-address.py) | _O(1)_ | _O(1)_ | Medium | | +520| [Detect Capital](https://leetcode.com/problems/detect-capital/) | [C++](./C++/detect-capital.cpp) [Python](./Python/detect-capital.py) | _O(l)_ | _O(1)_ | Easy | | +521| [Longest Uncommon Subsequence I](https://leetcode.com/problems/longest-uncommon-subsequence-i/) | [C++](./C++/longest-uncommon-subsequence-i.cpp) [Python](./Python/longest-uncommon-subsequence-i.py) | _O(min(a, b))_ | _O(1)_ | Easy | | +522| [Longest Uncommon Subsequence II](https://leetcode.com/problems/longest-uncommon-subsequence-ii/) | [C++](./C++/longest-uncommon-subsequence-ii.cpp) [Python](./Python/longest-uncommon-subsequence-ii.py) | _O(l * n^2)_ | _O(1)_ | Medium | | Sort +524| [Longest Word in Dictionary through Deleting](https://leetcode.com/problems/longest-word-in-dictionary-through-deleting/) | [C++](./C++/longest-word-in-dictionary-through-deleting.cpp) [Python](./Python/longest-word-in-dictionary-through-deleting.py) | _O((d * l) * logd)_ | _O(1)_ | Medium | | Sort +527| [Word Abbreviation](https://leetcode.com/problems/word-abbreviation/) | [C++](./C++/word-abbreviation.cpp) [Python](./Python/word-abbreviation.py) | _O(n * l)_ ~ _O(n^2 * l^2)_ | _O(n * l)_ | Hard |📖| +539| [Minimum Time Difference](https://leetcode.com/problems/minimum-time-difference/) | [C++](./C++/minimum-time-difference.cpp) [Python](./Python/minimum-time-difference.py) | _O(nlogn)_ | _O(n)_ | Medium | | +541| [Reverse String II](https://leetcode.com/problems/reverse-string-ii/) | [C++](./C++/reverse-string-ii.cpp) [Python](./Python/reverse-string-ii.py) | _O(n)_ | _O(1)_ | Easy | | +551 | [Student Attendance Record I](https://leetcode.com/problems/student-attendance-record-i/) | [C++](./C++/student-attendance-record-i.cpp) [Python](./Python/student-attendance-record-i.py) | _O(n)_ | _O(1)_ | Easy ||| +556| [Next Greater Element III](https://leetcode.com/problems/next-greater-element-iii/) |[C++](./C++/next-greater-element-iii.cpp) [Python](./Python/next-greater-element-iii.py) | _O(1)_ | _O(1)_ | Medium | | +557| [Reverse Words in a String III](https://leetcode.com/problems/reverse-words-in-a-string-iii/) |[C++](./C++/reverse-words-in-a-string-iii.cpp) [Python](./Python/reverse-words-in-a-string-iii.py) | _O(n)_ | _O(1)_ | Easy | | +564| [Find the Closest Palindrome](https://leetcode.com/problems/find-the-closest-palindrome/) |[C++](./C++/find-the-closest-palindrome.cpp) [Python](./Python/find-the-closest-palindrome.py) | _O(l)_ | _O(l)_ | Hard | | +591| [Tag Validator](https://leetcode.com/problems/tag-validator/) |[C++](./C++/tag-validator.cpp) [Python](./Python/tag-validator.py) | _O(n)_ | _O(n)_ | Hard | | +616| [Add Bold Tag in String](https://leetcode.com/problems/add-bold-tag-in-string/) | [C++](./C++/add-bold-tag-in-string.cpp) [Python](./Python/add-bold-tag-in-string.py) | _O(n * d * l)_ | _O(n)_ | Medium | 📖 | +647| [Palindromic Substrings](https://leetcode.com/problems/palindromic-substrings/) | [C++](./C++/palindromic-substrings.cpp) [Python](./Python/palindromic-substrings.py) | _O(n)_ | _O(n)_ | Medium || `Manacher's Algorithm` +648| [Replace Words](https://leetcode.com/problems/replace-words/) | [C++](./C++/replace-words.cpp) [Python](./Python/replace-words.py) | _O(n)_ | _O(t)_ | Medium || Trie | +657| [Judge Route Circle](https://leetcode.com/problems/judge-route-circle/) |[C++](./C++/judge-route-circle.cpp) [Python](./Python/judge-route-circle.py) | _O(n)_ | _O(1)_ | Easy | | +678| [Valid Parenthesis String](https://leetcode.com/problems/valid-parenthesis-string/) |[C++](./C++/valid-parenthesis-string.cpp) [Python](./Python/valid-parenthesis-string.py) | _O(n)_ | _O(1)_ | Medium | | +680| [Valid Palindrome II](https://leetcode.com/problems/valid-palindrome-ii/) | [C++](./C++/valid-palindrome-ii.cpp) [Python](./Python/valid-palindrome-ii.py) | _O(n)_ | _O(1)_ | Easy || +681| [Next Closest Time](https://leetcode.com/problems/next-closest-time/) | [C++](./C++/next-closest-time.cpp) [Python](./Python/next-closest-time.py) | _O(1)_ | _O(1)_ | Medium || +686 | [Repeated String Match](https://leetcode.com/problems/repeated-string-match/) | [C++](./C++/repeated-string-match.cpp) [Python](./Python/repeated-string-match.py) | _O(n + m)_ | _O(1)_ | Easy || `Rabin-Karp Algorithm` | +696| [Count Binary Substrings](https://leetcode.com/problems/count-binary-substrings/) | [C++](./C++/count-binary-substrings.cpp) [Python](./Python/count-binary-substrings.py) | _O(n)_ | _O(1)_ | Easy|| +720| [Longest Word in Dictionary](https://leetcode.com/problems/longest-word-in-dictionary/) | [C++](./C++/longest-word-in-dictionary.cpp) [Python](./Python/longest-word-in-dictionary.py) | _O(n)_ | _O(t)_ | Easy || Trie | +722| [Remove Comments](https://leetcode.com/problems/remove-comments/) | [C++](./C++/remove-comments.cpp) [Python](./Python/remove-comments.py) | _O(n)_ | _O(k)_ | Medium ||| +751| [IP to CIDR](https://leetcode.com/problems/ip-to-cidr/) | [C++](./C++/ip-to-cidr.cpp) [Python](./Python/ip-to-cidr.py) | _O(n)_ | _O(1)_ | Medium ||| +758| [Bold Words in String](https://leetcode.com/contest/weekly-contest-66/problems/bold-words-in-string/) | [C++](./C++/bold-words-in-string.cpp) [Python](./Python/bold-words-in-string.py) | _O(n * l)_ | _O(t)_ | Easy | 📖, variant of [Add Bold Tag in String](https://leetcode.com/problems/add-bold-tag-in-string/) | +791| [Custom Sort String](https://leetcode.com/problems/custom-sort-string/) | [C++](./C++/custom-sort-string.cpp) [Python](./Python/custom-sort-string.py) | _O(n)_ | _O(1)_ | Medium ||| +796| [Rotate String](https://leetcode.com/problems/rotate-string/) | [C++](./C++/rotate-string.cpp) [Python](./Python/rotate-string.py) | _O(n)_ | _O(1)_ | Easy || `KMP Algorithm` `Rabin-Karp Algorithm` | +804| [Unique Morse Code Words](https://leetcode.com/problems/unique-morse-code-words/) | [C++](./C++/unique-morse-code-words.cpp) [Python](./Python/unique-morse-code-words.py) | _O(n)_ | _O(n)_ | Easy ||| +806| [Number of Lines To Write String](https://leetcode.com/problems/number-of-lines-to-write-string/) | [C++](./C++/number-of-lines-to-write-string.cpp) [Python](./Python/number-of-lines-to-write-string.py) | _O(n)_ | _O(1)_ | Easy ||| +809| [Expressive Words](https://leetcode.com/problems/expressive-words/) | [C++](./C++/expressive-words.cpp) [Python](./Python/expressive-words.py) | _O(n + s)_ | _O(l + s)_ | Medium ||| +816| [Ambiguous Coordinates](https://leetcode.com/problems/ambiguous-coordinates/) | [C++](./C++/ambiguous-coordinates.cpp) [Python](./Python/ambiguous-coordinates.py) | _O(n^4)_ | _O(n)_ | Medium ||| +819| [Most Common Word](https://leetcode.com/problems/most-common-word/) | [C++](./C++/most-common-word.cpp) [Python](./Python/most-common-word.py) | _O(m + n)_ | _O(m + n)_ | Easy ||| +820| [Short Encoding of Words](https://leetcode.com/problems/short-encoding-of-words/) | [C++](./C++/short-encoding-of-wordss.cpp) [Python](./Python/short-encoding-of-words.py) | _O(n)_ | _O(t)_ | Medium || Trie | +824| [Goat Latin](https://leetcode.com/problems/goat-latin/) | [C++](./C++/goat-latin.cpp) [Python](./Python/goat-latin.py) | _O(n + w^2)_ | _O(l)_ | Easy ||| +831| [Masking Personal Information](https://leetcode.com/problems/masking-personal-information/) | [C++](./C++/masking-personal-information.cpp) [Python](./Python/masking-personal-information.py) | _O(1)_ | _O(1)_ | Medium ||| +833| [Find And Replace in String](https://leetcode.com/problems/find-and-replace-in-string/) | [C++](./C++/find-and-replace-in-string.cpp) [Python](./Python/find-and-replace-in-string.py) | _O(n + m)_ | _O(n)_ | Medium ||| +839 | [Similar String Groups](https://leetcode.com/problems/similar-string-groups/) | [C++](./C++/similar-string-groups.cpp) [Python](./Python/similar-string-groups.py) | _O(n^2 * l)_ | _O(n)_ | Hard || Union Find +848 | [Shifting Letters](https://leetcode.com/problems/shifting-letters/) | [C++](./C++/shifting-letters.cpp) [Python](./Python/shifting-letters.py) | _O(n)_ | _O(1)_ | Medium || +859 | [Buddy Strings](https://leetcode.com/problems/buddy-strings/) | [C++](./C++/buddy-strings.cpp) [Python](./Python/buddy-strings.py) | _O(n)_ | _O(1)_ | Easy || + +## Linked List +| # | Title | Solution | Time | Space | Difficulty | Tag | Note| +|-----|---------------- | --------------- | --------------- | --------------- | ------------- |--------------|-----| +002| [Add Two Numbers](https://leetcode.com/problems/add-two-numbers/) | [C++](./C++/add-two-numbers.cpp) [Python](./Python/add-two-numbers.py) | _O(n)_ | _O(1)_ | Medium || +021| [Merge Two Sorted Lists](https://leetcode.com/problems/merge-two-sorted-lists/)| [C++](./C++/merge-two-sorted-lists.cpp) [Python](./Python/merge-two-sorted-lists.py) | _O(n)_ | _O(1)_ | Easy || +023| [Merge k Sorted Lists](https://leetcode.com/problems/merge-k-sorted-lists/) | [C++](./C++/merge-k-sorted-lists.cpp) [Python](./Python/merge-k-sorted-lists.py) | _O(nlogk)_| _O(1)_| Hard | | Heap, Divide and Conquer +024| [Swap Nodes in Pairs](https://leetcode.com/problems/swap-nodes-in-pairs/)| [C++](./C++/swap-nodes-in-pairs.cpp) [Python](./Python/swap-nodes-in-pairs.py) | _O(n)_ | _O(1)_ | Easy || +025| [Reverse Nodes in k-Group](https://leetcode.com/problems/reverse-nodes-in-k-group/)| [C++](./C++/reverse-nodes-in-k-group.cpp) [Python](./Python/reverse-nodes-in-k-group.py) | _O(n)_ | _O(1)_ | Hard || +061| [Rotate List](https://leetcode.com/problems/rotate-list/)| [C++](./C++/rotate-list.cpp) [Python](./Python/rotate-list.py) | _O(n)_ | _O(1)_ | Medium || +082| [Remove Duplicates from Sorted List II](https://leetcode.com/problems/remove-duplicates-from-sorted-list-ii/)| [C++](./C++/remove-duplicates-from-sorted-list-ii.cpp) [Python](./Python/remove-duplicates-from-sorted-list-ii.py) | _O(n)_ | _O(1)_ | Medium || +083| [Remove Duplicates from Sorted List](https://leetcode.com/problems/remove-duplicates-from-sorted-list/)| [C++](./C++/remove-duplicates-from-sorted-list.cpp) [Python](./Python/remove-duplicates-from-sorted-list.py) | _O(n)_ | _O(1)_ | Easy || +092| [Reverse Linked List II](https://leetcode.com/problems/reverse-linked-list-ii/)| [C++](./C++/reverse-linked-list-ii.cpp) [Python](./Python/reverse-linked-list-ii.py) | _O(n)_ | _O(1)_ | Medium || +138| [Copy List with Random Pointer](https://leetcode.com/problems/copy-list-with-random-pointer/) | [C++](./C++/copy-list-with-random-pointer.cpp) [Python](./Python/copy-list-with-random-pointer.py) | _O(n)_ | _O(1)_ | Hard || +160| [Intersection of Two Linked Lists](https://leetcode.com/problems/intersection-of-two-linked-lists/)| [C++](./C++/intersection-of-two-linked-lists.cpp) [Python](./Python/intersection-of-two-linked-lists.py) | _O(m + n)_ | _O(1)_ | Easy || +203| [Remove Linked List Elements](https://leetcode.com/problems/remove-linked-list-elements/)| [C++](./C++/remove-linked-list-elements.cpp) [Python](./Python/remove-linked-list-elements.py) | _O(n)_ | _O(1)_ | Easy || +206| [Reverse Linked List](https://leetcode.com/problems/reverse-linked-list/)| [C++](./C++/reverse-linked-list.cpp) [Python](./Python/reverse-linked-list.py) | _O(n)_ | _O(1)_ | Easy || +234| [Palindrome Linked List](https://leetcode.com/problems/palindrome-linked-list/)| [C++](./C++/palindrome-linked-list.cpp) [Python](./Python/palindrome-linked-list.py) | _O(n)_ | _O(1)_ | Easy || +237| [Delete Node in a Linked List](https://leetcode.com/problems/delete-node-in-a-linked-list/)| [C++](./C++/delete-node-in-a-linked-list.cpp) [Python](./Python/delete-node-in-a-linked-list.py) | _O(1)_ | _O(1)_ | Easy | LintCode | +328| [Odd Even Linked List](https://leetcode.com/problems/odd-even-linked-list/)| [C++](./C++/odd-even-linked-list.cpp) [Python](./Python/odd-even-linked-list.py) | _O(n)_ | _O(1)_ | Medium | | +369| [Plus One Linked List](https://leetcode.com/problems/plus-one-linked-list/)| [C++](./C++/plus-one-linked-list.cpp) [Python](./Python/plus-one-linked-list.py) | _O(n)_ | _O(1)_ | Medium | 📖 | Two Pointers | +445| [Add Two Numbers II](https://leetcode.com/problems/add-two-numbers-ii/)| [C++](./C++/add-two-numbers-ii.cpp) [Python](./Python/add-two-numbers-ii.py) | _O(m + n)_ | _O(m + n)_ | Medium ||| +725 | [Split Linked List in Parts](https://leetcode.com/problems/split-linked-list-in-parts/) | [C++](./C++/split-linked-list-in-parts.cpp) [Python](./Python/split-linked-list-in-parts.py) | _O(n + k)_ | _O(1)_ | Medium || +817 | [Linked List Components](https://leetcode.com/problems/linked-list-components/) | [C++](./C++/linked-list-components.cpp) [Python](./Python/linked-list-components.py) | _O(m + n)_ | _O(m)_ | Medium || + +## Stack +| # | Title | Solution | Time | Space | Difficulty | Tag | Note| +|-----|---------------- | --------------- | --------------- | --------------- | ------------- |--------------|-----| +020| [Valid Parentheses](https://leetcode.com/problems/valid-parentheses/)| [C++](./C++/valid-parentheses.cpp) [Python](./Python/valid-parentheses.py) | _O(n)_ | _O(n)_ | Easy || +032| [Longest Valid Parentheses](https://leetcode.com/problems/longest-valid-parentheses/)| [C++](./C++/longest-valid-parentheses.cpp) [Python](./Python/longest-valid-parentheses.py) | _O(n)_ | _O(1)_ | Hard || +071| [Simplify Path](https://leetcode.com/problems/simplify-path/)| [C++](./C++/simplify-path.cpp) [Python](./Python/simplify-path.py) | _O(n)_ | _O(n)_ | Medium || +084| [Largest Rectangle in Histogram](https://leetcode.com/problems/largest-rectangle-in-histogram/) | [C++](./C++/largest-rectangle-in-histogram.cpp) [Python](./Python/largest-rectangle-in-histogram.py) | _O(n)_ | _O(n)_ | Hard || Ascending Stack, DP +085| [Maximal Rectangle](https://leetcode.com/problems/maximal-rectangle/)| [C++](./C++/maximal-rectangle.cpp) [Python](./Python/maximal-rectangle.py)| _O(m * n)_ | _O(n)_ | Hard | EPI | Ascending Stack +101| [Symmetric Tree](https://leetcode.com/problems/symmetric-tree/)| [C++](./C++/symmetric-tree.cpp) [Python](./Python/symmetric-tree.py) | _O(n)_ | _O(h)_ | Easy || +150| [Evaluate Reverse Polish Notation](https://leetcode.com/problems/evaluate-reverse-polish-notation/)| [C++](./C++/evaluate-reverse-polish-notation.cpp) [Python](./Python/evaluate-reverse-polish-notation.py)| _O(n)_| _O(n)_| Medium || +155| [Min Stack](https://leetcode.com/problems/min-stack/) | [C++](./C++/min-stack.cpp) [Python](./Python/min-stack.py) | _O(n)_ | _O(1)_ | Easy || +173| [Binary Search Tree Iterator](https://leetcode.com/problems/binary-search-tree-iterator/) | [C++](./C++/binary-search-tree-iterator.cpp) [Python](./Python/binary-search-tree-iterator.py) | _O(1)_| _O(h)_| Medium || +224| [Basic Calculator](https://leetcode.com/problems/basic-calculator/) | [C++](./C++/basic-calculator.cpp) [Python](./Python/basic-calculator.py) | _O(n)_| _O(n)_| Hard || +227| [Basic Calculator II](https://leetcode.com/problems/basic-calculator-ii/) | [C++](./C++/basic-calculator-ii.cpp) [Python](./Python/basic-calculator-ii.py) | _O(n)_| _O(n)_| Medium || +232| [Implement Queue using Stacks](https://leetcode.com/problems/implement-queue-using-stacks/) | [C++](./C++/implement-queue-using-stacks.cpp) [Python](./Python/implement-queue-using-stacks.py) | _O(1), amortized_| _O(n)_| Easy | EPI, LintCode | +255| [Verify Preorder Sequence in Binary Search Tree](https://leetcode.com/problems/verify-preorder-sequence-in-binary-search-tree/) | [C++](./C++/verify-preorder-sequence-in-binary-search-tree.cpp) [Python](./Python/verify-preorder-sequence-in-binary-search-tree.py) | _O(n)_| _O(1)_| Medium |📖|| +272| [Closest Binary Search Tree Value II](https://leetcode.com/problems/closest-binary-search-tree-value-ii/) | [C++](./C++/closest-binary-search-tree-value-ii.cpp) [Python](./Python/closest-binary-search-tree-value-ii.py) | _O(h + k)_| _O(h)_| Hard |📖|| +331| [Verify Preorder Serialization of a Binary Tree](https://leetcode.com/problems/verify-preorder-serialization-of-a-binary-tree/) | [C++](./C++/verify-preorder-serialization-of-a-binary-tree.cpp) [Python](./Python/verify-preorder-serialization-of-a-binary-tree.py) | _O(n)_| _O(1)_| Medium ||| +341| [Flatten Nested List Iterator](https://leetcode.com/problems/flatten-nested-list-iterator/)| [C++](./C++/flatten-nested-list-iterator.cpp) [Python](./Python/flatten-nested-list-iterator.py) | _O(n)_ | _O(h)_ | Medium |📖| Iterator | +385| [Mini Parser](https://leetcode.com/problems/mini-parser/)| [C++](./C++/mini-parser.cpp) [Python](./Python/mini-parser.py) | _O(n)_ | _O(h)_ | Medium ||| +394| [Decode String](https://leetcode.com/problems/decode-string/)| [C++](./C++/decode-string.cpp) [Python](./Python/decode-string.py) | _O(n)_ | _O(h)_ | Medium ||| +439| [Ternary Expression Parser](https://leetcode.com/problems/ternary-expression-parser/) | [C++](./C++/ternary-expression-parser.cpp) [Python](./Python/ternary-expression-parser.py) | _O(n)_ | _O(1)_ | Medium |📖| +456| [132 Pattern](https://leetcode.com/problems/132-pattern/) | [C++](./C++/132-pattern.cpp) [Python](./Python/132-pattern.py) | _O(n)_ | _O(n)_ | Medium || +636| [Exclusive Time of Functions](https://leetcode.com/problems/exclusive-time-of-functions/) | [C++](./C++/exclusive-time-of-functions.cpp) [Python](./Python/exclusive-time-of-functions.py) | _O(n)_ | _O(n)_ | Medium || +682| [Baseball Game](https://leetcode.com/problems/baseball-game/) | [C++](./C++/baseball-game.cpp) [Python](./Python/baseball-game.py) | _O(n)_ | _O(n)_ | Easy || +726| [Number of Atoms](https://leetcode.com/problems/number-of-atoms/) | [C++](./C++/number-of-atoms.cpp) [Python](./Python/number-of-atoms.py) | _O(n)_ | _O(n)_ | Hard || +735| [Asteroid Collision](https://leetcode.com/problems/asteroid-collision/) | [C++](./C++/asteroid-collision.cpp) [Python](./Python/asteroid-collision.py) | _O(n)_ | _O(n)_ | Medium || +736| [Parse Lisp Expression](https://leetcode.com/problems/parse-lisp-expression/) | [C++](./C++/parse-lisp-expression.cpp) [Python](./Python/parse-lisp-expression.py) | _O(n^2)_ | _O(n^2)_ | Hard || +739| [Daily Temperatures](https://leetcode.com/problems/daily-temperatures/) | [C++](./C++/daily-temperatures.cpp) [Python](./Python/daily-temperatures.py) | _O(n)_ | _O(n)_ | Medium || +770| [Basic Calculator IV](https://leetcode.com/problems/basic-calculator-iv/) | [C++](./C++/basic-calculator-iv.cpp) [Python](./Python/basic-calculator-iv.py) | add: _O(d * t)_
sub: _O(d * t)_
mul: _O(d * t^2)_
eval: _O(d * t)_
to_list: _O(d * tlogt)_ | _O(e + d * t)_ | Hard || +772| [Basic Calculator III](https://leetcode.com/problems/basic-calculator-iii/) | [C++](./C++/basic-calculator-iii.cpp) [Python](./Python/basic-calculator-iii.py) | _O(n)_ | _O(n)_ | Hard || +853| [Car Fleet](https://leetcode.com/problems/car-fleet/) | [C++](./C++/car-fleet.cpp) [Python](./Python/car-fleet.py) | _O(nlogn)_ | _O(n)_ | Medium || +856| [Score of Parentheses](https://leetcode.com/problems/score-of-parentheses/) | [C++](./C++/score-of-parentheses.cpp) [Python](./Python/score-of-parentheses.py) | _O(n)_ | _O(1)_ | Medium || +872| [Leaf-Similar Trees](https://leetcode.com/problems/leaf-similar-trees/) | [C++](./C++/leaf-similar-trees.cpp) [Python](./Python/leaf-similar-trees.py) | _O(n)_ | _O(h)_ | Easy || + +## Queue +| # | Title | Solution | Time | Space | Difficulty | Tag | Note| +|-----|---------------- | --------------- | --------------- | --------------- | ------------- |--------------|-----| +239| [Sliding Window Maximum](https://leetcode.com/problems/sliding-window-maximum/)| [C++](./C++/sliding-window-maximum.cpp) [Python](./Python/sliding-window-maximum.py) | _O(n)_ | _O(k)_ | Hard | EPI, LintCode | +281| [Zigzag Iterator](https://leetcode.com/problems/zigzag-iterator/)| [C++](./C++/zigzag-iterator.cpp) [Python](./Python/zigzag-iterator.py) | _O(n)_ | _O(k)_ | Medium |📖|| +346| [Moving Average from Data Stream](https://leetcode.com/problems/moving-average-from-data-stream/)| [C++](./C++/moving-average-from-data-stream.cpp) [Python](./Python/moving-average-from-data-stream.py) | _O(1)_ | _O(w)_ | Easy |📖|| +862| [Shortest Subarray with Sum at Least K](https://leetcode.com/problems/shortest-subarray-with-sum-at-least-k/)| [C++](./C++/shortest-subarray-with-sum-at-least-k.cpp) [Python](./Python/shortest-subarray-with-sum-at-least-k.py) | _O(n)_ | _O(n)_ | Hard ||| + +## Heap +| # | Title | Solution | Time | Space | Difficulty | Tag | Note| +|-----|---------------- | --------------- | --------------- | --------------- | ------------- |--------------|-----| +264| [Ugly Number II](https://leetcode.com/problems/ugly-number-ii/) | [C++](./C++/ugly-number-ii.cpp) [Python](./Python/ugly-number-ii.py) | _O(n)_ | _O(1)_ | Medium | CTCI, LintCode | BST, Heap | +295| [Find Median from Data Stream](https://leetcode.com/problems/find-median-from-data-stream/) | [C++](./C++/find-median-from-data-stream.cpp) [Python](./Python/find-median-from-data-stream.py) | _O(nlogn)_ | _O(n)_ | Hard | EPI, LintCode | BST, Heap | +313| [Super Ugly Number](https://leetcode.com/problems/super-ugly-number/) | [C++](./C++/super-ugly-number.cpp) [Python](./Python/super-ugly-number.py) | _O(n * k)_ | _O(n + k)_ | Medium || BST, Heap | +358| [Rearrange String k Distance Apart](https://leetcode.com/problems/rearrange-string-k-distance-apart/)| [C++](./C++/rearrange-string-k-distance-apart.cpp) [Python](./Python/rearrange-string-k-distance-apart.py) | _O(n)_ | _O(n)_ | Hard |📖| Greedy, Heap | +373 | [Find K Pairs with Smallest Sums](https://leetcode.com/problems/find-k-pairs-with-smallest-sums/) | [C++](./C++/find-k-pairs-with-smallest-sums.cpp) [Python](./Python/find-k-pairs-with-smallest-sums.py) | _O(k * log(min(n, m, k)))_ | _O(min(n, m, k))_ | Medium ||| +378 | [Kth Smallest Element in a Sorted Matrix](https://leetcode.com/problems/kth-smallest-element-in-a-sorted-matrix/) | [C++](./C++/kth-smallest-element-in-a-sorted-matrix.cpp) [Python](./Python/kth-smallest-element-in-a-sorted-matrix.py) | _O(k * log(min(n, m, k)))_ | _O(min(n, m, k))_ | Medium | LintCode || +407 | [Trapping Rain Water II](https://leetcode.com/problems/trapping-rain-water-ii/) | [C++](./C++/trapping-rain-water-ii.cpp) [Python](./Python/trapping-rain-water-ii.py) | _O(m * n * (logm + logn))_ | _O(m * n)_ | Hard | LintCode || +632 | [Smallest Range](https://leetcode.com/problems/smallest-range/) | [C++](./C++/smallest-range.cpp) [Python](./Python/smallest-range.py) | _O(nlogk)_ | _O(k)_ | Hard ||| +846 | [Hand of Straights](https://leetcode.com/problems/hand-of-straights/) | [C++](./C++/hand-of-straights.cpp) [Python](./Python/hand-of-straights.py) | _O(nlogn)_ | _O(n)_ | Medium ||| +855 | [Exam Room](https://leetcode.com/problems/exam-room/) | [C++](./C++/exam-room.cpp) [Python](./Python/exam-room.py) | seat: _O(logn)_
leave: _O(logn)_ | _O(n)_ | Medium || BST, Hash | +857 | [Minimum Cost to Hire K Workers](https://leetcode.com/problems/minimum-cost-to-hire-k-workers/) | [C++](./C++/minimum-cost-to-hire-k-workers.cpp) [Python](./Python/minimum-cost-to-hire-k-workers.py) | _O(nlogn)_ | _O(n)_ | Hard || Sort | +871 | [Minimum Number of Refueling Stops](https://leetcode.com/problems/minimum-number-of-refueling-stops/) | [C++](./C++/minimum-number-of-refueling-stops.cpp) [Python](./Python/minimum-number-of-refueling-stops.py) | _O(nlogn)_ | _O(n)_ | Hard || Sort | + +## Tree +| # | Title | Solution | Time | Space | Difficulty | Tag | Note| +|-----|---------------- | --------------- | --------------- | --------------- | ------------- |--------------|-----| +094 | [Binary Tree Inorder Traversal](https://leetcode.com/problems/binary-tree-inorder-traversal/) | [C++](./C++/binary-tree-inorder-traversal.cpp) [Python](./Python/binary-tree-inorder-traversal.py) | _O(n)_| _O(1)_| Medium || `Morris Traversal` | +099 | [Recover Binary Search Tree](https://leetcode.com/problems/recover-binary-search-tree/) | [C++](./C++/recover-binary-search-tree.cpp) [Python](./Python/recover-binary-search-tree.py) | _O(n)_| _O(1)_| Hard || `Morris Traversal` +144 | [Binary Tree Preorder Traversal](https://leetcode.com/problems/binary-tree-preorder-traversal/) | [C++](./C++/binary-tree-preorder-traversal.cpp) [Python](./Python/binary-tree-preorder-traversal.py) | _O(n)_| _O(1)_| Medium || `Morris Traversal` +145 | [Binary Tree Postorder Traversal](https://leetcode.com/problems/binary-tree-postorder-traversal/) | [C++](./C++/binary-tree-postorder-traversal.cpp) [Python](./Python/binary-tree-postorder-traversal.py) | _O(n)_| _O(1)_| Hard || `Morris Traversal` +208 | [Implement Trie (Prefix Tree)](https://leetcode.com/problems/implement-trie-prefix-tree/) | [C++](./C++/implement-trie-prefix-tree.cpp) [Python](./Python/implement-trie-prefix-tree.py) | _O(n)_ | _O(1)_ | Medium || Trie +211 | [Add and Search Word - Data structure design](https://leetcode.com/problems/add-and-search-word-data-structure-design/) | [C++](./C++/add-and-search-word-data-structure-design.cpp) [Python](./Python/add-and-search-word-data-structure-design.py) | _O(min(n, h))_ | _O(min(n, h))_ | Medium || Trie, DFS +226| [Invert Binary Tree](https://leetcode.com/problems/invert-binary-tree/) | [C++](./C++/invert-binary-tree.cpp) [Python](./Python/invert-binary-tree.py) | _O(n)_ | _O(h)_, _O(w)_ | Easy || +297 | [Serialize and Deserialize Binary Tree](https://leetcode.com/problems/serialize-and-deserialize-binary-tree/) | [C++](./C++/serialize-and-deserialize-binary-tree.cpp) [Python](./Python/serialize-and-deserialize-binary-tree.py) | _O(n)_ | _O(h)_ | Hard | LintCode | DFS +307 | [Range Sum Query - Mutable](https://leetcode.com/problems/range-sum-query-mutable/) | [C++](./C++/range-sum-query-mutable.cpp) [Python](./Python/range-sum-query-mutable.py) | ctor: _O(n)_, update: _O(logn)_, query: _O(logn)_ | _O(n)_ | Medium | LintCode | DFS, Segment Tree, BIT +308 | [Range Sum Query 2D - Mutable](https://leetcode.com/problems/range-sum-query-2d-mutable/) | [C++](./C++/range-sum-query-2d-mutable.cpp) [Python](./Python/range-sum-query-2d-mutable.py) | ctor: _O(m * n)_, update: _O(logm + logn)_, query: _O(logm + logn)_ | _O(m * n)_ | Hard | 📖 | DFS, Segment Tree, BIT +315|[Count of Smaller Numbers After Self](https://leetcode.com/problems/count-of-smaller-numbers-after-self/)| [C++](./C++/count-of-smaller-numbers-after-self.cpp) [Python](./Python/count-of-smaller-numbers-after-self.py)| _O(nlogn)_ | _O(n)_ | Hard | LintCode | BST, BIT, Divide and Conquer | +525| [Contiguous Array](https://leetcode.com/problems/contiguous-array/) |[C++](./C++/contiguous-array.cpp) [Python](./Python/contiguous-array.py) | _O(n)_ | _O(n)_ | Medium | | +529 | [Minesweeper](https://leetcode.com/problems/minesweeper/) | [C++](./C++/minesweeper.cpp) [Python](./Python/minesweeper.py) | _O(m * n)_ | _O(m + n)_ | Medium || +536 | [Construct Binary Tree from String](https://leetcode.com/problems/construct-binary-tree-from-string/) | [C++](./C++/construct-binary-tree-from-string.cpp) [Python](./Python/construct-binary-tree-from-string.py) | _O(n)_ | _O(h)_ | Medium | 📖 | +538 | [Convert BST to Greater Tree](https://leetcode.com/problems/convert-bst-to-greater-tree/) | [C++](./C++/convert-bst-to-greater-tree.cpp) [Python](./Python/convert-bst-to-greater-tree.py) | _O(n)_ | _O(h)_ | Easy || +543 | [Diameter of Binary Tree](https://leetcode.com/problems/diameter-of-binary-tree/) | [C++](./C++/diameter-of-binary-tree.cpp) [Python](./Python/diameter-of-binary-tree.py) | _O(n)_ | _O(h)_ | Easy || +545 | [Boundary of Binary Tree](https://leetcode.com/problems/boundary-of-binary-tree/) | [C++](./C++/boundary-of-binary-tree.cpp) [Python](./Python/boundary-of-binary-tree.py) | _O(n)_ | _O(h)_ | Medium |📖| +548 | [Split Array with Equal Sum](https://leetcode.com/problems/split-array-with-equal-sum/) | [C++](./C++/split-array-with-equal-sum.cpp) [Python](./Python/split-array-with-equal-sum.py) | _O(n^2)_ | _O(n)_ | Medium |📖| +563 |[Binary Tree Tilt](https://leetcode.com/problems/binary-tree-tilt/)| [C++](./C++/binary-tree-tilt.cpp) [Python](./Python/binary-tree-tilt.py)| _O(n)_ | _O(n)_ | Easy | | | +572 |[Subtree of Another Tree](https://leetcode.com/problems/construct-string-from-binary-tree/)| [C++](./C++/subtree-of-another-tree.cpp) [Python](./Python/subtree-of-another-tree.py)| _O(m * n)_ | _O(h)_ | Easy | | | +606 |[Construct String from Binary Tree](https://leetcode.com/problems/construct-string-from-binary-tree/)| [C++](./C++/construct-string-from-binary-tree.cpp) [Python](./Python/construct-string-from-binary-tree.py)| _O(n)_ | _O(h)_ | Easy | | | +617 |[Merge Two Binary Trees](https://leetcode.com/problems/merge-two-binary-trees/)| [C++](./C++/merge-two-binary-trees.cpp) [Python](./Python/merge-two-binary-trees.py)| _O(n)_ | _O(h)_ | Easy | | | +623 |[Add One Row to Tree](https://leetcode.com/problems/add-one-row-to-tree/)| [C++](./C++/add-one-row-to-tree.cpp) [Python](./Python/add-one-row-to-tree.py)| _O(n)_ | _O(h)_ | Medium | | | +637 |[Average of Levels in Binary Tree](https://leetcode.com/problems/average-of-levels-in-binary-tree/)| [C++](./C++/average-of-levels-in-binary-tree.cpp) [Python](./Python/average-of-levels-in-binary-tree.py)| _O(n)_ | _O(h)_ | Easy | | | +652 |[Find Duplicate Subtrees](https://leetcode.com/problems/find-duplicate-subtrees/)| [C++](./C++/find-duplicate-subtrees.cpp) [Python](./Python/find-duplicate-subtrees.py)| _O(n)_ | _O(n)_ | Medium | | DFS, Hash | +653 |[Two Sum IV - Input is a BST](https://leetcode.com/problems/two-sum-iv-input-is-a-bst/)| [C++](./C++/two-sum-iv-input-is-a-bst.cpp) [Python](./Python/two-sum-iv-input-is-a-bst.py)| _O(n)_ | _O(h)_ | Easy | | Two Pointers | +654 |[Maximum Binary Tree](https://leetcode.com/problems/maximum-binary-tree/)| [C++](./C++/maximum-binary-tree.cpp) [Python](./Python/maximum-binary-tree.py)| _O(n)_ | _O(n)_ | Medium | LintCode | Descending Stack | +655 | [Print Binary Tree](https://leetcode.com/problems/print-binary-tree/) | [C++](./C++/print-binary-tree.cpp) [Python](./Python/print-binary-tree.py) | _O(n)_ | _O(h)_ | Medium | | +662 | [Maximum Width of Binary Tree](https://leetcode.com/problems/maximum-width-of-binary-tree/) | [C++](./C++/maximum-width-of-binary-tree.cpp) [Python](./Python/maximum-width-of-binary-tree.py) | _O(n)_ | _O(h)_ | Medium | | DFS +663 | [Equal Tree Partition](https://leetcode.com/problems/strange-printer/) | [C++](./C++/equal-tree-partition.cpp) [Python](./Python/equal-tree-partition.py) | _O(n)_ | _O(n)_ | Medium | 📖 | Hash +677 | [Map Sum Pairs](https://leetcode.com/problems/map-sum-pairs/) | [C++](./C++/map-sum-pairs.cpp) [Python](./Python/map-sum-pairs.py) | _O(n)_ | _O(t)_ | Medium || Trie +684 | [Redundant Connection](https://leetcode.com/problems/redundant-connection/) | [C++](./C++/redundant-connection.cpp) [Python](./Python/redundant-connection.py) | _O(n)_ | _O(n)_ | Medium || Union Find +685 | [Redundant Connection II](https://leetcode.com/problems/redundant-connection-ii/) | [C++](./C++/redundant-connection-ii.cpp) [Python](./Python/redundant-connection-ii.py) | _O(n)_ | _O(n)_ | Hard || Union Find +687 | [Longest Univalue Path](https://leetcode.com/problems/longest-univalue-path/) | [C++](./C++/longest-univalue-path.cpp) [Python](./Python/longest-univalue-path.py) | _O(n)_ | _O(h)_ | Easy || +699 | [Falling Squares](https://leetcode.com/problems/falling-squares/) | [C++](./C++/falling-squares.cpp) [Python](./Python/falling-squares.py) | _O(nlogn)_ | _O(n)_ | Hard || Segment Tree | +814 | [Binary Tree Pruning](https://leetcode.com/problems/binary-tree-pruning/) | [C++](./C++/binary-tree-pruning.cpp) [Python](./Python/binary-tree-pruning.py) | _O(n)_ | _O(h)_ | Medium || DFS | +850 | [Rectangle Area II](https://leetcode.com/problems/rectangle-area-ii/) | [C++](./C++/rectangle-area-ii.cpp) [Python](./Python/rectangle-area-ii.py) | _O(nlogn)_ | _O(n)_ | Hard || Segment Tree | +863 | [All Nodes Distance K in Binary Tree](https://leetcode.com/problems/all-nodes-distance-k-in-binary-tree/) | [C++](./C++/all-nodes-distance-k-in-binary-tree.cpp) [Python](./Python/all-nodes-distance-k-in-binary-tree.py) | _O(n)_ | _O(n)_ | Medium || DFS + BFS | +866 | [Smallest Subtree with all the Deepest Nodes](https://leetcode.com/problems/smallest-subtree-with-all-the-deepest-nodes/) | [C++](./C++/smallest-subtree-with-all-the-deepest-nodes.cpp) [Python](./Python/smallest-subtree-with-all-the-deepest-nodes.py) | _O(n)_ | _O(h)_ | Medium || DFS | + +## Hash Table +| # | Title | Solution | Time | Space | Difficulty | Tag | Note| +|-----|---------------- | --------------- | --------------- | --------------- | ------------- |--------------|-----| +001| [Two Sum](https://leetcode.com/problems/two-sum/) | [C++](./C++/two-sum.cpp) [Python](./Python/two-sum.py) | _O(n)_ | _O(n)_ | Easy || +003| [Longest Substring Without Repeating Characters](https://leetcode.com/problems/longest-substring-without-repeating-characters/) | [C++](./C++/longest-substring-without-repeating-characters.cpp) [Python](./Python/longest-substring-without-repeating-characters.py) | _O(n)_ | _O(1)_ | Medium || +030| [Substring with Concatenation of All Words](https://leetcode.com/problems/substring-with-concatenation-of-all-words/) | [C++](./C++/substring-with-concatenation-of-all-words.cpp) [Python](./Python/substring-with-concatenation-of-all-words.py) | _O((m + n) * k)_ | _O(n * k)_ | Hard || +036| [Valid Sudoku](https://leetcode.com/problems/valid-sudoku/) | [C++](./C++/valid-sudoku.cpp) [Python](./Python/valid-sudoku.py) | _O(9^2)_ | _O(9)_ | Easy || +049| [Group Anagrams](https://leetcode.com/problems/group-anagrams/) | [C++](./C++/group-anagrams.cpp) [Python](./Python/group-anagrams.py) | _O(n * glogg)_ | _O(n)_ | Medium || +076| [Minimum Window Substring](https://leetcode.com/problems/minimum-window-substring/) | [C++](./C++/minimum-window-substring.cpp) [Python](./Python/minimum-window-substring.py) | _O(n)_ | _O(k)_ | Hard || +149| [Max Points on a Line](https://leetcode.com/problems/max-points-on-a-line/) | [C++](./C++/max-points-on-a-line.cpp) [Python](./Python/max-points-on-a-line.py) | _O(n^2)_ | _O(n)_ | Hard || +159| [Longest Substring with At Most Two Distinct Characters](https://leetcode.com/problems/longest-substring-with-at-most-two-distinct-characters/)| [C++](./C++/longest-substring-with-at-most-two-distinct-characters.cpp) [Python](./Python/longest-substring-with-at-most-two-distinct-characters.py) | _O(n)_ | _O(1)_ | Hard |📖| +170| [Two Sum III - Data structure design](https://leetcode.com/problems/two-sum-iii-data-structure-design/) | [C++](./C++/two-sum-iii-data-structure-design.cpp) [Python](./Python/two-sum-iii-data-structure-design.py) | _O(n)_ | _O(n)_ | Easy | 📖 | +187| [Repeated DNA Sequences](https://leetcode.com/problems/repeated-dna-sequences/) | [Python](./Python/repeated-dna-sequences.py) | _O(n)_ | _O(n)_ | Medium || +202| [Happy Number](https://leetcode.com/problems/happy-number/) | [C++](./C++/happy-number.cpp) [Python](./Python/happy-number.py) | _O(k)_ | _O(k)_ | Easy || +204| [Count Primes](https://leetcode.com/problems/count-primes/) | [C++](./C++/count-primes.cpp) [Python](./Python/count-primes.py) | _O(n)_ | _O(n)_ | Easy || +205| [Isomorphic Strings](https://leetcode.com/problems/isomorphic-strings/) | [C++](./C++/isomorphic-strings.cpp) [Python](./Python/isomorphic-strings.py) | _O(n)_ | _O(1)_ | Easy || +217| [Contains Duplicate](https://leetcode.com/problems/contains-duplicate/) | [C++](./C++/contains-duplicate.cpp) [Python](./Python/contains-duplicate.py) | _O(n)_ | _O(n)_ | Easy || +219| [Contains Duplicate II](https://leetcode.com/problems/contains-duplicate-ii/) | [C++](./C++/contains-duplicate-ii.cpp) [Python](./Python/contains-duplicate-ii.py) | _O(n)_ | _O(n)_ | Easy || +244| [Shortest Word Distance II](https://leetcode.com/problems/shortest-word-distance-ii/) | [C++](./C++/shortest-word-distance-ii.cpp) [Python](./Python/shortest-word-distance-ii.py) | ctor: _O(n)_, lookup: _O(a + b)_ | _O(n)_ | Medium |📖|| +246| [Strobogrammatic Number](https://leetcode.com/problems/strobogrammatic-number/) | [C++](./C++/strobogrammatic-number.cpp) [Python](./Python/strobogrammatic-number.py) | _O(n)_ | _O(1)_ | Easy |📖|| +249| [Group Shifted Strings](https://leetcode.com/problems/group-shifted-strings/) | [C++](./C++/group-shifted-strings.cpp) [Python](./Python/group-shifted-strings.py) | _O(nlogn)_ | _O(n)_ | Easy |📖|| +266| [Palindrome Permutation](https://leetcode.com/problems/palindrome-permutation/) | [C++](./C++/palindrome-permutation.cpp) [Python](./Python/palindrome-permutation.py) | _O(n)_ | _O(1)_ | Easy |📖|| +288| [Unique Word Abbreviation](https://leetcode.com/problems/unique-word-abbreviation/) | [C++](./C++/unique-word-abbreviation.cpp) [Python](./Python/unique-word-abbreviation.py) | ctor: _O(n)_, lookup: _O(1)_ | _O(k)_ | Easy |📖|| +290| [Word Pattern](https://leetcode.com/problems/word-pattern/) | [C++](./C++/word-pattern.cpp) [Python](./Python/word-pattern.py) | _O(n)_ | _O(c)_ | Easy | variant of [Isomorphic Strings](https://leetcode.com/problems/isomorphic-strings/) || +299| [Bulls and Cows](https://leetcode.com/problems/bulls-and-cows/) | [C++](./C++/bulls-and-cows.cpp) [Python](./Python/bulls-and-cows.py) | _O(n)_ | _O(1)_ | Easy ||| +305| [Number of Islands II](https://leetcode.com/problems/number-of-islands-ii/) | [C++](./C++/number-of-islands-ii.cpp) [Python](./Python/number-of-islands-ii.py) | _O(k)_ | _O(k)_| Hard | LintCode, 📖 | Union Find +314| [Binary Tree Vertical Order Traversal](https://leetcode.com/problems/binary-tree-vertical-order-traversal/) | [C++](./C++/binary-tree-vertical-order-traversal.cpp) [Python](./Python/binary-tree-vertical-order-traversal.py) | _O(n)_ | _O(n)_| Medium | 📖 | BFS +323| [Number of Connected Components in an Undirected Graph](https://leetcode.com/problems/number-of-connected-components-in-an-undirected-graph/) | [C++](./C++/number-of-connected-components-in-an-undirected-graph.cpp) [Python](./Python/number-of-connected-components-in-an-undirected-graph.py) | _O(n)_ | _O(n)_| Medium | 📖 | Union Find +325| [Maximum Size Subarray Sum Equals k](https://leetcode.com/problems/maximum-size-subarray-sum-equals-k/) | [C++](./C++/maximum-size-subarray-sum-equals-k.cpp) [Python](./Python/maximum-size-subarray-sum-equals-k.py) | _O(n)_ | _O(n)_| Medium | 📖 | +336| [Palindrome Pairs](https://leetcode.com/problems/palindrome-pairs/) | [C++](./C++/palindrome-pairs.cpp) [Python](./Python/palindrome-pairs.py) | _O(n * k^2)_ | _O(n * k)_ | Hard | | | +340| [Longest Substring with At Most K Distinct Characters](https://leetcode.com/problems/longest-substring-with-at-most-k-distinct-characters/)| [C++](./C++/longest-substring-with-at-most-k-distinct-characters.cpp) [Python](./Python/longest-substring-with-at-most-k-distinct-characters.py) | _O(n)_ | _O(1)_ | Hard |📖| +356| [Line Reflection](https://leetcode.com/problems/line-reflection/) | [C++](./C++/line-reflection.cpp) [Python](./Python/line-reflection.py) | _O(n)_| _O(n)_| Medium |📖| Hash, Two Pointers | +387| [First Unique Character in a String](https://leetcode.com/problems/first-unique-character-in-a-string/) | [C++](./C++/first-unique-character-in-a-string.cpp) [Python](./Python/first-unique-character-in-a-string.py) | _O(n)_| _O(n)_| Easy ||| +388| [Longest Absolute File Path](https://leetcode.com/problems/longest-absolute-file-path/) | [C++](./C++/longest-absolute-file-path.cpp) [Python](./Python/longest-absolute-file-path.py) | _O(n)_| _O(d)_| Medium || Stack | +409| [Longest Palindrome](https://leetcode.com/problems/longest-palindrome/) | [C++](./C++/longest-palindrome.cpp) [Python](./Python/longest-palindrome.py) | _O(n)_| _O(1)_| Easy ||| +424| [Longest Repeating Character Replacement](https://leetcode.com/problems/longest-repeating-character-replacement/) | [C++](./C++/longest-repeating-character-replacement.cpp) [Python](./Python/longest-repeating-character-replacement.py) | _O(n)_| _O(1)_| Medium ||| +438| [Find All Anagrams in a String](https://leetcode.com/problems/find-all-anagrams-in-a-string/) | [C++](./C++/find-all-anagrams-in-a-string.cpp) [Python](./Python/find-all-anagrams-in-a-string.py) | _O(n)_ | _O(1)_ | Easy || +447| [Number of Boomerangs](https://leetcode.com/problems/number-of-boomerangs/) | [C++](./C++/number-of-boomerangs.cpp) [Python](./Python/number-of-boomerangs.py) | _O(n^2)_ | _O(n)_ | Easy || +454| [4Sum II](https://leetcode.com/problems/4sum-ii/) | [C++](./C++/4sum-ii.cpp) [Python](./Python/4sum-ii.py) | _O(n^2)_ | _O(n^2)_ | Medium || +470| [Implement Rand10() Using Rand7()](https://leetcode.com/problems/implement-rand10-using-rand7/) | [C++](./C++/implement-rand10-using-rand7.cpp) [Python](./Python/implement-rand10-using-rand7.py) | _O(1)_ | _O(1)_ | Medium || +473| [Matchsticks to Square](https://leetcode.com/problems/matchsticks-to-square/) | [C++](./C++/matchsticks-to-square.cpp) [Python](./Python/matchsticks-to-square.py) | _O(n * s * 2^n)_ | _O(n * (2^n + s))_ | Medium || +523| [Continuous Subarray Sum](https://leetcode.com/problems/continuous-subarray-sum/) | [C++](./C++/continuous-subarray-sum.cpp) [Python](./Python/continuous-subarray-sum.py) | _O(n)_ | _O(k)_ | Medium || +532| [K-diff Pairs in an Array](https://leetcode.com/problems/k-diff-pairs-in-an-array/) |[C++](./C++/k-diff-pairs-in-an-array.cpp) [Python](./Python/k-diff-pairs-in-an-array.py) | _O(n)_ | _O(n)_ | Easy | | +554| [Brick Wall](https://leetcode.com/problems/brick-wall/) |[C++](./C++/brick-wall.cpp) [Python](./Python/brick-wall.py) | _O(n)_ | _O(m)_ | Medium | | +560| [Subarray Sum Equals K](https://leetcode.com/problems/subarray-sum-equals-k/) |[C++](./C++/subarray-sum-equals-k.cpp) [Python](./Python/subarray-sum-equals-k.py) | _O(n)_ | _O(n)_ | Medium | | +561| [Array Partition I](https://leetcode.com/problems/array-partition-i/) |[C++](./C++/array-partition-i.cpp) [Python](./Python/array-partition-i.py) | _O(r)_ | _O(r)_ | Easy | | +575| [Distribute Candies](https://leetcode.com/problems/distribute-candies/) |[C++](./C++/distribute-candies.cpp) [Python](./Python/distribute-candies.py) | _O(n)_ | _O(n)_ | Easy | | +594| [Longest Harmonious Subsequence](https://leetcode.com/problems/longest-harmonious-subsequence/) |[C++](./C++/longest-harmonious-subsequence.cpp) [Python](./Python/longest-harmonious-subsequence.py) | _O(n)_ | _O(n)_ | Easy | | +599| [Minimum Index Sum of Two Lists](https://leetcode.com/problems/minimum-index-sum-of-two-lists/) |[C++](./C++/minimum-index-sum-of-two-lists.cpp) [Python](./Python/minimum-index-sum-of-two-lists.py) | _O((m + n) * l)_ | _O(m * l)_ | Easy | | +609| [Find Duplicate File in System](https://leetcode.com/problems/find-duplicate-file-in-system/) |[C++](./C++/find-duplicate-file-in-system.cpp) [Python](./Python/find-duplicate-file-in-system.py) | _O(n * l)_ | _O(n * l)_ | Medium | | +721| [Accounts Merge](https://leetcode.com/problems/accounts-merge/) | [C++](./C++/accounts-merge.cpp) [Python](./Python/accounts-merge.py) | _O(nlogn)_ | _O(n)_| Medium || Union Find +734| [Sentence Similarity](https://leetcode.com/problems/sentence-similarity/) | [C++](./C++/sentence-similarity.cpp) [Python](./Python/sentence-similarity.py) | _O(n + p)_ | _O(p)_| Easy || +737| [Sentence Similarity II](https://leetcode.com/problems/sentence-similarity-ii/) | [C++](./C++/sentence-similarity-ii.cpp) [Python](./Python/sentence-similarity-ii.py) | _O(n + p)_ | _O(p)_| Medium || Union Find +748 | [Shortest Completing Word](https://leetcode.com/problems/shortest-completing-word/) | [C++](./C++/shortest-completing-word.cpp) [Python](./Python/shortest-completing-word.py) | _O(n)_ | _O(1)_ | Easy || +760 | [Find Anagram Mappings](https://leetcode.com/problems/find-anagram-mappings/) | [C++](./C++/find-anagram-mappings.cpp) [Python](./Python/find-anagram-mappings.py) | _O(n)_ | _O(n)_ | Easy || +771 | [Jewels and Stones](https://leetcode.com/problems/jewels-and-stones/) | [C++](./C++/jewels-and-stones.cpp) [Python](./Python/jewels-and-stones.py) | _O(m + n)_ | _O(n)_ | Easy || +811 | [Subdomain Visit Count](https://leetcode.com/problems/subdomain-visit-count/) | [C++](./C++/subdomain-visit-count.cpp) [Python](./Python/subdomain-visit-count.py) | _O(n)_ | _O(n)_ | Easy || +822 | [Card Flipping Game](https://leetcode.com/problems/card-flipping-game/) | [C++](./C++/card-flipping-game.cpp) [Python](./Python/card-flipping-game.py) | _O(n)_ | _O(n)_ | Medium || +825 | [Friends Of Appropriate Ages](https://leetcode.com/problems/friends-of-appropriate-ages/) | [C++](./C++/friends-of-appropriate-ages.cpp) [Python](./Python/friends-of-appropriate-ages.py) | _O(a^2 + n)_ | _O(a)_ | Medium || +869 | [Reordered Power of 2](https://leetcode.com/problems/reordered-power-of-2/) | [C++](./C++/reordered-power-of-2.cpp) [Python](./Python/reordered-power-of-2.py) | _O(1)_ | _O(1)_ | Medium || +873 | [Length of Longest Fibonacci Subsequence](https://leetcode.com/problems/length-of-longest-fibonacci-subsequence/) | [C++](./C++/length-of-longest-fibonacci-subsequence.cpp) [Python](./Python/length-of-longest-fibonacci-subsequence.py) | _O(n^2)_ | _O(n)_ | Medium || + +## Math +| # | Title | Solution | Time | Space | Difficulty | Tag | Note| +|-----|---------------- | --------------- | --------------- | --------------- | ------------- |--------------|-----| +007| [Reverse Integer](https://leetcode.com/problems/reverse-integer/) | [C++](./C++/reverse-integer.cpp) [Python](./Python/reverse-integer.py) | _O(1)_ | _O(1)_ | Easy || +009| [Palindrome Number](https://leetcode.com/problems/palindrome-number/) | [C++](./C++/palindrome-number.cpp) [Python](./Python/palindrome-number.py) | _O(1)_ | _O(1)_ | Easy || +012| [Integer to Roman](https://leetcode.com/problems/integer-to-roman/) | [C++](./C++/integer-to-roman.cpp) [Python](./Python/integer-to-roman.py) | _O(n)_ | _O(1)_ | Medium || +013| [Roman to Integer](https://leetcode.com/problems/roman-to-integer/) | [C++](./C++/roman-to-integer.cpp) [Python](./Python/roman-to-integer.py) | _O(n)_ | _O(1)_ | Easy || +029| [Divide Two Integers](https://leetcode.com/problems/divide-two-integers/) | [C++](./C++/divide-two-integers.cpp) [Python](./Python/divide-two-integers.py) | _O(1)_ | _O(1)_ | Medium || +050| [Pow(x, n)](https://leetcode.com/problems/powx-n/) | [C++](./C++/powx-n.cpp) [Python](./Python/powx-n.py) | _O(1)_ | _O(1)_ | Medium || +060| [Permutation Sequence](https://leetcode.com/problems/permutation-sequence/) | [C++](./C++/permutation-sequence.cpp) [Python](./Python/permutation-sequence.py) | _O(n^2)_ | _O(n)_ | Medium || `Cantor Ordering` +065| [Valid Number](https://leetcode.com/problems/valid-number/) | [C++](./C++/valid-number.cpp) [Python](./Python/valid-number.py) | _O(n)_ | _O(1)_ | Hard || `Automata` +089| [Gray Code](https://leetcode.com/problems/gray-code/) | [C++](./C++/gray-code.cpp) [Python](./Python/gray-code.py) | _O(2^n)_ | _O(1)_ | Medium || +166| [Fraction to Recurring Decimal](https://leetcode.com/problems/fraction-to-recurring-decimal/) | [C++](./C++/fraction-to-recurring-decimal.cpp) [Python](./Python/fraction-to-recurring-decimal.py) | _O(logn)_ | _O(1)_ | Medium || +168| [Excel Sheet Column Title](https://leetcode.com/problems/excel-sheet-column-title/) | [C++](./C++/excel-sheet-column-title.cpp) [Python](./Python/excel-sheet-column-title.py) | _O(logn)_ | _O(1)_ | Easy || +171| [Excel Sheet Column Number](https://leetcode.com/problems/excel-sheet-column-number/) | [C++](./C++/excel-sheet-column-number.cpp) [Python](./Python/excel-sheet-column-number.py) | _O(n)_ | _O(1)_ | Easy || +172| [Factorial Trailing Zeroes](https://leetcode.com/problems/factorial-trailing-zeroes/) | [C++](./C++/factorial-trailing-zeroes.cpp) [Python](./Python/factorial-trailing-zeroes.py) | _O(1)_ | _O(1)_ | Easy || +223| [Rectangle Area](https://leetcode.com/problems/rectangle-area/) | [C++](./C++/rectangle-area.cpp) [Python](./Python/rectangle-area.py) | _O(1)_ | _O(1)_ | Easy || +233| [Number of Digit One](https://leetcode.com/problems/number-of-digit-one/) | [C++](./C++/number-of-digit-one.cpp) [Python](./Python/number-of-digit-one.py) | _O(1)_ | _O(1)_ | Hard | CTCI, LintCode| +248| [Strobogrammatic Number III](https://leetcode.com/problems/strobogrammatic-number-iii/) | [C++](./C++/strobogrammatic-number-iii.cpp) [Python](./Python/strobogrammatic-number-iii.py) | _O(5^(n/2))_ | _O(n)_ | Hard |📖|| +258| [Add Digits](https://leetcode.com/problems/add-digits/) | [C++](./C++/add-digits.cpp) [Python](./Python/add-digits.py) | _O(1)_ | _O(1)_ | Easy ||| +263| [Ugly Number](https://leetcode.com/problems/ugly-number/) | [C++](./C++/ugly-number.cpp) [Python](./Python/ugly-number.py) | _O(1)_ | _O(1)_ | Easy ||| +292| [Nim Game](https://leetcode.com/problems/nim-game/) | [C++](./C++/nim-game.cpp) [Python](./Python/nim-game.py) | _O(1)_ | _O(1)_ | Easy | LintCode || +319 | [Bulb Switcher](https://leetcode.com/problems/bulb-switcher/) | [C++](./C++/bulb-switcher.cpp) [Python](./Python/bulb-switcher.py) | _O(1)_ | _O(1)_ | Medium ||| +326 | [Power of Three](https://leetcode.com/problems/power-of-three/) | [C++](./C++/power-of-three.cpp) [Python](./Python/power-of-three.py) | _O(1)_ | _O(1)_ | Easy ||| +335 | [Self Crossing](https://leetcode.com/problems/self-crossing/) | [C++](./C++/self-crossing.cpp) [Python](./Python/self-crossing.py) | _O(n)_ | _O(1)_ | Hard ||| +338 | [Counting Bits](https://leetcode.com/problems/counting-bits/) | [C++](./C++/counting-bits.cpp) [Python](./Python/counting-bits.py) | _O(n)_ | _O(n)_ | Medium ||| +343 | [Integer Break](https://leetcode.com/problems/integer-break/) | [C++](./C++/integer-break.cpp) [Python](./Python/integer-break.py) | _O(logn)_ | _O(1)_ | Medium || Tricky, DP | +365 | [Water and Jug Problem](https://leetcode.com/problems/water-and-jug-problem/) | [C++](./C++/water-and-jug-problem.cpp) [Python](./Python/water-and-jug-problem.py) | _O(logn)_ | _O(1)_ | Medium || `Bézout's identity` | +372 | [Super Pow](https://leetcode.com/problems/super-pow/) | [C++](./C++/super-pow.cpp) [Python](./Python/super-pow.py) | _O(n)_ | _O(1)_ | Medium ||| +382 | [Linked List Random Node](https://leetcode.com/problems/linked-list-random-node/) | [C++](./C++/linked-list-random-node.cpp) [Python](./Python/linked-list-random-node.py) | _O(n)_ | _O(1)_ | Medium || `Reservoir Sampling` | +386 | [Lexicographical Numbers](https://leetcode.com/problems/lexicographical-numbers/) | [C++](./C++/lexicographical-numbers.cpp) [Python](./Python/lexicographical-numbers.py) | _O(n)_ | _O(1)_ | Medium ||| +390 | [Elimination Game](https://leetcode.com/problems/elimination-game/) | [C++](./C++/elimination-game.cpp) [Python](./Python/elimination-game.py) | _O(logn)_ | _O(1)_ | Medium || +391 | [Perfect Rectangle](https://leetcode.com/problems/perfect-rectangle/) | [C++](./C++/perfect-rectangle.cpp) [Python](./Python/perfect-rectangle.py) | _O(n)_ | _O(n)_ | Hard | | +398 | [Random Pick Index](https://leetcode.com/problems/random-pick-index/) | [C++](./C++/random-pick-index.cpp) [Python](./Python/random-pick-index.py) | _O(n)_ | _O(1)_ | Medium || `Reservoir Sampling` | +400 | [Nth Digit](https://leetcode.com/problems/nth-digit/) | [C++](./C++/nth-digit.cpp) [Python](./Python/nth-digit.py) | _O(logn)_ | _O(1)_ | Easy ||| +413 | [Arithmetic Slices](https://leetcode.com/problems/arithmetic-slices/) | [C++](./C++/arithmetic-slices.cpp) [Python](./Python/arithmetic-slices.py) | _O(n)_ | _O(1)_ | Medium ||| +423 | [Reconstruct Original Digits from English](https://leetcode.com/problems/reconstruct-original-digits-from-english/) | [C++](./C++/reconstruct-original-digits-from-english.cpp) [Python](./Python/reconstruct-original-digits-from-english.py) | _O(n)_ | _O(1)_ | Medium | [GCJ2016 - Round 1B](https://code.google.com/codejam/contest/11254486/dashboard#s=p0)|| +441 | [Arranging Coins](https://leetcode.com/problems/arranging-coins/) | [C++](./C++/arranging-coins.cpp) [Python](./Python/arranging-coins.py) | _O(nlogn)_ | _O(1)_ | Easy || Binary Search| +453 | [Minimum Moves to Equal Array Elements](https://leetcode.com/problems/minimum-moves-to-equal-array-elements/) | [C++](./C++/minimum-moves-to-equal-array-elements.cpp) [Python](./Python/minimum-moves-to-equal-array-elements.py) | _O(n)_ | _O(1)_ | Easy ||| +458 | [Poor Pigs](https://leetcode.com/problems/poor-pigs/) | [C++](./C++/poor-pigs.cpp) [Python](./Python/poor-pigs.py) | _O(n)_ | _O(1)_ | Easy ||| +469 | [Convex Polygon](https://leetcode.com/problems/convex-polygon/) | [C++](./C++/convex-polygon.cpp) [Python](./Python/convex-polygon.py) | _O(n)_ | _O(1)_ | Medium |📖|| +470 | [Implement Rand10() Using Rand7()](https://leetcode.com/problems/implement-rand10-using-rand7/) | [C++](./C++/implement-rand10-using-rand7.cpp) [Python](./Python/implement-rand10-using-rand7.py) | _O(1)_ | _O(1)_ | Medium ||| +517 | [Super Washing Machines](https://leetcode.com/problems/super-washing-machines/) | [C++](./C++/super-washing-machines.cpp) [Python](./Python/super-washing-machines.py) | _O(n)_ | _O(1)_ | Hard ||| +537 | [Complex Number Multiplication](https://leetcode.com/problems/complex-number-multiplication/) | [C++](./C++/complex-number-multiplication.cpp) [Python](./Python/complex-number-multiplication.py) | _O(1)_ | _O(1)_ | Medium ||| +553 | [Optimal Division](https://leetcode.com/problems/optimal-division/) | [C++](./C++/optimal-division.cpp) [Python](./Python/optimal-division.py) | _O(n)_ | _O(1)_ | Medium ||| +573 | [Squirrel Simulation](https://leetcode.com/problems/squirrel-simulation/) | [C++](./C++/squirrel-simulation.cpp) [Python](./Python/squirrel-simulation.py) | _O(n)_ | _O(1)_ | Medium |📖|| +592 | [Fraction Addition and Subtraction](https://leetcode.com/problems/fraction-addition-and-subtraction/) | [C++](./C++/fraction-addition-and-subtraction.cpp) [Python](./Python/fraction-addition-and-subtraction.py) | _O(nlogx)_ | _O(n)_ | Medium ||| +593 | [Valid Square](https://leetcode.com/problems/valid-square/) | [C++](./C++/valid-square.cpp) [Python](./Python/valid-square.py) | _O(1)_ | _O(1)_ | Medium ||| +598 | [Range Addition II](https://leetcode.com/problems/range-addition-ii/) | [C++](./C++/range-addition-ii.cpp) [Python](./Python/range-addition-ii.py) | _O(p)_ | _O(1)_ | Easy ||| +625 | [Minimum Factorization](https://leetcode.com/problems/minimum-factorization/) | [C++](./C++/minimum-factorization.cpp) [Python](./Python/minimum-factorization.py) | _O(loga)_ | _O(1)_ | Medium |📖|| +628| [Maximum Product of Three Numbers](https://leetcode.com/problems/maximum-product-of-three-numbers/) | [C++](./C++/maximum-product-of-three-numbers.cpp) [Python](./Python/maximum-product-of-three-numbers.py) | _O(n)_ | _O(1)_ | Easy ||| +633| [Sum of Square Numbers](https://leetcode.com/problems/sum-of-square-numbers/) | [C++](./C++/sum-of-square-numbers.cpp) [Python](./Python/sum-of-square-numbers.py) | _O(sqrt(c) * logc)_ | _O(1)_ | Easy ||| +634| [Find the Derangement of An Array](https://leetcode.com/problems/find-the-derangement-of-an-array/) | [C++](./C++/find-the-derangement-of-an-array.cpp) [Python](./Python/find-the-derangement-of-an-array.py) | _O(n)_ | _O(1)_ | Medium |📖|| +640| [Solve the Equation](https://leetcode.com/problems/solve-the-equation/) | [C++](./C++/solve-the-equation.cpp) [Python](./Python/solve-the-equation.py) | _O(n)_ | _O(n)_ | Medium || | +651 | [4 Keys Keyboard](https://leetcode.com/problems/4-keys-keyboard/) | [C++](./C++/4-keys-keyboard.cpp) [Python](./Python/4-keys-keyboard.py) | _O(1)_ | _O(1)_ | Medium |📖| Math, DP | +660 | [Remove 9](https://leetcode.com/problems/remove-9/) | [C++](./C++/remove-9.cpp) [Python](./Python/remove-9.py) | _O(logn)_ | _O(1)_ | Hard |📖|| +672 | [Bulb Switcher II](https://leetcode.com/problems/bulb-switcher-ii/) | [C++](./C++/bulb-switcher-ii.cpp) [Python](./Python/bulb-switcher-ii.py) | _O(1)_ | _O(1)_ | Medium ||| +728 | [Self Dividing Numbers](https://leetcode.com/problems/self-dividing-numbers/) | [C++](./C++/self-dividing-numbers.cpp) [Python](./Python/self-dividing-numbers.py) | _O(n)_ | _O(1)_ | Medium ||| +754 | [Reach a Number](https://leetcode.com/problems/reach-a-number/) | [C++](./C++/reach-a-number.cpp) [Python](./Python/reach-a-number.py) | _O(logn)_ | _O(1)_ | Medium ||| +775 | [Global and Local Inversions](https://leetcode.com/problems/global-and-local-inversions/) | [C++](./C++/global-and-local-inversions.cpp) [Python](./Python/global-and-local-inversions.py) | _O(n)_ | _O(1)_ | Medium ||| +779 | [K-th Symbol in Grammar](https://leetcode.com/problems/k-th-symbol-in-grammar/) | [C++](./C++/k-th-symbol-in-grammar.cpp) [Python](./Python/k-th-symbol-in-grammar.py) | _O(1)_ | _O(1)_ | Medium || +780 | [Reaching Points](https://leetcode.com/problems/reaching-points/) | [C++](./C++/reaching-points.cpp) [Python](./Python/reaching-points.py) | _O(log(max(m, n)))_ | _O(1)_ | Hard || +781 | [Rabbits in Forest](https://leetcode.com/problems/rabbits-in-forest/) | [C++](./C++/rabbits-in-forest.cpp) [Python](./Python/rabbits-in-forest.py) | _O(n)_ | _O(n)_ | Medium || +782 | [Transform to Chessboard](https://leetcode.com/problems/transform-to-chessboard/) | [C++](./C++/transform-to-chessboard.cpp) [Python](./Python/transform-to-chessboard.py) | _O(n^2)_ | _O(n)_ | Hard || +789 | [Escape The Ghosts](https://leetcode.com/problems/escape-the-ghosts/) | [C++](./C++/escape-the-ghosts.cpp) [Python](./Python/escape-the-ghosts.py) | _O(n)_ | _O(1)_ | Medium || +800 | [Similar RGB Color](https://leetcode.com/problems/similar-rgb-color/) | [C++](./C++/similar-rgb-color.cpp) [Python](./Python/similar-rgb-color.py) | _O(1)_ | _O(1)_ | Easy |📖|| +810 | [Chalkboard XOR Game](https://leetcode.com/problems/chalkboard-xor-game/) | [C++](./C++/chalkboard-xor-game.cpp) [Python](./Python/chalkboard-xor-game.py) | _O(1)_ | _O(1)_ | Hard ||| +812 | [Largest Triangle Area](https://leetcode.com/problems/largest-triangle-area/) | [C++](./C++/largest-triangle-area.cpp) [Python](./Python/largest-triangle-area.py) | _O(n^3)_ | _O(1)_ | Easy ||| +829 | [Consecutive Numbers Sum](https://leetcode.com/problems/consecutive-numbers-sum/) | [C++](./C++/consecutive-numbers-sum.cpp) [Python](./Python/consecutive-numbers-sum.py) | _O(sqrt(n))_ | _O(1)_ | Medium ||| +836 | [Rectangle Overlap](https://leetcode.com/problems/rectangle-overlap/) | [C++](./C++/rectangle-overlap.cpp) [Python](./Python/rectangle-overlap.py) | _O(1)_ | _O(1)_ | Easy ||| +858 | [Mirror Reflection](https://leetcode.com/problems/mirror-reflection/) | [C++](./C++/mirror-reflection.cpp) [Python](./Python/mirror-reflection.py) | _O(1)_ | _O(1)_ | Medium ||| +867 | [Prime Palindrome](https://leetcode.com/problems/prime-palindrome/) | [C++](./C++/prime-palindrome.cpp) [Python](./Python/prime-palindrome.py) | _O(1)_ | _O(1)_ | Medium ||| +880 | [Random Pick with Weight](https://leetcode.com/problems/random-pick-with-weight/) | [C++](./C++/random-pick-with-weight.cpp) [Python](./Python/random-pick-with-weight.py) | ctor: _O(n)_
pick: _O(logn)_ | _O(n)_ | Medium ||| +881 | [Random Flip Matrix](https://leetcode.com/problems/random-flip-matrix/) | [C++](./C++/random-flip-matrix.cpp) [Python](./Python/random-flip-matrix.py) | ctor: _O(1)_
pick: _O(1)_ reset: _O(n)_ | _O(n)_ | Medium ||| +882 | [Random Point in Non-overlapping Rectangles](https://leetcode.com/problems/random-point-in-non-overlapping-rectangles/) | [C++](./C++/random-point-in-non-overlapping-rectangles.cpp) [Python](./Python/random-point-in-non-overlapping-rectangles.py) | ctor: _O(n)_
pick: _O(logn)_ | _O(n)_ | Medium ||| +883 | [Generate Random Point in a Circle](https://leetcode.com/problems/generate-random-point-in-a-circle/) | [C++](./C++/generate-random-point-in-a-circle.cpp) [Python](./Python/generate-random-point-in-a-circle.py) | _O(1)_ | _O(1)_ | Medium ||| + +## Sort +| # | Title | Solution | Time | Space | Difficulty | Tag | Note| +|-----|---------------- | --------------- | --------------- | --------------- | ------------- |--------------|-----| +056| [Merge Intervals](https://leetcode.com/problems/merge-intervals/)| [C++](./C++/merge-intervals.cpp) [Python](./Python/merge-intervals.py) | _O(nlogn)_ | _O(1)_ | Hard || +057| [Insert Interval](https://leetcode.com/problems/insert-interval/)| [C++](./C++/insert-interval.cpp) [Python](./Python/insert-interval.py) | _O(n)_ | _O(1)_ | Hard || +075| [Sort Colors](https://leetcode.com/problems/sort-colors/) | [C++](./C++/sort-colors.cpp) [Python](./Python/sort-colors.py) | _O(n)_ | _O(1)_ | Medium || Tri Partition +088| [Merge Sorted Array](https://leetcode.com/problems/merge-sorted-array/)| [C++](./C++/merge-sorted-array.cpp) [Python](./Python/merge-sorted-array.py) | _O(n)_ | _O(1)_ | Easy || +147| [Insertion Sort List](https://leetcode.com/problems/insertion-sort-list/)|[C++](./C++/insertion-sort-list.cpp) [Python](./Python/insertion-sort-list.py) | _O(n^2)_ | _O(1)_ | Medium || +148| [Sort List](https://leetcode.com/problems/sort-list/) | [C++](./C++/sort-list.cpp) [Python](./Python/sort-list.py) | _O(nlogn)_ | _O(logn)_ | Medium || +164| [Maximum Gap](https://leetcode.com/problems/maximum-gap/) | [C++](./C++/maximum-gap.cpp) [Python](./Python/maximum-gap.py)| _O(n)_ | _O(n)_ | Hard || Tricky +179| [Largest Number](https://leetcode.com/problems/largest-number/) | [C++](./C++/largest-number.cpp) [Python](./Python/largest-number.py) | _O(nlogn)_ | _O(1)_ | Medium || +218| [The Skyline Problem](https://leetcode.com/problems/the-skyline-problem/) | [C++](./C++/the-skyline-problem.cpp) [Python](./Python/the-skyline-problem.py) | _O(nlogn)_ | _O(n)_ | Hard || Sort, BST| +252| [Meeting Rooms](https://leetcode.com/problems/meeting-rooms/) | [C++](./C++/meeting-rooms.cpp) [Python](./Python/meeting-rooms.py) | _O(nlogn)_ | _O(n)_ | Easy |📖| | +253| [Meeting Rooms II](https://leetcode.com/problems/meeting-rooms-ii/) | [C++](./C++/meeting-rooms-ii.cpp) [Python](./Python/meeting-rooms-ii.py) | _O(nlogn)_ | _O(n)_ | Medium |📖| | +274| [H-Index](https://leetcode.com/problems/h-index/) | [C++](./C++/h-index.cpp) [Python](./Python/h-index.py) | _O(n)_ | _O(n)_ | Medium || Counting Sort | +280| [Wiggle Sort](https://leetcode.com/problems/wiggle-sort/) | [C++](./C++/wiggle-sort.cpp) [Python](./Python/wiggle-sort.py) | _O(n)_ | _O(1)_ | Medium |📖| | +324| [Wiggle Sort II](https://leetcode.com/problems/wiggle-sort-ii/) | [C++](./C++/wiggle-sort-ii.cpp) [Python](./Python/wiggle-sort-ii.py) | _O(n)_ on average | _O(1)_ | Medium | variant of [Sort Colors](https://leetcode.com/problems/sort-colors/) | Tri Partition | +347| [Top K Frequent Elements](https://leetcode.com/problems/top-k-frequent-elements/) | [C++](./C++/top-k-frequent-elements.cpp) [Python](./Python/top-k-frequent-elements.py) | _O(n)_ | _O(n)_ | Medium | | Quick Select, Heap, Bucket Sort | +406| [Queue Reconstruction by Height](https://leetcode.com/problems/queue-reconstruction-by-height/) | [C++](./C++/queue-reconstruction-by-height.cpp) [Python](./Python/queue-reconstruction-by-height.py) | _O(n * sqrt(n))_ | _O(n)_ | Medium | | Tricky | +451| [Sort Characters By Frequency](https://leetcode.com/problems/sort-characters-by-frequency/) | [C++](./C++/sort-characters-by-frequency.cpp) [Python](./Python/sort-characters-by-frequency.py) | _O(n)_ | _O(n)_ | Medium | | | +692| [Top K Frequent Words](https://leetcode.com/problems/top-k-frequent-words/) | [C++](./C++/top-k-frequent-words.cpp) [Python](./Python/top-k-frequent-words.py) | _O(n + klogk)_ on average | _O(n)_ | Medium | | Quick Select, Heap, Bucket Sort | + +## Two Pointers +| # | Title | Solution | Time | Space | Difficulty | Tag | Note| +|-----|---------------- | --------------- | --------------- | --------------- | ------------- |--------------|-----| +019| [Remove Nth Node From End of List](https://leetcode.com/problems/remove-nth-node-from-end-of-list/)| [C++](./C++/remove-nth-node-from-end-of-list.cpp) [Python](./Python/remove-nth-node-from-end-of-list.py) | _O(n)_ | _O(1)_ | Easy || +086| [Partition List](https://leetcode.com/problems/partition-list/)| [C++](./C++/partition-list.cpp) [Python](./Python/partition-list.py) | _O(n)_ | _O(1)_ | Medium || +141| [Linked List Cycle](https://leetcode.com/problems/linked-list-cycle/)| [C++](./C++/linked-list-cycle.cpp) [Python](./Python/linked-list-cycle.py) | _O(n)_ | _O(1)_ | Easy || +142| [Linked List Cycle II](https://leetcode.com/problems/linked-list-cycle-ii/)| [C++](./C++/linked-list-cycle-ii.cpp) [Python](./Python/linked-list-cycle-ii.py) | _O(n)_ | _O(1)_ | Medium || +143| [Reorder List](https://leetcode.com/problems/reorder-list/)| [C++](./C++/reorder-list.cpp) [Python](./Python/reorder-list.py) | _O(n)_ | _O(1)_ | Medium || +167| [Two Sum II - Input array is sorted](https://leetcode.com/problems/two-sum-ii-input-array-is-sorted/) | [C++](./C++/two-sum-ii-input-array-is-sorted.cpp) [Python](./Python/two-sum-ii-input-array-is-sorted.py) | _O(n)_ | _O(1)_ | Medium | | +259 | [3Sum Smaller](https://leetcode.com/problems/3sum-smaller/) | [C++](./C++/3sum-smaller.cpp) [Python](./Python/3sum-smaller.py) | _O(n^2)_ | _O(1)_ | Medium | 📖, LintCode | +283 | [Move Zeroes](https://leetcode.com/problems/move-zeroes/) | [C++](./C++/move-zeroes.cpp) [Python](./Python/move-zeroes.py) | _O(n)_ | _O(1)_ | Easy | | +287| [Find the Duplicate Number](https://leetcode.com/problems/find-the-duplicate-number/)| [C++](./C++/find-the-duplicate-number.cpp) [Python](./Python/find-the-duplicate-number.py) | _O(n)_ | _O(1)_ | Hard | | Binary Search, Two Pointers | +344| [Reverse String](https://leetcode.com/problems/reverse-string/) | [C++](./C++/reverse-string.cpp) [Python](./Python/reverse-string.py) | _O(n)_ | _O(1)_ | Easy | | +345| [Reverse Vowels of a String](https://leetcode.com/problems/reverse-vowels-of-a-string/) | [C++](./C++/reverse-vowels-of-a-string.cpp) [Python](./Python/reverse-vowels-of-a-string.py) | _O(n)_ | _O(1)_ | Easy | | +349| [Intersection of Two Arrays](https://leetcode.com/problems/intersection-of-two-arrays/) | [C++](./C++/intersection-of-two-arrays.cpp) [Python](./Python/intersection-of-two-arrays.py) | _O(m + n)_ | _O(min(m, n))_ | Easy | EPI | Hash, Binary Search +350| [Intersection of Two Arrays II](https://leetcode.com/problems/intersection-of-two-arrays-ii/) | [C++](./C++/intersection-of-two-arrays-ii.cpp) [Python](./Python/intersection-of-two-arrays-ii.py) | _O(m + n)_ | _O(1)_ | Easy | EPI | Hash, Binary Search +360| [Sort Transformed Array](https://leetcode.com/problems/sort-transformed-array/) | [C++](./C++/sort-transformed-array.cpp) [Python](./Python/sort-transformed-array.py) | _O(n)_ | _O(1)_ | Medium |📖| +457| [Circular Array Loop](https://leetcode.com/problems/circular-array-loop/) | [C++](./C++/circular-array-loop.cpp) [Python](./Python/circular-array-loop.py) | _O(n)_ | _O(1)_ | Medium || +567| [Permutation in String](https://leetcode.com/problems/permutation-in-string/) | [C++](./C++/permutation-in-string.cpp) [Python](./Python/permutation-in-string.py) | _O(n)_ | _O(1)_ | Medium || +611| [Valid Triangle Number](https://leetcode.com/problems/valid-triangle-number/) | [C++](./C++/valid-triangle-number.cpp) [Python](./Python/valid-triangle-number.py) | _O(n^2)_ | _O(1)_ | Medium || +777| [Swap Adjacent in LR String](https://leetcode.com/problems/swap-adjacent-in-lr-string/) | [C++](./C++/swap-adjacent-in-lr-string.cpp) [Python](./Python/swap-adjacent-in-lr-string.py) | _O(n)_ | _O(1)_ | Medium || +826| [Most Profit Assigning Work](https://leetcode.com/problems/most-profit-assigning-work/) | [C++](./C++/most-profit-assigning-work.cpp) [Python](./Python/most-profit-assigning-work.py) | _O(mlogm + nlogn)_ | _O(n)_ | Medium || +828| [Unique Letter String](https://leetcode.com/problems/unique-letter-string/) | [C++](./C++/unique-letter-string.cpp) [Python](./Python/unique-letter-string.py) | _O(n)_ | _O(1)_ | Hard || +844 | [Backspace String Compare](https://leetcode.com/problems/backspace-string-compare/) | [C++](./C++/backspace-string-compare.cpp) [Python](./Python/backspace-string-compare.py) | _O(m + n)_ | _O(1)_ | Easy || +876 | [Middle of the Linked List](https://leetcode.com/problems/middle-of-the-linked-list/) | [C++](./C++/middle-of-the-linked-list.cpp) [Python](./Python/middle-of-the-linked-list.py) | _O(n)_ | _O(1)_ | Easy || + +## Recursion +| # | Title | Solution | Time | Space | Difficulty | Tag | Note| +|-----|---------------- | --------------- | --------------- | --------------- | ------------- |--------------|-----| +095| [Unique Binary Search Trees II](https://leetcode.com/problems/unique-binary-search-trees-ii/) | [C++](./C++/unique-binary-search-trees-ii.cpp) [Python](./Python/unique-binary-search-trees-ii.py) | _O(4^n / n^(3/2)_ | _O(4^n / n^(3/2)_ | Medium || +098| [Validate Binary Search Tree](https://leetcode.com/problems/validate-binary-search-tree/)|[C++](./C++/validate-binary-search-tree.cpp) [Python](./Python/validate-binary-search-tree.py)| _O(n)_ | _O(1)_ | Medium || +100| [Same Tree](https://leetcode.com/problems/same-tree/) |[C+](./C++/same-tree.cpp) [Python](./Python/same-tree.py) | _O(n)_ | _O(h)_ | Easy || +104| [Maximum Depth of Binary Tree](https://leetcode.com/problems/maximum-depth-of-binary-tree/)| [C++](./C++/maximum-depth-of-binary-tree.cpp) [Python](./Python/maximum-depth-of-binary-tree.py)| _O(n)_ | _O(h)_ | Easy || +105| [Construct Binary Tree from Preorder and Inorder Traversal](https://leetcode.com/problems/construct-binary-tree-from-preorder-and-inorder-traversal/) | [C++](./C++/construct-binary-tree-from-preorder-and-inorder-traversal.cpp) [Python](./Python/construct-binary-tree-from-preorder-and-inorder-traversal.py) | _O(n)_ | _O(n)_ | Medium || +106| [Construct Binary Tree from Inorder and Postorder Traversal](https://leetcode.com/problems/construct-binary-tree-from-inorder-and-postorder-traversal/) | [C++](./C++/construct-binary-tree-from-inorder-and-postorder-traversal.cpp) [Python](./Python/construct-binary-tree-from-inorder-and-postorder-traversal.py) | _O(n)_ | _O(n)_ | Medium || +108| [Convert Sorted Array to Binary Search Tree](https://leetcode.com/problems/convert-sorted-array-to-binary-search-tree/) | [C++](./C++/convert-sorted-array-to-binary-search-tree.cpp) [Python](./Python/convert-sorted-array-to-binary-search-tree.py) | _O(n)_ | _O(logn)_ | Medium || +109| [Convert Sorted List to Binary Search Tree](https://leetcode.com/problems/convert-sorted-list-to-binary-search-tree/) | [C++](./C++/convert-sorted-list-to-binary-search-tree.cpp) [Python](./Python/convert-sorted-list-to-binary-search-tree.py) | _O(n)_ | _O(logn)_ | Medium || +110| [Balanced Binary Tree](https://leetcode.com/problems/balanced-binary-tree/) | [Python](./Python/balanced-binary-tree.py) | _O(n)_| _O(h)_ | Easy || +111| [Minimum Depth of Binary Tree](https://leetcode.com/problems/minimum-depth-of-binary-tree/)|[Python](./Python/minimum-depth-of-binary-tree.py)| _O(n)_ | _O(h)_ | Easy || +114| [Flatten Binary Tree to Linked List](https://leetcode.com/problems/flatten-binary-tree-to-linked-list/)|[Python](./Python/flatten-binary-tree-to-linked-list.py)| _O(n)_ | _O(h)_ | Medium || +116| [Populating Next Right Pointers in Each Node](https://leetcode.com/problems/populating-next-right-pointers-in-each-node/)|[Python](./Python/populating-next-right-pointers-in-each-node.py)| _O(n)_ | _O(1)_ | Medium || +124| [Binary Tree Maximum Path Sum](https://leetcode.com/problems/binary-tree-maximum-path-sum/)| [Python](./Python/binary-tree-maximum-path-sum.py) | _O(n)_| _O(h)_| Hard || +129| [Sum Root to Leaf Numbers](https://leetcode.com/problems/sum-root-to-leaf-numbers/) | [Python](./Python/sum-root-to-leaf-numbers.py) | _O(n)_ | _O(h)_ | Medium || +156| [Binary Tree Upside Down](https://leetcode.com/problems/binary-tree-upside-down/) | [Python](./Python/binary-tree-upside-down.py) | _O(n)_ | _O(1)_ | Medium |📖| +241| [Different Ways to Add Parentheses](https://leetcode.com/problems/different-ways-to-add-parentheses/) | [C++](./C++/different-ways-to-add-parentheses.cpp) [Python](./Python/different-ways-to-add-parentheses.py) | _O(n * 4^n / n^(3/2))_ | _O(n * 4^n / n^(3/2))_ | Medium || +298 | [Binary Tree Longest Consecutive Sequence](https://leetcode.com/problems/binary-tree-longest-consecutive-sequence/) | [C++](./C++/binary-tree-longest-consecutive-sequence.cpp) [Python](./Python/binary-tree-longest-consecutive-sequence.py) | _O(n)_ | _O(h)_ | Medium |📖| +327| [Count of Range Sum](https://leetcode.com/problems/count-of-range-sum/) | [C++](./C++/count-of-range-sum.cpp) [Python](./Python/count-of-range-sum.py) | _O(nlogn)_ | _O(n)_ | Hard || +333 | [Largest BST Subtree](https://leetcode.com/problems/largest-bst-subtree/) | [C++](./C++/largest-bst-subtree.cpp) [Python](./Python/largest-bst-subtree.py) | _O(n)_ | _O(h)_ | Medium |📖| +337| [House Robber III](https://leetcode.com/problems/house-robber-iii/) | [C++](./C++/house-robber-iii.cpp) [Python](./Python/house-robber-iii.py) | _O(n)_ | _O(h)_ | Medium || +395| [Longest Substring with At Least K Repeating Characters](https://leetcode.com/problems/longest-substring-with-at-least-k-repeating-characters/) | [C++](./C++/longest-substring-with-at-least-k-repeating-characters.cpp) [Python](./Python/longest-substring-with-at-least-k-repeating-characters.py) | _O(n)_ | _O(1)_ | Medium || +404| [Sum of Left Leaves](https://leetcode.com/problems/sum-of-left-leaves/) | [C++](./C++/sum-of-left-leaves.cpp) [Python](./Python/sum-of-left-leaves.py) | _O(n)_ | _O(h)_ | Easy || +437| [Path Sum III](https://leetcode.com/problems/path-sum-iii/) | [C++](./C++/path-sum-iii.cpp) [Python](./Python/path-sum-iii.py) | _O(n)_ | _O(h)_ | Easy || +544| [Output Contest Matches](https://leetcode.com/problems/output-contest-matches/) | [C++](./C++/output-contest-matches.cpp) [Python](./Python/output-contest-matches.py) | _O(n)_ | _O(n)_ | Medium || +549 | [Binary Tree Longest Consecutive Sequence II](https://leetcode.com/problems/binary-tree-longest-consecutive-sequence-ii/) | [C++](./C++/binary-tree-longest-consecutive-sequence-ii.cpp) [Python](./Python/binary-tree-longest-consecutive-sequence-ii.py) | _O(n)_ | _O(h)_ | Medium |📖| +669| [Trim a Binary Search Tree](https://leetcode.com/problems/trim-a-binary-search-tree/) | [C++](./C++/trim-a-binary-search-tree.cpp) [Python](./Python/trim-a-binary-search-tree.py) | _O(n)_ | _O(h)_ | Easy || +671| [Second Minimum Node In a Binary Tree](https://leetcode.com/problems/second-minimum-node-in-a-binary-tree/) | [C++](./C++/second-minimum-node-in-a-binary-tree.cpp) [Python](./Python/second-minimum-node-in-a-binary-tree.py) | _O(n)_ | _O(h)_ | Easy || +761| [Special Binary String](https://leetcode.com/problems/special-binary-string/) | [C++](./C++/special-binary-string.cpp) [Python](./Python/special-binary-string.py) | _O(n^2)_ | _O(n)_ | Hard || + +## Binary Search +| # | Title | Solution | Time | Space | Difficulty | Tag | Note| +|-----|---------------- | --------------- | --------------- | --------------- | ------------- |--------------|-----| +004| [Median of Two Sorted Arrays](https://leetcode.com/problems/median-of-two-sorted-arrays/) | [C++](./C++/median-of-two-sorted-arrays.cpp) [Python](./Python/median-of-two-sorted-arrays.py) | _O(log(min(m, n)))_ | _O(1)_ | Hard || +033| [Search in Rotated Sorted Array](https://leetcode.com/problems/search-in-rotated-sorted-array/) | [C++](./C++/search-in-rotated-sorted-array.cpp) [Python](./Python/search-in-rotated-sorted-array.py) | _O(logn)_ | _O(1)_ | Hard || +034| [Search for a Range](https://leetcode.com/problems/search-for-a-range/) | [C++](./C++/search-for-a-range.cpp) [Python](./Python/search-for-a-range.py) | _O(logn)_ | _O(1)_ | Medium || +305| [Search Insert Position](https://leetcode.com/problems/search-insert-position/) | [C++](./C++/search-insert-position.cpp) [Python](./Python/search-insert-position.py) | _O(logn)_ | _O(1)_ | Medium || +069| [Sqrt(x)](https://leetcode.com/problems/sqrtx/) | [C++](./C++/sqrtx.cpp) [Python](./Python/sqrtx.py) | _O(logn)_ | _O(1)_ | Medium || +074| [Search a 2D Matrix](https://leetcode.com/problems/search-a-2d-matrix/) | [C++](./C++/search-a-2d-matrix.cpp) [Python](./Python/search-a-2d-matrix.py) | _O(logm + logn)_ | _O(1)_ | Medium || +081| [Search in Rotated Sorted Array II](https://leetcode.com/problems/search-in-rotated-sorted-array-ii/) | [C++](./C++/search-in-rotated-sorted-array-ii.cpp) [Python](./Python/search-in-rotated-sorted-array-ii.py) | _O(logn)_ | _O(1)_ | Medium || +153| [Find Minimum in Rotated Sorted Array](https://leetcode.com/problems/find-minimum-in-rotated-sorted-array/) | [C++](./C++/find-minimum-in-rotated-sorted-array.cpp) [Python](./Python/find-minimum-in-rotated-sorted-array.py) | _O(logn)_ | _O(1)_ | Medium || +154| [Find Minimum in Rotated Sorted Array II](https://leetcode.com/problems/find-minimum-in-rotated-sorted-array-ii/) | [C++](./C++/find-minimum-in-rotated-sorted-array-ii.cpp) [Python](./Python/find-minimum-in-rotated-sorted-array-ii.py) | _O(logn)_ ~ _O(n)_ | _O(1)_ | Hard || +162| [Find Peak Element](https://leetcode.com/problems/find-peak-element/) | [C++](./C++/find-peak-element.cpp) [Python](./Python/find-peak-element.py) | _O(logn)_ | _O(1)_ | Medium || +222| [Count Complete Tree Nodes](https://leetcode.com/problems/count-complete-tree-nodes/) | [C++](./C++/count-complete-tree-nodes.cpp) [Python](./Python/count-complete-tree-nodes.py) | _O((logn)^2)_ | _O(1)_ | Medium || +275| [H-Index II](https://leetcode.com/problems/h-index-ii/) | [C++](./C++/h-index-ii.cpp) [Python](./Python/h-index-ii.py) | _O(logn)_ | _O(1)_ | Medium || Binary Search | +278| [First Bad Version](https://leetcode.com/problems/first-bad-version/) | [C++](./C++/first-bad-version.cpp) [Python](./Python/first-bad-version.py) | _O(logn)_ | _O(1)_ | Easy | LintCode || +300| [Longest Increasing Subsequence](https://leetcode.com/problems/longest-increasing-subsequence/) | [C++](./C++/longest-increasing-subsequence.cpp) [Python](./Python/longest-increasing-subsequence.py) | _O(nlogn)_ | _O(n)_ | Medium | CTCI, LintCode | Binary Search, DP| +302| [Smallest Rectangle Enclosing Black Pixels](https://leetcode.com/problems/smallest-rectangle-enclosing-black-pixels/)| [C++](./C++/smallest-rectangle-enclosing-black-pixels.cpp) [Python](./Python/smallest-rectangle-enclosing-black-pixels.py) | _O(nlogn)_ | _O(1)_ | Hard | 📖 | +354| [Russian Doll Envelopes](https://leetcode.com/problems/russian-doll-envelopes/) | [C++](./C++/russian-doll-envelopes.cpp) [Python](./Python/russian-doll-envelopes.py) | _O(nlogn)_ | _O(1)_ | Hard ||| +363| [Max Sum of Rectangle No Larger Than K](https://leetcode.com/problems/max-sum-of-sub-matrix-no-larger-than-k/) | [C++](./C++/max-sum-of-sub-matrix-no-larger-than-k.cpp) [Python](./Python/max-sum-of-sub-matrix-no-larger-than-k.py) | _O(min(m, n)^2 * max(m, n) * logn(max(m, n)))_ | _O(max(m, n))_ | Hard ||| +367| [Valid Perfect Square](https://leetcode.com/problems/valid-perfect-square/)| [C++](./C++/valid-perfect-square.cpp) [Python](./Python/valid-perfect-square.py) | _O(logn)_ | _O(1)_ | Medium | | +374| [Guess Number Higher or Lower](https://leetcode.com/problems/guess-number-higher-or-lower/)| [C++](./C++/guess-number-higher-or-lower.cpp) [Python](./Python/guess-number-higher-or-lower.py) | _O(logn)_ | _O(1)_ | Easy | | +410| [Split Array Largest Sum](https://leetcode.com/problems/split-array-largest-sum/)| [C++](./C++/split-array-largest-sum.cpp) [Python](./Python/split-array-largest-sum.py) | _O(nlogs)_ | _O(1)_ | Hard | | +436 | [Find Right Interval](https://leetcode.com/problems/find-right-interval/) | [C++](./C++/find-right-interval.cpp) [Python](./Python/find-right-interval.py) | _O(nlogn)_ | _O(n)_ | Medium | | +475 | [Heaters](https://leetcode.com/problems/heaters/) | [C++](./C++/heaters.cpp) [Python](./Python/heaters.py) | _O((m + n) * logn)_ | _O(1)_ | Easy | | +540|[Single Element in a Sorted Array](https://leetcode.com/problems/dsingle-element-in-a-sorted-array/)| [C++](./C++/single-element-in-a-sorted-array.cpp) [Python](./Python/single-element-in-a-sorted-array.py)| _O(logn)_ | _O(1)_ | Medium | | +658 | [Find K Closest Elements](https://leetcode.com/problems/find-k-closest-elements/) | [C++](./C++/find-k-closest-elements.cpp) [Python](./Python/find-k-closest-elements.py) | _O(logn + k)_ | _O(1)_ | Medium | | +668 | [Kth Smallest Number in Multiplication Table](https://leetcode.com/problems/kth-smallest-number-in-multiplication-table/) | [C++](./C++/kth-smallest-number-in-multiplication-table.cpp) [Python](./Python/kth-smallest-number-in-multiplication-table.py) | _O(m * log(m * n))_ | _O(1)_ | Hard | | +719 | [Find K-th Smallest Pair Distance](https://leetcode.com/problems/find-k-th-smallest-pair-distance/) | [C++](./C++/find-k-th-smallest-pair-distance.cpp) [Python](./Python/find-k-th-smallest-pair-distance.py) | _O(nlogn + nlogw)_ | _O(1)_ | Hard | | +744 | [Find Smallest Letter Greater Than Target](https://leetcode.com/problems/find-smallest-letter-greater-than-target/) | [C++](./C++/find-smallest-letter-greater-than-target.cpp) [Python](./Python/find-smallest-letter-greater-than-target.py) | _O(logn)_ | _O(1)_ | Easy | | +774 | [Minimize Max Distance to Gas Station](https://leetcode.com/problems/minimize-max-distance-to-gas-station/) | [C++](./C++/minimize-max-distance-to-gas-station.cpp) [Python](./Python/minimize-max-distance-to-gas-station.py) | _O(nlogr)_ | _O(1)_ | Hard | | +786 | [K-th Smallest Prime Fraction](https://leetcode.com/problems/k-th-smallest-prime-fraction/) | [C++](./C++/k-th-smallest-prime-fraction.cpp) [Python](./Python/k-th-smallest-prime-fraction.py) | _O(nlogr)_ | _O(1)_ | Hard | | +793 | [Preimage Size of Factorial Zeroes Function](https://leetcode.com/problems/preimage-size-of-factorial-zeroes-function/) | [C++](./C++/preimage-size-of-factorial-zeroes-function.cpp) [Python](./Python/preimage-size-of-factorial-zeroes-function.py) | _O((logn)^2)_ | _O(1)_ | Hard | | +852 | [Peak Index in a Mountain Array](https://leetcode.com/problems/peak-index-in-a-mountain-array/) | [C++](./C++/peak-index-in-a-mountain-array.cpp) [Python](./Python/peak-index-in-a-mountain-array.py) | _O(logn)_ | _O(1)_ | Easy | | +864 | [Random Pick with Blacklist](https://leetcode.com/problems/random-pick-with-blacklist/) | [C++](./C++/random-pick-with-blacklist.cpp) [Python](./Python/random-pick-with-blacklist.py) | ctor: _O(b)_
pick: _O(1)_ | _O(b)_ | Hard | | +875 | [Koko Eating Bananas](https://leetcode.com/problems/koko-eating-bananas/) | [C++](./C++/koko-eating-bananas.cpp) [Python](./Python/koko-eating-bananas.py) | _O(nlogr)_ | _O(1)_ | Medium | | +878 | [Nth Magical Number](https://leetcode.com/problems/nth-magical-number/) | [C++](./C++/nth-magical-number.cpp) [Python](./Python/nth-magical-number.py) | _O(logn)_ | _O(1)_ | Hard | | + +## Binary Search Tree +| # | Title | Solution | Time | Space | Difficulty | Tag | Note| +|-----|---------------- | --------------- | --------------- | --------------- | ------------- |--------------|-----| +220| [Contains Duplicate III](https://leetcode.com/problems/contains-duplicate-iii/) | [C++](./C++/contains-duplicate-iii.cpp) [Python](./Python/contains-duplicate-iii.py) | _O(nlogk)_ | _O(k)_ | Medium || +230 | [Kth Smallest Element in a BST](https://leetcode.com/problems/kth-smallest-element-in-a-bst/) | [C++](./C++/kth-smallest-element-in-a-bst.cpp) [Python](./Python/kth-smallest-element-in-a-bst.py) | _O(max(h, k))_ | _O(min(h, k))_ | Medium || +235 | [Lowest Common Ancestor of a Binary Search Tree](https://leetcode.com/problems/lowest-common-ancestor-of-a-binary-search-tree/) | [C++](./C++/lowest-common-ancestor-of-a-binary-search-tree.cpp) [Python](./Python/lowest-common-ancestor-of-a-binary-search-tree.py) | _O(h)_ | _O(1)_ | Easy | EPI | +270| [Closest Binary Search Tree Value](https://leetcode.com/problems/closest-binary-search-tree-value/)| [C++](./C++/closest-binary-search-tree-value.cpp) [Python](./Python/closest-binary-search-tree-value.py) | _O(h)_ | _O(1)_ | Easy | 📖 | +285| [Inorder Successor in BST](https://leetcode.com/problems/inorder-successor-in-bst/)| [C++](./C++/inorder-successor-in-bst.cpp) [Python](./Python/inorder-successor-in-bst.py) | _O(h)_ | _O(1)_ | Medium | 📖 | +352 | [Data Stream as Disjoint Intervals](https://leetcode.com/problems/data-stream-as-disjoint-intervals/) | [C++](./C++/data-stream-as-disjoint-intervals.cpp) [Python](./Python/data-stream-as-disjoint-intervals.py) | _O(logn)_ | _O(n)_ | Hard | | +449|[Serialize and Deserialize BST](https://leetcode.com/problems/serialize-and-deserialize-bst/)| [C++](./C++/serialize-and-deserialize-bst.cpp) [Python](./Python/serialize-and-deserialize-bst.py)| _O(n)_ | _O(h)_ | Medium | | | +450|[Delete Node in a BST](https://leetcode.com/problems/delete-node-in-a-bst/)| [C++](./C++/delete-node-in-a-bst.cpp) [Python](./Python/delete-node-in-a-bst.py)| _O(h)_ | _O(h)_ | Medium | | | +530|[Minimum Absolute Difference in BST](https://leetcode.com/problems/minimum-absolute-difference-in-bst/)| [C++](./C++/minimum-absolute-difference-in-bst.cpp) [Python](./Python/minimum-absolute-difference-in-bst.py)| _O(n)_ | _O(h)_ | Easy | | | +776|[Split BST](https://leetcode.com/problems/split-bst/)| [C++](./C++/split-bst.cpp) [Python](./Python/split-bst.py)| _O(n)_ | _O(h)_ | Medium | 📖 | | +783|[Minimum Distance Between BST Nodes](https://leetcode.com/problems/minimum-distance-between-bst-nodes/)| [C++](./C++/minimum-distance-between-bst-nodes.cpp) [Python](./Python/minimum-distance-between-bst-nodes.py)| _O(n)_ | _O(h)_ | Easy | | | + +## Breadth-First Search +| # | Title | Solution | Time | Space | Difficulty | Tag | Note| +|-----|---------------- | --------------- | --------------- | --------------- | ------------- |--------------|-----| +102| [Binary Tree Level Order Traversal](https://leetcode.com/problems/binary-tree-level-order-traversal/)| [C++](./C++/binary-tree-level-order-traversal.cpp) [Python](./Python/binary-tree-level-order-traversal.py)| _O(n)_| _O(n)_| Easy || +107| [Binary Tree Level Order Traversal II](https://leetcode.com/problems/binary-tree-level-order-traversal-ii/)| [Python](./Python/binary-tree-level-order-traversal-ii.py) | _O(n)_| _O(n)_| Easy || +103| [Binary Tree Zigzag Level Order Traversal](https://leetcode.com/problems/binary-tree-zigzag-level-order-traversal/)| [Python](./Python/binary-tree-zigzag-level-order-traversal.py) | _O(n)_| _O(n)_| Medium || +117| [Populating Next Right Pointers in Each Node II](https://leetcode.com/problems/populating-next-right-pointers-in-each-node-ii/)|[Python](./Python/populating-next-right-pointers-in-each-node-ii.py)| _O(n)_ | _O(1)_ | Hard || +127| [Word Ladder](https://leetcode.com/problems/word-ladder/)|[Python](./Python/word-ladder.py) | _O(n * d)_ | _O(d)_ | Medium || +130| [Surrounded Regions](https://leetcode.com/problems/surrounded-regions/)|[C++](./C++/surrounded-regions.cpp) [Python](./Python/surrounded-regions.py)| _O(m * n)_ | _O(m + n)_ | Medium || +133| [Clone Graph](https://leetcode.com/problems/clone-graph/)| [Python](./Python/clone-graph.py) | _O(n)_ | _O(n)_ | Medium || +207| [Course Schedule](https://leetcode.com/problems/course-schedule/)| [Python](./Python/course-schedule.py) | _O(\|V\| + \|E\|)_ | _O(\|E\|)_ | Medium || Topological Sort | +210| [Course Schedule II](https://leetcode.com/problems/course-schedule-ii/)| [Python](./Python/course-schedule-ii.py) | _O(\|V\| + \|E\|)_ | _O(\|E\|)_ | Medium || Topological Sort | +261| [Graph Valid Tree](https://leetcode.com/problems/graph-valid-tree/)| [C++](./C++/graph-valid-tree.cpp) [Python](./Python/graph-valid-tree.py) | _O(\|V\| + \|E\|)_ | _O(\|V\| + \|E\|)_ | Medium | 📖 | +269| [Alien Dictionary](https://leetcode.com/problems/alien-dictionary/) | [C++](./C++/alien-dictionary.cpp) [Python](./Python/alien-dictionary.py) | _O(n)_ | _O(1)_ | Hard |📖| Topological Sort, BFS, DFS | +286| [Walls and Gates](https://leetcode.com/problems/walls-and-gates/)| [C++](./C++/walls-and-gates.cpp) [Python](./Python/walls-and-gates.py) | _O(m * n)_ | _O(g)_ | Medium | 📖 | +310| [Minimum Height Trees](https://leetcode.com/problems/minimum-height-trees/)| [C++](./C++/minimum-height-trees.cpp) [Python](./Python/minimum-height-trees.py) | _O(n)_ | _O(n)_ | Medium || +317| [Shortest Distance from All Buildings](https://leetcode.com/problems/shortest-distance-from-all-buildings/)| [C++](./C++/shortest-distance-from-all-buildings.cpp) [Python](./Python/shortest-distance-from-all-buildings.py) | _O(k * m * n)_ | _O(m * n)_ | Hard | 📖 | +433| [Minimum Genetic Mutation](https://leetcode.com/problems/minimum-genetic-mutation/)| [C++](./C++/minimum-genetic-mutation.cpp) [Python](./Python/minimum-genetic-mutation.py) | _O(n * b)_ | _O(b)_ | Medium || +444| [Sequence Reconstruction](https://leetcode.com/problems/sequence-reconstruction/)| [C++](./C++/sequence-reconstruction.cpp) [Python](./Python/sequence-reconstruction.py) | _O(n * s)_ | _O(n)_ | Medium |📖| Topological Sort | +542| [01 Matrix](https://leetcode.com/problems/01-matrix/)| [C++](./C++/01-matrix.cpp) [Python](./Python/01-matrix.py) | _O(m * n)_ | _O(m * n)_ | Medium || DP +666| [Path Sum IV](https://leetcode.com/problems/path-sum-iv/)| [C++](./C++/path-sum-iv.cpp) [Python](./Python/path-sum-iv.py) | _O(n)_ | _O(w)_ | Medium |📖| Topological Sort | +675|[Cut Off Trees for Golf Event](https://leetcode.com/problems/cut-off-trees-for-golf-event/)| [C++](./C++/cut-off-trees-for-golf-event.cpp) [Python](./Python/cut-off-trees-for-golf-event.py)| _O(t * m * n)_ | _O(m * n)_ | Hard | | `A* Search Algorithm` | +742|[Closest Leaf in a Binary Tree](https://leetcode.com/problems/closest-leaf-in-a-binary-tree/)| [C++](./C++/closest-leaf-in-a-binary-tree.cpp) [Python](./Python/closest-leaf-in-a-binary-tree.py)| _O(n)_ | _O(n)_ | Medium | | | +743|[Network Delay Time](https://leetcode.com/problems/network-delay-time/)| [C++](./C++/network-delay-time.cpp) [Python](./Python/network-delay-time.py)| _O(\|E\| * log\|V\|)_ | _O(\|E\|)_ | Medium | | `Dijkstra's algorithm` | +752|[Open the Lock](https://leetcode.com/problems/open-the-lock/)| [C++](./C++/open-the-lock.cpp) [Python](./Python/open-the-lock.py)| _O(k * n^k + d)_ | _O(k * n^k + d)_ | Medium | | | +773|[Sliding Puzzle](https://leetcode.com/problems/sliding-puzzle/)| [C++](./C++/sliding-puzzle.cpp) [Python](./Python/sliding-puzzle.py)| _O((m * n) * (m * n)!)_ | _O((m * n) * (m * n)!)_ | Hard | | `A* Search Algorithm` | +787|[Cheapest Flights Within K Stops](https://leetcode.com/problems/cheapest-flights-within-k-stops/)| [C++](./C++/cheapest-flights-within-k-stops.cpp) [Python](./Python/cheapest-flights-within-k-stops.py)| _O(\|E\| * log\|V\|)_ | _O(\|E\|)_ | Medium | | `Dijkstra's algorithm` | +815|[Bus Routes](https://leetcode.com/problems/bus-routes/)| [C++](./C++/bus-routes.cpp) [Python](./Python/bus-routes.py)| _O(\|E\| + \|V\|)_ | _O(\|E\| + \|V\|)_ | Hard | | | +854|[K-Similar Strings](https://leetcode.com/problems/k-similar-strings/)| [C++](./C++/k-similar-strings.cpp) [Python](./Python/k-similar-strings.py)| _O(n * n!/(c_a!*...*c_z!))_ | _O(n * n!/(c_a!*...*c_z!))_ | Hard | | | +865|[Shortest Path to Get All Keys](https://leetcode.com/problems/shortest-path-to-get-all-keys/)| [C++](./C++/shortest-path-to-get-all-keys.cpp) [Python](./Python/shortest-path-to-get-all-keys.py)| _O(k * r * c + k^3*2^k)_ | _O(k*2^k)_ | Hard | | `Dijkstra's algorithm` | + +## Depth-First Search +| # | Title | Solution | Time | Space | Difficulty | Tag | Note| +|-----|---------------- | --------------- | --------------- | --------------- | ------------- |--------------|-----| +112| [Path Sum](https://leetcode.com/problems/path-sum/) | [Python](./Python/path-sum.py) | _O(n)_ | _O(h)_ | Easy || +113| [Path Sum II](https://leetcode.com/problems/path-sum-ii/) | [Python](./Python/path-sum-ii.py) | _O(n)_ | _O(h)_ | Medium || +199| [Binary Tree Right Side View](https://leetcode.com/problems/binary-tree-right-side-view/) | [Python](./Python/binary-tree-right-side-view.py) | _O(n)_ | _O(h)_ | Medium || +200| [Number of Islands](https://leetcode.com/problems/number-of-islands/) | [Python](./Python/number-of-islands.py) | _O(m * n)_ | _O(m * n)_| Medium || +236 | [Lowest Common Ancestor of a Binary Tree](https://leetcode.com/problems/lowest-common-ancestor-of-a-binary-tree/) | [C++](./C++/lowest-common-ancestor-of-a-binary-tree.cpp) [Python](./Python/lowest-common-ancestor-of-a-binary-tree.py) | _O(n)_ | _O(h)_ | Medium | EPI | +247| [Strobogrammatic Number II](https://leetcode.com/problems/strobogrammatic-number-ii/) | [C++](./C++/strobogrammatic-number-ii.cpp) [Python](./Python/strobogrammatic-number-ii.py) | _O(n^2 * 5^(n/2))_ | _O(n)_ | Medium |📖|| +250| [Count Univalue Subtrees](https://leetcode.com/problems/count-univalue-subtrees) | [C++](./C++/count-univalue-subtrees.cpp) [Python](./Python/count-univalue-subtrees.py) | _O(n)_ | _O(h)_ | Medium |📖|| +257| [Binary Tree Paths](https://leetcode.com/problems/binary-tree-paths/) | [C++](./C++/binary-tree-paths.cpp) [Python](./Python/binary-tree-paths.py) | _O(n * h)_ | _O(h)_ | Easy ||| +282| [Expression Add Operators](https://leetcode.com/problems/expression-add-operators/) | [C++](./C++/expression-add-operators.cpp) [Python](./Python/expression-add-operators.py) | _O(4^n)_ | _O(n)_ | Hard ||| +301| [Remove Invalid Parentheses](https://leetcode.com/problems/remove-invalid-parentheses/) | [C++](./C++/remove-invalid-parentheses.cpp) [Python](./Python/remove-invalid-parentheses.py) | _O(C(n, c))_ | _O(c)_ | Hard ||| +329| [Longest Increasing Path in a Matrix](https://leetcode.com/problems/longest-increasing-path-in-a-matrix/) | [C++](./C++/longest-increasing-path-in-a-matrix.cpp) [Python](./Python/longest-increasing-path-in-a-matrix.py) | _O(m * n)_ | _O(m * n)_ | Hard ||| +332| [Reconstruct Itinerary](https://leetcode.com/problems/reconstruct-itinerary/) | [C++](./C++/reconstruct-itinerary.cpp) [Python](./Python/reconstruct-itinerary.py) | _O(t! / (n1! * n2! * ... nk!))_ | _O(t)_ | Medium ||| +339| [Nested List Weight Sum](https://leetcode.com/problems/nested-list-weight-sum/) | [C++](./C++/nested-list-weight-sum.cpp) [Python](./Python/nested-list-weight-sum.py) | _O(n)_ | _O(h)_ | Easy |📖|| +364| [Nested List Weight Sum II](https://leetcode.com/problems/nested-list-weight-sum-ii/) | [C++](./C++/nested-list-weight-sum-ii.cpp) [Python](./Python/nested-list-weight-sum-ii.py) | _O(n)_ | _O(h)_ | Medium |📖|| +366| [Find Leaves of Binary Tree](https://leetcode.com/problems/find-leaves-of-binary-tree/) | [C++](./C++/find-leaves-of-binary-tree.cpp) [Python](./Python/find-leaves-of-binary-tree.py) | _O(n)_ | _O(h)_ | Medium |📖|| +399| [Evaluate Division](https://leetcode.com/problems/evaluate-division/) | [C++](./C++/evaluate-division.cpp) [Python](./Python/evaluate-division.py) | _O(q * \|V\|!)_ | _O(e)_ | Medium ||| +417 | [Pacific Atlantic Water Flow](https://leetcode.com/problems/pacific-atlantic-water-flow/) | [C++](./C++/pacific-atlantic-water-flow.cpp) [Python](./Python/pacific-atlantic-water-flow.py) | _O(m * n)_ | _O(m * n)_ | Medium || +440| [K-th Smallest in Lexicographical Order](https://leetcode.com/problems/k-th-smallest-in-lexicographical-order/) | [C++](./C++/k-th-smallest-in-lexicographical-order.cpp) [Python](./Python/k-th-smallest-in-lexicographical-order.py) | _O(logn)_ | _O(logn)_ | Hard || +464| [Can I Win](https://leetcode.com/problems/can-i-win/) | [C++](./C++/can-i-win.cpp) [Python](./Python/can-i-win.py) | _O(n!)_ | _O(n)_ | Medium || +515| [Find Largest Value in Each Tree Row](https://leetcode.com/problems/find-largest-value-in-each-tree-row/) | [C++](./C++/find-largest-value-in-each-tree-row.cpp) [Python](./Python/find-largest-value-in-each-tree-row.py) | _O(n)_ | _O(h)_ | Medium ||| +547| [Friend Circles](https://leetcode.com/problems/friend-circles/) | [C++](./C++/friend-circles.cpp) [Python](./Python/friend-circles.py) | _O(n^2)_ | _O(n)_ | Medium || Union Find | +582| [Kill Process](https://leetcode.com/problems/kill-process/) | [C++](./C++/kill-process.cpp) [Python](./Python/kill-process.py) | _O(n)_ | _O(n)_ | Medium |📖| DFS, BFS | +638| [Shopping Offers](https://leetcode.com/problems/shopping-offers/) | [C++](./C++/shopping-offers.cpp) [Python](./Python/shopping-offers.py) | _O(n * 2^n)_ | _O(n)_ | Medium || +690| [Employee Importance](https://leetcode.com/problems/employee-importance/) | [C++](./C++/employee-importance.cpp) [Python](./Python/employee-importance.py) | _O(n)_ | _O(h)_ | Easy || DFS, BFS +694| [Number of Distinct Islands](https://leetcode.com/problems/number-of-distinct-islands/) | [C++](./C++/number-of-distinct-islands.cpp) [Python](./Python/number-of-distinct-islands.py) | _O(m * n)_ | _O(m * n)_ | Medium |📖|| +695| [Max Area of Island](https://leetcode.com/problems/max-area-of-island/) | [C++](./C++/max-area-of-island.cpp) [Python](./Python/max-area-of-island.py) | _O(m * n)_ | _O(m * n)_ | Easy || +711| [Number of Distinct Islands II](https://leetcode.com/problems/number-of-distinct-islands-ii/) | [C++](./C++/number-of-distinct-islands-ii.cpp) [Python](./Python/number-of-distinct-islands-ii.py) | _O((m * n) * log(m * n))_ | _O(m * n)_ | Hard |📖| Hash | +733| [Max Area of Island](https://leetcode.com/problems/flood-fill/) | [C++](./C++/flood-fill.cpp) [Python](./Python/flood-fill.py) | _O(m * n)_ | _O(m * n)_ | Easy || +749| [Contain Virus](https://leetcode.com/problems/contain-virus/) | [C++](./C++/contain-virus.cpp) [Python](./Python/contain-virus.py) | _O((m * n)^(4/3))_ | _O(m * n)_ | Hard || Simulation| +753| [Cracking the Safe](https://leetcode.com/problems/cracking-the-safe/) | [C++](./C++/cracking-the-safe.cpp) [Python](./Python/cracking-the-safe.py) | _O(k^n)_ | _O(k^n)_ | Hard || `de Bruijn sequences`, `Lyndon word` | +756| [Pyramid Transition Matrix](https://leetcode.com/problems/pyramid-transition-matrix/) | [C++](./C++/pyramid-transition-matrix.cpp) [Python](./Python/pyramid-transition-matrix.py) | _O(a^b)_ | _O(a^b)_ | Medium ||| +785| [Is Graph Bipartite?](https://leetcode.com/problems/is-graph-bipartite/) | [C++](./C++/is-graph-bipartite.cpp) [Python](./Python/is-graph-bipartite.py) | _O(\|V\| + \|E\|)_ | _O(\|V\|)_ | Medium ||| +797| [All Paths From Source to Target](https://leetcode.com/problems/all-paths-from-source-to-target/) | [C++](./C++/all-paths-from-source-to-target.cpp) [Python](./Python/all-paths-from-source-to-target.py) | _O(p + r * n)_ | _O(n)_ | Medium ||| +802| [Find Eventual Safe States](https://leetcode.com/problems/find-eventual-safe-states/) | [C++](./C++/find-eventual-safe-states.cpp) [Python](./Python/find-eventual-safe-states.py) | _O(\|V\| + \|E\|)_ | _O(\|V\|)_ | Medium ||| +827| [Making A Large Island](https://leetcode.com/problems/making-a-large-island/) | [C++](./C++/making-a-large-island.cpp) [Python](./Python/making-a-large-island.py) | _O(n^2)_ | _O(n^2)_ | Hard ||| +834| [Sum of Distances in Tree](https://leetcode.com/problems/sum-of-distances-in-tree/) | [C++](./C++/sum-of-distances-in-tree.cpp) [Python](./Python/sum-of-distances-in-tree.py) | _O(n)_ | _O(n)_ | Hard ||| +841| [Keys and Rooms](https://leetcode.com/problems/keys-and-rooms/) | [C++](./C++/keys-and-rooms.cpp) [Python](./Python/keys-and-rooms.py) | _O(n!)_ | _O(n)_ | Medium ||| +851| [Loud and Rich](https://leetcode.com/problems/loud-and-rich/) | [C++](./C++/loud-and-rich.cpp) [Python](./Python/loud-and-rich.py) | _O(q + r)_ | _O(q + r)_ | Medium ||| + +## Backtracking +| # | Title | Solution | Time | Space | Difficulty | Tag | Note| +|-----|---------------- | --------------- | --------------- | --------------- | ------------- |--------------|-----| +017| [Letter Combinations of a Phone Number](https://leetcode.com/problems/letter-combinations-of-a-phone-number/)| [Python](./Python/letter-combinations-of-a-phone-number.py) | _O(n * 4^n)_ | _O(n)_ | Medium || +022| [Generate Parentheses](https://leetcode.com/problems/generate-parentheses/)| [Python](./Python/generate-parentheses.py)| _O(4^n / n^(3/2))_ | _O(n)_ | Medium || +037| [Sudoku Solver](https://leetcode.com/problems/sudoku-solver/) | [Python](./Python/sudoku-solver.py) | _O((9!)^9)_ | _O(1)_ | Hard || +039| [Combination Sum](https://leetcode.com/problems/combination-sum/)| [Python](./Python/combination-sum.py) | _O(k * n^k)_ | _O(k)_ | Medium || +040| [Combination Sum II](https://leetcode.com/problems/combination-sum-ii/)| [Python](./Python/combination-sum-ii.py)| _O(k * C(n, k))_| _O(k)_ | Medium || +046| [Permutations](https://leetcode.com/problems/permutations/)| [Python](./Python/permutations.py) | _O(n * n!)_ | _O(n)_ | Medium || +047| [Permutations II](https://leetcode.com/problems/permutations-ii/)| [Python](./Python/permutations-ii.py) | _O(n * n!)_ | _O(n)_ | Medium || +051| [N-Queens](https://leetcode.com/problems/n-queens/) | [Python](./Python/n-queens.py) | _O(n!)_ | _O(n)_ | Hard || +052| [N-Queens-II](https://leetcode.com/problems/n-queens-ii/) | [Python](./Python/n-queens-ii.py) | _O(n!)_ | _O(n)_ | Hard || +077| [Combinations](https://leetcode.com/problems/combinations/) | [Python](./Python/combinations.py) | _O(O(k * C(n, k)))_ | _O(k)_ | Medium || +079| [Word Search](https://leetcode.com/problems/word-search/) | [Python](./Python/word-search.py) | _O(m * n * l)_ | _O(l)_ | Medium || +093| [Restore IP Addresses](https://leetcode.com/problems/restore-ip-addresses/) | [Python](./Python/restore-ip-addresses.py) | _O(1)_ | _O(1)_ | Medium || +078| [Subsets](https://leetcode.com/problems/subsets/) | [C++](./C++/subsets.cpp) [Python](./Python/subsets.py) | _O(n * 2^n)_ | _O(1)_ | Medium || +090| [Subsets II](https://leetcode.com/problems/subsets-ii/) | [C++](./C++/subsets-ii.cpp) [Python](./Python/subsets-ii.py) | _O(n * 2^n)_ | _O(1)_ | Medium || +126| [Word Ladder II](https://leetcode.com/problems/word-ladder-ii/) |[Python](./Python/word-ladder-ii.py) | _O(n * d)_ | _O(d)_ | Hard || +131| [Palindrome Partitioning](https://leetcode.com/problems/palindrome-partitioning/) | [Python](./Python/palindrome-partitioning.py) | _O(n^2)_ ~ _O(2^n)_ | _O(n^2)_ | Medium || +140| [Word Break II](https://leetcode.com/problems/word-break-ii/) | [C++](./C++/word-break-ii.cpp) [Python](./Python/word-break-ii.py) | _O(n * l^2 + n * r)_ | _O(n^2)_ | Hard || +212| [Word Search II](https://leetcode.com/problems/word-search-ii/) | [C++](./C++/word-search-ii.cpp) [Python](./Python/word-search-ii.py) | _O(m * n * l)_ | _O(l)_ | Hard | LintCode | Trie, DFS +216| [Combination Sum III](https://leetcode.com/problems/combination-sum-iii/)| [C++](./C++/combination-sum-iii.cpp) [Python](./Python/combination-sum-iii.py) | _O(k * C(n, k))_ | _O(k)_ | Medium || +254| [Factor Combinations](https://leetcode.com/problems/factor-combinations/) | [C++](./C++/factor-combinations.cpp) [Python](./Python/factor-combinations.py) | _O(nlogn)_ | _O(logn)_ | Medium |📖|| +267| [Palindrome Permutation II](https://leetcode.com/problems/palindrome-permutation-ii/) | [C++](./C++/palindrome-permutation-ii.cpp) [Python](./Python/palindrome-permutation-ii.py) | _O(n * n!)_ | _O(n)_ | Medium |📖|| +291| [Word Pattern II](https://leetcode.com/problems/word-pattern-ii/) | [C++](./C++/word-pattern-ii.cpp) [Python](./Python/word-pattern-ii.py) | _O(n * C(n - 1, c - 1))_ | _O(n + c)_ | Hard |📖|| +294| [Flip Game II](https://leetcode.com/problems/flip-game-ii/) | [C++](./C++/flip-game-ii.cpp) [Python](./Python/flip-game-ii.py) | _O(n + c^2)_ | _O(c)_ | Medium |📖| DP, Hash | +320| [Generalized Abbreviation](https://leetcode.com/problems/generalized-abbreviation/) | [C++](./C++/generalized-abbreviation.cpp) [Python](./Python/generalized-abbreviation.py) | _O(n * 2^n)_ | _O(n)_ | Medium |📖|| +425| [Word Squares](https://leetcode.com/problems/word-squares/) | [C++](./C++/word-squares.cpp) [Python](./Python/word-squares.py) | _O(n^2 * n!)_ | _O(n^2)_ | Hard |📖|| +526| [Beautiful Arrangement](https://leetcode.com/problems/beautiful-arrangement/) | [C++](./C++/beautiful-arrangement.cpp) [Python](./Python/beautiful-arrangement.py) | _O(n!)_ | _O(n)_ | Medium || +676| [Implement Magic Dictionary](https://leetcode.com/problems/implement-magic-dictionary/) | [C++](./C++/implement-magic-dictionary.cpp) [Python](./Python/implement-magic-dictionary.py) | _O(n)_ | _O(d)_ | Medium || Trie, DFS +679| [24 Game](https://leetcode.com/problems/24-game/) | [C++](./C++/24-game.cpp) [Python](./Python/24-game.py) | _O(1)_ | _O(1)_ | Hard || DFS +698| [Partition to K Equal Sum Subsets](https://leetcode.com/problems/partition-to-k-equal-sum-subsets/) | [C++](./C++/partition-to-k-equal-sum-subsets.cpp) [Python](./Python/partition-to-k-equal-sum-subsets.py) | _O(n * 2^n)_ | _O(2^n)_ | Medium || DFS, DP, Memoization +718 | [Maximum Length of Repeated Subarray](https://leetcode.com/problems/maximum-length-of-repeated-subarray/) | [C++](./C++/maximum-length-of-repeated-subarray.cpp) [Python](./Python/maximum-length-of-repeated-subarray.py) | _O(m * n)_ | _O(min(m, n))_ | Medium || DP, Hash, Binary Search +784| [Letter Case Permutation](https://leetcode.com/problems/letter-case-permutation/) | [C++](./C++/letter-case-permutation.cpp) [Python](./Python/letter-case-permutation.py) | _O(n * 2^n)_ | _O(1)_ | Easy || + +## Dynamic Programming +| # | Title | Solution | Time | Space | Difficulty | Tag | Note| +|-----|---------------- | --------------- | --------------- | --------------- | ------------- |--------------|-----| +010| [Regular Expression Matching](https://leetcode.com/problems/regular-expression-matching/) | [Python](./Python/regular-expression-matching.py) | _O(m * n)_ | _O(n)_ | Hard || +053| [Maximum Subarray](https://leetcode.com/problems/maximum-subarray/)|[Python](./Python/maximum-subarray.py)| _O(n)_ | _O(1)_ | Medium || +062| [Unique Paths](https://leetcode.com/problems/unique-paths/) | [Python](./Python/unique-paths.py)| _O(m * n)_ | _O(m + n)_ | Medium || +063| [Unique Paths II](https://leetcode.com/problems/unique-paths-ii/) | [Python](./Python/unique-paths-ii.py) | _O(m * n)_ | _O(m + n)_ | Medium || +064| [Minimum Path Sum](https://leetcode.com/problems/minimum-path-sum/)| [Python](./Python/minimum-path-sum.py)| _O(m * n)_ | _O(m + n)_ | Medium || +070| [Climbing Stairs](https://leetcode.com/problems/climbing-stairs/)| [Python](./Python/climbing-stairs.py) | _O(n)_ | _O(1)_ | Easy || +072| [Edit Distance](https://leetcode.com/problems/edit-distance/)|[Python](./Python/edit-distance.py)| _O(m * n)_ | _O(m + n)_ | Hard || +087| [Scramble String](https://leetcode.com/problems/scramble-string/) | [Python](./Python/scramble-string.py) | _O(n^4)_ | _O(n^3)_ | Hard || +091| [Decode Ways](https://leetcode.com/problems/decode-ways/) | [C++](./Python/decode-ways.cpp) [Python](./Python/decode-ways.py)| _O(n)_ | _O(1)_ | Medium || +096| [Unique Binary Search Trees](https://leetcode.com/problems/unique-binary-search-trees/) | [Python](./Python/unique-binary-search-trees.py) | _O(n)_ | _O(1)_ | Medium || Math +097| [Interleaving String](https://leetcode.com/problems/interleaving-string/)|[Python](./Python/interleaving-string.py)| _O(m * n)_ | _O(m + n)_ | Hard || +115| [Distinct Subsequences](https://leetcode.com/problems/distinct-subsequences/)|[Python](./Python/distinct-subsequences.py)| _O(n^2)_ | _O(n)_ | Hard || +120| [Triangle](https://leetcode.com/problems/triangle/) | [Python](./Python/triangle.py) | _O(m * n)_ | _O(n)_ | Medium || +123| [Best Time to Buy and Sell Stock III](https://leetcode.com/problems/best-time-to-buy-and-sell-stock-iii/) | [Python](./Python/best-time-to-buy-and-sell-stock-iii.py) | _O(n)_ | _O(1)_ | Hard || +132| [Palindrome Partitioning II](https://leetcode.com/problems/palindrome-partitioning-ii/) | [Python](./Python/palindrome-partitioning-ii.py) | _O(n^2)_ | _O(n^2)_ | Hard || +139| [Word Break](https://leetcode.com/problems/word-break/) | [C++](./C++/word-break.cpp) [Python](./Python/word-break.py) | _O(n * l^2)_ | _O(n)_ | Medium || +152| [Maximum Product Subarray](https://leetcode.com/problems/maximum-product-subarray/)|[Python](./Python/maximum-product-subarray.py)| _O(n)_ | _O(1)_ | Medium || +174| [Dungeon Game](https://leetcode.com/problems/dungeon-game/) | [Python](./Python/dungeon-game.py)| _O(m * n)_ | _O(m + n)_ | Hard || +188| [Best Time to Buy and Sell Stock IV](https://leetcode.com/problems/best-time-to-buy-and-sell-stock-iv/)| [Python](./Python/best-time-to-buy-and-sell-stock-iv.py) | _O(k * n)_ | _O(k)_ | Hard || +198| [House Robber](https://leetcode.com/problems/house-robber/)| [Python](./Python/house-robber.py) | _O(n)_ | _O(1)_ | Easy || +213| [House Robber II](https://leetcode.com/problems/house-robber-ii/)| [C++](./C++/house-robber-ii.cpp) [Python](./Python/house-robber-ii.py) | _O(n)_ | _O(1)_ | Medium || +221| [Maximal Square](https://leetcode.com/problems/maximal-square/)| [C++](./C++/maximal-square.cpp) [Python](./Python/maximal-square.py) | _O(n^2)_ | _O(n)_ | Medium | EPI | +256| [Paint House](https://leetcode.com/problems/paint-house/) | [C++](./C++/paint-house.cpp) [Python](./Python/paint-house.py) | _O(n)_| _O(1)_| Medium |📖|| +265| [Paint House II](https://leetcode.com/problems/paint-house-ii/) | [C++](./C++/paint-house-ii.cpp) [Python](./Python/paint-house-ii.py) | _O(n * k)_| _O(k)_| Hard |📖|| +276| [Paint Fence](https://leetcode.com/problems/paint-fence/) | [C++](./C++/paint-fence.cpp) [Python](./Python/paint-fence.py) | _O(n)_| _O(1)_| Easy |📖|| +279| [Perfect Squares](https://leetcode.com/problems/perfect-squares/)| [C++](./C++/perfect-squares.cpp) [Python](./Python/perfect-squares.py) | _O(n * sqrt(n))_ | _O(n)_ | Medium || Hash | +303| [Range Sum Query - Immutable](https://leetcode.com/problems/range-sum-query-immutable/)| [C++](./C++/range-sum-query-immutable.cpp) [Python](./Python/range-sum-query-immutable.py) | ctor: _O(n)_, lookup: _O(1)_ | _O(n)_ | Easy || +304| [Range Sum Query 2D - Immutable](https://leetcode.com/problems/range-sum-query-2d-immutable/)| [C++](./C++/range-sum-query-2d-immutable.cpp) [Python](./Python/range-sum-query-2d-immutable.py) | ctor: _O(m * n)_, lookup: _O(1)_ | _O(m * n)_ | Medium || +309| [Best Time to Buy and Sell Stock with Cooldown](https://leetcode.com/problems/best-time-to-buy-and-sell-stock-with-cooldown/) | [C++](./C++/best-time-to-buy-and-sell-stock-with-cooldown.cpp) [Python](./Python/best-time-to-buy-and-sell-stock-with-cooldown.py) | _O(n)_ | _O(1)_ | Medium || +312| [Burst Balloons](https://leetcode.com/problems/burst-balloons/) | [C++](./C++/burst-balloons.cpp) [Python](./Python/burst-balloons.py) | _O(n^3)_ | _O(n^2)_ | Hard || +322| [Coin Change](https://leetcode.com/problems/coin-change/) | [C++](./C++/coin-change.cpp) [Python](./Python/coin-change.py) | _O(n * k)_ | _O(k)_ | Medium || +351| [Android Unlock Patterns](https://leetcode.com/problems/android-unlock-patterns/) | [C++](./C++/android-unlock-patterns.cpp) [Python](./Python/android-unlock-patterns.py) | _O(9^2 * 2^9)_ | _O(9 * 2^9)_ | Medium | 📖 | Backtracking | +357| [Count Numbers with Unique Digits](https://leetcode.com/problems/count-numbers-with-unique-digits/) | [C++](./C++/count-numbers-with-unique-digits.cpp) [Python](./Python/count-numbers-with-unique-digits.py) | _O(n)_ | _O(1)_ | Medium || Backtracking, Math | +361| [Bomb Enemy](https://leetcode.com/problems/bomb-enemy/) | [C++](./C++/bomb-enemy.cpp) [Python](./Python/bomb-enemy.py) | _O(m * n)_ | _O(m * n)_ | Medium | 📖 | | +368| [Largest Divisible Subset](https://leetcode.com/problems/largest-divisible-subset/) | [C++](./C++/largest-divisible-subset.cpp) [Python](./Python/largest-divisible-subset.py) | _O(n^2)_ | _O(n)_ | Medium | | | +375| [Guess Number Higher or Lower II](https://leetcode.com/problems/guess-number-higher-or-lower-ii/)| [C++](./C++/guess-number-higher-or-lower-ii.cpp) [Python](./Python/guess-number-higher-or-lower-ii.py) | _O(n^2)_ | _O(n^2)_ | Medium | | +377| [Combination Sum IV](https://leetcode.com/problems/combination-sum-iv/)| [C++](./C++/combination-sum-iv.cpp) [Python](./Python/combination-sum-iv.py) | _O(nlogn + n * t)_ | _O(t)_ | Medium | | +403 | [Frog Jump](https://leetcode.com/problems/frog-jump/) | [C++](./C++/frog-jump.cpp) [Python](./Python/frog-jump.py) | _O(n)_ | _O(n) ~ O(n^2)_ | Hard || +416 | [Partition Equal Subset Sum](https://leetcode.com/problems/partition-equal-subset-sum/) | [C++](./C++/partition-equal-subset-sum.cpp) [Python](./Python/partition-equal-subset-sum.py) | _O(n * s)_ | _O(s)_ | Medium || +418 | [Sentence Screen Fitting](https://leetcode.com/problems/sentence-screen-fitting/) | [C++](./C++/sentence-screen-fitting.cpp) [Python](./Python/sentence-screen-fitting.py) | _O(r + n * c)_ | _O(n)_ | Medium |📖| +446 | [Arithmetic Slices II - Subsequence](https://leetcode.com/problems/arithmetic-slices-ii-subsequence/) | [C++](./C++/arithmetic-slices-ii-subsequence.cpp) [Python](./Python/arithmetic-slices-ii-subsequence.py) | _O(n^2)_ | _O(n * d)_ | Hard || +465 | [Optimal Account Balancing](https://leetcode.com/problems/optimal-account-balancing/) | [C++](./C++/optimal-account-balancing.cpp) [Python](./Python/optimal-account-balancing.py) | _O(n * 2^n)_ | _O(n * 2^n)_ | Hard |📖| +466 | [Count The Repetitions](https://leetcode.com/problems/count-the-repetitions/) | [C++](./C++/count-the-repetitions.cpp) [Python](./Python/count-the-repetitions.py) | _O(s1 * min(s2, n1))_ | _O(s2)_ | Hard || +467 | [Unique Substrings in Wraparound String](https://leetcode.com/problems/unique-substrings-in-wraparound-string/) | [C++](./C++/unique-substrings-in-wraparound-string.cpp) [Python](./Python/unique-substrings-in-wraparound-string.py) | _O(n)_ | _O(1)_ | Medium || +471 | [Encode String with Shortest Length](https://leetcode.com/problems/encode-string-with-shortest-length/) | [C++](./C++/encode-string-with-shortest-length.cpp) [Python](./Python/encode-string-with-shortest-length.py) | _O(n^3)_ on average | _O(n^2)_ | Medium |📖| +472 | [Concatenated Words](https://leetcode.com/problems/concatenated-words/) | [C++](./C++/concatenated-words.cpp) [Python](./Python/concatenated-words.py) | _O(n * l^2)_ | _O(n * l)_ | Medium || +474 | [Ones and Zeroes](https://leetcode.com/problems/ones-and-zeroes/) | [C++](./C++/ones-and-zeroes.cpp) [Python](./Python/ones-and-zeroes.py) | _O(s * m * n)_ | _O(m * n)_ | Medium || +486 | [Predict the Winner](https://leetcode.com/problems/predict-the-winner/) | [C++](./C++/predict-the-winner.cpp) [Python](./Python/predict-the-winner.py) | _O(n^2)_ | _O(n)_ | Medium | | | +514 | [Freedom Trail](https://leetcode.com/problems/freedom-trail/) | [C++](./C++/freedom-trail.cpp) [Python](./Python/freedom-trail.py) | _O(k)_ ~ _O(k * r^2)_ | _O(r)_ | Hard ||| +516 | [Longest Palindromic Subsequence](https://leetcode.com/problems/longest-palindromic-subsequence/) | [C++](./C++/longest-palindromic-subsequence.cpp) [Python](./Python/longest-palindromic-subsequence.py) | _O(n^2)_ | _O(n)_ | Medium ||| +546 | [Remove Boxes](https://leetcode.com/problems/remove-boxes/) | [C++](./C++/remove-boxes.cpp) [Python](./Python/remove-boxes.py) | _O(n^3)_ ~ _O(n^4)_ | _O(n^3)_ | Hard ||| +552 | [Student Attendance Record II](https://leetcode.com/problems/student-attendance-record-ii/) | [C++](./C++/student-attendance-record-ii.cpp) [Python](./Python/student-attendance-record-ii.py) | _O(n)_ | _O(1)_ | Hard ||| +562 | [Longest Line of Consecutive One in Matrix](https://leetcode.com/problems/longest-line-of-consecutive-one-in-matrix/) | [C++](./C++/longest-line-of-consecutive-one-in-matrix.cpp) [Python](./Python/longest-line-of-consecutive-one-in-matrix.py) | _O(m * n)_ | _O(n)_ | Medium |📖|| +568 | [Maximum Vacation Days](https://leetcode.com/problems/maximum-vacation-days/) | [C++](./C++/maximum-vacation-days.cpp) [Python](./Python/maximum-vacation-days.py) | _O(n^2 * k)_ | _O(k)_ | Hard |📖|| +576 | [Out of Boundary Paths](https://leetcode.com/problems/out-of-boundary-paths/) | [C++](./C++/out-of-boundary-paths.cpp) [Python](./Python/out-of-boundary-paths.py) | _O(N * m * n)_ | _O(m * n)_ | Medium ||| +583 | [Delete Operation for Two Strings](https://leetcode.com/problems/delete-operation-for-two-strings/) | [C++](./C++/delete-operation-for-two-strings.cpp) [Python](./Python/delete-operation-for-two-strings.py) | _O(m * n)_ | _O(n)_ | Medium ||| +600 | [Non-negative Integers without Consecutive Ones](https://leetcode.com/problems/non-negative-integers-without-consecutive-ones/) | [C++](./C++/non-negative-integers-without-consecutive-ones.cpp) [Python](./Python/non-negative-integers-without-consecutive-ones.py) | _O(1)_ | _O(1)_ | Hard ||| +629 | [K Inverse Pairs Array](https://leetcode.com/problems/k-inverse-pairs-array/) | [C++](./C++/k-inverse-pairs-array.cpp) [Python](./Python/k-inverse-pairs-array.py) | _O(n * k)_ | _O(k)_ | Hard ||| +639 | [Decode Ways II](https://leetcode.com/problems/decode-ways-ii/) | [C++](./C++/decode-ways-ii.cpp) [Python](./Python/decode-ways-ii.py) | _O(n)_ | _O(1)_ | Hard ||| +650 | [2 Keys Keyboard](https://leetcode.com/problems/2-keys-keyboard/) | [C++](./C++/2-keys-keyboard.cpp) [Python](./Python/2-keys-keyboard.py) | _O(sqrt(n))_ | _O(1)_ | Medium ||| +656 | [Coin Path](https://leetcode.com/problems/coin-path/) | [C++](./C++/coin-path.cpp) [Python](./Python/coin-path.py) | _O(n * B)_ | _O(n)_ | Hard |📖| +664 | [Strange Printer](https://leetcode.com/problems/strange-printer/) | [C++](./C++/strange-printer.cpp) [Python](./Python/strange-printer.py) | _O(n^3)_ | _O(n^2)_ | Hard || +673 | [Number of Longest Increasing Subsequence](https://leetcode.com/problems/number-of-longest-increasing-subsequence/) | [C++](./C++/number-of-longest-increasing-subsequence.cpp) [Python](./Python/number-of-longest-increasing-subsequence.py) | _O(n^2)_ | _O(n)_ | Medium || +688 | [Knight Probability in Chessboard](https://leetcode.com/problems/knight-probability-in-chessboard/) | [C++](./C++/knight-probability-in-chessboard.cpp) [Python](./Python/knight-probability-in-chessboard.py) | _O(k * n^2)_ | _O(n^2)_ | Medium || +689 | [Maximum Sum of 3 Non-Overlapping Subarrays](https://leetcode.com/problems/maximum-sum-of-3-non-overlapping-subarrays/) | [C++](./C++/maximum-sum-of-3-non-overlapping-subarrays.cpp) [Python](./Python/maximum-sum-of-3-non-overlapping-subarrays.py) | _O(n)_ | _O(n)_ | Hard || +691 | [Stickers to Spell Word](https://leetcode.com/problems/stickers-to-spell-word/) | [C++](./C++/stickers-to-spell-word.cpp) [Python](./Python/stickers-to-spell-word.py) | _O(T * S^T)_ | _O(T * S^T)_ | Hard || Backtracking, Memoization +712 | [Minimum ASCII Delete Sum for Two Strings](https://leetcode.com/problems/minimum-ascii-delete-sum-for-two-strings/) | [C++](./C++/minimum-ascii-delete-sum-for-two-strings.cpp) [Python](./Python/minimum-ascii-delete-sum-for-two-strings.py) | _O(m * n)_ | _O(n)_ | Medium || +714 | [Best Time to Buy and Sell Stock with Transaction Fee](https://leetcode.com/problems/best-time-to-buy-and-sell-stock-with-transaction-fee/) | [C++](./C++/best-time-to-buy-and-sell-stock-with-transaction-fee.cpp) [Python](./Python/best-time-to-buy-and-sell-stock-with-transaction-fee.py) | _O(n)_ | _O(1)_ | Medium || +727 | [Minimum Window Subsequence](https://leetcode.com/problems/minimum-window-subsequence/) | [C++](./C++/minimum-window-subsequence.cpp) [Python](./Python/minimum-window-subsequence.py) | _O(s * t)_ | _O(s)_ | Hard |📖| +730 | [Count Different Palindromic Subsequences](https://leetcode.com/problems/count-different-palindromic-subsequences/) | [C++](./C++/count-different-palindromic-subsequences.cpp) [Python](./Python/count-different-palindromic-subsequences.py) | _O(n^2)_ | _O(n)_ | Hard || +740 | [Delete and Earn](https://leetcode.com/problems/delete-and-earn/) | [C++](./C++/delete-and-earn.cpp) [Python](./Python/delete-and-earn.py) | _O(n)_ | _O(1)_ | Medium || +741 | [Cherry Pickup](https://leetcode.com/problems/cherry-pickup/) | [C++](./C++/cherry-pickup.cpp) [Python](./Python/cherry-pickup.py) | _O(n^3)_ | _O(n^2)_ | Hard || +746 | [Min Cost Climbing Stairs](https://leetcode.com/problems/min-cost-climbing-stairs/) | [C++](./C++/min-cost-climbing-stairs.cpp) [Python](./Python/min-cost-climbing-stairs.py) | _O(n)_ | _O(1)_ | Easy || +750 | [Number Of Corner Rectangles](https://leetcode.com/problems/number-of-corner-rectangles/) | [C++](./C++/number-of-corner-rectangles.cpp) [Python](./Python/number-of-corner-rectangles.py) | _O(n * m^2)_ | _O(n * m)_ | Medium || +764 | [Largest Plus Sign](https://leetcode.com/problems/largest-plus-sign/) | [C++](./C++/largest-plus-sign.cpp) [Python](./Python/largest-plus-sign.py) | _O(n^2)_ | _O(n^2)_ | Medium || +788 | [Rotated Digits](https://leetcode.com/problems/rotated-digits/) | [C++](./C++/rotated-digits.cpp) [Python](./Python/rotated-digits.py) | _O(logn)_ | _O(logn)_ | Easy || Memoization | +790 | [Domino and Tromino Tiling](https://leetcode.com/problems/domino-and-tromino-tiling/) | [C++](./C++/domino-and-tromino-tiling.cpp) [Python](./Python/domino-and-tromino-tiling.py) | _O(logn)_ | _O(logn)_ | Medium || Matrix Exponentiation | +799 | [Champagne Tower](https://leetcode.com/problems/champagne-tower/) | [C++](./C++/champagne-tower.cpp) [Python](./Python/champagne-tower.py) | _O(n^2)_ | _O(n)_ | Medium ||| +801 | [Minimum Swaps To Make Sequences Increasing](https://leetcode.com/problems/minimum-swaps-to-make-sequences-increasing/) | [C++](./C++/minimum-swaps-to-make-sequences-increasing.cpp) [Python](./Python/minimum-swaps-to-make-sequences-increasing.py) | _O(n)_ | _O(1)_ | Medium ||| +805 | [Split Array With Same Average](https://leetcode.com/problems/split-array-with-same-average/) | [C++](./C++/split-array-with-same-average.cpp) [Python](./Python/split-array-with-same-average.py) | _O(n^4)_ | _O(n^3)_ | Hard ||| +808 | [Soup Servings](https://leetcode.com/problems/soup-servings/) | [C++](./C++/soup-servings.cpp) [Python](./Python/soup-servings.py) | _O(1)_ | _O(1)_ | Medium || Memoization | +813 | [Largest Sum of Averages](https://leetcode.com/problems/largest-sum-of-averages/) | [C++](./C++/largest-sum-of-averages.cpp) [Python](./Python/largest-sum-of-averages.py) | _O(k * n^2)_ | _O(n)_ | Medium || | +818 | [Race Car](https://leetcode.com/problems/race-car/) | [C++](./C++/race-car.cpp) [Python](./Python/race-car.py) | _O(nlogn)_ | _O(n)_ | Hard || | +823 | [Binary Trees With Factors](https://leetcode.com/problems/binary-trees-with-factors/) | [C++](./C++/binary-trees-with-factors.cpp) [Python](./Python/binary-trees-with-factors.py) | _O(n^2)_ | _O(n)_ | Medium || | +837 | [New 21 Game](https://leetcode.com/problems/new-21-game/) | [C++](./C++/new-21-game.cpp) [Python](./Python/new-21-game.py) | _O(n)_ | _O(n)_ | Medium || | +838 | [Push Dominoes](https://leetcode.com/problems/push-dominoes/) | [C++](./C++/push-dominoes.cpp) [Python](./Python/push-dominoes.py) | _O(n)_ | _O(n)_ | Medium || | +847 | [Shortest Path Visiting All Nodes](https://leetcode.com/problems/shortest-path-visiting-all-nodes/) | [C++](./C++/shortest-path-visiting-all-nodes.cpp) [Python](./Python/shortest-path-visiting-all-nodes.py) | _O(n *2^n)_ | _O(n * 2^n)_ | Hard || BFS | +877 | [Stone Game](https://leetcode.com/problems/stone-game/) | [C++](./C++/stone-game.cpp) [Python](./Python/stone-game.py) | _O(n^2)_ | _O(n)_ | Medium | variant of [Predict the Winner](https://leetcode.com/problems/predict-the-winner/) | | +879 | [Profitable Schemes](https://leetcode.com/problems/profitable-schemes/) | [C++](./C++/profitable-schemes.cpp) [Python](./Python/profitable-schemes.py) | _O(n * p * g)_ | _O(p * g)_ | || | + +## Greedy +| # | Title | Solution | Time | Space | Difficulty | Tag | Note| +|-----|---------------- | --------------- | --------------- | --------------- | ------------- |--------------|-----| +011| [Container With Most Water](https://leetcode.com/problems/container-with-most-water/)| [C++](./C++/container-with-most-water.cpp) [Python](./Python/container-with-most-water.py) | _O(n)_ | _O(1)_ | Medium || +042| [Trapping Rain Water](https://leetcode.com/problems/trapping-rain-water/) | [C++](./C++/trapping-rain-water.cpp) [Python](./Python/trapping-rain-water.py) | _O(n)_ | _O(1)_ | Hard || Tricky +044| [Wildcard Matching](https://leetcode.com/problems/wildcard-matching/) | [Python](./Python/wildcard-matching.py) | _O(m + n)_ | _O(1)_ | Hard || Tricky +045| [Jump Game II](https://leetcode.com/problems/jump-game-ii/) | [Python](./Python/jump-game-ii.py) | _O(n)_ | _O(1)_ | Hard || +055| [Jump Game](https://leetcode.com/problems/jump-game/) | [Python](./Python/jump-game.py) | _O(n)_ | _O(1)_ | Medium || +122| [Best Time to Buy and Sell Stock II](https://leetcode.com/problems/best-time-to-buy-and-sell-stock-ii/)| [Python](./Python/best-time-to-buy-and-sell-stock-ii.py) | _O(n)_ | _O(1)_ | Easy || +134| [Gas Station](https://leetcode.com/problems/gas-station/)| [Python](./Python/gas-station.py) | _O(n)_ | _O(1)_ | Medium || +135| [Candy](https://leetcode.com/problems/candy/)| [C++](./C++/candy.cpp) [Python](./Python/candy.py) | _O(n)_ | _O(n)_ | Hard || +316| [Remove Duplicate Letters](https://leetcode.com/problems/remove-duplicate-letters/) | [C++](./C++/remove-duplicate-letters.cpp) [Python](./Python/remove-duplicate-letters.py) | _O(n)_| _O(k)_| Hard || Ascending Stack | +321| [Create Maximum Number](https://leetcode.com/problems/create-maximum-number/)| [C++](./C++/create-maximum-number.cpp) [Python](./Python/create-maximum-number.py) | _O(k * (m + n + k))_ ~ _O(k * (m + n + k^2))_| _O(m + n + k^2)_ | Hard | variant of [Delete Digits](http://www.lintcode.com/en/problem/delete-digits/) | Greedy, DP +330| [Patching Array](https://leetcode.com/problems/patching-array/) | [C++](./C++/patching-array.cpp) [Python](./Python/patching-array.py) | _O(s + logn)_ | _O(1)_ | Hard || +376| [Wiggle Subsequence](https://leetcode.com/problems/wiggle-subsequence/)| [C++](./C++/wiggle-subsequence.cpp) [Python](./Python/wiggle-subsequence.py) | _O(n)_ | _O(1)_ | Medium || +392| [Is Subsequence](https://leetcode.com/problems/is-subsequence/)| [C++](./C++/is-subsequence.cpp) [Python](./Python/is-subsequence.py) | _O(n)_ | _O(1)_ | Medium || +397| [Integer Replacement](https://leetcode.com/problems/integer-replacement/)| [C++](./C++/integer-replacement.cpp) [Python](./Python/integer-replacement.py) | _O(n)_ | _O(1)_ | Medium || Math +402 | [Remove K Digits](https://leetcode.com/problems/remove-k-digits/) | [C++](./C++/remove-k-digits.cpp) [Python](./Python/remove-k-digits.py) | _O(n)_ | _O(n)_ | Medium | LintCode | +435 | [Non-overlapping Intervals](https://leetcode.com/problems/non-overlapping-intervals/) | [C++](./C++/non-overlapping-intervals.cpp) [Python](./Python/non-overlapping-intervals.py) | _O(nlogn)_ | _O(1)_ | Medium | | Line Sweep +452 | [Minimum Number of Arrows to Burst Balloons](https://leetcode.com/problems/minimum-number-of-arrows-to-burst-balloons/) | [C++](./C++/minimum-number-of-arrows-to-burst-balloons.cpp) [Python](./Python/minimum-number-of-arrows-to-burst-balloons.py) | _O(nlogn)_ | _O(1)_ | Medium | | +455 | [Assign Cookies](https://leetcode.com/problems/assign-cookies/) | [C++](./C++/assign-cookies.cpp) [Python](./Python/assign-cookies.py) | _O(nlogn)_ | _O(1)_ | Easy | | +621 | [Task Scheduler](https://leetcode.com/problems/task-scheduler/) | [C++](./C++/task-scheduler.cpp) [Python](./Python/task-scheduler.py) | _O(n)_ | _O(1)_ | Medium | | +630 | [Course Schedule III](https://leetcode.com/problems/course-schedule-iii/) | [C++](./C++/course-schedule-iii.cpp) [Python](./Python/course-schedule-iii.py) | _O(nlogn)_ | _O(k)_ | Hard || +646 | [Maximum Length of Pair Chain](https://leetcode.com/problems/maximum-length-of-pair-chain/) | [C++](./C++/maximum-length-of-pair-chain.cpp) [Python](./Python/maximum-length-of-pair-chain.py) | _O(nlogn)_ | _O(1)_ | Medium | similar to [Non-overlapping Intervals](https://leetcode.com/problems/non-overlapping-intervals/) | Line Sweep +649 | [Dota2 Senate](https://leetcode.com/problems/dota2-senate/) | [C++](./C++/dota2-senate.cpp) [Python](./Python/dota2-senate.py) | _O(n)_ | _O(n)_ | Medium ||| +659 | [Split Array into Consecutive Subsequences](https://leetcode.com/problems/split-array-into-consecutive-subsequences/) | [C++](./C++/split-array-into-consecutive-subsequences.cpp) [Python](./Python/split-array-into-consecutive-subsequences.py) | _O(n)_ | _O(1)_ | Medium | | +738 | [Monotone Increasing Digits](https://leetcode.com/problems/monotone-increasing-digits/) | [C++](./C++/monotone-increasing-digits.cpp) [Python](./Python/monotone-increasing-digits.py) | _O(1)_ | _O(1)_ | Medium | | +757 | [Set Intersection Size At Least Two](https://leetcode.com/problems/set-intersection-size-at-least-two/) | [C++](./C++/set-intersection-size-at-least-two.cpp) [Python](./Python/set-intersection-size-at-least-two.py) | _O(nlogn)_ | _O(n)_ | Hard | | +759 | [Employee Free Time](https://leetcode.com/problems/employee-free-time/) | [C++](./C++/employee-free-time.cpp) [Python](./Python/employee-free-time.py) | _O(m * logn)_ | _O(n)_ | Hard | | +763 | [Partition Labels](https://leetcode.com/problems/partition-labels/) | [C++](./C++/partition-labels.cpp) [Python](./Python/partition-labels.py) | _O(n)_ | _O(n)_ | Medium | | +767 | [Reorganize String](https://leetcode.com/problems/reorganize-string/) | [C++](./C++/reorganize-string.cpp) [Python](./Python/reorganize-string.py) | _O(n)_ | _O(1)_ | Medium | | +798 | [Smallest Rotation with Highest Score](https://leetcode.com/problems/smallest-rotation-with-highest-score/) | [C++](./C++/smallest-rotation-with-highest-score.cpp) [Python](./Python/smallest-rotation-with-highest-score.py) | _O(n)_ | _O(1)_ | Hard | | +843 | [Guess the Word](https://leetcode.com/problems/guess-the-word/) | [C++](./C++/guess-the-word.cpp) [Python](./Python/guess-the-word.py) | _O(n^2)_ | _O(n)_ | Hard || MinMax | +861 | [Score After Flipping Matrix](https://leetcode.com/problems/score-after-flipping-matrix/) | [C++](./C++/score-after-flipping-matrix.cpp) [Python](./Python/score-after-flipping-matrix.py) | _O(r * c)_ | _O(1)_ | Medium || +870 | [Advantage Shuffle](https://leetcode.com/problems/advantage-shuffle/) | [C++](./C++/advantage-shuffle.cpp) [Python](./Python/advantage-shuffle.py) | _O(nlogn)_ | _O(n)_ | Medium || + +## Graph +| # | Title | Solution | Time | Space | Difficulty | Tag | Note| +|-----|---------------- | --------------- | --------------- | --------------- | ------------- |--------------|-----| +765 | [Couples Holding Hands](https://leetcode.com/problems/couples-holding-hands/) | [C++](./C++/couples-holding-hands.cpp) [Python](./Python/couples-holding-hands.py) | _O(n)_| _O(n)_| Hard ||| + + +## Geometry +| # | Title | Solution | Time | Space | Difficulty | Tag | Note| +|-----|---------------- | --------------- | --------------- | --------------- | ------------- |--------------|-----| +587 | [Erect the Fence](https://leetcode.com/problems/erect-the-fence/) | [C++](./C++/erect-the-fence.cpp) [Python](./Python/erect-the-fence.py) | _O(nlogn)_| _O(n)_| Hard || `Monotone Chain` | + +## Simulation +| # | Title | Solution | Time | Space | Difficulty | Tag | Note| +|-----|---------------- | --------------- | --------------- | --------------- | ------------- |--------------|-----| +874 | [Walking Robot Simulation](https://leetcode.com/problems/walking-robot-simulation/) | [C++](./C++/walking-robot-simulation.cpp) [Python](./Python/walking-robot-simulation.py) | _O(n + k)_| _O(k)_| Easy ||| + +## Design +| # | Title | Solution | Time | Space | Difficulty | Tag | Note| +|-----|---------------- | --------------- | --------------- | --------------- | ------------- |--------------|-----| +146| [LRU Cache](https://leetcode.com/problems/lru-cache/) | [C++](./C++/lru-cache.cpp) [Python](./Python/lru-cache.py) | _O(1)_ | _O(k)_ | Hard || +225| [Implement Stack using Queues](https://leetcode.com/problems/implement-stack-using-queues/) | [C++](./C++/implement-stack-using-queues.cpp) [Python](./Python/implement-stack-using-queues.py) | push: _O(n)_, pop: _O(1)_, top: _O(1)_ | _O(n)_ | Easy || +284| [Peeking Iterator](https://leetcode.com/problems/peeking-iterator/)| [C++](./C++/peeking-iterator.cpp) [Python](./Python/peeking-iterator.py) | _O(1)_ | _O(1)_ | Medium || +348| [Design Tic-Tac-Toe](https://leetcode.com/problems/design-tic-tac-toe/) | [C++](./C++/design-tic-tac-toe.cpp) [Python](./Python/design-tic-tac-toe.py) | _O(1)_ | _O(n^2)_ | Medium |📖|| +353| [Design Snake Game](https://leetcode.com/problems/design-snake-game/) | [C++](./C++/design-snake-game.cpp) [Python](./Python/design-snake-game.py) | _O(1)_ | _O(s)_ | Medium |📖| Deque | +355| [Design Twitter](https://leetcode.com/problems/design-twitter/) | [C++](./C++/design-twitter.cpp) [Python](./Python/design-twitter.py) | _O(klogu)_ | _O(t + f)_ | Medium | LintCode | Heap | +359| [Logger Rate Limiter](https://leetcode.com/problems/logger-rate-limiter/) | [C++](./C++/logger-rate-limiter.cpp) [Python](./Python/logger-rate-limiter.py) | _O(1), amortized_ | _O(k)_ | Easy |📖| Deque | +362| [Design Hit Counter](https://leetcode.com/problems/design-hit-counter/) | [C++](./C++/design-hit-counter.cpp) [Python](./Python/design-hit-counter.py) | _O(1), amortized_ | _O(k)_ | Medium |📖| Deque | +379| [Design Phone Directory](https://leetcode.com/problems/design-phone-directory/) | [C++](./C++/design-phone-directory.cpp) [Python](./Python/design-phone-directory.py) | _O(1)_ | _O(n)_ | Medium |📖| | +380| [Insert Delete GetRandom O(1)](https://leetcode.com/problems/insert-delete-getrandom-o1/) | [C++](./C++/insert-delete-getrandom-o1.cpp) [Python](./Python/insert-delete-getrandom-o1.py) | _O(1)_ | _O(n)_| Hard || | +381| [Insert Delete GetRandom O(1) - Duplicates allowed](https://leetcode.com/problems/insert-delete-getrandom-o1-duplicates-allowed/) | [C++](./C++/insert-delete-getrandom-o1-duplicates-allowed.cpp) [Python](./Python/insert-delete-getrandom-o1-duplicates-allowed.py) | _O(1)_ | _O(n)_ | Hard || | +432| [All O\`one Data Structure](https://leetcode.com/problems/all-oone-data-structure/) | [C++](./C++/all-oone-data-structure.cpp) [Python](./Python/all-oone-data-structure.py) | _O(1)_ | _O(n)_| Hard || | +460| [LFU Cache](https://leetcode.com/problems/lfu-cache/) | [C++](./C++/lfu-cache.cpp) [Python](./Python/lfu-cache.py) | _O(1)_ | _O(k)_ | Hard || | +535| [Encode and Decode TinyURL](https://leetcode.com/problems/encode-and-decode-tinyurl/) | [C++](./C++/encode-and-decode-tinyurl.cpp) [Python](./Python/encode-and-decode-tinyurl.py) | _O(1)_ | _O(n)_ | Medium || | +588| [Design In-Memory File System](https://leetcode.com/problems/design-in-memory-file-system/) | [C++](./C++/design-in-memory-file-system.cpp) [Python](./Python/design-in-memory-file-system.py) | ls: _O(l + klogk)_
mkdir: _O(l)_
addContentToFile: _O(l + c)_
readContentFromFile: _O(l + c)_ | _O(n + s)_ | Hard |📖| | +604| [Design Compressed String Iterator](https://leetcode.com/problems/design-compressed-string-iterator/) | [C++](./C++/design-compressed-string-iterator.cpp) [Python](./Python/design-compressed-string-iterator.py) | _O(1)_ | _O(1)_ | Easy |📖| | +631| [Design Excel Sum Formula](https://leetcode.com/problems/design-excel-sum-formula/) | [C++](./C++/design-excel-sum-formula.cpp) [Python](./Python/design-excel-sum-formula.py) | set: _O((r * c)^2)_
get: _O(1)_
sum: _O((r * c)^2)_ | _O(r * c)_ | Hard |📖| | +635| [Design Log Storage System](https://leetcode.com/problems/design-log-storage-system/) | [C++](./C++/design-log-storage-system.cpp) [Python](./Python/design-log-storage-system.py) | put: _O(1)_
retrieve: _O(n + dlogd)_ | _O(n)_ | Medium |📖| | +642| [Design Search Autocomplete System](https://leetcode.com/problems/design-search-autocomplete-system/) | [C++](./C++/design-search-autocomplete-system.cpp) [Python](./Python/design-search-autocomplete-system.py) | _O(p^2)_ | _O(p * t + s)_ | Hard |📖| | +715| [Range Module](https://leetcode.com/problems/range-module/) | [C++](./C++/range-module.cpp) [Python](./Python/range-module.py) | add: _O(n)_
remove: _O(n)_
query: _O(logn)_ | _O(n)_ | Hard || | +716| [Max Stack](https://leetcode.com/problems/max-stack/) | [C++](./C++/max-stack.cpp) [Python](./Python/max-stack.py) | push: _O(logn)_
pop: _O(logn)_
popMax: _O(logn)_
top: _O(1)_
peekMax: _O(1)_ | _O(n)_ | Easy || | +745| [Prefix and Suffix Search](https://leetcode.com/problems/prefix-and-suffix-search/) | [C++](./C++/prefix-and-suffix-search.cpp) [Python](./Python/prefix-and-suffix-search.py) | ctor: _O(w * l^2)_
search : _O(p + s)_ | _O(t)_ | Hard || Trie | + +## SQL +| # | Title | Solution | Time | Space | Difficulty | Tag | Note| +|-----|---------------- | --------------- | --------------- | --------------- | ------------- |--------------|-----| +175| [Combine Two Tables](https://leetcode.com/problems/combine-two-tables/) | [MySQL](./MySQL/combine-two-tables.sql) | _O(m + n)_ | _O(m + n)_ | Easy || +176| [Second Highest Salary](https://leetcode.com/problems/second-highest-salary/) | [MySQL](./MySQL/second-highest-salary.sql) | _O(n)_ | _O(1)_ | Easy || +177| [Nth Highest Salary](https://leetcode.com/problems/nth-highest-salary/) | [MySQL](./MySQL/nth-highest-salary.sql) | _O(n^2)_ | _O(n)_ | Medium || +178| [Rank Scores](https://leetcode.com/problems/rank-scores/) | [MySQL](./MySQL/rank-scores.sql) | _O(n^2)_ | _O(n)_ | Medium || +180| [Consecutive Numbers](https://leetcode.com/problems/consecutive-numbers/) | [MySQL](./MySQL/consecutive-numbers.sql) | _O(n)_ | _O(n)_ | Medium || +181| [Employees Earning More Than Their Managers](https://leetcode.com/problems/employees-earning-more-than-their-managers/) | [MySQL](./MySQL/employees-earning-more-than-their-managers.sql) | _O(n^2)_ | _O(1)_ | Easy || +182| [Duplicate Emails](https://leetcode.com/problems/duplicate-emails/) | [MySQL](./MySQL/duplicate-emails.sql) | _O(n^2)_ | _O(n)_ | Easy || +183| [Customers Who Never Order](https://leetcode.com/problems/customers-who-never-order/) | [MySQL](./MySQL/customers-who-never-order.sql) | _O(n^2)_ | _O(1)_ | Easy || +184| [Department Highest Salary](https://leetcode.com/problems/department-highest-salary/) | [MySQL](./MySQL/department-highest-salary.sql) | _O(n^2)_ | _O(n)_ | Medium || +185| [Department Top Three Salaries](https://leetcode.com/problems/department-top-three-salaries/) | [MySQL](./MySQL/department-top-three-salaries.sql) | _O(n^2)_ | _O(n)_ | Hard || +196| [Delete Duplicate Emails](https://leetcode.com/problems/delete-duplicate-emails/) | [MySQL](./MySQL/delete-duplicate-emails.sql) | _O(n^2)_ | _O(n)_ | Easy || +197| [Rising Temperature](https://leetcode.com/problems/rising-temperature/) | [MySQL](./MySQL/rising-temperature.sql) | _O(n^2)_ | _O(n)_ | Easy || +262| [Trips and Users](https://leetcode.com/problems/trips-and-users/) | [MySQL](./MySQL/trips-and-users.sql) | _O((t * u) + tlogt)_ | _O(t)_ | Hard || + +## Shell Script +| # | Title | Solution | Time | Space | Difficulty | Tag | Note| +|-----|---------------- | --------------- | --------------- | --------------- | ------------- |--------------|-----| +192 | [Word Frequency](https://leetcode.com/problems/word-frequency/) | [Shell](./Shell/word-frequency.sh) | _O(n)_ | _O(k)_ | Medium || +193 | [Valid Phone Numbers](https://leetcode.com/problems/valid-phone-numbers/) | [Shell](./Shell/valid-phone-numbers.sh) | _O(n)_ | _O(1)_ | Easy || +194 | [Transpose File](https://leetcode.com/problems/transpose-file/) | [Shell](./Shell/transpose-file.sh) | _O(n^2)_ | _O(n^2)_ | Medium || +195 | [Tenth Line](https://leetcode.com/problems/tenth-line/) | [Shell](./Shell/tenth-line.sh) | _O(n)_ | _O(1)_ | Easy || diff --git a/Shell/tenth-line.sh b/Shell/tenth-line.sh new file mode 100644 index 000000000..631890a85 --- /dev/null +++ b/Shell/tenth-line.sh @@ -0,0 +1,35 @@ +# Time: O(n) +# Space: O(1) +# +# How would you print just the 10th line of a file? +# +# For example, assume that file.txt has the following content: +# +# Line 1 +# Line 2 +# Line 3 +# Line 4 +# Line 5 +# Line 6 +# Line 7 +# Line 8 +# Line 9 +# Line 10 +# Your script should output the tenth line, which is: +# Line 10 +# +# Hint: +# 1. If the file contains less than 10 lines, what should you output? +# 2. There's at least three different solutions. Try to explore all possibilities. +# +# Read from the file file.txt and output the tenth line to stdout. + +# Solution 1 +awk '{if(NR==10) print $0}' file.txt +awk 'NR == 10' file.txt + +# Solution 2 +sed -n 10p file.txt + +# Solution 3 +tail -n+10 file.txt | head -1 diff --git a/Shell/transpose-file.sh b/Shell/transpose-file.sh new file mode 100644 index 000000000..e912f219d --- /dev/null +++ b/Shell/transpose-file.sh @@ -0,0 +1,35 @@ +# Time: O(n^2) +# Space: O(n^2) +# +# Given a text file file.txt, transpose its content. +# +# You may assume that each row has the same number of +# columns and each field is separated by the ' ' character. +# +# For example, if file.txt has the following content: +# +# name age +# alice 21 +# ryan 30 +# Output the following: +# +# name alice ryan +# age 21 30 +# + +# Read from the file file.txt and print its transposed content to stdout. +awk ' +{ + for (i = 1; i <= NF; i++) { + if(NR == 1) { + s[i] = $i; + } else { + s[i] = s[i] " " $i; + } + } +} +END { + for (i = 1; s[i] != ""; i++) { + print s[i]; + } +}' file.txt diff --git a/Shell/valid-phone-numbers.sh b/Shell/valid-phone-numbers.sh new file mode 100644 index 000000000..ca2e85fbf --- /dev/null +++ b/Shell/valid-phone-numbers.sh @@ -0,0 +1,34 @@ +#!/usr/bin/env bash +# Time: O(n) +# Space: O(1) +# +# Given a text file file.txt that contains list of +# phone numbers (one per line), write a one liner +# bash script to print all valid phone numbers. +# +# You may assume that a valid phone number must +# appear in one of the following two formats: +# (xxx) xxx-xxxx or xxx-xxx-xxxx. (x means a digit) +# +# You may also assume each line in the text file +# must not contain leading or trailing white spaces. +# +# For example, assume that file.txt has the following content: +# +# 987-123-4567 +# 123 456 7890 +# (123) 456-7890 +# Your script should output the following valid phone numbers: +# 987-123-4567 +# (123) 456-7890 +# +# +# Read from the file file.txt and output all valid phone numbers to stdout. +# Using grep: +grep -P '^(\d{3}-|\(\d{3}\) )\d{3}-\d{4}$' file.txt + +# Using sed: +sed -n -E '/^([0-9]{3}-|\([0-9]{3}\) )[0-9]{3}-[0-9]{4}$/p' file.txt + +# Using awk: +awk '/^([0-9]{3}-|\([0-9]{3}\) )[0-9]{3}-[0-9]{4}$/' file.txt diff --git a/Shell/word-frequency.sh b/Shell/word-frequency.sh new file mode 100644 index 000000000..1775f0504 --- /dev/null +++ b/Shell/word-frequency.sh @@ -0,0 +1,29 @@ +# Time: O(n) +# Space: O(k), k is number of words +# +# Write a bash script to calculate the frequency of each word in a text file words.txt. +# +# For simplicity sake, you may assume: +# +# words.txt contains only lowercase characters and +# space ' ' characters. +# Each word must consist of lowercase characters only. +# Words are separated by one or more whitespace characters. +# For example, assume that words.txt has the following content: +# +# the day is sunny the the +# the sunny is is +# Your script should output the following, +# sorted by descending frequency: +# the 4 +# is 3 +# sunny 2 +# day 1 +# Note: +# Don't worry about handling ties, +# it is guaranteed that each word's frequency count is unique. +# + +# Read from the file words.txt and output the word frequency list to stdout. +awk '{for(i=1;i<=NF;i++) a[$i]++} END {for(k in a) print k,a[k]}' words.txt | sort -k2 -nr +