Monthly Archives: March 2017

LeetCode 01 Matrix

LeetCode 01 Matrix 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:
  1. The number of elements of the given matrix will not exceed 10,000.
  2. There are at least one 0 in the given matrix.
  3. The cells are adjacent in only four directions: up, down, left and right.

给定一个0/1矩阵,要求每个点到其最近的0的距离。显然,如果自身是0的话,到最近的0的距离就是0,所以这里只需要求当cell不为0时,它到最近的0的距离。 显然是个BFS的题,从点(i,j)往外广度搜索,第一个遇到0时,走过的步数就是和0的最短距离;没遇到0时,不断的把点加入到队列中。 自定义一个MyPoint结构体,保存点的坐标,以及从出发点到该点的步数。注意本题不需要visited数组,visited数组的目的是保证在BFS时不走之前走过的点,但是BFS走过的路径肯定有那种没走之前走过的点的路径,这样的路径到达0的距离肯定会比走重复点的路径到达0的距离要短。所以不用visited也能找到最短距离。 代码如下: [cpp] typedef struct MyPoint { int x, y; int step; MyPoint(int x_, int y_, int step_) :x(x_), y(y_), step(step_) {}; }; class Solution { private: int bfs(int i, int j, vector<vector<int>>& matrix) { int m = matrix.size(), n = matrix[0].size(); MyPoint p(i, j, 0); queue<MyPoint> qp; qp.push(p); while (!qp.empty()) { p = qp.front(); if (matrix[p.x][p.y] == 0)return p.step; qp.pop(); if (p.x – 1 >= 0) { MyPoint tmp(p.x – 1, p.y, p.step + 1); qp.push(tmp); } if (p.x + 1 < m) { MyPoint tmp(p.x + 1, p.y, p.step + 1); qp.push(tmp); } if (p.y – 1 >= 0) { MyPoint tmp(p.x, p.y – 1, p.step + 1); qp.push(tmp); } if (p.y + 1 < n) { MyPoint tmp(p.x, p.y + 1, p.step + 1); qp.push(tmp); } } } public: vector<vector<int>> updateMatrix(vector<vector<int>>& matrix) { int m = matrix.size(), n = 0; if (m > 0)n = matrix[0].size(); if (m == 0 || n == 0)return vector<vector<int>>(); vector<vector<int>> ans(m, vector<int>(n, 0)); for (int i = 0; i < m; ++i) { for (int j = 0; j < n; ++j) { if (matrix[i][j] != 0) { ans[i][j] = bfs(i, j, matrix); } } } return ans; } }; [/cpp] 本代码提交AC,用时276MS。]]>

LeetCode Find Largest Value in Each Tree Row

LeetCode Find Largest Value in Each Tree Row 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]

给定一棵二叉树,找出每层的最大值。 简单题,直接BFS,在每一层找最大值。代码如下: [cpp] class Solution { public: vector<int> largestValues(TreeNode* root) { vector<int> ans; if (!root)return ans; queue<TreeNode*> q; q.push(root); while (!q.empty()) { int largest = INT_MIN, n = q.size(); while (n–) { TreeNode* front = q.front(); q.pop(); largest = max(largest, front->val); if (front->left)q.push(front->left); if (front->right)q.push(front->right); } ans.push_back(largest); } return ans; } }; [/cpp] 本代码提交AC,用时13MS。]]>

LeetCode Beautiful Arrangement

LeetCode Beautiful Arrangement 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:

  1. The number at the ith position is divisible by i.
  2. 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:
  1. N is a positive integer and will not exceed 15.

本题要求1~N共有多少个漂亮的排列。一个漂亮的排列A[]需要满足以下两个条件中的一个:
  1. A[i]能整除i
  2. i能整除A[i]
上面的规则中下标从1开始。 本题使用DFS解决,想象数组长度为N,每个位置上都可能填入1~N这N个数。咱们从第一个位置,尝试填入1~N,然后把visited置位;第二个位置,尝试填入除第一个已经填入的其他所有数字。每个位置尝试填入时,都需要至少满足以上两个条件中的一个。当所有位置都填满时,找到一个可行解。回退时,需要把visited相关位复位。 完整代码如下: [cpp] class Solution { void dfs(int step, const int& N, vector<int>& visited, int& ans) { if (step > N) { ++ans; return; } for (int i = 1; i <= N; ++i) { if (visited[i] == 0 && (i%step == 0 || step%i == 0)) { visited[i] = 1; dfs(step + 1, N, visited, ans); visited[i] = 0; } } } public: int countArrangement(int N) { vector<int> visited(N + 1, 0); int ans = 0; dfs(1, N, visited, ans); return ans; } }; [/cpp] 本代码提交AC,用时149MS。]]>

