插入排序—直接插入排序
将一个记录插入到已排序好的有序表中,从而得到一个新,记录数增1的有序表。即:先将序列的第1个记录看成是一个有序的子序列,然后从第2个记录逐个进行插入,直至整个序列有序为止。
void InsertSort(int *a,int n)
{
int i = 1;
for (i = 1; i < n; i++)
{
int x = a[i];
int cur = i - 1;
while (cur>=0&&a[cur]>x)
{
a[cur + 1] = a[cur];
cur--;
}
a[cur + 1] = x;
}
}
如果碰见一个和插入元素相等的,那么插入元素把想插入的元素放在相等元素的后面。所以,相等元素的前后顺序没有改变,从原无序序列出去的顺序就是排好序后的顺序,所以插入排序是稳定的。
插入排序—希尔排序
是1959 年由D.L.Shell 提出来的,相对直接排序有较大的改进。希尔排序又叫缩小增量
先将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序,待整个序列中的记录“基本有序”时,再对全体记录进行依次直接插入排序。
我们简单处理增量序列:增量序列gap= {n/3 ,n/9, n/27 …..1} n为要排序数的个数
即:先将要排序的一组记录按某个增量d(n/2,n为要排序数的个数)分成若干组子序列,每组中记录的下标相差d.对每组中全部元素进行直接插入排序,然后再用一个较小的增量(d/2)对它进行分组,在每组中再进行直接插入排序。继续不断缩小增量直至为1,最后使用直接插入排序完成排序。
void ShellSort(int *a,int n)
{
int gap = n;
while (gap > 1)
{
gap = gap / 3+1;
int i = 0;
for (i = 0; i < n - gap; i++)
{
if (a[i] > a[i + gap])
{
Swap(&a [i], &a [i + gap]);
}
}
}
}
希尔排序时效分析很难,关键码的比较次数与记录移动次数依赖于增量因子序列gap的选取,特定情况下可以准确估算出关键码的比较次数和记录的移动次数。目前还没有人给出选取最好的增量因子序列的方法。增量因子序列可以有各种取法,有取奇数的,也有取质数的,但需要注意:增量因子中除1 外没有公因子,且最后一个增量因子必须为1。希尔排序方法是一个不稳定的排序方法。
选择排序—简单选择排序
在排序的一组数中,选出最小(或者最大)的一个数与第1个位置的数交换;然后在剩下的数当中再找最小(或者最大)的与第2个位置的数交换,依次类推,直到第n-1个元素(倒数第二个数)和第n个元素(最后一个数)比较为止。
简单选择排序的改进——二元选择排序
简单选择排序,每趟循环只能确定一个元素排序后的定位。我们可以考虑改进为每趟循环确定两个元素(当前趟最大和最小记录)的位置,从而减少排序所需的循环次数。改进后对n个数据进行排序,最多只需进行[n/2]趟循环即可。具体实现如下:
void SelectSort(int *a,int n)
{
while (n>0)
{
int i = 0;
int max = 0;
int min = 0;
for (i = 0; i < n; i++)
{
if (a[i] > a[max])
max = i;
if (a[i] < a[min])
min = i;
}
Swap(&a[0], &a[min]);
if (a[min] != a[max])
{
Swap(&a[n - 1], &a[max]);
}
n--;
}
}
选择排序—堆排序
堆排序是一种树形选择排序,是对直接选择排序的有效改进。由堆的定义可以看出,堆顶元素(即第一个元素)必为最小项(小顶堆)。若以一维数组存储一个堆,则堆对应一棵完全二叉树,且所有非叶结点的值均不大于(或不小于)其子女的值,根结点(堆顶元素)的值是最小(或最大)的。
从算法描述来看,堆排序需要两个过程,一是建立堆,二是堆顶与堆的最后一个元素交换位置。所以堆排序有两个函数组成。一是建堆的渗透函数,二是反复调用渗透函数实现排序的函数。
void AdjustDown(int*a, int n, int root)
{
int parent = root;
int child = (parent << 1) + 1;
while (child < n)
{
if (child + 1 < n&&a[child + 1] > a[child])
child++;
if (a[child] > a[parent])
Swap(&a[child], &a[parent]);
parent = child;
child = (parent << 1) + 1;
}
}
void HeapSort(int *a,int n)
{
int cur = ((n - 2) >>1);
for(;cur>=0;cur--)
{
AdjustDown(a, n, cur);
}
while (n--)
{
Swap(&a[0], &a[n]);
AdjustDown(a, n, 0);
}
}
交换排序—冒泡排序
在要排序的一组数中,对当前还未排好序的范围内的全部数,自上而下对相邻的两个数依次进行比较和调整,让较大的数往下沉,较小的往上冒。即:每当两相邻的数比较后发现它们的排序与排序要求相反时,就将它们互换。
void BubbleSort(int *a,int n)
{
int i = 0;
int flag=0;
for (i = 0; i < n; i++)
{
int j = 0;
for (j = 1; j < n - i; j++)
{
if (a[j - 1] > a[j])
{
Swap(&a[j - 1], &a[j]);
flag=1;
}
}
if(flag==0)
break;
}
}
交换排序—快速排序
选择一个基准元素,通常选择第一个元素或者最后一个元素,通过一趟排序讲待排序的记录分割成独立的两部分,其中一部分记录的元素值均比基准元素值小。另一部分记录的 元素值比基准值大。此时基准元素在其排好序后的正确位置,然后分别对这两部分记录用同样的方法继续进行排序,直到整个序列有序。
快速排序是通常被认为在同数量级(O(nlgn))的排序方法中平均性能最好的。但若初始序列按关键码有序或基本有序时,快排序反而蜕化为冒泡排序。为改进之,通常以“三者取中法”来选取基准记录,即将排序区间的两个端点与中点三个记录关键码居中的调整为支点记录。但是快速排序是一个不稳定的排序方法。
int PastSort1(int *a,int left,int right)
{
//左右指针法
int key = right;
while (left<right)
{
while(left<right&&a[left]<=a[key])
{
left++;
}while (left<right&&a[right]>=a[key])
{
right--;
}
Swap(&a[left], &a[right]);
}
Swap(&a[right], &a[key]);
return left;
}
int PastSort2(int *a, int left, int right)
{
//挖坑
int key = a[right];
while(left<right)
{
while (left<right&&a[left]<=key)
{
left++;
}
a[right] = a[left];
while(left<right&&a[right]>=key)
{
right--;
}
a[left] = a[right];
}
a[left] = key;
return left;
}
int PastSort3(int *a,int left,int right)
{
//前后指针
int key = right;
int prov = left-1;
int cur = left;
while (cur < right)
{
if (a[cur] <= a[key])
{
prov++;
Swap(&a[cur], &a[prov]);
}
cur++;
}
prov++;
Swap(&a[prov], &a[key]);
return prov;
}
void QuitSort(int *a,int left,int right)
{
if (left >= right)
return;
int div=PastSort3(a, left,right);
QuitSort(a, left, div - 1);
QuitSort(a, div + 1, right);
}
void QuitSortNotR(int *a,int left,int right)
{
if (left >= right)return;
Stack s;
StackInit(&s);
StackPush(&s, left);
StackPush(&s, right);
while(StackEmpty(&s))
{
int end = StackTop(&s);
StackPop(&s);
int begin = StackTop(&s);
StackPop(&s);
int div = PastSort1(a, begin, end);
if (div - 1 > begin)
{
StackPush(&s,begin);
StackPush(&s, div - 1);
}
if (div + 1 < end)
{
StackPush(&s, div + 1);
StackPush(&s, end);
}
}
}
归并排序
归并(Merge)排序法是将两个(或两个以上)有序表合并成一个新的有序表,即把待排序序列分为若干个子序列,每个子序列是有序的。然后再把有序子序列合并为整体有序序列。
void MergeSort1(int *a,int left,int right,int *tmp)
{
if (right-left<1)
return;
int mid = left + ((right - left) >> 1);
MergeSort1(a, left,mid,tmp);
MergeSort1(a, mid + 1, right,tmp);
int cur1 = left;
int cur2 = mid + 1;
int index = left;
while(cur1<=mid&&cur2<=right)
{
if (a[cur1] <= a[cur2])
tmp[index++] = a[cur1++];
else
tmp[index++] = a[cur2++];
}
while (cur1 <= mid)
tmp[index++] = a[cur1++];
while(cur2<=right)
tmp[index++] = a[cur2++];
int i = left;
for (; i <= right; i++)
{
a[i] = tmp[i];
}
}
void MergeSort(int *a, int n)
{
int *tmp = (int *)malloc(sizeof(int)*n);
MergeSort1(a, 0, n - 1, tmp);
free(tmp);
}