算法基础-数组篇

这篇博客详细讲解了数组基础中的算法应用,包括二分查找、双指针和滑动窗口。通过LeetCode的具体题目实例,如二分查找在704、34、69题的应用,双指针在27、26、977题的运用,以及滑动窗口在209、904题的实践,深入解析了这些算法的实现与思考过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

知识点:

算法:

1、二分查找(循环不变量原则)

例1:leetcode 704二分查找

例2:leetcode 34.在排序数组中查找元素的第一个和最后一个位置

例3:leetcode 69. x的平方根,使用二分查找

2、双指针

例1:leetcode 27. 移除元素

例2:leetcode 26. 删除有序数组中的重复项

例3:leetcode 977. 有序数组的平方

3、滑动窗口

例1:leetcode 209. 长度最小的子数组

例2:leetcode 904. 水果成篮


知识点:

  • 数组是存放在连续内存空间上相同类型数据的集合。
  • 数组下标是从0开始的;
  • 数组内存空间的地址是连续的,删除元素或者增添元素时要移动其他元素的地址。
  • 数组的元素不能删只能覆盖。
  • vector 与array区别:vector底层实现是array,vector是容器,不是数组。

算法:

1、二分查找(循环不变量原则)

重点:根据查找区间的定义做边界处理。

二分查找的使用前提条件:有序数组且数组中无重复元素。

区间定义一般有两种:左闭右开,左闭右闭。

例1:leetcode 704二分查找

给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target  ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。

#include<iostream>
using namespace std;
#include<vector>

int search(vector<int>&nums, int target) {
	int left = 0;
	int right = nums.size() - 1;//定义target在[left,right]左闭右闭区间里
	while (left <= right) {
		int middle = left + (right - left) / 2;//防止溢出,==(right - left) / 2
		if (nums[middle] < target) {
			left = middle + 1;
		}
		else if (nums[middle] > target) {
			right = middle - 1;
		}
		else {
			return middle;
		}
	}
	return -1;
}
int main() {
	cout << "请输入数组的元素个数: ";
	int n;
	while (cin >> n) {
		vector<int> nums(n);
		for (int i = 0; i < n; i++) cin >> nums[i];

		cout << "请输入目标值(整数): " ;
		int target;
		cin >> target;

		if (search(nums, target) == -1) {
			cout << "该数组中没有与目标值相等的元素。" << endl;
		}
		else {
			cout << "该数组中与目标值相等的元素下标为: " ;
			cout<<search(nums, target)<<endl;
		}
		
	}
	system("pause");
	return 0;
}

补充:<<1,左移1位相当于乘以2;>>1,右移1位相当于除以2;

例2:leetcode 34.在排序数组中查找元素的第一个和最后一个位置

给定一个按照升序排列的整数数组 nums,和一个目标值 target。找出给定目标值在数组中的开始位置和结束位置。

如果数组中不存在目标值 target,返回 [-1, -1]。

思路:

第一个位置:数组中第一个等于target的位置

最后一个位置:数组中最后一个大于target元素的位置减1;

int search4(vector<int>&nums, int target,bool lf) {//bool lf是用来判断计算的是左指针还是右指针
	int left = 0;
	int right = nums.size() - 1;//定义target在[left,right)左闭右开的区间里
	int result = nums.size();//比如说,数组为[7,7,8,8],目标值为8,其实进入不到if语句里面即result值不会变,所以一开始设置它为数组长度,最后取到的下标值减1
	while (left <= right) {
		int middle = left + (right - left) / 2;//防止溢出,==(right - left) / 2
		if ((nums[middle] >= target  && lf) || nums[middle]>target) {
			right = middle - 1; //target在右区间,[middle+1, right)
			result = middle;
		}
		else{
			left = middle + 1;//target在左区间,[left,middle)
		}
	}
	return result;
}
vector<int> serchFirstAndLast(vector<int>&nums, int target) {
	int leftptr = search4(nums, target, true);
	int rightptr = search4(nums, target, false)-1;
	if (leftptr <= rightptr && rightptr <nums.size() && nums[leftptr]==target && nums[rightptr] == target) {
		return vector<int> { leftptr, rightptr };
	}
	return vector<int> { -1, -1 };
}

