插入:基础的、改进的、希尔
选择:基础的、堆排序
交换:冒泡、快排及其改进
归并:将两个已经排好序的文件归并成一个有序的大文件(比较适合链表)
typedef int Item;
#define key(A) (A)
#define eq(A,B) (key(A) == key(B))
#define less(A,B) (key(A) < key(B))
#define exch(A,B) {Item t = A;A = B; B =t;}
#define compexch(A,B) if(less(B,A)) exch(A,B)
#define min(A,B) A < B ? A : B
//基础的插入排序
void simpleinsertsort(Item a[],int l,int r)
{
int i,j;
for(i = l+1;i <= r;i++)
for(j = i;j > l;j--)
compexch(a[j-1],a[j]); //从i处往前对比,把小的移到前面!
}
//改进的插入排序
void insertsort(Item a[],int l,int r)
{
int i,j;
Item v;
for(i = r;i > l;i--)
compexch(a[i-1],a[i]); //将最小值放在首位
for(i = l+2;i <= r;i++)
{
j = i;
v = a[i];
while(less(v,a[j-1])) //与当前索引值比较,比v大就将其右移,否则退出,
{
a[j] = a[j-1]; //只是赋值,并不交换
j--;
}
a[j] = v; //此时j+1与i之间的数都是比v大的,将v插入在j
}
}
//希尔排序
void shellsort(Item a[],int l,int r) //对步长的选择很重要(h要取到1)
{
int h,i,j;
for(h = 1;h <= (r-l)/7;h = 3*h+1); //创建一个步长序列,最终h要取到1
for(;h > 0;h = h/3) //对每一个步长都要进行一次扫描排序
for(i = l+h;i <= r;i++) //从l+h开始:因为后面有对j-h的比较,
{
j = i;
Item v = a[j];
while(j-h >= l && less(v,a[j-h])) //这一循环与insertsort的思想一样
{
a[j] = a[j-h]; //只不过insert是h=1,这里的步长h可变
j = j-h;
}
a[j] = v;
}
}
//选择排序
void selectsort(Item a[],int l,int r)
{
int i,j,Min;
for(i = l;i < r;i++) //当前索引左边的元素都是排好序的(最终位置)
{
Min = i;
for(j = i+1;j <= r;j++) //从左到右,记录最小元素下标,
if(less(a[j], a[Min]))
Min = j;
exch(a[i],a[Min]); //将最小元素交换到相应位置
}
}
//堆排序
void fixDown(Item *pq,int k,int N) //自顶向下堆化
{
int j;
while(2*k <= N)
{
j = 2*k;
if(j < N && less(pq[j],pq[j+1])) //找到子节点中的较大值
j++;
if(!less(pq[k],pq[j])) //若较大值小于父节点就结束,否则交换子父节点
break;
exch(pq[k],pq[j]);
k = j;
}
}
void heapsort(Item a[],int l,int r)
{
Item *pq = a;
int k,N = r-l;
for(k = N/2;k >= l;k--) //将最大值交换到数组首位
fixDown(pq,k,N);
show(a,l,r);
while(N > 0)
{
exch(pq[0],pq[N]); //每次将首位元素(剩余数组元素中最大值)与最后的元素交换
fixDown(pq,0,--N); //接着数组大小-1,将剩余元素堆化,接着下一轮循环
}
}
//冒泡排序
void bubblesort(Item a[],int l,int r) //遍历文件,依次比较相邻元素,将最小值排到前面
{
int i,j;
for(i = l;i < r;i++)
for(j = r;j > i;j--) //for(j = l+1;j <= i;j++)也可以用前面的循环
compexch(a[j-1],a[j]);
}
//快速排序中的划分函数
int qpartition(Item a[],int l,int r)
{
Item v = a[r];
int i,j;
i = l;
j = r-1;
for(;;)
{
while(less(a[i],v)) //左扫描,划分元素v左边不大于v
i++;
while(less(v,a[j])) //右扫描,划分元素v右边不小于v
{
j--;
if(j == l)
break;
}
if(j <= i)
break;
exch(a[i],a[j]); //左扫描大于v的元素与右扫描小于v的元素交换
}
exch(a[r],a[i]); //划分结束,将划分元素置换到结束位置
return i;
}
//基本的快排:
void quicksort(Item a[],int l,int r)
{
int i;
if(r < l)
return;
i = qpartition(a,l,r);
quicksort(a,l,i-1);
quicksort(a,i+1,r);
}
//三路取中快排:取待排序列的首、中、尾三个元素,取大小居中的作为划分元素
void quicksortMid(Item a[],int l,int r)
{
if(r-l <= 3)
{
simpleinsertsort(a,l,r); //当数组小于某一大小时,就采用插入操作
return;
}
exch(a[(l+r)/2],a[r-1]);
compexch(a[l],a[r-1]);
compexch(a[l],a[r]);
compexch(a[r-1],a[r]); //将三者中的中间元素放于r-1处,v=a[r-1]
int i = qpartition(a,l+1,r-1); //v=a[r-1]
quicksortMid(a,l,i-1);
quicksortMid(a,i+1,r);
}
//三路划分快排:将待排序列分为三路:小于v、等于v、大于v
void quicksortDiv(Item a[],int l,int r)
{
if(r <= l)
return;
int i,j,p,q;
Item v = a[r];
i = l;
j = r;
p = l;
q = r-1;
for(;;)
{
while(less(a[i],v)) i++;
while(less(v,a[--j]))
if(j == l)
break;
if(j <= i)
break;
exch(a[i],a[j]);
if(eq(a[i],v))
{
exch(a[p],a[i]); //需要对数组下标进行自加或自减时,最好与数组元素的引用分开
p++; //就如左边两句:先交换,然后再自加得到下一元素的下标!否则,有可能交换到相邻位置
}
if(eq(a[j],v))
{
exch(a[q],a[j]);
q--;
}
}
exch(a[i],a[r]); //这里a[r]不能用v代替,否则a[i]处存的是v,但是a[r]处没变
j = i-1;
i = i+1;
while(p > l)
{
p--;
exch(a[p],a[j]);
j--;
}
while(q < r-1)
{
q++;
exch(a[i],a[q]);
i++;
}
quicksortDiv(a,l,j);
quicksortDiv(a,i,r);
}
//利用快排里的划分函数来寻找序列中第k个最小元素(非递归的形式、递归的调试未成功,成功后再上传)
int selectsortPar2(Item a[],int l,int r,int k)
{
int i;
while(r > l)
{
i = qpartition(a,l,r);
if(i < k)
l = i+1;
if(i > k)
r = i-1;
if(i == k)
break;
}
return a[i];
}
//归并排序:会用到大量的额外空间,如果两个待归并序列并不有序,可以先排序,再将其进行归并
void mergesort(Item c[],Item a[],int N,Item b[],int M)
{
int i,j,k;
for(i = 0,j = 0,k = 0;k < N+M;k++)
{
if(i == N)
{
c[k] = b[j];
j++;
continue;
}
if(j == M)
{
c[k] = a[i];
i++;
continue;
}
if(a[i] < b[j])
{
c[k] = a[i];
i++;
}
else if(a[i] >= b[j])
{
c[k] = b[j];
j++;
}
}
}