本来是想直接复习数据结构的,但学姐建议我最好的复习方式是做题。对学姐的建议我是服气的,她的确很擅长学习。我现在刚刚开始在LeetCode上做题,实在不好意思用“刷”这个词,我做题是真的慢,实不相瞒,算法一直是我的软肋。好多时候自己根本找不到好的思路,所以在这里把我的思考过程,还有从别人那里借鉴到的好的思路都整理出来,日积月累总会有帮助。
刚接触LeetCode,我选择的语言是java,看题目时没有给main方法,我以为是要自己写的,写了半天,感觉不对劲:为什么题目没有给输入终止条件?然后还傻乎乎的跑到别人博客下面去留言。后来百度了一下才知道LeetCode上面等于说是提供了main函数的,只要把方法体里的内容补全,人家自己就会测试了,真是尴尬啊。啰啰嗦嗦的开了个篇,下面进入正题了。
一.旋转数组
给定一个数组,将数组中的元素向右移动 k 个位置,其中 k 是非负数。
示例 1:
输入:[1,2,3,4,5,6,7]
和 k = 3 输出:[5,6,7,1,2,3,4]
解释: 向右旋转 1 步:[7,1,2,3,4,5,6]
向右旋转 2 步:[6,7,1,2,3,4,5]
向右旋转 3 步:[5,6,7,1,2,3,4]
示例 2:
输入: [-1,-100,3,99]
和 k = 2
输出: [3,99,-1,-100]
解释:
向右旋转 1 步: [99,-1,-100,3]
向右旋转 2 步: [3,99,-1,-100]
说明:
- 尽可能想出更多的解决方案,至少有三种不同的方法可以解决这个问题。
- 要求使用空间复杂度为 O(1) 的原地算法。
我的错误分析:
设置一个Arraylist将数组放进去,然后继续将前Length-k个元素添加到这个Arraylist里然后再将排在前边的这些元素去掉,将剩下的再转为数组输出。
然后在本地答案是对的,在LeetCode上,一毛一样的测试用例答案就不对了。呵呵呵大约是人家本身就不想让我用Java里封装好的现成办法偷懒吧,这是我写的:
import java.lang.reflect.Array;
import java.util.*;
class Solution1 {
public static void main(String args[]){
int[] arr={1,2,3,4,5,6,7};
System.out.println(arr.length);
int k=3;
Solution1 solution1=new Solution1();
solution1.removeDuplicates(arr,k);
}
public void removeDuplicates(int[] nums,int k) {
int q = nums.length;
List<Integer> list = new ArrayList<>();
List<Integer> list1 = new ArrayList<>();
for (int i = 0; i < nums.length; i++) {
list.add(nums[i]);
list1.add(nums[i]);
}
for (int a = 0; a <= q - k - 1; a++) {
list.add(list.get(a));
}
//System.out.println(list);
for (int m = 0; m < q - k; m++) {
list.remove(0);
}
System.out.println(list);
}
}
然后本地结果也是对的:
7
[5, 6, 7, 1, 2, 3, 4]
Process finished with exit code 0
重点来看下正确的分析思路:
以及具体的代码实现:
class Solution {
public void rotate(int[] nums, int k) {
int n=nums.length;
if(k>n){
k=k%n;
}
int[] nums2=new int[n-k];
int[] nums3=new int[k];
System.arraycopy(nums,0,nums2,0,n-k);
//System.out.println(Arrays.toString(nums2));
System.arraycopy(nums,n-k,nums3,0,k);
//System.out.println(Arrays.toString(nums3));
nums2=reverse(nums2);
nums3=reverse(nums3);
int i=0;
for(int j=k-1;j>=0;j--){
nums[i]=nums3[j];
i++;
}
for(int j=n-k-1;j>=0;j--){
nums[i]=nums2[j];
i++;
}
System.out.println(Arrays.toString(nums));
}
private int[] reverse(int[] arr) {
for(int i=0;i<arr.length/2;i++){
int temp=arr[i];
arr[i]=arr[arr.length-i-1];
arr[arr.length-i-1]=temp;
}
return arr;
}
}
二.加一
给定一个由整数组成的非空数组所表示的非负整数,在该数的基础上加一。
最高位数字存放在数组的首位, 数组中每个元素只存储一个数字。
你可以假设除了整数 0 之外,这个整数不会以零开头。
示例 1:
输入: [1,2,3]
输出: [1,2,4]
解释: 输入数组表示数字 123。
示例 2:
输入: [4,3,2,1]
输出: [4,3,2,2]
解释: 输入数组表示数字 4321。
这个题需要注意的点就是进位的问题。如果数组中原来存的是【9,9,9】。那么末位加一后的数组应该是【1,0,0,0】,而不是【9,9,10】。
这个题我解的不怎么样,这篇博客里的解答还不错给定一个非负整数组成的非空数组,在该数的基础上加一,返回一个新的数组。(数组加1)
三.存在重复
给定一个整数数组,判断是否存在重复元素。
如果任何值在数组中出现至少两次,函数返回 true。如果数组中每个元素都不相同,则返回 false。
示例 1:
输入: [1,2,3,1]
输出: true
示例 2:
输入: [1,2,3,4]
输出: false
示例 3:
输入: [1,1,1,3,3,4,3,2,4,2]
输出: true
这道题我利用了Set中不能有重复元素,把数组放到Set里面,然后看Set和数组的大小是否相等。过的还是挺轻松的。
import java.util.HashSet;
import java.util.Set;
public class Solution3 {
public boolean containsDuplicate(int[] nums) {
Set<Integer> set=new HashSet<>();
for(int a:nums){
set.add(a);
}
if(set.size()==nums.length)
return false;
return true;
}
public static void main(String args[]){
int[] arr={1,2,3,1};
Solution3 solution3=new Solution3();
solution3.containsDuplicate(arr);
System.out.println(solution3.containsDuplicate(arr));
}
}
四.移动零
给定一个数组 nums
,编写一个函数将所有 0
移动到数组的末尾,同时保持非零元素的相对顺序。
示例:
输入:[0,1,0,3,12]
输出:[1,3,12,0,0]
说明:
- 必须在原数组上操作,不能拷贝额外的数组。
- 尽量减少操作次数。
这是我一开始做的:
class Solution {
public void moveZeroes(int[] nums) {
for(int i=0;i<nums.length;i++){
if(nums[i]==0){
//找到了0,要把后面所有元素都前移
for(int j=i+1;j<nums.length;j++){
nums[j-1]=nums[j];
}
nums[nums.length-1]=0;
}
}
System.out.println(Arrays.toString(nums));
}
}
循环整个数组,在找到0元素之后,把它后面的元素都往前移动。但在有两个零连在一起的时候,无法得出正确结果。真是让人困扰啊~
正确解法
统计0出现的个数num,每当遇到不为0的元素,就将它与前第num个元素交换,并且将这个元素所处的位置赋值为0.
class Solution {
public void moveZeroes(int[] nums) {
int zero=0;
for(int i=0;i<nums.length;i++){
if(nums[i]==0)
zero++;
//这里设置的条件是排除只有一个数组元素并且其不为0的情况
else if(zero!=0) {
nums[i-zero]=nums[i];
nums[i]=0;
}
}
System.out.println(Arrays.toString(nums));
}
}
五.从排序数组中删除重复项
给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。
不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。
示例 1:
给定数组 nums = [1,1,2], 函数应该返回新的长度 2, 并且原数组 nums 的前两个元素被修改为1
,2
。 你不需要考虑数组中超出新长度后面的元素。
示例 2:
给定 nums = [0,0,1,1,1,2,2,3,3,4], 函数应该返回新的长度 5, 并且原数组 nums 的前五个元素被修改为0
,1
,2
,3
,4
。 你不需要考虑数组中超出新长度后面的元素。
说明:
为什么返回数值是整数,但输出的答案是数组呢?
请注意,输入数组是以“引用”方式传递的,这意味着在函数里修改输入数组对于调用者是可见的。
你可以想象内部操作如下:
// nums 是以“引用”方式传递的。也就是说,不对实参做任何拷贝
int len = removeDuplicates(nums);
// 在函数里修改输入数组对于调用者是可见的。
// 根据你的函数返回的长度, 它会打印出数组中该长度范围内的所有元素。
for (int i = 0; i < len; i++) {
print(nums[i]);
}
class Solution {
public int removeDuplicates(int[] nums) {
if(nums.length == 0) return 0;
int i = 0;
for(int j = 1; j < nums.length; j++) {
if(nums[j] != nums[i]) { // 有序数组的判重,只要看元素和前一个元素是否重复
i++;
nums[i] = nums[j];
}
}
return i+1;
}
}