例3:leetcode 69. x的平方根,使用二分查找

int mySqrt(int x) {
	int l = 1, r = x, result = x;
	while (l <= r) {
		int mid = l + (r - l) / 2;
		if ( mid*mid <= x) {
			l = mid + 1;
			result = mid;
		}
		else {
			r = mid - 1;
		}
	}
	return result;
}

2、双指针

双指针法:通过一个快指针和慢指针在一个for循环下完成两个for循环的工作。

例1:leetcode 27. 移除元素

给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。

不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。

元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。

#include<iostream>
using namespace std;
#include<vector>

int removeElement2(vector<int>& nums, int val) {
	int slow = 0;//慢指针
	for (int i = 0; i < nums.size(); i++) {
		//若与目标值相等,快指针往前一步,若不等,则慢指针与快指针同时移动
		if (nums[i] != val) {
			nums[slow++] = nums[i];
		}
	}
	return slow;
}
int main() {
	cout << "请输入数组的元素个数: ";
	int n;
	while (cin >> n) {
		vector<int> nums(n);
		for (int i = 0; i < n; i++) cin >> nums[i];

		cout << "请输入目标值(整数): ";
		int target;
		cin >> target;

		//移除元素
		int len = removeElement2(nums, target);
		cout << "移除元素后的数组长度为:"<< len <<endl;
		for (int i = 0; i < len; i++) {
			cout<< nums[i] <<" ";
		}
	system("pause");
	return 0;
}

例2:leetcode 26. 删除有序数组中的重复项

核心代码如下:

int removeDuplicates(vector<int>& nums) {
	int n = nums.size();
	if (n == 0) return 0;
	int slow = 0;
	for (int i = 0; i < n; i++) {
		if (nums[slow] != nums[i]) {
			nums[++slow] = nums[i];
		}
	}
	return slow + 1;
}

例3:leetcode 977. 有序数组的平方

给你一个按 非递减顺序 排序的整数数组 nums,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。

    vector<int> sortedSquares(vector<int>& nums) {
	    int first = 0, last = nums.size() - 1;
	    vector<int> v(last+1,0);
	    for(int i=last;i>=0;i--) {
		    if (nums[first] * nums[first] > nums[last] * nums[last]) {
			    v[i] = nums[first] * nums[first];
			    first++;
		    }
		    else {
			    v[i] = nums[last] * nums[last];
			    last--;
		    }
	    }
	    return v;
    }

3、滑动窗口

不断的调节子序列的起始位置和终止位置,从而得出我们想要的结果。

滑动窗口主要应用在数组和字符串上。

例1:leetcode 209. 长度最小的子数组

int minSubArrayLen(int target, vector<int>& nums) {
	int n = nums.size();
	int first = 0;//滑动窗口起始位置
	int subLength = 0;//子数组长度
	int sum = 0;//子数组的和
	int result = INT32_MAX;//一开始默认为最大值
	for (int i = 0; i < n; i++) {
		sum += nums[i];
		while (sum >= target) {
			//当子数组总和大于等于目标值时,滑动窗口的起始位置需往前移
			subLength = i - first + 1;
			result = result < subLength ? result : subLength;
			sum -= nums[first];
			first++; //不断变更子序列的起始位置
		}
	}
	// 如果result没有被赋值的话,就返回0,说明没有符合条件的子序列
	return result==INT32_MAX ? 0 : result;
}

例2:leetcode 904. 水果成篮

这道题得先要弄清楚它的题目要求:即找出只存在两种类型的最长子数组,并返回其长度

int totalFruit(vector<int>& fruits) {
	int first = 0, second = 0;
	int len = 0;
	int subLen = 0;
	int temp = 0;//需要借助一个变量记录第三种元素出现时应该从哪个下标开始。
	for (int i = 0; i < fruits.size(); i++) {
		if (fruits[first] != fruits[i] && fruits[second] != fruits[i])
		{
			if (fruits[first] != fruits[second]) {
				first = temp;
			}
			second = i;
		}
		if (fruits[temp] != fruits[i]) {
			temp = i;
		}
		subLen = i - first + 1;
		len = len < subLen ? subLen : len;
	}
	return len;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值