Category Archives: LeetCode

LeetCode Average of Levels in Binary Tree

LeetCode Average of Levels in Binary Tree 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:
  1. The range of node’s value is in the range of 32-bit signed integer.

求二叉树每一层的平均数。简单题,BFS层次遍历。代码如下: [cpp] class Solution { public: vector<double> averageOfLevels(TreeNode* root) { if (root == NULL)return{}; vector<double> ans; queue<TreeNode*> q; q.push(root); while (!q.empty()) { double sum = 0; size_t n = q.size(); for (size_t i = 0; i < n; ++i) { TreeNode* f = q.front(); q.pop(); sum += f->val; if (f->left != NULL)q.push(f->left); if (f->right != NULL)q.push(f->right); } ans.push_back(sum / n); } return ans; } }; [/cpp] 本代码提交AC,用时15MS。]]>

LeetCode Continuous Subarray Sum

LeetCode Continuous Subarray Sum 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:
  1. The length of the array won’t exceed 10,000.
  2. You may assume the sum of all the numbers is in the range of a signed 32-bit integer.

给定一个非负整数数组和k,问数组中是否存在长度至少为2的连续子数组,子数组的和是k的n倍,n也是一个整数。 这个题之前好像在哪遇到过。遇到连续子数组的问题,首先要想到前缀和。因为这里要求和是k的n倍,有点麻烦。 首先我们需要知道一个数学知识,如果到i的前缀和accusum1和到j的前缀和accusum2除以k的余数相等,那么(i,j]的连续子数组的和就是k的整数倍。比如第一个样例中,连续子数组的和以及连续子数组的和除以k的余数:
  • [23,25,29,35,42]
  • [5,1,5,5,0]
