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

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

相关文章

关于Java内存访问重排序的研究

《关于Java内存访问重排序的研究》文章主要介绍了重排序现象及其在多线程编程中的影响,包括内存可见性问题和Java内存模型中对重排序的规则... 目录什么是重排序重排序图解重排序实验as-if-serial语义内存访问重排序与内存可见性内存访问重排序与Java内存模型重排序示意表内存屏障内存屏障示意表Int

不懂推荐算法也能设计推荐系统

本文以商业化应用推荐为例,告诉我们不懂推荐算法的产品,也能从产品侧出发, 设计出一款不错的推荐系统。 相信很多新手产品,看到算法二字,多是懵圈的。 什么排序算法、最短路径等都是相对传统的算法(注:传统是指科班出身的产品都会接触过)。但对于推荐算法,多数产品对着网上搜到的资源,都会无从下手。特别当某些推荐算法 和 “AI”扯上关系后,更是加大了理解的难度。 但,不了解推荐算法,就无法做推荐系

康拓展开(hash算法中会用到)

康拓展开是一个全排列到一个自然数的双射(也就是某个全排列与某个自然数一一对应) 公式: X=a[n]*(n-1)!+a[n-1]*(n-2)!+...+a[i]*(i-1)!+...+a[1]*0! 其中,a[i]为整数,并且0<=a[i]<i,1<=i<=n。(a[i]在不同应用中的含义不同); 典型应用: 计算当前排列在所有由小到大全排列中的顺序,也就是说求当前排列是第

csu 1446 Problem J Modified LCS (扩展欧几里得算法的简单应用)

这是一道扩展欧几里得算法的简单应用题,这题是在湖南多校训练赛中队友ac的一道题,在比赛之后请教了队友,然后自己把它a掉 这也是自己独自做扩展欧几里得算法的题目 题意:把题意转变下就变成了:求d1*x - d2*y = f2 - f1的解,很明显用exgcd来解 下面介绍一下exgcd的一些知识点:求ax + by = c的解 一、首先求ax + by = gcd(a,b)的解 这个

综合安防管理平台LntonAIServer视频监控汇聚抖动检测算法优势

LntonAIServer视频质量诊断功能中的抖动检测是一个专门针对视频稳定性进行分析的功能。抖动通常是指视频帧之间的不必要运动,这种运动可能是由于摄像机的移动、传输中的错误或编解码问题导致的。抖动检测对于确保视频内容的平滑性和观看体验至关重要。 优势 1. 提高图像质量 - 清晰度提升:减少抖动,提高图像的清晰度和细节表现力,使得监控画面更加真实可信。 - 细节增强:在低光条件下,抖

【数据结构】——原来排序算法搞懂这些就行,轻松拿捏

前言:快速排序的实现最重要的是找基准值,下面让我们来了解如何实现找基准值 基准值的注释:在快排的过程中,每一次我们要取一个元素作为枢纽值,以这个数字来将序列划分为两部分。 在此我们采用三数取中法,也就是取左端、中间、右端三个数,然后进行排序,将中间数作为枢纽值。 快速排序实现主框架: //快速排序 void QuickSort(int* arr, int left, int rig

usaco 1.3 Mixing Milk (结构体排序 qsort) and hdu 2020(sort)

到了这题学会了结构体排序 于是回去修改了 1.2 milking cows 的算法~ 结构体排序核心: 1.结构体定义 struct Milk{int price;int milks;}milk[5000]; 2.自定义的比较函数,若返回值为正,qsort 函数判定a>b ;为负,a<b;为0,a==b; int milkcmp(const void *va,c

poj 3974 and hdu 3068 最长回文串的O(n)解法(Manacher算法)

求一段字符串中的最长回文串。 因为数据量比较大,用原来的O(n^2)会爆。 小白上的O(n^2)解法代码:TLE啦~ #include<stdio.h>#include<string.h>const int Maxn = 1000000;char s[Maxn];int main(){char e[] = {"END"};while(scanf("%s", s) != EO

秋招最新大模型算法面试,熬夜都要肝完它

💥大家在面试大模型LLM这个板块的时候,不知道面试完会不会复盘、总结,做笔记的习惯,这份大模型算法岗面试八股笔记也帮助不少人拿到过offer ✨对于面试大模型算法工程师会有一定的帮助,都附有完整答案,熬夜也要看完,祝大家一臂之力 这份《大模型算法工程师面试题》已经上传CSDN,还有完整版的大模型 AI 学习资料,朋友们如果需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费

hdu 1285(拓扑排序)

题意: 给各个队间的胜负关系,让排名次,名词相同按从小到大排。 解析: 拓扑排序是应用于有向无回路图(Direct Acyclic Graph,简称DAG)上的一种排序方式,对一个有向无回路图进行拓扑排序后,所有的顶点形成一个序列,对所有边(u,v),满足u 在v 的前面。该序列说明了顶点表示的事件或状态发生的整体顺序。比较经典的是在工程活动上,某些工程完成后,另一些工程才能继续,此时