排序

一.插入排序

每一步将一个待排序的元素,按其排序码的大小,插入到前面已经排好序的一组元素的适当位置上去,直到元素全部插入为止;

查找定位->后移->插入;

1.直接插入排序

顺序查找;

先将序列中第一个元素看成有序序列(正序),从第二个记录开始逐个比较插入,使有序序列长度加1;

哨兵 a[0] 复制待排序元素,也可判断防止数组下标越界(a[0]=a[0]时退出 while );

分析:

时间复杂度 O(n^2)    最好情况O(n)

空间复杂度 O(1)

void InsertSort(int a[],int n)
{
    int i,j;
    for(i=2; i<=n; i++) //i为待排序元素;
    {
        a[0]=a[i];  //设置哨兵;
        j=i-1;      //j为有序序列的最后一个位置;
        while(a[0]<a[j])
        {
            a[j+1]=a[j];
            j--;
        }
                       //a[0]大于a[j];
        a[j+1]=a[0];  
    }
}

2.折半插入排序

比相对于直接插入排序元素减少了比较次数;

元素的移动次数没有改变,它依赖于待排序表的初始状态;

分析:时间复杂度 O(n^2)

void BInsertSort(int a[],int n)
{
    int i,low,high,j;
    for(i=2; i<=n; i++)
    {
        a[0]=a[i];
        low=1,high=i-1;
        while(low<=high)
        {
            int m=(low+high)/2;
            if(a[0]<m)
                high=m-1;
            else
                low=m+1;
        }
        for(j=i-1; j>=high+1; j--)  a[j+1]=a[j];
        a[high+1]=a[0];
    }
}

3.2-路插入排序

在折半插入排序的基础上对其进行改进,减少其在排序过程中移动记录的次数从而提高效率;

具体实现思路为:另外设置一个同存储记录的数组大小相同的数组 d,将无序表中第一个记录添加进 d[0] 的位置上,然后从无序表中第二个记录开始,同 d[0] 作比较:如果该值比 d[0] 大,则添加到其右侧;反之添加到其左侧。

在这里的数组 d 可以理解成一个环状数组;

分析:只是减少了移动记录的次数,没有根本上避免,所以其时间复杂度仍为O(n^2)

4.希尔排序

将需要排序的序列划分为若干个较小的序列,对这些序列进行直接插入排序,使序列基本有序,再使用一次直接插入排序;

划分序列:采取将相隔某个增量的数据组成一个序列;

关键字较小的跳跃式的往前移动;

分析:

时间复杂度 O(nlogn)   最坏O(n^2)

void ShellSort(int a[],int n,int d[],int t)
{                                //t个增量;
    int i,j,k,x;
    k=0;
    while(k<t)
    {
        for(i=d[k]+1; i<=n; i++)  //i未排序记录;
        {
            x=a[i];
            j=i-d[k];    //同组i前一个位置;
            while(j>0&&x<a[j])
            {
                a[j+d[k]]=a[j];
                j=j-d[k];
            }
            a[j+d[k]]=x;
        }
        k++;
    }
}

二.交换排序

1.冒泡排序

第一趟冒泡排序:比较两个相邻记录,“逆序”时交换,使关键字最大放到最后一个位置上;

结束条件:一趟排序中没有进行过交换的记录;

分析:

时间复杂度 O(n^2)

最好 正序 一趟排序    n-1次比较      不移动;

最坏 逆序 n-1趟排序  n(n-1)/2次比较;


for (i=0; i<n-1; ++i)
{
    for (j=0; j<n-1-i; ++j)
    {
        if (a[j] > a[j+1])
        {
            t = a[j];
            a[j] = a[j+1];
            a[j+1] = t;
        }
    }
}
void BubbleSort (int a[], int n)
{
    int flag=1;  //有交换;
    int m=n;    //每一趟交换的最后一个位置;
    int i,t;
    while(m>1&&flag==1)
    {
        flag=0; 
        for(i=1; i<m ;i++)
        {
            if(a[i]>a[i+1])
            {
                t=a[i];
                a[i]=a[i+1];
                a[i+1]=t;
                flag=1;
            }
        }
        m--;
    }
}

改进

一趟排序的最后一个位置是上一次最后交换记录位置的坐标 i (之后 i+1 没有交换的都是排好的正序);

void BubbleSort(int a[],int n)
{
     int m=n;
     while(m>1)
    {
        lastchange=1;
        for(i=1; i<m; i++)
        {
            if(a[i]>a[i+1])
            {
                swap(a[i],a[i+1])
                lastchange=i;           //i是进行交换的记录位置;
            }
        }
        m=lastchange;
    }
}

2.快速排序

分治法;挖坑填数;

  • 1.i =low; j = high; 将基准数挖出形成第一个坑a[i];
  • 2.j--由后向前找比它小的数,找到后挖出此数填前一个坑a[i]中;
  • 3.i++由前向后找比它大的数,找到后也挖出此数填到前一个坑a[j]中;
  • 4.再重复执行2,3二步,直到i==j,将基准数填入a[i]中;

