Tag Archives: 数学

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 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 Find All Duplicates in an Array

LeetCode Find All Duplicates in an Array Given an array of integers, 1 ≤ a[i] ≤ n (n = size of array), some elements appear twice and others appear once. Find all the elements that appear twice in this array. Could you do it without extra space and in O(n) runtime? Example:

Input:
[4,3,2,7,8,2,3,1]
Output:
[2,3]

长度为n的数组中存储了1~n的若干个数,有的出现了两次,有的只出现了一次,所以还会有一些没有出现。现在要找出所有出现两次的数字。因为不能使用额外的空间,时间复杂度也只能O(n),所以方法和LeetCode Find All Numbers Disappeared in an Array几乎一样。 第一种方法是取相反数,如果发现nums[idx]已经是负数了,说明nums[i]在之前出现过了。完整代码如下: [cpp] class Solution { public: vector<int> findDuplicates(vector<int>& nums) { vector<int> ans; for (int i = 0; i < nums.size(); ++i) { int idx = abs(nums[i]) – 1; if (nums[idx] > 0) { nums[idx] = -nums[idx]; } else { ans.push_back(abs(nums[i])); } } return ans; } }; [/cpp] 本代码提交AC,用时188MS。 第二种方法是把每个数放到它正确的位置,第二遍扫描的时候,如果发现数字和下标对不上,则存储的数字就是出现两次的数字,对应的下标+1就是没出现过的数字(LeetCode Find All Numbers Disappeared in an Array解法2)。完整代码如下: [cpp] class Solution { public: vector<int> findDuplicates(vector<int>& nums) { vector<int> ans; for (int i = 0; i < nums.size(); ++i) { if (nums[nums[i] – 1] != nums[i]) { swap(nums[nums[i] – 1], nums[i]); –i; } } for (int i = 0; i < nums.size(); ++i) { if (nums[i] != i + 1) { ans.push_back(nums[i]); } } return ans; } }; [/cpp] 本代码提交AC,用时129MS。]]>

LeetCode Construct the Rectangle

LeetCode Construct the Rectangle For a web developer, it is very important to know how to design a web page’s size. So, given a specific rectangular web page’s area, your job by now is to design a rectangular web page, whose length L and width W satisfy the following requirements:

1. The area of the rectangular web page you designed must equal to the given target area.
2. The width W should not be larger than the length L, which means L >= W.
3. The difference between length L and width W should be as small as possible.
You need to output the length L and the width W of the web page you designed in sequence. Example:
Input: 4
Output: [2, 2]
Explanation: The target area is 4, and all the possible ways to construct it are [1,4], [2,2], [4,1].
But according to requirement 2, [1,4] is illegal; according to requirement 3,  [4,1] is not optimal compared to [2,2]. So the length L is 2, and the width W is 2.
Note:
  1. The given area won’t exceed 10,000,000 and is a positive integer
  2. The web page’s width and length you designed must be positive integers.

给定一个面积area,要求出一个长L和宽W,使得L*W=area,并且L>=W,并且L和W的差越小越好。 简单题,先对area开根号,然后L不断++,直到L*W=area为止。 唯一需要注意的是,必须L=ceil(sqrt(area)),因为如果area=2,不加ceil的话,L=1,W=2了。 完整代码如下: [cpp] class Solution { public: vector<int> constructRectangle(int area) { int L = ceil(sqrt(area)); while (area%L != 0) { ++L; } return vector<int>({ L,area / L }); } }; [/cpp] 本代码提交AC,用时89MS。不开心,为啥这么慢呢。。。]]>

LeetCode Arithmetic Slices

LeetCode Arithmetic Slices A sequence of number is called arithmetic if it consists of at least three elements and if the difference between any two consecutive elements is the same. For example, these are arithmetic sequence:

1, 3, 5, 7, 9
7, 7, 7, 7
3, -1, -5, -9
The following sequence is not arithmetic.
1, 1, 2, 5, 7
  A zero-indexed array A consisting of N numbers is given. A slice of that array is any pair of integers (P, Q) such that 0 <= P < Q < N. A slice (P, Q) of array A is called arithmetic if the sequence: A[P], A[p + 1], …, A[Q – 1], A[Q] is arithmetic. In particular, this means that P + 1 < Q. The function should return the number of arithmetic slices in the array A.   Example:
A = [1, 2, 3, 4]
return: 3, for 3 arithmetic slices in A: [1, 2, 3], [2, 3, 4] and [1, 2, 3, 4] itself.

本题要求一个数组中等差数列的个数,并不要求是最长的等差数列。比如给的例子中数组A=[1,2,3,4],可以抽出3个等差数列,分别是[1,2,3]、[2,3,4]、[1,2,3,4]。 首先还是找特征,我们可以在数组中先找出最长的等差数列,然后在等差数列的基础上抽取(Slice)出比较小的等差数列,所以先研究一下长度为n的等差数列可以抽出多少个小的等差数列。
  • n=3,抽出1个
  • n=4,抽出3=1+2个,即1个长度为4的和2个长度为3的
  • n=5,抽出6=1+2+3个,即1个长度为5的、2个长度为4的和3个长度为3的
  • n=6,抽出10=1+2+3+4个,即…
