八种常见顺序存储的算法

目录

1、线性枚举

1)问题描述

2)动图演示

3)示例说明

4)算法描述

5)源码详解

2、前缀和差分

1)问题描述

2)动图演示

3)样例分析

4)算法描述

5)源码详解

3、双指针

1)问题描述

2)动图演示

3)样例说明

4)算法描述

5)源码详解

4、二分枚举

1)问题描述​编辑

2)动图演示

3)样例说明

4)算法描述

5)源码详解

5、三分枚举

6、插入排序

1)问题描述

2)动图演示

3)样例说明

4)算法描述

5)源码详解

7、选择排序

1)问题描述

2)动图演示

3)样例说明

4)算法描述

5)源码详解

8、冒泡排序

1)问题描述

2)动图演示

3)样例说明

4)算法描述

5)源码详解


1、线性枚举

1)问题描述

2)动图演示

3)示例说明

  蓝色的数据代表的是数组数据,红色的数据代表当前枚举到的数据,这样就可以遍历所有的数据进行逻辑处理了。

4)算法描述

  遍历数组,进行条件判断,条件满足则执行逻辑。这里的条件就是 枚举到的数 是否小于 当前最小值,执行逻辑为 将 当前枚举到的数 赋值给 当前最小值

5)源码详解

int findMin(int* nums, int numsSize){int i, min = 100000;for(i = 0; i < numsSize; ++i) {     // (1)if(nums[i] < min) {             // (2)min = nums[i];}}return min;                         // (3)
}

  • (1) 遍历数组中所有的数;
  • (2) 如果 当前枚举到的数 比记录的变量min小,则将它赋值给min;否则,不做任何处理;
  • (3) 最后,min中存储的就是整个数组的最小值。

2、前缀和差分

1)问题描述

2)动图演示

3)样例分析

  如上图所示,只需要记录一个前缀和,然后就可以通过一次减法将区间的值计算出来。时间复杂度 O(1)。这种就是差分的思想。

原理:

sum[r] = a[1] + a[2] + a[3] + a[l-1] + a[l] + a[l + 1] ...... a[r];

sum[l - 1] = a[1] + a[2] + a[3] + a[l - 1];

sum[r] - sum[l - 1] = a[l] + a[l + 1] + ......+ a[r];

4)算法描述

  第一个枚举,利用一个数组sum,存储前 i 个元素的和。
  第二个枚举,读入 m 组数据 l,r,对每组数据,通过 O(1) 获取答案,即
sum[r] - sum[l - 1]​。

5)源码详解

int sum[maxn];
int* prefixSum(int* nums, int numsSize, int m, int *l, int *r){int i;int *ret;for(i = 0; i < numsSize; ++i) {sum[i] = nums[i];if(i) sum[i] += sum[i-1];                 // (1) }ret = (int *) malloc( m * sizeof(int) );    // (2) for(i = 0; i < m; ++i) {int leftsum = l[i] == 0 ? 0 : sum[l[i]-1]; // (3) int rightsum = sum[r[i]];ret[i] = rightsum - leftsum;            // (4) }return ret;
}

  • (1) 计算前缀和;
  • (2) 需要返回的数组;
  • (3) 这里是为了防止数组下标越界;
  • (4) 核心 O(1) 的差分计算;

3、双指针

1)问题描述

2)动图演示

3)样例说明

  维护两个指针 i 和 j,区间 [i,j] 内的子串,应该时刻保持其中所有字符不重复,一旦发现重复字符,就需要自增 i(即执行 i = i + 1);否则,执行 j = j + 1,直到 j 不能再增加为止。
  过程中,记录合法情况下 j − i + 1 的最大值。

4)算法描述

  如上文所述,这种利用问题特性,通过两个指针,不断调整区间,从而求出问题最优解的算法就叫 “尺取法”,由于利用的是两个指针,所以又叫 “双指针” 算法。
  这里 “尺” 的含义,主要还是因为这类问题,最终要求解的都是连续的序列(子串),就好比一把尺子一样,故而得名。

