第一天|数组理论基础、704.二分查找、27.移除元素

1. 数组理论基础

卡哥建议:了解一下数组基础,以及数组的内存空间地址,数组也没那么简单。

1.搞清楚数组在内存中的存储方式

数组是存放在连续内存空间上的相同类型数据的集合
数组下标都是从0开始的
数组内存空间的地址是连续的

因为数组的在内存空间的地址是连续的,所以我们在删除或者增添元素的时候,就难免要移动其他元素的地址。
而数组的元素是不能删的,只能覆盖。

C++中,要注意vector 和 array的区别,vector的底层实现是array,严格来讲vector是容器,不是数组。
在C++中二维数组在地址空间上是连续的。
而Java二维数组每一行的头结点地址没有规则,不连续,所以二维数组在地址空间上不连续

这部分比较简单
接下来自己拓展一下vector的用法,工欲善其事必先利其器,好久没有刷题,最基本的STL都生疏(烂完 )了,浅浅复习一下~

2.关于vector

vector是向量类型,可以容纳许多类型的数据,因此也被称为容器
(可以理解为动态数组,是封装好了的类),进行vector操作前应添加头文件#include <vecotr>

vector的初始化
vector<int>a(10);
vector<int>a(10,1);
vector<int>a(b);
vector<int>a(b.begin(),b.begin+3);