由此可以得出规律,长度为n的等差数列,可以抽出1+2+…+(n-2)=(n-1)(n-2)/2个小的等差数列。 于是问题就转换为找出数组中所有最长连续等差数列,然后代入上述公式计算。 完整代码如下: [cpp] class Solution { public: inline int slice(int cnt) { if (cnt < 3)return 0; if (cnt == 3)return 1; return (cnt – 1)*(cnt – 2) / 2; // 1+2+…+(cnt-2) } int numberOfArithmeticSlices(vector<int>& A) { if (A.size() < 3)return 0; int ans = 0, cnt = 2, diff = A[1] – A[0]; for (int i = 2; i < A.size(); ++i) { if (A[i] – A[i – 1] == diff) { ++cnt; } else { ans += slice(cnt); cnt = 2; diff = A[i] – A[i – 1]; } } return ans + slice(cnt); } }; [/cpp] 本代码提交AC,用时3MS。 网上还有一种DP解法,我们看看上面的规律,当n从4增加到5时,抽取出来的小等差数列增加了3个,是在n=4时,最后+2的基础上+1=3;当n从5增加到6时,抽取出来的小等差数列增加了4个,是在n=5时,最后+3的基础上+1=4。 所以我们设置一个dp数组,dp[i]表示:如果第i个元素和前面构成等差数列了,则能贡献多少个小等差数列,根据前面的分析,dp[i]=dp[i-1]+1,这里的dp相当于比如n=6时的10=1+2+3+4(等号右边的数组);如果第i个元素不和前面构成等差数列,则dp[i]=0不贡献小等差数列。 这种解法的代码如下: [cpp] class Solution { public: int numberOfArithmeticSlices(vector<int>& A) { if (A.size() < 3)return 0; int ans = 0; vector<int> dp(A.size(), 0); for (int i = 2; i < A.size(); ++i) { if (A[i] – A[i – 1] == A[i – 1] – A[i – 2]) { dp[i] = dp[i – 1] + 1; } ans += dp[i]; } return ans; } }; [/cpp] 本代码提交AC,用时3MS。 DP一般都可以优化空间,上面的解法也可以不用一个dp数组,而只用一个变量。如下: [cpp] class Solution { public: int numberOfArithmeticSlices(vector<int>& A) { if (A.size() < 3)return 0; int ans = 0, cur = 0; for (int i = 2; i < A.size(); ++i) { if (A[i] – A[i – 1] == A[i – 1] – A[i – 2]) { ++cur; } else { cur = 0; } ans += cur; } return ans; } }; [/cpp] 本代码提交AC,用时也是3MS。]]>

LeetCode Find the Duplicate Number

287. Find the Duplicate Number 287. Find the Duplicate Number

Given an array nums containing n + 1 integers where each integer is between 1 and n (inclusive), prove that at least one duplicate number must exist. Assume that there is only one duplicate number, find the duplicate one.

Example 1:

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

Example 2:

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

Note:

  1. You must not modify the array (assume the array is read only).
  2. You must use only constant, O(1) extra space.
  3. Your runtime complexity should be less than O(n2).
  4. There is only one duplicate number in the array, but it could be repeated more than once. 287. Find the Duplicate Number

一个长度为n+1的数组,包含1~n的数字,证明至少有一个数字重复,并找出这个重复数字。 证明好说,鸽巢原理。要求在线性时间复杂度和常数空间复杂度把这个重复的数字找出来就有难度了,而且不能修改原数组,难以用上LeetCode Find All Numbers Disappeared in an Array的技巧。 这题确实hard,看了看提示,以及网上的题解,现总结如下。 第一种解法是二分搜索。根据鸽巢原理,如果有n+1个[1,n]的数,则必有一个数重复了,把区间分成[1,m]和[m+1,n],则重复的数字要么出现在[1,m],要么出现在[m+1,n],所以只需统计一下小于等于和大于m的数字的个数,数字多的那个区间肯定是存在重复数字的区间。然后在那个区间递归的找下去,直到找到重复数字。
完整代码如下:

class Solution {
public:
    int findDuplicate(vector<int>& nums)
    {
        int low = 1, high = nums.size() – 1;
        while (low < high) {
            int mid = (low + high) / 2, low_cnt = 0;
            for (int i = 0; i < nums.size(); ++i) {
                if (nums[i] <= mid)
                    ++low_cnt;
            }
            if (low_cnt > mid) {
                high = mid;
            }
            else {
                low = mid + 1;
            }
        }
        return low;
    }
};

本代码提交AC,用时9MS。
还有一种解法和LeetCode Linked List Cycle II很类似,如果存在重复数字,则使用下标递归访问的时候,会出现循环访问。首先通过快慢指针找到循环相遇点,然后慢指针回到原点,快慢指针按相同速度前进,直到遇到函数值相同时停止,这个相同的函数值即为重复数字。一些证明可以参考这个博客
完整代码如下:

class Solution {
public:
    int findDuplicate(vector<int>& nums)
    {
        int slow = 0, fast = 0;
        while (true) {
            slow = nums[slow];
            fast = nums[nums[fast]];
            if (slow == fast)
                break;
        }
        slow = 0;
        while (true) {
            slow = nums[slow];
            fast = nums[fast];
            if (slow == fast)
                return slow;
        }
    }
};

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

二刷。对二分搜索的解读。假设数组没有重复数字,比如[1,2,3,4,5,6,7],则对于任意一个数x,<=x的数的个数和x是相等的,比如<=1的数有1个,<=2的数有2个etc…如果出现重复数字,假设比中点小,则<=m的数的个数要大于m。所以代码中每次比较是low_cnt和m比较。

l、r、while(l<=r)都是标准的二分搜索框架。最后返回r+1,想象一直low_cnt>m,知道不满足while循环,则最后一个满足的就是r+1。

class Solution {
public:
	int findDuplicate(vector<int>& nums) {
		int n = nums.size();
		int l = 1, r = n - 1;
		while (l <= r) {
			int m = l + (r - l) / 2;
			int low_cnt = 0;
			for (int i = 0; i < n; ++i) {
				if (nums[i] <= m)++low_cnt;
			}
			if (low_cnt > m)r = m - 1;
			else l = m + 1;
		}
		return r + 1;
	}
};

LeetCode Find All Numbers Disappeared in an Array

LeetCode Find All Numbers Disappeared in an Array Given an array of integers where 1 ≤ a[i] ≤ n (n = size of array), some elements appear twice and others appear once. Find all the elements of [1, n] inclusive that do not appear in this array. Could you do it without extra space and in O(n) runtime? You may assume the returned list does not count as extra space. Example:

Input:
[4,3,2,7,8,2,3,1]
Output:
[5,6]

一个长度为n的数组,保存了1~n中的若干个数,有的出现了两次,有的出现了一次,有的数没有出现,现在要找出那些没有出现的数。要求线性时间复杂度和常数空间复杂度。 这一题有点意思,要求不使用额外的空间,那么唯一的办法就是借助原数组进行操作了。我们可以把数组中的数字都放到它们正确的位置上,然后扫描一遍数组,如果某个位置存储的数字和下标对不上,则这个位置存储的是出现两次的数字,同时说明下标对应那个数字没有出现在原数组中。 这种解法的代码如下: [cpp] class Solution { public: vector<int> findDisappearedNumbers(vector<int>& nums) { vector<int> ans; for (int i = 0; i < nums.size(); ++i) { if (nums[i] != nums[nums[i] – 1]) { swap(nums[i], nums[nums[i] – 1]); –i; } } for (int i = 0; i < nums.size(); ++i) { if (nums[i] – 1 != i) { ans.push_back(i + 1); } } return ans; } }; [/cpp] 本代码提交AC,用时159MS。 还有一种解法是,对于每一个数字nums[i],如果它对应的nums[nums[i]-1]是正数,则把nums[nums[i]-1]赋值为自身的相反数;否则不变。最后重新扫描一遍数组,如果是负数,则说明对应数字存在,否则下标对应数字不存在。这个不难理解,比如题目中的例子,nums[0]=4,则把nums[nums[0]-1]赋值为-nums[nums[0]-1]=-7,这样第二遍扫描nums数组时,发现下标为3的数字是负数,则说明数字4存在于原始数组中。 这种解法的代码如下: [cpp] class Solution { public: vector<int> findDisappearedNumbers(vector<int>& nums) { vector<int> ans; for (int i = 0; i < nums.size(); ++i) { int idx = abs(nums[i]) – 1; nums[idx] = nums[idx]>0 ? (-nums[idx]) : nums[idx]; } for (int i = 0; i < nums.size(); ++i) { if (nums[i] > 0)ans.push_back(i + 1); } return ans; } }; [/cpp] 本代码提交AC,用时142MS。]]>

