C++数据结构算法(二)排序算法集合(一)

简介: C++数据结构算法(二)排序算法集合(一)

排序算法(Sorting Algorithm) 的作用在于对于给定的一个元素序列,输出满足某种顺序的该序列的一个排列。


image.png

代码实现 —— 数最小值


数组最小值


首先,如何找到n个元素的最小值,并记录它的位置?


最开始,我们默认最小值出现在数组的第1位,所以,用于记录最小值位置的变量min_pos初始值为1。


然后,枚举数组中的每个元素,并且将当前记录的最小值和枚举到的第i个元素作比较,如果当前枚举到的元素更小,说明最小值不可能出现在原来的min_pos位置,而更有可能出现的位置i。所以,将min_pos更新为i。


当扫描完整个元素后,min_pos中的位置就是最小值出现的位置。

#include <bits/stdc++.h>
using namespace std;
int a[1010];
int n;
int main() {
    // 输入
    cin >> n;
    for (int i = 1; i <= n; ++i) cin >> a[i]; 
    //注意:为保持认知一致,我们直接从数组第2个元素开始。数组第二个元素索引为1,不是从索引为0的元素开始哦。
    int min_pos = 1;    // 设置最小值位置的初始值为0,即a[1] = 0
    for (int i = 2; i <= n; ++i) {
        if (a[i] < a[min_pos])  // 比较当前枚举到的元素与当前记录位置的元素
            min_pos = i;        // 如果当前记录位置的元素更小,则更新最小值出现的位置
    }
    cout << "minimum value = " << a[min_pos] << endl;    // 输出最小值
    cout << "minimum value pos = " << min_pos << endl;   // 输出最小值的位置
    return 0;
}
int main() {
    int min_pos = 1;    // 设置最小值位置的初始值为0,即a[1] = 0
    for (int i = 2; i <= n; ++i) {
    // TODO 请补全代码找到最小值
        if(a[i]<a[min_pos])  // 比较当前枚举到的元素与当前记录位置的元素
           min_pos = i;  // 如果当前记录位置的元素更小,则更新最小值出现的位置
    }
    cout << "minimum value = " << a[min_pos] << endl;    // 输出最小值
    cout << "minimum value pos = " << min_pos << endl;   // 输出最小值的位置
    return 0;
}

代码实现 —— 实现选择排序


实现选择排序


所以,给定长度为n的序列,我们现在用之前描述的选择排序过程用C++语言实现出来,将序列从小到大排序。


最外层用for循环枚举当前应该归位的第i个元素;


内层用上一步方法的寻找最小值位置,找出第i位一直到第n位的最小值,并且将其与原来的第i位元素交换。


具体实现代码如下:

#include <bits/stdc++.h>
using namespace std;
int a[1010];
int n;
int main() {
    // 输入
    cin >> n;
    for (int i = 1; i <= n; ++i) cin >> a[i];
    // 选择排序过程
    for (int i = 1; i < n; ++i) {  // 枚举应该归位的第i个元素,这里因为前n-1位归为以后,
                                   // 第n位也会归位,所以我们只枚举到n-1。
        int min_pos = i;           // 将最小值位置设置为当前范围i~n的首位
        for (int j = i + 1; j <= n; ++j) { // 将第i个元素和剩下的元素相比较
            if (a[j] < a[min_pos]) {       // 如果当前元素小于之前维护的最小值
                min_pos = j;               // 更改最小值出现的位置
            }
        }
        swap(a[i], a[min_pos]);            // 将最小值与第i个位置交换
    }
    // 输出
    for (int i = 1; i <= n; ++i) 
        cout << a[i] << ' ';
    return 0;
}


总结


选择排序的基本思路是:


按照1~n的顺序,将每个元素依次归位。


当归位第i个元素时,我们需要选择出第i个元素到第n个元素的最小值,并且与第i个位置的元素交换。此时,1~i的元素分别为第1小到第i小的元素。


当第n个元素归位完毕以后,整个序列的排序过程结束。


冒泡排序:

基本思想


试想一下,如果在上体育课的时候,通常学生都会随意站成一列,但是体育老师会帮忙调整学生的站位使得最终顺序是按照身高排序的。那么,回忆一下体育老师是如何调整顺序的呢?


假设体育老师想将学生从左到右按照身高从矮到高排序。通常情况下,他会从左到右扫视学生的身高。如果左边有一个同学,个子比右边的同学高,他就要将其进行调整。具体调整方法是:


如果一个同学比他右边的同学高,就让这两个同学交换位置。

如果交换过后,这个同学仍然比新的右边的同学高,那么继续交换,直到他不在比右边的同学高。

这样进行了若干次以后,队列就按照身高排好序了。


image.png

