排序(冒泡、选择、插入、希尔、快排、堆排、归并)

冒泡排序

  冒泡排序时通过无序区中相邻记录的关键字间的比较和位置的交换,使关键字最小的元素如气泡似的逐步上浮直水面。有序区逐渐扩大,无序区逐渐缩小。
  冒泡排序算法的原理如下:

  1. 比较相邻的元素。如果第一个比第二个大,就交换他们两个。
  2. 对每一对相邻元素做同样的工作,从开始第一对到结尾的最后一对。在这一点,最后的元素应该会是最大的数。
  3. 针对所有的元素重复以上的步骤,除了最后一个。
  4. 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较

动画演示
在这里插入图片描述

冒泡排序是一种非常容易理解的排序
时间复杂度:O(N^2)
空间复杂度:O(1)
稳定性:稳定

普通冒泡

void bubble0(vector<int>& arr)
{int size = arr.size();for(int i = 0; i < size; ++i) {  for(int j = 0; j < size-i-1; ++j){if(arr[j] > arr[j+1]){swap(arr[j+1], arr[j]);}}}
}

优化一
  第一种优化就是在交换的地方加一个标记,如果某一趟排序没有交换元素,说明这组数据已经有序,不用再继续下去。这样对部分连续有序而整体无序的数据大大提升了效率。


void bubble1(vector<int>& arr)
{int size = arr.size();for(int i = 0; i < size; ++i){  bool flag = true;for(int j = 0; j < size-i-1; ++j){if(arr[j] > arr[j+1]){flag = false;swap(arr[j+1], arr[j]);}}if(flag)return;}
}

优化二
  对于优化一来说,仅仅对部分连续有序而整体无序的数据效率高一些,如果这组数据时前面无序,但是后面的部分有序,效率就不是那么高了。因此可以在优化一的基础上记下最后一次交换的位置,这个位置后没有交换,必然是有序的,然后下一次排序从第一个比较到上次记录的位置结束即可。

void bubble2(vector<int>& arr)
{int size = arr.size() -1;int pos = size;for(int i = 0; i < size; ++i){bool flag = true;int k = 0;for(int j = 0; j < pos; ++j){if(arr[j+1] < arr[j]){flag = false;swap(arr[j+1], arr[j]);k = j;}}if(flag)return;pos = k;}
}

优化三
  冒泡算法经过优化二后效率有很大的提升,但是效率还可以继续优化,就是在一趟排序中确定两个最值,也就是一个正向查找最大值,另一个反向查找最小值

