排序算法

4 篇文章 0 订阅

1、冒泡排序:

冒泡排序就是把小的元素往前调或者把大的元素往后调。比较是相邻的两个元素比较,交换也发生在这两个元素之间。所以,如果两个元素相等,则不需要交换,所以冒泡排序是一种稳定的排序算法。

稳定的排序算法

冒泡排序平均时间复杂度:O(n^2)

c代码:

    int baseData[] = {9, 10, 78, 76, 1, 100, 35, 56, 20, 39, 70, 56, 27};
    int length = sizeof(baseData)/sizeof(int);

    for(int i = 0; i < length - 1; i++){
        for(int j = 0; j < length - i - 1; j++){
            if (baseData[j] > baseData[j + 1]) {
                int temp = baseData[j];
                baseData[j] = baseData[j+1];
                baseData[j+1] = temp;
            }
        }
    }

    for (int m = 0; m < length; m++) {
        printf("%3d\n", baseData[m]);
    }
输出:

  1
  9
 10
 20
 27
 35
 39
 56
 56
 70
 76
 78
100
Program ended with exit code: 0
2、鸡尾酒排序(即双向冒泡排序)

稳定的排序算法

鸡尾酒排序平均时间复杂度:O(n^2)

排序过程如图:


c代码:

    int baseData[] = {9, 10, 78, 76, 1, 100, 35, 56, 20, 39, 70, 56, 27};
    int length = sizeof(baseData)/sizeof(int);
    for(int i = 0; i < length/2; i++){
        for(int j = i; j < length - i - 1; j++){
            if (baseData[j] < baseData[j + 1]) {
                int temp = baseData[j];
                baseData[j] = baseData[j+1];
                baseData[j+1] = temp;
            }
        }
        for(int j = length - i - 1 - 1; j > i; j--){
            if (baseData[j] > baseData[j-1]) {
                int temp = baseData[j];
                baseData[j] = baseData[j-1];
                baseData[j-1] = temp;
            }
        }
    }
        
    for (int m = 0; m < length; m++) {
        printf("%3d\n", baseData[m]);
    }
输出:

100
 78
 76
 70
 56
 56
 39
 35
 27
 20
 10
  9
  1
Program ended with exit code: 0

3、直接插入排序

直接插入排序:把待排序的纪录按其关键码值的大小逐个插入到一个已经排好序的有序序列中,直到所有的纪录插入完为止,得到一个新的有序序列,实际操作是将数组分为两部分,一部分是排好序的部分,另一部分是待排序的部分,将待排序的部分逐个插入排好序的部分,插入时数组元素需要移动

稳定的排序算法

直接插入排序的时间复杂度:O(n^2)

c代码:

    int baseData[] = {9, 10, 78, 76, 1, 100, 35, 56, 20, 39, 70, 56, 27};
    int length = sizeof(baseData)/sizeof(int);    
    //从小到大排列
    for (int i = 1; i < length; i++) {
        int sign = 0;
        int temp = baseData[i];
        for (int j = 0; j < i; j++) {
            if (temp < baseData[j]) {
                sign = j;
                break;
            }else{
                sign = i;
            }
        }
        
        for (int j = i; j > sign; j--) {
            baseData[j] = baseData[j-1];
        }
        baseData[sign] = temp;
    } 
    for (int m = 0; m < length; m++) {
        printf("%3d\n", baseData[m]);
    }

输出:

  1
  9
 10
 20
 27
 35
 39
 56
 56
 70
 76
 78
100
Program ended with exit code: 0
4、希尔排序
希尔排序(Shell Sort)是插入排序的一种。也称缩小增量排序,是直接插入排序算法的一种更高效的改进版本。希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个文件恰被分成一组,算法便终止。

不稳定的排序算法

c代码:

    //希尔排序
    int increment = length;
    while(1)
    {
        increment = increment/2;
        for(int x=0; x < increment; x++)
        {
            for(int i = x + increment; i < length; i = i + increment)
            {
                int temp=baseData[i];
                int j;
                for(j = i-increment; j >= 0 && baseData[j] > temp; j = j - increment)
                {
                    baseData[j+increment] = baseData[j];
                }
                baseData[j+increment] = temp;
            }
        }
        if(increment == 1)
        {
            break;
        }
    }
    
    
    for (int m = 0; m < length; m++) {
        printf("%3d\n", baseData[m]);
    }

输出同上

