数据结构(三) 排序/并查集/图

目录

1. 排序

2.并查集

3.图


1.排序:

1.1 概念:

        排序就是将数据按照某种规则进行排列, 具有某种顺序. 分为内排序和外排序.

内排序就是: 将数据放在内存中的排序; 外排序是: 数据太多无法在内存中排序的.

 1.2 插入排序:

        插入排序包含: 直接插入排序和希尔排序.

(1) 直接插入排序:

        (这里图是借用其他博主的), 直接插入排序就是将第i个数值进行和前i数值依次比较, i数值小就一直放到前面, 直到值比他更小或者比完. 时间复杂度是O(N^2). 稳定性: 稳定.

 

void InsertSort(int* a, int n)
{for(int i = 0; i < n - 1; i++){int end = i;int tmp = a[end+1];while(end >= 0){if(tmp < a[end]){a[end + 1] = a[end];end--;}else{break;}}a[end + 1] = tmp;}
}
 (2) 希尔排序:

        是采用gap进行分割前后数, 第i个数和i+gap个数进行比较如果a[i+gap]小于a[i]就交换.

gap算一趟, gap每次缩小1/2;  进行每趟调整. 时间复杂度是:O(NlogN); 稳定性: 不稳定.

void ShellSort(int* a, int n)
{int gap = n;while(gap > 1){gap = gap / 2;for(int i = 0; i < n - gap; i++){int end = i;int tmp = a[end + gap];while(end >= 0){if(tmp < a[end]){a[end + gap] = a[end];end -= gap; }else{break;}}a[end + gap] = tmp;}}
}

1.3 选择排序:

选择排序包括选择排序和堆排序:

(1) 选择排序:

                每趟找到比最小的数, 遍历全数列的那种, 然后进行交换i和最小数值的位置. 时间复杂度是O(N^2); 稳定性: 不稳定.

        还可以依次选两个数, 最大和最小, 放在左边和右边, 进行遍历选择.

void Swap(int* x, int* y)
{int tmp = *x;*x = *y;*y = tmp;
}void SelectSort(int* a, int n)
{for(int i = 0; i < n; i++){int start = i;int min = start;while(start < n){if(a[start] < a[min])min = start;start++;}Swap(&a[i], &a[min]);}
}void SelectSort(int* a, int n)
{int left = 0;int right = n - 1;while(left < right){int minIndex = left;int maxIndex = left;for(int i = left; i <= right; i++){if(a[i] < a[minIndex])minIndex = i;if(a[i] > a[maxIndex])maxIndex = i;}Swap(&a[minIndex], &a[left]);if(left == maxIndex){maxIndex = minIndex;}Swap(&a[maxIndex], &a[right]);left++;right--;}
}
(2) 堆排序:

        具体看上一篇博客:数据结构(二)

void AdjustDown(int* a, int n, int parent)
{int child = parent * 2 + 1;while(child < n){if(child + 1 < n && a[child + 1] < a[child]){child++;}if(a[child] < a[parent]){Swap(&a[child], &a[parent]);parent = child;child = parent * 2 + 1;}else{break;}}
}void StackSort(int* a, int n)
{for(int i = (n-1-1) / 2; i >= 0; i--){AdjustDown(a, n, i);}int end = n - 1;while(end){Swap(&a[0], &a[end]);AdjustDown(a, end, 0);end--;}
}

 1.4 交换排序:

        交换排序包含冒泡排序和快速排序:

(1) 冒泡排序:

        相邻两个数进行比较, 大的就交换,这样到最后的就一定是最大的数, 下一次只要遍历到这个最大数前一个即可. 时间复杂度是: O(N^2) ; 稳定性: 稳定;

void BubbleSort(int* a, int n)
{int end = 0;for(end = n - 1; end >= 0; end--){int exchange = 0;for(int i = 0; i < end; i++){if(a[i] > a[i+1]){Swap(&a[i], &a[i+1]);exchange = 1;}}if(exchange = 0)break;}
}
 (2) 快速排序:

        时间复杂度就是O(NlogN), 稳定性: 不稳定.

 a.hoare版本

        最左边作为key进行比较的值, 其次就是left和right不断往中间走, right找到小于key的, left找到大于key的, 然后交换right和left; 将left和right相遇的点在进行分治法使用快速排序.

        
