致我们终将忘记的算法(说不清道不明的排序)

2023-11-10 03:59

本文主要是介绍致我们终将忘记的算法(说不清道不明的排序),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1->插入排序:

插入排序的思想很简单,将待排序的元素,从后往前在已经排序好的部分序列中寻找要插入的位置。

Code:

void InsertSort(int Array[]){

    for(int i=1;i<Array.Size();i++){

        int tmp=Array[i];

        int j=i-1;

        while(j>=0 && Array[j]>tmp) {Array[j+1]=Array[j];  j--;}

         Array[j+1]=tmp;

    }

}

如果目标是将n个元素的序列升序排列,那么插入排序在n个序列已经是升序的情况下,只需要n-1次比较操作。若初始序列是降序的情况,那么需要n*(n-1)/2次比较,另外插入排序的数值赋值操作是比较操作的次数减去(n-1)次。插入排序的平均时间复杂度为O(n^2),是稳定的排序算法。

 

2->二分插入排序

对于插入排序的重点是查找和插入,若将上述的查找过程,换成二分查找的算法,就形成了二分插入排序算法。

void BinaryInsertSort(int A[]){

    for(int i=1;i<A.size();i++){

        int Key=A[i] , low=0 , high=i-1;

        while(low<=right){

              int mid=(low+high)/2;

              if(A[mid]>Key) high=mid-1;

              else  low=mid+1;  

         }

         /*退出循环之后,low即是要插入的位置*/

        for(int j=i-1;j>=low;j--)  A[j+1]=A[j];

        A[low]=Key;

    }

}

二分插入排序是一种稳定的排序。插入每个记录需要比较O(log i)次,最大移动i+1次。最佳的情况时间复杂度为O(nlogn),最差和平均的复杂度均为O(n^2).

 

3->希尔排序

希尔排序是属于插入类排序算法。对于插入排序平均时间复杂度为O(n^2),但是在序列基本有序的情况下可以将时间复杂度提高到O(n).希尔排序就是利用了这一特点。

希尔排序的思想:先将整个待排序记录序列分割成若干个子序列,分别进行直接插入排序,待整个序列中的记录“基本有序”时,再对整个序列进行一次直接插入排序。

另外希尔排序的子序列构成不是简单的分段分割,而是将相隔某个“增量”的记录组成一个子序列。

void ShellInsert(int A[],int dk){

    for(int i=dk;i<A.size();i++){

        int j=i-d;   int tmp=A[i];

        while(j>=0 && A[j]>tmp){A[j+d]==A[j];j-=d;}

        A[j+d]=tmp;

    }

}

void ShellSort(int A[]){

    int dk=A.Size()/2;             //初始增量,为数组长度的一半

    while(d>=1){

        ShellInsert(A,dk);

        dk=dk/2;

    }

}

希尔排序是不稳定的排序算法,它的平均时间复杂度为O(n^2).在希尔排序中选取不同的步长,会有一个不同的时间复杂度。

 

4->简单选择排序算法

一趟简单选择排序的操作:通过n-i次关键字间的比较,从n-i+1的记录中选择最小的记录,与在i位置的记录交换。

void SelectSort(int A[],int n){

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

        int min=i;

        for(int j=i+1;j<len;j++)

             if(A[min]>A[j])  min=j;

        /*交换*/

        if(min!=i) swap(a[min],a[i]);

    }

}

简单的选择排序的交换次数在0到n-1次之间。比较次数为n(n-1)/2与初始序列无关。无论最好还是最坏的情况时间复杂度都为O(n^2)

 

5->冒泡排序

冒泡排序算法思想:比较相邻的两个元素,如果次序不对则交换它们。对每一对相邻元素做同样的工作。直到最后一对。那么最后一个元素应该是有序的。

void BubbleSort(int A[],int n){

    int i=n;

    while(i>0){

        for(int j=0;j<i-1;j++)

             if(A[j]>A[j+1])  swap(A[j],A[j+1]);

       i--;

    }

}

冒泡排序的最优的时间复杂度为O(n),是一种稳定的排序算法。平均时间复杂度为O(n^2);

 

6->快速排序

快速排序的基本思想:通过一趟排序将记录分割成独立的两个部分,其中一部分记录的关键字均比另一部分的关键字小,则可分别对这两部分记录继续进行排序,以达到整体有序。

void Partition(int A[],int low,int high){                         //一趟排序过程

   int pivotKey=A[low];

    while(low<high){

        while(low<high && A[high]>=pivotKey) --high;

        A[low]=A[high];

        while(low<high && A[low]<=pivotKey) low++;

       A[high]=A[low];

    }

    A[low]=privotKey;

    return low;

}