我们发现,这里我们使用了将原问题转换为规模更小的子问题的思路。


所以在选择排序的过程,相当于每次将当前序列未排序部分的最小元素归位,将未排序的序列长度缩小一位。同样,如果我们能够通过某种方式,把序列中的最大值移动到序列最后面,也可以起到将未排序序列长度缩小一位的效果。

image.png


所以,这节课我们设计的排序过程分为两步:


通过“体育老师交换方法”,使得每次我们都能把最大值移动到序列的最后一位。

利用上面的步骤进行问题转换,将未排序的序列长度缩减一个。

这就是冒泡排序的具体思路,而上面提到的“将最大值移动到最后一位”的操作,就叫做冒泡操作。


冒泡排序的思路


总结冒泡排序的思路:

冒泡排序分为n-1个阶段。

在第1个阶段,通过“冒泡”,将前n个元素的最大值移动到序列的最后一位。此时只剩前n-1个元素未排序。

在第i个阶段,此时序列前n-i+1个元素未排序。通过“冒泡”,我们将前n-i+1个元素中的最大值移动到最后一位。此时只剩前n-i个元素未排好序。

最终到第n-1个阶段,前2个元素未排序。将其中的较大值移动到后一位,则整个序列排序完毕。

单独冒泡:

if (a[i] > a[i + 1]) swap(a[i], a[i + 1]);

单独的冒泡过程:

#include <bits/stdc++.h>
#define N 1010
using namespace std;
int n, a[N];
int main() {
    // 输入
    cin >> n;
    for (int i = 1; i <= n; ++i) cin >> a[i];
    // 冒泡阶段:连续交换过程
    for (int i = 1; i < n; ++i)    // 枚举两两交换的前一个元素序号
        if (a[i] > a[i + 1]) swap(a[i], a[i + 1]);    // 如果前一个元素大于后一个,就进行交换
    // 输出
    for (int i = 1; i <= n; ++i) cout << a[i] << ' ';
    cout << endl;
    return 0;
}


完整的冒泡过程:

#include <bits/stdc++.h>
#define N 1010
using namespace std;
int n, a[N];
int main() {
    // 输入
    cin >> n;
    for (int i = 1; i <= n; ++i) cin >> a[i];
    // 冒泡排序
    for (int i = 1; i < n; ++i) {  // 一共n-1个阶段,在第i个阶段,未排序序列长度从n-i+1到n-i。
        for (int j = 1; j <= n - i; ++j)  // 将序列从1到n-i+1的最大值,移到n-i+1的位置
            if (a[j] > a[j + 1])    // 其中j枚举的是前后交换元素的前一个元素序号
                swap(a[j], a[j + 1]);
    }
    // 输出
    for (int i = 1; i <= n; ++i) cout << a[i] << ' ';
    cout << endl;
    return 0;
}

复杂度分析


从代码中,我们可以看到冒泡排序的主干部分有两层循环,并且每一层的循环次数都在O(n)O(n)左右的数量级。


所以完整的冒泡排序时间复杂度是O(n^2)O(n2)。


总结


冒泡排序和选择排序一样,都是将原问题转换为长度减一的子问题的过程。


冒泡排序分为n-1个阶段,每个阶段通过“冒泡”的过程,将未排序序列中的最大值移动到最后一位。


冒泡的过程,具体是通过一段连续交换过程使得最大元素被“传送”到最后一位。


练习4·代码题


明明的随机数_冒泡排序


明明想在学校中请一些同学一起做一项问卷调查,为了实验的客观性,他先用计算机生成了N个1到1000之间的随机整数(N≤100),对于其中重复的数字,只保留一个,把其余相同的数去掉,不同的数对应着不同的学生的学号。然后再把这些数从小到大排序,按照排好的顺序去找同学做调查。请你协助明明完成“去重”与“排序”的工作。


输入描述:


每组输入有2行,第1行为1个正整数,表示所生成的随机数的个数N,第2行有N个用空格隔开的正整数,为所产生的随机数。


输出描述:


每组输出也是2行,第1行为1个正整数M,表示不相同的随机数的个数。第2行为M个用空格隔开的正整数,为从小到大排好序的不相同的随机数。


示例 1:


输入:

10
20 40 32 67 40 20 89 300 400 15

输出:

8
15 20 32 40 67 89 300 400


代码:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 110
using namespace std;
int a[N], n, cnt;
int main() {
    scanf("%d", &n);
    for (int i = 0; i < n; ++i) scanf("%d", &a[i]);
    // TODO 请补全下述代码,使用冒泡排序完成排序
    for (int i=1; i<n; i++){
        for (int j=0; j<n-i; j++){
            if (a[j]>a[j+1]) swap(a[j] , a[j+1]);
        }
    }
    cnt = 0;
    for (int i = 0; i < n; ++i) 
        if (i == 0 || a[i] != a[i - 1]) 
            a[cnt++] = a[i];
    printf("%d\n", cnt);
    for (int i = 0; i < cnt; ++i) printf("%d ", a[i]); 
    return 0;
}