LeetCode Island Perimeter

LeetCode Island Perimeter You are given a map in form of a two-dimensional integer grid where 1 represents land and 0 represents water. Grid cells are connected horizontally/vertically (not diagonally). The grid is completely surrounded by water, and there is exactly one island (i.e., one or more connected land cells). The island doesn’t have “lakes” (water inside that isn’t connected to the water around the island). One cell is a square with side length 1. The grid is rectangular, width and height don’t exceed 100. Determine the perimeter of the island. Example:

[[0,1,0,0],
 [1,1,1,0],
 [0,1,0,0],
 [1,1,0,0]]
Answer: 16
Explanation: The perimeter is the 16 yellow stripes in the image below:

在一个海上有一个岛,要求这个岛的周长。简单题,只要发现一个land cell,看看它的四周有没有海,比如上下左右,有一边有海,则总的周长加1,如果四周也都是land cell,则这个land cell不贡献周长。 完整代码如下: [cpp] class Solution { public: int islandPerimeter(vector<vector<int>>& grid) { int ans = 0; for (int i = 0; i < grid.size(); ++i) { for (int j = 0; j < grid[i].size(); ++j) { if (grid[i][j] == 1) { if (i == 0 || grid[i – 1][j] == 0)ans++; // top if (i + 1 == grid.size() || grid[i + 1][j] == 0)ans++; //bottom if (j == 0 || grid[i][j – 1] == 0)ans++; // left if (j + 1 == grid[i].size() || grid[i][j + 1] == 0)ans++; //right } } } return ans; } }; [/cpp] 本代码提交AC,用时212MS。居然有五千多个测试样例。。。]]>

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 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。]]>