数据结构:排序

排序的概念

1.概念

就我当前所认识的排序来说。排序是把一串相同类型的数据,按照升序或者降序排列起来的操作。

以下介绍的排序大多可以排列不限于整型和文件,但也有一些算法有较明显的局限性。

2.稳定性

如果在排列之前,一组数据中,相同的数据A在B前面,排序后A仍然在B前面,则说明这样的算法是稳定的。如果不是这样,那么这样的算法是不稳定的。

3.内部排序和外部排序

内部排序:数据元素在内存中进行的排序。

外部排序:数据元素太多,根据排序的过程不能在内外存之间移动的排序。

常见的排序算法

1.直接插入排序

思想

(以升序为例)第一次从数据中拿最前面的数放到第一个,再从数据中拿第二个数,放置时和第一个数比较,比它大放后面,比它小放前面。后面的数再和已经有序的数组中最后一个比,比它小再往前依次比,直到找到比手中的数更小的数,就放它后面了。

特点

元素集合越接近有序,直接插入排序算法的时间效率越高

时间复杂度:O(N²)      【是O(N²)中最能打的】

空间复杂度:O(1),它是一种稳定的排序算法

稳定性:稳定

关于这个排序稳定性的说明,它取数是依次取的,就算值相等,那么先取的在前面,后取的在后面,根本不会改变相同值的位次。所以它是稳定的。

