Categories
不学无术

LeetCode 38. Count and Say

题目

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.

思路

好几题没刷leetcode手痒痒,又没太多时间,就选了个Easy的题啦…这个题目确实挺简单的,统计一下前面是不是重复就行了。在句末加一个特殊符号可以避免多写几行代码的样子~

代码

class Solution {
public:
    string countAndSay(int n) {
        string nStr = "1$";
        string nextStr = "";
        while(n > 1){
            char lastChr = '$';
            int lastCount = 0;
            for(int i=0; i<nStr.size(); i++){
                if(nStr[i] == lastChr){
                    lastCount++;
                } else {
                    if(lastCount > 0){
                        string tempStr;
                        while(lastCount > 0){
                            tempStr += (lastCount%10 +'0');
                            lastCount /= 10;
                        }
                        reverse(tempStr.begin(), tempStr.end());
                        nextStr += tempStr;
                        nextStr += lastChr;
                    }
                    lastCount = 1;
                    lastChr = nStr[i];
                }
            }
            nStr = nextStr + "$";
            nextStr = "";
            n--;
        }
        return nStr.substr(0, nStr.size()-1);
    }
};

 

Categories
不学无术

LeetCode347. Top K Frequent Elements

好久不刷题了,今晚来一道。

题目

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.

思路

题目中明确提示要O(nlogn)复杂度了…
我的做法是先统计结果到一个map<value, count>里面,然后用一个大小为k的最小堆处理,每次从map中读取元素时根据count值判断是否要替换掉堆顶。由于STL的map是用红黑树实现的,所以时间复杂度就保证了,要再快的话就直接用hash函数吧,可以到O(n)。维护k大小的堆,算最差情况,建堆复杂度是O(k),维护复杂度是O(logk), 要维护M-k次,其中0<M<=N是map中的元素个数.
代码

struct CountVal{
    int val;
    int count;
    CountVal(int _val, int _count){
        val = _val;
        count = _count;
    }
};
bool compareObj(const CountVal& x, const CountVal& y){
    return x.count > y.count;
}
class Solution {
public:
    vector<int> topKFrequent(vector<int>& nums, int k) {
        map<int, int> countMap;
        vector<CountVal> counts;
        for(int i=0; i<nums.size(); i++){
            if(countMap.count(nums[i]) == 0)
                countMap[nums[i]] = 1;
            else
                countMap[nums[i]]++;
        }
        int count = 0;
        for(auto it=countMap.begin(); it!=countMap.end(); it++){
            CountVal item(it->first, it->second);
            if(count < k){
                counts.push_back(item);
                if(count == k-1){
                    make_heap(counts.begin(), counts.end(), compareObj);
                }
            } else {
                if (it->second > counts.front().count) {
                    pop_heap(counts.begin(), counts.end(), compareObj);
                    counts.pop_back();
                    counts.push_back(item);
                    push_heap(counts.begin(), counts.end(), compareObj);
                }
            }
            count++;
        }
        vector<int> results;
        for(auto it=counts.begin(); it!=counts.end(); it++){
            results.push_back(it->val);
        }
        return results;
    }
};

 

Categories
不学无术

LeetCode 331. Verify Preorder Serialization of a Binary Tree

如家网络实在太卡了,不过还好没有人劫@!$#@W…
稍微说一下思路把,这个问题就是判断某一层上的节点它的孩子会不会“溢出”,或者第一层的节点有没有完全闭合。我用一个栈来保存每一层的孩子数目(包括null):

  • 当碰到数字的时候,压一层计数为0的栈,表示新开了一层并且这一层的孩子数为0
  • 当碰到#号时,说明多了一个空孩子,当前栈顶的计数+1,并且判断当前栈顶的计数是否>=2了,如果是的话,那说明这一层已经满了,那就退栈,然后再继续给新栈顶计数+1,继续判断是否满….如此往复

