排序算法总结(一)
24号上午结束了头疼的四个小时,心里的结算是落了下来,可以回来写文章,这次更加意识菜的不行差的太多,写个文章充实一下自己,总结下最近使用过的排序算法。
排序算法 | 时间复杂度(平均) | 时间复杂度(最好) | 时间复杂度(最差) | 空间复杂度 | 稳定性 |
---|---|---|---|---|---|
冒泡排序 | O( n 2 n^{2} n2) | O( n n^{} n) | O( n 2 n^{2} n2) | O( 1 1^{} 1) | 稳定 |
选择排序 | O( n 2 n^{2} n2) | O( n 2 n^{2} n2) | O( n 2 n^{2} n2) | O( 1 1^{} 1) | 不稳定 |
插入排序 | O( n 2 n^{2} n2) | O( n n^{} n) | O( n 2 n^{2} n2) | O( 1 1^{} 1) | 稳定 |
希尔排序 | O( n 1.3 n^{1.3} n1.3) | O( n n^{} n) | O( n 2 n^{2} n2) | O( 1 1^{} 1) | 不稳定 |
堆排序 | O( n l o g 2 n nlog_{2}n nlog2n) | O( n l o g 2 n nlog_{2}n nlog2n) | O( n l o g 2 n nlog_{2}n nlog2n) | O( 1 1^{} 1) | 不稳定 |
归并排序 | O( n l o g 2 n nlog_{2}n nlog2n) | O( n l o g 2 n nlog_{2}n nlog2n) | O( n l o g 2 n nlog_{2}n nlog2n) | O( n n^{} n) | 稳定 |
快速排序 | O( n l o g 2 n nlog_{2}n nlog2n) | O( n l o g 2 n nlog_{2}n nlog2n) | O( n 2 n^{2} n2) | O( n l o g 2 n nlog_{2}n nlog2n) | 不稳定 |
冒泡排序
-
算法思想: 冒泡排序主要是通过比较相邻的元素的大小来进行元素的排序,直到数组元素是有序的序列。
-
未优化版本:是将数组的从头开始依次比较两个相邻元素,大小关系相反则置换元素位置,这也是刚刚入门是就会用到的算法,在此直接给出代码。
#include <iostream>
using namespace std;
void bubblesort(int a[], int n){
for( int i = 0; i < n; i++){
for(int j = 0; j < n; j++){
if(a[j] > a[j+1])
{
int temp = a[j];
a[j] = a[j+1];
a[j+1] = temp;
}
}
}
}
int main()
{
int n;
cin >> n;
int a[n];
for(int i = 0; i < n; i++) cin >> a[i];
cout << endl;
bubblesort(a,n);
cout << "after the sort :\n" << endl;
for(int i =0;i<n;i++) cout << a[i] <<" ";
return 0;
}
优化版本
- 设置为从数组的末尾开始依次比较相邻的两个元素,大小相反则互换位置,也就是较小的元素象气泡一样上浮完成,达到排序的效果。在此有一点稍加改动,添加flag变量,当数组中的元素没有进行交换元素的的操作的话,flag 值则为0,循环提前退出提高了效率。
通过数组a={5,3,2,4,1}为例 排序过程如下图
#include <iostream>
using namespace std;
void bubble(int a[], int n){
int flag = 1;
for( int i = 0 ;i < n - 1 && flag; i++){
for(int j = n - 1; j > i; j--) {
flag = 0;
if(a[j-1] > a[j]){
int temp = a[j-1];
a[j-1] = a[j];
a[j] = temp;
flag = 1;
}
}
}
}
int main()
{
int n;
cin >> n;
int a[n];
for(int i = 0; i < n; i++) cin >> a[i];
cout << endl;
bubble(a,n);
cout << "after the sort :\n" << endl;
for(int i =0;i<n;i++) cout << a[i] <<" ";
return 0;
}
选择排序
- 算法思想:选择排序是通过找到未排序元素中的最小值minn,将最小值minn和数组的第一个元素交换,在剩下的未排序的元素中找的最小的值minn ,将最小值和和数组的第二个元素交换,重复这样的循环,直到数组有序。
通过数组a={5,4,8,7,9,3,1}为例 如下图所示。
void SelectionSort( int a[], int n){
int minn,i,j,temp;
for (i = 0; i < n; i++){
minn = i;
for(j = i + 1; j < n; j++){
if(a[minn] > a[j]){
minn = j;
}
}
//判断最小元素是否本身,如果不是就交换
if(minn != i){
temp = a[minn];
a[minn] = a[i];
a[i] = temp;
}
}
}
int main()
{
int n;
cin >> n;
int a[n];
for(int i = 0; i < n; i++) cin >> a[i];
cout << endl;
SelectionSort(a,n);
cout << "after the sort :\n" << endl;
for(int i =0;i<n;i++) cout << a[i] <<" ";
return 0;
}
插入排序
- 算法思想:按照排序的大小,将每一个待排序的元素插入到适当的位置,为了给元素插入腾出空间,需要将插入位置及之后的元素向后移动一位,移动元素类似于顺序存储中插入元素的过程。
#include <iostream>
using namespace std;
void insertsort(int a[], int n){
int i, j, temp;
for(i = 1; i < n; i++) {
temp = a[i];
j = i-1;
//找到插入位置 将元素逐个后移一位
while(j >= 0 && a[j] > temp){
a[j+1] =a [j];
j--;
}
//将元素插入
a[j+1] = temp;
}
}
int main()
{
int n;
cin >> n;
int a[n];
for(int i = 0; i < n; i++) cin >> a[i];
cout << endl;
insertsort(a,n);
cout << "after the sort :\n" << endl;
for(int i =0;i<n;i++) cout << a[i] <<" ";
return 0;
希尔排序
- 算法思想: 希尔排序是插入排序的优化版本,优先比较元素间隔大的元素 ,逐渐减小元素之间的间隔,之后不断缩小间隔至一,完成排序,因此希尔排序又称为缩小增量排序。
如下图所示 相同颜色进行互换,直到间距为一。
直观的图如下:
代码如下:
#include <stdio.h>
void ShellSort(int a[], int n){
int gap, i, j, temp;
gap = n;
do{
gap = gap/3+1;
for(i = gap; i < n; i++){
if(a[i] < a[i-gap]){
temp = a[i];
j= i - gap;
while(j >= 0 && a[j] > temp){
a[j+gap] = a[j];
j -= gap;
}
a[j+gap] = temp;
}
}
}while(gap > 1);
}
int main()
{
int n;
cin >> n;
int a[n];
for(int i = 0; i < n; i++) cin >> a[i];
cout << endl;
ShellSort(a,n);
cout << "after the sort :\n" << endl;
for(int i =0;i<n;i++) cout << a[i] <<" ";
return 0;
堆排序
- 介绍算法实现的过程首先理清最大(小)堆的概念 :在完全二叉树中每个双亲结点大(小于)于子结点 。 如下图所示给出最大堆的图示:
在完全二叉树中双亲结点与子节点的关系为 n,2n,2n+1
满足关系 :a[i]>a[2i] , a[i]>a[2i+1] 或 a[i]<a[2i] a[i]<[2i+1]
算法思想:
-
通过首先将数组通过完全二叉数建立最大(小)堆
-
将序列的最大(小)值(根节点)于数组中的末尾元素交换
-
将n-1个序列再次构成最大(小)堆
-
重复此过程,直到得到有序序列
- 上图中前两行表示构建最大堆的过程 ,之后通过swap交换 最大元素和末尾元素的位置,这样就选出了最大元素9,将红色部分被的元素忽略 接下来再次将n-1个元素构成最大堆,直到完成数组的排序
注意:堆排序是从元素数组下标为一时开始进行排序,即a[0]的位置是不参与排序的,因为要通过数组元素的下标和完全二叉树联系起来,下标为一代表第一个双亲结点位置,而下标零没有位置在二叉树中表示。
代码如下:
#include <iostream>
using namespace std;
void heap(int a[],int m,int n){
int temp ,s;
temp = a[m];
//建立最大堆
for( s = 2*m; s <= n;s *= 2){
//比较子节点中左右结点的大小,右节点大的话 指针移到右节点的位置
if(a[s] < a[s+1] && s < n) s++;
if(temp>=a[s]) break;
//交换元素
a[m] = a[s];
m = s;
}
a[m] = temp;
}
void heapsort(int a[], int n){
int i;
for(i = n/2; i > 0; i--) heap(a,i,n);
for(i = n; i > 1; i--){
//将根节点与二叉树中的最后一个结点交换,此时就选择出了最大元素,
swap(a[1],a[i]);
//将n-1个元素再次构成最大堆
heap(a,1,i-1);
}
}
int main(){
int n;
cin >> n;
int a[n];
for(int i = 0; i < n; i++) cin >> a[i];
cout << endl;
heapsort(a,n-1);
cout << "after the sort :\n" << endl;
for(int i =0;i<n;i++) cout << a[i] <<" ";
return 0;
}
归并排序
归并排序是通过分治的思想将元素不断分成两部分之后对每部分的元素进行排序
算法思想:
1.将包含n个元素的数组分成两部分,即两个n/2的数组
2.将两个数组分别执行排序
3.将已经排序的数组整合
4.重复上述过程直到完成排序
代码如下:
#include <iostream>
#define MAX_SIZE 50000
#define INFINITY 200000000
using namespace std;
int L[MAX_SIZE],R[MAX_SIZE];
void Merge( int a[], int left, int mid, int right){
int n1 = mid-left,n = 0,m = 0;
int n2 = right-mid;
for(int i = 0; i < n1; i++) L[i] = a[left+i];
for(int i = 0;i < n2; i++) R[i] = a[mid+i];
L[n1] = INFINITY;
R[n2] = INFINITY;
for(int i = left;i < right; i++){
if(L[n] >= R[m]) a[i] = R[m++];
else a[i] = L[n++];
}
}
void mergesort(int a[],int left,int right){
if(left + 1 < right){
int mid = (left+right)/2;
mergesort(a,left,mid);
mergesort(a,mid,right);
Merge(a,left,mid,right);
}
}
int main()
{
int n;
cin >> n;
int a[n];
for(int i = 0; i < n; i++) cin >> a[i];
cout << endl;
mergesort(a,0,n);
cout << "after the sort :\n" << endl;
for(int i =0;i<n;i++) cout << a[i] <<" ";
return 0;
}
快速排序
快速排序:快速排序是基于分治法的一种高效的排序方法,是在冒泡排序上的一种改进。
算法思想:
- 将数组分割成为前后两个局部的数组。
- 对前一部分进行执行快速排序
- 对后半部分的数组进行快速排序
通过以上的过程,将数组分割成两部分,使左侧部分的值比右侧部分的值小。随后将已经分割的部分重复此操作,直到数组有序。
下图为例:
代码如下:
#include <iostream>
using namespace std;
int Partition(int a[],int low ,int high){
int location = a[low];
while ( low< high){
// 从右边开始过滤掉比基准点大的数,即high向左移动
while (low < high && a[high] >= location) high--;
swap(a[low],a[high]);
// 从左边开始过滤掉比基准点小的数,即low向右移动
while(low<high&&a[low]<=location) low++;
swap(a[low],a[high]);
}
return low;
}
void quicksort ( int a [], int low, int high){
int location ;
if(low < high){
location=Partition(a,low,high);
quicksort(a,low,location-1);
quicksort(a,location+1,high);
}
}
int main()
{
int n;
cin >> n;
int a[n];
for(int i = 0; i < n; i++) cin >> a[i];
cout << endl;
quicksort(a,0,n-1);
cout << "after the sort :\n" << endl;
for(int i =0;i<n;i++) cout << a[i] <<" ";
return 0;
//
}
上述代码具体过程:设置基准点第一次以low数组的第一个开始 过滤掉比基准点大的数,即理解为high指针向左移动,跳出循环说明有比基准点小的元素,则进行元素交换。同理 low的指向的元素比基准点小的话 low的指向向右,跳出循环说明有比基准点大的元素,进行交换。反复执行这样的过程,完成排序。
总结:
以上是对比较类的排序的一个小总结,写的不好,有是很多不足之处和我自己不满意的二地方,希望有耐心看了的的朋友看到能帮我改正一下,这次考试完了也有心情来写的东西,之后在一周会不定期更新数据结构和算法的问题。