Monthly Archives: January 2017

LeetCode Reverse String

LeetCode Reverse String Write a function that takes a string as input and returns the string reversed. Example: Given s = “hello”, return “olleh”.


本题要把一个字符串逆序一下。最简单的题了,两种方法,一种是直接调用STL的reverse函数;另一种方法是用首尾指针不断swap。 方法1代码如下: [cpp] class Solution { public: string reverseString(string s) { reverse(s.begin(), s.end()); return s; } }; [/cpp] 本代码提交AC,用时9MS。 方法2代码如下: [cpp] class Solution { public: string reverseString(string s) { int i = 0, j = s.size() – 1; while (i < j) { swap(s[i], s[j]); ++i; –j; } return s; } }; [/cpp] 本代码提交AC,用时也是9MS。]]>

LeetCode Counting Bits

LeetCode Counting Bits 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?

这一题很有意思,给定数字num,要把0~num之间每个数的二进制中1的个数列出来,比如num=5时,需要计算0,1,2,3,4,5这6个数字每个数字的二进制中,1的个数,分别是0,1,1,2,1,2。 这样就不能用LeetCode Number of 1 Bits的方法对每个数都求一遍了,复杂度肯定太高了。这题一看就知道是找规律的题,所以我们先把前几个数的二进制1的个数列出来看看。   观察的时候需要分组观察,比如[0,1]的结果为0,1,[2,3]的结果为1,2,也就是在[0,1]的结果上加了1;[4,7]的结果为1,2,2,3,也就是在[0,3]的结果上加了1;[8,15]的结果为1,2,2,3,2,3,3,4,也就是在[0,7]的结果上加了1。 所以重复的周期是以2的幂递增的,初始的时候,我们可以在ans数组中只存[0,1]的结果,然后每次添加2的幂个结果进ans,直到ans的大小大于num为止。 完整代码如下: [cpp] class Solution { public: vector<int> countBits(int num) { vector<int> ans = { 0,1 }; while (ans.size() <= num) { int old_size = ans.size(); for (int i = 0; i < old_size; ++i) { ans.push_back(ans[i] + 1); } } return vector<int>(ans.begin(), ans.begin() + num + 1); } }; [/cpp] 本代码提交AC,用时73MS。 ]]>

LeetCode Battleships in a Board

LeetCode Battleships in a Board 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 an invalid board that you will not receive – as battleships will always have a cell separating between them. Follow up: Could you do it in one-pass, using only O(1) extra memory and without modifying the value of the board?
这一题的题意有点难懂,要问一个面板上有多少个舰队,注意是舰队,而不是单个的战舰。比如第一个例子中,为什么有两个舰队呢,左上角单独的X是一个舰队,右边的一列是另一个舰队,所以一共有两个舰队。 题目还限制了舰队只可能是横向或者纵向排列的,而且两个舰队之间至少有一个空位.隔开。 因为题中说所有测试数据都是合法的,且两个舰队之间至少有一个空位.隔开,所以可以用深搜DFS找有多少个X连成的区域,就有多少个舰队。完整代码如下: [cpp] class Solution { public: void dfs(vector<vector<char>>& board, vector<vector<char>>& mark, int i, int j) { if (mark[i][j] == 1)return; if (board[i][j] == ‘.’)return; mark[i][j] = 1; if (i – 1 >= 0) { dfs(board, mark, i – 1, j); } if (i + 1 < board.size()) { dfs(board, mark, i + 1, j); } if (j – 1 >= 0) { dfs(board, mark, i, j – 1); } if (j + 1 < board[i].size()) { dfs(board, mark, i, j + 1); } } int countBattleships(vector<vector<char>>& board) { int ans = 0; vector<vector<char>> mark(board.size(), vector<char>(board[0].size(), 0)); for (int i = 0; i < board.size(); ++i) { for (int j = 0; j < board[i].size(); ++j) { if (mark[i][j] == 0 && board[i][j] == ‘X’) { ++ans; dfs(board, mark, i, j); } } } return ans; } }; [/cpp] 本代码提交AC,用时3MS。 Follow up中提到能否只用O(1)空间,且one-pass,网上题解提到,一个合法舰队的起始点的左边和上边肯定是空位.,否则这个X就会和其左边或者上边的X组成一个更大的舰队。所以我们只需要找面板中其左边和上边是.的X的个数。完整代码如下: [cpp] class Solution2 { public: int countBattleships(vector<vector<char>>& board) { int ans = 0; for (int i = 0; i < board.size(); ++i) { for (int j = 0; j < board[i].size(); ++j) { if (board[i][j] == ‘X’) { if ((i == 0 || board[i – 1][j] == ‘.’) && (j == 0 || board[i][j – 1] == ‘.’)) { ++ans; } } } } return ans; } }; [/cpp] 本代码提交AC,用时6MS。]]>