LeetCode Queue Reconstruction by Height

LeetCode Queue Reconstruction by Height 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]]

本题给了一个存储pair的数组,每个pair是一个(h,k)对,表示身高为h的人,其前面有k个身高大于等于h的人。现在要对这个数组重新排序,使得所有pair都满足其自己的(h,k)约束。 网上有一种很巧妙的方法,先对数组排序,排序规则是h大的在前,如果h相等,则k小的在前。然后新建一个数组,把每个pair插入到下标k的位置。 我们来举个例子,比如样例数据,先按上述规则排序之后变成了: [7,0], [7,1], [6,1], [5,0], [5,2], [4,4] 上述排序规则基于这样一个事实:h越小的,其前面比h大的数越多,越有可能满足其k约束;h相同时,肯定是k小的排前面,因为相同的h对k是有贡献的。 然后新建一个空的数组,不断把上述排好序的元素插入空数组中。
  1. [7,0]插入第0个位置,数组为[7,0]
  2. [7,1]插入第1个位置,数组为[7,0], [7,1]
  3. [6,1]插入第1个位置,数组为[7,0], [6,1], [7,1]
  4. [5,0]插入第0个位置,数组为[5,0], [7,0], [6,1], [7,1]
  5. [5,2]插入第2个位置,数组为[5,0], [7,0], [5,2], [6,1], [7,1]
  6. [4,4]插入第4个位置,数组为[5,0], [7,0], [5,2], [6,1], [4,4], [7,1]
完整代码如下: [cpp] bool comp(const pair<int, int>& p1, const pair<int, int>& p2) { return p1.first > p2.first || (p1.first == p2.first&&p1.second < p2.second); } class Solution { public: vector<pair<int, int>> reconstructQueue(vector<pair<int, int>>& people) { sort(people.begin(), people.end(), comp); vector<pair<int, int>> ans; for (int i = 0; i < people.size(); ++i) { ans.insert(ans.begin() + people[i].second, people[i]); } return ans; } }; [/cpp] 本代码提交AC,用时49MS。]]>

LeetCode Find Bottom Left Tree Value

LeetCode Find Bottom Left Tree Value 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.
找出一棵二叉树最后一行的最左边元素。 简单题,直接BFS,每层保存队列第一个元素,BFS结束之后,就能得到最后一行的第一个元素。 代码如下: [cpp] class Solution { public: int findBottomLeftValue(TreeNode* root) { queue<TreeNode*> q; q.push(root); int ans = 0; while (!q.empty()) { int n = q.size(); ans = q.front()->val; while (n–) { TreeNode* front = q.front(); q.pop(); if (front->left)q.push(front->left); if (front->right)q.push(front->right); } } return ans; } }; [/cpp] 本代码提交AC,用时12MS。]]>

LeetCode Third Maximum Number

LeetCode Third Maximum Number Given a non-empty array of integers, return the third maximum number in this array. If it does not exist, return the maximum number. The time complexity must be in O(n). Example 1:

Input: [3, 2, 1]
Output: 1
Explanation: The third maximum is 1.
Example 2:
Input: [1, 2]
Output: 2
Explanation: The third maximum does not exist, so the maximum (2) is returned instead.
Example 3:
Input: [2, 2, 3, 1]
Output: 1
Explanation: Note that the third maximum here means the third maximum distinct number.
Both numbers with value 2 are both considered as second maximum.

找出一个数组中的第三大的数,如果不存在第三大的数,则返回最大的数。数组中可能会有重复数字。要求用O(n)的时间。 定义最大的数、第二大、第三大的数分别为first、second、third,遍历数组然后不断更新这三个数,更新的方法是从最大数错位赋值,很容易理解。求数组中第二大的数也是类似的道理。 看代码就很好理解了,注意数组中可能会有INT_MIN,所以需要用long数据类型。另外,本题要求的第三大是相异数字的排名,比如第三个样例,第三大的应该是1,而不是2,虽然有第二个2排名第三,但和第二名重复了,不能算,这一点在两个else if中需要保证严格小于第一名或第二名。 [cpp] class Solution { public: int thirdMax(vector<int>& nums) { long first = LONG_MIN, second = LONG_MIN, third = LONG_MIN; for (int i = 0; i < nums.size(); ++i) { if (nums[i] > first) { third = second; second = first; first = nums[i]; } else if (nums[i] < first && nums[i] > second) { third = second; second = nums[i]; } else if (nums[i] < second && nums[i] > third)third = nums[i]; } if (third == LONG_MIN)return first; else return third; } }; [/cpp] 本代码提交AC,用时6MS。]]>

