常用排序算法
排序算法大体可分为两种:
第一种:比较排序
主要有冒泡排序、选择排序、插入排序、归并排序、堆排序、快速排序等。
第二种:非比较排序
主要有计数排序、基数排序、桶排序等。
常见排序算法的性能如下:
1:冒泡排序
通过与相邻元素的比较和交换来把小的数交换到最前面。一次遍历把最小(或最大)的数放到最顶端,再对剩下的序列依次冒泡得到一个有序序列。
冒泡排序的时间复杂度是O(n^2),所需辅助空间0(1);
对数量比较多的元素进行排序时,效率低下。
代码如下:
BubbleSort.cpp文件
#include <stdio.h>
class CBubbleSort{
public:
void Swap(int* pArr, int i, int j)
{
int temp = pArr[i];
pArr[i] = pArr[j];
pArr[j] = temp;
}
void Sort(int* pArr, int length)
{
if (pArr == NULL || length == 0)
return;
int nIndex = 0;
for (int i = 0; i < length; i++)
{
for (int j = 0; j < length - i - 1; j ++)
{
if (pArr[j] > pArr[j + 1])
Swap(pArr, j, j+1);
}
}
return;
}
};
main.cpp文件
#include <iostream>
#include <stdio.h>
#include "BubbleSort.cpp"
using namespace std;
void main()
{
int test[]={5,6,8,3,6,11,33,7};
int count = sizeof(test) / sizeof(int);
CBubbleSort* pSort = new CBubbleSort();
if (pSort == NULL)
return;
pSort->Sort(&test[0], count);
delete pSort;
pSort = NULL;
cout << "After sort :" << endl;
for (int i = 0; i < count; i++)
{
cout << test[i] << " ";
}
cout << endl;
system("pause");
}
运行结果如下:
2:改进的冒泡排序
比冒泡排序的改进之处是,从低到高排序,然后从高到底排序。
改进的冒泡排序的时间复杂度是O(n^2),所需辅助空间0(1);
比冒泡排序效率稍微高点。
代码如下:
BubbleSort2.cpp文件
#include <stdio.h>
class CBubbleSort_new{
public:
void Swap(int* pArr, int i, int j)
{
int temp = pArr[i];
pArr[i] = pArr[j];
pArr[j] = temp;
}
void Sort(int* pArr, int length)
{
if (pArr == NULL || length == 0)
return;
int begin = 0;
int end = length - 1;
while(begin < end)
{
// 将最大的元素放到后面
for (int i = begin; i < end; i++)
{
if (pArr[i] > pArr[i + 1])
Swap(pArr, i, i + 1);
}
end--;
// 将最小的元素放到前面
for (int i = end; i > begin; i--)
{
if (pArr[i] < pArr[i - 1])
Swap(pArr, i, i - 1);
}
begin++;
}
}
};
main.cpp文件
#include <iostream>
#include <stdio.h>
#include "BubbleSort2.cpp"
using namespace std;
void main()
{
int test[]={5,6,8,3,6,11,33,7};
int count = sizeof(test) / sizeof(int);
CBubbleSort_new* pSort = new CBubbleSort_new();
if (pSort == NULL)
return;
pSort->Sort(&test[0], count);
delete pSort;
pSort = NULL;
cout << "After sort :" << endl;
for (int i = 0; i < count; i++)
{
cout << test[i] << " ";
}
cout << endl;
system("pause");
}
运行结果如下:
3:选择排序
在未排序序列中找到最小(大)的元素,存放到有序序列的末尾;再从剩余未排序的元素中继续寻找最小(大)的元素,存放到有序序列的末尾;依次类推,直到所有的元素均排序完毕。
时间复杂度o(n^2),空间复杂度o(1)。
代码如下:
SelectSort.cpp文件
#include <stdio.h>
class CSelectSort{
public:
void Swap(int* pArr, int i, int j)
{
int temp = pArr[i];
pArr[i] = pArr[j];
pArr[j] = temp;
}
void Sort(int* pArr, int length)
{
if (pArr == NULL || length == 0)
return;
for (int i = 0; i < length; i++)
{
int nMinIdx = i;
for (int j = i + 1; j < length; j++)
{
if (pArr[j] < pArr[nMinIdx])
nMinIdx = j;
}
if (nMinIdx != i)
Swap(pArr, nMinIdx, i );
}
}
};
main.cpp文件
#include <iostream>
#include <stdio.h>"
#include "SelectSort.cpp"
using namespace std;
void main()
{
int test[]={5,6,8,3,6,11,33,7};
int count = sizeof(test) / sizeof(int);
CSelectSort* pSort = new CSelectSort();
if (pSort == NULL)
return;
pSort->Sort(&test[0], count);
delete pSort;
pSort = NULL;
cout << "After sort :" << endl;
for (int i = 0; i < count; i++)
{
cout << test[i] << " ";
}
cout << endl;
system("pause");
}
运行结果如下:
4:插入排序
从未排序的序列中选择一个新元素,在已排序的序列中从后向前扫描,知道找到比小于或等于新元素的元素,并将新元素插入到找到的元素的后面,对其后面的元素依次后移。
时间复杂度 o(n^2),空间复杂度o(1);
代码如下:
InsertSort.cpp文件
#include <stdio.h>
class CInsertSort{
public:
void Sort(int* pArr, int length)
{
if (pArr == NULL || length == 0)
return;
for (int i = 1; i < length; i++)
{
int temp = pArr[i];
int j = i;
while(j >= 1 && pArr[j - 1] > temp)
{
pArr[j] = pArr[j - 1];
j--;
}
pArr[j] = temp;
}
}
};
main.cpp文件
#include <iostream>
#include <stdio.h>
#include "InsertSort.cpp"
using namespace std;
void main()
{
int test[]={5,6,8,3,6,11,33,7};
int count = sizeof(test) / sizeof(int);
CInsertSort* pSort = new CInsertSort();
if (pSort == NULL)
return;
pSort->Sort(&test[0], count);
delete pSort;
pSort = NULL;
cout << "After sort :" << endl;
for (int i = 0; i < count; i++)
{
cout << test[i] << " ";
}
cout << endl;
system("pause");
}
运行结果如下:
5:二分插入排序
是插入排序的改进。可以减少比较操作的次数。
时间复杂度o(n^2),空间复杂度o(1)。
代码如下:
BinInsertSort.cpp文件
#include <stdio.h>
class CBinInsertSort{
public:
void Sort(int* pArr, int length)
{
if (pArr == NULL || length == 0)
return;
for (int i = 1; i < length; i++)
{
int nTemp = pArr[i];
int nLeft = 0;
int nRight = i - 1;
while (nLeft <= nRight)
{
int nMiddle = (nLeft + nRight) / 2;
if (pArr[nMiddle] > nTemp)
nRight = nMiddle - 1;
else
nLeft = nMiddle + 1;
}
for (int j = i; j > nLeft; j--)
pArr[j] = pArr[j - 1];
if (nLeft != i)
pArr[nLeft] = nTemp;
}
}
};
main.cpp文件
#include <iostream>
#include <stdio.h>
#include "BinInsertSort.cpp"
using namespace std;
void main()
{
int test[]={5,6,8,3,6,11,33,7};
int count = sizeof(test) / sizeof(int);
CBinInsertSort* pSort = new CBinInsertSort();
if (pSort == NULL)
return;
pSort->Sort(&test[0], count);
delete pSort;
pSort = NULL;
cout << "After sort :" << endl;
for (int i = 0; i < count; i++)
{
cout << test[i] << " ";
}
cout << endl;
system("pause");
}
运行结果如下:
6:希尔排序
也叫递减增量排序。插入排序的更高效的改进。
希尔排序是基于插入排序的以下两点性质而提出改进方法的:
插入排序在对几乎已经排好序的数据操作时,效率高,即可以达到线性排序的效率
但插入排序一般来说是低效的,因为插入排序每次只能将数据移动一位
希尔排序通过将比较的全部元素分为几个区域来提升插入排序的性能。这样可以让一个元素可以一次性地朝最终位置前进一大步。然后算法再取越来越小的步长进行排序,算法的最后一步就是普通的插入排序,但是到了这步,需排序的数据几乎是已排好的了(此时插入排序较快)。
最优时间复杂度是o(n),平均时间复杂度根据步长序列的不同而不同,空间复杂度是o(1)。
代码如下:
ShellSort.cpp文件
#include <stdio.h>
class CShellSort{
public:
void Sort(int* pArr, int length)
{
if (pArr == NULL || length == 0)
return;
int nInterval = 0;
while (nInterval < length)
{
nInterval = nInterval * 3 + 1;
}
while (nInterval >= 1)
{
for (int i = 0; i < length; i++)
{
int j = i - nInterval;
int nTemp = pArr[i];
while ( j >= 0 && pArr[j] > nTemp)
{
pArr[j + nInterval] = pArr[j];
j = j - nInterval;
}
pArr[j + nInterval] = nTemp;
}
nInterval = (nInterval - 1) / 3;
}
}
};
main.cpp文件
#include <iostream>
#include <stdio.h>
#include "ShellSort.cpp"
using namespace std;
void main()
{
int test[]={5,6,8,3,6,11,33,7,24,66,9,35,85,11,22};
int count = sizeof(test) / sizeof(int);
CShellSort* pSort = new CShellSort();
if (pSort == NULL)
return;
pSort->Sort(&test[0], count);
delete pSort;
pSort = NULL;
cout << "After sort :" << endl;
for (int i = 0; i < count; i++)
{
cout << test[i] << " ";
}
cout << endl;
system("pause");
}
运行结果如下:
7:归并排序
归并排序是建立在归并操作上的一种有效的排序算法,该算法采用分治法的经典应用。
将已有序的子序列合并,得到完全有序的序列,即先使每个子序列有序,再使子序列段间有序。
基本思路:
1:“分解”—将序列每次折半划分。
2:“合并”—将划分后的序列段两两合并后排序。
基本步骤:
1:将数组平分为两个子数组。
2:递归调用划分数组函数,最后每个数组只有一个元素,即为有序的数组。
3:调用排序函数,把两个有序的数组合并成一个有序的数组。
时间复杂度是o(nlogn),空间复杂度是o(n)
代码如下:
MergeSort.cpp文件
#include <stdio.h>
class CMergerSort{
public:
void Sort(int* pArr, int length)
{
if (pArr == NULL || length == 0)
return;
int* pTemp = new int[length];
if (pTemp == NULL)
return;
MergeSort(pArr, 0, length - 1, pTemp);
delete[] pTemp;
pTemp = NULL;
}
void MergeSort(int* pArr, int nStart, int nEnd, int* pTemp)
{
if (nStart >= nEnd)
return;
int nMiddle = (nStart + nEnd) / 2;
MergeSort(pArr, nStart, nMiddle, pTemp); // 递归划分左边的数组
MergeSort(pArr, nMiddle + 1, nEnd, pTemp); // 递归划分右边的数组
Merge(pArr, nStart, nMiddle, nEnd, pTemp); // 将有序的两个序列合并成一个
}
void Merge(int* pArr, int nStart, int nMiddle, int nEnd, int* pTemp)
{
int nFirst = nStart;
int nSecond = nMiddle + 1;
int nIndex = nStart;
if (nFirst <= nMiddle && nSecond <= nEnd)
{
if (pArr[nFirst] >= pArr[nSecond])
pTemp[nIndex++] = pArr[nSecond++];
else
pTemp[nIndex++] = pArr[nFirst++];
}
while (nFirst <= nMiddle) pTemp[nIndex++] = pArr[nFirst++];
while (nSecond <= nEnd) pTemp[nIndex++] = pArr[nSecond++];
for (int i = nStart; i <= nEnd; i++)
pArr[i] = pTemp[i];
}
};
main.cpp文件
#include <iostream>
#include <stdio.h>
#include "MergeSort.cpp"
using namespace std;
void main()
{
int test[]={5,6,8,3,6,11,33,7,24,66,9,35,85,11,22};
int count = sizeof(test) / sizeof(int);
CMergerSort* pSort = new CMergerSort();
if (pSort == NULL)
return;
pSort->Sort(&test[0], count);
delete pSort;
pSort = NULL;
cout << "After sort :" << endl;
for (int i = 0; i < count; i++)
{
cout << test[i] << " ";
}
cout << endl;
system("pause");
}
运行结果如下:
9:快速排序
基本思路是使用分治法策略,确定一个基准元素,把一个序列分为两个子序列,小于基准的元素放在前面的序列,大于基准的元素放在后面的序列,递归进行此操作直到序列的大小是0或1。
时间复杂度是o(nlogn),空间复杂度一般是o(logn)。
代码如下:
QuickSort.cpp文件
// 使用分治法实现快速排序
#include <stdio.h>
class CQuickSort{
public:
void Swap(int* pArr, int i, int j)
{
int temp = pArr[i];
pArr[i] = pArr[j];
pArr[j] = temp;
}
void Sort(int* pArr, int length)
{
if (pArr == NULL || length == 0)
return;
QuickSort(pArr, 0, length - 1);
}
void QuickSort(int* pArr, int left, int right)
{
int nIdx;
if (left < right)
{
nIdx = Partition(pArr, left, right);
QuickSort(pArr, left, nIdx - 1);
QuickSort(pArr, nIdx + 1, right);
}
}
int Partition(int* pArr, int left, int right) // 划分函数
{
int nIdx = left - 1;
int nTemp = pArr[right]; // 选择最后一个元素作为基准
for (int i = left; i < right; i++)
{
if (pArr[i] <= nTemp) // 把小于基准的元素放在前面子数组
{
nIdx++;
Swap(pArr, nIdx, i);
}
}
int nFindIdx = nIdx + 1;
Swap(pArr, nFindIdx, right); // 将基准放在前面子数组的后面,剩下的就是大于基准的元素
return nFindIdx; // 返回基准的索引
}
};
main.cpp文件
#include <iostream>
#include <stdio.h>
#include "QuickSort.cpp"
using namespace std;
void main()
{
int test[]={5,6,8,3,6,11,33,7,24,66,9,35,85,11,22};
int count = sizeof(test) / sizeof(int);
CQuickSort* pSort = new CQuickSort();
if (pSort == NULL)
return;
pSort->Sort(&test[0], count);
delete pSort;
pSort = NULL;
cout << "After sort :" << endl;
for (int i = 0; i < count; i++)
{
cout << test[i] << " ";
}
cout << endl;
system("pause");
}
运行结果如下:
10:计数排序
步骤:
1:统计数组pArr中每个A[i]出现的次数,存入C[pArr[i]];
2:从前向后,使数组C中的每个值等于其与前一项相加,这样数组C[pArr [i]]就代表了数组pArr中小于等于pArr [i]的元素个数;
3:为了保证稳定性,从后向前反向填充目标数组B,将数组元素pArr [i]放在数组B的第C[pArr [i]]项(即B[C[pArr [i]] - 1]),每放一个元素就将C[pArr [i]]递减;
时间复杂度为o(n + k),空间复杂度为o(n + k)。
计数排序的时间复杂度和空间复杂度取决于数组A的数据范围(等于A中元素的最大值与最小值的差加上1),因此对于数据范围很大的数组,计数排序需要大量时间和内存。
代码如下:
CountingSort.cpp文件
#include <string.h>
#include <stdio.h>
const int nMax = 100;
class CCountingSort{
public:
void Sort(int* pArr, int length)
{
int* C = new int[nMax];
if (C == NULL)
return;
memset(C, 0, nMax * sizeof(int));
for (int i = 0; i < length; i++)
C[pArr[i]]++;
for (int i = 1; i< nMax; i++)
C[i] = C[i] + C[i - 1];
int* B = new int[length];
if (B == NULL)
return;
for (int i = length - 1; i >= 0; i--)
{
int nTemp = pArr[i];
B[C[nTemp] - 1] = nTemp;
C[nTemp]--;
}
for (int i = 0; i < length; i++)
pArr[i] = B[i];
delete C;
C = NULL;
delete B;
B = NULL;
}
};
main.cpp文件
#include <iostream>
#include <stdio.h>
#include "CountingSort.cpp"
using namespace std;
void main()
{
int test[]={5,6,8,3,6,11,33,7,24,66,9,35,85,11,22};
int count = sizeof(test) / sizeof(int);
CCountingSort* pSort = new CCountingSort();
if (pSort == NULL)
return;
pSort->Sort(&test[0], count);
delete pSort;
pSort = NULL;
cout << "After sort :" << endl;
for (int i = 0; i < count; i++)
{
cout << test[i] << " ";
}
cout << endl;
system("pause");
}
运行结果如下:
11:基数排序
基本思路:
将整形10进制按位拆分,然后从低位到高位依次比较各个位。
1:分配,从个位开始,根据位值分别放到0-9号桶中。
2:收集,将10个桶中的数据,按顺序取出到数组中。
3:从个位到高位,依次重复1,2步骤。
时间复杂度为o(n * nMaxPos),空间复杂度为o(n * nMaxPos)
代码如下:
RadixSort.cpp文件
#include <stdlib.h>
const int nMaxPos = 6; // 本程序中元素的最大位数
class RadixSort{
public:
int GetNumIndex(int nNum, int nPos)
{
int nTemp = 1;
for (int nIdx = 1; nIdx < nPos; nIdx++)
nTemp *= 10;
return (nNum / nTemp) % 10;
}
void Sort(int* pArr, int length)
{
int* C[10];
for (int i = 0; i < 10; i++)
{
C[i] = (int*)malloc(sizeof(int) * (length + 1));
C[i][0] = 0; // 0为记录这个数组的个数
}
for (int nPos = 1; nPos <= nMaxPos; nPos++) // 从第一位到最高位依次处理
{
for (int i = 0; i < length; i++) // 分配到0-9号桶中
{
int nNum = GetNumIndex(pArr[i], nPos);
int nIndex = ++C[nNum][0];
C[nNum][nIndex] = pArr[i];
}
for (int i = 0, j = 0; i < 10; i++) // 从0-9号桶收集
{
for (int k = 1; k <= C[i][0]; k++)
pArr[j++] = C[i][k];
C[i][0] = 0; // 个数复位
}
}
}
};
main.cpp文件
#include <iostream>
#include <stdio.h>
#include "RadixSort.cpp"
using namespace std;
void main()
{
int test[]={99999, 65, 24, 47, 13, 878, 321, 5, 82222, 66, 33, 22445, 10001, 624159, 624158, 6251};
int count = sizeof(test) / sizeof(int);
RadixSort* pSort = new RadixSort();
if (pSort == NULL)
return;
pSort->Sort(&test[0], count);
delete pSort;
pSort = NULL;
cout << "After sort :" << endl;
for (int i = 0; i < count; i++)
{
cout << test[i] << " ";
}
cout << endl;
system("pause");
}
运行结果