void InsertSort(int* arr, int n)
{for (int i = 0; i < n-1; i++)//{int end = i;int tmp = arr[end + 1];//用tmp来记住下一个待比较的数while (end >= 0)//end 和tmp比较{if (tmp < arr[end]){arr[end+1] = arr[end];//如果tmp小,那么end往tmp的位置挪,tmp和前一个位置比end--;                 //end减减才能和前一个位置比}else{break;   //如果tmp大,直接跳出来}}arr[end+1] = tmp;//tmp插入到比它小的数后面}}

2.希尔排序

希尔排序法又称为缩小增量法。基本思想:选定一个整数gap,把待排序的N个数据,分成N/gap个组。每组gap个,以gap为间隔。第一次把这N/gap个组进行直接插入排序。第二次是间隔 N/gap/gap。以此类推,当这个分母越大,逼近于N时,间隔就越小,间隔趋近于1,整体越发有序,当gap=1时,就是有序排序了。

所以希尔排序是要排很多趟的,越有序,越简单。

特点

时间复杂度   O(N*log(N));

空间复杂度   O(1);

void ShellSort(int* arr, int n)
{int gap = n;  //把n给gap,这样n就不用改变了,待会方便它自己÷while (gap>1){gap = gap/3 +1;  //gap就是间隔,+1防止它变成0,并且保证最后一定是1,那么前面的while条件就要给跳出条件。for (int i = 0; i < n - gap; i++)//先排1  gap gap+gap。。。再排2  2+gap。。这样一个for循环就解决了{int end = i; int tmp = arr[end + gap];//用tmp来记住下一个待比较的数while (end >= 0)//end 和tmp比较{if (tmp < arr[end]){arr[end + gap] = arr[end];//如果tmp小,那么end往tmp的位置挪,tmp和前一个位置比end-=gap;                 //end减减才能和前一个位置比}else{break;   //如果tmp大,直接跳出来}}arr[end + gap] = tmp;//tmp插入到比它小的数后面}}
}

3.选择排序

 思想

从待排序的数中每次挑一个最大的,一个最小的排在数组的第一个和最后一个位置。直到选完。

特点

时间复杂度O(N²)

空间复杂度O(1)

稳定性:不稳定。

void SelectSort(int* a, int n)
{for (int min = 0, max = n - 1; min <= max; min++, max--){for (int i = min+1; i < n-min; i++){if (a[min] > a[i]){swap(&a[min], &a[i]);}if (a[max] < a[i]){swap(&a[max], &a[i]);i--;}}}
}

4.堆排序

思想

把数组进行向下调整,形成一个大堆(升序建大堆,降序建小堆)。把堆顶的数换到数组最后一个位置,再把整个数组size--,使这个数不再参与接下来的排序。每调整一个数,就要进行一次向下调整。这样,数组从后向前开始变得有序。直到堆里不再有数。那么整个数组就算完全有序了。

特点

时间复杂度:O(N*log(N));

空间复杂度:O(1);

稳定性:不稳定;

void swap(int* p1, int* p2)
{int tmp = *p1;*p1 = *p2;*p2 = tmp;
}void AdjustDwon(int* a, int n, int root)
{int parent = root;int child = 2 * parent + 1;while (child < n){if (child+1<n && a[child] < a[child+1]){child++;//左孩子和右孩子之间存在紧密联系,所以不能新建一个值来表示更小的那个,那样会导致交换难以进行}if ( a[child] > a[parent])//如果孩子小于父亲,交换{swap(&a[child], &a[parent]);parent = child;child = 2 * parent + 1;}else{break;}}
}
void HeapSort(int* a, int n)
{//向下调整建堆for (int i = (n - 1 -1) / 2; i >= 0; i--)//从最后一颗子树开始调,最后一棵树的下标是n-1,(n-1-1)/2就是最后一棵子树的根节点,{AdjustDwon(a,n, i);}//堆排序int end = n - 1;while (end>0){swap(&a[0], &a[end]);AdjustDwon(a, end, 0);end--;}
}

5.冒泡排序

思想

数组第一个数和第二个数比,大的往后,然后第二和第三比,大的往后,走一趟确定一个最大的数,然后排剩下的,把剩下的中最大的排在倒数第二。然后接着排,直到排完。

特点

时间复杂度O(N²)

空间复杂度O(1)

稳定性:稳定

void BubbleSort(int* a, int n)
{for (int i = 0; i < n; i++){int flag = 0;for (int j = 0; j < n - i-1;j++){if (a[j] > a[j + 1]){swap(&a[j], &a[j + 1]);flag = 1;}}if (!flag){break;}}
}

6.快速排序

思想

选择一个key,一般选最左边的数。然后定义左下标和右下标。左下标从0开始,右下标从最后一个数开始。右下标找比key小的数,找到了后左下标找比key大的数,找到了就和右下标的数进行交换。当两人相遇时,左下标就和key所在的数进行交换。这样key把整个数组分为比key小的数和比key大的数。利用递归,把比key小的这组数照这种方式排,比key大的这组数也这样排。相当于一个前序的遍历了。排完以后就有序了。

特点

时间复杂度O(N*log(N))

空间复杂度O(logN)

稳定性:不稳定

void swap(int* p1, int* p2)
{int tmp = *p1;*p1 = *p2;*p2 = tmp;
}
int PartSort1(int* a, int left, int right)
{int keyi = left;while (left < right){if (a[right] < a[keyi]){if (a[left] > a[keyi]){swap(&a[left], &a[right]);}else{left++;}}else{right--;}}swap(&a[keyi], &a[left]);return left;//此时left将数组划分为两个地盘}快速排序挖坑法
int PartSort2(int* a, int left, int right)
{int keyi = left;int key = a[left];while(left < right){if (a[right] < key){swap(&a[right], &a[left]);while (left < right){if (a[left] > key){swap(&a[right], &a[left]);break;}else{left++;}}}else{right--;}}return left;}int Getmidindex(int* a, int left, int right)
{assert(left < right);int mid = (left + right) / 2;// 对三个值进行排序,确保a[left] <= a[mid] <= a[right]if (a[left] > a[mid])swap(&a[left], &a[mid]);if (a[left] > a[right])swap(&a[left], &a[right]);if (a[mid] > a[right])swap(&a[mid], &a[right]);return mid;
}int PartSort3(int* a, int left, int right)
{//后指针找小,前指针不用找,找到了两人交换int mid = Getmidindex(a, left, right);int keyi = left;swap(&a[mid], &a[keyi]);//交换以后a[keyi]就是最合适的数了。int prev = left;int cur = prev + 1;while (cur <= right){if (a[cur] < a[keyi] && a[++prev] != a[cur])//cur小于key,再把prev++,如果等于cur,不执行。swap(&a[prev], &a[cur]);//如果连续是小,那么最后只能用最后一个小和key交换,然后还要再排一遍。cur++;}swap(&a[prev], &a[keyi]);return prev;
}void QuickSort(int* a, int left, int right)
{if (right - left <= 1){return;}int div = PartSort3(a, left, right);QuickSort(a, left, div);QuickSort(a, div + 1, right);
}

非递归要用到栈

void QuickSortNonR(int* a, int left, int right) {Stack st;StackInit(&st);StackPush(&st, right);StackPush(&st, left);while (!StackEmpty(&st)){left = StackTop(&st);StackPop(&st);right = StackTop(&st);StackPop(&st);int div = PartSort3(a, left, right);if (div + 1 < right){StackPush(&st, right);StackPush(&st, div + 1);}if (left < div-1 ){StackPush(&st, div-1);StackPush(&st, left);}}StackDestroy(&st);
}

7.归并排序

思想

把数两两分组,然后排序。排好后返回,然后四个四个排,然后八个八个排。这是递归。

合并的思想是创建一个新的数组,这两两分组,就有两个数组。分别设置好两组的下标。两个坐标的第一个数依次比较,谁小就把谁插入到新数组中。最后再把有序的新数组的数复制到之前的数组。

存在的问题是并不是所有数组都是以2、4、8的倍数生成的。因此可能会发生数组越界的问题,那么就要进行修正。当最后剩下的数不再是倍数,就直接复制回原来的数组,参与下一次的排序。

特点

时间复杂度:O(N*log(N))

空间复杂度:O(N)

稳定性:稳定

void _MergeSort(int* a, int begin, int end, int* tmp)
{if (begin >= end)//如果只有一个数或者没有数,返回,不比{return;}int mid = (begin + end) / 2;int begin1 = begin, end1 = mid;int begin2 = mid + 1, end2 = end;int index = begin;//用于新建的空间的下标。_MergeSort(a, begin, mid, tmp);_MergeSort(a, mid+1, end, tmp);while (begin1 <= end1 && begin2 <= end2)//当遵守游戏规则的时候,开始比较{if (a[begin1] <= a[begin2])tmp[index++] = a[begin1++];elsetmp[index++] = a[begin2++];}while(begin1 <=end1)//begin1里面还有数,就说明begin2没数了,把begin1的数全放进去tmp[index++] = a[begin1++];while(begin2 <=end2)tmp[index++] = a[begin2++];memcpy(a + begin, tmp + begin, (end-begin+1)*sizeof(int));
}
// 归并排序递归实现
void MergeSort(int* a, int n)
{int* tmp = (int*)malloc(sizeof(int) * n);if (tmp == NULL){perror("malloc failed!");exit(1);}_MergeSort(a, 0, n - 1, tmp);free(tmp);
}
// 归并排序非递归实现
void MergeSortNonR(int* a, int n)
{int* tmp = (int*)malloc(sizeof(int) * n);if (tmp == NULL){perror("malloc failed!");exit(1);}int gap = 1;while (gap < n){//两两归并,然后以2的倍数再排再归并,多出的数要修正,不进入,直接复制for (int i = 0; i < n; i +=2*gap){int begin1 = i, end1 = i + gap - 1;int begin2 = i + gap, end2 = i+(gap * 2) - 1;//修正区间if (end1 >= n){end1 = n - 1;}if (begin2 >= n){begin2 = n;end2 = n-1;}if (begin2 < n && end2 >= n){end2 = n - 1;}int index = i;while (begin1 <= end1 && begin2 <= end2){if (a[begin1] <= a[begin2])tmp[index++] = a[begin1++];elsetmp[index++] = a[begin2++];}while (begin1 <= end1)//begin1里面还有数,就说明begin2没数了,把begin1的数全放进去tmp[index++] = a[begin1++];while (begin2 <= end2)tmp[index++] = a[begin2++];}memcpy(a, tmp, sizeof(int) * n);gap *= 2;}free(tmp);
}

8.计数排序(非比较排序)

思想

按照数组的特点,先遍历一遍数组,选出最大的数和最小的数。生成一个这个范围的新的数组。然后再把数往计数数组里放,出现一次的对应新数组储存的数就+1.两次就加2,最后一步,从计数数组中依次取数。

特点

时间复杂度O(N或者range)

空间复杂度O(range)

稳定性:稳定

只能用于整型

void CountSort(int* a, int n)
{int min = a[0];int max = a[0];for (int i = 1; i < n; i++){if (min > a[i]){min = a[i];}if (max < a[i]){max = a[i];}}//知道范围以后可以开空间了int range = max - min + 1;int* countmp = (int*)malloc(sizeof(int) * range);assert(countmp);memset(countmp, 0, sizeof(int) * range);//初始化为0for (int i = 0; i < n; i++){countmp[a[i] - min]++;//读一个数,这个数对应的countmp的数字就++}int j = 0;for (int i = 0; i < range; i++){while (countmp[i]--){a[j++] = i+ min;}}free(countmp);
}

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

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

相关文章

MVCC详细总结

简介 MVCC&#xff08;Multi-Version Concurrency Control&#xff09;是一种多版本并发控制机制&#xff0c;主要用于数据库管理系统中&#xff0c;实现对数据库的并发访问。在编程语言中&#xff0c;MVCC可以实现事务内存。 MVCC的特点是读不加锁&#xff0c;读写不冲突。MVC…

Fog Project 安装与应用

一、Fog Project 的安装 要求 在开始安装 FOG 之前&#xff0c;您需要决定要使用哪个服务器操作系统。 FOG 可以安装在基于 RedHat 的发行版 CentOS、Fedora、RHEL 等以及 Debian、Ubuntu 和 Arch Linux 上。 选择您最喜欢并且最了解的&#xff01;众所周知&#xff0c;光纤陀…

佳能打印机E568扫描书和文件方法

官方网站; Canon : Inkjet 手册 : IJ Scan Utility : 启动IJ Scan Utility 打开打印机电源 扫描一个文件&#xff0c;翻页后盖好盖子。再点击扫描。 所有扫描结束之后点退出 点击保存

docker部署nacos,单例模式(standalone),使用内置的derby数据库,简易安装

文章目录 前言安装创建文件夹docker指令安装docker指令安装-瘦身版 制作docker-compose.yaml文件查看页面 前言 nacos作为主流的服务发现中心和配置中心&#xff0c;广泛应用于springcloud框架中&#xff0c;现在就让我们一起简易的部署一个单例模式的nacos&#xff0c;版本可…

ruoyi-nbcio-plus基于vue3的flowable流程设计器主界面升级修改

更多ruoyi-nbcio功能请看演示系统 gitee源代码地址 前后端代码&#xff1a; https://gitee.com/nbacheng/ruoyi-nbcio 演示地址&#xff1a;RuoYi-Nbcio后台管理系统 http://122.227.135.243:9666/ 更多nbcio-boot功能请看演示系统 gitee源代码地址 后端代码&#xff1a…

window中如何在Anaconda虚拟环境中安装compressai

1, 进入CompressAI的Github代码页下载压缩包并解压到自己的项目路径 2&#xff0c;打开anaconda的prompt命令行&#xff0c;激活需要安装的虚拟环境&#xff0c;然后进入compressai文件夹&#xff0c;比如下操作&#xff1a; 3&#xff0c;输出安装命令行 pip install -e . -…

前端订阅后端推送WebSocket定时任务

0.需求 后端定时向前端看板推送数据&#xff0c;每10秒或者30秒推送一次。 1.前言知识 HTTP协议是一个应用层协议&#xff0c;它的特点是无状态、无连接和单向的。在HTTP协议中&#xff0c;客户端发起请求&#xff0c;服务器则对请求进行响应。这种请求-响应的模式意味着服务器…

微信公众号如何开通留言功能?

首先&#xff0c;我们需要了解为什么现在注册的公众号没有留言功能。这是因为所有在2018年之后注册的微信公众号都无法再自带留言功能。这一变化是根据微信的通知而实施的。自2018年2月12日起&#xff0c;微信对新注册的公众号进行了调整&#xff0c;取消了留言功能。这一决策主…

yolo v5 中 letterbox对不规则矩形框的输入调整

在对数据或特征的处理中&#xff0c;为了避免输入图像或特征&#xff0c;经过resize等等操作&#xff0c;改变了目标特征的尺度信息&#xff0c;一般会引入一些操作&#xff0c;比如&#xff1a; 在特征维度&#xff0c;加入SPP&#xff08;空间金字塔池化&#xff09;&#x…

信创(统信)系统上的软件安装及软件使用手册

一.各软件的安装文档 1.达梦数据库在统信系统上的安装 官方手册:https://eco.dameng.com/document/dm/zh-cn/start/install-dm-linux-prepare.html 1.1下载安装包 官网:https://www.dameng.com/list_103.html 点击”服务与合作”--> “下载中心” 这里选择对应的cpu和操作…

字节二面:为什么SpringBoot的 jar 可以直接运行?我说因为内嵌了Tomcat容器,他让我出门左转。。

引言 在传统的Java应用程序开发和部署场景中&#xff0c;开发者往往需要经历一系列复杂的步骤才能将应用成功部署到生产环境。例如&#xff0c;对于基于Servlet规范的Java Web应用&#xff0c;开发完成后通常会被打包成WAR格式&#xff0c;然后部署到像Apache Tomcat、Jetty这…

普联一面4.2面试记录

普联一面4.2面试记录 文章目录 普联一面4.2面试记录1.jdk和jre的区别2.java的容器有哪些3.list set map的区别4.get和post的区别5.哪个更安全6.java哪些集合类是线程安全的7.创建线程有哪几种方式8.线程的状态有哪几种9.线程的run和start的区别10.什么是java序列化11.redis的优…