5、快速排序

设要排序的数组是A[0]……A[N-1],首先任意选取一个数据(通常选用数组的第一个数)作为关键数据,然后将所有比它小的数都放到它前面,所有比它大的数都放到它后面,这个过程称为一趟快速排序。值得注意的是,快速排序不是一种稳定的排序算法,也就是说,多个相同的值的相对位置也许会在算法结束时产生变动。

一趟快速排序的算法是:
1)设置两个变量i、j,排序开始的时候:i=0,j=N-1;
2)以第一个数组元素作为关键数据,赋值给 key,即 key=A[0];
3)从j开始向前搜索,即由后开始向前搜索(j--),找到第一个小于 key的值A[j],将A[j]和A[i]互换;
4)从i开始向后搜索,即由前开始向后搜索(i++),找到第一个大于 key的A[i],将A[i]和A[j]互换;
5)重复第3、4步,直到i=j; (3,4步中,没找到符合条件的值,即3中A[j]不小于 key,4中A[i]不大于 key的时候改变j、i的值,使得j=j-1,i=i+1,直至找到为止。找到符合条件的值,进行交换的时候i, j指针位置不变。另外,i==j这一过程一定正好是i+或j-完成的时候,此时令循环结束)。

不稳定的排序算法

排序过程如图:


c代码:

void sort(int *temp, int left, int right)
{
    if(left >= right)/*如果左边索引大于或者等于右边的索引就代表已经整理完成一个组了*/
    {
        return ;
    }
    int i = left;
    int j = right;
    int key = temp[left];
    
    while(i < j)      /*控制在当组内寻找一遍*/
    {
        while(i < j && key <= temp[j])
        /*而寻找结束的条件就是,
         1,找到一个小于或者大于key的数(大于或小于取决于你想升序还是降序)
         2,没有符合条件1的,并且i与j的大小没有反转
         */
        {
            j--;/*向前寻找*/
        }
        
        temp[i] = temp[j];
        /*找到一个这样的数后就把它赋给前面的被拿走的i的值(如果第一次循环且key是temp[left],那么就是给key)*/
        
        while(i < j && key >= temp[i])
        /*这是i在当组内向前寻找,同上,不过注意与key的大小关系停止循环和上面相反,因为排序思想是把数往两边扔,所以左右两边的数大小与key的关系相反*/
        {
            i++;
        }
        
        temp[j] = temp[i];
    }
    
    temp[i] = key;/*当在当组内找完一遍以后就把中间数key回归*/
    sort(temp, left, i - 1);/*最后用同样的方式对分出来的左边的小组进行同上的做法*/
    sort(temp, i + 1, right);/*用同样的方式对分出来的右边的小组进行同上的做法*/
    /*当然最后可能会出现很多分左右,直到每一组的i = j 为止*/
}



//快速排序
    sort(baseData, 0, length - 1);
    
    for (int m = 0; m < length; m++) {
        printf("%3d\n", baseData[m]);
    }
输出同上

6、简单选择排序

每次找到剩余部分最小值,与第i个进行交换

不稳定排序算法

c代码:

    //简单选择排序
    int m = 0;
    for(int i = 0; i < length - 1; i++){
        m = i;
        for (int j = i+1; j < length; j++) {
            if (baseData[m] > baseData[j]) {
                m = j;
            }
        }
        if (m != i) {
            int temp = baseData[m];
            baseData[m] = baseData[i];
            baseData[i] = temp;
        }
    }
    
    
    for (int m = 0; m < length; m++) {
        printf("%3d\n", baseData[m]);
    }

输出同上

7、堆排序


