[数据结构与算法]基础算法(排序, 二分, 前缀, 差分)

简介: 快速排序:(分治的思想)✅确定分界点:q[l], q[(r+l)/2], q[r] (中间点可以随机选, 按照同一规则, 这里选(l+r)/2该点)维护数组:维护分界点的左边都比分界点小,分界点的右边都比分界点大按照维护关系, 递归处理左右两段💡思想解释: 先整后细:先让大体总的符合条件,再部分部分解决

目录


一. 快速排序


二. 归并排序


三.  二分


✨整数二分:


✨浮点数的二分


四.  前缀和


✨ 一维前缀


✨二维前缀


五. 差分


一. 快速排序

快速排序:(分治的思想)✅


确定分界点:q[l],  q[(r+l)/2],  q[r] (中间点可以随机选, 按照同一规则, 这里选(l+r)/2该点)


维护数组:维护分界点的左边都比分界点小,分界点的右边都比分界点大


按照维护关系, 递归处理左右两段


💡思想解释:


       先整后细:先让大体总的符合条件,再部分部分解决


模板代码

void quick_sort(int q[], int L, int R){
    if(L >= R) return;
    int x = q[L], i = L - 1, j = 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) 对于 x = q[l + r >> 1], j 始终走到q[R - 1] 的位置, 因此为了不陷入死循环必须选择


quick_sort(q, l, j);


quick_sort(q, j + 1, r);



因为选择 i 的话, i 即可能出现在 L 的位置上也可能出现在 R 的位置上, 当出现在第一种情况的时候(图中的情况一)不会有影响, 但是出现在情况二的时候就会出现陷入死循环的情况


quick_sort(q, l, i- 1), quick_sort(q, i, r);


以上代码代入就会出现quick_sort(q, l, l) , quick_sort(q, l, r), 对于第二个函数, 与我起初调用的函数想同, 这说明有一次执行了该函数, 因此下一次也会执行, 这样往复, 就陷入了死循环.


⚠ : 重点👑🌈🎉👉✨⭐


1. 分治点的选择


2. i++   和   j--


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>> 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, i - 1), quick_sort(q, i, r);
}


二. 归并排序

(也是分治的思想)✅


1. 将数组从中间分开, 分别对其按照这三步进行排序, 直到无法分割


2. 把两边排好序的数组再次进行统一排序


3. 再将左右两个数组进行合并


👑 思路 : 先细后整, 先保证这部分是排好序的, 直接那这一部分去和另一部分排好序的进行组合, 这样整个数组就是排好序的

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 k = 0, i = l, j = mid + 1;
    while (i <= mid && j <= r)
        if (a[i] >= a[j]) temp[k++] = a[i++];
        else temp[k++] = a[j++];
    while (i <= mid) temp[k++] = a[i++];
    while (j <= r) temp[k++] = a[j++];
    for (i = l, j = 0; i <= r; ++i, ++j) a[i] = temp[j];
}


🌈图解 :



三.  二分

✨整数二分:

看 mid(你选取的中点) 是 左边 还是 右边



代码 :


int bSearch_1(int l, int r) {
    while (l < r) {
        int mid = l + r >> 1;
        if (check(mid)) r = mid;  // 如果左边界满足就执行这个
        else l = mid + 1;
    }
    return l;
}
int bSearch_2(int a[], int l, int r) {
    while (l < r) {
        int mid = l + r + 1 >> 1;
        if (check(mid)) l = mid;  // 如果右边界满足就执行这个
        else r = mid - 1;
    }
    return l;
}


⭐总结的口诀 : 不管快排和二分, 中点偏左选右边, 中点偏右选左边


偏左 : mid = l + r >> 1


偏右 : mid = l + r + 1 >> 1


解释 : 中点分别对应快排中的分治点和二分中的中点, 当快排中的中点偏左那么就选 j (j 是右边的, i 是左边的),  反之, 选 i 为递归参数;  当二分中的中点偏左时, 那么就选r = mid, 反之选 l = mid.


✨浮点数的二分

浮点数二分主要变化为截至条件为 : ( r - l ) > 1e6  (差的值)


