Monthly Archives: November 2016

LeetCode Triangle

120. Triangle

Given a triangle, find the minimum path sum from top to bottom. Each step you may move to adjacent numbers on the row below.

For example, given the following triangle

[
     [2],
    [3,4],
   [6,5,7],
  [4,1,8,3]
]

The minimum path sum from top to bottom is 11 (i.e., 2 + 3 + 5 + 1 = 11).

Note:

Bonus point if you are able to do this using only O(n) extra space, where n is the total number of rows in the triangle.


给一个数字三角形,求从顶到底的最小路径和,很常规的DP题,要求只用O(n)的空间,思路和LeetCode Pascal’s Triangle II有点类似,即从后往前算。 把题目中的三角形推到左边,成下面的样子:

[2],
[3,4],
[6,5,7],
[4,1,8,3]
可以看到除了首尾两个元素,其他元素的递推公式为:dp[col]=min(dp[col-1],dp[col])+triangle[row][col],首尾元素只能从其上面的一个元素下来,所以只有一条路。完整代码如下:

class Solution {
public:
    int minimumTotal(vector<vector<int> >& triangle)
    {
        vector<int> dp(triangle.size(), 0);
        dp[0] = triangle[0][0];
        for (int row = 1; row < triangle.size(); row++) {
            dp[row] = dp[row – 1] + triangle[row][row];
            for (int col = row – 1; col > 0; col–) {
                dp[col] = min(dp[col – 1], dp[col]) + triangle[row][col];
            }
            dp[0] = dp[0] + triangle[row][0];
        }
        return *min_element(dp.begin(), dp.end());
    }
};

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

LeetCode Pascal’s Triangle II

119. Pascal’s Triangle II

Given a non-negative index k where k ≤ 33, return the kth index row of the Pascal’s triangle.

Note that the row index starts from 0.


In Pascal’s triangle, each number is the sum of the two numbers directly above it.

Example:

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

Follow up:

Could you optimize your algorithm to use only O(k) extra space?


本题在LeetCode Pascal’s Triangle的基础上,要求只用O(k)空间复杂度,输出杨辉三角的第k行结果。
关键在于只能用O(k)的空间。我们要算第k行的结果,只要知道第k-1行结果就可以了,所以可以用O(k)的空间保留前一行的结果。每计算到一行就交换一下ans和tmp,完整代码如下:

class Solution {
public:
    vector<int> getRow(int rowIndex)
    {
        vector<int> ans(rowIndex + 1, 1), tmp(rowIndex + 1, 1);
        if (rowIndex == 0 || rowIndex == 1)
            return ans;
        for (int row = 2; row <= rowIndex; row++) {
            for (int col = 1; col < row; col++) {
                tmp[col] = ans[col – 1] + ans[col];
            }
            swap(ans, tmp);
        }
        return ans;
    }
};

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

其实这题不需要tmp数组也行,就是每行算的时候从后往前算,因为ans[col]只会用到ans[col]和ans[col-1],所以从后往前填数组,即使覆盖了之前的结果,也对前面的计算没有影响。
这种思路在DP解背包问题中很常见,完整代码如下:

class Solution {
public:
    vector<int> getRow(int rowIndex)
    {
        vector<int> ans(rowIndex + 1, 1);
        if (rowIndex == 0 || rowIndex == 1)
            return ans;
        for (int row = 2; row <= rowIndex; row++) {
            for (int col = row – 1; col > 0; col–) {
                ans[col] += ans[col – 1];
            }
        }
        return ans;
    }
};

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

LeetCode Pascal’s Triangle

118. Pascal’s Triangle

Given a non-negative integer numRows, generate the first numRows of Pascal’s triangle.


In Pascal’s triangle, each number is the sum of the two numbers directly above it.

Example:

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

本题要求生成前n行的Pascal’s Triangle,其实这东西就是中国的杨辉三角。下一行的第j个元素等于上一行的第j-1、j个元素之和。根据这个规律很容易写出代码,如下:

class Solution {
public:
    vector<vector<int> > generate(int numRows)
    {
        vector<vector<int> > ans;
        if (numRows == 0)
            return ans;
        ans.push_back({ 1 });
        for (int i = 2; i <= numRows; i++) {
            vector<int> cur = { 1 };
            for (int j = 1; j <= i – 2; j++) {
                cur.push_back(ans[i – 2][j – 1] + ans[i – 2][j]);
            }
            cur.push_back(1);
            ans.push_back(cur);
        }
        return ans;
    }
};

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

LeetCode Partition List

86. Partition List

Given a linked list and a value x, partition it such that all nodes less than x come before nodes greater than or equal to x.

You should preserve the original relative order of the nodes in each of the two partitions.

Example:

Input: head = 1->4->3->2->5->2, x = 3
Output: 1->2->2->4->3->5

