快排非递归与计数排序

2024-04-22 15:04
文章标签 计数 排序 递归 快排

本文主要是介绍快排非递归与计数排序,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

 

感谢大佬的光临各位,希望和大家一起进步,望得到你的三连,互三支持,一起进步

个人主页:LaNzikinh-CSDN博客

收入专栏:初阶数据结构_LaNzikinh篮子的博客-CSDN博客

文章目录

  • 前言
  • 一.快速排序非递归
  • 二.数据结构栈与内存栈
  • 三.计数排序
  • 四.稳定性
  • 总结

前言

计算机在实现递归时会调用系统的堆栈,这很消耗计算机内存资源,所以采用非递归算法的本质就是手动模拟系统的堆栈调用来降低computer资源的消耗,有时候还会造成栈溢出所以这些厉害的排序我们都要掌握他的非递归,然后在在补充一个计数排序。


一.快速排序非递归

递归改非递归有两种改法,一种是改循环,还有一种就是通过数据结构来转换,我们之前的归并排序非递归就是通过改循环,这次就是通过栈,去搭建

思路:先搭建一个栈,利用栈的特点,后进先出,先入右,在入左若左右存在有序,则有序不排

#include<stdio.h>
void QuickSortNonR(int* a, int n)
{//创建栈,初始化一个栈ST st;stackInit(&st);//先入右,在入左stackPush(&st, n - 1);stackPush(&st,0);//检查栈里还有没有元素while (!stackEmpty(&st)){//拿左边的值,因为是先入右,在入左,所以可以拿到int left = stackTop(&st);stackPop(&st);int right= stackTop(&st);stackPop(&st);//进行一次快排int keyIndex = Quick1sort(a, left, right);//如果中间右边的数小于最右,就继续入栈if (keyIndex + 1 < right){stackPush(&st, right);stackPush(&st, keyIndex + 1);}//同理if (left < keyIndex - 1){stackPush(&st, keyIndex - 1);stackPush(&st, left);}}//最后销毁栈stackDestory(&st);
}

这样利用数据结构搭建,大的减少了栈空间的利用,减少了大量的函数调用,不会栈溢出

二.数据结构栈与内存栈

很多人不理解,这个栈溢出和数据结构里我们写的栈有什么区别,一个是栈区,一个是数据结构线性表,这两个不是一个东西,计算机内存分为四个区域,代码区,静态区,栈区和堆区

 

  1. 代码区:存放函数体的二进制代码,由操作系统管理创建,代码区时共享的,对于频繁被执行的程序,只需要存有一份代码即可;
  2. 静态区:存放全局变量和静态变量以及常量,在程序结束后由操作系统释放;
  3. 栈区:由编译其自动分配释放,存放函数的参数值以及局部变量等;
  4. 堆区:一般由程序员通过 new 开辟空间,进行分配和释放,若程序员不释放,则程序结束时由操作系统回收通俗一点就是,栈区就是函数和函数调用,堆区就是动态开辟,栈区内容小,堆区内容大

所以多次开辟内存是可以的,但是要记得释放,但是多次函数调用会让栈的空间满,之后就溢出了,没有栈的空间了

而数据结构的栈它就是一种运算受限的线性表。限定仅在表尾进行插入和删除操作的线性表,特点是后进先出

所以这两个不是一个东西,不用搞混了

三.计数排序

计数排序顾名思义,就是通过计数来排序的一种排序算法,他和我们之前的算法不一样,计数排序是一种非比较排序算法,适用于整数或有限范围内的非负整数排序,它的基本思想是统计数组中每个元素出现的次数,然后根据出现次数重新构造有序序列。

思路:计数排序使用一个额外的数组,来记录次数,把带排序的数字统计到额外数组中去,最后用额外数组来反映顺序

但是有缺陷,如果我是从100开始的5个数字,101,102,...105,难道我要开辟105个空间吗?

我们之前这种思路是求的绝对映射位置,我们现在要相对的映射位置,这样才能节省空间用

最大的减最小的加1来开辟范围

	int range = max - min + 1;int* count = (int*)malloc(sizeof(int) * range);memset(count, 0, sizeof(int) * range);

开辟范围来计数,用memset来初始化,当然直接用calloc函数也是可以的

总代码思路:求出最大最小值,利用最大与最小值,开辟空间,遍历一遍,统计次数,利用次数得出顺序

