Monthly Archives: January 2017

LeetCode Total Hamming Distance

LeetCode Total Hamming Distance 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:
  1. Elements of the given array are in the range of 0 to 10^9
  2. Length of the array will not exceed 10^4.

本题要求数组中所有数两两之间的汉明距离之和,最笨的方法就是两层循环,然后调用LeetCode Hamming Distance的方法计算每两个数之间的汉明距离。 网上还有一种更巧妙的方法,仔细观察下面三个数的二进制表示,我们可以位的角度计算所有汉明距离,而不是每次针对两个完整的数。比如最低位,三个数都是0,则任意两个数之间不会贡献汉明距离;次低位有一个为0,两个为1,要产生汉明距离,必须一个为0,一个为1,也就是(4,14)和(4,2)才能产生次低位的汉明距离,而(14,2)因为都是1没有汉明距离。所以我们可以统计每一位中0和1的个数zero[i]和one[i],在第i位上所有数的两两汉明距离之和为zero[i]*one[i]。所以总的汉明距离就是ans=Σ zero[i]*one[i]。
  • 4:  0100
  • 14:1110
  • 2:  0010
完整代码如下: [cpp] class Solution { public: int totalHammingDistance(vector<int>& nums) { int bit_len = sizeof(int) * 8; vector<int> one(bit_len, 0), zero(bit_len, 0); for (int i = 0; i < bit_len; ++i) { for (int j = 0; j < nums.size(); ++j) { if (nums[j] & 1)++one[i]; else ++zero[i]; nums[j] >>= 1; } } int ans = 0; for (int i = 0; i < bit_len; ++i) { ans += one[i] * zero[i]; } return ans; } }; [/cpp] 本代码提交AC,用时112MS。]]>

LeetCode Sum of Left Leaves

LeetCode Sum of Left Leaves 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.

本题要求二叉树中所有左叶子节点之和。简单题,直接递归,递归的时候记录或判断当前节点是父亲节点的左孩子还是右孩子。完整代码如下: [cpp] class Solution { public: int work(TreeNode *cur, TreeNode *par) { if (cur->left == NULL&&cur->right == NULL&&par->left == cur)return cur->val; int ans = 0; if (cur->left)ans += work(cur->left, cur); if (cur->right)ans += work(cur->right, cur); return ans; } int sumOfLeftLeaves(TreeNode* root) { if (root == NULL)return 0; int sum = 0; if (root->left)sum += work(root->left, root); if (root->right)sum += work(root->right, root); return sum; } }; [/cpp] 本代码提交AC,用时6MS。]]>

LeetCode Intersection of Two Arrays

LeetCode Intersection of Two Arrays 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到unordered_set里面,然后另一个数组去这个unordered_set找等。本题采用第二个思路,为了保证结果的互异性,使用了两个unordered_set,分别针对第一个集合和结果集合。完整代码如下: [cpp] class Solution { public: vector<int> intersection(vector<int>& nums1, vector<int>& nums2) { vector<int> ans; unordered_set<int> s1, s_ans; for (int i = 0; i < nums1.size(); ++i) { s1.insert(nums1[i]); } for (int i = 0; i < nums2.size(); ++i) { if (s1.find(nums2[i]) != s1.end()) { s_ans.insert(nums2[i]); } } unordered_set<int>::iterator it = s_ans.begin(); while (it != s_ans.end()) { ans.push_back(*it); ++it; } return ans; } }; [/cpp] 本代码提交AC,用时12MS。]]>

LeetCode Top K Frequent Elements

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

