在c++中,数组是连续进行存储的,数组的学习过程中需要注意以下的两点:
不同的编程语言,二维数组的空间的连续性是不同的,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], 区间为左闭右闭,我们下面的操作都是基于此。
方案二:
定义边界[left, right), 区间为左闭右闭,我们下面的操作都是基于此。
如果说定义 target 是在一个在左闭右开的区间里,也就是[left, right) ,那么二分法的边界处理方式则截然不同。
有如下两点:
代码:
方案一:
#include
#include
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
#include
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
#include
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
#include
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)
#include
#include
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;
}
参考:
原网址:代码随想录