以下为求 n 的平方根的习题, 可以更好地理解浮点数二分

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
int main() {
    double n;
    cin >> n;
    double l = 0, r = n;
    while((r-l) > 1e-6) {
        double mid = (l + r)/2;
        if(n <= mid * mid) r = mid;
        else l = mid;
    }
    printf("%0.2lf\n", l);
    return 0;
}


 


四.  前缀和

前缀和 : 顾名思义是数组中某元素前面的所有和或者部分和


为什么会出现它 : 求一段和的时间复杂度为 O(1)


✨ 一维前缀

a[1],a[2],a[3].....a[n]

s[i] = a[i] + a[i-1]...a[2] + a[1]


a[3] + a[4]...a[14] + a[15] = s[15] - s[3-1]

s[l,r] = s[r] - s[l-1]


for (int i = 1; i <= n; i ++ ) scanf("%d", &a[i]);       //读入n个数


for (int i = 1; i <= n; i ++ ) s[i] = s[i - 1] + a[i];   //处理前缀和


✨二维前缀

s[i][j] 表示二维数组中,左上角(1, 1)到右下角(i, j)所包围的矩阵元素的和


s[i][j] = s[i - 1][j] + s[i][j - 1 ] + a[i] [j] - s[i - 1][j - 1]

以(x1, y1)为左上角,(x2, y2)为右下角的子矩阵的和为:

s[x2, y2] - s[x1 - 1, y2] - s[x2, y1 - 1] + s[x1 - 1, y1 - 1]



五. 差分

差分就是将前缀和的操作反过来


已知 : s[i]   得到   a[i] = s[i] - s[i - 1]


差分的好处在于, 给一段数组加一个数只需要对两个位置( b[i]   和  b[j+1] )进行操作即可


🎉差分数组:


首先给定一个原数组a:a[1], a[2], a[3],,,,,, a[n];


然后我们构造一个数组b : b[1], b[2], b[3],,,,,, b[i];


使得 a[i] = b[1] + b[2] + b[3] + ,,,,,, + b[i]


也就是说,a数组是b数组的前缀和数组,反过来我们把b数组叫做a数组的差分数组。换句话说,每一个a[i]都是b数组中从头开始的一段区间和。


👉构造差分数组的时候的巧妙方法 : 初始化就进行差分


如下:(⚠ : b 数组从下标 1 开始)


a[0 ]= 0; (为了呈现规律, 可有可无)


b[1] = a[1] - a[0];


b[2] = a[2] - a[1];


b[3] = a [3] - a[2];


........


b[n] = a[n] - a[n - 1];


我们只要有b数组,通过前缀和运算,就可以在O(n) 的时间内得到 a 数组 。


✨差分的用处解析 :


给定区间[l, r ],让我们把a数组中的[l, r] 区间中的每一个数都加上c,即 a[l] + c , a[l + 1] + c , a[l + 2] + c ,,,,,, a[r] + c;


始终要记得,a数组是b数组的前缀和数组,比如对b数组的b[i]的修改,会影响到a数组中从a[i]及往后的每一个数。


首先让差分b数组中的 b[l] + c ,通过前缀和运算,a数组变成 a[l] + c ,a[l + 1] + c,,,,,, a[n] + c;


然后我们打个补丁,b[r + 1] - c, 通过前缀和运算,a数组变成 a[r + 1] - c,a[r + 2] - c,,,,,,,a[n] - c;