void QuickSort1(int* a, int begin, int end)
{if(begin >= end)return;int left = begin;int right = end;int keyi = left;while(left < right){while(left < right && a[right] >= a[keyi]){right--;}while(left < right && a[left] <= a[keyi]){left++;}if(left < right){Swap(&a[left], &a[right]);}}int meeti = left;Swap(&a[keyi], &a[meeti]);QuickSort1(a, begin, meeti-1);QuickSort1(a,  meeti+1, end);
}
b.挖坑法:

        和上面差别就是把key下标的值取出来了, 但是过程还是和上面一样.

void QuickSort2(int* a, int begin, int end)
{if(begin >= end)return;int left = begin;int right = end;int key = a[left];while(left < right){while(left < right && a[right] >= key){right--;} a[left] = a[right];while(left < right && a[left] <= key){left++;}a[right] = a[left];}int meeti = left;a[meeti] = key;QuickSort2(a, begin, meeti - 1);QuickSort2(a, meeti + 1, end);
}
 c. 前后指针法:

//三数取中;
int GetMidIndex(int* a, int left, int right)
{int mid = left + (right - left) / 2;if(a[mid] > a[left]){if(a[mid] < a[right])return mid;else if(a[left] > a[right])return left;elsereturn right;}
}void QuickSort3(int* a, int begin, int end)
{if(begin >= end)return;int minIndex = GetMidIndex(a, begin, end);Swap(&a[begin], &a[minIndex]);int prev = begin;int cur = begin + 1;int keyi = begin;while(cur <= end){if(a[cur] < a[keyi] && ++prev != cur){Swap(&a[prev], &a[cur]);}cur++;}int meeti = prev;Swap(&a[keyi], &a[meeti]);QuickSort3(a, begin, meeti-1);QuickSort3(a, meeti + 1, end);
}

1.5 归并排序:

        归并排序是采用分治的方法, 将数据对半分开, 使用额外的空间进行收集对半开的数组之间的比较大小的数据. 时间复杂度是O(NlogN); 稳定性: 不稳定.

void _MergeSort(int* a, int left, int right, int* tmp)
{if(left >= right)return;int mid = left + (right - left) / 2;_MergeSort(a, left, mid, tmp);_MergeSort(a, mid+1, right, tmp);int begin1 = left, end1 = mid;int begin2 = mid + 1, end2 = right;int i = left;while(begin1 <= end1 && begin2 <= end2){if(a[begin1] < a[begin2])tmp[i++] = a[begin1++];elsetmp[i++] = a[begin2++];}while(begin1 <= end1)tmp[i++] = a[begin1++];while(begin2 <= end2)tmp[i++] = a[begin2++];for(int j = left; j <= right; j++)a[j] = tmp[j];}void MergeSort(int* a, int n)
{int* tmp = (int*)malloc(sizeof(int) * n);if(tmp == nullptr){printf("malloc fail\n");exit(-1);}_MergeSort(a, 0, n - 1, tmp);free(tmp);
}

 1.6 计数排序:

        采用计数每个元素出现的次数, 以及最小值和最大值记录, 利用额外空间记录每个元素出现次数, 然后再将原来数组进行额外数组的替换.

void CountSort(int* a, int n)
{int min = a[0];int max = a[0];for(int i = 0; i < n; i++){if(a[i] < min)min = a[i];if(a[i] > max)max = a[i];}int range = max - min + 1;int* count = (int*)calloc(range, sizeof(int));if(count == nullptr){printf("malloc fail!");exit(-1);}for(int i = 0; i < n; i++){count[a[i] - min]++;}int i = 0;for(int j = 0; j < range; j++){while(count[j]--){a[i++] = j + min;}}free(count);
}

2. 并查集:

2.1 概念:

        由于不同元素但是又属于某种集合的数据, 存储使用到并查集合. 元素属于某种集合是按照某种规则来分类的. 需要查询某个元素, 需要找到对应集合去寻找.

        下标对应的就是集合编号, 里面的值对应这个元素属于哪个集合里的. 如果值为负数代表这个集合|拥有的元素数目|-1.

 2.2 并查集实现:

(1) 并查集结构:

        就是采用数组即可.

private:vector<int> _ufs;
(2) 初始化并查集:

        刚开始每个元素都是-1, 为根结点.

 //初始化并查集:刚开始都是-1.UnionFindSet(int n):_ufs(n, -1){}
(3) 查找元素的集合结点:

        遍历到负数的结点就是集合结点. 返回下标即可.