#include<stdio.h>
#include<stdlib.h>
void CountSort(int* a, int n)
{//求出最大最小值int max = a[0], min = a[0];for (int i = 0; i < n; i++){if (a[i] > max){max = a[i];}if (a[i] < min){min = a[i];}}//利用最大与最小值,开辟空间int range = max - min + 1;int* count = (int*)malloc(sizeof(int) * range);memset(count, 0, sizeof(int) * range);//遍历一遍,统计次数for (int i = 0; i < n; i++){//注意要减去最小值count[a[i] - min]++;}int j = 0;for (int i = 0; i < range; i++){while (count[i]--){//前面减了现在要加上a[j] = i + min;j++;}}free(count);
}

计数排序的时间复杂度为O(N+range),所以要求的范围越小这个排序效果越好

四.稳定性

定义:假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,ri=rj,且ri在rj之前,而在排序后的序列中,ri仍在rj之前,则称这种排序算法是稳定的;否则称为不稳定的

注意:堆排序、快速排序、希尔排序、直接选择排序不是稳定的排序算法,而基数排序、冒泡排序、直接插入排序、折半插入排序、归并排序是稳定的排序算法。


总结

初级的排序算法就是这些了,还有一些不常用的和一些难的,等学了更多的知识我们再去了解

这篇关于快排非递归与计数排序的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



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

相关文章

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

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

hdu 1285(拓扑排序)

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

《数据结构(C语言版)第二版》第八章-排序(8.3-交换排序、8.4-选择排序)

8.3 交换排序 8.3.1 冒泡排序 【算法特点】 (1) 稳定排序。 (2) 可用于链式存储结构。 (3) 移动记录次数较多,算法平均时间性能比直接插入排序差。当初始记录无序,n较大时, 此算法不宜采用。 #include <stdio.h>#include <stdlib.h>#define MAXSIZE 26typedef int KeyType;typedef char In

【软考】希尔排序算法分析

目录 1. c代码2. 运行截图3. 运行解析 1. c代码 #include <stdio.h>#include <stdlib.h> void shellSort(int data[], int n){// 划分的数组,例如8个数则为[4, 2, 1]int *delta;int k;// i控制delta的轮次int i;// 临时变量,换值int temp;in

学习记录:js算法(二十八):删除排序链表中的重复元素、删除排序链表中的重复元素II

文章目录 删除排序链表中的重复元素我的思路解法一:循环解法二:递归 网上思路 删除排序链表中的重复元素 II我的思路网上思路 总结 删除排序链表中的重复元素 给定一个已排序的链表的头 head , 删除所有重复的元素,使每个元素只出现一次 。返回 已排序的链表 。 图一 图二 示例 1:(图一)输入:head = [1,1,2]输出:[1,2]示例 2:(图

鸡尾酒排序算法

目录 引言 一、概念 二、算法思想 三、图例解释 1.采用冒泡排序:   2.采用鸡尾酒排序:  3.对比总结 四、算法实现  1.代码实现  2.运行结果 3.代码解释   五、总结 引言 鸡尾酒排序(Cocktail Sort),也被称为双向冒泡排序,是一种改进的冒泡排序算法。它在冒泡排序的基础上进行了优化,通过双向遍历来减少排序时间。今天我们将学习如何在C

快速排序(java代码实现)

简介: 1.采用“分治”的思想,对于一组数据,选择一个基准元素,这里选择中间元素mid 2.通过第一轮扫描,比mid小的元素都在mid左边,比mid大的元素都在mid右边 3.然后使用递归排序这两部分,直到序列中所有数据均有序为止。 public class csdnTest {public static void main(String[] args){int[] arr = {3,

PHP实现二叉树遍历(非递归方式,栈模拟实现)

二叉树定义是这样的:一棵非空的二叉树由根结点及左、右子树这三个基本部分组成,根据节点的访问位置不同有三种遍历方式: ① NLR:前序遍历(PreorderTraversal亦称(先序遍历)) ——访问结点的操作发生在遍历其左右子树之前。 ② LNR:中序遍历(InorderTraversal) ——访问结点的操作发生在遍历其左右子树之中(间)。 ③ LRN:后序遍历(PostorderT

O(n)时间内对[0..n^-1]之间的n个数排序

题目 如何在O(n)时间内,对0到n^2-1之间的n个整数进行排序 思路 把整数转换为n进制再排序,每个数有两位,每位的取值范围是[0..n-1],再进行基数排序 代码 #include <iostream>#include <cmath>using namespace std;int n, radix, length_A, digit = 2;void Print(int *A,