void QSort(int A[],int low,int high){

    if(low<high){

        int mid=Partition(A,low,high);

        QSort(A,low,mid-1);

        QSort(A,mid+1,high);

    }

}

快排是不稳定的排序算法,最坏的情况时间复杂度为O(n^2),平均时间复杂度为O(nlogn).

 

7->堆排序

要了解堆排序,首先要了解下面几个问题:

a):堆是怎么定义的?在堆排序里面提到的堆一般指的是二叉堆:满足性质,父结点的键值总是大于或者等于(小于或者等于)任何一个子节点的键值。每个结点的左右子树都是一个二叉堆。

b):堆调整:堆调整是为了保持堆的性质而进行的一些操作。对某一节点为根的子树做堆调整,其实就是将该根节点“下沉”一直沉到合适的位置。

例如:对于最大堆的堆调整:

1,在对应的数组元素A[i]、左孩子A[Left(i)]、右孩子A[Right(i)]中最大的那个标记为max;

2,如果A[i]已经是最大的元素,则程序结束。

3,否则将A[i]与子节点当中最大的那个交换

4,再从交换的子节点开始,重复1,2,3步骤,直到叶子节点

一次堆调整的时间复杂度为log(n).

c):如何建堆:建堆是一个通过不断的堆调整,使得整个二叉树满足堆的性质的操作。在数组中的话,我们一般都是从下标为n/2的数开始做堆调整,直到下标为0的数满足堆的性质。

d):如何进行建堆:堆排序在上述(c)中对数组建堆的操作之后,第一次将A[0]与A[n-1]交换,再对A[0]到A[n-2]进行堆调整,一直重复直到A[0]与A[1]交换,就形成了一个有序数组

void HeapAdjust(int A[],int s,int m){                 //使s到m之间成为一个大顶堆

    int tmp=A[s];

    for(int j=2*s ;j<=m;j*=2){

        if(j<m && A[j]<A[j+1]) j++;

        if(tmp>A[j]) break;

         A[s]=A[j];   s=j;

    }

    A[s]=tmp;

}

void HeapSort(int A[]){

    for(int i=A.size()/2;i>=0;i--)    HeapAdjust(A,i,A.size());            //建堆的过程

    for(int i=A.size()-1;i>1;i--){                       //堆排序的过程

        swap(A[0],A[i]);

        HeapAdjust(A,0,i);

    }

}

堆排序的最差最优以及平均时间复杂度都为O(nlogn).空间复杂度为O(1),堆排序是不稳定的排序算法。

 

8->归并排序

算法思想:假设初始序列含有n个记录,则可以看成有n个有序子序列,每个子序列的长度为1,然后两两归并,得到n/2个长度为1或者2的子序列,重复上述过程,直到得到一个长度为n的有序序列为止。

void Merge(int A&[],int B[],int k,int m,int n){     //将B[k----m]和B[m+1-----n]合并存入A数组 

    for(int i=k,j=m+1;i<=m && j<=n;k++){

          if(B[i]>B[j]) A[k]=B[j++];   else A[k]=B[i++];

    }

    if(i<=m) A[k...n]=B[i...m];   if(j<=n) A[k...n]=B[j....n];

}

void MSort(int A&[], int B[],int s,int t){

    if(s==t) A[s]=B[s];

    else{

         int mid=(s+t)/2;

         Msort(C,B,s,mid);

         Msort(C,B,mid-1,t);

         Merge(A,C,s,m,t);

    }

}

归并排序是稳定的排序算法,时间复杂度为O(nlogn)

 

9->计数排序

计数排序使用一个额外的数组C,其中第i个元素是待排序数组A中值等于i的元素的个数,然后根据数组C来将A中的元素排序到正确的位置,它只能多整数进行排序。

它是一种稳定的排序算法。正常情况时间复杂度会达到O(n).

 

10->基数排序

基数排序是一种非比较型整数排序算法,其原理是将整数按位切割成不同的数字,然后按照每个位分别比较。

算法的过程:

将所有待比较数值(正整数)统一为同样的数位长度,数位较短的前面补0;从最低位开始,依次进行一次排序;这样从最低位直到最高位完成以后就变成了一个有序数列。

基数排序的时间复杂度为O(k*n)

 

11->桶排序

桶排序也称为箱排序。工作原理:将数组分到有限数量的桶子里面。每个桶再个别排序(有可能采用其他方法对桶进行个别排序,也有可能继续递归的进行桶排序)

