【代码随想录打卡day1---数组】
数组的理论基础
在c++中,数组是连续进行存储的,数组的学习过程中需要注意以下的两点:
- 数组是进行连续存储的
- 数组的下标是从0进行开始的
不同的编程语言,二维数组的空间的连续性是不同的,c++的二维数组的空间存储是连续的,而java就不是连续的了。
可以通过以下的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();
}
二分查找
接下来以题目进行分析,下面的题目是来自于leetcode中的第704题。
【题目描述】
给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。
示例 1:
输入: nums = [-1,0,3,5,9,12], target = 9
输出: 4
解释: 9 出现在 nums 中并且下标为 4
示例 2:
输入: nums = [-1,0,3,5,9,12], target = 2
输出: -1
解释: 2 不存在 nums 中因此返回 -1
提示:
1.你可以假设 nums 中的所有元素是不重复的。
2.n 将在 [1, 10000]之间。
3.nums 的每个元素都将在 [-9999, 9999]之间。
思路分析:
解决二分法的问题,最关键的是弄清出边界情况,本题目的边界,无疑就是while(left < right) OR while(left <= right), 以及更新时候的操作。为了解决这些问题,我们需要找到循环不变量,本题目中的循环不变量就是left 和 right,我们在解题的最一开始就定义好边界就不会出现这些问题了。
方案一:
定义边界[left, right], 区间为左闭右闭,我们下面的操作都是基于此。
- while(left <= right) 要加上=,因为left == right是有意义的,所以要使用left <= right
- if (nums[middle] > target) right 要赋值为 middle - 1,因为当前这个nums[middle]一定不是target,那么接下来要查找的左区间结束下标位置就是 middle - 1
方案二:
定义边界[left, right), 区间为左闭右闭,我们下面的操作都是基于此。
如果说定义 target 是在一个在左闭右开的区间里,也就是[left, right) ,那么二分法的边界处理方式则截然不同。
有如下两点:
- while (left < right),这里使用 < ,因为left == right在区间[left, right)是没有意义的
- if (nums[middle] > target) right 更新为 middle,因为当前nums[middle]不等于target,去左区间继续寻找,而寻找区间是左闭右开区间,所以right更新为middle,即:下一个查询区间不会去比较nums[middle]
代码:
方案一:
#include<iostream>
#include<vector>
using namespace std;
const int maxn = 1e6 + 10;
vector<int> nums;int search(vector<int> &nums, int target) {int left = 0;int right = nums.size() - 1; // 定义左闭右闭[left, right] while (left <= right) { // 当left = right时, [left, right]仍然有效, 所以可以使用left <= right int mid = left + (right - left) / 2; // 防止溢出,等价于left + right / 2 if (nums[mid] < target) {left = mid + 1;}else if (nums[mid] > target) {right = mid - 1;}else {return mid;}}
}
int main() {int n, q, target;cin >> n;cin >> target;for (int i = 0; i <= n - 1; i ++) {cin >> q;nums.push_back(q);}int result = search(nums, target);cout << "最终的目标值为:" << result << endl;return 0;
}
方案二:
#include<iostream>
#include<vector>
using namespace std;
const int maxn = 1e6 + 10;
vector<int> nums;int search(vector<int> &nums, int target) {int left = 0;int right = nums.size(); // 定义左闭右闭[left, right] while (left < right) { // 当left = right时, [left, right]仍然有效, 所以可以使用left <= right int mid = left + (right - left) / 2; // 防止溢出,等价于left + right / 2 if (nums[mid] < target) {left = mid + 1;}else if (nums[mid] > target) {right = mid;}else {return mid;}}
}
int main() {int n, q, target;cin >> n;cin >> target;for (int i = 0; i <= n - 1; i ++) {cin >> q;nums.push_back(q);}int result = search(nums, target);cout << "最终的目标值为:" << result << endl;return 0;
}
移除元素
力扣的链接:https://leetcode.cn/problems/remove-element/description/
题目的描述:
给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。
不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并原地修改输入数组。
元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
示例 1: 给定 nums = [3,2,2,3], val = 3, 函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。 你不需要考虑数组中超出新长度后面的元素。
示例 2: 给定 nums = [0,1,2,2,3,0,4,2], val = 2, 函数应该返回新的长度 5, 并且 nums 中的前五个元素为 0, 1, 3, 0, 4。
你不需要考虑数组中超出新长度后面的元素。
思路的分析:
两种思路:暴力求解和双指针算法
方案一:暴力求解
这个题目暴力的解法就是两层for循环,一个for循环遍历数组元素 ,第二个for循环更新数组。
删除过程如下:(下图引用于代码随想录,网址:https://www.programmercarl.com/0027.%E7%A7%BB%E9%99%A4%E5%85%83%E7%B4%A0.html#%E7%AE%97%E6%B3%95%E5%85%AC%E5%BC%80%E8%AF%BE)

暴力求解
暴力求解:
// 时间复杂度:O(n^2)
// 空间复杂度:O(1)
#include<iostream>
#include<vector>
using namespace std;
const int maxn = 1e6 + 10;
vector<int> nums;
int n, q, target;
int main() {cin >> n;cin >> target;for (int i = 0; i <= n - 1; i ++) {cin >> q;nums.push_back(q);}int size = nums.size();for (int i = 0; i <= size - 1; i ++) {if (nums[i] == target) { // 发现需要移除的元素,就将数组集体向前移动一位for (int j = i + 1; j <= size - 1; j ++) {nums[j - 1] = nums[j];}i --; // 因为下标i以后的数值都向前移动了一位,所以i也向前移动一位size --; // 此时数组的大小-1}}for (int i = 0; i <= size - 1; i ++) {cout << nums[i] << " ";}cout << endl;cout << "更新过后的数组长度为:" << size << endl;return 0;
}
方案二:双指针算法
// 时间复杂度:O(n)
// 空间复杂度:O(1)
双指针法(快慢指针法)
没有改变元素的相对位置
#include<iostream>
#include<vector>
using namespace std;
const int maxn = 1e6 + 10;
vector<int> nums;
int n, q, target;
int main() {cin >> n;cin >> target;for (int i = 0; i <= n - 1; i ++) {cin >> q;nums.push_back(q);}int size = nums.size();for (int fastIndex = 0, lowIndex = 0; fastIndex <= size - 1; fastIndex ++) {if (nus[fastIndex] != target) {nums[lowIndex ++] = nums[fastIndex];}}for (int i = 0; i <= size - 1; i ++) {cout << nums[i] << " ";}cout << endl;cout << "更新过后的数组长度为:" << size << endl;return 0;
}
相向的双指针算法
/**
- 相向双指针方法,基于元素顺序可以改变的题目描述改变了元素相对位置,确保了移动最少元素
- 时间复杂度:O(n)
- 空间复杂度:O(1)
*/
// 时间复杂度:O(n)
// 空间复杂度:O(1)
#include<iostream>
#include<vector>
using namespace std;
const int maxn = 1e6 + 10;
vector<int> nums;
int n, q, target;
int main() {cin >> n;cin >> target;for (int i = 0; i <= n - 1; i ++) {cin >> q;nums.push_back(q);}int left = 0, right = nums.size() - 1;while (left <= right) {while(left <= right && nums[left] != target) ++left; // 找左边等于val的元素while(left <= right && nums[right] == target) --right; // 找右边不等于val的元素if (left < right) nums[left ++] = nums[right --]; // 将右边不等于val的元素覆盖左边等于val的元素}int size = left; // leftIndex一定指向了最终数组末尾的下一个元素for (int i = 0; i <= size - 1; i ++) {cout << nums[i] << " ";}cout << endl;cout << "更新过后的数组长度为:" << size << endl;return 0;
}
参考:
原网址:代码随想录