当前位置: 首页 > news >正文

c++编程题-笔记

c++编程题-笔记

  • 前言
  • 一、二分法
    • 1、区分边界
  • 二、双指针
    • 1、左右指针
    • 2、快慢指针
      • ①两个指针指向同一个序列
  • 三、滑动窗口
  • 四、模拟过程
  • 五、前缀和


前言

这里是学习c++,练习一下算法题,简单的笔记

一、二分法

1、区分边界

情景:在有序无重复元素的数组中(因为一旦有重复元素,使用二分查找法返回的元素下标可能不是唯一的),找满足某个条件的数的位置;

对于左闭右闭的区间里,也就是**[left, right]** ,默认升序数组。

// 版本一
class Solution {
public:int search(vector<int>& nums, int target) {int left = 0;int right = nums.size() - 1; // 定义target在左闭右闭的区间里,[left, right]while (left <= right) { // 当left==right,区间[left, right]依然有效,所以用 <=int middle = left + ((right - left) / 2);// 防止溢出 等同于(left + right)/2if (nums[middle] > target) {right = middle - 1; // target 在左区间,所以[left, middle - 1]} else if (nums[middle] < target) {left = middle + 1; // target 在右区间,所以[middle + 1, right]} else { // nums[middle] == targetreturn middle; // 数组中找到目标值,直接返回下标}}// 未找到目标值return -1;}
};

对于在左闭右开的区间里,也就是**[left, right)** ,默认升序数组。

// 版本二
class Solution {
public:int search(vector<int>& nums, int target) {int left = 0;int right = nums.size(); // 定义target在左闭右开的区间里,即:[left, right)while (left < right) { // 因为left == right的时候,在[left, right)是无效的空间,所以使用 <int middle = left + ((right - left) >> 1);if (nums[middle] > target) {right = middle; // target 在左区间,在[left, middle)中} else if (nums[middle] < target) {left = middle + 1; // target 在右区间,在[middle + 1, right)中} else { // nums[middle] == targetreturn middle; // 数组中找到目标值,直接返回下标}}// 未找到目标值return -1;}
};

int right = nums.size(); 是因为“ 左闭右开 ”,但是我得包含整个数组的元素,所以 right = nums.size() ,此时nums[1]~nums[-1]都可以取到,nums[ nums.size()]不会取到,且符合“ 左闭右开 ”。

二、双指针

1、左右指针

场景:左右双指针算法在解决问题的过程中会生成两个指针,一个指向头部,一个指向尾部,从两端向中间逼近,直到满足条件或者两个指针相遇为止. 二分查找就是一种左右双指针算法的应用场景,其他使用情况基本上可以总结为数组中元素组合问题。

在这里插入图片描述
i :代表左指针,指向数组首部。
j :代表右指针,指向数组尾部。

class Solution {
public:vector<int> sortedSquares(vector<int>& A) {int k = A.size() - 1;vector<int> result(A.size(), 0);//赋值为0for (int i = 0, j = A.size() - 1; i <= j;) { // 注意这里要i <= j,因为最后要处理两个元素if (A[i] * A[i] < A[j] * A[j])  {result[k--] = A[j] * A[j];j--;}else {result[k--] = A[i] * A[i];i++;}}return result;}
};

2、快慢指针

场景:通过设置两个指针不断进行单向移动来解决问题的方法。

形式:两个指针分别指向不同的序列;两个指针指向同一个序列。比如:快速排序的划分过程。

实现:本来是双层for循环,O(n²)的时间复杂度。通过双指针算法可以优化到O(n)的时间复杂度。那是如何优化的时间复杂度呢?其实是当一个指针满足条件后才会单向移动,没有指针的回溯,而每次都会有指针的移动。简单说就是有个快指针可以不停的移动,有个慢指针需要符合条件后才能移动

①两个指针指向同一个序列

模板:

slow = 0 # 1for fast in range(len(nums)):if  是slow想要的元素:nums[slow] = nums[fast]  #  nums[slow+1] = nums[fast]slow += 1return slow, nums # slow+1, nums

在这里插入图片描述
fast 快指针:指向符合要求的值。
slow 慢指针:指向数组中需要更新的位置。

// 时间复杂度: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;}
};

三、滑动窗口

所谓滑动窗口,就是不断的调节子序列的起始位置和终止位置,从而得出我们想要的结果,本质上还是双指针问题。

分清楚:

窗口内是什么?
如何移动窗口的起始位置?
如何移动窗口的结束位置?

滑动窗口,快慢指针类型:
在这里插入图片描述
窗口就是 满足其和 ≥ s 的长度最小的 连续 子数组。
窗口的起始位置如何移动:如果当前窗口的值大于等于s了,窗口就要向前移动了(也就是该缩小了)。
窗口的结束位置如何移动:窗口的结束位置就是遍历数组的指针,也就是for循环里的索引。

其中,i 是慢指针,j 是快指针,前窗口的值大于等于s了就移动慢指针,这里要用while循环,不能用if,这是关键点。

class Solution {
public:int minSubArrayLen(int s, vector<int>& nums) {int result = INT32_MAX;//nums.size()+1即可int sum = 0; // 滑动窗口数值之和int i = 0; // 滑动窗口起始位置int subLength = 0; // 滑动窗口的长度for (int j = 0; j < nums.size(); j++) {sum += nums[j];// 注意这里使用while,每次更新 i(起始位置),并不断比较子序列是否符合条件while (sum >= s) {subLength = (j - i + 1); // 取子序列的长度result = result < subLength ? result : subLength;sum -= nums[i++]; // 这里体现出滑动窗口的精髓之处,不断变更i(子序列的起始位置)}}// 如果result没有被赋值的话,就返回0,说明没有符合条件的子序列return result == INT32_MAX ? 0 : result;}
};

四、模拟过程

坚持循环不变量原则,就是在循环中保持一定的规则、不变的因素。

例题1
在这里插入图片描述
vector<vector<int>> vc :代表右下左上四个方向的dx与dy。

class Solution {
public:vector<vector<int>> generateMatrix(int n) {vector<int> v( n, 0 );vector<vector<int>> vv(n, v);vector<vector<int>> vc = {//int vc[][]{ 0, 1 },{ 1, 0 },{ 0, -1 },{ -1, 0 }};int x = 0;int y = 0;int tmp = 1;vv[x][y] = 1;while (1) {if (tmp == n * n) break;for (int i = 0; i < 4; i++) {int dx = vc[i][0];int dy = vc[i][1];while (x+dx >=0 && x+dx <n && y+dy >=0 && y+dy < n && tmp<= n*n) {//越界情况if (vv[x + dx][y + dy] != 0)//已经赋值过的情况break;x += dx;y += dy;tmp++;vv[x][y] = tmp;if (tmp == n * n) break;};}}return vv;}
};

五、前缀和

前缀和的思想是重复利用计算过的子数组之和,从而降低区间查询需要累加计算的次数。
sum[ i ] :代表nums[]数组中,下标为0 - i 的元素之和。
or nums[] 数组中前 i 个元素之和,下标为 0~ i-1的元素之和。

例题1
在这里插入图片描述
sum[ i ] :代表nums[]数组中,下标为0 - i 的元素之和。
要求 区间下标 [2, 5] 的区间和,那么应该是 sum[5] - sum[1].

class Solution {
public:int takeSum(vector<int>nums, int n, int m) {int presum = 0;vector<int> pre(nums.size()-1 , 0);for(int i = 0; i < nums.size(); i++){presum += nums[i];pre[i] = presum;}return pre[m] - pre[n-1];};
http://www.lryc.cn/news/619699.html

相关文章:

  • 电商双11美妆数据分析
  • 《Foundations and Recent Trends in Multimodal Mobile Agents: A Survey》论文精读笔记
  • 2025年手游防护终极指南:四维防御体系破解DDoS、外挂与协议篡改
  • 从人机协作到情感共鸣:智能销售机器人如何重塑零售体验
  • 织构表面MATLAB仿真
  • 来伊份×养馋记:社区零售4.0模式加速渗透上海市场
  • 10.反射获取静态类的属性 C#例子 WPF例子
  • python的滑雪场雪具租赁服务数据可视化分析系统
  • mapbox进阶,实现精灵图生成和拆分(小图任意大小,不固定),并简单使用
  • 10、系统规划与分析
  • AI编程:python测试MQ消息服务联接和消息接收
  • csp知识基础——贪心算法
  • 神经网络训练核心组件
  • 一条n8n工作流
  • electron进程间通信- 从渲染进程到主进程
  • Python open 函数详解:参数用法与文件操作实战指南
  • 美团搜索推荐统一Agent之需求分析与架构设计
  • Queue参考代码
  • CompletableFuture介绍及使用方式
  • 闹钟时间到震动与声响提醒的实现-库函数版(STC8)
  • 基于R语言的现代贝叶斯统计学方法(贝叶斯参数估计、贝叶斯回归、贝叶斯计算)实践
  • 计算机网络——协议
  • LangGraph 指南篇-基础控制
  • Linux软件编程3.(文件IO和目录IO)
  • 谷歌、facebook、tiktok广告账户多开怎么安全?亚马逊、ebay、shopee多店铺怎么做好?看看adspower工具,注册免费试用及实用技巧分享
  • 美团搜索推荐统一Agent之交互协议与多Agent协同
  • 在es中安装kibana
  • 动静态库
  • ICCV 2025 | 4相机干掉480机位?CMU MonoFusion高斯泼溅重构4D人体!
  • 内容索引之word转md工具 - markitdown