由于有了#表示空节点所以层级变得明确,举个例子:一个叶节点读入两个#,当前层的计数等于2后就退栈,并且给父亲层的计数器+1(表示下一层已经结束了,父亲多了一个左/右孩子)。
代码(好久没有纯手打一遍AC了)

class Solution {
public:
    bool isValidSerialization(string preorder) {
        int size = preorder.size();
        stack<int> working;
        int i = 0;
        while(i < size){
            int end = preorder.find(',', i);
            if(end == string::npos)
                end = size;
            if(preorder[i] != '#'){
                //number
                working.push(0);
            } else {
                //'#'
                if(i == 0){
                    //The null root?!
                    if(size > 1) return false;
                    else return true;
                }
                working.top() += 1;
                while(working.size()>1 && working.top() >= 2){
                    working.pop();
                    working.top() += 1;
                }
            }
            if(working.size() == 1 && working.top() > 2)
                return false;
            i = end+1;
        }
        return (working.size()==1) && (working.top()==2);
    }
};

BTW,苏州的交通状况和路面比杭州好多了。

Categories
不学无术

LeetCode 139. Word Break

题目

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".

思路1

写的第一个版本是超时的,但是思路应当是正确的,反正就是动态规划,但我用的是递归方法。
就是如果找到s[start,end]是单词表中的单词时,有两种处理,一种是算入然后往后走;另一种是忽略这次匹配成功,继续往后寻找。
代码大概这样:

class Solution {
public:
	int findNextWord(const string& s, int start, int curr, unordered_set<string>& wordDict) {
		//Find the end position of next possible word, or return -1
		string word = s.substr(start, curr - start + 1);
		for (int i = curr + 1; i < s.size(); i++) {
			word += s[i];
			if (wordDict.count(word) > 0) {
				return i;
			}
		}
		return -1;
	}
	bool parseSeq(const string& s, unordered_set<string>& wordDict, int start, int end, map<int, bool>& cache) {
		//Judge s[start...end]
		if (end >= s.size())
			return false;
		if (cache.count(start) > 1)
			return cache[start];
		string word = s.substr(start, end - start + 1);
		if (wordDict.count(word) > 0) {
			bool withThisWord = parseSeq(s, wordDict, end + 1, end + 1, cache);
			if (end + 1 == s.size() || withThisWord) {
				cache[start] = true;
				return true;
			}
		}
		int nextPos = findNextWord(s, start, end, wordDict);
		if (-1 != nextPos) {
			bool appendWord = parseSeq(s, wordDict, start, nextPos, cache);
			if (appendWord) {
				cache[start] = true;
				return true;
			}
		}
		cache[start] = false;
		return false;
	}
	bool wordBreak(string s, unordered_set<string>& wordDict) {
		map<int, bool> cache;
		return parseSeq(s, wordDict, 0, 0, cache);
	}
};

但是如果这里出现了超级多的单字,比如字符串是

aaaaaaaaaaaaaaaaaaaaaaa

然后字典是

{a,aa,aaa,aaaa}

这就搞笑了,得匹配死,所以肯定是超时。

思路2

这个是讨论组里看来的,突然发现一般来说DP递归能做的,就可以从后往前用非递归的方法写。对于这个问题来说,从后往前并不像做LCS那样时要完全倒着写。举个例子,有字符串str=thisisawesome 和字典{this, is, a, we, some, awesome} 的时候,可以用一个缓存cache来记录是否在str[i]位置有单词结束,比如str[3]位置就是”this”这个词结束的地方。当顺着继续搜索的时候,往前查找缓存记录,可以知道如果cache[i]=true的话,那才有可能从i这个位置往后到当前位置组成新单词,接着去查找字典看看有没有这个单词,如果没有的话,就得继续往前找到新的可用的开头i。这里加黑的“才有可能”是比较关键的。
代码大概是这样

