数组:理论基础、二分查找、移除元素

数组

2.1理论基础

数组是连续存放在内存空间上的相同类型数据的集合

  • 数组下标从0开始
  • 数组内存空间联系
  • 数组元素是不能删的,只能覆盖

vector底层实现是array,但不是数组是容器
C++中二维数组内存是连续的

2.2二分查找(最简单前提:数组为左右闭区间,有序且无重复元素)

  • 例题1:给定一个n个元素有序的(升序)整型数组nums和一个目标值target,写出一个函数搜索nums中的target,如果目标存在返回1,否则返回-1.
    解法1:C++的自定义数组解法
    #include <iostream>
    using namespace std;
     int erFen(int a[],int n, int t) {
		int mid, right = n - 1, left = 0;
		while (left != right)
		{
			mid = (right + left) / 2;
			if (a[mid] == t)
			{
				return mid;
			}
			else if (a[mid] < t)
			{
				left = mid + 1;
			}
			else if (a[mid] > t)
			{
				right = mid - 1;
			}				
			}
		return -1;
	}
int main() {
	int n,result;
	cin >> n;
	int* nums = new int[n];
	cout << "please input the array:" << endl;
	int i = 0, target;
	for (i = 0; i < n; i++)
	{
		cin >> nums[i];
	}
	cout << "please input target:" << endl;
	cin >> target;
	result=erFen(nums,n, target);
	cout << result << endl;
	return 0;
}

解法2:力扣解题模式,只有关键代码

class Solution {
public:
    int search(vector<int>& nums, int target) {
int right=nums.size()-1,left=0;
while(left<=right)//因为在双闭区间内,所以left==right是有意义的
{
   int mid=(right+left)/2;//等同于(right-left)/2+left防止溢出
    if(nums[mid]==target)
    {
        return mid;
    }
    else if(nums[mid]<target)
    {
        left=mid+1;//在[left,right]闭区间内,因为处于mid的值一定不是target,所以下一个left是mid+1
    }
    else
    {
        right=mid-1;//同理,mid的值一定不是target,所以right=mid-1
    }
}
    return -1;
}
};
  • 写法二(数组为左闭右开区间)
class Solution {
public:
    int search(vector<int>& nums, int target) {
int right=nums.size();//与上个方法不同
int left=0;
while(left<right){//left==right没有意义
{
   int mid=(right+left)/2;//等同于(right-left)/2+left防止溢出
    if(nums[mid]==target)
    {
        return mid;
    }
    else if(nums[mid]<target)
    {
        left=mid+1;//在[left,right)区间内,因为处于mid的值一定不是target,所以下一个left是mid+1
    }
    else
    {
        right=mid;//target在左区间[left,middle)内,所以right=mid
    }
}
    return -1;
}
};
  • 例题2(35题):给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。请必须使用时间复杂度为 O(log n) 的算法。

①暴力解法:循环遍历数组是否有target,时间复杂度为O(n),空间复杂度为O(1)
②二分法(前提是有序数组,如果有重复元素则下标不唯一):左闭右闭区间(return left)、左闭右开区间(return right)

class Solution {
public:
    int searchInsert(vector<int>& nums, int target) {
	int left = 0, right = nums.size() - 1;
	while (left <= right)
	{
		int mid = (right - left) / 2 + left;
		if (nums[mid] == target)
		{
			return mid;
		}
		else if (nums[mid] > target)
		{
			right = mid - 1;
		}
		else
		{
			left = mid + 1;
		}
	}
	return left;//return right+1;是相同的效果
    }
};

2.3移除元素

  • 例题1(题目27):给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。
    不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。
    元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
    (数组中元素不能删去,只能覆盖)

①暴力解法:双循环,一层遍历数组,一层更新数组
双指针法:在一层循环中,快指针找到非删除元素,慢指针记录新数组位置,完成两层循环的作用

//时间复杂度O(n^2)
//空间复杂度O(1)
#include "p.h"
int Solution1::removeElement(vector<int>& nums, int val) {
	int i=0, len = 0, j;
	int n = nums.size();
	int k = 0;
	while (k < n)
	{
		j = i;
		if (nums[i] == val)
		{
			len++;
			while (j < n  - len)
			{
				nums[j] = nums[j + 1];
				j++;
			}
			nums[n - len] = val;
			k++;
		}
		else
		{
			i++;
			k++;
		}
	}
	return n - len;
}
// 时间复杂度:O(n)
// 空间复杂度:O(1)
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;
    }
};
  • 例题2(26题):给你一个 升序排列 的数组 nums ,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。
    由于在某些语言中不能改变数组的长度,所以必须将结果放在数组nums的第一部分。更规范地说,如果在删除重复项之后有 k 个元素,那么 nums 的前 k 个元素应该保存最终结果。
    将最终结果插入 nums 的前 k 个位置后返回 k 。
    不要使用额外的空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。
class Solution {
public:
    int removeDuplicates(vector<int>& nums) {
        int expectedNums[nums.size()];
        int t=0,j;
        int i=0;
        int w=0;
while(i<nums.size())
{
    int flag=1;
    expectedNums[t]=nums[i];
    t++;
    for(j=i+1;j<nums.size();j++)
    {
    if(nums[i]==nums[j])
    {
        flag=0;//如果有与当前元素相同的,则标志为0,且记录最后一个相同元素的位置
        w=j+1;//将i置为最后相同元素的下一个位置
    }
    }
    if(flag==1)//如果遍历后没有相同的,则将i加1
    {
    i=i+1;
    w=i;
    }
    i=w;//记录i的位置
}
for(int i=0;i<t;i++)
{
    nums[i]=expectedNums[i];
}
return t;
    }
};
  • 例题3(283题):给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。
    请注意 ,必须在不复制数组的情况下原地对数组进行操作。
//此方法覆盖数组元素后重写0,如果是{0,0,0.....0,1}的数组,则需要从1到N-1重写0,效率不高
#include "p.h"
void Solution1:: moveZeroes(vector<int>& nums) {
	int low = 0, fast = 0;
	int n = nums.size();
	for (fast = 0; fast < n; fast++)
	{
		if (nums[fast] != 0)
		{
			nums[low++] = nums[fast];
		}
	}
	for (int i = low; i < n; i++)
	{
		nums[i] = 0;
	}
	cout << "[";
	for (int i = 0; i < n; i++)
	{
		if (i != n - 1)
			cout <<nums[i] << ",";
		else
			cout << nums[i] << "]"<<endl;
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值