【数据结构-C语言】内部排序算法

这是一些大佬整理的:

C语言八大排序算法,一文带你弄清所有
判断各种排序算法的稳定性
常用(内部)排序算法
八大排序算法
因为我对选择排序的不稳定性有点不理解,所以我找了一篇相关的博客,希望能帮助到有相同困惑的朋友
理解选择排序的不稳定性

下面是我自己整理的:

(如有错误请指正,谢谢)

数据类型

#define MAXSIZE 100 /*参加排序元素的最大个数*/
typedef int KeyType;

typedef struct{
     
    KeyType key; //按关键字排序
    int judge;  //判断排序的稳定性
}RedType;

判断算法的稳定性

检测某排序算法的稳定性,只需将原序列每个元素的判断关键字设置为0到n-1,待排序完成,比较排序关键字相同的元素的判断关键字的大小即可,索引小的元素,判断关键字也小,为稳定的,反之不稳定。
但是如果一个序列中没有相同元素,不能判断出排序算法的稳定性,但是如果没有相同元素,也就没有判断稳定不稳定的必要了。(为自己的菜找借口,哈哈哈哈哈)

bool Judge_Stability(RedType nums[],int n){
     
    int i,j;
    for(i=0;i<n;i++){
     
        for(j=i+1;j<n;j++){
     
            if(nums[i].key==nums[j].key){
     
                if(nums[i].judge>nums[j].judge){
     
                    return 0;
                }
            }
        }
    }
    return 1;
}

冒泡排序

设置一个标志,当一趟下来没有数组元素交换时,则表示已有序,退出循环,减少不必要的操作。

void Bubble_Sort(){
     
    int n, i, p;
    int com_num=0, move_num=0, num=0;
    int flag;
    RedType nums[MAXSIZE];
    printf("您已进入冒泡排序\n");
    printf("请输入排序序列长度:");
    scanf("%d",&n);

    printf("请输入排序序列:");
    for(i=0;i<n;i++){
     
        scanf("%d",&nums[i].key);
        nums[i].judge=i;
    }

    printf("\n");
    for(p=n-1;p>=0;p--){
     
        flag=0;
        //每趟寻找一个最大值
        for(i=0;i<p;i++){
     
            com_num++;
            if(nums[i].key>nums[i+1].key){
     
                RedType temp=nums[i];
                nums[i]=nums[i+1];
                nums[i+1]=temp;
                move_num++;
                flag=1;
            }
        }

        printf("第%d趟排序结果:",++num);
        for(i=0;i<n;i++)
            printf("%d ",nums[i].key);
        printf("\n");

        //如果某次比较无元素移动,则序列已经有序,退出循环
        if(flag==0) break;
    }

    printf("排序过程比较次数:%d\n",com_num);
    printf("排序过程移动次数:%d\n",move_num);

    //判断排序算法的稳定性
    bool f=Judge_Stability(nums,n);
    if(f) printf("该序列采取冒泡排序后的结果是稳定的\n");
    else printf("该序列采取冒泡排序后的结果是不稳定的\n");
    printf("\n");

}

直接插入排序

void Insertion_Sort(){
     
    RedType nums[MAXSIZE];
    int i, p, n;
    RedType temp;
    int com_num=0, move_num=0;
    int num=0;

    printf("您已进入直接插入排序\n");
    printf("请输入排序序列长度:");
    scanf("%d",&n);

    printf("请输入排序序列:");
    for(i=0 ; i<n ; i++){
     
        scanf("%d",&nums[i].key);
        nums[i].judge=i;
    }

    printf("\n");
    for(p=1;p<n;p++){
     
        temp=nums[p];
        com_num++;
        //找到插入的位置
        for(i=p;i>0&&nums[i-1].key>temp.key;i--){
     
            nums[i]=nums[i-1];
            move_num++;
            com_num++;
        }
        nums[i]=temp;

        printf("第%d趟排序结果:",++num);
        for(i=0;i<n;i++){
     
            printf("%d ", nums[i].key);
        }
        printf("\n");
    }
    printf("排序过程比较次数:%d\n",com_num);
    printf("排序过程移动次数:%d\n",move_num);

    //判断排序算法的稳定性
    bool f=Judge_Stability(nums,n);
    if(f) printf("该序列采取直接插入排序后的结果是稳定的\n");
    else printf("该序列采取直接插入排序后的结果是不稳定的\n");
    printf("\n");
}

简单选择排序

定义一个临时变量,把每一趟中比较的较小值赋值到这个临时变量,在这趟结束之后,将临时变量的值赋值给此趟所选择的数,减少交换次数,提高效率