如果下标从0开始,则下标为0和2的accusum%k都等于5,说明(0,2]的连续子数组的和是k的整数倍。accusum(0,2]=2+4=6,确实是6的整数倍。 这个道理其实很好理解,accusum0%k=5,到了accusum2%k还等于5,说明中间只加了整数倍的k,才导致余数没变,因为整数倍的k 模k是等于0的。 知道了这个道理就好办了,我们用map保存accusum%k的值和计算到现在的accusum的下标,如果后来遇到一个accusum模k的余数在map中,说明找到了一个可能,我们再判断一下他们之间的距离是否至少为2即可。 还有一个特殊的地方需要注意的是,如果k等于0,则不能取模运算。 最后还要注意的一点是,第12行,把当前余数和下标插入map是在else分支的,只有当map中不存在这个余数才插入,否则不插入。比如上面的例子,我们遇到多个accusum模k的余数是5,但是我们map中保存的应该是第0个下标,后续的余数等于5的下标不能更新0这个下标,因为这样才能使得后续的余数相等的下标和map中的下标的差越大,即更好的满足距离至少为2的约束。 完整代码如下: [cpp] class Solution { public: bool checkSubarraySum(vector<int>& nums, int k) { unordered_map<int, int> reminders; reminders[0] = -1; int accusum = 0; for (int i = 0; i < nums.size(); ++i) { accusum += nums[i]; if (k != 0)accusum %= k; // 注意k为0时,不能取模 if (reminders.find(accusum) != reminders.end()) { if (i – reminders[accusum] >= 2)return true; } else reminders[accusum] = i; // 注意必须是在else分支 } return false; } }; [/cpp] 本代码提交AC,用时26MS。]]>

LeetCode Add and Search Word – Data structure design

211. Add and Search Word – Data structure design 211. Add and Search Word – Data structure design

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.

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. 211. Add and Search Word – Data structure design


实现一个字典,要求可以插入单词和搜索单词,并且支持一种正则表达式,即点号’.’表示任意一个字符。 本题明显是用Trie树来做,插入和搜索普通字符串的方法和Trie树是一模一样的。 唯一需要注意的是搜索带点号的字符串,此时用递归来做,递归的出口是,当遍历到字符串的最后一个字符时,根据是否为点号进行判断。递归向下的过程也是分是否为点号的,如果是点号,则需要尝试26种递归路径。想清楚这个逻辑之后,代码就很好写了。 完整代码如下:

const int N = 26;
class WordDictionary {
private:
    struct Node {
        bool isWord;
        vector<Node*> children;
        Node(bool i)
            : isWord(i)
        {
            for (int i = 0; i < N; ++i)
                children.push_back(NULL);
        };
    };
    Node* root;
    bool search(const string& word, int i, Node* root)
    {
        if (root == NULL)
            return false;
        int idx = word[i] – ‘a’;
        if (i == word.size() – 1) {
            if (word[i] != ‘.’)
                return root->children[idx] != NULL && root->children[idx]->isWord;
            else {
                for (int j = 0; j < N; ++j) {
                    if (root->children[j] != NULL && root->children[j]->isWord)
                        return true;
                }
                return false;
            }
        }
        if (word[i] != ‘.’)
            return search(word, i + 1, root->children[idx]);
        else {
            for (int j = 0; j < N; ++j) {
                if (search(word, i + 1, root->children[j]))
                    return true;
            }
            return false;
        }
    }
public: /** Initialize your data structure here. */
    WordDictionary() { root = new Node(false); } /** Adds a word into the data structure. */
    void addWord(string word)
    {
        Node* cur = root;
        for (const auto& c : word) {
            int idx = c – ‘a’;
            if (cur->children[idx] == NULL)
                cur->children[idx] = new Node(false);
            cur = cur->children[idx];
        }
        cur->isWord = 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)
    {
        Node* cur = root;
        return search(word, 0, cur);
    }
};

本代码提交AC,用时105MS。

LeetCode Range Sum Query – Mutable

LeetCode Range Sum Query – Mutable 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:
  1. The array is only modifiable by the update function.
  2. You may assume the number of calls to update and sumRange function is distributed evenly.

给定一个一维数组,要求实现范围求和,即求[i,j]之间的元素的和。sumRange(i,j)求i~j的元素和,update(i,val)更新下标为i的元素值为val。 第一敏感使用线段树,很久之前在hihoCoder上遇到过。 建树的方法是类似于树的后序遍历,即左右根。不断把[start,end]二分,构建左右子树,然后构建当前节点,当前节点的sum等于左右子树的sum的和。在递归的时候,递归到start==end时,说明只有一个元素了,此时sum就等于该元素。 查询的方法和建树方法类似,判断区间[i,j]和区间[start,end]的关系,假设start和end的中点是mid,如果j<=mid,递归在左子树查询;如果i>mid,递归在右子树查询;否则在[i,mid]和[mid+1,j]查询然后求和。 更新的方法和查询的方法类似,也是不断判断i和mid的关系,在左子树或者右子树递归更新,当找到该叶子节点时,更新它的sum,返回父节点也更新sum等于新的左右子树的sum的和。 完整代码如下: [cpp] class NumArray { private: struct Node { int start, end, sum; Node *left, *right; Node(int s, int e) :start(s), end(e), sum(0), left(NULL), right(NULL) {}; }; Node *root; Node* constructTree(vector<int> &nums, int start, int end) { Node* node = new Node(start, end); if (start == end) { node->sum = nums[start]; return node; } int mid = start + (end – start) / 2; node->left = constructTree(nums, start, mid); node->right = constructTree(nums, mid + 1, end); node->sum = node->left->sum + node->right->sum; return node; } int sumRange(int i, int j, Node *root) { if (root == NULL)return 0; if (i == root->start&&j == root->end)return root->sum; int mid = root->start + (root->end – root->start) / 2; if (j <= mid)return sumRange(i, j, root->left); else if (i > mid)return sumRange(i, j, root->right); else return sumRange(i, mid, root->left) + sumRange(mid + 1, j, root->right); } void update(int i, int val, Node *root) { if (root->start == root->end && root->start == i) { root->sum = val; return; } int mid = root->start + (root->end – root->start) / 2; if (i <= mid)update(i, val, root->left); else update(i, val, root->right); root->sum = root->left->sum + root->right->sum; } public: NumArray(vector<int> nums) { root = NULL; if (!nums.empty())root = constructTree(nums, 0, nums.size() – 1); } void update(int i, int val) { if (root == NULL)return; update(i, val, root); } int sumRange(int i, int j) { if (root == NULL)return 0; return sumRange(i, j, root); } }; [/cpp] 本代码提交AC,用时172MS。]]>

LeetCode Range Sum Query 2D – Immutable

LeetCode Range Sum Query 2D – Immutable

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:
  1. You may assume that the matrix does not change.
  2. There are many calls to sumRegion function.
  3. You may assume that row1 ≤ row2 and col1 ≤ col2.

给定一个矩阵,要求矩阵中的任意一个子矩阵的和,而且可能有很多次这样的请求。 因为之前在hiho上做过一个用DP求子矩阵的和的题,所以这题就很easy了。 定义一个二维数组dp[i][j]表示固定子矩阵的左上角坐标为(1,1)(假设矩阵的下标从1开始),当右下角坐标为(i,j)时,这个子矩阵的和是多少。则有: dp[i][j]=dp[i][j-1]+dp[i-1][j]-dp[i-1][j-1]+matrix[i][j] 这个很好理解,相当于求面积,dp[i][j-1]+dp[i-1][j]加了两遍dp[i-1][j-1],所以要减掉一个,最后再加上dp[i][j]独有的matrix[i][j]。 有了dp[i][j]之后,怎样求任意一个子矩阵的和呢?假设我们要求第i,j两行、u,v两列交成的子矩阵的和,应该怎样计算呢。这个也很好算,也是面积的加加减减,如下图所示,把(j,v)的面积减去(j,u)左边以及(i,v)上边的面积,但是(i,u)左上的面积减了两次,所以还要加上。总结成公式就是: cursum=dp[j][v]-dp[j][u-1]-dp[i-1][v]+dp[i-1][u-1]
      |               |
-----(i,u)----------(i,v)----
      |               |
      |               |
-----(j,u)----------(j,v)----
      |               |
通过cursum公式,我们很容易就能求出任意一个子矩阵的和了。代码如下: [cpp] class NumMatrix { private: vector<vector<int>> dp; public: NumMatrix(vector<vector<int>> matrix) { if (matrix.empty() || matrix[0].empty())return; int m = matrix.size(), n = matrix[0].size(); for (int i = 0; i < m + 1; ++i)dp.push_back(vector<int>(n + 1, 0)); // 为了方便,多增加了一行和一列 for (int i = 1; i <= m; ++i) { for (int j = 1; j <= n; ++j) { dp[i][j] = dp[i][j – 1] + dp[i – 1][j] – dp[i – 1][j – 1] + matrix[i – 1][j – 1]; } } } int sumRegion(int row1, int col1, int row2, int col2) { if (dp.empty())return 0; ++row1; // 因为dp多一行和一列,所以这里提前加上 ++col1; ++row2; ++col2; return dp[row2][col2] – dp[row2][col1 – 1] – dp[row1 – 1][col2] + dp[row1 – 1][col1 – 1]; } }; [/cpp] 本代码提交AC,用时22MS。]]>

LeetCode Design Twitter

LeetCode Design Twitter

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:
  1. postTweet(userId, tweetId): Compose a new tweet.
  2. 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.
  3. follow(followerId, followeeId): Follower follows a followee.
  4. 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);

设计一个Twitter系统,实现三个接口,postTweet发文,follow某人,unfollow某人,getNewsFeed从自己以及follow的人中取出最近发表的三篇推文,按时间从近到远排序。比较麻烦的是getNewsFeed函数的实现。 先来个暴力版本,用unordered_map<int, unordered_set<int>>保存某个人follow的所有人,这样follow和unfollow的操作都可以在O(1)实现。 用vector<Tweet>保存所有推文,而且是最新postTweet的推文都push_back到末尾,所以该容器保存了全局从远到近的所有推文。那么getNewsFeed时,直接从容器末尾往前遍历所有推文,判断该推文是否是自己或者自己follow的用户发表的,如果是,则收集起来,直到收集到10篇推文。 思路和实现都非常简单,代码如下: [cpp] class Twitter { private: struct Tweet { int userId; int tweetId; Tweet(int u, int t) :userId(u), tweetId(t) {}; }; vector<Tweet> Tweets; unordered_map<int, unordered_set<int>> following; public: /** Initialize your data structure here. */ Twitter() { } /** Compose a new tweet. */ void postTweet(int userId, int tweetId) { Tweets.push_back({ userId,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<int> getNewsFeed(int userId) { vector<int> ans; for (int i = Tweets.size() – 1; i >= 0; –i) { int &uid = Tweets[i].userId; if (uid == userId || following[userId].find(uid) != following[userId].end()) { ans.push_back(Tweets[i].tweetId); if (ans.size() == 10)break; } } return ans; } /** Follower follows a followee. If the operation is invalid, it should be a no-op. */ void follow(int followerId, int followeeId) { following[followerId].insert(followeeId); } /** Follower unfollows a followee. If the operation is invalid, it should be a no-op. */ void unfollow(int followerId, int followeeId) { following[followerId].erase(followeeId); } }; [/cpp] 本代码提交AC,用时143MS。 上面的版本比较慢,主要是每次getNewsFeed时都要遍历全局所有的推文。讨论区有一种做法是把每个人发表的推文也保存到一个unordered_map<int, vector<Tweet>>中,那么在getNewsFeed时,只需要拿出自己或自己follow的用户发表的推文,放到一个优先队列中,优先队列自动按时间先后顺序排序了,从优先队列中取出10条推文即可。 为了按时间排序,需要一个全局时间戳,并且重载优先队列的比较运算符。 代码如下: [cpp] class Twitter { private: int timestamp; struct Tweet { int tweetId; int timestamp; Tweet(int tid, int ts) :tweetId(tid), timestamp(ts) {}; bool operator<(const Tweet& t)const { return timestamp < t.timestamp; } }; unordered_map<int, vector<Tweet>> Tweets; unordered_map<int, unordered_set<int>> following; public: /** Initialize your data structure here. */ Twitter() :timestamp(0) { } /** Compose a new tweet. */ void postTweet(int userId, int tweetId) { Tweets[userId].push_back({ tweetId,timestamp++ }); } /** 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<int> getNewsFeed(int userId) { priority_queue<Tweet> pq; for (const auto& uid : following[userId]) { for (const auto& t : Tweets[uid]) { pq.push(t); } } for (const auto& t : Tweets[userId]) { pq.push(t); } vector<int> ans; while (!pq.empty() && ans.size() < 10) { ans.push_back(pq.top().tweetId); pq.pop(); } return ans; } /** Follower follows a followee. If the operation is invalid, it should be a no-op. */ void follow(int followerId, int followeeId) { if (followerId != followeeId)following[followerId].insert(followeeId); // map中不考虑自己follow自己,因为getNewsFeed单独考虑了 } /** Follower unfollows a followee. If the operation is invalid, it should be a no-op. */ void unfollow(int followerId, int followeeId) { following[followerId].erase(followeeId); } }; [/cpp] 本代码提交AC,用时33MS,快了好几倍!]]>

LeetCode Contains Duplicate III

220. Contains Duplicate III

Given an array of integers, find out whether there are two distinct indices i and j in the array such that the absolute difference between nums[i] and nums[j] is at most t and the absolute difference between i and j is at most k.

Example 1:

Input: nums = [1,2,3,1], k = 3, t = 0
Output: true

Example 2:

Input: nums = [1,0,1,1], k = 1, t = 2
Output: true

Example 3:

Input: nums = [1,5,9,1,5,9], k = 2, t = 3 Output: false

给定一个数组,问是否存在两个相距k之内的差的绝对值在t之内的数。
这个题有点难度,是LeetCode Contains Duplicate II的升级版。
首先,要求距离在k之内,所以想到维护一个长度为k的滑动窗口,在这个滑动窗口内判断是否有差的绝对值在t之内的两个数。
具体参考讨论区。用一个set来保存长度为k的滑动窗口window,每次往前滑动一格,如果窗口大于k了,则删除窗口左边的元素。对于即将进入窗口的数nums[i],我们需要判断窗口内是否存在一个x,使得$|x-nums[i]|\leq t$,展开就是$nums[i]-t\leq x\leq t+nums[i]$。
由于set内部是有序的树结构,所以可以用set.lower_bound直接判断大于等于$nums[i]-t$的数x是否在window中。如果在的话,我们再判断是否满足$x\leq t+nums[i]$,这个式子等价于$x-nums[i]\leq t$,为什么要这么写呢,因为nums[i]有可能是INT_MAX或者INT_MIN,为了防止溢出,需要把相关的操作数转换为long long。
完整代码如下:

typedef long long ll;
class Solution {
public:
    bool containsNearbyAlmostDuplicate(vector<int>& nums, int k, int t)
    {
        set<ll> window;
        for (int i = 0; i < nums.size(); ++i) {
            if (i > k)
                window.erase(nums[i – k – 1]); // 删除窗口左边的元素
            auto it = window.lower_bound(ll(nums[i]) – t); // x>=nums[i]-t
            if (it != window.end() && *it – nums[i] <= t)
                return true; // x<=nums[i]+t
            window.insert(nums[i]);
        }
        return false;
    }
};

本代码提交AC,用时19MS。

LeetCode Smallest Range

LeetCode Smallest Range 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 language=",d"][/c] 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:
  1. The given list may contain duplicates, so ascending order means >= here.
  2. 1 <= k <= 3500
  3. -105 <= value of elements <= 105.
  4. For Java users, please note that the input type has been changed to List<List<Integer>>. And after you reset the code template, you’ll see this point.

给定k个升序数组,求一个整数区间,该区间能至少包括所有数组的一个元素,且该区间最小。最小的定义是区间长度最小,如果区间长度相同,则区间起始点小则小。 参考Find smallest range containing elements from k lists这篇博客,突然发现GeeksforGeeks网站上的题比Leetcode上的多好多,而且都有详细题解和代码,好棒。 要求区间最小,则区间只包括每个数组的一个元素最好,所以我们要找k个数组中的k个数,使得这k个数的最大值和最小值的差最小,那么这个区间的两个端点就是这个最大值和最小值。 所以我们对k个数组维护k个指针,初始时都指向每个数组的首元素,然后找到这k个数的最大值和最小值,得到一个区间及其长度。然后我们不断循环:向前移动k个指针中的最小者,在新的k个数中求最大值和最小值及其区间长度。直到某个数组遍历结束,则返回得到的最小区间。 以样例为例。
  1. i,j,k分别指向三个数组的开头,即i=j=k=0,相当于在[4,0,5]中找最大值和最小值,构成区间[0,5],长度为5。
  2. 最小值为j所在数组,所以++j,在新的数组[4,9,5]中找最大值和最小值,构成区间[4,9],长度为5。
  3. 最小值为i所在数组,所以++i,在新的数组[10,9,5]中找最大值和最小值,构成区间[5,10],长度为5。
  4. ++k,新数组[10,9,18],区间[9,18],长度为9。
  5. ++j,新数组[10,12,18],区间[10,18],长度为8。
  6. ++i,新数组[15,12,18],区间[12,18],长度为6。
  7. ++j,新数组[15,20,18],区间[15,20],长度为5。
  8. ++i,新数组[24,20,18],区间[18,24],长度为6。
  9. ++k,新数组[24,20,22],区间[20,24],长度为4。
  10. ++j,第二个数组遍历完毕,结束,遍历过程中,得到的长度最短的区间是[20,24]。
完整代码如下: [cpp] class Solution { public: vector<int> smallestRange(vector<vector<int>>& nums) { int ansmin = 0, ansmax = 0, ansrange = INT_MAX, k = nums.size(); vector<int> ptr(k, 0); while (true) { bool done = false; int curmin = INT_MAX, curmax = INT_MIN, minid = 0; for (int i = 0; i < k; ++i) { if (ptr[i] >= nums[i].size()) { done = true; break; } if (nums[i][ptr[i]] < curmin) { curmin = nums[i][ptr[i]]; minid = i; } if (nums[i][ptr[i]] > curmax) { curmax = nums[i][ptr[i]]; } } if (done)break; if (curmax – curmin < ansrange) { ansrange = curmax – curmin; ansmin = curmin; ansmax = curmax; } ++ptr[minid]; } return{ ansmin,ansmax }; } }; [/cpp] 本代码提交AC,用时1016MS。 因为每一轮都需要从k个数组中找最大值和最小值,线性查找的复杂度为O(k)。最坏情况下,需要遍历k个数组的每个元素,假设k个数组所有元素个数为n,则总的时间复杂度为O(nk)。 求k个数组的最大值和最小值的过程,可以用大小为k的优先队列(最小堆)来优化,复杂度可以降为O(nlgk)。代码如下: [cpp] class Solution { private: struct Node { int value; int idx; int nxt; Node(int v, int i, int n) :value(v), idx(i), nxt(n) {}; bool operator<(const Node& nd) const { return value > nd.value; } bool operator>(const Node& nd) const { return value < nd.value; } }; public: vector<int> smallestRange(vector<vector<int>>& nums) { int ansmin = INT_MAX, ansmax = INT_MIN, ansrange = INT_MAX, k = nums.size(); priority_queue<Node> pq; for (int i = 0; i < k; ++i) { pq.push(Node(nums[i][0], i, 1)); if (nums[i][0] < ansmin) { ansmin = nums[i][0]; } ansmax = max(ansmax, nums[i][0]); } int curmax = ansmax; while (true) { int curmin = pq.top().value; int minidx = pq.top().idx; int minnxt = pq.top().nxt; pq.pop(); if (curmax – curmin < ansrange) { ansrange = curmax – curmin; ansmin = curmin; ansmax = curmax; } if (minnxt < nums[minidx].size()) { curmax = max(curmax, nums[minidx][minnxt]); pq.push(Node(nums[minidx][minnxt], minidx, minnxt + 1)); } else break; } return{ ansmin,ansmax }; } }; [/cpp] 本代码提交AC,用时56MS。  ]]>

LeetCode Find the Derangement of An Array

LeetCode Find the Derangement of An Array In combinatorial mathematics, a derangement is a permutation of the elements of a set, such that no element appears in its original position. There’s originally an array consisting of n integers from 1 to n in ascending order, you need to find the number of derangement it can generate. Also, since the answer may be very large, you should return the output mod 109 + 7. Example 1:

Input: 3
Output: 2
Explanation: The original array is [1,2,3]. The two derangements are [2,3,1] and [3,1,2].
Note: n is in the range of [1, 106].
给定一个长度为n的数组[1,2,3,…,n],如果该数组的一个排列中,每个数字都不在它原来的位置了,我们称这个排列是原数组的一个Derangement(错排)。问长度为n的数组,有多少个Derangement。 维基百科关于错排问题有比较详细的解释。 假设dp[n]表示长度为n的数组的错排个数,显然dp[0]=1,dp[1]=0,dp[2]=1。如果已经知道dp[0,…,n-1],怎样求dp[n]。考虑第n个数,因为错排,它肯定不能放在第n位,假设n放在第k位,则有如下两种情况: 数字k放在了第n位,这时,相当于n和k交换了一下位置,还剩下n-2个数需要错排,所以+dp[n-2] 数字k不放在第n位,此时,可以理解为k原本的位置是n,也就是1不能放在第1位、2不能放在第2位、…、k不能放在第n位,也就相当于对n-1个数进行错排,所以+dp[n-1] 因为n可以放在1~n-1这n-1个位置,所以总的情况数等于dp[n]=(n-1)(dp[n-1]+dp[n-2])。 这就类似于求斐波那契数列的第n项了,维护前两个变量,不断滚动赋值就好了,时间复杂度O(n),代码如下: [cpp] const int MOD = 1000000007; class Solution { public: int findDerangement(int n) { if (n == 0)return 1; if (n == 1)return 0; long long p = 0, pp = 1; for (int i = 2; i <= n; ++i) { long long cur = ((i – 1)*(p + pp)) % MOD; pp = p; p = cur; } return p; } }; [/cpp] 本代码提交AC,用时13MS。]]>

LeetCode Design Log Storage System

LeetCode Design Log Storage System You are given several logs that each log contains a unique id and timestamp. Timestamp is a string that has the following format: Year:Month:Day:Hour:Minute:Second, for example, 2017:01:01:23:59:59. All domains are zero-padded decimal numbers. Design a log storage system to implement the following functions: void Put(int id, string timestamp): Given a log’s unique id and timestamp, store the log in your storage system. int[] Retrieve(String start, String end, String granularity): Return the id of logs whose timestamps are within the range from start to end. Start and end all have the same format as timestamp. However, granularity means the time level for consideration. For example, start = “2017:01:01:23:59:59”, end = “2017:01:02:23:59:59”, granularity = “Day”, it means that we need to find the logs within the range from Jan. 1st 2017 to Jan. 2nd 2017. Example 1:

put(1, "2017:01:01:23:59:59");
put(2, "2017:01:01:22:59:59");
put(3, "2016:01:01:00:00:00");
retrieve("2016:01:01:01:01:01","2017:01:01:23:00:00","Year"); // return [1,2,3], because you need to return all logs within 2016 and 2017.
retrieve("2016:01:01:01:01:01","2017:01:01:23:00:00","Hour"); // return [1,2], because you need to return all logs start from 2016:01:01:01 to 2017:01:01:23, where log 3 is left outside the range.
Note:
  1. There will be at most 300 operations of Put or Retrieve.
  2. Year ranges from [2000,2017]. Hour ranges from [00,23].
  3. Output for Retrieve has no order required.

设计一个日志系统,该系统有两个操作,put(id,timestamp)把timestamp时刻的日志id放到日志系统中,retrieve(start,end,gra)从系统中取出timestamp范围在[start,end]之间的日志id,时间的粒度是gra。 我设计的系统是这样的,为了方便retrieve,系统中的日志都按timestamp排序了。有趣的是,在zero-padded(每部分不足补前导0)的情况下,timestamp的字符串排序就是timestamp表示的时间的排序。 定义一个Node结构体,保持一个日志,信息包括日志id和timestamp。用一个链表存储所有Node,并且当新Node插入时,采用插入排序的方法使得链表始终有序。 retrieve的时候,根据粒度,重新设置start和end,比如样例中粒度为Year时,把start改为Year固定,其他时间最小
"2016:00:00:00:00:00"
把end改为Year固定,其他时间最大
"2017:12:31:23:59:59"
这样我只需要遍历链表,把所有timestamp字符串在这个范围内的日志id取出来就好了。其他粒度也是类似的。 完整代码如下: [cpp] class LogSystem { private: struct Node { int id; string timestamp; Node(int i, string t) :id(i), timestamp(t) {}; }; list<Node> log; string start, end; public: LogSystem() { start = "2000:00:00:00:00:00"; end = "2017:12:31:23:59:59"; } void put(int id, string timestamp) { Node node(id, timestamp); if (log.empty())log.push_back(node); else { auto it = log.begin(); while (it != log.end() && (*it).timestamp <= timestamp)++it; log.insert(it, node); } } vector<int> retrieve(string s, string e, string gra) { if (gra == "Year") { s = s.substr(0, 4) + start.substr(4); e = e.substr(0, 4) + end.substr(4); } else if (gra == "Month") { s = s.substr(0, 7) + start.substr(7); e = e.substr(0, 7) + end.substr(7); } else if (gra == "Day") { s = s.substr(0, 10) + start.substr(10); e = e.substr(0, 10) + end.substr(10); } else if (gra == "Hour") { s = s.substr(0, 13) + start.substr(13); e = e.substr(0, 13) + end.substr(13); } else if (gra == "Minute") { s = s.substr(0, 16) + start.substr(16); e = e.substr(0, 16) + end.substr(16); } vector<int> ans; auto it = log.begin(); while (it != log.end() && (*it).timestamp < s)++it; while (it != log.end() && (*it).timestamp <= e) { ans.push_back((*it).id); ++it; } return ans; } }; [/cpp] 本代码提交AC,用时19MS。]]>