本题要找出数组中出现频率最高的前k个数。要求时间复杂度低于O(nlgn),所以直接对原始数组排序是不行的。 第一直觉是用hash,找到数字和频率的对应关系,然后对频率排序,取频率最高的前k个数。对频率的排序可以用最大堆,就是建立以频率为key的堆,然后不断从堆顶取数字,直到取够k个为止。 STL中的优先队列priority_queue就是一个最大堆;对pair进行排序时,优先对pair中的first排序,然后是second。 完整代码如下: [cpp] class Solution { public: vector<int> topKFrequent(vector<int>& nums, int k) { unordered_map<int, int> hash; for (int i = 0; i < nums.size(); ++i) { ++hash[nums[i]]; } priority_queue<pair<int, int>> max_heap; for (unordered_map<int, int>::iterator it = hash.begin(); it != hash.end(); ++it) { max_heap.push({ it->second,it->first }); } vector<int> ans; for (int i = 0; i < k; i++) { ans.push_back(max_heap.top().second); max_heap.pop(); } return ans; } }; [/cpp] 本代码提交AC,用时59MS。 找到数字和频率的对应关系之后,还可以用桶排序找频率最高的前k个数字,其实相当于对频率再次hash。因为长度为n的数组中不同的频率最多有n个(应该到不了n个),至少是1个(即所有数都是一样的,则只有一个频数n),所以我们开一个长度为n的二维数组,一级下标对应频率,然后把相同频率的数扔到以频数为下标的桶里,最后从后到前遍历桶,也就是先遍历频数大的数,直到取到k个数位置。 完整代码如下: [cpp] class Solution { public: vector<int> topKFrequent(vector<int>& nums, int k) { unordered_map<int, int> hash; for (int i = 0; i < nums.size(); ++i) { ++hash[nums[i]]; } vector<vector<int>> bucket(nums.size() + 1); for (unordered_map<int, int>::iterator it = hash.begin(); it != hash.end(); ++it) { bucket[it->second].push_back(it->first); } vector<int> ans; for (int i = bucket.size() – 1; k > 0 && i > 0; –i) { if (bucket[i].size() > 0) { for (int j = 0; j < bucket[i].size(); ++j) { ans.push_back(bucket[i][j]); –k; } } } return ans; } }; [/cpp] 本代码提交AC,用时29MS,果然比之前的快很多。]]>

LeetCode Add Two Numbers II

LeetCode Add Two Numbers II You are given two non-empty linked lists representing two non-negative integers. 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

有两个数字链表,分别存储了两个整数,要求把这两个整数加起来。和LeetCode Add Two Numbers的区别是,之前的链表最低位在表头,我们正常加法也是从低位到高位,所以之前的题可以直接从表头开始加。但是这个题稍有不同,最低位在表尾,表头是最高位,所以必须先跑到链表尾才能做加法。一种方法是我们可以先把链表逆序,这样就可以用之前的解法了,但是题目中说不能修改原链表,所以不能用这种解法。 那么怎样获取到表尾的数据进行加法呢,我们可以把两个链表压入两个堆栈,因为堆栈是后进先出,所以再次从栈顶取数据做加法的时候就是从低位到高位的加了。 完整代码如下: [cpp] class Solution { public: ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) { stack<int> s1, s2; while (l1) { s1.push(l1->val); l1 = l1->next; } while (l2) { s2.push(l2->val); l2 = l2->next; } ListNode* ans = new ListNode(1); // 预设好的最高位进位:-) int sum = 0; while (!s1.empty() || !s2.empty()) { if (!s1.empty()) { sum += s1.top(); s1.pop(); } if (!s2.empty()) { sum += s2.top(); s2.pop(); } ListNode* tmp = new ListNode(sum%10); tmp->next = ans->next; ans->next = tmp; sum /= 10; // 下一次的进位 } if (sum)return ans; else return ans->next; } }; [/cpp] 本代码提交AC,用时55MS。 代码中有两个地方解释一下,一般链表的题都会在结果链表中加一个没用的头结点,一般是0,但是因为最终的加法结果可能进位,所以把表头的值定为1,如果有进位,这个1也可以用上,如果没进位,就返回1的next。另一个就是我们没有单独定义add1、add2和carry,而是只用一个sum变量搞定了,sum%10相当于进位之后的结果,sum/10相当于进位。]]>

LeetCode Assign Cookies

LeetCode Assign Cookies 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.

妈妈要给若干个孩子分饼干,没人一个饼干,不同饼干有不同的大小,同时不同孩子对饼干有一个最小的要求。问怎样分饼干才能满足最多的孩子的要求,求最多满足的孩子的数目。 明显是贪心的思路,把饼干从大到小排序,孩子的要求也从大到小排序,然后我们先尽量用大的饼干满足要求高的孩子,比如你会用一个100大小的饼干满足一个要求为90的孩子,而不是拿这个饼干满足要求为10的孩子,因为要求越低的孩子,被满足的可能性越高。如果当前最大的饼干都不能满足当前的孩子,则当前的策略满足不了这个孩子。 完整代码如下: [cpp] class Solution { public: int findContentChildren(vector<int>& g, vector<int>& s) { sort(g.begin(), g.end()); sort(s.begin(), s.end()); int i = g.size() – 1, j = s.size() – 1, ans = 0; while (i >= 0) { if (j >= 0 && s[j] >= g[i]) { ++ans; –j; } –i; } return ans; } }; [/cpp] 本代码提交AC,用时582MS。为什么会这么慢呀,后来试了下从小到大给孩子分饼干,也就是用能满足当前孩子的最小饼干,其实原理是一样的,尽量不浪费大的饼干: [cpp] class Solution { public: int findContentChildren(vector<int>& g, vector<int>& s) { sort(g.begin(), g.end()); sort(s.begin(), s.end()); int i = 0, j = 0, ans = 0; while (i < g.size()) { while (j < s.size() && g[i] > s[j]) { ++j; } if (j >= s.size())break; ++ans; ++i; ++j; } return ans; } }; [/cpp] AC了,但是还是很慢,449MS。。。]]>

