算法基础---基础算法


文章目录

  • 快速排序
  • 归并排序
  • 二分
    • 整数二分
    • 浮点数二分
  • 高精度        
    • 高精度加法
    • 高精度减法
    • 高精度乘法
    • 高精度除法
  • 前缀和
    • 一维前缀和
    • 二维前缀和
  • 差分
    • 一维差分
    • 二维差分
  • 双指针
  • 位运算
  • 离散化
  • 区间合并


一、快速排序

思想:1.首先确定一个分界点(随机取任意一点为分界点,一般取中点)

           2.将小于x的数移动到左边,大于x的数移动到右边,将区间分为[l,j],[j+1,r];

           3.递归左右两个区间即可。

void quick_sort(int q[], int l, int r)
{
    if (l >= r) return;

    int i = l - 1, j = r + 1, x = q[l + r >> 1];
    while (i < j)
    {
        do i ++ ; while (q[i] < x);
        do j -- ; while (q[j] > x);
        if (i < j) swap(q[i], q[j]);
    }
    quick_sort(q, l, j), quick_sort(q, j + 1, r);
}

二、归并排序

思想:1.首先去数组的中间数作为分界点.

           2.递归分界点的左右两个区间

           3.将左右两边进行合并。

int tmp[N];

void Merge_sort(int a[], int l, int r)
{
    if (l >= r) return;
    int mid = (l + r) >> 1;
    //先分后合
    Merge_sort(a, l, mid);
    Merge_sort(a, mid + 1, r);
    int i = l, j = mid + 1, k = 0;
    //归并
    while (i <= mid && j <= r)
    {
        if (a[i] <= a[j]) tmp[k++] = a[i++];
        else tmp[k++] = a[j++];
    }
    //续尾
    while (i <= mid) tmp[k++] = a[i++];
    while (j <= r) tmp[k++] = a[j++];
    for (i = l, j = 0; i <= r;)
        a[i++] = tmp[j++];
}

三、二分

思想:可以划分为满足某种性质与不满足某种性质的两个区间。

1.整数二分

bool check(int x) {/* ... */} // 检查x是否满足某种性质

// 区间[l, r]被划分成[l, mid]和[mid + 1, r]时使用:
int bsearch_1(int l, int r)
{
    while (l < r)
    {
        int mid = l + r >> 1;
        if (check(mid)) r = mid;    // check()判断mid是否满足性质
        else l = mid + 1;
    }
    return l;
}
// 区间[l, r]被划分成[l, mid - 1]和[mid, r]时使用:
int bsearch_2(int l, int r)
{
    while (l < r)
    {
        int mid = l + r + 1 >> 1;
        if (check(mid)) l = mid;
        else r = mid - 1;
    }
    return l;
}

2.浮点数二分

bool check(double x) {/* ... */} // 检查x是否满足某种性质

double bsearch_3(double l, double r)
{
    const double eps = 1e-6;   // eps 表示精度,取决于题目对精度的要求
    while (r - l > eps)
    {
        double mid = (l + r) / 2;
        if (check(mid)) r = mid;
        else l = mid;
    }
    return l;
}

四、高精度

1.高精度加法

// C = A + B, A >= 0, B >= 0
vector<int> add(vector<int> &A, vector<int> &B)
{
    if (A.size() < B.size()) return add(B, A);

    vector<int> C;
    int t = 0;
    for (int i = 0; i < A.size(); i ++ )
    {
        t += A[i];
        if (i < B.size()) t += B[i];
        C.push_back(t % 10);
        t /= 10;
    }

    if (t) C.push_back(t);
    return C;
}

2.高精度减法

// C = A - B, 满足A >= B, A >= 0, B >= 0
vector<int> sub(vector<int> &A, vector<int> &B)
{
    vector<int> C;
    for (int i = 0, t = 0; i < A.size(); i ++ )
    {
        t = A[i] - t;
        if (i < B.size()) t -= B[i];
        C.push_back((t + 10) % 10);
        if (t < 0) t = 1;
        else t = 0;
    }

    while (C.size() > 1 && C.back() == 0) C.pop_back();
    return C;
}