//查找元素所在集合:int FindRoot(int x){int parent = x;//遍历到值为负数就是根结点.while(_ufs[parent] >= 0){//不停迭代下标查询.parent = _ufs[parent];}return parent;}//递归方法查找;int _FindRoot(int x){return _ufs[x] < 0 ? x : _FindRoot(_ufs[x]);}
(4) 检查两个元素是否在一个集合:

        只要检查两个结点是否是同一个集合结点即可.

 //判断两个元素是否在同一个集合中.bool InSameSet(int x1, int x2){//检查两个元素根结点是否同一个即可.return FindRoot(x1)  == FindRoot(x2); }
(5) 两个结点合并:

        首先找到两个元素结点的集合结点, 如果在一个集合里面就不用插入了, 不是的话, 将parent1作为元素个数大的集合, parent2进行合并到parent1里面. 然后改变parent1值的个数以及parent2集合的新集合结点.

//合并两个元素所在集合.bool UnionSet(int x1, int x2){int parent1 = FindRoot(x1); int parent2 = FindRoot(x2);if(parent1 == parent2)return false;if(_ufs[parent1] > _ufs[parent2]){swap(parent1, parent2);}_ufs[parent1] += _ufs[parent2];_ufs[parent2] = parent1;return true;}
(6) 计算集合个数:
//查询集合里面的个数:int GetNum(){int count = 0;for(const int& val : _ufs){if(val < 0)count++;}return count;}
(7) 压缩路径:

        在查找数据的时候就进行压缩路径, 找到该元素的集合结点, 以及它的父结点, 然后进行将这个结点一条路的元素都直接插入到集合结点里面. 而且一般使用于数据量比较大的时候.

//查找元素所在集合://在查找集合结点的时候进行压缩.//+压缩路径:int FindRoot(int x){int root = x;//遍历到值为负数就是根结点.while(_ufs[root] >= 0){//不停迭代下标查询.root = _ufs[root];}while(_ufs[x] >= 0){int parent = _ufs[x];_ufs[x] = root;x = parent;}return root;}//递归方法查找 + 压缩;int _FindRoot(int x){//return _ufs[x] < 0 ? x : _FindRoot(_ufs[x]);int parent = x;if(_ufs[x] >= 0){parent = _FindRoot(_ufs[x]);_ufs[x] = parent;}}
(8) 元素编号和用户输入问题:

        用户一般不会输入数字编号, 可能会输入关键词, 这时候模板函数解决. 以及使用关键词和集合进行互相关联的方法, 就可以解决了.

#include <iostream>
#include <string>
#include <vector>
#include <utility>
#include <unordered_map>
using namespace std;
template<class T>
class UnionFindSet
{
public://初始化并查集:刚开始都是-1.UnionFindSet(const vector<T>& v):_ufs(v.size(), -1){for (int i = 0; i < v.size(); i++){_indexMap[v[i]] = i;}}//查找元素所在集合://在查找集合结点的时候进行压缩.//+压缩路径:int FindRoot(const T& x){int root = _indexMap[x];//遍历到值为负数就是根结点.while (_ufs[root] >= 0){//不停迭代下标查询.root = _ufs[root];}//一般数据量少不需要压缩.// while(_ufs[x] >= 0)// {//     int parent = _ufs[x];//     _ufs[x] = root;//     x = parent;// }return root;}//递归方法查找 + 压缩;// int _FindRoot(int x)// {//     //return _ufs[x] < 0 ? x : _FindRoot(_ufs[x]);//     int parent = x;//     if(_ufs[x] >= 0)//     {//         parent = _FindRoot(_ufs[x]);//         _ufs[x] = parent;//     }// }//判断两个元素是否在同一个集合中.bool InSameSet(const T& x1, const T& x2){//检查两个元素根结点是否同一个即可.return FindRoot(x1) == FindRoot(x2);}//合并两个元素所在集合.bool UnionSet(const T& x1, const T& x2){int parent1 = FindRoot(x1); int parent2 = FindRoot(x2);if (parent1 == parent2)return false;if (_ufs[parent1] > _ufs[parent2]){swap(parent1, parent2);}_ufs[parent1] += _ufs[parent2];_ufs[parent2] = parent1;return true;}//查询集合里面的个数:int GetNum(){int count = 0;for (const int& val : _ufs){if (val < 0)count++;}return count;}private:vector<int> _ufs;//原来标记数据T的处于哪个集合里面.unordered_map<T, int> _indexMap;
};int main() {vector<string> v = { "张三", "李四", "王五", "赵六", "田七", "周八", "吴九" };UnionFindSet<string> ufs(v);cout << ufs.GetNum() << endl; //7ufs.UnionSet("张三", "李四");ufs.UnionSet("王五", "赵六");cout << ufs.GetNum() << endl; //5ufs.UnionSet("张三", "赵六");cout << ufs.GetNum() << endl; //4return 0;
}

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

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

