参考资料:《C++数据结构与算法》(第4版)
01 题目感知…
题目:分别采用以下3种排序方法,对数组[5,2,3,8,1]按升序排序。
方法一:【选择排序】
include<iostream>
using namespace std;
void selection(int *num,int n ) {
int i, j, least, temp;
for (i = 0; i < n; i++) {
least = i;
for (j = i + 1; j < n; j++)
if (num[j] < num[least])
least = j;
if (least != i)
swap(num[j - 1], num[j]);
}
int main() {
int num[5] = {5, 2, 3, 8, 1};
selection(num, 5);
for (int i = 0; i < 5; i++)
cout << num[i];
}
方法二:【冒泡排序】
01 从前往后冒
#include<iostream>
using namespace std;
//从前往后冒
void bubblesortFromhead(int *num,int n ) {
int i, j, temp;
for (i = 0; i < n - 1; i++)
for (j = 0; j < n - 1 - i; j++)//最大的放在最后一个
if (num[j + 1] < num[j])//谁小谁往前
swap(num[j], num[j + 1]);
}
int main(){
int num[5]={5,2,3,8,1};
bubblesortFromhead(num,5);
for(int i=0;i<5;i++)
cout << num[i];
}
02 从后往前冒
#include<iostream>
using namespace std;
//从后往前冒
void bubblesortFromback(int *num,int n ) {
int i, j, temp;
for (i = 0; i < n - 1; i++)
for (j = n - 1; j > i; j--)
if (num[j - 1] > num[j]) {
swap(num[j-1],num[j]);
}
}
int main(){
int num[5]={5,2,3,8,1};
bubblesortFromback(num,5);
for(int i=0;i<5;i++)
cout << num[i];
}
方法三:【插入排序】
#include<iostream>
using namespace std;
void insertionsort(int *num,int n ) {
int i, j, temp;
for (i = 1; i < n ; i++) {
temp = num[i];
for (j = i; j > 0 && temp < num[j - 1]; j--) {//到0位置或前面小停止
num[j] = num[j - 1];
num[j-1] = temp;
}
}
}
int main(){
int num[5]={5,2,3,8,1};
insertionsort(num,5);
for(int i=0;i<5;i++)
cout << num[i];
}
02 方法剖析…
PART one:基本的排序方法
1.选择排序
选择排序就是先找到位置不合适的元素,再把它放在其最终的合适位置上,很明确地直接交换数组元素。
(1)算法
如N个数升序排列:
step1:0到N-1位置找最小值(注意是找到最小的再交换,不是找到了小的就交换)和第0位置交换。
step2:1到N-1位置找最小值,和第1位置交换。
···
stepN-1:N-2到N-1位置找最小值,和第N-2位置交换。
伪代码如下:
selectionsort(data[],n)
for i=0 到 n-2//n-2s是最后一个i值,因为如果除最后一个元素之外的所有元素都放在合适的位置上,那么第n个元素(位于n-1位置上)就一定是最大的
找出元素data[i],…,data[n-1]中的最小元素;
将其与data[i]交换;
(2)复杂度
时间复杂度:
额外空间复杂度:
(3)优缺点
优点:赋值次数少。这次其它算法无法比拟的。
2.冒泡排序
(1)算法
如N个数升序排列:
①从前往后冒泡
round1
step1:0到1位置哪个大,谁跑后面去。
step2:1到2位置哪个大,谁跑后面去。
···
stepN-1:N-2到N-1位置哪个大,谁跑后面去。
round2
step1:0到1位置哪个大,谁跑后面去。
step2:1到2位置哪个大,谁跑后面去。
···
stepN-2:N-3到N-2位置哪个大,谁跑后面去。
.
.
.
roundN-1
step1:0到1位置哪个大,谁跑后面去。
伪代码如下:
bubblesort(data[],n)
for i=0 到 n-2
for j=0 往上到 n-2-i
如果两者逆序,则交换位置j和位置j-1的元素
②从后往前冒泡
round1
step1:N-1到N-2位置哪个小,谁跑前面去。
step2:N-2到N-3位置哪个小,谁跑前面去。
···
stepN-1:1到0位置哪个小,谁跑前前去。
round2
step1:N-2到N-3位置哪个小,谁跑前面去。
step2:N-3到N-4位置哪个小,谁跑前面去。
···
stepN-2:1到0位置哪个小,谁跑前面去。
.
.
.
roundN-1
step1:1到0位置哪个小,谁跑前面去。
伪代码如下:
bubblesort(data[],n)
for i=0 到 n-2
for j=n-1 往下到 j+1
如果两者逆序,则交换位置j和位置j-1的元素
(2)复杂度
时间复杂度:
额外空间复杂度:
(3)优缺点
缺点:元素需要一步步地向上冒泡知道数组的顶部,这是非常费时的。
3.插入排序
(1)算法
如N个数升序排列:
使0到位置有序。
step1:从1位置往前看,前面大就交换,否则停止。
step2:到0位置或前面小停止。
这里的时间复杂度与数据的具体状况有关,这是与前两者排序的区别。插入排序在最好的情况下只需要次比较,但是在平均情况和最坏情况下,插入排序需要次比较。
伪代码如下:
insertionsort(data[],n)
for i=1 到 n-1
将大于data[i]的所有元素data[j]都移动有一个位置,将data[i]放在合适的位置上
(2)复杂度
时间复杂度:(算法流程按照最差情况来估计时间复杂度,但是做实验一定比前两者好。)
额外空间复杂度:
【拓展】对有序数列查找某元素——二分法
在一个有序数组中,找某个数是否存在
在一个有序数组中,找>=某个数最左侧的位置
局部最小值问题
如N个升序排列的数,要找n:
时间复杂度:。多少次才能分成只有一个数。
(3)优缺点
优点:只有需要时才会对数组进行排序。如果数组已经排好序,就不会再对数据进行移动。
PART two:高效的排序方法
4.快速排序
(1)算法
快速排序本身是递归的,因为它在划分的每个层次上都将快速排序应用到数组的两个子数组上。
伪代码如下:
quicksort2 (array[])
if length(array)>10
将arrary划分为子数组subarry1和subarry2
quicksort(subarray1);
quicksort(subarray2);
else insertionsort(array);//对于少于30个数据项的小数组来说,插入排序比快速排序更加有效
最简单的策略之一是选择数组的第一个元素作为边界值。一种更加谨慎的办法就是选取位于数组中间位置的元素作为边界值。
(2)复杂度
(3)优缺点
5.归并排序
快速排序最大的问题是它在最坏的情况下复杂度是O(n^2)(很难控制划分的过程)。另一个策略是使划分尽可能简单,着重于合并两个已经排好序的数组。
归并排序的主要过程是将多个已经排好序的子数组合并一个排好序的数组。然而这些子数组必须先排好序,合并方法是合并更小的、已排好序的子数组。这种算法的本身也是递归的。
伪代码如下:
mergesort (data[],first,last)
if first<last
mid=(first+last)/2;
mergesort(data,first,mid);
merge(data,mid+1,last);
merge(data,first,last);