3.高精度乘法

// C = A * b, A >= 0, b >= 0
vector<int> mul(vector<int> &A, int b)
{
    vector<int> C;

    int t = 0;
    for (int i = 0; i < A.size() || t; i ++ )
    {
        if (i < A.size()) t += A[i] * b;
        C.push_back(t % 10);
        t /= 10;
    }

    while (C.size() > 1 && C.back() == 0) C.pop_back();

    return C;
}

4.高精度除法

// A / b = C ... r, A >= 0, b > 0
vector<int> div(vector<int> &A, int b, int &r)
{
    vector<int> C;
    r = 0;
    for (int i = A.size() - 1; i >= 0; i -- )
    {
        r = r * 10 + A[i];
        C.push_back(r / b);
        r %= b;
    }
    reverse(C.begin(), C.end());
    while (C.size() > 1 && C.back() == 0) C.pop_back();
    return C;
}

五、前缀和

1.一维前缀和

S[i] = a[1] + a[2] + ... a[i]
a[l] + ... + a[r] = S[r] - S[l - 1]

#include<iostream>

using namespace std;

const int N = 100010;

int n, m;
int a[N], s[N];

int main()
{
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	cout.tie(nullptr);

	cin >> n >> m;

	for (int i = 1; i <= n; i++)
		cin >> a[i];

	for (int i = 1; i <= n; i++)
		s[i] = s[i - 1] + a[i];

	while (m--)
	{
		int l, r;
		cin >> l >> r;

		cout << s[r] - s[l - 1] << endl;
	}
	return 0;
}

 2.二维前缀和

 S[i] = a[1] + a[2] + ... a[i]
a[l] + ... + a[r] = S[r] - S[l - 1]

#include<iostream>

using namespace std;

const int N = 1010;

int n, m, q;
int a[N][N], s[N][N];

int main()
{
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	cout.tie(nullptr);

	cin >> n >> m >> q;

	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= m; j++)
			cin >> a[i][j];

	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= m; j++)
			s[i][j] = s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1] + a[i][j];

	while (q--) {
		int x1, y1, x2, y2;
		cin >> x1 >> y1 >> x2 >> y2;
		cout << s[x2][y2] - s[x1 - 1][y2] - s[x2][y1 - 1] + s[x1 - 1][y1 - 1]<<endl;
	}
	return 0;
}

六、差分

差分数组的定义:记录当前位置的数与上一位置的数的差值.

 差分的作用:在差分数组的 减去 在位置处加上,就能达到整个区间修改的操作.

  1. 快速处理区间加减操作:
  2. 询问区间和:处理查询.
  3. 求出前缀和.

1.一维差分

给区间[l, r]中的每个数加上c:B[l] += c, B[r + 1] -= c

#include<iostream>

using namespace std;

const int N = 100010;

int n, m;
int a[N], b[N];

void insert(int l, int r, int c)
{
	b[l] += c;
	b[r + 1] -= c;
}

int main()
{
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	cout.tie(nullptr);

	cin >> n >> m;
	for (int i = 1; i <= n; i++)
		cin >> a[i];

	for (int i = 1; i <= n; i++)
		insert(i, i, a[i]);

	while (m--) {
		int l, r, c;
		cin >> l >> r >> c;
		insert(l, r, c);
	}

	for (int i = 1; i <= n; i++)
		b[i] += b[i - 1];

	for (int i = 1; i <= n; i++)
		cout << b[i] << " ";
	return 0;
}

2.二维差分

给以(x1, y1)为左上角,(x2, y2)为右下角的子矩阵中的所有元素加上c:
S[x1, y1] += c, S[x2 + 1, y1] -= c, S[x1, y2 + 1] -= c, S[x2 + 1, y2 + 1] += c

#include<iostream>

using namespace std;

const int N = 1010;

int n, m,q;
int a[N][N], b[N][N];

