《算法导论》中并没有讨论这么多排序算法。在此罗列出来,仅仅是为了今后便于查看。基数排序、桶排序后续补充。。。。。
冒泡排序有很多种实现方式。下面总结常见的几种,并对冒泡排序进行改进。
冒泡排序1
//冒泡升序排序1,强烈推荐,好记
void BubbleSort(int arr[],int length) {
int tmp;
int i,j;
for(i=length-1; i>0; i--) {
for(j=0; jif(arr[j] > arr[j+1]) {
tmp=arr[j];
arr[j]=arr[j+1];
arr[j+1]=tmp;
}
}
}
}
冒泡升序排序2
void BubbleSort(int arr[],int length) { //冒泡排序2
int i,j;
int tmp;
for(i=0; i<length-1; i++) {
for(j=i+1; j<length; j++)
if(arr[i] > arr[j]) {
tmp=arr[i];
arr[i]=arr[j];
arr[j]=tmp;
}
}
}
冒泡升序排序3
void BubbleSort(int arr[],int length) { //冒泡排序3
int i,j;
int tmp;
for(i=0; i<length-1; i++) {
for(j=length-2; j>=i; j--) //从右向左冒泡,较小的数向前端冒泡
if(arr[j] > arr[j+1]) {
tmp=arr[j];
arr[j]=arr[j+1];
arr[j+1]=tmp;
}
}
}
冒泡升序排序4(对冒泡1进行优化,冒泡2、3同理可以优化)
void BubbleSort(int arr[],int length) { //冒泡排序(优化)4
int i,j;
int tmp;
bool flag=true; //设置一个标记,数组中没有逆序就置flag为false,排序结束;
//有逆序则置为true,做进一步操作。
for(i=length-1;i>0 && flag ; i--){
flag =false;
for(j=0;jif(arr[j] > arr[j+1] ){
tmp=arr[j];
arr[j]=arr[j+1];
arr[j+1]=tmp;
flag=true;//存在逆序,所以置为true
}
}
}
}
选择排序时间复杂度为 O(n2) ,是一个不稳定的算法。
void SelectionSort(int arr[],int length){
int i,j,tmp;
int minPos;
for(i=0;i<length-1;i++){
minPos=i; //将当前下标定义为最小值的下标
for(j=i+1;j<length;j++){
if(arr[minPos] > arr[j])//如果小于当前的最小值
minPos=j; //将此值的下标赋值给minPos
}
if(minPos!=i){ //如果当前i位置上的值不是最小值,则交换
tmp=arr[minPos];
arr[minPos]=arr[i];
arr[i]=tmp;
}
}
}
插入排序时间复杂度为 O(n2) ,是一个稳定的算法。
插入排序C实现1
void InsectionSort(int arr[],int length){ //插入排序1
int i,j;
int tmp;
for(i=1;i<length;i++){ //从1位置开始遍历数组。0位置视为已排序
tmp=arr[i]; //记录待插入的元素,tmp为哨兵
for(j=i-1;j>=0 && arr[j]>tmp ;j--){
arr[j+1]=arr[j]; //待插入元素前面有元素比tmp大,较大的元素向后移动一个位置
}
arr[j+1]=tmp;
}
}
插入排序C实现2
void InsectionSort(int arr[],int length){ //插入排序2(因为要设置哨兵,arr[0]位置不能存元素)
int i,j;
for(i=2;i<length;i++){ //依次将arr[2]--arr[n]插入到前面的已排序序列中
if(arr[i] < arr[i-1]) //arr[i]小于前驱,需要插入已有序的前驱部分中
arr[0]=arr[i]; //设置arr[0]设置为哨兵。节约一个临时变量tmp。下面的移位操作更简单
for(j=i-1;arr[0] < arr[j];j--){
arr[j+1]=arr[j];
}
arr[j+1]=arr[0];
}
}
折半插入排序仅仅减少了比较元素的次数,约为 O(nlog2n) ,且比较次数与待排数组的初始化状态无关。而元素的移动次数没有改变,移动次数依赖于数组的初始化状态。时间复杂度为 O(n2) ,是一个稳定算法。
void BinaryInsectionSort(int arr[],int length) {
int i,j,low,high,mid,tmp;
for(i=1; i<length; i++) { //依次将arr[1]---arr[length-1]插入到前面已排序序列中,arr[0]视为已排序
tmp=arr[i]; //用哨兵tmp记录待插入的元素
low=0;
high=i-1; //设置二分查找的起点、终点
while(low<=high) {
mid=(low+high)/2;
if(arr[mid] > tmp)
high=mid-1;
else
low=mid+1;
}
for(j=i-1; j>=high+1; j--) {//移动元素
arr[j+1]=arr[j];
}
arr[high+1]=tmp;
}
}
归并排序和快速排序都是分治算法的思想,但又明显的不同,对照学习会有更大收获。空间复杂度为 O(n) ,时间复杂度为 O(nlog2n) ,2-路归并排序算法是一个稳定的排序算法。
#include
#include
#define MAX 100
int arr[MAX];
void Merge(int arrA[],int low,int middle,int high) {//Merge函数有多种实现方式,下面贴出来
int i,j,k;
int * arrB=new int[high-low+1]; //动态申请一个辅助数组
i=low;
j=middle+1;
k=0; //k为arrB数组的索引,从0开始。很多书上写k=low,在本方法中不适合;
while(i<=middle && j<=high) {
if(arrA[i]<=arrA[j])
arrB[k++]=arrA[i++];
else
arrB[k++]=arrA[j++];
}
while(i<=middle) {
arrB[k++]=arrA[i++];
}
while(j<=high) {
arrB[k++]=arrA[j++];
}
for(i=low,k=0; i<=high; k++, i++) {
arrA[i]=arrB[k];
}
delete [] arrB;
}
void MergeSort(int arr[],int low,int high) {
int middle;
if(low2; //取中点
MergeSort(arr,low,middle); //对arr[low:middle]进行排序
MergeSort(arr,middle+1,high); //对arr[middle+1:high]进行排序
Merge(arr,low,middle,high); //合并
}
}
void PrintArray(int arr[],int length) { //打印数组,用于查看排序效果
printf("[");
for(int i=0; iif(i==length-1)
printf("%d]\n",arr[i]);
else
printf("%d ,",arr[i]);
}
}
int main() {
int num=0;
printf("请输入数组元素个数:");
scanf("%d",&num);
for(int i=0; i//读入待排序数据
scanf("%d",&arr[i]);
}
printf("排序前数据为:");
PrintArray(arr,num);
MergeSort(arr,0,num-1);
printf("排序后数据为:");
PrintArray(arr,num);
return 0;
}
Merge函数的第二种实现方式
int arrB[MAX]; //把辅助数组arrB定义在全局范围。也省得delete操作了
void Merge(int arrA[],int low,int middle,int high) {
int i,j,k;
for(i=low;i<=high;i++) //将arrA[]中的数据,备份到arrB[]中
arrB[i]=arrA[i];
i=low;
j=middle+1;
k=low;
while(i<=middle && j<=high) {
if(arrB[i]<=arrB[j]) //比较arrB[]的左右两段中的元素
arrA[k++]=arrB[i++]; //较小的元素赋值到arrA[]中
else
arrA[k++]=arrB[j++];
}
while(i<=middle) { //arrB[]前半段未检测完,直接拼接arrA[]后面
arrA[k++]=arrB[i++];
}
while(j<=high) { //arrB[]后半段未检测完,直接拼接arrA[]后面
arrA[k++]=arrB[j++];
}
}
Merge函数的第三种实现方式
//左右两个子数组,分别设置一个哨兵,避免每次比较都必须检查子数组是否为空,从而简化代码。
//仅给出伪代码,详细解释请翻阅《算法导论》17页。
快速排序是一种非常重要的排序,在学习程序设计、数据结构、算法设计与分析等课程的时候,老师三令五申,多次讲到。这里仅仅贴出实现方式。
快速排序算法的详细设计思路请参考另一篇博文:算法导论——-快速排序QuickSort 快速排序算法中,最重要的就是Partition函数。Partition函数有好几个实现方式。请参考另外一篇博文:(后续补上)
//交换数组中两个元素位置
void swap(int &a,int & b) {
int tmp;
tmp=a;
a=b;
b=tmp;
}
int Partition(int * Arr,int low,int high) { //划分方法。有多种实现方式。
//i和j分别指向数组下界和上界,pivot是轴点,本算法默认选取左端为轴点
int i=low,j=high,pivot=Arr[low];
while (i/* j自j位置开始向左扫面,如果j位置所对应的元素的值大于等于pivot,则j前移一个位置(即j--)。
重复该过程,直到找到第一个小于pivot的元素R[j],将R[j]和R[i]进行交换,i++。
其实交换后R[j]所对应的元素就是pivot。*/
while (i=pivot) {
j--;
}
if (i//注意这里是交换元素,另外还有挖坑法实现,是元素覆盖。
}
/* 令i自i位置开始向右扫描,如果i位置所对应的元素的值小于等于pivot,则i后移(即i++)。
重复该过程,直至找到第一个大于pivot的元素R[i],将R[i]与R[j]进行交换,j--。
其实,交换后R[i]所对应的元素就是pivot。*/
while (iif (ireturn i; //i和j相同,即基准元素pivot的最终位置。返回i的值
}
void QuickSort(int * Arr,int low,int high) { //对数组Arr[low high]进行快速排序
int pivotpos; //划分的基本元素所在的位置
if(low//区间长度大于1时才排序
pivotpos=Partition(Arr,low,high); //对Arr[low high]进行划分
QuickSort(Arr,low,pivotpos-1);
QuickSort(Arr,pivotpos+1,high);
}
}
希尔排序是对插入排序的一个改进算法。排序的思路和算法分析,详见另一篇博文。算法导论——ShellSort希尔排序
void shellSort (int[] a, int n)
{
int i, j, k, h, tmp;
int[] cols = {1391376, 463792, 198768, 86961, 33936, 13776, 4592,
1968, 861, 336, 112, 48, 21, 7, 3, 1}
for (k=0; k<16; k++)
{
h=cols[k];
for (i=h; i//i=h,意味着从第二行开始对每列InsertSort
{
tmp=a[i]; //插入排序需要的临时变量。
j=i;
while (j>=h && a[j-h]>tmp)
{
a[j]=a[j-h];
j=j-h;
}
a[j]=tmp;
}
}
}
堆排序是一种基于完全二叉树的树形选择排序 方法。在排序的过程中将待排序列看成是一颗完全二叉树的顺序存储结构,树上的每一个结点对应数组中的一个元素,可以利用完全二叉树中双亲结点和孩子结点之间的内在关系,在当前无序序列中构建“二叉堆”,简称“建堆”操作。从而在二叉堆的根部找到关键字最大(小)的元素。这种叫“堆”的数据结构可以保存每趟排序过程中的中间比较结果。堆按性质可分为大顶堆和小顶堆。如果想让序列按升序(降序)排序,就需要将待排序的n个元素构造成大顶堆(小顶堆)。此时堆顶即为最大值(最小值),将它和堆数组的尾元素交换。然后将剩余的n-1个待排元素重新建堆,从而得到n个元素中的次大元素,将它和堆数组的尾元素交换。反复迭代,最终得到一个有序的序列。关于堆排序更详细的解释,请参考另一篇博文:算法导论——堆排序heapsort
#include
#include
int arrtest[100];
void swap(int *a,int *b) {
int temp=*a;
*a=*b;
*b=temp;
}
void maxHeapify(int arr[],int i,int heapsize) { //维护堆的性质
int left=2*i+1;
int right=2*i+2;
int largest;
if (left<=heapsize && arr[left]>arr[i])
largest=left;
else
largest=i;
if (right<=heapsize && arr[right]>arr[largest])
largest=right;
if(largest!=i) {
swap(&arr[largest],&arr[i]);
maxHeapify(arr,largest,heapsize);
}
}
void buildMaxHeap(int arr[],int heapsize) { //建堆
for(int i=(heapsize-1)/2; i>=0; i--) {
maxHeapify(arr,i,heapsize);
}
}
void heapSort(int arr[],int len) { //堆排序
buildMaxHeap(arr,len-1);
for(int i=len-1; i>0; i--) {
swap(&arr[0],&arr[i]);
buildMaxHeap(arr,i-1);
}
}
void PrintArray(int arr[],int length) { //打印数组,用于查看排序效果
printf("[");
for(int i=0; i<length; i++) {
if(i==length-1)
printf("%d]\n",arr[i]);
else
printf("%d ,",arr[i]);
}
}
int main() {
int num=0;
printf("请输入数组元素个数:");
scanf("%d",&num);
for(int i=0; i//读入待排序数据
scanf("%d",&arrtest[i]);
}
printf("排序前数据为:");
PrintArray(arrtest,num);
heapSort(arrtest,num);
printf("排序后数据为:");
PrintArray(arrtest,num);
return 0;
}