相关文章

算法随笔_13: 有效三角形的个数

上一篇:算法随笔_12:最短无序子数组-CSDN博客 题目描述如下: 给定一个包含非负整数的数组 nums &#xff0c;返回其中可以组成三角形三条边的三元组个数。 示例 1: 输入: nums [2,2,3,4] 输出: 3 解释:有效的组合是: 2,3,4 (使用第一个 2) 2,3,4 (使用第二个 2) 2,2,3 算法…

CSS 网络安全字体

适用于 HTML 和 CSS 的最佳 Web 安全字体 下面列出了适用于 HTM L和 CSS 的最佳 Web 安全字体&#xff1a; Arial (sans-serif)Verdana (sans-serif)Helvetica (sans-serif)Tahoma (sans-serif)Trebuchet MS (sans-serif)Times New Roman (serif)Georgia (serif)Garamond (se…

线程池的数据结构是什么 为什么会占用堆内存 线程池是一个对象吗

线程池是一种用于管理和复用线程以执行多个任务的设计模式&#xff0c;它通过预先创建一组线程&#xff0c;并将这些线程重复用于执行提交给线程池的任务&#xff0c;从而减少因频繁创建和销毁线程带来的开销。在Java中&#xff0c;线程池通常通过java.util.concurrent包下的Th…

PyTorch 神经协同过滤 (NCF) 推荐系统教程

目录 教程概述1. 神经协同过滤模型概述NCF 模型的主要组成部分&#xff1a; 2. 数据加载与预处理3. 定义神经协同过滤模型4. 训练模型5. 模型评估6. 推荐物品7. 完整示例8. 总结 在本教程中&#xff0c;我们将使用 PyTorch 实现一个神经协同过滤&#xff08;Neural Collaborat…

03.选择排序

一、题目思路 选择排序是一种简单直观的排序算法。它的工作原理是&#xff1a;首先在未排序序列中找到最小&#xff08;或最大&#xff09;元素&#xff0c;存放到排序序列的起始位置&#xff0c;然后&#xff0c;再从剩余未排序元素中继续寻找最小&#xff08;或最大&#xff…

大模型学习笔记 - 第一期 - Milvus向量数据库

大模型学习笔记 - 向量数据库 目录 大模型学习笔记 - 向量数据库传统文字检索(无嵌入)面临的困境1. 用户和商户表述差异2. 不同语种的表述差异3. 不同背景下的音译表述差异 向量检索向量化服务 参考 传统文字检索(无嵌入)面临的困境 1. 用户和商户表述差异 ​ 如果商户维护了…

详细图文解读Transformer模型:《Attention is All You Need》完整版

目录 前言1、Transformer模型《Attention is All You Need》总结2、Transformer整体结构2.1、工作流程 3、Transformer的输入4、Self-Attention&#xff08;自注意力机制&#xff09;4.1、Self-Attention 结构4.2、Q, K, V计算4.3、Self-Attention 的输出4.4、Multi-Head Atten…

Hadoop•用Web UI查看Hadoop状态词频统计

听说这里是目录哦 通过Web UI查看Hadoop运行状态&#x1f407;一、关闭防火墙二、在物理计算机添加集群的IP映射三、启动集群四、进入HDFS的Web UI 词频统计&#x1f9a9;1、准备文本数据2、在HDFS创建目录3、上传文件4、查看文件是否上传成功5、运行MapReduce程序6、查看MapRe…

vue编写一个可拖动的模块,并可以和任何其他组件组合使用

实现思路&#xff1a; 使用 Vue 的自定义指令&#xff08;directive&#xff09;来处理拖动逻辑。在 mounted 钩子中添加鼠标事件监听器&#xff0c;以实现拖动功能。在 unmounted 钩子中移除鼠标事件监听器&#xff0c;防止内存泄漏。 代码示例&#xff1a; <template&g…

Ubuntu、Windows系统网络设置(ping通内外网)

一、 虚拟机VMware和Ubuntu系统的网络配置说明 1、虚拟机的网络适配器的模式有三种&#xff1a; 桥接模式NAT模式主机模式 2、虚拟机VMware的网卡配置(如何进行配置界面(虚拟机->设置)) 注意&#xff1a; 1、以上桥接模式(ubuntu有独立IP)、NAT模式(没有独立IP)都可以联…

