排序-总结

排序 

排序:重点在于对于记录的关键字进行排序,得到按关键字有序记录序列
分为:
    A.内部排序: 排序过程在内存中进行 
    B.外部排序: 待排序记录数据量过大,需要借助外部存储设备
排序的稳定性:排序后有相同关键字的记录顺序不变就是稳定的排序


插入类排序:


1.直接插入排序:

将新加入的记录的关键字与之前的记录关键字从后往前比较,
               若较小,则向前比较,同时进行比较的记录后移一个位置,直到找到小于等于的关键字,插入在其后. 

实例代码如下: 
void InsSort(int r[], int length){//r可以设置为结构数组,这里认为是数组 
    int i,j;
    for(i = 2; i < length; i++){ // i=2开始,i=1为第一个元素,认为是子表,i=0设置为监视哨 
        r[0] = r[i];//将待插入记录存到监视哨中,临时保存 
        j = i - 1;  //i为初始待插入记录位置,i-1为需要比较的记录位置
        while(r[0] < r[j]){
            r[j+1] = r[j];
            j--;
        } 
        r[j+1] = r[0];
    }

优点:算法简单,适用于记录数目较少且基本有序
时间复杂度:O(n^2).

2.折半插入排序:类似于快排

示例代码如下:
void BinSort(int r[], int length){
    int i, x, j;
    int low, high, mid;
    for(i = 2;i <= length; i++){
        x = r[i];
        low = 1;
        high = i - 1;
        while(low <= high){
            mid = (low + high) / 2;
            if(x < r[mid])
                high = mid - 1;
            else
                low = mid + 1;
        }
        for(j = i - 1; j >= low; j--)
            r[j+1] = r[j];
        r[low] = x; 
    }

时间复杂度:O(n^2).
需要比较的次数最大为其折半判定树的深度log2(n)

3.希尔排序:

排序结果,基本有序;又称缩小增量排序;将关键字序列分为若干个子序列,对子序列插入排序

void f1(int r[], int length, int d){//d为这一轮子序列长度(增量) 
    int i, j;
    
    for(i = 1+d; i <= length; i++){
        if(r[i] < r[i-d]){
            r[0] = r[i];
            for(j = i - d; j > 0 && r[j] > r[0]; j -= d){
                r[j + d] = r[j];
            }//如果子序列后者的记录关键字比前小,就复制前者到后者 
            r[j + d] = r[0];//复制要交换的一个到适合的位置 
        }
    }

 
void f2(int r[], int length, int d[], int n){
    for(i = 0; i < n; i++)//d[]为增量数组,n为该数组长度 d[n-1] == 1; 
        f1(r, length, d[i]);
}
时间复杂度:O(n^1.5).
算法不是稳定的 .


交换类排序:

1.冒泡排序(相邻比序法):

反复扫描记录序列,依次交换逆序记录的位置

void BubbleSort(int r[], int n){
    bool change = true;
    int i,j;
    int x = 0;
    for(i = 1; i < n && change; i++){
        change = false;
        for(j = 1; j <= n - i; j++){
            if(r[j]>r[j+1])
            {
                x = r[j];
                r[j] = r[j+1];
                r[j+1] = x;
                change = true;
            }
        }
    }

//下面这种简单些:上升法,不带标记 
void BubbleSort(int r[], int n){
    int i, j, k;
    
    for(i = 0; i < n; i++){
        for(j = n - 2; j >= i; j--){
            if(r[j] > r[j+1]){
                k = r[j];
                r[j] = r[j+1];
                r[j+1] = k;
            }
        }
    } 
}

时间的复杂度:O(n^2). 

2.快排:

原理:一次性可以消除多个逆序来减少耗费时间
找到一个划分元,关键字小的移到前面,大的移到后面,递归在子序列中找出划分元.直到子表长度小于等于1

void QKSort(int r[], int low. int high){
    if(low < high){
        pos = QKPass(r, low, high);//再次快排
        QKSort(r, low, pos -1);
        QKSort(r, pos +1, high); 
    }

一趟快速排序算法: 
int QKPass(int r[], int low, int high){
    int x;
    while(low < high){
        while(low < high && r[high] > x)
            high--;
        if(low < high){
            r[low] = r[high];
            low++;
        }    
        while(low < high && r[low] < x)
            low++;
        if(low < high){
            r[high] = r[low];
            high--;
        }        
    }
    r[low] = x;
    return low;
}
时间复杂度:O(nlog2(n)) 


选择类排序:

1.简单选择排序:

直接从数组中选择最小的记录和第一个记录交换位置,循环

void SelectSort(int r[], int b){
    int i, j, k;
    int x;
    
    for(i = 1; i < n; i++){
        k = i;
        for(j = i+1; j <= n; j++){
            if(r[j] < r[k])//选择最小的记录,得到在数组中的位置 
                k = j;
        }
        if(k != i){
            x = r[i];
            r[i] = r[k];
            r[k] = x;
        }//交换位置 
    }

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

2.树形选择排序(锦标赛排序):

与简单选择排序不同点是,占用空间更多,保留了之前的比较结果
每一个记录看作叶子节点,两两比较,选出最小的为双亲,进一步递归向上,找出根,比较成功后,
该记录对应的叶子节点置为无穷;
进一步两两比较重复上述过程,直到记录全部输出
时间复杂度:O(nlog2(n)). 


3.堆排序:

排序过程中把向量中储存的数据看作一颗完全二叉树来进行操作
重建堆:
    大堆,筛选最大的元素出去,然后最后的元素补根节点,调整堆使最大的元素在最上面 
void sift(Type r[], int k, int m){
    //r[k...m]是以r[k]为根的完全二叉树,调整r[k]使之满足堆的性质 
    int i, j, t, x;
    
    t = r[k];
    x = r[k].key; 
    i = k;
    j = 2 * k;//j指向t的左孩子
    bool finished = false;
    
    while(j <= m && !finished){
        if(j + 1 <= m && r[j].key < r[j+1].key){
            j++;
        }//得到左右孩子中记录关键字较大的位置坐标 
        if(x >= r[j].key) //如果满足堆的性质,上面的比孩子大 
            finished = true;
        else{
            r[i] = r[j];
            i = j;
            j = 2 * i;
        }
    } 
    r[i] = t;

建初堆:
void crt_heap(Type r[], int n)
{
    //对r[]建立堆, n为数组长度
    int i;
    for(i = n / 2; i >= 1; i--)//i指向最后一个非叶子节点 
        sift(r, i, n); 
 } 

堆排序算法:
void HeapSort(Type r[], int n)
{
    crt_heap(r, n);
    
    for(i = n;  i>= 2 ;--i){
        r[0] = r[1];
        r[1] = r[i];
        r[i] = r[0];//最后一个元素和第一个元素交换位置,把最大的换到最后面去,以此达到升序排列x 
        sift(r, 1, i-1);
    }
 } 

时间复杂度:O(nlog2(n)).
算法是不稳定的, 空间复杂度O(1) .

归并类排序:将两个或两个以上的有序表合并成一个表

两个有序子序列合并算法: 
void Merge(Type r1[], int low, int mid, int high, Type r2[])
{
    //r1[low...mid]和r1[mid+1,..high]分别按照关键字有序排列 ,合并存放在r2[]中
    int i, j, k;
    i = low;
    j = mid + 1;
    k = low;
    
    while(i <= mid && j <= high){
        if(r1[i].key <= r1[j].key)
            r2[k++] = r[i++];
        else
            r2[k++] = r[j++];
    }
    while(i <= mid){
        r2[k++] = r1[i++];
    }
    while(j <= high){
        r2[k++] = r1[j++];
    }
 } 

路归并排序的递归算法:
void MSort(Type r1[], int low, int high, Type r3[])
{
    //r1[low...high]排序后放在r3[low...high] 中, r2为辅助空间
    Type *r2;
    int mid;
    
    
    r2 = (Type *)malloc(sizeof(Type) * (high - low + 1));
    if(low == high) r3[low] = r1[low];
    //这个是递归最终退出条件
    else{//r1前半段放到r2前半段中,同理对于后半段,再将r2合并排序 
        mid = (low + high) / 2;
        MSort(r1, low, mid, r2);
        MSort(r1, mid + 1, high, r2); 
        Merge(r2, low, mid, high, r3);
    } 
    free(r2);
 } 

调用:
void MergeSort(Type r[], int n){
    MSort(r, 1, n, r);

分配类排序:

核心是分配和收集,利用关键字的优先级进行排序的思想 

高位优先排序:

比如桥牌,先比较花色在比较面值;比如学号,比较级,院,班,号;

低位优先排序:

链式基数排序


思想:基于"分配"和"收集"的操作, 将单关键字转化为多关键字排序
将链表作为存储结构, 待排序记录以指针相连,构成链表;
分配:按照关键字取值分配记录到链队列相应队列中,每个队列关键字取值相同
收集:按照关键字大小,从小到大,将队列首尾相接连接成一个链表;
重复上述步骤..

定义:
//待排序记录结点
typedef struct node{
    int data;//比如说一个三位数 
    struct node *next;
}TNode;

//队列首尾指针
typedef struct{
    node *front;
    node *rear;
}TPointer; 


//根据数组R[](已经存在元素),构建带头结点待排序记录链表
TNode *create_list(int R[], int n){
    
    TNode *p, *ph;
    //p为每一个存了记录的结点, ph为头结点
    ph = (TNode *)malloc(sizeof(TNode));
    if(!ph) return NULL; 
    ph->next = NULL;
    
    int i;
    for(i = 0; i < n; i++){
        p = (TNode *)malloc(sizeof(TNode));
        if(!p) return NULL;
        
        p->data = R[i];
        //头插法插入 
        p->next = ph->next;
        ph->next = p;
    }
    return ph;//返回头结点 

#define N 10
//分配算法,对三位数的记录序列按照关键字低位排序分配 
void dispatch(TNode *ph, TPointer *Q, int d){    
    //ph存记录, Q队列:存指针,d根据关键字到那一趟取值不同    
    TNode *p = NULL;//作为辅助空间拆分ph 
    int i, idx;
    
     
    //初始化Q
    for(i = 0; i < N; i++){
        Q[i].front = NULL;
        Q[i].rear = NULL; 
    } 
    
    p = ph->next;
    if(p){
        ph->next = p->next;
        p->next = NULL;
    }//第一个记录被单独拆到p里面
    
    while(p){
        idx = p->data;
        for(i = 0; i < d; i++)
            idx = idx / N;
        //第一趟排序,d = 0
        idx = idx % N;
        
        //根据关键字分配到Q中
        if(Q[idx].front = NULL){
            Q[idx].front = p;
            Q[idx].rear = p;
        } 
        else{//尾插法 
            Q[idx].rear->next = p;
            Q[idx].rear = p;
        }
        p = ph->next;
        if(p){//拆,直到拆完 
            ph->next = p->next;
            p->next = NULL;
        }
    } 
}

void collect(TNode *ph, TPointer *Q){
    TNode *p;
    int i;
    //ph为头结点,最终可以传出去
    
    for(i = 0; !Q[i].front; i++)
        ;//找出第一个队列中非空结点
    ph->next = Q[i].front;
    p = Q[i].rear;
    i++;
    
    for(; i < N; i++){
        if(Q[i].front){
            p->next = Q[i].front;
            p = Q[i].rear;//注意的是Q[i]内部的结点是连接好的 
        }
    }
    p->next = NULL;

void list_sort(int *R, int n){
    int i;
    TNode *ph, *p;
    TPointer Q[N];
    int m = max(R, n);
    
    ph = create_list(R, n);
    
    for(i = 0; m; i++, m /= N){
        dispatch(ph, Q, i);
        collect(ph, Q);
    }
    for(i = 0, p = ph->next; p; p = p->next){
        R[i] = p->data;
    }
    free(ph);
}

码字不易, 赞赞啊!

欢迎评论交流!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/311503.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

.NET Core开发实战(第10课:环境变量配置提供程序)--学习笔记

10 | 环境变量配置提供程序&#xff1a;容器环境下配置注入的最佳途径环境变量的配置提供程序主要适应场景&#xff1a;1、在 Docker 中运行时2、在 Kubernetes 中运行时3、需要设置 ASP.NET Core 的一些内置特殊配置时环境变量和命令行这两个提供程序在早期是没有容器化的&…

dotnetcore3.1 WPF 实现多语言

dotnetcore3.1 WPF 实现多语言Intro最近把 DbTool 从 WinForm 迁移到了 WPF&#xff0c;并更新到了 dotnet core 3.1&#xff0c;并实现了基于 Microsoft.Extensions.Localization 实现了基本的多语言支持。下面来分享一下如何来实现服务注册如果不熟悉如何在 WPF 中使用依赖注…

卸载chrome_Chrome 浏览器必备“扩展管理工具”,一键管理 Chrome 扩展

前言丰富的扩展插件可以说是 Chrome 浏览器的灵魂了&#xff0c;但是扩展安装的多了&#xff0c;难免会引起卡顿&#xff0c;而且每次打开/关闭扩展都要进入扩展程序页面&#xff0c;切换起来很不方便。下面分享的三款 Chrome 扩展管理工具&#xff0c;可以让我们更便捷地管理 …

面试官:你连HTTP请求Post和Get都不了解?

IT界知名的程序员曾说&#xff1a;对于那些月薪三万以下&#xff0c;自称IT工程师的码农们&#xff0c;其实我们从来没有把他们归为我们IT工程师的队伍。他们虽然总是以IT工程师自居&#xff0c;但只是他们一厢情愿罢了。此话一出&#xff0c;不知激起了多少(码农)程序员的愤怒…

运维进化论:微盟“删库跑路”给我们的启示

作者&#xff1a;茹炳晟&#xff0c;软件质量和研发工程效能专家事件背景微盟是国内移动互联网营销引领者&#xff0c;中国最大的微信公众智能服务平台&#xff0c;基于微信为企业提供开发、运营、培训、推广一体化解决方案&#xff0c;帮助企业实现线上线下互通&#xff0c;社…

拦截器如何获取@requestbody_分布式系统中如何优雅地追踪日志(原理篇)

分布式系统中日志追踪需要考虑的几个点&#xff1f;需要一个全服务唯一的id&#xff0c;即traceId&#xff0c;如何保证&#xff1f;traceId如何在服务间传递&#xff1f;traceId如何在服务内部传递&#xff1f;traceId如何在多线程中传递&#xff1f;我们一一来解答&#xff1…

.NET Core开发实战(第11课:文件配置提供程序)--学习笔记

11 | 文件配置提供程序&#xff1a;自由选择配置的格式文件配置提供程序Microsoft.Extensions.Configuration.IniMicrosoft.Extensions.Configuration.JsonMicrosoft.Extensions.Configuration.NewtonsoftJsonMicrosoft.Extensions.Configuration.XmlMicrosoft.Extensions.Conf…

前端demo_【前端3分钟】Script Error产生的原因和解法

Script Error对于前端开发者相信都不陌生&#xff0c;而且由于没有具体错误堆栈和代码行列号&#xff0c;成为可能是最神秘的错误之一。下面介绍Script Error产生的原理和解决办法。1、Script Error是如何产生的跨域资源引用假如&#xff1a;abc.com 下的页面引用了属于 http:/…

基于Abp VNext框架设计 - Masstransit分布式消息

abp 通过IDistributedEventBus接口集成自IEventBus实现分布式事件消息的发布订阅。IEventBus在什么时机触发PublishAsync?当前UnitOfWork完成时&#xff0c;触发IEventBus的PublishAsync在没有事务环境下&#xff0c;同步调用IEventBus的PublishAsyncabp 默认实现基于RabbitMq…

16进制数用空格分开 tcp_面试时,你是否被问到过TCP/IP协议?

点击蓝字关注我们看到这句话&#xff0c;有没有感到很熟悉呀&#xff1f;相信很多人在面试的时候都被要求&#xff0c;很多人会觉得我们在实际开发中一般用不到这些知识&#xff0c;所以对这些东西不屑一顾。但是小编认为想要成为一个完美的网工,那么对这些基础知识必须要有一定…

直接使用汇编编写 .NET Standard 库

前言Common Language Runtime&#xff08;CLR&#xff09;是一个很强大的运行时&#xff0c;它接收 Common Intermediate Language&#xff08;CIL&#xff09; 的输入并最终产生机器代码并执行。CIL 在 CLR 上相当于 ASM 汇编代码的存在。CLR 之上的语言 C#、F#、VB.NET 等语言…

[蓝桥杯2016决赛]七星填数-next_permutation枚举

题目描述 如下图所示&#xff1a; 在七角星的14个节点上填入1~14 的数字&#xff0c;不重复&#xff0c;不遗漏。要求每条直线上的四个数字之和必须相等。 图中已经给出了3个数字。请计算其它位置要填充的数字&#xff0c;答案唯一。 填好后&#xff0c;请提交绿色节点的4个数…

系统蓝屏的几种姿势,确定不了解下么?

前言在 蓝屏&#xff08;BSOD&#xff09;转储设置&#xff0c;看本文就够了&#xff01;这篇文章里比较详细的介绍了蓝屏转储设置。做好设置后&#xff0c;我们就可以在需要的时候使系统蓝屏了。本文介绍几种使系统蓝屏的办法&#xff0c;当然肯定还有其它办法&#xff0c;如果…

最长公共子串-dp

题目: 给定两个字符串&#xff0c;求出它们之间最长的相同子字符串的长度。 公共子串和公共子序列不同&#xff0c;公共子序列不要求连续&#xff0c;但公共子串必须是连续的。如: A “helloworld” B “loop” A和B的最长公共子序列是"loo",但最长公共子串是&quo…

智能对话引擎:两天快速打造疫情问答机器人

01微软AI技术开源知识库疫情机器人近一个月来&#xff0c;“新冠肺炎疫情”成了所有人的热点话题&#xff0c;抗击疫情的战役在全国紧张有序地进行着。随着全国各地的企业陆续复工&#xff0c;怎样防范、保护自己和家人成了当下每个人的焦点。为了配合奋战在一线的医护人员打赢…

数码管

题目背景 小明的单片机上面的LED显示屏坏掉了&#xff0c;于是他请你来为他修显示屏。 屏幕上可以显示0~9的数字&#xff0c;其中每个数字由7个小二极管组成&#xff0c;各个数字对应的表示方式如图所示&#xff1a; 题目描述 为了排除电路故障&#xff0c;现在你需要计算&am…

fh 幅频特性曲线怎么画fl_初学者怎么练习线条?教你如何画出流畅线条的技巧...

初学者怎么练习线条&#xff1f;怎样才能画出流畅线条&#xff1f;画出流畅线条有哪些技巧&#xff1f;想必这些问题都是绘画初学者们比较伤脑筋的问题&#xff0c;那么到底怎样才能画出流畅线条呢&#xff1f;今天灵猫课堂老师就在网络上收集整理了关于初学者怎么练习线条&…

.NET Core开发实战(第12课:配置变更监听)--学习笔记

12 | 配置变更监听&#xff1a;配置热更新能力的核心这一节讲解如何使用代码来监视配置变化并做出一些动作当我们需要追踪配置发生的变化&#xff0c;可以在变化发生时执行一些特定的操作配置主要提供了一个 GetReloadToken 方法&#xff0c;这就是跟踪配置的关键方法接着使用上…