bool wordBreak(string s, unordered_set<string>& wordDict) {
		if (wordDict.empty())
			return false;
		vector<bool> cache(s.size() + 1, false); //If a word ends at i, cache[i] = true
		cache[0] = true;
		for (int i = 0; i <= s.size(); i++) {
			for (int j = i - 1; j >= 0; j--) {
				if (cache[j]) {
					//Look backward and start at the end place of previous word
					string word = s.substr(j, i - j); // s[j...i-1]
					if (wordDict.find(word) != wordDict.end()) {
						cache[i] = true;
						break;
					}
				}
			}
		}
		return cache[s.size()];
	}

这里面向前迭代不断寻找cache[i]并尝试的情况,对应于之前思路1里面找到可用单词后的决定是否使用的分支情况。但是由于减少了一大堆递归调用,所以实际情况下的复杂度降低了不少。

Categories
不学无术

LeetCode 151. Reverse Words in a String

题目

Given an input string, reverse the string word by word.
For example,
Given s = “the sky is blue“,
return “blue is sky the“.
Update (2015-02-12):
For C programmers: Try to solve it in-place in O(1) space.

思路

一!定!要!考!虑!各!种!情!况!比如

" "//一个空格
"   What am   I doing     "

然后这种倒转问题的原地处理思路应该都明白的,先把整个字符串倒转一遍,然后按照单词再倒转回来,拿”the sky is blue”为例:

  1. eulb si yks eht
  2. blue si yks eht”
  3. “blue is yks eht”
  4. “blue is sky the”

代码

class Solution {
public:
	void reverseWordsSub(string &s, int start, int end) {
		//[start,end]
		char c;
		int count = 0;
		const int maxInd = (end - start - 1) / 2 + start;
		for (int i = start; i <= maxInd; i++) {
			c = s[i];
			s[i] = s[end - count];
			s[end - count] = c;
			count++;
		}
	}
	void clearStr(string &s) {
		//Clear unnecessary spaces
		int validIndex = -1;
		bool spaceFlag = false;
		for (int i = 0; i<s.size(); i++) {
			if (s[i] != ' ') {
				s[++validIndex] = s[i];
				spaceFlag = false;
			}
			else if (!spaceFlag) {
				if (validIndex < 0) {
					continue;
				}
				spaceFlag = true;
				s[++validIndex] = s[i];
			}
		}
		if (spaceFlag)
			validIndex--;
		if (validIndex < 0)
			s = "";
		else
			s = s.substr(0, validIndex + 1);
	}
	void reverseWords(string &s) {
		clearStr(s);
		int strSz = s.size();
		reverseWordsSub(s, 0, strSz - 1);
		int start = 0;
		for (int i = 0; i<strSz; i++) {
			if (s[i] == ' ') {
				reverseWordsSub(s, start, i - 1);
				start = i + 1;
			}
		}
		reverseWordsSub(s, start, strSz - 1);
	}
};

 

Categories
不学无术

LeetCode 9. Palindrome Number

题目

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 know that the reversed integer might overflow. How would you handle such case?
There is a more generic way of solving this problem.

思路

首先要注意,负数不是回文数。
然后题目中要求不能使用额外的空间,我的理解是O(1)空间复杂度,即不能用字符串来存储?

代码

我这个解决方案不知道是不是违规了,如果再苛刻一点,那就是每次测试当前数字的位数(连续除10),然后用除法和模10判断首位和末尾的数字。

class Solution {
public:
    bool isPalindrome(int x) {
        if(x < 0)
            return false;
        int cover = 1;
        int x_copy = x;
        while(x_copy/10 > 0){
            x_copy /= 10;
            cover *= 10;
        }
        while(cover >= 10){
            int front = x/cover;
            int end = x%10;
            if(front != end){
                return false;
            }
            x -= front * cover;
            x /= 10;
            cover /= 100;
        }
        return true;
    }
};

 

Categories
不学无术

LeetCode 8. String to Integer (atoi)

题目

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 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.
Update (2015-02-10):
The signature of the C++ function had been updated. If you still see your function signature accepts a const char * argument, please click the reload button to reset your code definition.

思路