void Select_Sort(){
     
    RedType nums[MAXSIZE];
    int i, j, n, t;
    RedType temp;
    int com_num=0, move_num=0;
    int num=0;
    int flag;

    printf("您已进入简单选择排序\n");
    printf("请输入排序序列长度:");
    scanf("%d",&n);

    printf("请输入排序序列:");
    for(i=0 ; i<n ; i++){
     
        scanf("%d",&nums[i].key);
        nums[i].judge=i;
    }

    printf("\n");
    for(i=0;i<n;i++){
     
        temp = nums[i];
        t = i;
        flag=0;
        for(j=i+1;j<n;j++){
     
            com_num++;
            if(temp.key>nums[j].key){
     
                temp=nums[j];
                t=j;
                flag=1;
            }
        }
        //如果存在比nums[i]小的值,才进行交换
        if(flag==1){
     
            temp=nums[i];
            nums[i]=nums[t];
            nums[t]=temp;
            move_num++;
        }

        printf("第%d趟排序结果:",++num);
        for(j=0 ; j<n ; j++){
     
            printf("%d ", nums[j].key);
        }
        printf("\n");
    }
    printf("排序过程比较次数:%d\n",com_num);
    printf("排序过程移动次数:%d\n",move_num);

    //判断排序算法的稳定性
    bool f=Judge_Stability(nums,n);
    if(f) printf("该序列采取简单选择排序后的结果是稳定的\n");
    else printf("该序列采取简单选择排序后的结果是不稳定的\n");
    printf("\n");
}

折半插入排序

利用折半查找的思想,找到一个元素应该在有序数组插入的位置

void Bisection_Sort(){
     
    RedType nums[MAXSIZE];
	int i, j, n;
	RedType temp;
	int low, high, middle;
	int com_num=0, move_num=0;
	int num=0;

	printf("您已进入折半插入排序\n");
	printf("请输入排序序列长度:");
    scanf("%d",&n);

    printf("请输入排序序列:");
	for(i=0;i<n;i++){
     
        scanf("%d",&nums[i].key);
        nums[i].judge=i;
    }

    printf("\n");
    for(i=1;i<n;i++){
     
        low = 0; //有序数组的开头下标
		high = i-1; //有序数组的末尾下标
		temp = nums[i]; //要被插入的数
		middle = 0;
		//查找要被插入的下标
		while(low <= high){
     
			middle = (low + high) / 2;
			if(temp.key < nums[middle].key) high = middle-1;
			else low = middle+1;
			com_num++;
		}
		//被插入的下标的数及右边所有数右移,移完之后插入目标数
		for(j=i;j>low;j--){
     
            nums[j]=nums[j-1];
            move_num++;
		}

		//要被插入的下标与i不等时,将nums[i]的数据写入nums[low]的位置
		if(low!=i) {
     
            move_num++;
            nums[low] = temp;
		}

		printf("第%d趟排序结果:",++num);
		for(j=0 ; j<n ; j++){
     
            printf("%d ",nums[j].key);
        }
        printf("\n");
    }
    printf("排序过程比较次数:%d\n",com_num);
    printf("排序过程移动次数:%d\n",move_num);

    //判断排序算法的稳定性
    bool f=Judge_Stability(nums,n);
    if(f) printf("该序列采取折半插入排序后的结果是稳定的\n");
    else printf("该序列采取折半插入排序后的结果是不稳定的\n");
    printf("\n");
}

希尔排序

void Shell_Sort(){
     
    RedType nums[MAXSIZE];
    int p, i, j, n, dk;
    RedType temp;
    int com_num=0, move_num=0;
    int num=0;

    printf("您已进入希尔排序\n");
    printf("请输入排序序列长度:");
    scanf("%d",&n);

    printf("请输入排序序列:");
    for(i=0 ; i<n ; i++){
     
        scanf("%d",&nums[i].key);
        nums[i].judge=i;
    }

    printf("\n");
    for(dk=n/2;dk>0;dk=dk/2){
     

        for(j=0;j<dk;j++){
     
            //单独的一次插入排序
            for(p=j+dk;p<n;p=p+dk){
     
                temp=nums[p];
                com_num++;
                for(i=p;i>=dk&&nums[i-dk].key>temp.key;i=i-dk){
     
                    nums[i]=nums[i-dk];
                    move_num++;
                    com_num++;
                }
                nums[i]=temp;
            }
        }

        printf("第%d趟排序结果:",++num);
        for(i=0;i<n;i++){
     
            printf("%d ", nums[i].key);
        }
        printf("\n");
    }
    printf("排序过程比较次数:%d\n",com_num);
    printf("排序过程移动次数:%d\n",move_num);

    //判断排序算法的稳定性
    bool f=Judge_Stability(nums,n);
    if(f) printf("该序列采取希尔排序后的结果是稳定的\n");
    else printf("该序列采取希尔排序后的结果是不稳定的\n");
    printf("\n");
}