本题要求把链表中小于某个数的节点移到大于等于某个数的节点前面,要求两个部分中的节点的相对顺序不变。 思路很简单,维护两个链表,一个是小于x的,另一个是大于等于x的,然后遍历原链表,分别把小于和大于等于x的节点接到对应的链表上。最后记得把第一个链表的尾部连到第二个链表的头部,然后第二个链表的尾节点置空。 完整代码如下:

class Solution {
public:
    ListNode* partition(ListNode* head, int x)
    {
        ListNode *head1 = new ListNode(0), *head2 = new ListNode(0);
        ListNode *prior1 = head1, *prior2 = head2;
        while (head != NULL) {
            if (head->val < x) {
                prior1->next = head;
                prior1 = prior1->next;
            }
            else {
                prior2->next = head;
                prior2 = prior2->next;
            }
            head = head->next;
        }
        prior2->next = NULL;
        prior1->next = head2->next;
        return head1->next;
    }
};

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

LeetCode Product of Array Except Self

238. Product of Array Except Self

Given an array nums of n integers where n > 1,  return an array output such that output[i] is equal to the product of all the elements of nums except nums[i].

Example:

Input:  [1,2,3,4]
Output: [24,12,8,6]

Constraint: It’s guaranteed that the product of the elements of any prefix or suffix of the array (including the whole array) fits in a 32 bit integer.

Note: Please solve it without division and in O(n).

Follow up:
Could you solve it with constant space complexity? (The output array does not count as extra space for the purpose of space complexity analysis.)


很有意思的一个题。给定一个数组,要求数组中除了当前元素的其他所有元素的乘积。比如数组[1,2,3,4],则3对应的值应该是1*2*4=8。
题目要求不能用除法,且要是常数时间空间复杂度。
如果能用除法就很简单了,先把数组完整的乘积算出来,然后依次除以每个元素。
网上看了题解,这题还是很巧妙的。我们可以扫描两遍数组,第一遍从左往右,计算不包括当前元素的之前所有元素的乘积;第二遍从右往左,计算不包括当前元素的之后所有元素的乘积;最后把两遍的结果乘起来就是要求的结果。比如样例
原始数组    1,  2, 3, 4
第一遍        1,  1, 2, 6
第二遍     24, 12, 4, 1
两遍乘积 24, 12, 8, 6
仔细想想很容易就知道其中的原理,对于第i个元素,最终结果应该是
$(x_0*x_1*…*x_{i-1})*(x_{i+1}*x_{i+2}*…*x_{n-1})$
第一遍相当于计算了前半部分,第二遍相当于计算了后半部分,最后两个部分乘起来就好了。
题目还有一个要求,就是常数空间复杂度,方法是:第二遍的时候,借助一个变量right_prod来存储从右到左的乘积,ans里面就是第二遍乘第二遍的结果,也就是最终结果。
完整代码如下:

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

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

LeetCode Delete Node in a Linked List

237. Delete Node in a Linked List

Write a function to delete a node (except the tail) in a singly linked list, given only access to that node.

Given linked list — head = [4,5,1,9], which looks like following:

Example 1:

Input: head = [4,5,1,9], node = 5
Output: [4,1,9]
Explanation: You are given the second node with value 5, the linked list should become 4 -> 1 -> 9 after calling your function.

Example 2:

Input: head = [4,5,1,9], node = 1
Output: [4,5,9]
Explanation: You are given the third node with value 1, the linked list should become 4 -> 5 -> 9 after calling your function.

Note:

  • The linked list will have at least two elements.
  • All of the nodes’ values will be unique.
  • The given node will not be the tail and it will always be a valid node of the linked list.
  • Do not return anything from your function.237. Delete Node in a Linked List

删掉单向链表中的某个指定的节点,这个节点不是尾节点。 本科面试的时候遇到过这个题,当时好像没做出来…因为要删掉某个节点,肯定要知道其前驱节点prior,然后prior->next=prior->next->next。但是这一题只让访问当前节点,单向链表又无法获取其前驱节点,所以当时想了很久都没解出来。 今天灵光一闪,只能访问当前节点,又得不到其前驱节点,唯一可以知道的是其后继节点,所以我们可以不用真正的删掉当前节点,可以把后继节点的值赋给当前节点,然后删掉后继节点! 想到这一点就很简单了,也许这也是为什么题目说不包括尾节点,因为删掉尾节点不能用上述方法,只能用其前驱节点删除。 完整代码如下:

class Solution {
public:
    void deleteNode(ListNode* node)
    {
        node->val = node->next->val;
        ListNode *no_use=node->next;
        node->next = node->next->next;
        delete no_use;
    }
};

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

LeetCode Remove Duplicates from Sorted Array II