算法描述如下:
  1)初始化 i = 0, j = i − 1,代表一开始 “尺子” 的长度为 0;
  2)增加 “尺子” 的长度,即 j = j + 1;
  3)判断当前这把 “尺子” [i,j] 是否满足题目给出的条件:
    3.a)如果不满足,则减小 “尺子” 长度,即 i = i + 1,回到 3);
    3.b)如果满足,记录最优解,回到 2);

  • 上面这段文字描述的比较官方,其实这个算法的核心,只有一句话:

    满足条件时,j++;不满足条件时,i++;

  • 如图所示,当区间 [i,j] 满足条件时,用蓝色表示,此时 j 自增;反之闪红,此时 i 自增。
    在这里插入图片描述

5)源码详解

int getmaxlen(int n, char *str, int& l, int& r) {int ans = 0, i = 0, j = -1, len;   // 1)memset(h, 0, sizeof(h));           // 2)while (j++ < n - 1) {              // 3)++h[ str[j] ];                 // 4)while (h[ str[j] ] > 1) {      // 5)--h[ str[i] ];++i;}len = j - i + 1;              if(len > ans)                  // 6)ans = len, l = i, r = j;}return ans;
}

  • 1)初始化 i = 0, j = -1,代表 s[i:j] 为一个空串,从空串开始枚举;
  • 2)需要维护一个哈希表,哈希表记录的是当前枚举的区间 s[i:j] 中每个字符的个数;
  • 3)只推进子串的右端点;
  • 4)在哈希表中记录字符的个数;
  • 5)当 h[ str[j] ] > 1满足时,代表出现了重复字符str[j],这时候左端点 i 推进,直到没有重复字符为止;
  • 6)记录当前最优解的长度 j - i + 1,更新;
  • 这个算法执行完毕,我们就可以得到最长不重复子串的长度为 ans,并且 i 和 j 这两个指针分别只自增 n 次,两者自增相互独立,是一个相加而非相乘的关系,所以这个算法的时间复杂度为 O(n) 。

4、二分枚举

1)问题描述

2)动图演示

3)样例说明

  需要找值为 5 的这个元素。
  黄色箭头代表都是左区间端点 l,红色箭头代表右区间端点 r。蓝色的数据为数组数据,绿色的数字代表的是数组下标,初始化 l = 0,r = 7,由于数组有序,则可以直接折半,令 mid =(l+r)/2=3,则 55 一定落入区间 [0,3],这时候令 r = 3,继续执行,直到 l > r 结束迭代。
  最后,当 mid = 2 时,找到数据 5。

4)算法描述

  a)令初始情况下,数组下标从 0 开始,且数组长度为 n,则定义一个区间,它的左端点是      l = 0,右端点是 r = n−1;
  b)生成一个区间中点 mid = (l+r)/2,并且判断 mid 对应的数组元素和给定的目标值的大小关系,主要有三种:
    b.1)目标值 等于 数组元素,直接返回 mid;
    b.2)目标值 大于 数组元素,则代表目标值应该出现在区间 [mid+1,r],迭代左区间端点:l=mid+1;
    b.3)目标值 小于 数组元素,则代表目标值应该出现在区间 [l,mid−1],迭代右区间端点:r=mid−1;
  c)如果这时候 l>r,则说明没有找到目标值,返回 −1;否则,回到 b)继续迭代。

5)源码详解

int search(int *nums, int numsSize, int target) {int l = 0, r = numsSize - 1;         // (1)while(l <= r) {                      // (2)int mid = (l + r) >> 1;          // (3)if(nums[mid] == target) {   return mid;                  // (4)}else if(target > nums[mid]) {l = mid + 1;                 // (5)}else if(target < nums[mid]) {r = mid - 1;                 // (6)}}return -1;                           // (7)
}

  • (1) 初始化区间左右端点;
  • (2) 一直迭代左右区间的端点,直到 左端点 大于 右端点 结束;
  • (3) >> 1等价于除 2,也就是这里mid代表的是lr的中点;
  • (4) nums[mid] == target表示正好找到了这个数,则直接返回下标mid
  • (5) target > nums[mid]表示target这个数在区间 [���+1,�][mid+1,r] 中,所以才有左区间赋值如下:l = mid + 1;
  • (6) target < nums[mid]表示target这个数在区间 [�,���−1][l,mid−1] 中,所以才有右区间赋值如下:r = mid - 1;
  • (7) 这一步呼应了 (2),表示这不到给定的数,直接返回 -1

