今日任务:
- 有序数组的平方
- 长度最小的子数组
- 螺旋矩阵 II
- 总结
今日学习的文章 && 和视频
有序数组
这个题目我一开始想着是将原数组进行平方之后,整体的进行排序,但是因为最好的情况就是快速排序的时间复杂度 O(nlog(n)) 的情况,但是题目要求的是时间复杂度 O(n) 所以就没下去手,也想过是不是用双指针,但是没有想到说是它的给定的数组是有序的这个条件,那么如果他给定的数组是 [-2, -1, 1, 2] 这样的数组的话,修改之后,改变数组顺序的就是两边的值,可能负数平方之后的值要大于后面的某一个值,但是如果传入的不是一个有序的数组,那么比如说是 [-1, -2, 1, 2] 这样子,是无法使用双指针来进行排序的,但是后来实在想不到办法了就使用这个暴力法做了
public static void square(int[] nums) {
for (int i = 0; i < nums.length; i++) {
nums[i] = nums[i] * nums[i];
}
Arrays.sort(nums);
System.out.println("Man Stop!");
}
但是我得志不在此,因此我又看了看题解使用,创建一个新的数组定义一个指针k指向它的最后,并在原数组的两端各放置一个指针,它们相互的移动,比较两个指针位置的值平方之后的大小,如果左边的大于右边的,就将左边的放置到新数组中,同时新数组的指针向前移动一位,如果右边的大于或等于左边的同理,这样就可以将平方之后的值,进行排序,典型的时间换空间
//使用双指针来将平方之后的元素进行排序
//需要明确的条件是:
//1. 原来的数组是有序的
//2. 影响我们平方之后数组顺序的是负数,可能大于后面的正数,因此排序的关键是左侧的元素,使用双指针,从两端开始查找
//3. 题目对空间没有要求,因此我们可以用空间来换时间!
public static int[] sortArr(int[] nums) {
int left = 0;
int right = nums.length - 1;
int leftSquare = 0;
int rightSquare = 0;
int[] newArr = new int[nums.length];
int index = newArr.length - 1;
while (left <= right) {
leftSquare = nums[left] * nums[left];
rightSquare = nums[right] * nums[right];
if (leftSquare < rightSquare) {
newArr[index--] = rightSquare;
right--;
} else {
newArr[index--] = leftSquare;
left++;
}
}
System.out.println("Man Stop!");
return newArr;
}
这种双指针的解法我记得好像在排序算法里面有一个合并两个有序数组的情况用过,也是创建一个新的数组,然后定义两个指针分别指向两个数组,将他们之中较小的值存入到新创建的数组中,同时存入值得数组和新数组得指针都向后移动一位这样得
长度最小的子数组
这道题我刚开始看的时候想到了自己之前做的类似的题目,印象中有一个解法就是使用滑动窗口来做的,但是我已经忘记了那个解法,所以就是用了暴力破解来做,双层 For 循环进行遍历,外层 For 循环用于控制执行的位置,内层 For 循环用于进行具体的操作,然后将最小的值存入其中,第一次看的时候我没读清楚题目还以为是只有等于 target 的情况,后面看了看题解发现应该是 >= 就可以
public static int findSmallLen(int[] nums, int target) {
int smallLen = Integer.MAX_VALUE;
boolean isChange = true;
int sum = 0;
for (int index = 0; index < nums.length; index++) {
for (int i = index; i < nums.length; i++) {
if (sum != target) {
sum += nums[i];
}
if (sum == target) {
smallLen = Math.min(smallLen, i - index + 1);
isChange = false;
}
}
sum = 0;
}
smallLen = smallLen == Integer.MAX_VALUE ? 0 : smallLen;
return smallLen;
}
这个代码是对的,不过 LeetCode 里面他那个测试的用例有问题,所以报错了,但是我知道它是没问题的! 双指针的做法就是将两层 For 循环做的事情在一层 For 循环里面解决了,它和双层 For 循环定义索引不一样的是,一个是外层的 For 循环不断地相后移动,在满足题目的判断条件之后进入到 while 循环当中,然后可以移动first指针,这样滑动的操作这个数组的两个指针,因此被称之为滑动数组,它的格式就是 for 循环 + while() 条件判断,重要的是 for 循环下面的添加操作,以及 while 循环的判断条件
public static int minSubArrayLen(int s, int[] nums) {
int left = 0;
int right = 0;
int sum = 0;
int smallLen = Integer.MAX_VALUE;
int len = 0;
for (right = 0; right < nums.length; right++) {
sum += nums[right];
while (sum >= s) {
len = right - left + 1;
smallLen = Math.min(smallLen, len);
sum -= nums[left++];
}
}
System.out.println("Man Stop!");
return smallLen == Integer.MAX_VALUE ? 0 : smallLen;
}
然后我还做了一道类似的题目: leetcode.cn/problems/fr… 水果成篮问题
public static int totalFruit(int[] fruits) {
int first = 0;
int end = 0;
//定义一个HashMap来接受
int maxLen = Integer.MIN_VALUE;
int len = 0;
HashMap<Integer, Integer> hashMap = new HashMap<Integer, Integer>();
for (end = 0; end < fruits.length; end++) {
if (hashMap.containsKey(Integer.valueOf(fruits[end]))) {
int count = hashMap.get(fruits[end]);
hashMap.put(fruits[end], ++count);
} else {
hashMap.put(fruits[end], 1);
}
while(hashMap.size() >= 2) {
if (hashMap.size() > 2) {
int firstCount = hashMap.get(fruits[first]);
if (firstCount != 1) {
hashMap.put(fruits[first], --firstCount);
} else {
hashMap.remove(fruits[first]);
}
first++;
}
//获取到 HashMap 里面得值
if (hashMap.size() == 2) {
len = end - first + 1;
maxLen = Math.max(maxLen, len);
break;
}
}
}
System.out.println("Man Stop!");
return maxLen == Integer.MIN_VALUE ? fruits.length : maxLen;
}
调试了半天才做出来,我也不知道该怎么办了,而且执行用时 58 ms ……
螺旋矩阵 II 这个我以前好像看过题解,好像是用一个动态规划做的,想到这里我直接放弃,去看了看视频,记住了左闭右开,循环不变量,胡乱改了一通成了现在这个样子,不过我还是没有完全理解因为我在做他的另一个类型题目的时候写错了、 leetcode.cn/problems/sp… 而且现在还没搞懂!
public static int[][] matrix(int n) {
int[][] nums = new int[n][n];
int col = 0;
int row = 0;
int loop = 0;
int count = 1;
int start = 0;
//如果n为奇数,那么就在遍历之后将中间的值赋予给它
while (loop++ < (n / 2)) {
for (col = start; col < n - loop; col++) {
nums[start][col] = count++;
}
for (row = start; row < n - loop; row++) {
nums[row][col] = count++;
}
for (col = col; col > loop - 1; col--) {
nums[row][col] = count++;
}
for (row = row; row > loop - 1; row--) {
nums[row][col] = count++;
}
start++;
}
if (n % 2 == 1) {
nums[n / 2][n / 2] = count;
}
System.out.println("Man Stop!");
return nums;
}
总结
二分查找,双指针(快慢指针和头尾指针),滑动窗口(也是双指针的一种,只不过它的指针可以双向移动,展现在数组上面就是一个滑动的数组,因此我们就将他叫做滑动窗口),螺旋矩阵(就是遵循循环不变量的原则,如果确定了一开始遍历的规则是左闭右开之后对应的索引就不可以进行修改,然后真正的解法一定是有规律的而不是乱来的) 最后就是数组的题目它的删除操作都是将删除的数组往后面进行移动,无法真正的做到删除,因为数组是一个物理结构,她是在内存中整块存在的,如果要删除的话是将声明的整个数组进行删除,因此在将数组往后面移动的过程中,我们就需要考虑它的索引移动,比如说在移除元素的暴力解法里面就是需要将for循环的长度--然后因为后面的元素移动到前面了,因此for循环的i也要减减这样的操作,比较考验对代码的掌控能力! 没事再看看第一天的题目。