void insert(int x1, int y1, int x2, int y2,int c)
{
	b[x1][y1] += c;
	b[x2 + 1][y1] -= c;
	b[x1][y2 + 1] -= c;
	b[x2 + 1][y2 + 1] += c;
}

int main()
{
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	cout.tie(nullptr);

	cin >> n >> m>>q;

	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= m; j++)
			cin >> a[i][j];

	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= m; j++)
			insert(i, j, i, j, a[i][j]);

	while (q--) {
		int x1, y1, x2, y2, c;
		cin >> x1 >> y1 >> x2 >> y2 >> c;
		insert(x1, y1, x2, y2, c);
	}

	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= m; j++)
			b[i][j] += b[i - 1][j] + b[i][j - 1] - b[i - 1][j - 1];

	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= m; j++)
			cout << b[i][j] << " ";
	return 0;
}

七、双指针

在区间问题上,暴力的做法的复杂度往往达到O(n^2)复杂度,而双指针的思想挖掘区间“单调”性质将复杂度降到O(n)。

常见问题分类:
    (1) 对于一个序列,用两个指针维护一段区间
    (2) 对于两个序列,维护某种次序,比如归并排序中合并两个有序序列的操作

for (int i = 0, j = 0; i < n; i ++ )
{
    while (j < i && check(i, j)) j ++ ;

    // 具体问题的逻辑
}

八、位运算

求n的第k位数字: n >> k & 1
返回n的最后一位1:lowbit(n) = n & -n

常用技巧:

1、 用于整数的奇偶性判断(n&1)

2、 判断n是否是2的正整数幂((!(n&(n-1)) )&& n)

3、 统计n中1的个数

九、离散化

思想:1.首先操作设计的下标,把将要存数字的下标与求和范围两端的下标,存入小数组q中。

        2.将小数组q进行排序。

        3.创建出一个与小数组q相同大小的数组s,从数组q中找出对应大数组要存入数据的位置的映射,在s相同位置存入数据(可以利用二分的思想)。

        4.找大数组求和范围两端点在q中的映射位置,在数组s对应映射位置求和即可,可用前缀和。

vector<int> alls; // 存储所有待离散化的值
sort(alls.begin(), alls.end()); // 将所有值排序
alls.erase(unique(alls.begin(), alls.end()), alls.end());   // 去掉重复元素

// 二分求出x对应的离散化的值
int find(int x) // 找到第一个大于等于x的位置
{
    int l = 0, r = alls.size() - 1;
    while (l < r)
    {
        int mid = l + r >> 1;
        if (alls[mid] >= x) r = mid;
        else l = mid + 1;
    }
    return r + 1; // 映射到1, 2, ...n
}

十、区间合并

思想:1.首先我们以每个分区的左侧为标准进行排序,这样我们的每次区间合并只需要采用当前区间和下一个区间对比即可,此外我们的左侧不需要改变。

        2.右侧的情况分为三种:包裹,接触,不接触 分别对应着右侧边界为a.r,b.r以及两个区间都添加的情况。

思路:1.按区间的左端点排序。

        2.从左到右扫描,维护一个当前区间

        3.每次遍历的区间和当前区间有三种情况:

        (1)右端点小于当前区间的左端点,当前区间不变。

        (2)右端点大于当前区间的右端点,当前区间变长。

        (3)左端点大于当前区间右端点,将区间置为当前区间。