分析:

时间复杂度为O(nlogn),空间复杂度为O(logn);

 最坏情况:

每次递归进行后,都是只减少一个元素(基准数);

时间复杂度O(n^2),每次划分只能将序列分为一个元素与其他元素两部分,退化为冒泡排序,空间复杂度为O(n);

void quicksort (int a[],int l, int r)
{
    if(l<r)        //只有一个元素时有序不用排序;
    {
        int i=l,j=r,x=a[l];
        while(i<j)
        {
            while(i<j&&a[j]>=x) j--;
            if(i<j) a[i]=a[j];
            while(i<j&&a[i]<=x) i++;
            if(i<j) a[j]=a[i];
        }
        a[i]=x;    //一趟快速排序;
        quicksort(a,l,i-1);
        quicksort(a,i+1,r);  //递归快排;
    }
}

三.选择排序

1.简单选择排序

未排序序列中找到最小(大)元素,存放到有序序列末尾;

i从1~n-1,每次从剩下的n-i个元素中比较找到最小值;

分析

T( n )=O( n^2 )        不稳定的排序

最好正序 交换 0;最坏逆序 交换n-1 移动次数 3(n-1);

void selectionSort(int a[], int n)
{
    int i,j,min,t;
    for(i=0; i<n-1; i++)
    {
        min=i;       //min是下标;
        for(j=i+1;j<n;j++)
        {
            if(a[j]<a[mim]) min=j;
        }
        if(i!=min) 
        {
            t=a[i];
            a[i]=a[min];
            a[min]=t;
        }
    }
}

 

2.堆排序

每个结点的值都大于或等于其左右孩子结点的值为大顶堆;每个结点的值都小于或等于其左右孩子结点的值为小顶堆;

思想

(1).将待排序的数组构造成一个大顶堆,此时,整个数组的最大值就是堆结构的顶端;

(2).将顶端的数与末尾的数交换,此时,末尾的数为最大值,剩余待排序数组个数为n-1;

(3).将剩余的n-1个数再构造成大顶堆,再将顶端数与n-1位置的数交换,如此反复执行,便能得到有序数组;

步骤:

建堆

我们先从最小的子树开始调整,然后慢慢的往外扩充;

二叉树结点总数为 n,则最后一个非叶子结点是第 ⌊n/2⌋ 个;

从最后一个非叶子结点开始,依次筛选,交换可能会引起孩子结点不满足堆的性质,所以每次交换之后需要重新对被交换的孩子结点进行调整;

顶端与末尾交换

筛选

顶端与末尾交换,拿顶端与其左右孩子较大的数进行比较;

如果顶端的数大于其左右孩子较大的,则停止;

如果顶端的数小于其左右孩子较大的数,则交换,然后继续与下面的孩子进行比较;

void headsort(int a[])
{
    //调整为大顶堆;
    for(int i=n/2-1;i>=0;i--)
    {
        headjust(a,i,n-1);   //筛选;
    }
    for(int i=n;i>0;i--)
    {
        swap(a,0,i);  //顶端与末尾交换;
        headjust(a,0,i-1);  //筛选;
    }
}

void headjust(int a[],int i,int len)  //len为数组下标最大值;
{
    int x=a[i];
    for(int k=2*i; k<=len; k=k*2)
    {
        if(k+1<=len&&a[k]<a[k+1]) k++;
        if(x<a[k])
        {
            a[i]=a[k];
            i=k;
        }
        else break;
    } 
       a[i]=x; 
/*先不急着交换,因为已经知道下标和temp的值
  我们到最后循环结束再放回来
  就像快排里面的枢轴归位一样  */
}

排序完为小顶堆;

分析:

不稳定排序   时间复杂度为O(nlogn)

 

 

 

 

四.归并排序

归并排序是利用递归和分而治之的技术将数据序列划分成为越来越小的半子表,再对半子表排序,最后再用递归步骤将排好序的半子表合并成为越来越大的有序序列;

分析:

稳定的排序方法   时间复杂度为O(nlogn)

两个相邻的有序子序列的合并


/* l为左边起始位置 l1为左序列起始位置 l2为右序列起始位置
   排序合并复制到b中 再复制回a数组 */
void Merge(int a[],int b[],int l1,int r1,int l2,int r2)
{
    int k=l1;    //存放结果的数组起始位置;
    int i=l1,j=l2;
    while(i<=r1&&j<=r2)
    {
        if(a[i]<=a[j]) b[k++]=a[i++];
        else b[k++]=a[j++];
    }
    while(i<=r1) 
    {
        b[k++]=a[i++];
    }
    while(j<=r2) 
    {
        b[k++]=a[j++];
    }
    for(i=l1; i<=r2; i++)
    {
        a[i]=b[i];
    }
}
void MSort(int a[],int b[],int s,int t)
{
    if(s==t) return ;//待排数组只有一个元素;
    else
    {
        int m=(s+t)/2;
        MSort(a,b,s,m);
        MSort(a,b,m+1,t);
        Merge(a,b,s,m,m+1,t);
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值