这道题没什么思路,但是要主要一定要在溢出之前解决溢出!不同的编译器对溢出的处理方法不一样的,所以得到的溢出结果也是不同的,比如int的正整数溢出就是负整数什么的就肯定不对。我的想法是用unsigned int把可能溢出的情况兜住判断,可能溢出的情况无非两个,乘以10的时候,加一个个位数的时候。

代码

class Solution {
public:
    bool willOverflow(unsigned int currNum, char i, bool negFlag){
        const unsigned int M_INTM = INT_MAX / 10;
        if(currNum > M_INTM){
            return true;
        } else if(currNum == M_INTM){
            if(!negFlag){
                if(i - '0' > 7) {
                    return true;
                }
            } else{
                if(i - '0' > 8){
                    return true;
                }
            }
        }
        return false;
    }
    int myAtoi(string str) {
        unsigned int retVal = 0;
        bool negFlag = false;
        bool overflow = false;
        if(str.size() < 1)
            return 0;
        int i=0;
        while(str[i] == ' '){
            i++;
        }
        if(str[i] == '+')
            i++;
        else if(str[i] == '-'){
            i++;
            negFlag = true;
        }
        int digCount = 0;
        while(i < str.size() && str[i] >= '0' && str[i] <= '9'){
            if(digCount < 9 || !willOverflow(retVal, str[i], negFlag)){
                retVal = retVal * 10 + str[i] - '0';
            } else {
                //overflow!
                overflow = true;
                break;
            }
            i++;
            digCount++;
        }
        if(!overflow){
            if(negFlag)
                return -retVal;
            else
                return retVal;
        } else {
            return negFlag? INT_MIN: INT_MAX;
        }
    }
};

 

Categories
不学无术

LeetCode 236. Lowest Common Ancestor of a Binary Tree

题目

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.

思路1

找两个节点的公共最小祖先,可以利用递归的思想,即假设我们有个函数,能判断root为根节点的子树里有几个符合的节点(0,1或2个),在递归跳出的过程中,一旦我们发现有返回2个节点的,那这个根元素就肯定是p和q的最低公共节点了。

思路2

假设要找的节点是5和1,那么从根节点通往5的路径是3->5,通往1的路径是3->1。可以用递归方法构造一个路径栈,比较容易地找到路径,然后就变成两条路径的最长公共节点的问题了,“分岔”的地方就是LCA.
相比思路1,这个方法实施起来简单一些,但是要遍历两遍树,会稍微慢一点。

代码1

* struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    int LCASub(TreeNode* root, TreeNode* p, TreeNode* q, TreeNode*& retNode){
        //If we find p or q, return 1
        //If we find root contains p and q, return 2 and set the retNode to common node
        //Else return 0
        if(root == NULL)
            return 0;
        int leftRet = LCASub(root->left, p, q, retNode);
        if(leftRet == 2)
            return 2;
        int rightRet = LCASub(root->right, p, q, retNode);
        if(rightRet == 2){
            return 2;
        }
        if(leftRet + rightRet == 2){
            retNode = root;
            return 2;
        } else if(root == p || root == q){
            if(leftRet==1 || rightRet == 1){
                retNode = root;
                return 2;
            } else {
                return 1;
            }
        } else {
            return leftRet + rightRet;
        }
    }
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        TreeNode* retVal = NULL;
        LCASub(root, p, q, retVal);
        return retVal;
    }
};

代码2

class Solution {
public:
    bool getPath(TreeNode* root, TreeNode* p, stack<TreeNode*>& path){
        //Get path from root to p
        //If not available, return false
        if(root == NULL || root == p){
            path.push(root);
            return true;
        }
        bool foundPath = false;
        if(root->left != NULL){
            //try left
            foundPath = getPath(root->left, p, path);
            if(foundPath){
                //Mission complete, push current node and return
                path.push(root);
                return true;
            }
        }
        if(root->right != NULL){
            foundPath = getPath(root->right, p, path);
            if(foundPath){
                path.push(root);
                return true;
            }
        }
        return false;
    }
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        stack<TreeNode*> pPath;
        stack<TreeNode*> qPath;
        getPath(root, p, pPath);
        getPath(root, q, qPath);
        TreeNode* result = NULL;
        while(!pPath.empty() && !qPath.empty()){
            if(pPath.top() != qPath.top())
                break;
            result = pPath.top();
            pPath.pop();
            qPath.pop();
        }
        return result;
    }
};

 

