三大O(n^2)算法详解及其优化(冒泡,插入,选择排序)

一.冒泡排序:

1.原理:不停比较若是不和序数交换两个相邻的数即可

2.时间复杂度:最大,对比:n*(n-1)/2+交换3*n*(n-1)/2=n*(n-1)*2;

O(n^2)算法

  1. 冒泡排序的优点:1)每进行一趟排序,就会少比较一次,因为每进行一趟排序都会找出一个较大值。如上例:第一趟比较之后,排在最后的一个数一定是最大的一个数,第二趟排序的时候,只需要比较除了最后一个数以外的其他的数,同样也能找出一个最大的数排在参与第二趟比较的数后面,第三趟比较的时候,只需要比较除了最后两个数以外的其他的数,以此类推……也就是说,没进行一趟比较,每一趟少比较一次,一定程度上减少了算法的量每次之后一个元素到达位置
  1. 稳定,相邻项的比较
  2. 最大10000元素,在元素小比复杂算法快(5个)
  1. 优化:1)一次移动多位   先存在大K中不放进去数组,比较好最大的再放进去(暂时无实现代码,循环之中不好操作)
  1. 提前预判已经结束排序(弱优化)

 

  1. 双向排序

推荐题目:冒泡排序双向优化洛谷P2094_不良人821的博客-CSDN博客

 

 

寻找最小少打了if  34行

4)以上次交换最后一位为标记

 

记录最后一次

  • 数组中的插入排序

原理:插入即表示将一个新的数据插入到一个有序数组中,并继续保持有序。例如有一个长度为N的无序数组,进行N-1次的插入即能完成排序

2、算法分析

  1. 采用双层循环:时间复杂度也是O(n^2)
    1. 外层循环表示的是排序的趟数,n个数字需要n-1趟,因此,外层循环的次数是n-1次;同时也代表数的位置。
    2. 内层循环表示的是每一趟排序的数字。根据插入排序的思想,第i趟排序时,有序数组中的数字就有i个,最坏的情况下就需要进行i次比较,因此循环i次。注意采用的是从后往前进行比较。
  2. 从后往前扫描的时候,如果必须插入的数大于有序数组中当前位置的数,则有序数组中的数和它之后的数均往后移一个位置,否则,把插入的数插入到有序数组中。(稳定排序)

2.1、复杂度分析

2.1.1、时间复杂度

  • 最好情况O(n):排序表是已经排好序,共需比较n-1次,因为没有移动的记录,时间复杂度为O(n)。
  • 最坏情况O(n^2):排序表是逆序的情况,需要进行的比较共有n(n-1)/2次,赋值操作是比较操作的次数加上  (n-1) 次
  • 平均情况O(n^2):平均来说插入排序算法的时间复杂度为O(n^2)

因而,插入排序不适合对于数据量比较大的排序应用

2.1.2、空间复杂度

O(1)

2.2、算法稳定性

插入排序是在一个已经有序的小序列的基础上,一次插入一个元素。当然,刚开始这个有序的小序列只有1个元素,就是第一个元素。比较是从有序序列(已排序好的)的末尾开始,也就是新元素和已经有序的最大者开始比起,直到找到一个已排序的元素小于或者等于新元素的位置,将新元素插入到该位置后。如果碰见一个和待插入元素(新元素)相等的,那么就把待插入元素(新元素)放在相等元素的后面。所以,相等元素的前后顺序没有改变,从原无序序列出去的顺序就是排好序后的顺序,所以插入排序是稳定的。

1.不优化模板

for(int i=2;i<=n;i++)

for(int j=i;j>1;j--)

{

if(a[j]<a[j-1]){

swap(a[j],a[j-1]);

}

else break;

}

  1. 初步优化

 

  1. 二分优化
  2. 推荐题目HDU1040插入排序二分优化_不良人821的博客-CSDN博客

 

选择排序:

1.原理:选择排序的基本思想:刚开始的时候,有序区没有元素,每一趟在无序区中选出来一个最小的元素,然后与无序区第一个元素交换,则此时有序区多一个元素,而无序区少一个元素,知道无序区元素为0结束。

直接选择排序:第一趟排序在R[0..n]中选出一个最小的元素与R[0]交换,第二趟在R[1..n]中选择最小元素,与第一个与R[1]交换,直到无序区的元素只剩下一个排序完成。

注:理解其实也就是插入排序的反向思想,在无序中找最小然后拿出来放入有序,这和插入排序思想类似,前者是无序拿出最小放入有序,后者是无序数拿一个放入有序

2.时间复杂度和算法稳定性

从代码中可以看出一共遍历了n + n-1 + n-2 + … + 2 + 1 = n * (n+1) / 2 = 0.5 * n ^ 2 + 0.5 * n,那么时间复杂度是O(N^2)。

因为在无序部分最大元素和有序部分第一个元素相等的时候,可以将无序部分最大元素放在前面,所以选择排序是稳定的。

3.未优化代码:

for(int i=1;i<=n;i++){

int pos=i;
for(int j=i+1;j<=n;j++)

{

if(a[j]<a[pos]){

pos=j;

}

}

swap(a[j],a[i]);//交换两数

}

O(n^2)

4.优化:其实类似冒泡排序的双向优化我们一样可以同时找最大和最小值

#include<stdio.h>

int n,i,j,l,r,posl,posr,max,min,k;

int a[10005];

int main(){

scanf("%d",&n);

for(i=1;i<=n;i++){

scanf("%d",&a[i]);

}

l=0;

r=n+1;

while(l<r){

posl=l+1;

posr=r-1;

max=a[r-1];

min=a[l+1];

for(i=l+1;i<=r-1;i++){

if(a[i]>max){

posr=i;

max=a[posr];

}

if(a[i]<min){

posl=i;

min=a[posl];

}

}

if(posl==r-1&&posr==l+1){

k=a[l+1];

a[l+1]=a[r-1];

a[r-1]=k;

posl=l+1;

posr=r-1;

}

if(posl==r-1&&posr!=l+1){

k=a[l+1];

a[l+1]=a[r-1];

a[r-1]=k;

posl=l+1;

}

if(posl!=r-1&&posr==l+1){

k=a[l+1];

a[l+1]=a[r-1];

a[r-1]=k;

posr=r-1;//这三个判断一定要打要不然会在对换的时候出现错误

}

if(posl!=l+1){

k=a[l+1];

a[l+1]=a[posl];

a[posl]=k;

}

if(posr!=r-1){

k=a[posr];

a[posr]=a[r-1];

a[r-1]=k;

}

l++;

r--;

}

for(i=1;i<=n;i++){

printf("%d",a[i]);

}

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值