排序算法学习
本文包含:
1.冒泡排序
2.及时终止的冒泡排序
3.计数排序
4.插入排序
5.选择排序
6.及时终止的选择排序
7.归并排序
8.快速排序
9.基数排序
10.希尔排序
1.冒泡排序
主要思想:两重循环,遇到一个数比它后一个数大时就交换位置。
步骤:
1.比较相邻的元素。如果第一个比第二个大,就交换它们两个;
2. 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对,这样在最后的元素应该会是最大的数;
3. 针对所有的元素重复以上的步骤,除了最后一个;
4. 重复步骤1~3,直到循环完成。
//冒泡排序
template<class T>
void bubbleSort(T a[], int n) {
for (int i = 0; i < n; ++i) {
for (int j = 0; j < n; ++j) {
if (a[j] > a[j + 1]) {
swap(a,j,j+1);
}
}
}
}
时间复杂度O(n^2)
空间复杂度O(1)
2.及时终止的冒泡排序
添加判断代码,避免做无用功
主要思想:两重循环,遇到一个数比它后一个数大时就交换位置,直到每一个数的后一个数都比它本身大。只是当数组已经有序时便跳出循环
步骤:
1.比较相邻的元素。如果第一个比第二个大,就交换它们两个;
2.对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对,这样在最后的元素应该会是最大的数;
3.针对所有的元素重复以上的步骤,除了最后一个;
4. 重复步骤1~3,直到排序完成。
//及时终止的冒泡排序
template<class T>
void bubbleSort2(T a[], int n) {
bool swapped = true;//用于判断数组是否已经有序了
for (int i = n; i > 1 && swapped; --i) {
swapped = false;
for (int j = 0; j < i - 1; ++j) {
if (a[j] > a[j + 1]) {
swap(a,j,j+1);
swapped = true;
}
}
}
}
时间复杂度O(n^2)
空间复杂度O(2)
3.计数排序
最快的但只能进行整数排序也不适合数值跨度范围很大的排序
主要思想:先计算每个数应该排在第几名,再通过数字的名次把它交换到它应处的位置。
步骤:
1.找出待排序的数组中最大和最小的元素;
2.统计数组中每个值为i的元素出现的次数,存入数组C的第i项;
3.反向填充目标数组:将每个元素i放在新数组的第C(i)项,每放一个元素就将C(i)减去1。
//计数排序
template<class T>
void countSort(T a[], int n) {
int bias, min = array[0], max = array[0];
for (int i = 1; i < array.length; i++) {
if (array[i] > max)
max = array[i];
if (array[i] < min)
min = array[i];
}
bias = 0 - min;
T *b = new T[max - min + 1];
//先初始化数组b
for (int i = 0; i < n; ++i) {
b[i] = 0;
}
//计算名次
for (int i = 0; i < array.length; i++) {
bucket[array[i] + bias]++;
}
//根据名次排序
int index = 0, i = 0;
while (index < array.length) {
if (bucket[i] != 0) {
array[index] = i - bias;
bucket[i]--;
index++;
} else
i++;
}
}
时间复杂度O(n+k)
k为输入的元素的最大值减最小值加1
空间复杂度O(k)
4.插入排序
主要思想:将每个数都插入到当前顺序中它应在的位置,当完成所有数的插入后,便成了有序数组。
步骤:
1.从第一个元素开始,该元素可以认为已经被排序;
2.取出下一个元素,在已经排序的元素序列中从后向前扫描;
3. 如果该元素(已排序)大于新元素,将该元素移到下一位置;
4.重复步骤3,直到找到已排序的元素小于或者等于新元素的位置;
5.将新元素插入到该位置后;
6. 重复步骤2~5直到循环结束。
//插入排序
template<class T>
void insertSort(T a[], int n) {
for (int i = 0; i < n; ++i) {
T temp = a[i];
int j;
for (j = i - 1; j >= 0 && a[j] > temp; --j) {
a[j + 1] = a[j];
}
a[j + 1] = temp;
}
}
时间复杂度O(n^2)
空间复杂度O(1)
5.选择排序
主要思想:选择最大的数放到最后面,再选择除了已选出来的数中最大的,放到这些数都后面。
步骤:
1.选出数组中最大的数
2.将最大的数与数组最后一个数交换
3.数组长度减1
4.重复1-3直到循环结束
//选择排序
template<class T>
void selectionSort(T a[], int n) {
for (int i = n; i > 1; --i) {
int indexOfMax = 0;
for (int j = 0; j < i; ++j) {
if (a[indexOfMax] <= a[j]) {
indexOfMax = j;
}
}
swap(a,indexOfMax,n-1);
}
}
时间复杂度O(n^2)
空间复杂度O(1)
6.及时终止的选择排序
主要思想:同上添加判断代码,避免做无用功
后面的数都>前面的数了则停止。
步骤:
1.选出数组中最大的数
2.将最大的数与数组最后一个数交换
3.数组长度减1
4.重复1-3直到排序完成
//及时终止的选择排序
template<class T>
void selectionSort2(T a[], int n) {
bool sorted = false;//用于判断数组是否已经有序了
for (int i = n; !sorted && i > 1; --i) {
int indexOfMax = 0;
sorted = true;
for (int j = 0; j < i; ++j) {
if (a[indexOfMax] <= a[j]) {
indexOfMax = j;
} else {
sorted = false;
}
}
swap(a,indexOfMax,n-1);
}
}
时间复杂度O(n^2)
空间复杂度O(2)
7.归并排序
主要思想:两个两个的排序,当有序后就合并为一个单位元看待,再进行两个两个的排序,直到都合并成一个。
步骤:
1.把长度为n的输入序列分成两个长度为n/2的子序列;
2.对这两个子序列分别采用归并排序;
3.将两个排序好的子序列合并成一个最终的排序序列。
驱动程序
//归并排序
template<class T>
void mergeSort(T a[], int n) {
T *b = new T[n];
int segmentSize = 1;
while (segmentSize < n) {
mergePass(a, b, n, segmentSize);
segmentSize += segmentSize;
mergePass(b, a, n, segmentSize);
segmentSize += segmentSize;
}
delete[] b;
}
确定需要归并的子序列的左右边界
template<class T>
void mergePass(T a[], T b[], int n, int segmentSize) {
int i = 0;
while (i <= n - 2 * segmentSize) {
merge(a, b, i, i + segmentSize - 1, i + 2 * segmentSize - 1);
i = i + 2 * segmentSize;
}
if (i + segmentSize < n) {
merge(a, b, i, i + segmentSize - 1, n - 1);
} else {
for (int j = i; j < n; ++j) {
b[j] = a[j];
}
}
}
实际完成基本的归并操作
template<class T>
void merge(T a[], T b[], int x, int y, int z) {
int first = x, second = y + 1, result = x;
while((first<=y)&&(second<=z)){
if(a[first]<=a[second]){
b[result++]=a[first++];
} else{
b[result++]=a[second++];
}
}
if(first>y){
for (int i = second; i <= z; ++i) {
b[result++]=a[i];
}
} else{
for (int i = first; i <= y; ++i) {
b[result++]=a[i];
}
}
}
时间复杂度O(nlogn)
空间复杂度O(n)
8.快速排序
主要思想:先找一个数(这里找的是第一个),将小于它的数放到它左边,将大于它的数放到它右边,再将它左边和右边的数分别进行快速排序。
步骤:
1.从数列中挑出一个元素,称为 “基准”,这里取数组第一个元素;
2.重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区操作;
3.递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。
驱动程序
//快速排序
template<class T>
void quickSort(T a[],int n){
if(n<=1)
return;
int indexOfMax = 0;
for (int j = 0; j < n; ++j) {
if (a[indexOfMax] <= a[j]) {
indexOfMax = j;
}
}
swap(a[n-1],a[indexOfMax]);//将最大元素移到最右边
quickSortStep(a,0,n-2);//进入递归程序
}
递归快速排序
template<class T>
void quickSortStep(T a[],int left,int right){
if(left>=right)
return;
int lTemp=left+1,rTemp=right;
T x=a[left];
//将比支点小的元素换到从右侧换到左侧
while (true){
while (a[lTemp]<a[left]){
lTemp++;
}
while (a[rTemp]>a[left]){
rTemp--;
}
if(lTemp>=rTemp)
break;
swap(a,lTemp,rTemp);
}
swap(a[left],a[rTemp]);//将支点换到中间
//在分别将左右侧元素进行快速排序
quickSortStep(a,left,rTemp-1);
quickSortStep(a,rTemp+1,right);
}
时间复杂度O(n^2)
空间复杂度O(logn)
9.基数排序
主要思想:先用最低位排序,再用下一位,一直排到最高位。
步骤:
1.取得数组中的最大数,并取得位数;
2.从最低位开始取每个位组成数组;
3.对每个由为元素组成的数组进行计数排序(利用计数排序适用于小范围数的特点);
//基数排序
template<class T>
void radixSort(T a[], int n) {
//找出所有数中的最大位数
int m = 0;
for (int i = 0; i < n; ++i) {
int k = 1;
T temp = a[i];
while (temp / 10 != 0) {
temp /= 10;
k++;
}
if (k > m) {
m = k;
}
}
//排序
int num = 1;
for (int j = 0; j < m; ++j) {//最大是几位就循环几次
int x[10][n];//第j位属于0-9的哪一个就存入对应的x[0-9]
int y[10];//存每个x[0-9]中有几个数
for (int j = 0; j < 10; j++) {
y[j] = 0;
}//初始化
int e, temp;
for (int i = 0; i < n; i++) {
temp = a[i] / num;
e = temp % 10;
x[e][y[e]] = a[i];
y[e]++;
}//存入
int f = 0;
for (int k = 0; k < 10; k++) {
if (y[k] > 0)
for (int i = 0; i < y[k]; i++) {
a[f] = x[k][i];
f++;
}
}//取出按顺序放回
num *= 10;//进入高一位排序
}
}
时间复杂度O(kn)
(k为最大数的位数)
空间复杂度O(n+k)
10.希尔排序
主要思想:又叫缩小增量排序,具体操作是把记录按下表的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个文件恰被分成一组,算法便终止。
步骤:
1.先将数组分为gap=length/2个序列
2.对每个序列进行插入排序
3.将gap缩小为gap/2,将数组分为gap个序列
4.重复2-3直到gap为0
void ShellSort(int[] array, int n) {
int temp, gap = n / 2;
while (gap > 0) {
for (int i = gap; i < len; i++) {
temp = array[i];
int preIndex = i - gap;
while (preIndex >= 0 && array[preIndex] > temp) {
array[preIndex + gap] = array[preIndex];
preIndex -= gap;
}
array[preIndex + gap] = temp;
}
gap /= 2;
}
}
时间复杂度为O(nlog2n)
空间复杂度O(1)