桶排序是稳定的,大多数是常见排序里面最快的一种,比快排还要快,确定就是太耗费内存空间。

算法描述:

设置一定量的数组当作空桶子

遍历待排序数列,把每个数放到相应的桶中。

对于每个不是空的桶进行子排列

 

归纳总结:

这篇关于致我们终将忘记的算法(说不清道不明的排序)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/380281

相关文章

openCV中KNN算法的实现

《openCV中KNN算法的实现》KNN算法是一种简单且常用的分类算法,本文主要介绍了openCV中KNN算法的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的... 目录KNN算法流程使用OpenCV实现KNNOpenCV 是一个开源的跨平台计算机视觉库,它提供了各

springboot+dubbo实现时间轮算法

《springboot+dubbo实现时间轮算法》时间轮是一种高效利用线程资源进行批量化调度的算法,本文主要介绍了springboot+dubbo实现时间轮算法,文中通过示例代码介绍的非常详细,对大家... 目录前言一、参数说明二、具体实现1、HashedwheelTimer2、createWheel3、n

Mybatis 传参与排序模糊查询功能实现

《Mybatis传参与排序模糊查询功能实现》:本文主要介绍Mybatis传参与排序模糊查询功能实现,本文通过实例代码给大家介绍的非常详细,感兴趣的朋友跟随小编一起看看吧... 目录一、#{ }和${ }传参的区别二、排序三、like查询四、数据库连接池五、mysql 开发企业规范一、#{ }和${ }传参的

SpringBoot实现MD5加盐算法的示例代码

《SpringBoot实现MD5加盐算法的示例代码》加盐算法是一种用于增强密码安全性的技术,本文主要介绍了SpringBoot实现MD5加盐算法的示例代码,文中通过示例代码介绍的非常详细,对大家的学习... 目录一、什么是加盐算法二、如何实现加盐算法2.1 加盐算法代码实现2.2 注册页面中进行密码加盐2.

Java时间轮调度算法的代码实现

《Java时间轮调度算法的代码实现》时间轮是一种高效的定时调度算法,主要用于管理延时任务或周期性任务,它通过一个环形数组(时间轮)和指针来实现,将大量定时任务分摊到固定的时间槽中,极大地降低了时间复杂... 目录1、简述2、时间轮的原理3. 时间轮的实现步骤3.1 定义时间槽3.2 定义时间轮3.3 使用时

C++快速排序超详细讲解

《C++快速排序超详细讲解》快速排序是一种高效的排序算法,通过分治法将数组划分为两部分,递归排序,直到整个数组有序,通过代码解析和示例,详细解释了快速排序的工作原理和实现过程,需要的朋友可以参考下... 目录一、快速排序原理二、快速排序标准代码三、代码解析四、使用while循环的快速排序1.代码代码1.由快

如何通过Golang的container/list实现LRU缓存算法

《如何通过Golang的container/list实现LRU缓存算法》文章介绍了Go语言中container/list包实现的双向链表,并探讨了如何使用链表实现LRU缓存,LRU缓存通过维护一个双向... 目录力扣:146. LRU 缓存主要结构 List 和 Element常用方法1. 初始化链表2.

Oracle登录时忘记用户名或密码该如何解决

《Oracle登录时忘记用户名或密码该如何解决》:本文主要介绍如何在Oracle12c中忘记用户名和密码时找回或重置用户账户信息,文中通过代码介绍的非常详细,对同样遇到这个问题的同学具有一定的参... 目录一、忘记账户:二、忘记密码:三、详细情况情况 1:1.1. 登录到数据库1.2. 查看当前用户信息1.

golang字符串匹配算法解读

《golang字符串匹配算法解读》文章介绍了字符串匹配算法的原理,特别是Knuth-Morris-Pratt(KMP)算法,该算法通过构建模式串的前缀表来减少匹配时的不必要的字符比较,从而提高效率,在... 目录简介KMP实现代码总结简介字符串匹配算法主要用于在一个较长的文本串中查找一个较短的字符串(称为

通俗易懂的Java常见限流算法具体实现

《通俗易懂的Java常见限流算法具体实现》:本文主要介绍Java常见限流算法具体实现的相关资料,包括漏桶算法、令牌桶算法、Nginx限流和Redis+Lua限流的实现原理和具体步骤,并比较了它们的... 目录一、漏桶算法1.漏桶算法的思想和原理2.具体实现二、令牌桶算法1.令牌桶算法流程:2.具体实现2.1