算法训练第一天|数组理论基础,704. 二分查找,27. 移除元素

数组理论基础

数组是存放在连续内存空间上的相同类型数据的集合。

  • 数组下标都是从0开始的。
  • 数组内存空间的地址是连续的。
  • 数组的元素是不能删的,只能覆盖。
  • C++注意区别array和vector。

 数组第 i 个元素与第 j 个元素间的内存地址(假设j > i)相差: (j - i)* 元素数据类型字节数。如int arr[],元素数据类型字节数为4;short arr[],元素数据类型字节数为2,以此类推。

二维数组的存储顺序是按行来进行的。

C++测试代码如下:

void test_arr() {
    int array[2][3] = {
		{0, 1, 2},
		{3, 4, 5}
    };
    cout << &array[0][0] << " " << &array[0][1] << " " << &array[0][2] << endl;
    cout << &array[1][0] << " " << &array[1][1] << " " << &array[1][2] << endl;
}

int main() {
    test_arr();
}

测试结果:

0x7ffee4065820 0x7ffee4065824 0x7ffee4065828
0x7ffee406582c 0x7ffee4065830 0x7ffee4065834

二分查找

力扣链接

题目描述:给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target  ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。

 二分查找前提:数组为有序。

二分查找的思想是使用不断缩小的区间(以2为倍率缩小区间,提高查找效率)来确定所查找元素的位置。

左闭右闭写法:[left, right], 初始时left = 0,right = nums.size() - 1(最后一个元素的下标)。middle = ( right - left )/ 2 + left,循环条件为 while(left <= right) 。

  • 如果nums[middle] > target, 则target在[ left, middle-1 ]区间内,所以将right更新为middle-1;
  • 如果nums[middle] < target, 则target在[ middle+1, right ]区间内,所以将left更新为middle+1;
  • 如果nums[middle] == target, 则找到了target,返回target所在的坐标middle。
class Solution {
public:
    int search(vector<int>& nums, int target) {
        int left = 0;
        int right = nums.size() - 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; // 数组中找到目标值,直接返回下标
            }
        }
        // 未找到目标值
        return -1;
    }
};

移除元素

力扣链接

题目描述:

给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。

不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组

元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。

方法一:暴力解法

class Solution {
public:
       int removeElement(vector<int>& nums, int val) {
           //暴力法
           int size = nums.size();
           for(int i=0; i<size; i++)
           {
               if(nums[i] == val)
               {
                   for(int j=i+1; j<size; j++)
                   {
                        nums[j-1] = nums[j];
                   }
                   size--;
                   i--;
               } 
                //size--;  犯的错误
                //  i--;         
            }
           return size;
    }
};

用变量size记录数组nums的长度。变量i从前往后一次遍历每一个元素,如果该元素等于val,则使用第二个for循环将val后面的所有元素依次向前移动一位,覆盖掉前面一个元素的值,此时最后一个元素在内存中仍然存在,但是我们还将它往前覆盖了一份,所以最后的这个元素就不用再访问了,相当于size总长度减一。所以有一个size--。

注意,此时 i 处的val被新值覆盖后,i处的值是原来i+1位置的元素的值,还没有和val判断是否相等,如果进入下一轮i++后,该值将被忽略,所以在此处有一个 i--,使得即使进入下一轮外循环,和val进行比较的仍是i处的新值。

自己犯的错误:将size--和i--写在了代码中注释的位置,if语句之外。显然不正确。

方法二:快慢指针

定义快慢指针

  • 快指针:寻找新数组的元素 ,新数组就是不含有目标元素的数组
  • 慢指针:指向更新 新数组下标的位置

快指针的任务是去遍历数组nums中的每一个数字,查看是否等于val。

满指针的任务是记录调整后新元素的下标。

在使用快指针遍历nums时,执行的一个很重要的基础操作就是快指针处的值将慢指针处的值覆盖掉,然后慢指针++。

  • 若nums[ fastIndex ] 不等于val,快指针处的值将慢指针处的值覆盖掉,快慢指针均++;
  • 若nums[ fastIndex ] 等于val,只有快指针++,跳过该元素,准备用后面的元素将其覆盖。

该方法很巧妙。。。 

class Solution {
public:
    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];
                slowIndex++;
            }
        }
        return slowIndex;
    }
};
  • 17
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值