相关文章
|
2月前
|
算法 数据处理 C语言
C语言中的位运算技巧,涵盖基本概念、应用场景、实用技巧及示例代码,并讨论了位运算的性能优势及其与其他数据结构和算法的结合
本文深入解析了C语言中的位运算技巧,涵盖基本概念、应用场景、实用技巧及示例代码,并讨论了位运算的性能优势及其与其他数据结构和算法的结合,旨在帮助读者掌握这一高效的数据处理方法。
74 1
|
2月前
|
机器学习/深度学习 算法 数据挖掘
K-means聚类算法是机器学习中常用的一种聚类方法,通过将数据集划分为K个簇来简化数据结构
K-means聚类算法是机器学习中常用的一种聚类方法,通过将数据集划分为K个簇来简化数据结构。本文介绍了K-means算法的基本原理,包括初始化、数据点分配与簇中心更新等步骤,以及如何在Python中实现该算法,最后讨论了其优缺点及应用场景。
170 4
|
22天前
|
存储 人工智能 算法
【C++数据结构——内排序】二路归并排序(头歌实践教学平台习题)【合集】
本关任务是实现二路归并算法,即将两个有序数组合并为一个有序数组。主要内容包括: - **任务描述**:实现二路归并算法。 - **相关知识**: - 二路归并算法的基本概念。 - 算法步骤:通过比较两个有序数组的元素,依次将较小的元素放入新数组中。 - 代码示例(以 C++ 为例)。 - 时间复杂度为 O(m+n),空间复杂度为 O(m+n)。 - **测试说明**:平台会对你编写的代码进行测试,提供输入和输出示例。 - **通关代码**:提供了完整的 C++ 实现代码。 - **测试结果**:展示代码运行后的排序结果。 开始你的任务吧,祝你成功!
33 10
|
22天前
|
搜索推荐 算法 数据处理
【C++数据结构——内排序】希尔排序(头歌实践教学平台习题)【合集】
本文介绍了希尔排序算法的实现及相关知识。主要内容包括: - **任务描述**:实现希尔排序算法。 - **相关知识**: - 排序算法基础概念,如稳定性。 - 插入排序的基本思想和步骤。 - 间隔序列(增量序列)的概念及其在希尔排序中的应用。 - 算法的时间复杂度和空间复杂度分析。 - 代码实现技巧,如循环嵌套和索引计算。 - **测试说明**:提供了测试输入和输出示例,帮助验证代码正确性。 - **我的通关代码**:给出了完整的C++代码实现。 - **测试结果**:展示了代码运行的测试结果。 通过这些内容,读者可以全面了解希尔排序的原理和实现方法。
49 10
|
22天前
|
搜索推荐 C++
【C++数据结构——内排序】快速排序(头歌实践教学平台习题)【合集】
快速排序是一种高效的排序算法,基于分治策略。它的主要思想是通过选择一个基准元素(pivot),将数组划分成两部分。一部分的元素都小于等于基准元素,另一部分的元素都大于等于基准元素。然后对这两部分分别进行排序,最终使整个数组有序。(第一行是元素个数,第二行是待排序的原始关键字数据。本关任务:实现快速排序算法。开始你的任务吧,祝你成功!
35 7
|
22天前
|
存储 算法 测试技术
【C++数据结构——树】二叉树的遍历算法(头歌教学实验平台习题) 【合集】
本任务旨在实现二叉树的遍历,包括先序、中序、后序和层次遍历。首先介绍了二叉树的基本概念与结构定义,并通过C++代码示例展示了如何定义二叉树节点及构建二叉树。接着详细讲解了四种遍历方法的递归实现逻辑,以及层次遍历中队列的应用。最后提供了测试用例和预期输出,确保代码正确性。通过这些内容,帮助读者理解并掌握二叉树遍历的核心思想与实现技巧。
41 2
|
1月前
|
存储 运维 监控
探索局域网电脑监控软件:Python算法与数据结构的巧妙结合
在数字化时代,局域网电脑监控软件成为企业管理和IT运维的重要工具,确保数据安全和网络稳定。本文探讨其背后的关键技术——Python中的算法与数据结构,如字典用于高效存储设备信息,以及数据收集、异常检测和聚合算法提升监控效率。通过Python代码示例,展示了如何实现基本监控功能,帮助读者理解其工作原理并激发技术兴趣。
58 20
|
2月前
|
存储 算法 搜索推荐
Python 中数据结构和算法的关系
数据结构是算法的载体,算法是对数据结构的操作和运用。它们共同构成了计算机程序的核心,对于提高程序的质量和性能具有至关重要的作用
|
2月前
|
数据采集 存储 算法
Python 中的数据结构和算法优化策略
Python中的数据结构和算法如何进行优化?
|
2月前
|
算法
数据结构之路由表查找算法(深度优先搜索和宽度优先搜索)
在网络通信中,路由表用于指导数据包的传输路径。本文介绍了两种常用的路由表查找算法——深度优先算法(DFS)和宽度优先算法(BFS)。DFS使用栈实现,适合路径问题;BFS使用队列,保证找到最短路径。两者均能有效查找路由信息,但适用场景不同,需根据具体需求选择。文中还提供了这两种算法的核心代码及测试结果,验证了算法的有效性。
136 23