将IDLE里面python环境pyqt5配置的vscode

首先安装pyqt5全套&#xff1a;pip install pyqt5-tools 打开Vscode&#xff1a; 安装第三方扩展&#xff1a;PYQT Integration 成功配置designer.exe的路径【个人安装pyqt5的执行路径】&#xff0c;便可直接打开UI文件&#xff0c;进行编辑。 配置pyuic,如果下图填写方法使用…

大模型之三十三- 开源Melo 语音合成

大模型之三十三- 开源Melo 语音合成 文本到语音(TTS)系统从基于基础音素的模型演变成复杂的端到端神经方法,这种方法可以直接将文本转换为语音。这一变革得益于深度学习的进步和计算能力的提升,已经在语音的自然度、韵律控制和跨语言能力方面取得了重大进展 。现代TTS系统…

C# OpenCV机器视觉:特征匹配 “灵魂伴侣”

在一个阳光仿佛被施了魔法&#xff0c;欢快得直蹦跶的早晨&#xff0c;阿强像个即将踏上神秘寻宝之旅的探险家&#xff0c;一屁股墩在实验室那张堆满各种奇奇怪怪小玩意儿的桌前。桌上&#xff0c;零件、线路、半成品设备乱成一团&#xff0c;唯有他那宝贝电脑屏幕散发着清冷又…

【SSH端口转发:实现安全的远程端口映射】

SSH端口转发&#xff1a;实现安全的远程端口映射 在网络应用开发和运维过程中&#xff0c;我们经常需要进行端口转发来实现各种网络访问需求。今天我要分享一个使用SSH进行端口转发的实用脚本&#xff0c;并详细讲解其工作原理。 脚本内容 免密 ssh-copy-id -p 20080 rootxx…

GPT-4o背后的语音技术

GPT-4o背后的语音技术 GPT-4o是一个any2any的多模态模型,能够接受文本、音频、图像、视频等多模态输入,也能够生成包含文本、语音、图像和视频等混合内容的多模态输出。本文主要谈语音多模态的实现,并分享一些对于语音研究未来发展的看法。 GPT-4o (“o” 代表 “omni”) …

简述mysql 主从复制原理及其工作过程,配置一主两从并验证

第一种基于binlog的主从同步 首先对主库进行配置&#xff1a; [rootopenEuler-1 ~]# vim /etc/my.cnf 启动服务 [rootopenEuler-1 ~]# systemctl enable --now mysqld 主库的配置 从库的配置 第一个从库 [rootopenEuler-1 ~]# vim /etc/my.cnf [rootopenEuler-1 ~]# sys…

Spring自定义BeanPostProcessor实现bean的代理Java动态代理知识

上文&#xff1a;https://blog.csdn.net/qq_26437925/article/details/145241149 中大致了解了spring aop的代理的实现&#xff0c;其实就是有个BeanPostProcessor代理了bean对象。顺便复习下java代理相关知识 目录 自定义BeanPostProcessor实现aopJava动态代理知识动态代理的几…

医院挂号就诊系统设计与实现(代码+数据库+LW)

摘 要 传统办法管理信息首先需要花费的时间比较多&#xff0c;其次数据出错率比较高&#xff0c;而且对错误的数据进行更改也比较困难&#xff0c;最后&#xff0c;检索数据费事费力。因此&#xff0c;在计算机上安装医院挂号就诊系统软件来发挥其高效地信息处理的作用&#…

【GORM】初探gorm模型,字段标签与go案例

GORM是什么&#xff1f; GORM 是一个Go 语言 ORM&#xff08;对象关系映射&#xff09;库&#xff0c;它让我们可以使用结构体来操作数据库&#xff0c;而无需编写SQL 语句 GORM 模型与字段标签详解 在 GORM 中&#xff0c;模型是数据库表的抽象表示&#xff0c;字段标签&am…

R 语言科研绘图第 20 期 --- 箱线图-配对

在发表科研论文的过程中&#xff0c;科研绘图是必不可少的&#xff0c;一张好看的图形会是文章很大的加分项。 为了便于使用&#xff0c;本系列文章介绍的所有绘图都已收录到了 sciRplot 项目中&#xff0c;获取方式&#xff1a; R 语言科研绘图模板 --- sciRplothttps://mp.…