80. Remove Duplicates from Sorted Array II

Given a sorted array nums, remove the duplicates in-place such that duplicates appeared at most twice and return the new length.

Do not allocate extra space for another array, you must do this by modifying the input array in-place with O(1) extra memory.

Example 1:

Given nums = [1,1,1,2,2,3],

Your function should return length = 5, with the first five elements of nums being 1, 1, 2, 2 and 3 respectively.

It doesn't matter what you leave beyond the returned length.

Example 2:

Given nums = [0,0,1,1,1,1,2,3,3],

Your function should return length = 7, with the first seven elements of nums being modified to 0, 0, 1, 1, 2, 3 and 3 respectively.

It doesn't matter what values are set beyond the returned length.

Clarification:

Confused why the returned value is an integer but your answer is an array?

Note that the input array is passed in by reference, which means modification to the input array will be known to the caller as well.

Internally you can think of this:

// nums is passed in by reference. (i.e., without making a copy)
int len = removeDuplicates(nums);

// any modification to nums in your function would be known by the caller.
// using the length returned by your function, it prints the first len elements.
for (int i = 0; i < len; i++) {
    print(nums[i]);
}

这一题是LeetCode Remove Duplicates from Sorted Array的升级版,如果数组中的重复元素允许最多保留2个,又应该怎么处理呢。
可以通过修改LeetCode Remove Duplicates from Sorted Array的代码实现,添加一个cnt来统计重复出现的次数,如果小于3次,则i(最多不超过2次重复的最末尾的下标)的下标还是不停往前移,否则i暂停,但j还是要往前移。如果遇到不等的元素,则重置cnt=1。完整代码如下:

class Solution {
public:
    int removeDuplicates(vector<int>& nums)
    {
        if (nums.size() < 3)
            return nums.size();
        int i = 0, j = i + 1, cnt = 1;
        while (j < nums.size()) {
            if (nums[j] != nums[i]) {
                cnt = 1;
                nums[++i] = nums[j++];
            }
            else {
                cnt++;
                if (cnt < 3)
                    nums[++i] = nums[j++];
                else
                    j++;
            }
        }
        return i + 1;
    }
};

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

上面的代码理解起来有点绕,网上有一种解法还是很好理解的,直接比较当前元素和前一个元素是否相等,相等则cnt++,否则cnt=1,同时如果cnt<3,则把最多重复2次的元素往前移。完整代码如下:

class Solution {
public:
    int removeDuplicates(vector<int>& nums)
    {
        if (nums.size() < 3)
            return nums.size();
        int cnt = 0, j = 0;
        for (int i = 0; i < nums.size(); i++) {
            if (i > 0 && nums[i] == nums[i – 1]) {
                cnt++;
                if (cnt >= 3)
                    continue;
            }
            else {
                cnt = 1;
            }
            nums[j++] = nums[i];
        }
        return j;
    }
};

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

二刷。双指针,i表示合法的填入位置,j一直往前走,如果和target相等,则填入i位置;否则更新target值。

class Solution {
public:
	int removeDuplicates(vector<int>& nums) {
		int n = nums.size();
		if (n == 0)return 0;
		int target_value = nums[0], target_id = 0;
		int i = 0, j = 0;
		while (j < n) {
			while (j < n&&nums[j] == target_value) {
				if (j < target_id + 2)nums[i++] = target_value;
				++j;
			}
			if (j < n) {
				target_id = j;
				target_value = nums[j];
			}
		}
		return i;
	}
};

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

LeetCode Move Zeroes

283. Move Zeroes

Given an array nums, write a function to move all 0‘s to the end of it while maintaining the relative order of the non-zero elements.

Example:

Input: [0,1,0,3,12]
Output: [1,3,12,0,0]

Note:

  1. You must do this in-place without making a copy of the array.
  2. Minimize the total number of operations.

本题要把数组中的所有0元素移到数组末尾,同时保证其他元素的相对顺序不变,并且要求in-place和尽量少的操作。
我的思路是首先找0元素,然后在0后面找第一个非0元素,交换它们,直到数组末尾。完整代码如下:

class Solution {
public:
    void moveZeroes(vector<int>& nums)
    {
        for (int i = 0; i < nums.size(); i++) {
            if (nums[i] == 0) {
                for (int j = i + 1; j < nums.size(); j++) {
                    if (nums[j] != 0) {
                        swap(nums[i], nums[j]);
                        break;
                    }
                }
            }
        }
    }
};

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

后来在网上看了看别人的解法,发现只需要一遍循环就可以,而且代码非常简洁,真是太棒了。
思路是找非0元素,然后和之前的元素交换,但是之前的元素(下标j对应元素)不一定是0,因为只有非0时才交换,所以交换次数是非0元的个数。
完整代码如下:

