数组
代码随想录算法训练营Day1任务。
认识数组
数组是一种我们最常用也是最常见的数据结构。
学过数据结构的同学们都知道,数组是一种线性表,数组是一串连续的内存空间中相同类型的数据聚合
接下来我会根据自己的理解写出在C语言中的数组与在JS中的数组
C中的数组
C语言中的数组就是我们在数据结构中所描述的最经典的数组,它有着如下的一些特点
- 数组的下标起始位置都是从0开始
- 数组是一串连续的地址(内存空间中)
- 数组的每一片空间都是同一种数据类型,且数据类型和数组长度在定义时就确定了
- 数组无法增加或删除(即改变数组的长度)
- 数组只允许覆盖
JS中的数组
数组在JS这一门语言中与C和C++就大有不同了,这是因为JS的数组的底层设计原因。
- JS的数组下标起始位置是从0开始(相同)
- JS的数组可以存放不同类型的数据(不同)
- JS的数组在相同数据类型下是一串连续的地址(相同)
- JS的数组在内部存放不同数据类型的情况下,地址是不连续的(不同)
- JS的数组允许增加或删除,即可以随意的改变数组的长度(不同)
二分查找
力扣链接:https://leetcode.cn/problems/binary-search/description/
二分查找这道题其实并不难理解,只要理解了二分法就可以了,其中最重要的就是要明白里面的一些坑,到底判断的是left< right 还是 left<=right right的初值是nums.length 还是 nums.lenght-1;在缩小范围的时候到底是加1 还是 不加1
二分法
二分法最重要的是要在一个有序数组中,如果数组无序,则二分法无法判断。
在有序的这个条件下,通过一个左右边界来找出中间值,通过传入的数与中间值的比较来丢弃掉一半的数,进而缩小范围,来达到快速搜索的理念。
在这里我们有两种方法 这两种方法就可以理解上面的一系列坑
-
左闭右闭区间
左闭右闭区间的意思就是,这个二分的范围的左边界和右边界都会纳入这一次的检查范围
根据上图我们就能充分明白左闭右闭区间中的各个判断条件
即 nums[mid] < target 时 left = mid +1
nums[mid] > target 时 right = mid -1
nums[mid] == target 时 return
而循环条件则是 left <= right 因为如图最后一次查询, 如果是left < right 则没有最后一次判断 直接 结束循环了 -
左闭右开区间
左闭右开即右边界所指向的数不计入当此的检索范围
![在这里插入图片描
根据上图我们就能充分明白左闭右开区间中的各个判断条件
即 nums[mid] < target 时 left = mid +1
nums[mid] > target 时 right = mid
nums[mid] == target 时 return
而循环条件则是 left < right 因为right右边界是不计入当次检索范围的 所以若是let <= right的话则是不必要的
C语言解法
左闭右闭区间
int search(int* nums, int numsSize, int target){
int left = 0;
int right = numsSize-1;
int mid = 0;
while(left <= right){
mid = (right + left) /2 ;
if(nums[mid] < target){
left = mid+1;
}
else if(nums[mid] > target){
right = mid-1;
}
else if(nums[mid] == target){
return mid;
}
}
return -1;
}
左闭右开区间
int search(int* nums, int numsSize, int target){
int left = 0;
int right = numsSize;
int mid = 0;
while(left < right){
mid = (right + left) /2 ;
if(nums[mid] < target){
left = mid+1;
}
else if(nums[mid] > target){
right = mid;
}
else if(nums[mid] == target){
return mid;
}
}
return -1;
}
JS解法
左闭右闭区间
/**
* @param {number[]} nums
* @param {number} target
* @return {number}
*/
var search = function(nums, target) {
let left = 0;
let right = nums.length -1 ; //左闭右开区间 所以right = nums.length 时 不会检查right
while(left<=right){
let mid = left + ((right - left)>>1);
if(nums[mid] < target){
left = mid+1;
}else if(nums[mid] > target){
//左闭右开
right = mid-1;
}else{
return mid;
}
}
return -1;
};
左闭右开区间
/**
* @param {number[]} nums
* @param {number} target
* @return {number}
*/
var search = function(nums, target) {
let left = 0;
let right = nums.length; //左闭右开区间 所以right = nums.length 时 不会检查right
while(left< right){
let mid = left + ((right - left)>>1);
if(nums[mid] < target){
left = mid+1;
}else if(nums[mid] > target){
//左闭右开
right = mid;
}else{
return mid;
}
}
return -1;
};
移除元素
https://leetcode.cn/problems/remove-element/description/
本题一共我能够想到3种解法
- 暴力解
即用一层for循环遍历每一位,当遍历到的位与nums相同 则用第二层for循环 将后续的每一位覆盖前一位,之后 i-- 继续判断后续的数值,因为所有数都前移了一位 所以i需要减一位 重新判断当前位置的值 - 双指针(左右指针)
该方法是用两个指针,右侧指针指向能够存放数据的空位,先把right移动到不为val的位置,然后判断nums[left] 是否等于val。若是等于 则把nums[left]的值赋值为nums[right],然后再把right–之后 再把right移动到部位val的位置,如此循环到最后返回left值就为新数组的大小
注意:在每一次循环过后都要再将右指针移动到不为 val的位置,否则会有些事项通不过,因为–了之后可能这个值就为val,再这个地方卡了挺久了,错了很多次。
3.双指针(快慢指针)
该方法时用两个指针,当遇到nums[slot] == val时,该指针不动, fast指针继续往前走,并将nums[slot]的值赋值为nums[fast]。循环结束后 slot就是新数组的长度
C语言解法
快慢指针
int removeElement(int* nums, int numsSize, int val){
int slot =0;
int fast;
for(fast =0;fast < numsSize;fast++){
if(nums[fast] != val ){
nums[slot++] = nums[fast];
}
}
return slot;
}
JS解法
暴力
/**
* @param {number[]} nums
* @param {number} val
* @return {number}
*/
var removeElement = function(nums, val) {
let size = nums.length;
for (let i=0;i<size;i++){
if(val == nums[i]){
for(let j=i+1;j<size;j++){
nums[j -1] = nums[j]
}
i--;
size--;
}
}
return size;
};
左右指针
/**
* @param {number[]} nums
* @param {number} val
* @return {number}
*/
var removeElement = function(nums, val) {
let left = 0;
let right = nums.length -1 ;
while(nums[right] == val && right >=0)right--; //将右侧指针移动到 右侧不为val的值
while(left <= right){
if(nums[left] == val){
nums[left] = nums[right];
right--;
}
left++;
while(nums[right] == val && right >=0)right--;
}
return left
};
快慢指针
/**
* @param {number[]} nums
* @param {number} val
* @return {number}
*/
var removeElement = function(nums, val) {
let slot = 0;
let fast = 0;
for(fast;fast<nums.length;fast++){
if(nums[fast]!= val){
nums[slot++] = nums[fast]
}
}
return slot
};
今日心得
今日完成算法+完成文章的总共时间共2小时,今日的这几道题其实我也不是第一次刷了,在进入训练营之前也刷过一两遍代码随想录,但是没有特别的认真,都是比较粗略的一些想法,然后看一看代码随想录的思路,就稀里胡录的把代码写出来了,也没有进行一些复盘之类的,导致一些踩过的坑再刷仍然会踩,今天用心的理了思路,画了图,对题的记忆和解法更深刻了,对这些题也有一些自己的想法了,不像之前基本看着题没什么思路