5、三分枚举

  三分枚举 类似 二分枚举 的思想,也是将区间一下子砍掉一块基本完全不可能的块,从而减小算法的时间复杂度。只不过 二分枚举 解决的是 单调性 问题。而 三分枚举 解决的是 极值问题。

6、插入排序

1)问题描述

  给定一个 n 个元素的数组,数组下标从 0 开始,采用「 插入排序 」将数组按照 「升序」排列。

2)动图演示

3)样例说明

图示含义
蓝色柱形代表尚未排好序的数
绿色柱形代表正在执行 比较 和 移动 的数
橙色柱形代表已经排好序的数
红色柱形代表待执行插入的数

  我们看到,首先需要将 「第二个元素」 和 「第一个元素」 进行 「比较」,如果 前者 小于等于 后者,则将 后者 进行向后 「移动」前者 则执行插入;
  然后,进行第二轮「比较」,即 「第三个元素」 和 「第二个元素」、「第一个元素」 进行 「比较」, 直到 「前三个元素」 保持有序 。
  最后,经过一定轮次的「比较」 和 「移动」之后,一定可以保证所有元素都是 「升序」 排列的。

4)算法描述

整个算法的执行过程分以下几步:
  1) 循环迭代变量 i=1→n−1;
  2) 每次迭代,令 x=a[i],j=i−1,循环执行比较 x 和 a[j],如果产生 x≤a[j] 则执 行 a[j+1]=a[j]。然后执行j=j+1,继续执行 2);否则,跳出循环,回到 1)

5)源码详解

#include <stdio.h>int a[1010];void Input(int n, int *a) {for(int i = 0; i < n; ++i) {scanf("%d", &a[i]);}
}void Output(int n, int *a) {for(int i = 0; i < n; ++i) {if(i)printf(" ");printf("%d", a[i]);}puts("");
}void InsertSort(int n, int *a) {       // (1)int i, j; for(i = 1; i < n; ++i) {int x = a[i];                  // (2)for(j = i-1; j >= 0; --j) {    // (3)if(x <= a[j]) {            // (4)a[j+1] = a[j];         // (5)}elsebreak;                 // (6)}a[j+1] = x;                    // (7)}
} int main() {int n;while(scanf("%d", &n) != EOF) {Input(n, a);InsertSort(n, a);Output(n, a);}return 0;
} 

  • (1) void InsertSort(int n, int *a)为 插入排序 的实现,代表对a[]数组进行升序排序。
  • (2) 此时a[i]前面的 i-1个数都认为是排好序的,令x = a[i]
  • (3) 逆序的枚举所有的已经排好序的数;
  • (4) 如果枚举到的数a[j]比需要插入的数x大,则当前数往后挪一个位置;
  • (5) 执行挪位置的 �(1)O(1) 操作;
  • (6) 否则,跳出循环;
  • (7) 将x插入到合适位置;

7、选择排序

1)问题描述

  给定一个 �n 个元素的数组,数组下标从 00 开始,采用「 选择排序 」将数组按照 「升序」排列。

2)动图演示

3)样例说明

图示含义
蓝色柱形代表尚未排好序的数
绿色柱形代表正在执行 比较 的数
橙色柱形代表已经排好序的数
红色柱形有两种:1、记录最小元素 2、执行交换的元素

  我们发现,首先从 「第一个元素」 到 「最后一个元素」 中选择出一个 「最小的元素」,和 「第一个元素」 进行 「交换」
  然后,从 「第二个元素」 到 「最后一个元素」 中选择出一个 「最小的元素」,和 「第二个元素」 进行 「交换」
  最后,一定可以保证所有元素都是 「升序」 排列的。

4)算法描述

整个算法的执行过程分以下几步:
  1) 循环迭代变量 i=0→n−1;
  2) 每次迭代,令 min=i,j=i+1;
  3) 循环执行比较 a[j] 和 a[min],如果产生 a[j]<a[min] 则执行 min=j。执行 j=j+1,继续执行这一步,直到 j==n;
  4) 交换 a[i] 和 a[min],回到 1)