快速排序

快速排序算法找基值是非常重要的,基值找的好,算法效率就高

数组第一个元素作基值

该算法简单,但是如果数组已经有序或基本有序,算法效率较低

/***********快速排序***********/

/**下面三个全局变量,
    用于快速排序统计比较次数、交换次数,
    及第几次排序的计数*/
int CN,MN,T;

//交换数组中的两个元素
void Q_swap(RedType nums[], int i, int j){
     
    RedType temp;
    temp=nums[i];
    nums[i]=nums[j];
    nums[j]=temp;
    MN++;
}


//快速排序
void Q_Sort(RedType nums[], int left, int right, int n){
     
    if(left>=right) return;
    RedType pivot=nums[left];
    int low=left;
    int high=right;
    //以pivot为轴,将大于它的放在后边,小于它的放在前面
    while(low<high){
     
        while(low<high&&nums[high].key>=pivot.key) {
     
            CN++;
            high--;
        }
        Q_swap(nums,low,high);
        while(low<high&&nums[low].key<=pivot.key) {
     
            CN++;
            low++;
        }
        Q_swap(nums,low,high);

    }

    printf("第%d趟排序结果:",++T);
    for(int i=0 ; i<n ; i++){
     
        printf("%d ",nums[i].key);
    }
    printf("\n");
    //对低子表递归排序
    Q_Sort(nums,left,low-1,n);
    //对高子表递归排序
    Q_Sort(nums,low+1,right,n);

}

//快速排序的入口,用于主函数调用
void Quick_Sort(){
     
    int i, n;
	RedType nums[MAXSIZE];
	CN = 0;
    MN = 0;
    T = 0;

	printf("您已进入快速排序\n");
    printf("请输入排序序列长度:");
    scanf("%d",&n);
    printf("请输入排序序列:");
	for (i=0; i<n; i++){
     
		scanf("%d",&nums[i].key);
		nums[i].judge=i;
	}

	printf("\n");
	Q_Sort(nums, 0, n-1, n);

	printf("排序过程比较次数:%d\n",CN);
    printf("排序过程移动次数:%d\n",MN);

    //判断排序算法的稳定性
    bool f=Judge_Stability(nums,n);
    if(f) printf("该序列采取快速排序后的结果是稳定的\n");
    else printf("该序列采取快速排序后的结果是不稳定的\n");
    printf("\n");
}

中位数作基值

下面的程序寻找基值的方法为:选第一个元素、中间元素和最后一个元素的中位数作基值,可改善上述情况。并且设置一个值(比如100),当数组的长度小于这个值的时候,采用直接插入排序(因为当数组规模较小时,快速排序的性能不如直接插入排序)。
因为当数组规模小时,采用直接插入排序,所以稳定性是不确定的。

/**下面三个全局变量,
    用于快速排序统计比较次数、交换次数,
    及第几次排序的计数*/
int CN,MN,T;

//直接插入排序
void Insertion_Sort(RedType nums[],int n){
     
    int i, p;
    RedType temp;
    for(p=1;p<n;p++){
     
        temp=nums[p];
        CN++;
        for(i=p;i>0&&nums[i-1].key>temp.key;i--){
     
            nums[i]=nums[i-1];
            CN++;
            MN++;
        }
        nums[i]=temp;

        printf("第%d趟排序结果:",++T);
        for(i=0 ; i<n ; i++){
     
            printf("%d ",nums[i].key);
        }
        printf("\n");
    }
}

//交换数组中的两个元素
void Q_swap(RedType nums[], int i, int j){
     
    RedType temp;
    temp=nums[i];
    nums[i]=nums[j];
    nums[j]=temp;
}

//取第一个元素、中间元素、最后一个元素的中位数作为主元,即基准Pivot
RedType median3(RedType nums[], int low, int high){
     
    int center=(low+high)/2;
    if(nums[low].key>nums[center].key) Q_swap(nums,low,center);
    if(nums[low].key>nums[high].key) Q_swap(nums,low,high);
    if(nums[center].key>nums[high].key) Q_swap(nums,center,high);
    Q_swap(nums,center,high-1); //将基准pivot藏到右边
    return nums[high-1];
}

//快速排序
void Q_Sort(RedType nums[], int left, int right){
     
    if(right-left<100) {
     
        Insertion_Sort(nums+left,right-left+1);
    }
    //如果序列元素充分多,进入快排
    else{
     
        int low,high;
        RedType pivot;
        pivot=median3(nums,left,right);
        low=left; high=right-1;
        //将序列中比基准小的移到基准左边,大的移到右边
        while(1){
     
            while(nums[++low].key<pivot.key);
            while(nums[--high].key>pivot.key);
            if(low<high) Q_swap(nums,low,high);
            else break;
        }
        Q_swap(nums,low,right-1); //将基准换到正确的位置

        printf("第%d趟排序结果:",++T);
        for(int i=0 ; i<right-left+1 ; i++){
     
            printf("%d ",nums[i].key);
        }
        printf("\n");
		
		//对低子表递归排序
        Q_Sort(nums,left,low-1); 
        //对高子表递归排序
        Q_Sort(nums,low+1,right);
    }

}