LeetCode Encode and Decode TinyURL

LeetCode Encode and Decode TinyURL

Note: This is a companion problem to the System Design problem: Design TinyURL.
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.
实现短网址的功能,即把一个长的网址加密成短网址,并且能够把短网址还原为原来的长网址。 显然要用Hash,有多种方法,参考:https://leetcode.com/articles/encode-and-decode-tinyurl/ 直接用一个计数器当key,相当于自己维护了一个长的url池,给每个url编了一个号。加密的时候把号码给他,解密的时候根据编号找到原始的url。代码如下: [cpp] class Solution { private: unsigned long long m_ullCnt; unordered_map<unsigned long long, string> m_umHash; public: Solution() :m_ullCnt(0) {}; // Encodes a URL to a shortened URL. string encode(string longUrl) { m_umHash[m_ullCnt] = longUrl; return "http://tinyurl.com/" + to_string(m_ullCnt++); } // Decodes a shortened URL to its original URL. string decode(string shortUrl) { int id = atoi(shortUrl.substr(19).c_str()); return m_umHash[id]; } }; [/cpp] 本代码提交AC,用时9MS。 既然可以用计数器,也可以生成不重复的随机数作为key,不断random,和上面的解法类似。 还有一种生成key的方法,就是用STL自带的hash函数,把string hash成一个size_t作为key。注意要从短url中还原回hash值时,不能用atoi和atol,因为size_t可能是unsigned int,也可能和平台有关,所以必须用sscanf “%zu”的形式转换成size_t。 代码如下: [cpp] class Solution { private: unordered_map<size_t, string> m_umHash; public: // Encodes a URL to a shortened URL. string encode(string longUrl) { hash<string> h; m_umHash[h(longUrl)] = longUrl; return "http://tinyurl.com/" + to_string(h(longUrl)); } // Decodes a shortened URL to its original URL. string decode(string shortUrl) { size_t id = 0; sscanf(shortUrl.substr(19).c_str(), "%zu", &id); return m_umHash[id]; } }; [/cpp] 本代码提交AC,用时6MS。]]>

LeetCode Nth Digit

LeetCode Nth Digit 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:
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.

自然数的连续序列中,第n个数字是什么。 简单的数学题,没有捷径,直接统计吧。
  • 1~9:宽度为1,数字个数1*9
  • 10~99:宽度为2,数字个数2*90
  • 100~999:宽度为3,数字个数3*900
首先不断累加1*9+2*90+3*900+…+k*9*pow(10,k-1)直到和大于n时停止,此时能定位到第n个数字所在的数的宽度width以及该宽度的第一个数start。然后计算在该宽度范围内,偏移了几个数字leftdigit=n-{1*9+2*90+…+(k-1)*9*pow(10,k-2)}。根据leftdigit和width又能定位到原始第n个数字所在的数target以及第n个数字在target中的第几个位置pos。最后把target转换为string取出第pos位数字即可。 代码如下,注意要把代码中的变量都定义为long long,否则大数可能溢出。 [cpp] class Solution { public: int findNthDigit(int n) { long long width = 1, start = 1, num = 9; while (n > num) { ++width; start *= 10; num += width * 9 * start; } long long leftdigit = n – (num – width * 9 * start); long long offset = leftdigit / width + ((leftdigit%width == 0) ? 0 : 1); long long target = start + offset – 1, pos = leftdigit%width; if (pos == 0)pos = width; string s = to_string(target); char c = s[pos – 1]; return atoi(&c); } }; [/cpp] 本代码提交AC,用时3MS。 最后定位到target之后,不转换成string,直接用除法和取模运算也能得到第pos位数字,代码如下: [cpp] class Solution { public: int findNthDigit(int n) { long long width = 1, start = 1, num = 9; while (n > num) { ++width; start *= 10; num += width * 9 * start; } long long leftdigit = n – (num – width * 9 * start); long long offset = leftdigit / width + ((leftdigit%width == 0) ? 0 : 1); long long target = start + offset – 1, pos = leftdigit%width; if (pos == 0)pos = width; int cnt = 0, ans; while (cnt < pos) { ans = target / start; target -= ans*start; start /= 10; ++cnt; } return ans; } }; [/cpp] 本代码提交AC,用时6MS。]]>