5)源码详解

#include <stdio.h>int a[1010];void Input(int n, int *a) {for(int i = 0; i < n; ++i) {scanf("%d", &a[i]);}
}void Output(int n, int *a) {for(int i = 0; i < n; ++i) {if(i)printf(" ");printf("%d", a[i]);}puts("");
}void Swap(int *a, int *b) {int tmp = *a;*a = *b;*b = tmp;
}void SelectionSort(int n, int *a) {  // (1)int i, j;for(i = 0; i < n - 1; ++i) {     // (2)int min = i;                 // (3)for(j = i+1; j < n; ++j) {   // (4)if(a[j] < a[min]) {min = j;             // (5)}}Swap(&a[i], &a[min]);        // (6) }
}int main() {int n;while(scanf("%d", &n) != EOF) {Input(n, a);SelectionSort(n, a);Output(n, a);}return 0;
} 

  • (1) void SelectionSort(int n, int *a)为选择排序的实现,代表对a[]数组进行升序排序。
  • (2) 从首元素个元素开始进行 n−1 次跌迭代。
  • (3) 首先,记录min代表当前第 i 轮迭代的最小元素的下标为 i。
  • (4) 然后,迭代枚举第 i+1 个元素到 最后的元素。
  • (5) 选择一个最小的元素,并且存储下标到 min中。
  • (6) 将 第 i 个元素 和 最小的元素 进行交换。

8、冒泡排序

1)问题描述

  给定一个 n 个元素的数组,数组下标从 0 开始,采用「 冒泡排序 」将数组按照 「升序」排列。

2)动图演示

3)样例说明

图示含义
蓝色柱形代表尚未排好序的数
绿色柱形代表正在执行比较的两个数
黄色柱形代表已经排好序的数

  我们看到,首先需要将 「第一个元素」 和 「第二个元素」 进行 「比较」,如果 前者 大于 后者,则进行 「交换」,然后再比较 「第二个元素」 和 「第三个元素」 ,以此类推,直到 「最大的那个元素」 被移动到 「最后的位置」 。
  然后,进行第二轮「比较」,直到 「次大的那个元素」 被移动到 「倒数第二的位置」 。
  最后,经过一定轮次的「比较」 和 「交换」之后,一定可以保证所有元素都是 「升序」 排列的。

4)算法描述

整个算法的执行过程分以下几步:
  1) 循环迭代变量 i=0→n−1;
  2) 每次迭代,令 j=i,循环执行比较 a[j] 和 a[j+1],如果产生 a[j]>a[j+1] 则交换两者的值。然后执行 j=j+1,这时候对 j 进行判断,如果 j≥n−1,则回到 1),否则继续执行 2)

5)源码详解

#include <stdio.h>int a[1010];void Input(int n, int *a) {for(int i = 0; i < n; ++i) {scanf("%d", &a[i]);}
}void Output(int n, int *a) {for(int i = 0; i < n; ++i) {if(i)printf(" ");printf("%d", a[i]);}puts("");
}void Swap(int *a, int *b) {int tmp = *a;*a = *b;*b = tmp;
}void BubbleSort(int n, int *a) {             // (1)bool swapped;int last = n;do {swapped = false;                     // (2)for(int i = 0; i < last - 1; ++i) {  // (3)if(a[i] > a[i+1]) {              // (4)Swap(&a[i], &a[i+1]);        // (5)swapped = true;              // (6)}}--last;}while (swapped);
} int main() {int n;while(scanf("%d", &n) != EOF) {Input(n, a);BubbleSort(n, a);Output(n, a);}return 0;
} 

  • (1) void BubbleSort(int n, int *a)为冒泡排序的实现,代表对a[]数组进行升序排序。
  • (2) swapped标记本轮迭代下来,是否有元素产生了交换。
  • (3) 每次冒泡的结果,会执行last的自减,所以待排序的元素会越来越少。
  • (4) 如果发现两个相邻元素产生逆序,则将它们进行交换。保证右边的元素一定不比左边的小。
  • (5) swap实现了元素的交换,这里需要用&转换成地址作为传参。
  • (6) 标记更新。一旦标记更新,则代表进行了交换,所以下次迭代必须继续。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

