排序算法代码总结
前言
以下排序方法都是从1至n的排序
int main()
{
int n;
int a[10010];
//输入
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
}
//直接插入排序
InsertSort(a, n);
//折半插入排序
//InsertSort2(a, n);
//冒泡排序
//BubbleSort(a, n);
//快速排序
// QuickSort(a, 1, n);
//简单选择排序
//SelectSort(a, n);
//堆排序
//HeapSort(a, n);
//归并排序
//MergeSort(a, 1, n);
//输出
for(int i=1;i<=n;i++)
{
printf("%d ", a[i]);
}
return 0;
}
直接插入排序
基本思想:对于有 n 个初始元素的序列,将第 2 至 n 个元素依次插入前面已排好序的子序列中,从而使序列整体有序
void InsertSort(int a[], int n)
{
int i,j;
for(i=2;i<=n;i++)//将a[2]-a[n]依次插入前面已排序序列
{
if(a[i]<a[i-1])//a[i]值比前驱小,将其插入有序表中
{
a[0]=a[i]; //a[0]不存储值,只起辅助交换作用
for(j=i-1;a[0]<a[j];j--)//从后往前查找待插入的位置
a[j+1]=a[j];
a[j+1]=a[0];//插入找到的位置
}
}
}
折半插入排序
折半插入排序是在直接插入排序算法上的改进,由于是顺序存储的线性表,所以查找有序子表时可以用折半查找来实现。确定待插入位置后,就可统一地向后移动元素
void InsertSort2(int a[], int n)
{
int i,j,low,mid,high;
for(i=2;i<=n;i++)//将a[2]-a[n]依次插入前面已排序序列
{
a[0]=a[i]; //用a[0]暂存a[i]的值
low=1;high=i-1; //设置折半查找的范围
while(low<=high)
{
mid = (low+high)/2;
if(a[mid]>a[0])//查找左半子表
high=mid-1;
else //查找右半子表
low=mid+1;
}
for(j=i-1;j>=high+1;j--) //统一后移元素
{
a[j+1]=a[j];
}
a[high+1]=a[0]; //将值插入对应位置
}
}
冒泡排序
基本思想:从后往前两两比较相邻元素的值,若为逆序则交换它们,直到序列比较完。
void BubbleSort(int a[], int n)
{
bool flag;//一趟冒泡是否发生交换的标志
int tmp; //用于辅助交换的元素
for(int i=1;i<n;i++)//n-1趟冒泡
{
flag=false;
for(int j=n;j>i;j--)//一趟冒泡过程
{
if(a[j-1]>a[j])//逆序则交换
{
tmp=a[j-1];
a[j-1]=a[j];
a[j]=tmp;
flag=true;
}
}
if(flag==false)//一趟冒泡没有交换,则序列已经有序
return;
}
}
快速排序
基本思想:选取一个记录(一般选取第一个记录)作为枢纽(哨兵),然后将所有关键字比它小的记录都放在它的位置之前,将所有关键字比它大的记录都放在它的位置之后。由此,以该 “哨兵” 记录最后所落的位置 i 为分界线,将整个序列分割成两个子序列,对子序列继续重复上述操作。
int Partition(int a[], int low, int high)
{
int tmp = a[low]; //通常将第一个元素当成哨兵
while(low < high)
{
while(low<high && a[high]>=tmp)//比哨兵小的元素移动到左边
high--;
a[low] = a[high];
while(low<high && a[low]<=tmp)//比哨兵大的元素移动到右边
low++;
a[high] = a[low];
}
a[low] = tmp; //哨兵放到最终位置
return low;
}
void QuickSort(int a[], int l, int r)
{
if(l<r)
{
int p = Partition(a, l, r); //寻找哨兵
QuickSort(a, l, p-1); //分割为两个子序列,再分别排序
QuickSort(a, p+1, r);
}
}
简单选择排序
基本思想:第 i
趟在后面 n-i+1
个待排序元素中选取关键字最小的元素,作为有序子序列的第 i 个元素,直到 n-1 趟做完,待排序元素只剩下一个,就不用再选了。
void SelectSort(int a[], int n)
{
int min=0,tmp=0;//记录位置的元素,辅助元素
for(int i=1;i<n;i++)
{
min=i;//记录最小元素的位置
for(int j=i+1;j<=n;j++)//选出最小元素
{
if(a[j]<a[min])
{
min=j; //更新最小元素位置
}
}
if(min!=i) //找到最小元素则交换
{
tmp=a[i];
a[i]=a[min];
a[min]-a[i];
}
}
}
堆排序
堆是具有下列性质的完全二叉树:每个结点的值都小于或等于其左右孩子结点的值(称为小根堆),或每个结点的值都大于或等于其左右孩子结点的值(称为大根堆)。堆采用一维数组存储。
堆排序的基本思想:首先将待排序的记录序列构造成一个堆,此时,选出了堆中所有记录的最小者,然后将它从堆中移走,并将剩余的记录再调整成堆,这样又找出了次小的记录,以此类推,直到堆中只有一个记录。
//调整
void HeapAdjust(int a[], int k, int n)
{
int top = a[k]; //暂存子树的根结点
for(int i=2*k;i<=n;i*=2)//沿k较大的子结点向下筛选
{
if(i<n && a[i]>a[i+1])//取k较小的子结点的下标
i++;
if(top<=a[i]) //筛选结束
break;
else
{
a[k]=a[i]; //将a[i]调整到双亲结点上
k=i; //修改k值,继续筛选
}
}
a[k] = top; //将调整前的堆顶记录插入到k位置
}
//建小根堆
void HeapSort(int a[], int n)
{
for(int i=n/2; i>0; i--)//初始化小根堆
{
HeapAdjust(a, i, n);
}
int tmp=0;
for(int i=n;i>1;i--)//n-1调整
{
tmp = a[1];
a[1] = a[i];
a[i] = tmp; //将堆顶记录与未排序的最后一个记录交换
HeapAdjust(a, 1, i-1);//重新调整为小根堆
}
}
如果要建立大根堆,只需要把筛选的条件反过来即可
if(i<n && a[i]<a[i+1])//取k较大的子结点的下标
i++;
if(top>=a[i]) //筛选结束
break;
归并排序
基本思想:设初始序列含有 n 个记录,则可看成 n 个有序的子序列,每个子序列长度为 1。
两两合并,得到 ⌊ n / 2 ⌋ \lfloor n/2 \rfloor ⌊n/2⌋个长度为 2 或 1 的有序子序列。
再两两合并,……如此重复,直至得到一个长度为 n 的有序序列为止。
int b[10010]; //辅助数组B
//归并函数
void Merge(int a[], int low, int mid, int high)
{
for(int k=low;k<=high;k++)//将A所有元素复制到B
{
b[k]=a[k];
}
int i,j,k;
for(i=low,j=mid+1,k=i; i<=mid && j<=high; k++)
{
//比较B中左右两段的元素,将较小值复制到A
if(b[i]<=b[j])
a[k]=b[i++];
else
a[k]=b[j++];
}
while(i<=mid) //若第一段还有元素,复制到A中
a[k++]=b[i++];
while(j<=high) //若第二段还有元素,复制到A中
a[k++]=b[j++];
}
//归并排序
void MergeSort(int a[], int low, int high)
{
if(low<high)
{
int mid = (low+high)/2; //找到序列中点
MergeSort(a, low, mid); //左侧序列递归排序
MergeSort(a, mid+1, high); //右侧序列递归排序
Merge(a, low, mid, high); //归并
}
}
归并排序需要一个辅助数组 B,其长度与原数组 A 相同即可。