// 将所有存在交集的区间合并
void merge(vector<PII> &segs)
{
    vector<PII> res;

    sort(segs.begin(), segs.end());

    int st = -2e9, ed = -2e9;
    for (auto seg : segs)
        if (ed < seg.first)
        {
            if (st != -2e9) res.push_back({st, ed});
            st = seg.first, ed = seg.second;
        }
        else ed = max(ed, seg.second);

    if (st != -2e9) res.push_back({st, ed});

    segs = res;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/2058.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【云原生】k8s集群命令行工具kubectl基础操作命令实践详解

kubectl基础操作命令详解一、准备工作1.1、Replication Controller1.2、Deployment1.3、DaemonSet1.4、查看创建的svc和pod1.5、kubectl 命令自动补全设置二、kubectl语法三、基础操作命令3.1、api-resources3.2、api-versions3.3、create3.4、expose3.5、run3.6、set3.6.1、en…

filebrowser的权限实现RBAC效果

filebrowser安装和支持定制化&#xff0c;建议参考我这一篇文章filebrowser安装言归正传&#xff0c;目前发现客户需要有文件权限管理功能&#xff0c;我一开始也没仔细研究这块&#xff0c;我以为不支持呢&#xff0c;我今天就认真的了研究 下这个存储服务&#xff0c;其实是支…

前端网络安全

什么是同源策略同源指的是&#xff1a;协议、端口号、域名必须一致。他是浏览器的一个用于隔离潜在恶意文件的重要安全机制。限制了从同一个源加载的文档或脚本&#xff0c;与另一个源的资源进行交互。同源策略主要限制了三个方面&#xff1a;当前域下的js脚本不能够访问其他域…

jsoup 框架的使用指南

概述 参考&#xff1a; 官方文档jsoup的使用JSoup教程jsoup 在 GitHub 的开源代码 概念简介 jsoup 是一款基于 Java 的 HTML 解析器&#xff0c;它提供了一套非常省力的 API&#xff0c;不但能直接解析某个 URL 地址、HTML 文本内容&#xff0c;而且还能通过类似于 DOM、CS…

【Java进阶篇】——反射机制

一、反射的概念 1.1 反射出现的背景 Java程序中&#xff0c;所有对象都有两种类型&#xff1a;编译时类型和运行时类型&#xff0c;而很多时候对象的编译时类型和运行时类型不一致 Object obj new String("hello")、obj.getClass(); 如果某些变量或形参的声明类型…

1、Linux初级——linux命令

下载镜像&#xff1a;http://cn.ubuntu.com/dowload 一、基本命令 1、alias&#xff08;给命令取别名&#xff09; 例如&#xff1a;alias clls -la&#xff08;只是临时的&#xff09; 2、配置文件$ vim ~/.bashrc $ vim ~/.bashrc // 使用vim打开配置文件 (1)在配置文件…

初识STM32单片机

目录 一、单片机基本认知 二、STM系列单片机命名规则 三、标准库与HAL库区别 四、通用输入输出端口GPIO 五、推挽输出与开漏输出 六、复位和时钟控制&#xff08;RCC&#xff09; 七、时钟控制 八、中断和事件 九、定时器介绍 一、单片机基本认知 单片机和PC电脑相比…

搜索系统(二)

term weight 如何衡量一个词在一篇文档中的重要性 词频率&#xff08;tf&#xff09;&#xff1a;term在文档中出现了多少次&#xff0c;tf越大说明越重要 逆文档频率&#xff08;idf&#xff09;&#xff1a;有多少文档包含了这个term&#xff0c;idf越大表明越不重要 综合…

Unity --- 游戏案例 --- 英雄无敌与Ruby

1.如何在场景中标识出一个空游戏物体&#xff08;对象集群&#xff09; 1.选中该空游戏物体&#xff0c;然后在Inspector面板处的物体名旁边添加想要的颜色的图标即可&#xff0c;最终效果如下图 2.这个图标只在场景中能开到&#xff0c;在游戏画面中是看不到的&#xff0c;其存…

Vulnhub项目:Web Machine(N7)

靶机地址&#xff1a;Web Machine(N7)渗透过程&#xff1a;kali ip&#xff1a;192.168.56.104&#xff0c;靶机ip&#xff0c;使用arp-scan进行查看靶机地址&#xff1a;192.168.56.128收集靶机开放端口&#xff1a;nmap -sS -sV -T5 -A 192.168.56.128开放了80端口&#xff0…

索尼mxf变成rsv的修复方法

索尼的影视级摄像机一般是用MXF文件结构&#xff0c;在一些极端情况下(如断电)会生成RSV文件&#xff0c;遇到这种情况我们应该如何处理&#xff1f;下面来看看今天这个案例。故障文件:12.51G RSV文件故障现象:断电后仅生成了一个扩展名为rsv的文件&#xff0c;使用播放器可以播…

python+django+vue全家桶鲜花商城售卖系统

重点&#xff1a; &#xff08;1&#xff09; 网上花店网站中各模块功能之间的的串联。 &#xff08;2&#xff09; 网上花店网站前台与后台的连接与同步。 &#xff08;3&#xff09; 鲜花信息管理模块中鲜花的发布、更新与删除。 &#xff08;4&#xff09; 订单…

java多线程之线程的六种状态

线程的六种状态(1) NEW(初始状态)(2) TERMINATED(终止状态 / 死亡状态)(3) RUNNABLE(运行时状态)(4) TIMED_WAITING(超时等待状态)(5) WAITING(等待状态)(6) BLOCK(阻塞状态)sleep和wait的区别:操作系统里的线程自身是有一个状态的,但是java Thread 是对系统线程的封装,把这里的…

基于C++的AI五子棋游戏项目开发教程

项目资源下载 基于C的AI五子棋游戏项目源码压缩包下载地址基于C的AI五子棋游戏项目源码Github下载地址基于C的AI五子棋游戏项目所需素材基于C的AI五子棋游戏项目所需要的EasyX 项目简介 本项目基于C开发&#xff0c;整体来说比较简单&#xff0c;实现了人与AI之间的五子棋对弈…

Java实习生------Redis常见面试题汇总(AOF持久化、RDB快照、分布式锁、缓存一致性)⭐⭐⭐

“年轻人&#xff0c;就要勇敢追梦”&#x1f339; 参考资料&#xff1a;图解redis 目录 谈谈你对AOF持久化的理解&#xff1f; redis的三种写回策略是什么&#xff1f; 谈谈你对AOF重写机制的理解&#xff1f;AOF重写机制的具体过程&#xff1f; 谈谈你对RDB快照的理解&a…

面试官:说一下MySQL中的锁机制吧

5. 1MySQL有哪些锁&#xff1f; 为保证数据的一致性&#xff0c;需要对并发操作进行控制&#xff0c;因此产生了锁。同时锁机制也为实现MySQL的各个隔离级别提供了保证。 锁冲突 也是影响数据库并发访问性能的一个重要因素。所以锁对数据库而言显得尤其重要&#xff0c;也更加…

seata服务搭建

它支持两种存储模式&#xff0c;一个是文件&#xff0c;一个是数据库&#xff0c;下面我们分别介绍一下这两种配置nacos存储配置&#xff0c;注意如果registry.conf中注册和配置使用的是file&#xff0c;就会去读取file.config的配置&#xff0c;如果是nacos则通过nacos动态读取…

Kafka和RabbitMQ有哪些区别,各自适合什么场景?

目录标题1. 消息的顺序2. 消息的匹配3. 消息的超时4. 消息的保持5. 消息的错误处理6. 消息的吞吐量总结1. 消息的顺序 有这样一个需求&#xff1a;当订单状态变化的时候&#xff0c;把订单状态变化的消息发送给所有关心订单变化的系统。 订单会有创建成功、待付款、已支付、已…

Cookie和Session详解

目录 前言&#xff1a; Session详解 Cookie和Session区别和关联 服务器组织会话的方式 使用Tomcat实现登录成功跳转到欢迎页面 登录前端页面 登录成功后端服务器 重定向到欢迎页面 抓包分析交互过程 小结&#xff1a; 前言&#xff1a; Cookie之前博客有介绍过&#x…

音视频技术开发周刊 | 285

每周一期&#xff0c;纵览音视频技术领域的干货。新闻投稿&#xff1a;contributelivevideostack.com。GPT-4 Office全家桶发布谷歌前脚刚宣布AI工具整合进Workspace&#xff0c;微软后脚就急匆匆召开了发布会&#xff0c;人狠话不多地祭出了办公软件王炸——Microsoft 365 Cop…