隆道吴树贵:生成式人工智能在招标采购中的应用

12月22日&#xff0c;由中国招标投标协会主办的招标采购数字发展大会在北京召开&#xff0c;北京隆道网络科技有限公司总裁吴树贵受邀出席大会&#xff0c;并在“招标采购数字化交易创新成果”专题会议上发言&#xff0c;探讨生成式人工智能如何在招标采购业务中落地应用。 本次…

第二十一章Java网络通信

网络通信这一章 基本分为三个部分 网络基础概念和TCP,UDP这三个部分主要如下&#xff1a; 计算机网络实现了堕胎计算机间的互联&#xff0c;使得它们彼此之间能够进行数据交流。网络应用程序就是再已连接的不同计算机上运行的程序&#xff0c;这些程序借助于网络协议&#x…

Github项目推荐:KaTeX

项目地址 GitHub - KaTeX/KaTeX: Fast math typesetting for the web. 项目描述 这是一个渲染公式的JavaScript库。有时候可能网页中需要写一些公式&#xff0c;但html本身并没有提供相应的标签。这个时候这个库就能派上用场了。 项目截图

JVM篇:JVM内存结构

程序计数器 程序计数器英文名叫&#xff1a;Program Counter Register 作用&#xff1a;用来记录下一条jvm指令的地址行号。 先来查看一段jvm指令&#xff0c;这些指令对应的java代码就是输出1-5 操作系统运行该Java程序时具体流程如下 语言解释&#xff1a;源文件通过编译转…

常用的 linux 命令

常用的 linux 命令 1.从其他机器拷贝文件夹2.查看哪个程序在用特定端口3.实时监控日志文件内容4.查看指定用户拥有的进程5.查看磁盘空间使用情况6.文件搜索which&#xff08;whereis&#xff09; 显示系统命令所在目录find 查找任何文件或目录1&#xff09; 根据文件名称查找2)…

MATLAB - 使用 YOLO 和基于 PCA 的目标检测,对 UR5e 的半结构化智能垃圾箱拣选进行 Gazebo 仿真

系列文章目录 前言 本示例展示了在 Gazebo 中使用 Universal Robots UR5e cobot 模拟智能垃圾桶拣选的详细工作流程。本示例提供的 MATLAB 项目包括初始化、数据生成、感知、运动规划和积分器模块&#xff08;项目文件夹&#xff09;&#xff0c;可创建完整的垃圾桶拣选工作流…

[Angular] 笔记 11:可观察对象(Observable)

chatgpt: 在 Angular 中&#xff0c;Observables 是用于处理异步数据流的重要工具。它们被广泛用于处理从异步操作中获取的数据&#xff0c;比如通过 HTTP 请求获取数据、定时器、用户输入等。Observables 提供了一种机制来订阅这些数据流&#xff0c;并可以在数据到达时执行相…

启明智显开源项目分享|基于Model 3c芯片的86中控面板ZX3D95CM20S-V11项目软硬件全开源

前言&#xff1a; 本文为4寸 480*480 RGB接口IPS全面触屏的86中控面板&#xff08;RT-ThreadLVGL&#xff09;软硬件开源干货内容&#xff0c;该项目是综合性非常强的RTOS系列项目&#xff01;项目主控芯片使用 Model 3c&#xff0c;整体实现了简化版本的86中控面板的功能需求…

git 如何将某个分支的某个提交复制到另外一个分支

请直接去看原文: 原文链接:git 如何将某个分支的某个提交复制到另外一个分支_gitlab里面的markdown文件可以复用其他分支的吗-CSDN博客 --------------------------------------------------------------------------------------------------------------------------------…

深入浅出:分布式、CAP 和 BASE 理论(荣耀典藏版)

大家好&#xff0c;我是月夜枫&#xff0c;一个漂泊江湖多年的 985 非科班程序员&#xff0c;曾混迹于国企、互联网大厂和创业公司的后台开发攻城狮。 在计算机科学领域&#xff0c;分布式系统是一门极具挑战性的研究方向&#xff0c;也是互联网应用中必不可少的优化实践&…

百度每天20%新增代码由AI生成,Comate SaaS服务8000家客户 采纳率超40%

