排序算法分类:
1、基于比较的排序算法
(1)交换排序
冒泡排序、快速排序
(2)插入排序
直接插入排序、二分插入排序、Shell排序
(3)选择排序
简单选择排序、堆排序
(4)合并排序
2、基于数字和地址计算的排序方法
计数排序、桶排序、基数排序
冒泡排序 O(n2)
第一个数和第二个数进行比较,若逆序则交换,然后比较第二个和第三个....直至n-1个数和第n个数比较使最大的安放在最后一个位置(第一趟)。接着对n-1个数(从第一个到n-1,第n大的数已在第一轮被确定)进行第二趟冒泡排序,使最大的安放在第n-1个记录位置。重复上述过程排剩下n-2个数。
时间复杂度:
正序情况:
比较次数:
交换次数:0
逆序情况:
比较次数:
交换次数:
代码如下:
void bubble_modified(int *a, int n)
{
for(int i = 0; i < n - 1; i++) // n-1趟排序
for(int j = 0; j < n - i - 1; j++)
{
if(a[j + 1] < a[j]) swap(a[j], a[j + 1]);
}
}
优化版冒泡排序O(n2)
思路:当某一趟遍历时,没有发生任何数的交换,说明此时数组已经有序,排序可结束。
例如: 4 3 1 2 3
第一次执行后数组为 3 1 2 3 4
第二次执行后数组为 1 2 3 3 4
第三轮执行后数组为 1 2 3 3 4
循环不用执行n-1次,当执行第三次时,发现没有任何数进行了交换(数组已有
序)则可停止。
复杂度仍为O(n2) 但该做法在正序情况下可减少很多时间。
时间复杂度:
正序情况:
比较次数: n-1
交换次数:0
逆序情况:
比较次数:
交换次数:
void Bubble_Modified(int *a, int n)
{
int flag = 1;
for(int i = 0; i < n - 1 && flag; i++)
{
flag = 0; //每一次循环需要重新置0
for(int j = 0; j < n - i - 1; j++)
{
if(a[j] > a[j + 1])
{
flag = 1;//需要再进行下一轮
swap(a[j], a[j + 1]);
}
}
}
}
插入排序O(n2)
思想:将第1个记录看成有序子列,然后从第2个记录开始,逐个插入,直至整个序列有序。
整个过程为n-1趟插入操作。
时间复杂度:
正序情况:
比较次数:n-1
交换次数:0
逆序情况:
比较次数:
交换次数:
代码如下:
void insertion(int *a, int n)
{
for(int i = 1; i < n; i++)
{
int tmp = a[i];
int j = i - 1;
while(tmp < a[j] && j >= 0)
{
a[j + 1] = a[j];
j--;
}
a[j + 1] = tmp;
}
}
二分插入排序
基本思想:用二分查找确定插入位置
时间复杂度:
正序
void Binary_InsertionSort(int *a, int n)
{
int mid, l, r;
for(int i = 1; i < n; i++)
{
l = 0, r = i - 1;
mid = r + l >> 1;
int x = a[i];
while(l <= r)
{
mid = l + r >> 1;
if(x < a[mid]) r = mid - 1;
else l = mid + 1;
}
for(int k = i - 1; k >= l; k--)
a[k + 1] = a[k];
a[l] = x;
}
}
希尔排序(缩小增量法)
思想:先取一个正整数d1<n,把所有相隔d1的数放一组,组内进行直接插入排序
然后取d2<d1,重复上述分组和排序操作
直至di=1,即所有记录放进一个组中排序为止。
(为了区间不重叠,d1、d2....di应该互素,例如若d1取4,而d2取2,重复率太高)
提高速度原因:分组后n值减小,n2更小,而T(n) = O(n2),所以T(n)总体上是减小了。
在进行最后一趟增量为1的插入排序时,序列已基本有序
时间复杂度约为O(n1.3)
选择排序
思想:通过n-1次比较,从n个记录中找出最小的数,将它与第一个数做交换。
再通过n-2次比较,从剩余n-1个记录中找出关键词次小的数,与第二个数做交换
重复上述操作,共进行n-1趟排序后,排序结束。
void selection(int *a, int n)
{
for(int i = 0; i < n - 1; i++)
for(int j = i; j < n - 1; j++)
{
if(a[j + 1] < a[j]) swap(a[j], a[j + 1]);
}
}
void selection_standard(int *a, int n)
{
for(int i = 0; i < n - 1; i++)
{
int j, k;
for(k = i, j = i + 1; j < n; j++)
if(a[k] > a[j]) k = j;
if(k != i) swap(a[i], a[k]);
}
}
选择算法改进版
int Max(int *a, int n, int &m)
{
// 确定a[0:n-1]中最大元素的下标
int pos = 0;
for(int i = 1; i < n; i++)
if(a[pos] < a[i])
{
pos = i;
m ++;
}
return pos;
}
void Selection_Modified(int *a, int n)
{
for(int size = n; size > 1; size --)
{
int m = 0;
int j = Max(a, size, m);
swap(a[j], a[size - 1]);
if(m == size - 1) break;
}
}
)
递归版的合并排序算法
思想:平衡的二分分治
将待排序元素序列简单德分成大小大致相等的左右两段,对这两段子序列递归地进行合并排序,然后利用两段子序列已得到的有序性,将它们有序地合并到一个工作区,最后用工作区中排好序的全序列更新原待排序的元素序列。
void merge_sort(int *a, int l, int r)
{
if(l >= r) return;
int mid = r + l >> 1;
merge_sort(a, l, mid), merge_sort(a, mid + 1, r);
int i = l, j = mid + 1, k = 0;
while(i <= mid && j <= r)
{
if(a[i] <= a[j]) tmp[k++] = a[i++];
else tmp[k++] = a[j++];
}
while(i <= mid) tmp[k++] = a[i++];
while(j <= r) tmp[k++] = a[j++];
for(int i = l, j = 0; i <= r; i++, j++) a[i] = tmp[j];
}
合并排序算法非递归版
思想:将数组a中相邻元素两两配对,用Merge算法将它们合并,得到n/2个长度为2的排好序的数组段,然后再将它们Merge成长度为4的排好序的数组段....如此下去,直至排好整个数组。