数组理论基础、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
今天差不多到这里,先歇了