相关文章
|
2月前
|
机器学习/深度学习 算法 数据挖掘
K-means聚类算法是机器学习中常用的一种聚类方法,通过将数据集划分为K个簇来简化数据结构
K-means聚类算法是机器学习中常用的一种聚类方法,通过将数据集划分为K个簇来简化数据结构。本文介绍了K-means算法的基本原理,包括初始化、数据点分配与簇中心更新等步骤,以及如何在Python中实现该算法,最后讨论了其优缺点及应用场景。
117 4
|
3月前
|
存储 人工智能 算法
数据结构与算法细节篇之最短路径问题:Dijkstra和Floyd算法详细描述,java语言实现。
这篇文章详细介绍了Dijkstra和Floyd算法,这两种算法分别用于解决单源和多源最短路径问题,并且提供了Java语言的实现代码。
99 3
数据结构与算法细节篇之最短路径问题:Dijkstra和Floyd算法详细描述,java语言实现。
|
11天前
|
存储 运维 监控
探索局域网电脑监控软件:Python算法与数据结构的巧妙结合
在数字化时代,局域网电脑监控软件成为企业管理和IT运维的重要工具,确保数据安全和网络稳定。本文探讨其背后的关键技术——Python中的算法与数据结构,如字典用于高效存储设备信息,以及数据收集、异常检测和聚合算法提升监控效率。通过Python代码示例,展示了如何实现基本监控功能,帮助读者理解其工作原理并激发技术兴趣。
49 20
|
3天前
|
存储 算法 安全
基于红黑树的局域网上网行为控制C++ 算法解析
在当今网络环境中,局域网上网行为控制对企业和学校至关重要。本文探讨了一种基于红黑树数据结构的高效算法,用于管理用户的上网行为,如IP地址、上网时长、访问网站类别和流量使用情况。通过红黑树的自平衡特性,确保了高效的查找、插入和删除操作。文中提供了C++代码示例,展示了如何实现该算法,并强调其在网络管理中的应用价值。
|
1天前
|
存储 算法 安全
基于哈希表的文件共享平台 C++ 算法实现与分析
在数字化时代,文件共享平台不可或缺。本文探讨哈希表在文件共享中的应用,包括原理、优势及C++实现。哈希表通过键值对快速访问文件元数据(如文件名、大小、位置等),查找时间复杂度为O(1),显著提升查找速度和用户体验。代码示例展示了文件上传和搜索功能,实际应用中需解决哈希冲突、动态扩容和线程安全等问题,以优化性能。
|
8天前
|
算法 安全 C++
用 C++ 算法控制员工上网的软件,关键逻辑是啥?来深度解读下
在企业信息化管理中,控制员工上网的软件成为保障网络秩序与提升办公效率的关键工具。该软件基于C++语言,融合红黑树、令牌桶和滑动窗口等算法,实现网址精准过滤、流量均衡分配及异常连接监测。通过高效的数据结构与算法设计,确保企业网络资源优化配置与安全防护升级,同时尊重员工权益,助力企业数字化发展。
32 4
|
2月前
|
数据采集 存储 算法
Python 中的数据结构和算法优化策略
Python中的数据结构和算法如何进行优化?
|
2月前
|
算法
数据结构之路由表查找算法(深度优先搜索和宽度优先搜索)
在网络通信中,路由表用于指导数据包的传输路径。本文介绍了两种常用的路由表查找算法——深度优先算法(DFS)和宽度优先算法(BFS)。DFS使用栈实现,适合路径问题;BFS使用队列,保证找到最短路径。两者均能有效查找路由信息,但适用场景不同,需根据具体需求选择。文中还提供了这两种算法的核心代码及测试结果,验证了算法的有效性。
112 23
|
2月前
|
并行计算 算法 测试技术
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面,旨在通过综合策略提升程序性能,满足实际需求。
65 1
|
2月前
|
算法 vr&ar 计算机视觉
数据结构之洪水填充算法(DFS)
洪水填充算法是一种基于深度优先搜索(DFS)的图像处理技术,主要用于区域填充和图像分割。通过递归或栈的方式探索图像中的连通区域并进行颜色替换。本文介绍了算法的基本原理、数据结构设计(如链表和栈)、核心代码实现及应用实例,展示了算法在图像编辑等领域的高效性和灵活性。同时,文中也讨论了算法的优缺点,如实现简单但可能存在堆栈溢出的风险等。
59 0