void bubble3(vector<int>& arr)
{int size = arr.size() -1;int pos = size;int pos1 = 0; for(int i = 0; i < size; ++i){bool flag = true;int k = 0; //正向冒最大值for(int j = 0; j < pos; ++j){if(arr[j+1] < arr[j]){flag = false;swap(arr[j+1], arr[j]);k = j;}}if(flag)return;pos = k;//反向冒最小值int n = pos1;for(int j = k; j > pos1; --j){if(arr[j-1] > arr[j]){swap(arr[j-1], arr[j]);n = j-1;flag = false;}}if(flag)return;pos1 = n;}
}

选择排序

  选择排序是一种简单直观的排序算法。
  选择排序原理

  1. 初始时设第一个元素为最大值,并记录其下标为maxpos
  2. 从剩余未排序元素中继续寻找最大元素,如果当前元素比下标为maxpos的元素大,将maxpos更新到当前位置
  3. 一次遍历后,将下标为maxpos的元素与最后一个元素交换位置
  4. 以此类推,直到整个数组有序。

  注意:选择排序与冒泡排序是区别的,冒泡排序通过依次交换相邻两个顺序不合法的元素位置,从而将当前最大元素放到合适的位置,而选择排序每遍历一次都记住了当前最大元素的位置,最后仅需一次交换操作即可将其放到合适的位置。

直接选择排序思考非常好理解,但是效率不是很好。实际中很少使用
时间复杂度:O(N^2)
空间复杂度:O(1)
稳定性:不稳定

void slectSort(vector<int>& arr)
{int size = arr.size();for (int i = 0; i < size - 1; ++i){int maxPos = 0;for (int j = 1; j < size - i; ++j){if (arr[j] > arr[maxPos])maxPos = j;}if (maxPos != size-i-1)swap(arr[maxPos], arr[size - i-1]);}
}

优化
  选择排序的优化就是在原来的一次只标记一个最值,优化为一个标记两个最值,这样效率可以提升原来的一半。

void SlectSort(vector<int>& arr)
{int size = arr.size();int begin = 0;int end = size - 1;while (begin < end){int minPos = begin;int maxPos = begin;int index = begin + 1;while (index <= end){if (arr[minPos] > arr[index])minPos = index;if (arr[maxPos] < arr[index])maxPos = index;++index;}if (maxPos != end)swap(arr[maxPos], arr[end]);//这段小代码在在下面介绍其作用if (minPos == end)minPos = maxPos;if (minPos != begin)swap(arr[begin], arr[minPos]);++begin;--end;}
}

  上面代码标记的一小段代码是为了防止minpos在最大值要插入的位置。比如序列(2,15,4,1)这时候的maxpos为1,minpos为3,调整过最大值后,序列就成了(2,1,4,15),这时候的minpos还是3,如果直接进行最小值交换,就恢复到之前的位置了,所以要加上这段判断代码。这样如果最小值在最大值要交换的位置,最大值交换后要将最小值的位置更新到maxpos的位置。

插入排序

  直接插入排序是一种简单的插入排序法,其基本思想是:把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中,直到所有的记录插入完为止,得到一个新的有序序列 。实际中我们玩扑克牌时,就用了插入排序的思想
  当插入第i(i>=1)个元素时,前面的arr[0],arr[1],…,arr[i-1]已经排好 序,此时用arr[i]与arr[i-1],arr[i-2]进行比较,找到插入位置即将array[i]插入,原来位置上的元素顺序后移。
元素集合越接近有序,直接插入排序算法的时间效率越高,其时间效率在O(n)与O(n^2)之间。直接插入排序的空间复杂度为O(1),它是一种稳定的排序算法。

元素集合越接近有序,直接插入排序算法的时间效率越高
时间复杂度:O(N^2)
空间复杂度:O(1),它是一种稳定的排序算法
稳定性:稳定

在这里插入图片描述

void InsertSort(vector<int>& arr)
{int size = arr.size();for (int i = 1; i < size; ++i){//待插入元素int key = arr[i];//找插入位置int end = i - 1;while (end >= 0 && arr[end] > key){//向后搬移数据arr[end + 1] = arr[end];end--;}//开始插入arr[end + 1] = key;}
}

优化
  普通的插入排序就是不断的依次将元素插入前面已排好序的序列中。由于前半部分为已排好序的数列,这样我们不用按顺序依次寻找插入点,可以采用折半查找的方法来加快寻找插入点的速度。
  折半插入排序算法是一种稳定的排序算法,比普通插入算法明显减少了关键字之间比较的次数,因此速度比直接插入排序算法快,但记录移动的次数没有变,所以折半插入排序算法的时间复杂度仍然为O(n^2),与直接插入排序算法相同。

void InsertSort1(vector<int>& arr)
{int size = arr.size();for (int i = 1; i < size; ++i){//待插入元素int key = arr[i];//找插入位置int right = i - 1;int left = 0;if(arr[right] > key){while (left <= right ){int mid = left+((right-left)>>1);if(arr[mid] < key)left = mid+1;elseright = mid-1;}}//开始搬移数据int end = i -1;while(end >= left){arr[end+1] = arr[end];--end;}//开始插入数据arr[end + 1] = key;}
}

希尔排序

  希尔排序法又称缩小增量法。希尔排序法的基本思想是:先选定一个整数,把待排序文件中所有记录分成个组,所有距离为的记录分在同一组内,并对每一组内的记录进行排序。然后,重复上述分组和排序的工作。当到达=1时,所有记录在统一组内排好序。

希尔排序是对直接插入排序的优化。
当gap > 1时都是预排序,目的是让数组更接近于有序。当gap == 1时,数组已经接近有序的了,这样就会很快。这样整体而言,可以达到优化的效果。我们实现后可以进行性能测试的对比。
希尔排序的时间复杂度不好计算,需要进行推导,推导出来平均时间复杂度: O(N^1.3 - N^2)
稳定性:不稳定

在这里插入图片描述

void ShellSort(vector<int>& arr)
{int size = arr.size();int gap = size;while (gap > 0){gap = gap / 3 + 1;for (int i = gap; i < size; ++i){//待插入元素int key = arr[i];//找插入位置int end = i - gap;while (end >= 0 && arr[end] > key){arr[end + gap] = arr[end];end -= gap;}//开始插入arr[end + gap] = key;}if(gap == 1)break;}
}

快速排序

  快速排序快速排序是Hoare于1962年提出的一种二叉树结构的交换排序方法,其基本思想为:任取待排序元素序列中的某元素作为基准值,按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右子序列中所有元素均大于基准值,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止。

快速排序整体的综合性能和使用场景都是比较好的,所以才敢叫快速排序
时间复杂度:O(N*logN)
空间复杂度:O(logN)
稳定性:不稳定

普通快排:
普通快排就是将当前序列的最后一个值作为标志,然后开始分割序列。
在这里插入图片描述
在这里插入图片描述

int Partion(vector<int>& arr, int left, int right)
{int key = arr[right-1];int begin = 0;int end = right-1;while (begin < end){while (begin < end && arr[begin] <= key)begin++;while (begin < end && arr[end] >= key)end--;if (begin != end)swap(arr[begin], arr[end]);}if (begin != right - 1)swap(arr[begin], arr[right - 1]);return begin;
}

优化一:挖坑法
  挖坑法的原理就是在左边找到不符合条件的元素时,将这个元素放在end处,这时候begin位置就成了一个“坑”,在右边找到不符合条件的元素时,将这个元素放到begin位置,将之前的“坑”填好,以此类推,最后将标志key保存的值放在begin位置,将最后一个“坑”填满。
在这里插入图片描述

int Partion1(vector<int>& arr, int left, int right)
{int end = right - 1;int begin = left;int key = arr[right - 1];while (begin < end){while (begin < end && arr[begin] <= key)begin++;if (begin < end){arr[end] = arr[begin];end--;}while (begin < end && arr[end] >= key)end--;if (begin < end){arr[begin] = arr[end];begin++;}}arr[begin] = key;return begin;
}
//齐头并进法
int Partion2(vector<int>& arr, int left, int right)
{int key = arr[right - 1];int cur = left;int pre = cur - 1;while (cur < right){if (arr[cur] < key && ++pre != cur){swap(arr[pre], arr[cur]);}cur++;}if (++pre != right-1)swap(arr[pre], arr[right - 1]);return pre;
}
//三数取中法
int MidNum(vector<int>& arr,int left, int right)
{int mid = left + ((right - left) >> 1);if (arr[left] > arr[right]){if (arr[mid] > arr[left])mid = left;if (arr[mid] < arr[right])mid = right;}else{if (arr[mid] < arr[left])mid = left;if (arr[mid] > arr[right])mid = right;}return mid;
}
int Partion3(vector<int>& arr, int left, int right)
{int end = right - 1;int begin = left;int mid = MidNum(arr, left, end);swap(arr[mid], arr[right - 1]);int key = arr[right - 1];while (begin < end){while (begin < end && arr[begin] <= key)begin++;if (begin < end){arr[end] = arr[begin];end--;}while (begin < end && arr[end] >= key)end--;if (begin < end){arr[begin] = arr[end];begin++;}}arr[begin] = key;return begin;
}
void QuickSort(vector<int>& arr, int left, int right)
{if (right - left > 1){int key = Partion3(arr, left, right);QuickSort(arr, left, key);QuickSort(arr, key + 1, right);}
}
//非递归快排
void QuickSortNor(vector<int>& arr)
{int right = arr.size();int left = 0;stack<int> s;s.push(right);s.push(left);while (!s.empty()){left = s.top();s.pop();right = s.top();s.pop();if (right - left > 1){int key = Partion3(arr, left, right);//保存右值s.push(right);s.push(key + 1);//保存左值s.push(key);s.push(left);}}}

堆排序

  堆排序就是利用堆(堆的详细介绍)这种数据结构进行排序的算法,堆排序属于选择排序

时间复杂度:O(nlogn)
空间复杂度:O(1)
稳定性:不稳定
堆排序的步骤为:
1、基于所给元素创建一个大堆
2、使用堆删除的思想从最后一个结点向前调整

在这里插入图片描述

typedef struct Heap
{HPData* _array;int _size;int _capacity;
}Heap;void HeapAdjust(HPData* array, int size, int root)
{int child = root * 2 + 1;while (child < size){if (child + 1 < size && array[child] < array[child + 1])child += 1;if (array[child] > array[root]){swap(array[child], array[root]);root = child;child = root * 2 + 1;}elsereturn;}
}void HeapSort(HPData* array, int size)
{//建大堆//找倒数第一个非叶子节点int root = (size - 2) >> 1;for (; root >= 0; --root)HeapAdjust(array, size, root);//开始排序,使用删除节点的思想int end = size - 1;while (end){swap(array[0], array[end]);HeapAdjust(array, end, 0);end--;}
}

归并排序

  归并排序是简历在归并操作上的一中有效的排序算法,该算法是采用分治法的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列。显示每个子序列有序,再使子序列段间有序。若两个有序列表合并成一个有序列表,陈伟二路归并。

归并的缺点在于需要O(N)的空间复杂度,归并排序的思考更多的是解决在磁盘中的外排序问题。
时间复杂度:O(N*logN)
空间复杂度:O(N)
稳定性:稳定

在这里插入图片描述

void _MergeData(vector<int>& arr, int left, int mid, int right, vector<int>& temp)
{int begin1 = left;int end1 = mid;int begin2 = mid;int end2 = right;int index = left;while (begin1 < end1 && begin2 < end2){if (arr[begin1] < arr[begin2])temp[index++] = arr[begin1++];elsetemp[index++] = arr[begin2++];} while (begin1 < end1)temp[index++] = arr[begin1++];while (begin2 < end2)temp[index++] = arr[begin2++];}void _MergeSort(vector<int>& arr, int left, int right, vector<int>& temp)
{if ((right - left) > 1){int mid = left + ((right - left) >> 1);_MergeSort(arr, left, mid, temp);_MergeSort(arr, mid, right, temp);_MergeData(arr, left, mid, right, temp);copy(temp.begin() + left, temp.begin() + right, arr.begin() + left);}
}

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

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

相关文章

人民日报:人工智能,务实发展是正道

来源&#xff1a;人民日报摘要&#xff1a;近日&#xff0c;由中国人工智能学会主办的中国人工智能大会在深圳召开&#xff0c;利用这个人工智能领域产、学、研紧密结合的高端前沿交流平台&#xff0c;围绕关键核心技术发展等当前热点话题&#xff0c;学者和业界人士进行了充分…

自动驾驶芯片:GPU 的现在和 ASIC 的未来

来源&#xff1a;乐晴智库精选▌车载芯片的发展趋势(CPU-GPU-FPGA-ASIC)过去汽车电子芯片以与传感器一一对应的电子控制单元(ECU)为主&#xff0c;主要分布与发动机等核心部件上。随着汽车智能化的发展&#xff0c;汽车传感器越来越多&#xff0c;传统的分布式架构逐渐落后&…

电动汽车:新一轮三年十倍,“补贴”结束“高端”开启

来源&#xff1a;乐晴智库精选摘要&#xff1a;从最早的十城千辆新能源车示范推广&#xff0c;到2014年正式启动的二级市场新能源车大行情&#xff0c;再到当下新能源乘用车型的快速升级迭代&#xff0c;新能源汽车产业发展和投资已历经8余年。▌新能源汽车投资&#xff0c;推倒…

使用easyUI给datagrid添加pagination

author YHC 这个示例展示我们如何从服务器端加载数据和如何添加pagination 到datagrid. 查看 Demo 创建 DataGrid 从服务器端加载数据, 你应该设置url属性, 在你的服务器端你应该返回JSON格式数据.请看datagrid文档得到更多关于它的数据格式信息. <table id"tt" c…

一篇文章搞懂数据仓库:四种常见数据模型(维度模型、范式模型等)

目录 写在前面 一、为什么要进行数据仓库建模&#xff1f; 二、四种常见模型 2.1 维度模型 2.1.1 星型模型 2.1.2 雪花模型 2.1.3 星座模型 2.2 范式模型 2.3 Data Vault模型 2.4 Anchor模型 三 数据模型的评价标准 小编有话 写在前面 大数据时代&#xff0c;维度…

学习C语言可以从以下几个方面入手

学习C语言可以从以下几个方面入手&#xff1a; 了解基础知识&#xff1a;首先&#xff0c;你需要了解C语言的基本语法和规则&#xff0c;包括变量、数据类型、运算符、控制结构等。可以通过阅读相关的教材或在线教程来学习这些基础知识。动手实践&#xff1a;理论知识的学习是…

王飞跃谈GE艰难的数字化转型启示:从工业智联网到工业5.0

来源&#xff1a;德先生外患&#xff1a;2018年6月26日&#xff0c;通用电气&#xff08;下文称GE&#xff09;被剔除出道琼斯工业平均指数&#xff0c;而GE自1907年即是道指成分股&#xff0c;至今坚守了111年。2017年以来&#xff0c;通用电气股价从30美元左右下跌到现在的13…

一篇文章搞懂数据仓库:常用ETL工具、方法

目录 一、什么是ETL&#xff1f; 二、ETL & ELT 三、常用的ETL工具 3.1 sqoop 3.2 DataX 3.3 Kettle 3.4 canal 3.5 StreamSets 四、ETL加载策略 4.1 增量 4.2 全量 4.3 流式 小编有话 一、什么是ETL&#xff1f; ETL&#xff0c;是英文Extract-Transform-Lo…

经典排序之 堆排序

开了个公众号「aCloudDeveloper」&#xff0c;专注技术干货分享&#xff0c;期待与你相遇。 Author: bakari Date: 2012.7.30 排序算法有很多种&#xff0c;每一种在不同的情况下都占有一席之地。关于排序算法我分“经典排序之”系列分别述之。本篇为堆排序。 堆排序是运用二叉…

操作系统之进程概念

进程概念 进程是什么&#xff1a; 表面上来说进程是程序的一个执行实例&#xff0c;或者是一个正在执行的程序等&#xff0c;从操作系统的角度来说&#xff0c;程序运行需要将代码数据加载到内存中&#xff0c;由于在操作系统中运行了很多的程序&#xff0c;操作系统就必须去管…

也谈压缩感知和贝叶斯大脑

来源&#xff1a;科学网压缩感知和人工智能都是当下很热很热的研究课题。不过许多论文数学论述高深莫测&#xff0c;加之一大堆各种千奇百怪的数学符号&#xff0c;不仅让人望而生畏&#xff0c;望而却步。笔者对希望用形象的比喻把问题大致轮廓讲清楚就好。所谓压缩感知是什么…

一篇文章搞懂数据仓库:三种事实表(设计原则,设计方法、对比)

目录 1、三种事实表概述 2、三种事实表对比 3、事实表设计 8 大原则 4、事实表设计方法 第一步&#xff1a;选择业务过程及确定事实表类型 第二步&#xff1a;声明粒度 第三步&#xff1a;确定维度 第四步&#xff1a;确定事实 事实表作为数据仓库维度建模的核心&#…

深思考人工智能蝉联SMP2018多轮语义对话冠军,报告解读多轮人机对话实现过程...

人机对话技术近年来受到了学术界和产业界的广泛关注&#xff0c;其发展影响并推动着语音识别与合成、自然语言理解、对话管理以及自然语言生成等研究的进展。众多产业界巨头相继推出了人机对话技术相关产品&#xff0c;并将人机对话技术作为其公司的重点研发方向。8月3日&#…

一篇文章搞懂数据仓库:数据仓库规范设计

目录 一、为什么要进行规范设计&#xff1f; 二、设计规范 - 指标 三、命名规范 - 表命名 3.1 常规表 3.2 中间表 3.3 临时表 3.4 维度表 四、开发规范 五、流程规范 一、为什么要进行规范设计&#xff1f; 无规矩、不方圆。规范设计是在具体开发工作之前制定的&…

map的详解及常见面试题

map的概念 map是STL中的一个关联式容器&#xff0c;它提供一对一的K-V的数据处理能力&#xff0c;由于这个特性&#xff0c;在我们需要完成Key-Value数据处理的时候可以很方便的调用。map的底层结构是红黑树&#xff0c;这棵树对数据有自动排序的功能&#xff0c;所以map中的数…

无处不在的人工智能,IBM沃森的20个行业应用

来源&#xff1a;资本实验室聚焦前沿科技创新与传统产业升级自2011年在美国综艺电视节目《危险边缘》中一战成名后&#xff0c;IBM的Watson就一直是最受关注的人工智能之一。从菜谱分析到球队管理&#xff0c;从健康顾问到酒店礼宾服务&#xff0c;Watson基于自然语言处理和机器…

一篇文章搞懂数据仓库:数据仓库架构-Lambda和Kappa对比

在介绍Lambda和Kappa架构之前&#xff0c;我们先回顾一下数据仓库的发展历程&#xff1a; 传送门-数据仓库发展历程 写在前面 咳&#xff0c;随着数据量的暴增和数据实时性要求越来越高&#xff0c;以及大数据技术的发展驱动企业不断升级迭代&#xff0c;数据仓库架构方面也在…

宇宙和你,本质上其实只是个八维数字?

剑桥大学的数学物理学家Cohl Furey正在寻找粒子物理标准模型和八元数之间的联系。八元数的乘法规则被编码在被称为法诺面的三角图中。来源&#xff1a; 环球科学对于一维、二维乃至四维的数字&#xff0c;人们都不陌生&#xff1a;一维的实数一直都存在于经典物理中&#xff0c…

一篇文章搞懂数据仓库:数据应用--OLAP

目录 1、OLAP和OLTP的区别 2、OLAP分类 3、OLAP基本操作 4、OLAP选型 1、olap和oltp的区别 OLTPOLAP对象业务开发人员分析决策人员功能日常事务处理面向分析决策模型关系模型多维模型数据量几条或几十条记录&#xff1e;百万于万条记录操作类型增、删、查、改(CRUD)查询为主…

欧洲、美国、中国智慧城市的不同实践路径

来源&#xff1a;远望智库摘要&#xff1a;随着ICT、大数据、物联网等各类新兴技术的不断发展&#xff0c;智慧城市的运营和实践也不断趋于成熟。随着ICT、大数据、物联网等各类新兴技术的不断发展&#xff0c;智慧城市的运营和实践也不断趋于成熟。通过整理欧美各大典型智慧城…