class Solution {
public:
    void moveZeroes(vector<int>& nums)
    {
        for (int i = 0, j = 0; i < nums.size(); i++) {
            if (nums[i] != 0) {
                swap(nums[i], nums[j++]);
            }
        }
    }
};

本代码提交AC,用时13MS,果然快了不少。

二刷。简单方法,两个指针,j指针一直往前走,i指针指向当前可以填入非0元素的位置。j扫完之后,i后面的都应该是0。

class Solution {
public:
	void moveZeroes(vector<int>& nums) {
		int n = nums.size(), num_zeors = 0;
		int i = 0;
		for (int j = 0; j < n; ++j) {
			if (nums[j] != 0)nums[i++] = nums[j];
		}
		for (int j = i; j < n; ++j)nums[j] = 0;
	}
};

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

LeetCode Super Ugly Number

LeetCode Super Ugly Number Write a program to find the nth super ugly number. Super ugly numbers are positive numbers whose all prime factors are in the given prime list primes of size k. For example, [1, 2, 4, 7, 8, 13, 14, 16, 19, 26, 28, 32] is the sequence of the first 12 super ugly numbers given primes = [2, 7, 13, 19] of size 4. Note: (1) 1 is a super ugly number for any given primes. (2) The given numbers in primes are in ascending order. (3) 0 < k ≤ 100, 0 < n ≤ 106, 0 < primes[i] < 1000.


这题是超级丑数,再也不想见到丑数了… 其实本质和LeetCode Ugly Number II没什么两样,只不过素数不局限于2,3,5了,只要用一个数组idx来存储不同素数对应的小丑数的下标。完整代码如下: [cpp] class Solution { public: int nthSuperUglyNumber(int n, vector<int>& primes) { if (n == 1)return 1; vector<int> ugly_nums = { 1 }; vector<int> idx(primes.size(), 0); int next_ugly_num = 0; while (ugly_nums.size() < n) { next_ugly_num = INT_MAX; for (int i = 0; i < primes.size(); i++) { next_ugly_num = min(next_ugly_num, primes[i] * ugly_nums[idx[i]]); } for (int i = 0; i < primes.size(); i++) { if (next_ugly_num == primes[i] * ugly_nums[idx[i]]) { idx[i]++; } } ugly_nums.push_back(next_ugly_num); } return next_ugly_num; } }; [/cpp] 本代码提交AC,用时143MS。]]>

LeetCode Ugly Number II

264. Ugly Number II 264. Ugly Number II

Write a program to find the n-th ugly number.

Ugly numbers are positive numbers whose prime factors only include 2, 3, 5

Example:

Input: n = 10
Output: 12
Explanation: 1, 2, 3, 4, 5, 6, 8, 9, 10, 12 is the sequence of the first 10 ugly numbers.

Note:  

  1. 1 is typically treated as an ugly number.
  2. n does not exceed 1690.

本题是LeetCode Ugly Number的升级版,题目还是不错的,要求出第n个丑数是什么。非常naive的方法就是从1开始不断调用LeetCode Ugly Number中判断丑数的函数,如果是丑数,cnt++,直到cnt==n时输出第n个丑数,但是hint里指出很多都不是丑数,所以这样很浪费时间。 hint里同时给出了怎样直接生成所有丑数序列的方法,不错。 主要思路是这样的,每个丑数都是更小的丑数乘以2,3或5得到的,所以我们可以维护一个丑数序列,要求更大的丑数时,用更小的丑数乘以2,3,5来得到,但是2,3,5对应的那个小丑数并不是一样的。举个例子,首先丑数序列中只有{1}这个元素,对于2,3,5,初始的时候i2=i3=i5=0,表明2,3,5对应丑数序列中的第0个元素1,此时用1分别乘以2,3,5得到的最小值是2,所以下一个丑数是1*2得到的,那么i2++去对应下一个小丑数,i3和i5对应的下标不变。不断这样产生丑数。 不太好描述,请看代码:

class Solution {
public:
    int nthUglyNumber(int n)
    {
        if (n == 1)
            return 1;
        vector<int> ugly_num = { 1 };
        int i2 = 0, i3 = 0, i5 = 0;
        int next_ugly_num = 0;
        while (ugly_num.size() < n) {
            next_ugly_num = min(min(ugly_num[i2] * 2, ugly_num[i3] * 3), ugly_num[i5] * 5);
            if (next_ugly_num == ugly_num[i2] * 2)
                i2++;
            if (next_ugly_num == ugly_num[i3] * 3)
                i3++;
            if (next_ugly_num == ugly_num[i5] * 5)
                i5++;
            ugly_num.push_back(next_ugly_num);
        }
        return next_ugly_num;
    }
};

代码很简洁,但是要想到这个思路还是有难度的。本代码提交AC,用时16MS。