LeetCode Number Complement

LeetCode Number Complement Given a positive integer, output its complement number. The complement strategy is to flip the bits of its binary representation. Note:

  1. The given integer is guaranteed to fit within the range of a 32-bit signed integer.
  2. 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.

求一个数的二进制补,比如整数5的二进制是101,那么它的二进制补就是010,也就是整数2。求补的时候不能考虑前导零(leading zero)。 这个题好像AC率很高的,但是我还想了几天,之前的思路一直不对,我想的是判断到num中的一个0之后,ans就置1,然后往左移,但是这样的顺序和正确结果正好反了。 后来开窍了,num可以用移位的思想,但是ans可以不用移位,直接与上某个数,把对应位置1就好了。所以先要用一个变量i记录当前num已经移了多少位了,如果发现num当前位为0,则把ans中第i位置为1就好了,直到num为0。完整代码如下: [cpp] class Solution { public: int findComplement(int num) { int ans = 0, i = 0; while (num > 0) { if ((num & 1) == 0)ans |= (1 << i); num >>= 1; ++i; } return ans; } }; [/cpp] 本代码提交AC,用时3MS,居然只打败了5%的人,肯定还有其他方法。 网上找到这样一种解法。位运算的波浪号~本身就是取反操作,其实可以借用的。(我隐隐约约也感觉,这个位翻转的操作应该会有直接的位运算吧,可能是太久没用过~了,想不起来。) 但是~是对所有位进行翻转,包括翻转leading zero,所以需要对~稍加改造。~num翻转完之后,我们还要把leading zero翻回来,所以得知道哪些是leading zero。当num右移到0时,之前移过的肯定不是leading zero,所以之后的位肯定就是leading zero了,根据这个思路有下面的代码: [cpp] class Solution { public: int findComplement(int num) { int mask = 0xffffffff, tmp = num; while (tmp > 0) { mask <<= 1; tmp >>= 1; } return (~mask)&(~num); } }; [/cpp] 本代码提交AC,用时6MS,居然变慢了。。。不过多掌握一种思路也是好的。]]>

LeetCode Power of Four

LeetCode Power of Four 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?


判断一个数是否是4的幂,之前那些通用方法还是可以用的。 首先4的幂肯定是2的幂,所以可以先用LeetCode Power of Two的方法判断是否为2的幂,如果是2的幂,进一步找4的幂和2的幂的差别。 首先,4的幂减1肯定能够被3整除,所以有下面的解法: [cpp] class Solution { public: bool isPowerOfFour(int n) { return (n > 0) && ((n & (n-1)) == 0) &&((n – 1) % 3 == 0); } }; [/cpp] 本代码提交AC,用时3MS。 其次,观察一下4的幂的二进制表示:
  • 1:1
  • 4:100
  • 16:10000
从右往左数,其最高位1所在的位置都是奇数位,所以如果把4的幂num和$$0x55555555_2=1010101010101010101010101010101$$相与,得到的还是num本身,所以有下面的解法: [cpp] class Solution { public: bool isPowerOfFour(int n) { return (n > 0) && ((n & (n-1)) == 0) && ((n & 0x55555555) == n); } }; [/cpp] 本代码提交AC,用时3MS。 以上解法参考这个题解。]]>

LeetCode Power of Two

231. Power of Two

Given an integer, write a function to determine if it is a power of two.

Example 1:

Input: 1
Output: true 
Explanation: 20 = 1

Example 2:

Input: 16
Output: true
Explanation: 24 = 16

Example 3:

Input: 218 Output: false

判断一个数是否是2的幂次方。 递归或迭代等常规方法就不说了,可以参考LeetCode Power of Three网上看到两个比较有意思的解法,和位运算有关。下面列出了2的幂次方以及对应的二进制表示,先找找规律。

  • 1:1
  • 2:10
  • 4:100
  • 8:1000
  • 16:10000

发现所有2的幂的二进制中都只有1位是1,所有可以统计二进制中1出现的次数,等于1表示是2的幂。完整代码如下:

class Solution {
public:
    bool isPowerOfTwo(int n)
    {
        int cnt = 0;
        while (n > 0) {
            cnt += (n & 1);
            n >>= 1;
        }
        return cnt == 1;
    }
};

本代码提交AC,用时3MS。
另外,不考虑前导零(leading zero),这些2的幂只有最高位是1,如果把这个数减1,那么最高位变为0了,后面的位全变为1。把前后的两个二进制相与,正好等于0,比如$4_2=100$,$4-1=3_2=011$,$4\&3=100\&011=000$。这个思路的完整代码只需一行,如下:

class Solution {
public:
    bool isPowerOfTwo(int n) { return (n > 0) && ((n & (n - 1)) == 0); }
};

本代码提交AC,用时6MS,按理应该要比上一种解法快,不知为何。

二刷。解法太多了,直接1移位判断相等即可:

class Solution {
public:
    bool isPowerOfTwo(int n) {
        int one=1;
        for(int i=0;i<31;++i){
            int cur = one << i;
            if(cur==n)return true;
        }
        return false;
    }
};

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

LeetCode Power of Three

LeetCode Power of Three 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?


判断一个数是否是3的幂次方,能否不用递归或迭代的方法。 递归和迭代的方法都类似,就是不断除以3,看能否整除直到最后为1。完整代码如下: [cpp] class Solution { public: bool isPowerOfThree(int n) { if(n <= 0) return false; if(n == 1) return true; if(n % 3 == 0) return isPowerOfThree(n / 3); else return false; } }; [/cpp] 本代码提交AC,用时65MS。 网上题解有一些有意思的方法,摘录几个如下。 因为传入的n的范围是int,int范围内最大的3的幂次方的数是1162261467,所以任意一个3的幂,都应该能被1162261467整除,所以只要除一下就可以判断。完整代码如下: [cpp] class Solution { public: bool isPowerOfThree(int n) { return n > 0 ? (1162261467 % n == 0) : false; } }; [/cpp] 本代码提交AC,用时59MS。 当然还有一种更简单粗暴的方法,int范围内3的幂就20个,直接枚举出来用if判断就好了。。。 还有一种思路是用log,如果n是3的幂,则$$log_3(n)$$一定是整数,$$log_3$$可以用换底公式换成$$log_{10}$$。完整代码如下: [cpp] class Solution { public: bool isPowerOfThree(int n) { double p = log10(n) / log10(3); return (p – int(p)) == 0; } }; [/cpp] 本代码提交AC,用时62MS。]]>

LeetCode Word Pattern

290. Word Pattern

Given a pattern and a string str, find if str follows the same pattern.

Here follow means a full match, such that there is a bijection between a letter in pattern and a non-empty word in str.

Example 1:

Input: pattern = "abba", str = "dog cat cat dog"
Output: true

Example 2:

Input:pattern = "abba", str = "dog cat cat fish"
Output: false

Example 3:

Input: pattern = "aaaa", str = "dog cat cat dog"
Output: false

Example 4:

Input: pattern = "abba", str = "dog dog dog dog"
Output: false

Notes:
You may assume pattern contains only lowercase letters, and str contains lowercase letters that may be separated by a single space.


给定一个pattern字符串和一个实例字符串str,问这个str和pattern是否是一个双射(bijection)。双射这个词很重要,这表明str要和pattern一致,pattern也要和str一致。比如题目中第四个例子,str其实是符合pattern的,因为pattern说首尾和中间两个单词需要分别相等,但是a和b一定相等吗?我们从pattern对应到str就不行了,因为str四个单词都相等,但pattern的a和b不相等。 所以我们首先把str切分成单词数组,然后构建两个unordered_map分别对应pattern到str的映射和str到pattern的映射。只有当两个映射都正确才正确。 完整代码如下:

class Solution {
public:
    bool wordPattern(string pattern, string str)
    {
        vector<string> segs;
        int s = 0;
        for (int t = 1; t <= str.size(); ++t) {
            if (t == str.size() || str[t] == ‘ ‘) {
                segs.push_back(str.substr(s, t – s));
                s = t + 1;
            }
        }
        if (pattern.size() != segs.size())
            return false;
        unordered_map<char, string> bijection1;
        unordered_map<string, char> bijection2;
        for (int i = 0; i < pattern.size(); ++i) {
            if (bijection1.find(pattern[i]) == bijection1.end()) {
                bijection1[pattern[i]] = segs[i];
            }
            else {
                if (bijection1[pattern[i]] != segs[i])
                    return false;
            }
            if (bijection2.find(segs[i]) == bijection2.end()) {
                bijection2[segs[i]] = pattern[i];
            }
            else {
                if (bijection2[segs[i]] != pattern[i])
                    return false;
            }
        }
        return true;
    }
};

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

二刷。很简单:

class Solution {
public:
	bool wordPattern(string pattern, string str) {
		vector<string> words;
		int i = 0, j = 0, n = str.size();
		while (i < n) {
			j = i + 1;
			while (j < n&&str[j] != ' ')++j;
			string word = str.substr(i, j - i);
			words.push_back(word);
			i = j + 1;
		}
		if (pattern.size() != words.size())return false;
		int m = pattern.size();
		unordered_map<char, string> hash1;
		unordered_map<string, char> hash2;
		for (int i = 0; i < m; ++i) {
			if (hash1.find(pattern[i]) == hash1.end()) {
				hash1[pattern[i]] = words[i];
			}
			if (hash2.find(words[i]) == hash2.end()) {
				hash2[words[i]] = pattern[i];
			}

			if (hash1[pattern[i]] != words[i] || hash2[words[i]] != pattern[i])return false;
		}
		return true;
	}
};

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

LeetCode House Robber

198. House Robber

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.

Example 1:

Input: [1,2,3,1]
Output: 4
Explanation: Rob house 1 (money = 1) and then rob house 3 (money = 3).
             Total amount you can rob = 1 + 3 = 4.

Example 2:

Input: [2,7,9,3,1]
Output: 12
Explanation: Rob house 1 (money = 2), rob house 3 (money = 9) and rob house 5 (money = 1).
             Total amount you can rob = 2 + 9 + 1 = 12.

很常见的动态规划问题。 一个小偷要偷街上的一系列屋子,但是不能偷连续的两个屋子,问最大能偷到多少钱。 设置一个二维数组dp[i][j],i表示第i个屋子,j有两种取值,dp[i][0]表示不偷第i个屋子,到目前最多能偷多少钱;dp[i][1]表示偷第i个屋子,到目前最多能偷多少钱。
显然dp[i][0]=max(dp[i-1][0],dp[i-1][1]),不偷第i个屋子,前一个屋子可偷或不偷;dp[i][1]=dp[i-1][0]+nums[i],偷第i个屋子,则前一个屋子不能偷。
实际实现的时候,dp长度比nums大1,因为dp[0]用来保留初始状态,所以for循环的时候,访问nums时i要减一。
完整代码如下:

class Solution {
public:
    int rob(vector<int>& nums)
    {
        int ans = 0;
        vector<vector<int> > dp(nums.size() + 1, vector<int>(2, 0));
        for (int i = 1; i <= nums.size(); i++) {
            dp[i][0] = max(dp[i – 1][0], dp[i – 1][1]);
            dp[i][1] = dp[i – 1][0] + nums[i – 1];
            ans = max(ans, max(dp[i][0], dp[i][1]));
        }
        return ans;
    }
};

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

二刷。简化,只需要一维DP,代码如下:

class Solution {
public:
	int rob(vector<int>& nums) {
		int n = nums.size();
		if (n == 0)return 0;
		if (n == 1)return nums[0];
		vector<int> dp(n, 0);
		dp[0] = nums[0];
		dp[1] = max(nums[1], nums[0]);
		for (int i = 2; i < n; ++i) {
			dp[i] = max(dp[i], dp[i - 1]); // 不偷
			dp[i] = max(dp[i], dp[i - 2] + nums[i]); // 偷
		}
		return dp[n - 1];
	}
};

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

LeetCode Find the Difference

LeetCode Find the Difference 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.

有两个字符串s和t,其中t是由s shuffle而来,并且在随机位置上随机加上了一个字符,现在要找出这个随机加上的字符是哪个。 第一思路是,t由s shuffle而来的部分和s的组成是一样的,只是字符排列顺序不一样,所以每个字符出现的次数是一样的,而加上的那个字符导致该字符的出现次数在s和t中不一样,所以用一个hash就可以找到这个出现次数不一样的字符。 完整代码如下: [cpp] class Solution { public: char findTheDifference(string s, string t) { unordered_map<char, size_t> mpS, mpT; for (int i = 0; i < s.size(); i++) { mpS[s[i]]++; } for (int i = 0; i < t.size(); i++) { mpT[t[i]]++; } unordered_map<char, size_t>::iterator it = mpT.begin(); while (it != mpT.end()) { if (mpS.find((*it).first) == mpS.end() || mpS[(*it).first] != (*it).second)return (*it).first; it++; } return 0; // never reach here } }; [/cpp] 本代码提交AC,用时13MS。 后来想到之前做的一个题LeetCode Single Number,把s和t组合起来,不就相当于除了加入的字符,其他字符都重复出现了偶数次吗,所以把s和t中的所有字符异或一下,得到的结果就是那个插入的字符,真是高明。 完整代码如下: [cpp] class Solution { public: char findTheDifference(string s, string t) { char ans = 0; for (int i = 0; i < s.size(); i++) { ans ^= s[i]; } for (int i = 0; i < t.size(); i++) { ans ^= t[i]; } return ans; } }; [/cpp] 本代码提交AC,用时3MS,相当于前一个版本的零头,果然位运算的效率就是高。]]>