十大经典排序算法二(希尔排序、堆排序、计数排序、桶排序和基数排序)
起泡排序,别名“冒泡排序”,该算法的核心思想是将无序表中的所有记录,通过两两比较关键字,得出升序序列或者降序序列。
例如,对无序表{49,38,65,97,76,13,27,49}进行升序排序的具体实现过程如图 1 所示:
通过一趟趟的比较,一个个的“最大值”被找到并移动到相应位置,直到检测到表中数据已经有序,或者比较次数等同于表中含有记录的个数,排序结束,这就是起泡排序。
#include
#include
// 采用两层循环实现的方法。
// 参数arr是待排序数组的首地址,len是数组元素的个数。
void swap1(int* a,int* b){
int c = *a;
*a = *b;
*b = c;
}
void bubblesort1(int *arr,unsigned int len)
{
if(len < 2) return;
for(int i = len-1;i>0;i--){
for (int j = 0; j < i; ++j) {
if(arr[j]>arr[j+1]){
swap1(&(arr[j]),&(arr[j+1]));
}
}
}
}// 采用递归实现的方法。
// 参数arr是待排序数组的首地址,len是数组元素的个数。
void bubblesort2(int *arr,unsigned int len)
{
if(len < 2) return ;
for(int i = 0;i<len - 1;i++){
if(arr[i]>arr[i+1]){
swap1(&(arr[i]),&(arr[i+1]));
}
}
bubblesort2(arr,--len);
}
观察上面的冒泡排序算法可知,通过么len - 1趟排序完成,每一趟排序将排好一个数,当排到一定程度时,序列已经有序的情况下,还会完成对有序序列的排序。
#include
#include
// 采用两层循环实现的方法。
// 参数arr是待排序数组的首地址,len是数组元素的个数。
void swap1(int* a,int* b){
int c = *a;
*a = *b;
*b = c;
}
void bubblesort1(int *arr,unsigned int len)
{
int swap ;
if(len < 2) return ;
for(int i = len - 1;i>0;i--){
swap = 0;
for(int j = 0;j < i;j++) {
if (arr[j] > arr[j + 1]){
swap1(&(arr[j]), &(arr[j + 1]));
swap = 1;
}
}
printf("i = %d\n",i);
if(swap == 0) return ;
}
}
// 采用递归实现的方法。
// 参数arr是待排序数组的首地址,len是数组元素的个数。
void bubblesort2(int *arr,unsigned int len)
{
if(len < 2) return ;
int tmp = 0;
for(int i = 0;i<len - 1;i++){
if(arr[i]>arr[i+1]){
swap1(&(arr[i]),&(arr[i+1]));
tmp = 1;
}
}
if(tmp == 0) return ;
printf("len = %d\n",len);
bubblesort2(arr,--len);
}
该算法的实现思想为:对于具有 n 个记录的无序表遍历 n-1 次,第 i 次从无序表中第 i 个记录开始,找出后序关键字中最小的记录,然后放置在第 i 的位置上。
例如对无序表{56,12,80,91,20}采用简单选择排序算法进行排序,具体过程为:
#include
#include
void swap(int *x,int *y)
{
int itmp=*x;
*x=*y;
*y=itmp;
}
// 采用两层循环实现的方法。
// 参数arr是待排序数组的首地址,len是数组元素的个数。
void selectsort1(int *arr,unsigned int len)
{
int npos;
if(len < 2) return ;
for(int i = 0;i < len - 1;i++){
npos = i;
for (int j = i+1; j < len; ++j) {
if(arr[npos]>arr[j]) npos = j;
}
if(i!=npos) swap(&(arr[i]),&(arr[npos]));
}
}
// 采用递归实现的方法。
// 参数arr是待排序数组的首地址,len是数组元素的个数。
void selectsort2(int *arr,unsigned int len)
{
if(len < 2) return ;
int npos = 0;
for(int i = 0;i<len;i++){
if(arr[npos]>arr[i]) npos = i;
}
if(npos != 0) swap(&(arr[0]),&(arr[npos]));
selectsort2(arr+1,--len);
}
由以上的选择排序算法可知,每一趟选择都会选出最小的和第一个交换(第一个就最小除外),需要进行n次选择,可以优化为每一趟选择出两个(一个最大和一个最小的)分别个第一个与最后一个交换。只需要进行n/2次选择。
// 优化后的选择排序,作者:C语言技术网(www.freecplus.net)码农有道。
#include
#include
// 交换两个变量的值。
void swap(int *x,int *y)
{
int itmp=*x;
*x=*y;
*y=itmp;
}
// 采用两层循环实现的方法。
// 参数arr是待排序数组的首地址,len是数组元素的个数。
void selectsort1(int *arr,unsigned int len)
{
if(len < 2) return ;
int minPos;
int maxPos;
int lift = 0,right = len -1;
while (lift < right){
minPos = maxPos = lift;
for(int i = lift ;i<right;i++){
if(arr[minPos] > arr[i]) minPos = i;
if(arr[maxPos] < arr[i]) maxPos = i;
}
if(lift != minPos) swap(&(arr[lift]),&(arr[minPos]));
if(lift == right) maxPos = minPos;
if(maxPos != right) swap(&(arr[right]),&(arr[maxPos]));
lift ++;
right --;
}
}
// 采用递归实现的方法。
// 参数arr是待排序数组的首地址,len是数组元素的个数。
void selectsort2(int *arr,unsigned int len)
{
if(len < 2) return;
int minPos = 0;
int maxPos = 0;
int lift = 0,right = len - 1;
for(int i = lift;i<right;i++) {
if (arr[minPos] > arr[i]) minPos = i;
if (arr[maxPos] < arr[i]) maxPos = i;
}
if (lift != minPos) swap(&(arr[lift]), &(arr[minPos]));
if (lift == right) maxPos = minPos;
if (maxPos != right) swap(&(arr[right]), &(arr[maxPos]));
len-=2;
selectsort2(++arr,len);
}
插入排序算法是所有排序方法中最简单的一种算法,其主要的实现思想是将数据按照一定的顺序一个一个的插入到有序的表中,最终得到的序列就是已经排序好的数据。
直接插入排序
是插入排序算法中的一种,采用的方法是:在添加新的记录时,使用顺序查找的方式找到其要插入的位置,然后将新记录插入。
很多初学者所说的插入排序,实际上指的就是直接插入排序算法,插入排序算法还包括折半插入排序
、2-路插入排序
,表插入排序
和希尔排序
等,后序文章都会一一讲到。
想象我们打扑克摸排的时候每摸到一张牌时我们都会按照该牌面的大小插入到合适的位置。
例如采用直接插入排序算法将无序表{3,1,7,5,2,4,9,6}进行升序排序的过程为:
// 参数arr是待排序数组的首地址,len是数组元素的个数。
void insertsort(int *arr,unsigned int len)
{
int tmp;
for (int i = 1; i < len; ++i) {
tmp = arr[i];
int j;
for ( j = i - 1 ; (j>=0&&arr[j]>tmp); j--) {
arr[j+1] = arr[j];
}
arr[j+1] = tmp;
}
}
不足:
优化方案:
希尔排序
快速排序基本思想
void swap(int *x,int *y)
{
int itmp=*x;
*x=*y;
*y=itmp;
}
void quicksort(int *arr,unsigned int len)
{
if(len<2) return ;
int tmp = arr[0];
int left = 0,right = len - 1;
while(left<right) {
while (arr[right] >= tmp && left < right) right--;
swap(&(arr[left]),&(arr[right]));
while (arr[left]<tmp&&left<right) left++;
swap(&(arr[right]),&(arr[left]));
}
arr[left] = tmp;
quicksort(arr,left);
quicksort(arr+right+1,len - right -1);
}
本节介绍一种不同于插入排序和选择排序的排序方法——归并排序,其排序的实现思想是先将所有的记录完全分开,然后两两合并,在合并的过程中将其排好序,最终能够得到一个完整的有序表。
例如对于含有 n 个记录的无序表,首先默认表中每个记录各为一个有序表(只不过表的长度都为 1),然后进行两两合并,使 n 个有序表变为 ⌈n/2⌉ 个长度为 2 或者 1 的有序表(例如 4 个小有序表合并为 2 个大的有序表),通过不断地进行两两合并,直到得到一个长度为 n 的有序表为止。这种归并排序方法称为:2-路归并排序。
例如对无序表{49,38,65,97,76,13,27}进行 2-路归并排序的过程如图 1 所示:
#include
using namespace std;
const int MAX = 100005;
typedef long long ll;
ll a[MAX] = {5,4,4,7,4,3,2,1,1},b[MAX];
void merge(ll l,ll mid,ll r){
int i = l,j = mid + 1;
int tmp = 0;
while (i<=mid&&j<=r){
if(a[i]>a[j]) b[tmp++] = a[j++];
else b[tmp++] = a[i++];
}
while (i<=mid) b[tmp++] = a[i++];
while (j<=r)b[tmp++] = a[j++];
for( i = 0 ;i< tmp ;i++){
a[l+i] = b[i];
}
}
void mergeSort(ll l,ll r){
if(l<r){
int mid = (l + r) / 2;
mergeSort(l,mid);
mergeSort(mid+1,r);
merge(l,mid,r);
}
}
int main(){
mergeSort(0,8);
for(int i = 0;i<9;i++){
cout<<a[i]<<" ";
}
}