堆排序利用了大根堆(或小根堆)堆顶记录的关键字最大(或最小)这一特征,使得在当前无序区中选取最大(或最小)关键字的记录变得简单。
(1)用大根堆排序的基本思想
① 先将初始文件R[1..n]建成一个大根堆,此堆为初始的无序区
② 再将关键字最大的记录R[1](即堆顶)和无序区的最后一个记录R[n]交换,由此得到新的无序区R[1..n-1]和有序区R[n],且满足R[1..n-1].keys≤R[n].key
③由于交换后新的根R[1]可能违反堆性质,故应将当前无序区R[1..n-1]调整为堆。然后再次将R[1..n-1]中关键字最大的记录R[1]和该区间的最后一个记录R[n-1]交换,由此得到新的无序区R[1..n-2]和有序区R[n-1..n],且仍满足关系R[1..n-2].keys≤R[n-1..n].keys,同样要将R[1..n-2]调整为堆。
……
直到无序区只有一个元素为止。
(2)大根堆排序算法的基本操作:
①建堆,建堆是不断调整堆的过程,从len/2处开始调整,一直到第一个节点,此处len是堆中元素的个数。建堆的过程是线性的过程,从len/2到0处一直调用调整堆的过程,相当于o(h1)+o(h2)…+o(hlen/2) 其中h表示节点的深度,len/2表示节点的个数,这是一个求和的过程,结果是线性的O(n)。
②调整堆:调整堆在构建堆的过程中会用到,而且在堆排序过程中也会用到。利用的思想是比较节点i和它的孩子节点left(i),right(i),选出三者最大(或者最小)者,如果最大(小)值不是节点i而是它的一个孩子节点,那边交互节点i和该节点,然后再调用调整堆过程,这是一个递归的过程。调整堆的过程时间复杂度与堆的深度有关系,是lgn的操作,因为是沿着深度方向进行调整的。
③堆排序:堆排序是利用上面的两个过程来进行的。首先是根据元素构建堆。然后将堆的根节点取出(一般是与最后一个节点进行交换),将前面len-1个节点继续进行堆调整的过程,然后再将根节点取出,这样一直到所有节点都取出。堆排序过程的时间复杂度是O(nlgn)。因为建堆的时间复杂度是O(n)(调用一次);调整堆的时间复杂度是lgn,调用了n-1次,所以堆排序的时间复杂度是O(nlgn)

注意
①只需做n-1趟排序,选出较大的n-1个关键字即可以使得文件递增有序。
②用小根堆排序与利用大根堆类似,只不过其排序结果是递减有序的。堆排序和直接选择排序相反:在任何时刻堆排序中无序区总是在有序区之前,且有序区是在原向量的尾部由后往前逐步扩大至整个向量为止

特点
堆排序(HeapSort)是一树形选择排序。堆排序的特点是:在排序过程中,将R[l..n]看成是一棵完全二叉树的顺序存储结构,利用完全二叉树中双亲结点和孩子结点之间的内在关系(参见二叉树的顺序存储结构),在当前无序区中选择关键字最大(或最小)的记录

(参考:http://baike.baidu.com/link?url=3-gL9GHfLbboDeMu6uX3reuQ90IaAa-zpbatNmSDLOeQkdg771U5cu2JlM3GwWsPOAJFbbiwINV6xgSCWwwxNK)

c代码

//array是待调整的堆数组,i是待调整的数组元素的位置,nlength是数组的长度
//本函数功能是:根据数组array构建大根堆
void HeapAdjust(int array[],int i,int nLength)
{
    int nChild;
    int nTemp;
    for(;2*i+1<nLength;i=nChild)
    {
        //子结点的位置=2*(父结点位置)+1
        nChild=2*i+1;
        //得到子结点中较大的结点
        if(nChild<nLength-1&&array[nChild+1]>array[nChild])++nChild;
        //如果较大的子结点大于父结点那么把较大的子结点往上移动,替换它的父结点
        if(array[i]<array[nChild])
        {
            nTemp=array[i];
            array[i]=array[nChild];
            array[nChild]=nTemp;
        }
        else break; //否则退出循环
    }
}
//堆排序算法
void HeapSort(int array[],int length)
{
    int i;
    //调整序列的前半部分元素,调整完之后第一个元素是序列的最大的元素
    //length/2-1是最后一个非叶节点,此处"/"为整除
    for(i=length/2-1;i>=0;--i)
        HeapAdjust(array,i,length);
    //从最后一个元素开始对序列进行调整,不断的缩小调整的范围直到第一个元素
    for(i=length-1;i>0;--i)
    {
        //把第一个元素和当前的最后一个元素交换,
        //保证当前的最后一个位置的元素都是在现在的这个序列之中最大的
        array[i]=array[0]^array[i];
        array[0]=array[0]^array[i];
        array[i]=array[0]^array[i];
        //不断缩小调整heap的范围,每一次调整完毕保证第一个元素是当前序列的最大值
        HeapAdjust(array,0,i);
    }
}


 
     //堆排序
    HeapSort(baseData, length);
    
    
    for (int m = 0; m < length; m++) {
        printf("%3d\n", baseData[m]);
    }
输出同上


  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值