int b[7]={1,2,3,4,5,6,7};
vector<int> a(b,b+7;
vector的功能
#include<vector>
vector<int> a,b;
//b为向量,将b的0-2个元素赋值给向量a
a.assign(b.begin(),b.begin()+3);
//a含有4个值为2的元素
a.assign(4,2);
//返回a的最后一个元素
a.back();
//返回a的第一个元素
a.front();
//返回a的第i元素,当且仅当a存在
a[i];
//清空a中的元素
a.clear();
//判断a是否为空,空则返回true,非空则返回false
a.empty();
//删除a向量的最后一个元素
a.pop_back();
//删除a中第一个(从第0个算起)到第二个元素,也就是说删除的元素从a.begin()+1算起(包括它)一直到a.begin()+3(不包括它)结束
a.erase(a.begin()+1,a.begin()+3);
//在a的最后一个向量后插入一个元素,其值为5
a.push_back(5);
//在a的第一个元素(从第0个算起)位置插入数值5,
a.insert(a.begin()+1,5);
//在a的第一个元素(从第0个算起)位置插入3个数,其值都为5
a.insert(a.begin()+1,3,5);
//b为数组,在a的第一个元素(从第0个元素算起)的位置插入b的第三个元素到第5个元素(不包括b+6)
a.insert(a.begin()+1,b+3,b+6);
//返回a中元素的个数
a.size();
//最常用功能,返回a在内存中总共可以容纳的元素个数
a.capacity();
//将a的现有元素个数调整至10个,多则删,少则补,其值随机
a.resize(10);
//将a的现有元素个数调整至10个,多则删,少则补,其值为2
a.resize(10,2);
//将a的容量扩充至100,
a.reserve(100);
//b为向量,将a中的元素和b中的元素整体交换
a.swap(b);
//b为向量,向量的比较操作还有 != >= > <= <
a==b;
vector的顺序访问
vector<int>a;
for(int i=0;i<10;++i){a.push_back(i);}

int a[6]={1,2,3,4,5,6};
vector<int> b;
for(int i=0;i<=4;++i){b.push_back(a[i]);}

int a[6]={1,2,3,4,5,6};
vector<int>b;
vector<int>c(a,a+4);
for(vector<int>::iterator it=c.begin();it<c.end();++it)
{
	b.push_back(*it);
}

ifstream in("data.txt");
vector<int>a;
for(int i;in>>i){a.push_back(i);}

//错误方法哦
vector<int>a;
for(int i=0;i<10;++i){a[i]=i;}//下标只能用来获取已经存在的元素

int a[6]={1,2,3,4,5,6};
vector<int>b(a,a+4);
for(int i=0;i<=b.size()-1;++i){cout<<b[i]<<endl;}

 int a[6]={1,2,3,4,5,6};
 vector<int>b(a,a+4);
 for(vector<int>::iterator it=b.begin();it!=b.end();it++){cout<<*it<<"  ";}
vector相关算法
 #include<algorithm>
 //对a中的从a.begin()(包括它)到a.end()(不包括它)的元素进行从小到大排列
 sort(a.begin(),a.end());
 //对a中的从a.begin()(包括它)到a.end()(不包括它)的元素倒置,但不排列,如a中元素为1,3,2,4,倒置后为4,2,3,1
 reverse(a.begin(),a.end());
  //把a中的从a.begin()(包括它)到a.end()(不包括它)的元素复制到b中,从b.begin()+1的位置(包括它)开始复制,覆盖掉原有元素
 copy(a.begin(),a.end(),b.begin()+1);
 //在a中的从a.begin()(包括它)到a.end()(不包括它)的元素中查找10,若存在返回其在向量中的位置
  find(a.begin(),a.end(),10);

2. LeetCode 704.二分查找

卡哥建议:了解一下数组基础,以及数组的内存空间地址,数组也没那么简单
墨迹两句,因为这道题我印象是特别深的,大概两年多前,大二时候学校上课讲算法和数据结构,因为我提前学过,感觉自己无敌了(实际上也没有系统学过,只是纯应付),然后就上手做了这个题,然后发现自己写不明白,二分查找语言描述原理就那么回事,很简单,然后上手发现烂完了,那时候感触还是挺深的,对自己眼高手低有了一个更具体的体会,最后应该是写了蛮久过了这道题(也可能没过是抄的)
两年过去了,眼高手低这个毛病我一直都还留着,算法这块也没有继续去学,看看目前水平咋样
还行,第一次测试编译错误,第二次测试对了提交然后一遍过

class Solution {
public:
    int search(vector<int>& nums, int target) {
        int left=0;
        int right=nums.size()-1;
        int mid;
        while(left<=right){
            mid=(left + right)/2;
            if(nums[mid] <target){
                //mid除出来是小数咋处理?向下还是向上取整来着了?
                left = mid +1;
            }else if(nums[mid]>target){
                right = mid - 1;
            }else{
                return mid;
            }
        }
        return -1;
    }
};

写这个代码时候心里真在想是小数咋办,纯粹是烂完了,重开吧

C++中使用Int型计算,并且赋值给Int型变量,将会舍弃小数位,不是四舍五入,是直接舍弃小数部分,如5.1 将赋值为5,5.9也会赋值为5。

然后看卡哥写的还是比较有收获的

二分查找的前提条件

(一)数组为有序数组
(二)数组中无重复元素
因为一旦有重复元素,使用二分查找法返回的元素下标可能不是唯一的
卡哥这块讲的确实细

二分查找的边界条件

二分查找涉及的很多的边界条件,逻辑比较简单,但就是写不好。例如到底是 while(left < right) 还是 while(left <= right),到底是right = middle呢,还是要right = middle - 1呢?
这个倒是不难,毕竟以前学过四舍五入还算打过ACM虽然现在烂完了连int都忘了
区间问题在脑子里模拟一下边界条件就行

Leetcode 35.

秒了,但有点云,最后那个return最开始是return的mid,发现测试没有过,改成的return left + (right - left)/2,这个在脑子里没模拟明白,知道是不符合条件之后因为没进去while所以没更新mid,但上一个mid咋不成呢?让脑子转转,这个问题没想清,可能有点犯困了,反正过了嘛,面向OJ编程 还是要想清楚

class Solution {
public:
    int searchInsert(vector<int>& nums, int target) {
        int left =0;
        int right = nums.size();
        int mid;
        while(left < right){
            mid = left + (right - left)/2;
            if(nums[mid] > target){
                right = mid;
            }else if(nums[mid] < target){
                left = mid + 1;
            }else{
                return mid;
            }
        }
        return left + (right - left)/2;;
    }
};
LeetCode 367.

卡了一下,在这个大数问题上,然后秒了
不算难,没考虑大数,之后要留意,这个题也算拓展一下视野了,如果不是在二分刷到这题,做这题可能想不到用二分的~

class Solution {
public:
    bool isPerfectSquare(int num) {
        int left = 1;
        int right = num;
        long long mid;
        while(left <= right){//写这个边界时候脑子里模拟num=1就行
            mid = left + (right - left)/2;
            if(mid*mid > num){ //这个mid*mid超出int边界了,改成longlong就过了
                right = mid - 1;
            }else if(mid*mid < num){
                left = mid + 1;
            }else{
                return true;
            }
        }
        return false;
    }
};

3. LeetCode 27.移除元素

卡哥建议:暴力的解法,可以锻炼一下我们的代码实现能力,建议先把暴力写法写一遍。 双指针法 是本题的精髓,今日需要掌握,至于拓展题目可以先不看。

我的解法

这道题烂完了,被21年的自己爆了啊,21年能做出来的题(看了21年的提交,确实用的还是快慢指针,好兄弟),今天纯纯面向测试用例编程,还疯狂的调试,烂完了啊…
被爆了
下面这是今天最终提交通过的代码,基本一个边界测试用例,一版代码,写的非常云,测试用例怎么编我就怎么编,译验丁真——鉴定为面向测试用例与调试器编程

class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        int i=0;
        int j=nums.size()-1;
        if(j<0) return 0;
        //这里的改进用来解决测试用例[]
        while(i<j){
            if(nums[i]==val){
                while(nums[j]==val&&j>i+1) j--;
                //这个&&后面的是面向测试用例[3,3]
                int tmp = nums[i];
                nums[i] = nums[j];
                nums[j] = tmp;
                j-=1;
            }
            i+=1;
        }
        return nums[j]==val?j:j+1;
        //从numsize-1-j改到j改到j+1,j+1过了测试[1]但不通过
        //面向测试用例编程改成nums[j]==val?j:j+1
        //然后这个用例过了,然后另一个测试用例[]超出边界了,评价是寄
    }
};
卡哥暴力法

暴力
有一说一,卡哥动画做的确实好,一眼懂

卡哥双指针法

双指针
双指针法(快慢指针法): 通过一个快指针和慢指针在一个for循环下完成两个for循环的工作。
快指针:寻找新数组的元素 ,新数组就是不含有目标元素的数组
慢指针:指向更新 新数组下标的位置
双指针法(快慢指针法)在数组和链表的操作中是非常常见的,很多考察数组、链表、字符串等操作的面试题,都使用双指针法。

卡哥的双指针代码
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];
            }
        }
        return slowIndex;
    }
};

class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        int leftIndex = 0;
        int rightIndex = nums.size() - 1;
        while (leftIndex <= rightIndex) {
            // 找左边等于val的元素
            while (leftIndex <= rightIndex && nums[leftIndex] != val){
                ++leftIndex;
            }
            // 找右边不等于val的元素
            while (leftIndex <= rightIndex && nums[rightIndex] == val) {
                -- rightIndex;
            }
            // 将右边不等于val的元素覆盖左边等于val的元素
            if (leftIndex < rightIndex) {
                nums[leftIndex++] = nums[rightIndex--];
            }
        }
        return leftIndex;   // leftIndex一定指向了最终数组末尾的下一个元素
    }
};

all right
今天差不多到这里,先歇了

  • 22
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值