数据结构(1)二分法,移除元素

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了,在上次赋值之后,所以也指向要删除元素。然后慢指针一直指向这个元素,直到快指针找到不需要删除元素,然后将慢指针指向的要删除元素用不需要删除元素覆盖,向前移动。从而达到删除目的。

注意:要删除的在最后一个元素上:直接不进入判断,返回慢指针下标!!

要删除元素在数组之间时,找到要删除元素后,慢指针不更新了,然后快指针王虎继续找不需要删除的元素,找到之后,即将要删除的覆盖,然后下一个元素不等于,就继续慢指针和快指针继续一起移动。最后是要删除的,慢指针不移动了,返回慢指针,即数组长度。

相向指针:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值