排序算法详解

插入排序

插入排序基本思想: 

每次将一个待排序的记录,按其关键字大小插入到前面已经排好序的文件中的适当位置,直到全部记录插入完成为止。

直接插入排序

核心思想

假设待排序的记录存放在数组R中,排序过程中的某一中间时刻,R被划分为2个区间【R[1],R[i-1]】和【R[i],R[n]】,前一个区间是已经完成排序的有序区,后一个区间则是为排序的无序区。直接插入排序就是将无序区的第一个记录R[i]插入到有序区中适当位置,R[1]到R[i]就成为新的有序区,递归下去,无序区为空时(即R[n]插入有序区),排序完成。

动画演示

如下图,蓝色就是无序区,橙色就是有序区,绿色表示正在比较,红色是无序区取出的第一个记录。

 时间复杂度

文件初始态为正序时,算法时间复杂度为O(n)

最坏情况:文件初始态为逆序,算法时间复杂度为O(n^{2}

平均时间复杂度:O(n^{2}

空间复杂度

空间复杂度:O(1)

稳定性

直接插入是稳定的排序方法

 算法优化

监视哨

 优化后的代码

void InsertSort(int R[],int n){   
	int i,j;
    for(i=2;i<=n;i++)    
    {
        R[0]=R[i];    
        j=i-1;    
        while(R[0]<R[j])
        {
            R[j+1]=R[j];    
            j--;    
        }
        R[j+1]=R[0];   
    }
}
#注意:此处的n是待排序数数量+1,因为定义数组时要增加把R[0]赋值为0作为监视哨,数组空间从R[1]开始存放数据

例题:

待排序的记录有8个,关键字分别为 49,38,65,97,76,13,27,18,用直接插入排序算法或者直接选择排序算法进行排序。程序实现对该排序的排序码动态变化过程进行模拟显示,要求按递增顺序排序。

答案:

#include<stdio.h>

void InsertSort(int num[]){
	int i,j;
    for(i=2;i<=9;i++)    
    {
        num[0]=num[i];    
        j=i-1;    
        while(num[0]<num[j])
        {
            num[j+1]=num[j];    
            j--;    
        }
        num[j+1]=num[0];   
    }
}


int main(){
	int num[9]={0,49,38,65,97,76,13,27,18};
	
	InsertSort(num);
	int i=0;
	for(i=1;i<9;i++)       #输出时从R[1]开始,因为R[0]是监视哨
	{
		printf("%d ",num[i]);
	}

	
}

希尔排序

核心思想

取定一个小于n的整数d1作为第一个增量,把文件全部记录分成若干个组,所有距离为d1的倍数的记录放在同一个组内,再在各个组内使用直接插入排序进行排序。完成后缩小整数d1的取值作为下一个增量,再次分组,继续组内直接插入排序。递归,当d1取值为1时,则为最后一次排序,为0时排序结束。

动画演示

抱歉,没时间学动画,先网图凑合

 时间复杂度

最好情况:时间复杂度为O(n)

最坏情况:文件初始态为逆序,算法时间复杂度为O(n^{2}

平均时间复杂度:O(n^{1.3} 

空间复杂度

空间复杂度:O(1)

稳定性

不稳定

希尔排序为何优于直接插入排序

直接插入排序在文件初始状态为正序时所需时间最少,实际上,当文件初始状态基本有序时直接排序所需的比较和移动次数均较少。在希尔排序刚开始时增量较大,分组较多,每组的记录数目少,故各组直接插入较快,当增量逐渐减小时,分组数直接减少,而组内记录数目增加,由于已经按前一次的增量拍过序,使文件较接近于有序状态,所以新的一趟排序较快,所以在效率上,希尔排序较直接插入排序有较大改进。

代码(未添加监视哨)

void shellsort(int *k,int n)
{
    int i, j, temp, gap = n;
    while(gap > 1)
    {
        gap = gap/2;                /*增量缩小,每次减半*/
        for(i=0;i<n-gap;i++) //n-gap 是控制上限不让越界
        {
            j = i + gap;    //相邻间隔的前后值进行比较
            if(k[i]>k[j])
            {
                temp = k[i];
                k[i] = k[j];
                k[j] = temp;
            }
        }
    }
}

交换排序

基本思想

两两比较待排序记录的关键字,发现两个记录的次序相反时即进行交换,直到没有反序的记录为止。

冒泡排序

核心思想

设想被排序的记录数组R[0]到R[n-1]垂直竖立,将每个记录R[i]看成质量为R[i]的气泡,轻气泡会在重气泡的上方,从上往下扫描数组R,凡扫描到违反这一原则的气泡,使其往上飘(冒),反复执行,直到轻的气泡都在重的上方为止。

动画演示

蓝色未排序,橙色已排序,绿色正在比较

 时间复杂度

最坏:O(n^{2}

最好:  O  ( n^{} )

平均:O(n^{2}

空间复杂度

O(1)

稳定性

稳定

代码(优化)

void sort(int a[],int len)
{   
    int i=0;
    int j;
    int t;
    int flag;    #循环提前结束变量
    for(i=0;i<len-1;i++) [1] 
    {
        flag=0;
        for(j=0;j<len-i-1;j++)
        {
            if(a[j]>a[j+1])
            {
                t=a[j];
                a[j]=a[j+1];
                a[j+1]=t;
                flag=1;
            }
        }
        if(flag==0)
            break;
    }
}

快速排序

核心思想

在当前无序区R[low]和R[high]中任取一个记录作为比较的“基准”,用此基准将当前无序区划分为左右两个较小的无序子区:R[low]到R[i-1]和R[i+1]到R[high]。且左边的无序子区中记录均小于或等于基准,右边的无序子区中记录的关键字均大于或等于基准,基准则位于最终排序的位置上。左右两个无序子区均非空时,重复上述步骤,直到所有无序区中记录均已排好序为止。

动画演示

 时间复杂度

最坏     O(n^{2})

最好     O(nlog2^{n})

平均     O(nlog2^{n}

稳定性

不稳定

代码

//  划分算法
int Partition(RcdType rcd[], int low, int high){
    //  对子序列rcd[low..high]进行一次划分,并返回枢轴应当所处的位置
    //  使得在枢轴之前的关键字均不大于它的关键字,枢轴之后的关键字均不小于它的关键字
    rcd[0] = rcd[low];      //  将枢轴移至数组的闲置单元
    while (low < high){     //  low和high从待排序列的两端交替地向中间移动
        while (low < high && rcd[high].key >= rcd[0].key)   --high;
        rcd[low] = rcd[high];   //  将比枢轴关键字小得关键字移至低端
        while (low < high && rcd[low].key <= rcd[0].key)    ++low;
        rcd[high] = rcd[low];   //  将比枢轴关键字打的关键字移至高端
    }
    rcd[low] = rcd[0];  //  枢轴关键字移到正确位置
    return low;         //  返回枢轴的位置
}

//  快速排序
void QSort(RcdType rcd[], int s, int t){
    //  对记录序列rcd[s..t]进行快速排序
    int privotloc;
    if (s < t){         //  长度大于1
        privotloc = Partition(rcd, s, t);   //  对rcd[s..t]进行划分并返回枢轴位置
        QSort(rcd, s, privotloc - 1);       //  对枢轴之前的子序列递归快排
        QSort(rcd, privotloc + 1, t);       //  对枢轴之后的子序列递归快排
    }
}

//  对记录的顺序表L进行快速排序
void QuickSort(RcdSqList &L){
    QSort(L.rcd, 1, L.length);
}

选择排序

核心思想

每一趟从待排序的记录中选出关键字最小的记录,顺序放在排好序的子文件的最后,直到全部记录排好为止。

直接选择排序

思想

第一趟排序在无序区中R[0]到R[n-1]中选出最小记录,与R[0]交换;第二趟则是在无序区R[1]到R[n-1]中选出最小记录,与R[1]交换,没趟都使有序区中记录加一。如此循环下去。进行n-1趟后,整个文件就是有序的。

动画演示

 时间复杂度

O(n^{2}

空间复杂度

O(1)

稳定性

不稳定

代码

void select_sort(int a[],int n)//n为数组a的元素个数
{
 //进行N-1轮选择
 for(int i=0; i<n-1; i++)
 {
  int min_index = i; 
  //找出第i小的数所在的位置
  for(int j=i+1; j<n; j++)
  {
   if(a[j] < a[min_index])
   {
    min_index = j;
   }
  }
  //将第i小的数,放在第i个位置;如果刚好,就不用交换
  if( i != min_index)
  {
   int temp = a[i];
   a[i] = a[min_index];
   a[min_index] = temp;
  }
 }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Java排序算法是用于对一系列数据进行排列顺序的一种算法。在Java中,常见的排序算法包括冒泡排序、选择排序、插入排序、快速排序、归并排序等。 冒泡排序是一种比较简单的排序算法,它通过对相邻的元素进行比较和交换来实现排序。该算法的时间复杂度为O(n^2),属于比较低效的排序算法。选择排序是一种简单直观的排序算法,它通过选择最小的元素并放置在已排序的部分来实现排序。该算法的时间复杂度也是O(n^2)。 插入排序是一种比较高效的排序算法,它通过将未排序的元素插入到已排序的部分来实现排序。该算法的时间复杂度也是O(n^2)。快速排序是一种递归的排序算法,它通过选取一个基准值来对数组进行分区,并对每个分区进行排序来实现最终的排序。该算法的时间复杂度为O(nlogn),是比较高效的排序算法之一。 归并排序是一种分治的排序算法,它将数组分成两个子数组,并对每个子数组进行排序,最后将两个子数组合并成一个有序数组。该算法的时间复杂度也是O(nlogn)。在实际应用中,我们通常会选择合适的排序算法来应对不同的排序需求,比如对于小规模数据可以选择简单的排序算法,对于大规模数据可以选择高效的排序算法。总之,了解Java排序算法的原理和性能表现对于编程人员来说是非常重要的。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

予我心安A3

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值