1.数组
(1)连续内存上存储
(2)相同数据类型
注意:数组‘删除’一个下标的元素,其实是使用后面的元素向前移动进行覆盖,从而‘删除’。
数组‘增添’一个下标元素,向后移动元素从而‘增添’
数组元素只能覆盖,不能删除。
在C++中,二维数组的地址分布是连续的。
2.二分法:
(1)返回数组的长度,用整体数组字节长度/一个元素字节长度 sizeof(array)/sizeof(array[0])
(2)二分法:1.查找内容上有序 2.内容不重复,只查找一个
(1)左闭右闭,右边是闭区间,所以左边可以等于右边。搜索的时候,将整个区间划分为两部分,如果中间值小于目标值,说明目标值在右半部分(右半部分不用修改右边终止值right,只需要修改左边的开始值left);(left=middle+1,这样更新范围是因为,左边是闭区间,left也是可以取值的,此时的中间值已经小于目标值,所以此时的中间值不是我们要找到,左边的起始值,可以向右移动一位。开始取值,也会取到middle+1这个值)。
同理,中间值大于目标值,目标值在左半部分(左半部分不用修改左边起始值left,只需要修改右边的终止值right=middle-1)(right=middle-1,这样更新范围是因为,右边是闭区间,right也是可以取值的,此时的中间值已经大于目标值,所以此时的中间值不是我们要找到,右边的终止值,可以向左移动一位。开始取值,也会取到middle-1这个值)。
这样修改范围之后,范围是新的,那么中间值也需要更新(放在while循环里面,每循环一次更新一次中间值)。如果目标值等于中间值,即找见了,所要找的数值,返回middle,函数即结束。
(2)如果目标值不存在于数组中,返回它将会被按顺序插入的位置
比起搜索有序内容的一个值,增加一个这个要求,返回值为 return right+1; 即可
1.题目:给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
不用二分法:
(1)目标值小于某个元素,即目标值就排在此元素之前,也就是顶替此元素的位置(下标),返回此元素的下标,也就是返回插入目标值后,目标值的下标!!!!
(2)目标值等于某元素,那么在按顺序插入元素之后,目标值也可以顶替此元素的位置,所以返回此元素下标即可。
(3)目标值小于第一个元素,按顺序插入,也是在第一个元素的位置,返回下标0,即可。
(4)目标值大于最后一个元素,按顺序插入,也是在最后一个元素的后一个元素,为原数组的长度:numsSize。是最后一个元素下标+1:right+1。
2.使用二分法
(1)左闭右闭
二分法:找到元素之后就返回了元素所在下标,所以此时只需要解决(1)目标值在全部元素之前(2)目标值在元素之间(3)目标值在元素之后。
(1)在全部元素之前,在最后搜索,left一直不变,right会改变,最后跳出循环时候为-1,也就是
[0,-1];应该目标值返回下标为0,right+1=0,符合
(2)目标值在元素之间,在之间的时候,我们可以返回目标值小于的哪个元素的下标。由于,二分法,目标值小于此元素后,会将右边终止值right向左移一位,所以此时right就不是此元素下标,而是前一个元素下标,使用right+1;即可返回。
(3)在全部元素之后,要放在numsSize的位置上,right+1,就是numsSize。
class Solution {
public:
int searchInsert(vector<int>& nums, int target) {
int n = nums.size();
int left = 0;
int right = n - 1; // 定义target在左闭右闭的区间里,[left, right]
while (left <= right) { // 当left==right,区间[left, right]依然有效
int middle = left + ((right - left) / 2);// 防止溢出 等同于(left + right)/2
if (nums[middle] > target) {
right = middle - 1; // target 在左区间,所以[left, middle - 1]
} else if (nums[middle] < target) {
left = middle + 1; // target 在右区间,所以[middle + 1, right]
} else { // nums[middle] == target
return middle;
}
}
// 分别处理如下四种情况
// 目标值在数组所有元素之前 [0, -1]
// 目标值等于数组中某一个元素 return middle;
// 目标值插入数组中的位置 [left, right],return right + 1
// 目标值在数组所有元素之后的情况 [left, right], 因为是右闭区间,所以 return right + 1
return right + 1;
}
};
(2)左闭右开,看力扣35题
注意:对于升序内容,解决以上问题,只要找到,比目标值大的元素,返回此元素的下标即可(按顺序找,这个是比目标值大,且最接近目标值的);对于降序内容,解决以上问题,只要找到,比目标值小的元素,返回此元素下标即可(按顺序找,这个是比目标值小,且最接近目标值的)
都是向后覆盖!!!
3.移除元素
题目:给你一个数组 nums
和一个值 val
,你需要 原地 移除所有数值等于 val
的元素,并返回移除后数组的新长度。
(1)暴力解法
int i,j;
for(i=0;i<numsSize;i++)
{
if(nums[i]==val)
{
for(j=i;j<numsSize-1;j++)
{
nums[j]=nums[j+1]; // 数组的删除本质上是覆盖
}
numsSize--;
i--;
}
}
return numsSize;
解释:使用两个循环,外围循环找到要删除的元素,内围循环进行元素删除(覆盖)。
1. 删除元素在数组中
外围循环遍历整个数组,如果发现要删除的元素,即从此元素开始,将后面的元素都向前移动一位,覆盖此元素(numsSize-1是为了避免越界),不用管移动几位,反正下面从此元素位置开始,到最后一位移动。这样覆盖循环结束之后,元素少1个,所以数组长度需要-1;此时,原本要删除元素位置已经被后面元素覆盖,所以要从此元素开始继续下一次遍历,执行完一次循环后,下标i会+1,指向此元素下一个元素,所以为了从此元素开始,需要将下标减1(i--)最后返回数组长度。
2. 删除元素在数组元素最后一个
此时 j=numsSize-1的,但是要求 j<numsSize-1,不符合条件,不用移动,直接numSize-1,数组长度减一,然后跳出循环,返回数组长度,正好是删除最后一个元素的数组长度。
(2)双指针法
快慢指针法:
int removeElement(vector<int>& nums, int val) {
int slowIndex = 0;
for (int fastIndex = 0; fastIndex < nums.size(); fastIndex++) {
if (val != nums[fastIndex]) {
nums[slowIndex++] = nums[fastIndex];
}
}
return slowIndex;
}
};
通过快指针找到数组元素中,不删除的元素,慢指针找到要删除的元素,用快指针找到的元素覆盖慢指针的元素。
开始,快慢指针都是一样走的(遍历数组)nums[slowIndex++] = nums[fastIndex],第一次遍历,元素和要删除元素不一样,快指针指向的下标为0,快指针指向的下标赋值给慢指针后,慢指针指向下一个元素,然后快指针+1,即同时指向一个元素。当快指针指向的元素和要删除的元素一样,不执行给慢指针赋值,慢指针此时已经加1了,在上次赋值之后,所以也指向要删除元素。然后慢指针一直指向这个元素,直到快指针找到不需要删除元素,然后将慢指针指向的要删除元素用不需要删除元素覆盖,向前移动。从而达到删除目的。
注意:要删除的在最后一个元素上:直接不进入判断,返回慢指针下标!!
要删除元素在数组之间时,找到要删除元素后,慢指针不更新了,然后快指针王虎继续找不需要删除的元素,找到之后,即将要删除的覆盖,然后下一个元素不等于,就继续慢指针和快指针继续一起移动。最后是要删除的,慢指针不移动了,返回慢指针,即数组长度。
相向指针: