当前位置: 首页 > 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.dtcms.com/a/328938.html

相关文章:

  • kali linux 2025.2安装Matlab的详细教程
  • 通过限制网络访问来降低服务器被攻击风险的方法
  • 服务器如何应对SYN Flood攻击?
  • FluxApi - 使用Spring进行调用Flux接口
  • Gradle(三)创建一个 SpringBoot 项目
  • 深度学习(3):全连接神经网络构建
  • mysql的快照读与当前读的区别
  • 11G RAC数据文件创建到本地如何处理
  • 【C语言强化训练16天】--从基础到进阶的蜕变之旅:Day3
  • 《算法导论》第 22 章 - 基本的图算法
  • [AXI5]AXI协议中的Scalar atomic和Vector atomic有什么区别?
  • 【算法】位运算经典例题
  • BM25:概率检索框架下的经典相关性评分算法
  • ADB 无线调试连接(Windows + WSL 环境)
  • 如何在VS里使用MySQL提供的mysql Connector/C++的debug版本
  • C++ 优选算法 力扣 209.长度最小的子数组 滑动窗口 (同向双指针)优化 每日一题 详细题解
  • Java Spring框架最新版本及发展史详解(截至2025年8月)-优雅草卓伊凡
  • graphql接口快速使用postman添加接口以及输入返回参数
  • 超越相似名称:Elasticsearch semantic text 如何在简洁、高效、集成方面超越 OpenSearch semantic 字段
  • 5.语句几个分类
  • 自建知识库,向量数据库 体系建设(四)之文本向量与相似度计算——仙盟创梦IDE
  • 药房智能盘库系统的Python编程分析与实现—基于计算机视觉与时间序列预测的智能库存管理方案
  • Ubuntu下快速安装Tomcat教程
  • ubuntu24.04安装 bpftool 以及生成 vmlinux.h 文件
  • 4 种方法将联系人从 iPhone 传输到 realme
  • java中在多线程的情况下安全的修改list
  • 【C++ STL】list详解和模拟
  • 冒泡排序专栏
  • Java研学-RabbitMQ(七)
  • LeetCode_二叉树