12月28日&#xff0c;由深度学习技术及应用国家工程研究中心主办的WAVE SUMMIT深度学习开发者大会2023在北京召开。百度首席技术官、深度学习技术及应用国家工程研究中心主任王海峰现场公布了飞桨文心五载十届最新生态成果&#xff0c;文心一言最新用户规模破1亿&#xff0c;截…

【数据库系统概论】第6章-关系数据库理论

真别看吧&#xff0c;抄ppt而已啊 文章目录 6.1 引言6.2 规范化6.2.1 函数依赖6.2.2 码6.2.3 范式&#xff08;Normal Form&#xff09;6.2.4 BC范式6.2.5 规范化小结 6.1 引言 我们有这样一张表&#xff1a; but 为啥这样设计呢&#xff1f;由此引出怎样设计一个关系数据库…

三维可视化智慧工地源码,数字孪生可视化大屏,微服务架构+Java+Spring Cloud +UniApp +MySql

源码技术说明 微服务架构JavaSpring Cloud UniApp MySql&#xff1b;支持多端展示&#xff08;PC端、手机端、平板端&#xff09;;数字孪生可视化大屏&#xff0c;一张图掌握项目整体情况;使用轻量化模型&#xff0c;部署三维可视化管理&#xff0c;与一线生产过程相融合&#…

设计模式----解释器模式

一、简介 解释器模式使用频率并不高&#xff0c;通常用来构建一个简单语言的语法解释器&#xff0c;它只在一些非常特定的领域被用到&#xff0c;比如编译器、规则引擎、正则表达式、sql解析等。 解释器模式是行为型设计模式之一&#xff0c;它的原始定义为&#xff1a;用于定义…

自动生成数控加工的轨迹刀具轨迹阿基米德螺旋线(3D)

文章目录 1. 阿基米德螺旋线2. 生成步骤目标: 基于点云自动生成阿基米德螺旋线轨迹点 针对的是半球形模型效果 1. 阿基米德螺旋线 阿基米德螺旋线(Archimedean spiral)是一种数学曲线,由古希腊数学家阿基米德(Archimedes)在公元前225年左右首次研究和描述。这条曲线的方…

GIT如何重新生成ssh密钥过程

GIT如何重新生成ssh密钥过程 一、生成密钥前需要把之前的密钥删除吆 第一步&#xff1a;重新配置用户名和邮箱&#xff08; Git Bash 或命令窗口&#xff09; 1、配置用户命令&#xff1a;git config --global user.name “xxxxx” 2、配置邮箱命令&#xff1a;git config …

windows进行udp端口转发,解决项目中服务器收不到组播数据的问题

说明 windows7的netsh interface portproxy命令只支持tcp端口转发 如果要进行udp端口转发可以使用sokit 运行sokit 端口转发&#xff08;以为tcp作为讲解&#xff0c;udp类似&#xff09; 选择转发器 输入监听地址&#xff08;SRC地址&#xff09;和端口 输入转发地址&am…

springboot整合minio做文件存储

一,minio介绍 MinIO 是一个基于Apache License v2.0开源协议的对象存储服务。它兼容亚马逊S3云存储服务接口&#xff0c;非常适合于存储大容量非结构化的数据&#xff0c;例如图片、视频、日志文件、备份数据和容器/虚拟机镜像等&#xff0c;而一个对象文件可以是任意大小&…

微信邀请函怎么做_重塑品牌形象,点燃营销之火

在数字化时代&#xff0c;微信已成为人们生活中不可或缺的一部分。微信邀请函&#xff0c;作为一种新型的邀请方式&#xff0c;不仅具有时尚感&#xff0c;更承载着邀请者的诚意与创意。今天&#xff0c;让我们一起探讨如何为主题撰写一篇关于微信邀请函的1000字营销软文&#…

ES6的一些高级技巧

✨ 专栏介绍 在现代Web开发中&#xff0c;JavaScript已经成为了不可或缺的一部分。它不仅可以为网页增加交互性和动态性&#xff0c;还可以在后端开发中使用Node.js构建高效的服务器端应用程序。作为一种灵活且易学的脚本语言&#xff0c;JavaScript具有广泛的应用场景&#x…