今天是代码随想录算法训练营的第一天,关于数组的,有一些基础知识,备份如下:
数组是存放在连续内存空间上的相同类型的数据集合;
数组可以通过下标很方便的获取到元素;
数组下标是从0开始;
由于数组在内存地址上是连续的,所以在删除或者添加数组元素时,难免要移动其他元素的地址;
数组元素不能删除,只能覆盖。
704.二分查找
其实断断续续学习过很多次二分查找,思想能够理解,但是代码实现上其实是有需要注意的。
就是二分查找的while条件里,左右区间,可以分为左闭右开和左闭右闭两种方式。
如果是左闭右开的区间,while条件里,left 就不能包括=right,因为left=right没有实际意义,此时while条件应该是while(left < right),这里的right = nums.length;
而在数值判断的if else里,如果target > nums[mid]时,right就应该=mid
代码如下:
class Solution {
public int search(int[] nums, int target) {
//二分查找主要是每次通过与中间值进行比较,决定程序是返回还是在左边或者右边查找
//所以我认为二分查找的重点是如何确认中间值
int left = 0;
int right = nums.length;
//终止条件是left和right指针到一起了
while(left < right) {//这里right我取的是数组长度,所以这里是左闭右开的区间,因为此时left=right是没有意义的,left最多等于right-1,区间是[left,right-1]
int mid = left + (right -left) / 2;
if(nums[mid] == target) {
return mid;
}else if(nums[mid] > target) {//去左边查找
right = mid; //区间是[lelft,middle)
}else {//去右边查找
left = mid + 1;//区间是[mid+1,right)
}
}
return -1;
// //如果是闭区间,要注意的是right的变化,需要mid -1
// int left = 0;
// int right = nums.length - 1;
// while(left <= right) {
// int mid = left + (right - left)/ 2;
// if(nums[mid] == target) {
// return mid;
// }if(nums[mid] > target) {//target 在左区间,[left,mid-1]是有意义的
// right = mid - 1;
// }else {
// left = mid;
// }
// }
// return -1;
}
}
而在左闭右闭的区间情况下,right = nums.length -1; 此时left = right是有意义的,所以while(left <= right) ,在内层的if else判断中,如果target < nums[mid],此时,right应该等于mid -1;
代码片段如下:
//如果是闭区间,要注意的是right的变化,需要mid -1
int left = 0;
int right = nums.length - 1;
while(left <= right) {
int mid = left + (right - left)/ 2;
if(nums[mid] == target) {
return mid;
}if(nums[mid] > target) {//target 在左区间,[left,mid-1]是有意义的
right = mid - 1;
}else {
left = mid;
}
}
return -1;
27.移除元素
这题原先我的思路是设定左右两个指针,通过比较两个指针值和target的值,相等或者不相等时移动相应的指针,调试过程中有个别case未通过,看了算法讲解,才明白两种解法都比我的思路要好,贴上相应代码:
暴力解代码片段:
int size = nums.length;
for (int i = 0; i < size; i++) {
if (nums[i] == val) { // 发现需要移除的元素,就将数组集体向前移动一位
for (int j = i + 1; j < size; j++) {
nums[j - 1] = nums[j];
}
i--; // 因为下标i以后的数值都向前移动了一位,所以i也向前移动一位
size--; // 此时数组的大小-1
}
}
return size;
快慢指针法:
这个方法我看了很长时间,原来是仅仅用慢指针来标记新数组的长度,通过前面的快指针和target做判断,只要快指针对应下标的数组值不等于目标值,就移动慢指针一次,如果快指针下标对应的值是目标值,那慢指针就停下来,等快指针寻找到下一个非目标值的元素,此时慢指针再继续移动,太绝了!
//这里快慢指针的做法太有新意了,慢指针更新新数组的位置,返回满指针的基数就行了。
int slowIndex = 0;
for (int fastIndex = 0; fastIndex < nums.length; fastIndex++) {
if (val != nums[fastIndex]) {
nums[slowIndex++] = nums[fastIndex];
}
}
return slowIndex;
977.数组元素的平方
给你一个按 非递减顺序 排序的整数数组 nums
,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。
简单粗暴的写法,上来直接数组对应下标上的元素直接平方,最后排序一下即可。
public int[] sortedSquares(int[] nums) {
//我这里做复杂了,其实都不需要创建一个新数组,直接在原数组上修改就好了
int[] result = new int[nums.length];
for(int i = 0; i < result.length; i++) {
result[i] = nums[i] * nums[i];
nums[i] *= nums[i];
}
Arrays.sort(result);
//Arrays.sort(nums);
return result;
}
另一种解法,针对有负数的情况,其实只要判断首尾元素平方后谁大,再按照顺序排入即可。
class Solution {
public int[] sortedSquares(int[] nums) {
//我这里做复杂了,其实都不需要创建一个新数组,直接在原数组上修改就好了
// int[] result = new int[nums.length];
// for(int i = 0; i < result.length; i++) {
// result[i] = nums[i] * nums[i];
// nums[i] *= nums[i];
// }
// Arrays.sort(result);
// //Arrays.sort(nums);
// return result;
int[] result = new int[nums.length];
index k = result.length;
int left = 0;
int right = nums.length - 1;
//更快的做法,比较数组前后数字平方的大小,再对应移动较大的那个指针
while(left <= right) {
if(nums[left] * nums[left] < nums[right] * nums[right]) {
result[k--] = nums[right] * nums[right];
right--;
}else {
result[k--] = nums[left] * nums[left];
left++;
}
}
return result;
}
}