Array
刷LeetCode,按照LeetCode中分类的顺序由易到难开刷
27. Remove Element
https://leetcode.com/problems/remove-element/
挺简单的题目,看了别人的答案才意识到只需要返回新数组的长度,方法复杂度均为O(n)
我的代码:
public class Solution {
public int removeElement(int[] nums, int val) {
int i = 0;
int j = 0;
for(i = 0; i < nums.length; i++)
{
if(nums[i] == val)
{
continue;
}
else
{
nums[j] = nums[i];
j++;
}
}
return j;
}
}
给出的最优方法也差不多,只是每次将查出的元素用当前数组的最后一个元素替换,继续搜索
public class Solution {
public int removeElement(int[] nums, int val) {
int len = nums.length;
for(int i = 0; i < len; i++)
{
if(nums[i] == val)
{
nums[i--] = nums[len-- -1];
}
}
return len;
}
}
26.Remove Duplicates from Sorted Array
https://leetcode.com/problems/remove-duplicates-from-sorted-array/
有序数组去重,和前一题类似,虽然是嵌套循环,复杂度还是O(n)
public class Solution {
public int removeDuplicates(int[] nums) {
int len = nums.length;
int dup = 0;
for( int i = 0; i < len; )
{
int j = i+1;
for(; j < len && nums[i] == nums[j]; j++)
{
dup++;
continue;
}
i = j;
nums[i - dup-1] = nums[i-1];
}
return len-dup;
}
}
看了给出的方法,感觉自己秀逗了,既然是O(n)的方法,遍历的只是两个游标,可以改到一个循环中的,
public class Solution {
public int removeDuplicates(int[] nums) {
if(nums.length == 0)
return 0;
int j = 0;
for(int i = 0; i < nums.length; i++)
if(nums[i]!=nums[j])
nums[++j] = nums[i];
return ++j;
}
}
80. Remove Duplicates from Sorted Array II
和上面一个很类似,但是多了一个两次的条件,在原代码基础上稍微改下,添加了个对是否为两次的标记,我是用int型的,其实用布尔型的也是一样的效果
public class Solution {
public int removeDuplicates(int[] nums) {
int j = 0;
int dup = 0;
for ( int i = 0; i < nums.length; i++)
{
if(nums[i] != nums[j])
{
nums[++j] = nums[i];
dup = 0;
}
else if(dup == 0 && i!=j)//此处为了避免处理第一个元素时出问题,增加了一个i!=j的判断
{
dup = 1;
nums[++j] = nums[i];
}
}
return ++j;
}
}
看了答案,提供了一个最多允许K个重复的方法,思路类似,不过是直接从下标为1的开始,赋值时先自增;比较时每个元素与前一个元素比较,更方便一些
public class Solution {
public int removeDuplicates(int[] nums) {
int n = nums.length;
int k = 2;
if( n <= k)
return n;
int i = 1, j = 1;
int cnt = 1;
while(i < n)
{
if(nums[i]!=nums[i-1])
{
cnt = 1;
nums[j++] = nums[i];
}
else
{
if(cnt < k)
{
nums[j++] = nums[i];
cnt++;
}
}
i++;
}
return j;
}
}
66. Plus One
https://leetcode.com/problems/plus-one/
用数组存的数字,返回+1之后的结果,涉及的是进位的考虑。
public class Solution {
public int[] plusOne(int[] digits) {
int carry = 1;
int i = digits.length - 1;
while( i >= 0)
{
int sum = digits[i] + carry;
digits[i] = sum%10;
carry = sum/10;
if(carry == 0)
return digits;
i--;
}
int[] res = new int [digits.length+1];
res[0] = carry;
for(int j = 1; j < res.length; j++)
{
res[j] = 0;
}
return res;
}
}
看了下参考的答案,针对的就是+1,所以是直接判断是否为9,不是的话+1返回,是的话该位变成0,有意思的一点是如果要增加一位数只需要对首位数赋为1,查了下Java中数组的用法, 确实可以
public class Solution {
public int[] plusOne(int[] digits) {
int n = digits.length;
for(int i=n-1; i>=0; i--) {
if(digits[i] < 9) {
digits[i]++;
return digits;
}
digits[i] = 0;
}
int[] newNumber = new int [n+1];
newNumber[0] = 1;
return newNumber;
}
118. Pascal’s Triangle
Java中List的初始化一般用ArrayList
public class Solution {
public List<List<Integer>> generate(int numRows) {
List<List<Integer>> res = new ArrayList<List<Integer>>();
if(numRows<=0)
return res;
for(int i = 0; i < numRows; i++)
{
List<Integer> newRow = new ArrayList<Integer>();
for(int j = 0; j <=i; j++)
{
if( j==0 || j == i)
newRow.add(1);
else{
newRow.add(res.get(i-1).get(j-1)+res.get(i-1).get(j));
}
}
res.add(newRow);
}
return res;
}
}
119. Pascal’s Triangle II
https://leetcode.com/problems/pascals-triangle-ii/
和上题类似,但是要求只用O(k)的空间,因此需要一点小技巧,在赋值的时候从后面开始逐渐赋值
public class Solution {
public List<Integer> getRow(int rowIndex) {
List<Integer> res = new ArrayList<Integer>();
for(int i = 0; i <= rowIndex; i++){
res.add(1);//增加的操作放在内层循环外面会省事很多
for(int j = i-1; j > 0; j--){
res.set(j,res.get(j)+res.get(j-1));
}
}
return res;
}
}
与参考答案思路类似
2Sum
https://leetcode.com/problems/two-sum/
直接暴力解决,两层遍历解决,复杂度O(n^2)
public class Solution {
public int[] twoSum(int[] nums, int target) {
int[] res = new int[2];
for(int i = 0 ; i < nums.length; i++)
{
for(int j = i+1; j < nums.length; j++)
{
if(nums[i]+nums[j] == target)
{
res[0] = i;
res[1] = j;
}
}
}
return res;
}
}
写的时候就想O(n^2)的方法应该不是最优的,还有更好的,看了下参考答案确实是,思路也是常见的空间换时间,增加一个map结构的缓存,标记对应的数字是否出现过
话说回来,map结构真的很好用,改后的算法是O(n)时间O(n)空间
public class Solution {
public int[] twoSum(int[] nums, int target) {
int[] res = new int[2];
Map<Integer,Integer> map = new HashMap<Integer,Integer>();
for(int i = 0 ; i < nums.length; i++)
{
if(map.containsKey(target - nums[i]))
{
res[0] = map.get(target - nums[i]);
res[1] = i;
return res;
}
map.put(nums[i],i);
}
return res;
}
}
3Sum
https://leetcode.com/problems/3sum/
想按照上题的思路,先写一个时间O(n^2)空间O(n)的方法,但发现有个问题没法解决,无法保证组合只出现一次,尝试用Set来写还是有问题。
目前的出错代码
public class Solution {
public List<List<Integer>> threeSum(int[] nums) {
if(nums.length <= 2)
return new ArrayList<List<Integer>>();
Arrays.sort(nums);
Map<Integer,Integer> oneSum = new HashMap<Integer,Integer>();
Set<List<Integer>> resSet = new HashSet<List<Integer>>();
for(int i = 0; i < nums.length; i++)
{
oneSum.put(nums[i],i);
for(int j = i+1; j < nums.length; j++)
{
if(oneSum.containsKey(0 - nums[i]-nums[j]))
{
List<Integer> tmp = new ArrayList<Integer>();
tmp.add(0 - nums[i]-nums[j]);
tmp.add(nums[i]);
tmp.add(nums[j]);
resSet.add(tmp);
}
}
}
List<List<Integer>> res = new ArrayList<List<Integer>>(resSet);
return res;
}
}
参考了下Top Solutions,有意思的是其中做了对重复元素的跳过,保证了答案的唯一性。
public class Solution {
public List<List<Integer>> threeSum(int[] nums) {
Arrays.sort(nums);
List<List<Integer>> res = new ArrayList<List<Integer>>();
for(int i = 0; i < nums.length-2; i++)
{
if(i == 0 || (i > 0 && nums[i]!=nums[i-1]))
{
int low = i+1, high = nums.length-1, sum = 0 - nums[i];
while(low < high)
{
if(nums[low] + nums[high] == sum)
{
res.add(Arrays.asList(nums[i],nums[low],nums[high]));
while(low < high && nums[low] == nums[low+1])
low++;
while(low < high && nums[high] == nums[high-1])
high--;
low++;
high--;
}
else if(nums[low]+nums[high] < sum)
low++;
else
high--;
}
}
}
return res;
}
}
考虑了下为什么不能在2Sum中用这种方法,一是需要排序,时间复杂度会变成O(nlogn),二是2Sum要求返回的是下标,那样就需要一个结构来存数据值与下标的映射关系,而且还要保证先后顺序,逻辑会比较复杂,就远不如那个O(n)的算法好用啦
3Sum Closest
https://leetcode.com/problems/3sum-closest/
和上题的思路差不多,而且因为是假设有唯一答案,返回值只是三者的和,也不需要对重复元素跳过
public class Solution {
public int threeSumClosest(int[] nums, int target) {
Arrays.sort(nums);
int res = nums[0]+nums[1]+nums[2];
for(int i = 0; i < nums.length-2; i++)
{
int low = i+1, high = nums.length -1;
while(low < high)
{
int sum = nums[low]+nums[high]+nums[i];
if (sum < target)
{
low++;
}
else
{
high--;
}
if(Math.abs(target - sum) < Math.abs(target - res))
{
res = sum;
}
}
}
return res;
}
}
Search a 2D Matrix
https://leetcode.com/problems/search-a-2d-matrix/
这个题目是在一个有序的矩阵里找指定元素,而且虽然是二维的,但是完全可以当作一维有序数组,第一眼看到题目,想用二分的想法,复杂度应该是O(log(mn))
public class Solution {
public boolean searchMatrix(int[][] matrix, int target) {
if(matrix == null || matrix.length == 0)
return false;
int row_num = matrix.length;
int col_num = matrix[0].length;
int begin = 0, end = row_num * col_num - 1;
while(begin <= end){
int mid = (begin + end) / 2;
int mid_value = matrix[mid/col_num][mid%col_num];
if( mid_value == target){
return true;
}else if(mid_value < target){
begin = mid+1;
}else{
end = mid-1;
}
}
return false;
}
}
看了下参考资料,有一种看起来更简洁的方法,但复杂度是O(m+n),这种解法不仅适用于本题目,也适用于另外一道类似的题目
public class Solution {
public boolean searchMatrix(int[][] matrix, int target) {
if(matrix == null || matrix.length == 0)
return false;
int clen = matrix[0].length;
int rlen = matrix.length;
int rnum = 0, cnum = clen-1;
while(rnum < rlen && cnum >= 0){
if(matrix[rnum][cnum] > target)
cnum--;
else if(matrix[rnum][cnum] < target)
rnum++;
else
return true;
}
return false;
}
}
Find Peak Element
https://leetcode.com/problems/find-peak-element/
这道题目也可以用二分法来做,需要注意的地方在于边界条件的判断,如何保证数据不会溢出
public class Solution {
public int findPeakElement(int[] nums) {
int low = 0, high = nums.length -1;
if(nums.length <= 1)
return 0;
while( low <= high){
int mid = (low+high)>>1;
if((mid==nums.length-1||nums[mid]>nums[mid+1])&&(mid==0||nums[mid]>nums[mid-1])){
return mid;
}
else if(nums[mid] < nums[mid+1]){
low = mid+1;
}else{
high = mid-1;
}
}
return low;
}
}