数组理论基础
文档讲解 : 代码随想录 - 数组理论基础
状态:再次回顾。
记录重点
- 数组是存放在连续内存空间上的相同类型数据的集合。
- 数组具有连续性,在内存空间的地址是连续的 --> 删除或者增添元素的时候,得移动其他元素地址
- 数组下标都是从0开始的。
- 数组内存空间的地址是连续的。
验证数组连续性代码
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
其他知识
C++基本数据类型所占的内存空间:
类型 | 32位系统 | 64位系统 |
---|---|---|
bool | 1 | 1 |
char | 1 | 1 |
short | 2 | 2 |
int | 4 | 4 |
long | 4 | 4 |
double | 8 | 8 |
float | 4 | 4 |
* | 4 | 8 |
704. 二分查找
文档讲解 : 代码随想录 - 704. 二分查找
状态:再次回顾,但是需要一段时间,问题:left = middle + 1时忘记 +1。
二分法前提条件
- 有序数组
- 数组中无重复元素 (因为一旦有重复元素,使用二分法返回的元素下标可能不是唯一的。)
二分法重点
想清楚区间定义,保持区间不变量
区间定义
左闭右闭代码(ACM) [left, right]
#include <iostream>
#include <vector>
using namespace std;
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 return middle; // nums[middle] == target,数组中找到目标值,直接返回下标
}
return -1;
}
int main() {
/*
输入描述:
第一行一个整数,表示nums数组长度
第二行n个数,第i个数表示nums[i]
第三行一个整数,表示target
*/
int n;
cin >> n;
vector <int> nums(n);
int target;
for (int i = 0; i < n; i++) cin >> nums[i];
cin >> target;
cout << search(nums, target);
return 0;
}
左闭右开 (ACM) [left, right)
#include <iostream>
#include <vector>
using namespace std;
int search(vector<int>& nums, int target) {
int left = 0;
int right = nums.size(); // 定义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; // target 在左区间, 所以在[left, middle)中
}
else if (nums[middle] < target) {
left = middle + 1; // 易错点,无论是左闭右闭还是左闭右开,target都在右区间, 所以在[middle + 1, right)中
}
else return middle; // nums[middle] == target,数组中找到目标值,直接返回下标
}
return -1;
}
int main() {
/*
输入描述:
第一行一个整数,表示nums数组长度
第二行n个数,第i个数表示nums[i]
第三行一个整数,表示target
*/
int n;
cin >> n;
vector <int> nums(n);
int target;
for (int i = 0; i < n; i++) cin >> nums[i];
cin >> target;
cout << search(nums, target);
return 0;
}
总结
注意自己使用的是什么区间,在循环中坚持根据查找区间的定义来做边界处理,就是循环不变量规则。
27. 移除元素
文档讲解 : 代码随想录 - 27. 移除元素
状态:再次回顾。
验证前文所说数组的元素在内存地址中是连续的,不能单独删除数组中的某个元素,只能覆盖。
本题是双指针入门经典题目,需要多次进行思考和练习。
双指针法
双指针法 (快慢指针法):通过一个快指针和慢指针在一个for循环下完成两个for循环的工作。
定义快慢指针
- 快指针:寻找新数组的元素,新数组就是不含有目标元素的数组
- 慢指针:指向更新,更新新数组下标的位置
本题删除过程如下:
本题代码(ACM)
#include <iostream>
#include <vector>
using namespace std;
int removeElement(vector <int>& nums, int val){
int slowIndex = 0;
for (int fastIndex = 0; fastIndex < nums.size(); fastIndex++) {
if (nums[fastIndex] != val) {
nums[slowIndex++] = nums[fastIndex]; // 注意fastIndex++已经在for循环内完成,是先赋值然后再++
}
}
return slowIndex;
}
int main() {
/*
输入描述:
第一行一个整数,表示nums数组长度
第二行n个整数,第i个数表示nums[i]
第三行一个整数,表示移除元素val
*/
int n;
cin >> n;
vector<int> nums(n);
int val;
for (int i = 0; i < n; i++) cin >> nums[i];
cin >> val;
cout << removeElement(nums, val) << endl;
return 0;
}