//快速排序的入口,用于主函数调用
void Quick_Sort(){
     
    int i, n;
	RedType nums[MAXSIZE];
	CN = 0;
    MN = 0;
    T = 0;

	printf("您已进入快速排序\n");
    printf("请输入排序序列长度:");
    scanf("%d",&n);
    printf("请输入排序序列:");
	for (i=0; i<n; i++){
     
		scanf("%d",&nums[i].key);
		nums[i].judge=i;
	}

	printf("\n");
//	Q_Sort(nums, 0, n-1);
    Insertion_Sort(nums,n);
	printf("排序过程比较次数:%d\n",CN);
    printf("排序过程移动次数:%d\n",MN);

    //判断排序算法的稳定性
    bool f=Judge_Stability(nums,n);
    if(f) printf("该序列采取快速排序后的结果是稳定的\n");
    else printf("该序列采取快速排序后的结果是不稳定的\n");
    printf("\n");
}

堆排序

/************堆排序************/

//下滤:将堆中以某节点为根的子堆调整为最大堆
void PercDown(RedType nums[], int p, int n){
     
    int parent,child;
    RedType temp;
    //将n个元素的数组中以nums[p]为根的子堆调整为最大堆
    for(parent=p;parent*2+1<n;parent=child){
     
        child=parent*2+1;
        if(child<n-1&&nums[child].key<nums[child+1].key){
     
            //Child指向左右子结点的较大者
            child=child+1;
            HCN++;
        }
        if(nums[parent].key<nums[child].key){
     
            temp=nums[parent];
            nums[parent]=nums[child];
            nums[child]=temp;
            HCN++;
			HMN++;
        }
        else break;
    }
}

//堆排序
void H_Sort(RedType nums[], int n){
     
    int i,j;
    RedType temp;
    //建立一个大根堆
    for(i=n/2-1;i>=0;i--){
     
        PercDown(nums,i,n);

        printf("第%d趟排序结果:",++H);
		for(j=0 ; j<n ; j++){
     
            printf("%d ",nums[j].key);
        }
        printf("\n");
    }
    //排序
    for(i=n-1;i>0;i--){
     
        //删除最大堆顶
        temp = nums[i];
        nums[i] = nums[0];
        nums[0] = temp;
        HMN++;
        //调整大根堆
        PercDown(nums,0,i);

        printf("第%d趟排序结果:",++H);
		for(j=0 ; j<n ; j++){
     
            printf("%d ",nums[j].key);
        }
        printf("\n");
    }
}

//堆排序的入口,用于主函数调用
void Heap_Sort(){
     
    int i , n;
    RedType nums[MAXSIZE];
    HCN = 0;
    HMN = 0;
    H = 0;

    printf("您已进入堆排序\n");
    printf("请输入排序序列长度:");
    scanf("%d",&n);

    printf("请输入排序序列:");
	for (i=0; i<n; i++){
     
		scanf("%d",&nums[i].key);
		nums[i].judge=i;
	}

    printf("\n");
	H_Sort(nums, n);

	printf("排序过程比较次数:%d\n",HCN);
    printf("排序过程移动次数:%d\n",HMN);

    //判断排序算法的稳定性
    bool f=Judge_Stability(nums,n);
    if(f) printf("该序列采取堆排序后的结果是稳定的\n");
    else printf("该序列采取堆排序后的结果是不稳定的\n");
    printf("\n");
}

//判断排序算法的稳定性
bool Judge_Stability(RedType nums[],int n){
     
    int i,j;
    for(i=0;i<n;i++){
     
        for(j=i+1;j<n;j++){
     
            if(nums[i].key==nums[j].key){
     
                if(nums[i].judge>nums[j].judge){
     
                    return 0;
                }
            }
        }
    }
    return 1;
}

小总结

当数组里面有重复元素时,冒泡排序、折半排序、直接插入排序是稳定的,希尔排序、简单选择排序、快速排序、堆排序是不稳定的。
当数组规模大小、有序性强弱不同时,每个排序算法的性能是不一样的。当规模较大时,快速排序和堆排序性能较好;当数组元素基本有序时,冒泡排序、希尔排序、插入排序性能较好;当数组规模很大,且只取前面几个元素时,堆排序是最好的选择。目前的内部排序算法中,综合考虑下,快速排序是性能最好的排序算法。

你可能感兴趣的:(数据结构,数据结构)