704.二分查找
给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。
首先,假设数组元素大从大到小是升序的。
一开始,我拿到题目,觉得这个题目很简单,但是,编写代码过程中,遇到两个问题
(1)如何求解数组的长度?
(2)“nums[middle]<target”还是“nums[middle]<=target”,它们之间存在什么区别?
以下是我的代码。我采用闭区间的方案。
#include<iostream>
#include<string>
#include<cmath>
#include<vector>
using namespace std;
//template<typename T, size_t N>
//size_t getArrayLength(T(&)[N])
//{
// return N;
//}
//
//int Get_length_array(int array[]) {
//
//}
class arraysolution {
public:
//闭区间方案
int half_find(vector<int> &sum, int target) { //在函数参数中,数组类型应该包含数组元素的类型和数组的维度。例如,int array[] 或 int* array 表示传递一个整数类型的一维数组作为函数参数。
//int half_find(int sum[], int target,int len) { //在函数参数中,数组类型应该包含数组元素的类型和数组的维度。例如,int array[] 或 int* array 表示传递一个整数类型的一维数组作为函数参数。
//int arr[] = { 1, 2, 3, 4, 5 };
//int len = sizeof(sum) / sizeof(sum[0]);//在 32 位架构下,int 类型的大小通常为 4 字节。因此,一个 int 类型的数组 arr[N] 在内存中占用的字节数为 N * sizeof(int),即数组长度乘以元素类型的大小。对于 int arr[10],它占用的空间大小为 40 字节。
//int len = getArrayLength(sum);
//当将数组作为参数传递给函数时,由于数组名被转换为指向第一个元素的指针,因此实际上传递给函数的是一个指向 int 类型的指针。在函数内部接收数组参数的形参也需要声明为指针类型,例如 int* arr。因此,在函数内部使用 sizeof(arr) 得到的结果是指针变量的大小(通常为 4 字节),而不是整个数组所占用的空间大小。如果需要在函数内部获取数组长度,可以采用先将数组长度作为另一个参数进行传递的方式,或者使用模板函数和指针等技术。
//
//typedef std::array<int,4> Myarray;
// Myarray c0 = { 0,1,2,3 };
//
display contents " 0 1 2 3"
// for (const auto& it : c0) {
// std::cout << " u << it;
// }
// std::cout << std::endl!
// // display size " 4"std::cout << u << c0.size();std::cout << std::endl;
// return (0);
int left = 0;
int right = sum.size()-1;//size不能用于返回数组长度;size可用于字符串等
//int right = len - 1;
while (sum[left] <= sum[right]) {
int middle = int((left + right) / 2);
if (sum[middle] < target) {//为啥这里<=呢?
left = middle + 1;
}
else if (sum[middle] > target) {
right = middle - 1;
}
else {
return middle;
}
//cout << middle;
}
return -1;
}
};
int main() {
/*int i = 0
cin >> array[i] >> " ";
i++;*/
arraysolution s1;
//int array[9] = { 0,2,3,4,5,6,7,8,9 };
vector<int> sums= { 0,2,3,4,5,6,7,8,9 };
int target = 9;
int result = s1.half_find(sums, target);
cout << result;
return 0;
}
运行结果如下:
其实,数组操作没有.size()求长度;而且,数组做函数参数时,仅仅是以指针的方式传递第一个节点,当将数组作为参数传递给函数时,由于数组名被转换为指向第一个元素的指针,因此实际上传递给函数的是一个指向 int 类型的指针。在函数内部接收数组参数的形参也需要声明为指针类型,例如 int* arr。因此,在函数内部使用 sizeof(arr) (sizeof函数是获得)得到的结果是指针变量的大小(通常为 4 字节),而不是整个数组所占用的空间大小。如果需要在函数内部获取数组长度,可以采用先将数组长度作为另一个参数进行传递的方式,或者使用模板函数和指针等技术。如果采用下面的sizeof()函数;
int arr[] = { 1, 2, 3, 4, 5 };
int len = sizeof(sum) / sizeof(sum[0]);
无论输入什么测试数组,都会输出第二个元素数值,这是因为,int len始终等于2(在 64 位架构下,int
类型的大小通常为 4 字节,而指针的大小通常为 8 字节。)如下:
#include<iostream>
#include<string>
#include<cmath>
#include<vector>
using namespace std;
//template<typename T, size_t N>
//size_t getArrayLength(T(&)[N])
//{
// return N;
//}
//
//int Get_length_array(int array[]) {
//
//}
class arraysolution {
public:
//闭区间方案
int half_find(vector<int> &sum, int target) { //在函数参数中,数组类型应该包含数组元素的类型和数组的维度。例如,int array[] 或 int* array 表示传递一个整数类型的一维数组作为函数参数。
//int half_find(int sum[], int target,int len) { //在函数参数中,数组类型应该包含数组元素的类型和数组的维度。例如,int array[] 或 int* array 表示传递一个整数类型的一维数组作为函数参数。
//int arr[] = { 1, 2, 3, 4, 5 };
int len = sizeof(sum) / sizeof(sum[0]);//在 32 位架构下,int 类型的大小通常为 4 字节。因此,一个 int 类型的数组 arr[N] 在内存中占用的字节数为 N * sizeof(int),即数组长度乘以元素类型的大小。对于 int arr[10],它占用的空间大小为 40 字节。
//int len = getArrayLength(sum);
//当将数组作为参数传递给函数时,由于数组名被转换为指向第一个元素的指针,因此实际上传递给函数的是一个指向 int 类型的指针。在函数内部接收数组参数的形参也需要声明为指针类型,例如 int* arr。因此,在函数内部使用 sizeof(arr) 得到的结果是指针变量的大小(通常为 4 字节),而不是整个数组所占用的空间大小。如果需要在函数内部获取数组长度,可以采用先将数组长度作为另一个参数进行传递的方式,或者使用模板函数和指针等技术。
//
//typedef std::array<int,4> Myarray;
// Myarray c0 = { 0,1,2,3 };
//
display contents " 0 1 2 3"
// for (const auto& it : c0) {
// std::cout << " u << it;
// }
// std::cout << std::endl!
// // display size " 4"std::cout << u << c0.size();std::cout << std::endl;
// return (0);
int left = 0;
//int right = sum.size()-1;//size不能用于返回数组长度;size可用于字符串等
int right = len - 1;
while (sum[left] <= sum[right]) {
int middle = int((left + right) / 2);
if (sum[middle] < target) {//为啥这里<=呢?
left = middle + 1;
}
else if (sum[middle] > target) {
right = middle - 1;
}
else {
return middle;
}
//cout << middle;
}
return -1;
}
};
int main() {
/*int i = 0
cin >> array[i] >> " ";
i++;*/
arraysolution s1;
//int array[9] = { 0,2,3,4,5,6,7,8,9 };
vector<int> sums= { 0,2,3,4,5,6,7,8,9 };
int target = 9;
int result = s1.half_find(sums, target);
cout << result;
return 0;
}
同时,也有朋友建议,可以使用数据结构里的顺序表。
第二个问题,我的理解如下:
如图所示,使用左闭右闭的方案:
假设target=2,第一次迭代,middle对应的值小于target,如果我们取“>”,这就代表在【middle,right】中不存在target,则right=middle-1;相应的,如果middle对应的元素值大于target,则【left,middle】中都不包含target,则left=middle+1.
注意:在左闭右闭区间内,left=right是有意义的,所以要写“left<=right”
下面看一下,左闭右开的情况
在左闭右开区间内,left=right是无意义的,所以要写“left<right”,另外,right代表的左边界是虚的,没有元素,所以在缩进边界时,牵扯到right,就不用middle-1了,左边界不用动。
下面是左闭右开的代码:
while (left < right) {
int middle = int((left + right) / 2);
if (sum[middle] < target) {//为啥这里<=呢?
left = middle + 1;
}
else if (sum[middle] > target) {
right = middle;
}
else {
return middle;
}
//cout << middle;
}
运行结果:
27.移除元素
给你一个数组 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。
暴力解法的时间复杂度为
// 时间复杂度:O(n^2)
// 空间复杂度:O(1)
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
int size = nums.size();
for (int i = 0; i < size; i++) {
if (nums[i] == val) { // 发现需要移除的元素,就将数组集体向前移动一位
for (int j = i + 1; j < size; j++) {
nums[j - 1] = nums[j];
}
i--; // 因为下标i以后的数值都向前移动了一位,所以i也向前移动一位
size--; // 此时数组的大小-1
}
}
return size;
}
};
所以,使用双指针(快慢指针)来解决这个问题,可以降低时间复杂度。
定义一个快指针和一个慢指针,快指针向前寻找元素值,慢指针紧跟其后储存新的数组元素值,当快指针寻找到目标元素时,慢指针暂停,快指针将下一个元素赋给慢指针,从而删除目标元素值,注意:数组和链表还不一样,数组元素值是连续寻址的,删除的实质是通过覆盖实现,链表是通过重新建立指针,回收旧址实现的。
#include<iostream>
#include<vector>
#include<cmath>
#include<string>
using namespace std;
class ArraySolution {
public:
int RemoveArrayelement(vector<int> array,int target) {
int* fast = &array[0];
//int* slow = &array[0];
std::vector<int>::iterator it();
while (std::vector<int>::iterator it != array.end())
// for (std::vector<int>::iterator it=array.begin(); it != array.end(); it++) {//在这里,it代表容器array里的元素值,相当于for循环里的数组元素索引,而不是for循环里的数组索引,使用it!=array.size()就会报错:
// //在这里,array.end()代表容器array里的最后一个元素
// //array.begin()代表容器array里的第一个元素
// if (*it != target) {
//
// slow++;
// }
// else if(*it == target) {
// slow = *it;
// }else {
// return array;
// }
// }
// return array;
//int fast = 0;
int slow = 0;
for (int fast = 0; fast < array.size(); fast++) {
if (target != array[fast]) {
array[slow++] = array[fast];
}
}
return slow;
}
};
int main() {
ArraySolution s1;
vector<int>sums = { 1,2,4,4,5,6,4,8,9 };
int target = 4;
int result = s1.RemoveArrayelement(sums, target);
cout << result;
}