上一篇博文中提到 “快速寻找满足条件的两个数” 问题需要分别用到排序和查找两个函数,那么这里就系统地介绍下C++中一些常用的排序算法 (略去上一篇博文中的“快速排序”算法)。
一、交换排序
1. 冒泡排序:
这个算法的名字由来是因为越小(大)的元素会经由交换慢慢“浮”到数列的顶端。它重复地访问所要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。直到没有再需要交换,也就是说该数列已经排序完成。
//冒泡排序
void bubbleSort(int arr[],int n)
{
int i, j, temp;
bool isChanged; //记录此次遍历是否有数值的交换
for (i = 0; i < n - 1; i++) //i用于控制循环次数并记录已排好序的数列长度
{
for(j = 0; j < n - i - 1; j++) //j用于对该数列未排好序部分进行遍历控制
{
isChanged = false;
//把最大的那个数“沉”到数列的最底端
if (arr[j + 1] < arr[j])
{
temp = arr[j + 1];
arr[j + 1] = arr[j];
arr[j] = temp;
isChanged = true; //发生数值交换
}
}
//未发生数值交换,说明数列已完成排序
if (false == isChanged)
{
break;
}
}
}
2. 快速排序
快速排序是对冒泡排序的一种改进。它的基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
**代码略(见上一篇博客)
二、插入排序
1.直接插入排序
将数组分为无序区和有序区两个区,然后不断将无序区的第一个元素按大小顺序插入到有序区中去,最终将所有无序区元素都移动到有序区完成排序。直接插入排序与打扑克时整理手上的牌非常类似。摸来的第1张牌无须整理,此后每次从桌上的牌(无序区)中摸最上面的1张并插入左手的牌(有序区)中正确的位置上。为了找到这个正确的位置,须自左向右(或自右向左)将摸来的牌与左手中已有的牌逐一比较。
//直接插入排序
void insertSort(int arr[],int n)
{
int i, j, temp;
for (i = 0; i != n - 1; i++) //记录有序区的大小
{
if (arr[i + 1] < arr[i])
{
j = i;
temp = arr[i + 1];
do
{
arr[j + 1] = arr[j]; //将数值大于temp的记录后移
j--;
} while (temp < arr[j] && j >= 0); //比较大小并判断数组边界
arr[j + 1] = temp;
}
}
}
2. 希尔排序
先取一个小于n的整数d1作为第一个增量,把文件的全部记录分成d1个组。所有距离为d1的倍数的记录放在同一个组中。先在各组内进行直接插入排序;然后,取第二个增量d2<d1重复上述的分组和排序,直至所取的增量dt=1(dt<d(t-l)<…<d2<d1),即所有记录放在同一组中进行直接插入排序为止。
//希尔排序
void shellSort(int arr[],int n)
{
int gap; //增量,即"d"
for(gap = n / 2; gap > 0; gap = gap / 2) //缩小增量
{
//在各组内进行直接插入排序
for(int i = 0; i < gap; i++) //arr[0]~arr[gap-1]为每个组的首元素
{
for(int j = i + gap; j < n; j = j + gap) //对所有距离为gap的倍数的记录进行操作
{
if(arr[j] < arr[j - gap])
{
int temp = arr[j];
int k = j - gap;
while(k >= 0 && arr[k] > temp)
{
arr[k + gap] = arr[k];
k = k - gap;
}
arr[k + gap] = temp;
}
}
}
}
}
三、选择排序
1. 直接选择排序
直接选择排序也是一种简单的排序方法,它的基本思想是:第一次从R[0]~R[n-1]中选取最小值,与R[0]交换,第二次从R[1]~R[n-1]中选取最小值,与R[1]交换...
//直接选择排序
void selectSort(int arr[],int n)
{
int i, j, k, temp;
for (i = 0; i != n - 1; i++)
{
k = i;
for (j = i + 1; j != n; j++)
{
if (arr[j] < arr[k])
{
k = j;
}
}
temp = arr[i];
arr[i] = arr[k];
arr[k] = temp;
}
}
2. 堆排序
堆排序是指利用堆积树(堆)这种资料结构所设计的一种排序算法,可以利用数组的特点快速定位指定索引的元素。
**具体代码部分将在以后的博客中详解。
四、归并排序
归并排序是建立在归并操作上的一种有效的排序算法。该算法是采用分治法的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。
**具体代码部分将在以后的博客中详解。
五、基数排序
基数排序属于“分配式排序”,有最高位优先(Most Significant Digit first)法,简称MSD法和最低位优先(Least Significant Digit first)法,简称LSD法。其原理是将整数按位数切割成不同的数字,然后按每个位数分别比较。
**具体代码部分将在以后的博客中详解。
--------------------我---是---分---割---线--------------------
各种排序算法性能比较
1. 稳定性比较:
稳定的:基数排序、冒泡排序、直接插入排序、折半插入排序、归并排序
不稳定的:快速排序、希尔排序、堆排序、直接选择排序
2. 时间复杂度比较:
3. 空间复杂度比较:
空间性能是排序算法所需辅助空间大小
1) 所有简单排序和堆排序都是O(1)
2) 快速排序为O(log2n),要为递归程序执行过程中的栈分配所需的辅助空间
3) 归并排序和基数排序所需辅助空间最多,为O(n)