LeetCode Isomorphic Strings

205. Isomorphic Strings

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.

Example 1:

Input: s = "egg", t = "add"
Output: true

Example 2:

Input: s = "foo", t = "bar"
Output: false

Example 3:

Input: s = "paper", t = "title"
Output: true

Note:
You may assume both and have the same length.


判断两个字符串是否同构。同构的意思是字符串s和t中的字母能找到一种一一映射的关系,使得通过这个映射之后s能变成t,t也能变成s。 简单题,维护两个hash表,一个保存s到t的映射,另一个保存t到s的映射。在遍历字符串的过程中,同时判断两个映射是否满足一一映射。 注意不能只用一个hash,因为可能出现s=ab,t=aa的情况,看s->t,a映射到a,b映射到b,没有问题。但是看t->s时,有问题,出现了a既映射到a又映射到b的情况。所以需要同时保存s到t和t到s的映射。 代码如下:

class Solution {
public:
    bool isIsomorphic(string s, string t)
    {
        unordered_map<char, char> hash1, hash2;
        for (int i = 0; i < s.size(); ++i) {
            if (hash1.find(s[i]) == hash1.end())
                hash1[s[i]] = t[i];
            else {
                if (hash1[s[i]] != t[i])
                    return false;
            }
            if (hash2.find(t[i]) == hash2.end())
                hash2[t[i]] = s[i];
            else {
                if (hash2[t[i]] != s[i])
                    return false;
            }
        }
        return true;
    }
};

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

LeetCode Diameter of Binary Tree

543. Diameter of Binary Tree

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.


本题要求一棵树的直径,树的直径的定义是树中任意两点间的最长距离。
任意两点间的最长路径有如下三种选择:

  1. 经过根节点的最长路径
  2. 在左子树中不经过根节点的最长路径
  3. 在右子树中不经过根节点的最长路径

其中第1种情况比较好处理,必须经过根节点,又必须是最长路径,则路径长度肯定是左右子树的最大高度之和+1(根节点)。第2、3种情况就更好处理了,就是以左右孩子为根递归计算最长路径。
代码如下:

class Solution {
private:
    int getHeight(TreeNode* root)
    {
        if (!root)
            return 0;
        int lh = getHeight(root->left);
        int rh = getHeight(root->right);
        return 1 + max(lh, rh);
    }

public:
    int diameterOfBinaryTree(TreeNode* root)
    {
        if (!root)
            return 0;
        //int ans = 1 + getHeight(root->left) + getHeight(root->right); // 节点数
        int ans = getHeight(root->left) + getHeight(root->right); // 路径数
        ans = max(ans, diameterOfBinaryTree(root->left));
        ans = max(ans, diameterOfBinaryTree(root->right));
        return ans;
    }
};

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

参考:http://www.geeksforgeeks.org/diameter-of-a-binary-tree/,这篇博客的路径是指节点数,而本题的路径是指边数,所以第12行不能再加1了。

以上代码递归的过程是从根节点往孩子节点递归,这样计算高层节点的高度时,其实已经计算过低层节点的高度了,后面递归到低层节点时,会重复计算。所以代码还可以优化,比如可以不断递归到叶子节点再返回,这样高层节点的高度就可以直接用低层节点的高度加1计算得到,可以省不少时间。

二刷。在求解路径的同时可计算高度,而且先递归到叶子,然后不断从往上走,代码如下:

class Solution {
public:
	int DiameterAndHeight(TreeNode* root, int &height) {
		if (root == NULL || (root->left == NULL && root->right == NULL)) {
			height = 0;
			return 0;
		}
		int left_height = 0, right_height = 0;
		int left_diameter = DiameterAndHeight(root->left, left_height);
		int right_diameter = DiameterAndHeight(root->right, right_height);
		height = max(left_height, right_height) + 1;
		int left_edge = root->left == NULL ? 0 : 1, right_edge = root->right == NULL ? 0 : 1;
		return max(max(left_diameter, right_diameter), left_height + right_height + left_edge + right_edge);
	}
	int diameterOfBinaryTree(TreeNode* root) {
		int height = 0;
		return DiameterAndHeight(root, height);
	}
};

本代码提交AC,用时8MS,快了不少。