Categories
不学无术

LeetCode 319. Bulb Switcher 智商碾压题

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 ith round, you toggle every i bulb. 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.

我的超时解

递推法,当从n-1到n时,新加上去的一次操作是判断从[1,n]里面n的因子个数,这里记作f(n)好了,即a[n] = a[n-1] + f(n)
因子个数的求法可以精简一下时间,排除0,1,2这三个特殊值后,计算[2, sqrt(n)]里头能整除的数的个数就行了,如果碰到个平方数那个数+1,不然个数就是+2的(i能被n整除,那么它的补数n/i也行)。
这个方法的复杂度嘛,O(n^1.5)吧

class Solution {
public:
    int factors(int n){
        if(n == 0){
            return 0;
        } else if(n == 1){
            return 1;
        } else if(n == 2){
            return 2;
        }
        int count = 2;  //1和自己
        int sqr = (int)(sqrt(n));
        for(int i=2; i <= sqr; i++){
            if(n % i == 0){
                if(i *i == n)
                    count += 1; //平方数
                else
                    count += 2; //一个数和它的补数
            }
        }
        return count;
    }
    int bulbSwitch(int n) {
        if(n < 1){
            return 0;
        }
        int bCount = 0;
        for(int i=1; i<=n; i++){
            if((factors(i)&1) == 1){
                bCount ++;
            }
        }
        return bCount;
    }
};

智商碾压,O(1)解

真正的奥义用一行代码就能解释

class Solution {
public:
    int bulbSwitch(int n) {
        return sqrt(n);
    }
};

呵呵哒!这个规律如果多试几次说不定能发现,0,1,1,1,2,2,2,2,2,3这种数列嘛
https://leetcode.com/discuss/91371/share-my-o-1-solution-with-explanation
大概意思是,要在[1,n]里头找哪些灯泡被执行了奇数次操作

  •  对于一个非平方数来说(比如12),因为有成对的补数(1vs12; 2vs6),所以总是会按下2的倍数次
  • 但是对于一个平方数来说(比如36),因为其中有个数(6)它的补数是自己,所以会按被下奇数次

然后这道题就变成了找[1,n]中间有几个平方数了
OMG…

Categories
不学无术

LeetCode 322. Coin Change

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.

思路

动态规划的问题,不过如果倒着做的话,似乎会超时…
所以考虑往前递推的情况,即当前要解决X元的找零问题的最佳解法且已知<X时的解法,手头有一堆面值为coins[i]的硬币的时候,对于每种可用的硬币我们要做的选择是:

  • 尝试使用coin[i],即当前的最优结果=(X-coins[i])元时的最优结果+1,这个1就是用了coins[i]这个硬币;
  • 保持现有结果不变

这两个方法当然是取最好的存下来了。

代码

class Solution {
public:
    int MAX_NUM = 65535;
    int coinChange(vector<int>& coins, int amount) {
        vector<int> solutions(amount+1, MAX_NUM); //0~amount,共amount+1个
        solutions[0] = 0;
        int coinSz = coins.size();
        for(int i=1; i<=amount; i++){
            for(int c=0; c<coinSz; c++){
                //得到当前的amount,要么就是用原有的结果,要么就是使用amount[i-coin] + 1个当前面值的
                if(coins[c] <= i){
                    solutions[i] = min(solutions[i], 1 + solutions[i-coins[c]]);
                }
            }
        }
        if(solutions[amount] == MAX_NUM)
            return -1;
        return solutions[amount];
    }
};