LeetCode Magical String

LeetCode Magical String 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 Sitself. 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.

这道题给了一个magic字符串,具体怎么magic题目说得很清楚了。这个字符串只包含字符1和2,依次计算字符串中连续的1或2出现的次数,把这些数字串起来,构成的一个新的字符串和原字符串是完全一样的!然后问字符串中前n个字符出现1的次数。 这是一个模拟题,我们根据magic的规则模拟生成这个字符串,然后统计一下前n个字符中1出现的次数。 模拟的方法就是把字符串中的数当做原字符串中1或2出现的次数,然后不断在原字符串中追加新的字符。比如开始是122:1表示1出现1次;第一个2表示出现两次,因为前面有1出现1次的约束,所以出现两次的只可能是2;第二个2也表示出现两次,因为前面已经出现了两个2了,所以这里不能再接两个2了(要不然就出现4个2了),只能表示出现了两次1,所以我们可以在原字符串中追加两个1,变为12211。如此不断的模拟下去,直到字符串长度满足要求。 完整代码如下: [cpp] class Solution { public: int magicalString(int n) { string magic = "122"; int pos = 2; while (magic.size() < n) { switch (magic[magic.size()-1]) { case ‘1’: magic += string(magic[pos] – ‘0’, ‘2’); break; case ‘2’: magic += string(magic[pos] – ‘0’, ‘1’); break; } ++pos; } int ans = 0; for (int i = 0; i < n; ++i) { ans += magic[i] == ‘1’ ? 1 : 0; } return ans; } }; [/cpp] 本代码提交AC,用时9MS。]]>

LeetCode Two Sum II – Input array is sorted

167. Two Sum II – Input array is sorted

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.

Note:

  • Your returned answers (both index1 and index2) are not zero-based.
  • You may assume that each input would have exactly one solution and you may not use the same element twice.

Example:

Input: numbers = [2,7,11,15], target = 9
Output: [1,2]
Explanation: The sum of 2 and 7 is 9. Therefore index1 = 1, index2 = 2.

本题要在一个已排序数组中找两个数,使得这两个数的和等于一个目标target。这应该是LeetCode Two Sum的简化版吧,因为之前那个题有一种解法就是先对数组排序,然后首尾指针夹逼,现在这个题都排好序了,那还有什么好说的呢,直接借用之前的方法。 完整代码如下:

class Solution {
public:
    vector<int> twoSum(vector<int>& numbers, int target)
    {
        int i = 0, j = numbers.size() – 1;
        while (numbers[i] + numbers[j] != target) {
            if (numbers[i] + numbers[j] > target)
                –j;
            else
                ++i;
        }
        return vector<int>({ i + 1, j + 1 });
    }
};

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

LeetCode Single Number III

260. Single Number III 260. Single Number III

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.

Example:

Input:  [1,2,1,3,2,5]
Output: [3,5]

Note:

  1. The order of the result is not important. So in the above example, [5, 3] is also correct.
  2. Your algorithm should run in linear runtime complexity. Could you implement it using only constant space complexity? 260. Single Number III

本题是LeetCode Single Number的升级版,即数组中有两个只出现一次的数,其他的数都出现了两次,现在要把这两个只出现一次的数找出来。
直接用LeetCode Single Number异或的方法是不行的,因为虽然出现两次的数异或等于0了,但是有两个只出现一次的数a和b,得到的结果是它们的异或a^b。
如果有办法把数组中的数分成两堆,a和b在不同的堆里,并且每堆除了a或b,其他数也都恰好出现了两次,即如果3出现两次,则两个3要么都在a堆中,要么都在b堆中。这样我们就可以对两个堆分别异或,找到每堆中只出现一次的数a和b。
那么根据a^b的结果能否把数分成两堆呢,答案是可以的。异或的规则是相同为0,不同为1。既然a和b分别只出现了1次,那么a和b肯定是不同的两个数,所以他们的异或结果的二进制中肯定有一位是1,记这一位为x,那么a和b在x位的二进制肯定是不一样的,这就找到了a和b的区别。
所以我们可以把数组中的所有数按x位为0或1分成两堆,这样就达到了开头的目的,然后利用LeetCode Single Number的方法,在两堆进行异或,最终就能找到a和b了。完整代码如下:

class Solution {
public:
    vector<int> singleNumber(vector<int>& nums)
    {
        int flag = 0;
        for (int i = 0; i < nums.size(); ++i) {
            flag ^= nums[i];
        }
        int mask = 1;
        for (int i = 0; i < 32; ++i) {
            if ((flag & mask) != 0)
                break;
            mask <<= 1;
        }
        vector<int> ans(2, 0);
        for (int i = 0; i < nums.size(); ++i) {
            if (nums[i] & mask) {
                ans[0] ^= nums[i];
            }
            else {
                ans[1] ^= nums[i];
            }
        }
        return ans;
    }
};

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

二刷。相同思路,很简洁的代码,对于n,直接n&(-n)就能得到其最后一位1的位置,请看面试笔试宝典第三版P97,代码如下:

class Solution {
public:
	vector<int> singleNumber(vector<int>& nums) {
		int c = 0, n = nums.size();
		for (int i = 0; i < n; ++i) {
			c ^= nums[i];
		}
		c = c & (-c); // 得到c最后一个1
		int a = 0, b = 0;
		for (int i = 0; i < n; ++i) {
			if ((nums[i] & c) == 0) { // 注意加括号,位运算优先级低于==
				a ^= nums[i];
			}
			else {
				b ^= nums[i];
			}
		}
		return { a,b };
	}
};

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

LeetCode Single Number II

137. Single Number II

Given a non-empty array of integers, every element appears three times except for one, which appears exactly once. Find that single one.

Note:

Your algorithm should have a linear runtime complexity. Could you implement it without using extra memory?

Example 1:

Input: [2,2,3,2]
Output: 3

Example 2:

Input: [0,1,0,1,0,1,99]
Output: 99

数组中有出现3次的数,但是也有一个出现1次的数,现在要把这个只出现1次的数找出来。要求是线性时间复杂度和常数空间复杂度。 相比于LeetCode Single Number有难度。网上看到一个很不错的题解,分析得很详细。 初级解法是,把数组中每个数字的对应二进制位加起来,因为3个相同的数对应二进制位加起来之后模3一定等于0,比如5的二进制为101,三个101加起来就是303,每一位模3就是000。所以如果混入了一个只出现一次的数,这样取模之后,剩余位为1的就是我们要找的数的二进制形式。 这种解法需要一个32位的数组,用来统计所有数每一位出现1的次数之和,所以空间复杂度是O(32)=O(1)。时间复杂度的话,相当于O(32n)=O(n),是线性的。完整代码如下:

class Solution {
public:
    int singleNumber(vector<int>& nums)
    {
        int bit_len = sizeof(int) * 8;
        vector<int> bit(bit_len, 0);
        for (int i = 0; i < bit_len; ++i) {
            for (int j = 0; j < nums.size(); ++j) {
                bit[i] += (nums[j] & 1);
                nums[j] >>= 1;
            }
        }
        int ans = 0;
        for (int i = 0; i < bit_len; ++i) {
            int r = bit[i] % 3;
            ans |= (r << i);
        }
        return ans;
    }
};

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

高级解法是,使用三个int的每一位表示所有数每一位出现1次数之和的情况,one对应位为1表示这一位出现了1次1,two对应位为1表示这一位出现了2次1,three对应位为1表示这一位出现了3次1。但是one,two,three对应位不可能都同时为1。

通过分析对应位的关系(请看原博客),发现one,two,three可以快速通过位运算计算得到。完整代码如下:

class Solution {
public:
    int singleNumber(vector<int>& nums)
    {
        int one = 0, two = 0, three = 0;
        for (int i = 0; i < nums.size(); ++i) {
            two |= (one & nums[i]);
            one ^= nums[i];
            three = one & two;
            one -= three;
            two -= three;
        }
        return one;
    }
};

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

初级解法还是好理解一些,会不会是一个通用的解法呢,比如数组中出现1次和出现4次(甚至k次)的数混在一起,都可以通过模k求解呢?