*稳定排序算法:对于值相同的元素经过排序后其顺序和输入一样的算法。
不稳定排序算法即值相同的元素排序后顺序发生变化。
当数据包含多个信息而要按其中的某一个信息排序,要求其它信息尽量按输入的顺序排列时,稳定排序算法就很重要了。
1. 冒泡排序
第一次循环找出最大值,第二次循环找出第二大的值。。。直到找到最后一个。
2. 插入排序
先将前面 i ( 1 <= i <= N) 个元素排序,然后将第 (i+1) 个元素插入到 前面 i 个有序序列中。
// 版本1
void InsertSort(int a[], int n) {
for(int i=1; i<n; ++i)
for(int j=i; j>0 && a[j-1]>a[j]; --j)
swap(&a[j-1], &a[j]);
}
// 版本2
void InsertSort1(int a[], int n) {
for(int i=1; i<n; ++i) {
int t = a[i];
int j = i;
for(; j>0 && a[j-1]>t; --j)
a[j] = a[j-1];
a[j] = t;
}
}
3. Bitmap排序
用一个bit位来标记某个元素对应的Value, 而Key即是该元素。由于采用了Bit为单位来存储数据,因此在存储空间方面,可以大大节省。
假设我们要对0-7内的5个元素(4,7,2,5,3)排序(所有元素不能重复)。
当采用Bit-map的方法排序时,要表示8个数,我们就需要8个Bit(1Bytes),首先我们开辟1Byte的空间,将这些空间的所有Bit位都置为0;
然后遍历这5个元素,首先第一个元素是4,那么就把4对应的位置为1(可以这样操作 p+(i/8)|(0×01<<(i%8)) 当然了这里的操作涉及到Big-ending和Little-ending的情况,
这里默认为Big-ending);
因为是从零开始的,所以要把第五位置为1。
然后再处理第二个元素7,即把第八位置为1;接着再处理第三个元素,一直到最后处理完所有的元素,将相应的位置为1。
然后我们现在遍历一遍Bit区域,将该位是一的位的编号输出(2,3,4,5,7),这样就达到了排序的目的。
4. 归并排序
算法步骤:
-
申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列;
-
设定两个指针,最初位置分别为两个已经排序序列的起始位置;
-
比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置;
-
重复步骤 3 直到某一指针达到序列尾;
-
将另一序列剩下的所有元素直接复制到合并序列尾。
// 迭代法 实现归并排序(还可以用递归法实现)
void merge_sort(int arr[], int len)
{
int *a = arr;
int *b = (int*)malloc(len * sizeof(int)); // 需要额外的空间支持
int seg, start;
// 先将序列按两两排序,然后将排好的两个数当成一个整体,再进行两两排序,直到完成
for(seg = 1; seg < len; seg += seg)
{
for(start = 0; start < len; start += seg * 2)
{
int low = start, mid = min(start + seg, len), high = min(start + seg*2, len);
int k = low;
int start1 = low, end1 = mid;
int start2 = mid, end2 = high;
while(start1 < end1 && start2 < end2)
b[k++] = a[start1] < a[start2] ? a[start1++] : a[start2++];
while(start1 < end1)
b[k++] = a[start1++];
while(start2 < end2)
b[k++] = a[start2++];
}
int *temp = a; // 两个指针的交换。
a = b; // 将部分排好的序列存入 a,然后对 a 再进行排序的结果又存入 b 中。
b = temp;
}
if(a != arr)
{
int i;
for(i = 0; i < len; i++)
b[i] = a[i];
b = a;
}
free(b);
}
5. 快速排序
5.1 单向移动版本
这个版本的关键是设置一快一慢两个指针,慢指针左侧都是小于pivot(包含慢指针所在位置),
慢指针到快指针之间的值是大于等于pivot,快指针右侧的值是还未比较过的。如下图所示:
|————————————|————————————|——————————————|
head 小于pivot slow 大于等于pivot fast 还未比较 end
// 交换两个地址中的值
void swap(int *a, int *b)
{
int tmp = *a;
*a = *b;
*b = tmp;
}
// 对于数组 a[len] 排序,直接调用 QSort(a, 0, len - 1);
void QSort(int a[], int head, int end) {
if(a==NULL || head>=end) return;
int slow = head, fast = head + 1;
int pivot = a[head];
while(fast <= end) {
if(a[fast] < pivot)
swap(&a[++slow], &a[fast]);
++fast;
}
swap(&a[head], &a[slow]);
QSort(a, head, slow-1);
QSort(a, slow+1, end);
}
5.2 双向移动版本
|—————————————|———————————————|————————————————|
head 小于pivot left 还未比较的值 right 大于等于pivot end
// 对于要排序的 a[len], 可直接调用 QSort(a, 0, len - 1)
void QSort(int *a, int head, int end)
{
if(head >= end) /*如果左边索引大于或者等于右边的索引就代表已经整理完成一个组了*/
{
return ;
}
int left = head;
int right = end;
int pivot = a[head];
while(left < right) /*控制在当组内寻找一遍*/
{
while(left < right && pivot <= a[right])
{
right--; /*向前寻找*/
}
a[left] = a[right];
while(left < right && pivot >= a[left])
{
left++;
}
a[right] = a[left]; // a[right]的值不会丢失,因为上面已经将其保存在其他位置了
}
a[left] = pivot; /*当在当组内找完一遍以后就把中间数key回归*/
sort(a, head, left - 1); /*最后用同样的方式对分出来的左边的小组进行同上的做法*/
sort(a, left + 1, end); /*用同样的方式对分出来的右边的小组进行同上的做法*/
}