【数据结构】计数排序 排序系列所有源代码 复杂度分析(终章)

news2024/11/23 2:58:23

目录

一,计数排序

1,基本思想

2,思路实现

3,计数排序的特性总结:

二,排序算法复杂度及稳定性分析

三,排序系列所有源代码

Sort.h

Sort.c

Stack.h

Stack.c


一,计数排序

计数排序也叫非比较排序;

1,基本思想

计数排序又称为鸽巢原理,是对哈希直接定址法的变形应用

操作步骤

1,统计相同元素出现次数

2,根据统计的结果将序列回收到原来的序列中

图解原理:

对这样一个不需要比较的排序就完成了;

2,思路实现

// 计数排序
void CountSort(int* arr, int n)
{
	int i = 0;
	int max = arr[0], min = arr[0];
	//找最大,最小值
	for (i = 0; i < n; i++)
	{
		if (arr[i] > max)
		{
			max = arr[i];
		}
		if (arr[i] < min)
		{
			min = arr[i];
		}
	}
	//空间大小
	int sum = max - min + 1;
	//开辟空间并且使元素值都为0
	int* arr1 = (int*)calloc(sum, sizeof(int));
	//给新数组赋值
	for (i = 0; i < n; i++)
	{
		arr1[arr[i] - min]++;
	}
	int j = 0;
	//回收到原来的序列中
	for (i = 0; i < sum; i++)
	{
		while (arr1[i]--)
		{
			arr[j++] = i + min;
		}
	}
}

然后我们运行测试一下:

可以看到是有序的,选择排序就 OK 了;

3,计数排序的特性总结:

1, 计数排序在数据范围集中时,效率很高,但是适用范围及场景有限

2.,时间复杂度:O(N+K)

3, 空间复杂度:O(N)

4, 稳定性:稳定

二,排序算法复杂度及稳定性分析

排序方法平均情况最好情况最坏情况辅助空间稳定性
冒泡排序O(N^2)O(N)O(N^2)O(1)稳定
选择排序O(N^2)    O(N^2) O(N^2) O(1)不稳定
直接插入排序O(N^2) O(N)O(N^2)O(1)稳定
希尔排序O(NlongN)~O(N^2)O(N^1.3)O(N^2)O(1)不稳定
堆排序O(NlongN)O(NlongN)O(NlongN)O(1)不稳定
归并排序O(NlongN)O(NlongN)O(NlongN)O(N)稳定
快速排序O(NlongN)O(NlongN)O(N^2)O(N)不稳定
计数排序O(N+K)O(N+K)O(N+K)O(K)稳定

三,排序系列所有源代码

Sort.h

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#include"Stack.h"

//打印
void PrintSort(int* arr, int n);

//插入排序
void InsertSort(int* arr, int n);

//希尔排序
void HillSort(int* arr, int n);

//选择排序
void SeleSort(int* arr, int n);

//堆排序
void HeapSort(int* arr, int n);
//向下调整
void DownAdjust(int* arr, int n, int i);

冒泡排序
//void BubblSort(int* arr, int n);

//快速排序
void QuickSort(int* arr, int begin, int end);
//三数取中
int middle(int* arr, int left, int right);
//快慢指针法
int PartSort3(int* arr, int left, int right);
//挖坑法
int PartSort2(int* arr, int left, int right);
//霍尔排序
int PartSort1(int* arr, int left, int right);
//快速排序(非递归)
void QuickNon(int* arr, int begin, int end);

//归并排序
void MergerSort(int* arr, int begin, int end);

//归并排序(非递归)
void MergerSortNon(int* arr, int begin, int end);


// 计数排序
void CountSort(int* arr, int n);

Sort.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"Sort.h"

//打印
void PrintSort(int* arr, int n)
{
	int i = 0;
	for (i = 0; i < n; i++)
	{
		printf("%d ", arr[i]);
	}
}

//交换
void Swap(int* a, int* b)
{
	int tmp = *a;
	*a = *b;
	*b = tmp;
}

//插入排序
void InsertSort(int* arr, int n)
{
	int i = 0;
	for (i = 0; i < n-1; i++)
	{
		int end = i;
		int tmp = arr[end + 1];
		while (end >= 0)
		{
			if (arr[end] >= tmp)
			{
				//交换
				Swap(&arr[end], &arr[end+1]);
				end--;
			}
			else
			{
				break;
			}
		}
		arr[end + 1] = tmp;
	}
}

//希尔排序
void HillSort(int* arr, int n)
{
	int gap = n;
	int i = 0;
	while (gap > 1)
	{
		gap = gap / 2;
		for (i = 0; i < n-gap; i++)
		{
			int end = i;
			int tmp = arr[end + gap];
			while (end >= 0)
			{
				if (arr[end] >= tmp)
				{
					//交换
					Swap(&arr[end], &arr[end + gap]);
					end -= gap;
				}
				else
				{
					break;
				}
			}
			arr[end + gap] = tmp;
		}
	}
}

//选择排序
void SeleSort(int* arr, int n)
{
	int begin = 0, end = n - 1;
	while (begin < end)
	{
		int maxi = begin, mini = begin;
		for (int i = begin; i <= end; i++)
		{
			if (arr[i] > arr[maxi])
			{
				maxi = i;
			}
			if (arr[i] < arr[mini])
			{
				mini = i;
			}
		}
		Swap(&arr[begin], &arr[mini]);
		// 如果maxi和begin重叠,修正一下即可
		if (begin == maxi)
		{
			maxi = mini;
		}
		Swap(&arr[end], &arr[maxi]);
		++begin;
		--end;
	}
}

//向下调整
void DownAdjust(int* arr, int n, int i)
{
	int perent = i;
	int child = perent* 2 + 1;
	while (child<n)
	{
		if (child+1<n && arr[child + 1] > arr[child])
		{
			child++;
		}
		if (arr[perent] < arr[child])
		{
			//交换
			Swap(&arr[perent], &arr[child]);
			perent = child;
			child = perent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}

//堆排序
void HeapSort(int* arr, int n)
{
	//建堆
	int i = 0;
	for (i = (n - 1 - 1) / 2; i >= 0; i--)
	{
		//向下调整
		DownAdjust(arr, n, i);
	}
	//交换,删除排序法
	while (n > 1)
	{
		//交换
		Swap(&arr[0], &arr[n - 1]);
		n--;
		//向下调整
		DownAdjust(arr, n, 0);
	}
}

//三数取中
int middle(int* arr, int left, int right)
{
	//int mid = (left +right)/ 2;
	//随机数取中
	int mid = left + (rand() % (right - left));
	if (arr[left] < arr[mid])
	{
		if (arr[mid] < arr[right])
		{
			return mid;
		}
		if (arr[left] < arr[right])
		{
			return right;
		}
		else
		{
			return left;
		}
	}
	//arr[mid]<=arr[left]
	else
	{
		if (arr[mid] > arr[right])
		{
			return mid;
		}
		if (arr[left] > arr[right])
		{
			return right;
		}
		else
		{
			return left;
		}
	}
}

//霍尔排序
int PartSort1(int* arr, int left, int right)
{
	//三数取中
	int ret = middle(arr, left, right);
	Swap(&arr[left], &arr[ret]);
	int keyi = left;
	while (left < right)
	{
		//右边先走
		while (left<right && arr[right]>=arr[keyi])
		{
			right--;
		}
		//左边后走
		while (left < right && arr[left] <= arr[keyi])
		{
			left++;
		}
		//交换
		Swap(&arr[left], &arr[right]);
	}
	Swap(&arr[left], &arr[keyi]);
	return left;
}

//挖坑法
int PartSort2(int* arr, int left, int right)
{
	//三数取中
	int ret = middle(arr, left, right);
	Swap(&arr[left], &arr[ret]);
	int key = arr[left];
	int hole = left;
	while (left < right)
	{
		while (left < right && arr[right] >= key)
		{
			right--;
		}
		arr[hole] = arr[right];
		hole = right;
		while (left < right && arr[left] <= key)
		{
			left++;
		}
		arr[hole] = arr[left];
		hole = left;
	}
	arr[hole] = key;
	return hole;
}

//前后指针法
int PartSort3(int* arr, int left, int right)
{
	//三数取中
	int ret = middle(arr, left, right);
	Swap(&arr[left], &arr[ret]);
	int keyi = left;
	int slow = left, fast = left+1;
	while (fast<=right)
	{
		if (arr[fast] < arr[keyi] && ++slow!=fast)
		{
			//交换
			Swap(&arr[fast], &arr[slow]);
		}
		fast++;
	}
	Swap(&arr[slow], &arr[keyi]);
	return slow;
}

//插入排序(改造版)
void InsertSort1(int* arr, int left, int right)
{
	int i = 0;
	for (i = left; i < right; i++)
	{
		int end = i;
		int tmp = arr[end + 1];
		while (end >= 0)
		{
			if (arr[end] >= tmp)
			{
				//交换
				Swap(&arr[end], &arr[end + 1]);
				end--;
			}
			else
			{
				break;
			}
		}
		arr[end + 1] = tmp;
	}
}

//快速排序
void QuickSort(int* arr, int begin, int end)
{
	srand(time(0));
	if (begin >= end)
	{
		return NULL;
	}
	if (end - begin <10)
	{
		InsertSort1(arr,begin,end);
	}
	else
	{
		int keyi = PartSort1(arr, begin, end);
		//排序[begin,keyi) & [keyi+1,end]
		QuickSort(arr, begin, keyi);
		QuickSort(arr, keyi + 1, end);
	}
}

//快速排序(非递归)
void QuickNon(int* arr, int begin, int end)
{
	srand(time(0));
	ST ps;
	//初始化
	STInit(&ps);
	if (begin >= end)
	{
		return;
	}
	//插入
	STPush(&ps, end);
	STPush(&ps, begin);
	//栈不为空就进去
	while (!STEmpty(&ps))
	{
		int left = STInsert(&ps);//栈顶元素
		STPop(&ps);//删除
		int right = STInsert(&ps);
		STPop(&ps);

		int keyi = PartSort1(arr, left, right);
		//排序[left,keyi-1] & [keyi+1,right]
		if (keyi + 1 < right)
		{
			//插入
			STPush(&ps, right);
			STPush(&ps, keyi + 1);
		}
		if (left < keyi - 1)
		{
			//插入
			STPush(&ps, keyi - 1);
			STPush(&ps, left);
		}
	}
	//销毁
	STDestroy(&ps);
}

//归并
void Merger(int* arr, int* tmp,int begin,int end)
{
	int mid = (begin + end) / 2;
	if (begin == end)
	{
		return;
	}
	//排序【begin,mid】& 【mid+1,end】
	Merger(arr, tmp, begin,mid);
	Merger(arr, tmp, mid+1, end);

	int begin1 = begin, end1 = mid;
	int begin2 = mid + 1, end2 = end;
	int i = 0;
	while (begin1 <= end1 && begin2 <= end2)
	{
		if (arr[begin1] < arr[begin2])
		{
			tmp[i++] = arr[begin1++];
		}
		else
		{
			tmp[i++] = arr[begin2++];
		}
	}
	while(begin1 <= end1)
	{
		tmp[i++] = arr[begin1++];
	}
	while (begin2 <= end2)
	{
		tmp[i++] = arr[begin2++];
	}
	//进行拷贝
	memcpy(arr + begin, tmp, (end - begin+1)*sizeof(int));
}

//归并排序
void MergerSort(int* arr, int begin, int end)
{
	if (begin >= end)
	{
		return;
	}
	//开辟同等大小数组
	int* tmp = (int*)malloc((end - begin + 1)*sizeof(int));
	//归并
	Merger(arr, tmp, begin, end);
	free(tmp);
	tmp = NULL;
}

//归并排序(非递归)
void MergerSortNon(int* arr, int begin, int end)
{
	if (begin >= end)
	{
		return;
	}
	//开辟同等大小数组
	int* tmp = (int*)malloc((end - begin + 1) * sizeof(int));
	int gap = 1;
	int j = 0;
	while (gap < end)
	{
		for (j = 0; j < end; j += 2 * gap)
		{
			int begin1 = j, end1 = begin1+gap-1;
			int begin2 =end1+1, end2 = begin2+gap-1;
			int i = 0;
			//处理边界问题
			if (end1 >= end)
			{
				break;
			}
			if (end2 >end)
			{
				end2 = end;
			}
			while (begin1 <= end1 && begin2 <= end2)
			{
				if (arr[begin1] < arr[begin2])
				{
					tmp[i++] = arr[begin1++];
				}
				else
				{
					tmp[i++] = arr[begin2++];
				}
			}
			while (begin1 <= end1)
			{
				tmp[i++] = arr[begin1++];
			}
			while (begin2 <= end2)
			{
				tmp[i++] = arr[begin2++];
			}
			//进行拷贝
			memcpy(arr + j, tmp, (end2 - j+ 1) * sizeof(int));
		}
		gap *= 2;
	}
	free(tmp);
	tmp = NULL;
}

// 计数排序
void CountSort(int* arr, int n)
{
	int i = 0;
	int max = arr[0], min = arr[0];
	//找最大,最小值
	for (i = 0; i < n; i++)
	{
		if (arr[i] > max)
		{
			max = arr[i];
		}
		if (arr[i] < min)
		{
			min = arr[i];
		}
	}
	//空间大小
	int sum = max - min + 1;
	//开辟空间并且使元素值都为0
	int* arr1 = (int*)calloc(sum, sizeof(int));
	//给新数组赋值
	for (i = 0; i < n; i++)
	{
		arr1[arr[i] - min]++;
	}
	int j = 0;
	//回收到原来的序列中
	for (i = 0; i < sum; i++)
	{
		while (arr1[i]--)
		{
			arr[j++] = i + min;
		}
	}
}

Stack.h

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>

typedef int STDataType;
typedef struct StackTop
{
	STDataType* a;
	int top;
	int capacity;
}ST;

//初始化
void STInit(ST* ps);
//销毁
void STDestroy(ST* ps);
//插入
void STPush(ST* ps, STDataType x);
//删除
void STPop(ST* ps);
//返回栈顶
STDataType STInsert(ST* ps);
//数量
int STSize(ST* ps);
//判断是否为空
int STEmpty(ST* ps);

Stack.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"Stack.h"

//初始化
void STInit(ST* ps)
{
	assert(ps);
	ps->a = NULL;
	ps->top = ps->capacity = 0;
}
//销毁
void STDestroy(ST* ps)
{
	assert(ps);
	free(ps->a);
	ps->a = NULL;
	ps->top = ps->capacity = 0;
}
//插入
void STPush(ST* ps, STDataType x)
{
	assert(ps);
	if (ps->top == ps->capacity)
	{
		ps->capacity = ps->top == 0 ? 4 : ps->capacity*2;
		ps->a = (STDataType*)realloc(ps->a,sizeof(STDataType)*ps->capacity);
	}
	ps->a[ps->top] = x;
	ps->top++;
}
//删除
void STPop(ST* ps)
{
	assert(ps);
	assert(ps->top > 0);
	ps->top--;
}
//返回栈顶
STDataType STInsert(ST* ps)
{
	assert(ps);
	assert(ps->top > 0);
	return ps->a[ps->top-1];
}
//数量
int STSize(ST* ps)
{
	assert(ps);
	return ps->top;
}
//判断是否为空
int STEmpty(ST* ps)
{
	assert(ps);
	if (ps->top == 0)
	{
		return 1;
	}
	else
	{
		return 0;
	}
}

同志们!排序的知识就到这里了!

后面博主会陆续更新;

如有不足之处欢迎来补充交流!

完结。。

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

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

相关文章

origin作图上下对开,修改颜色

一般上下对开后默认两幅图颜色相同&#xff0c;如果要修改成不同的颜。双击空白处&#xff0c;在图层栏里取消勾选绘图属性。

基于Java的在线文档编辑管理系统设计与实现(源码+lw+部署文档+讲解等)

文章目录 前言具体实现截图论文参考详细视频演示为什么选择我自己的网站自己的小程序&#xff08;小蔡coding&#xff09;有保障的售后福利 代码参考源码获取 前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计划导师、全栈领域优质创作…

BRISK: Binary Robust Invariant Scalable Keypoints全文翻译

pdf链接&#xff1a;https://pan.baidu.com/s/1gFAYMPJStl4cF0CswY9cMQ 提取码&#xff1a;yyds 摘要 从图像中有效和高效地生成关键点是文献中深入研究的问题&#xff0c;并形成了许多计算机视觉应用的基础。该领域的领导者是SIFT和SURF算法&#xff0c;它们在各种图像转换下…

图文结合丨Prometheus+Grafana+GreatSQL性能监控系统搭建指南(上)

一、环境介绍 本文环境&#xff0c;以及本文所采用数据库为GreatSQL 8.0.32-24 $ cat /etc/system-release Red Hat Enterprise Linux Server release 7.9 (Maipo) $ uname -a Linux gip 3.10.0-1160.el7.x86_64 #1 SMP Tue Aug 18 14:50:17 EDT 2020 x86_64 x86_64 x86_64 G…

Xcode 15下,包含个推的项目运行时崩溃的处理办法

升级到Xcode15后&#xff0c;部分包含个推的项目在iOS17以下的系统版本运行时&#xff0c;会出现崩溃&#xff0c;由于崩溃在个推Framework内部&#xff0c;无法定位到具体代码&#xff0c;经过和个推官方沟通&#xff0c;确认问题是项目支持的最低版本问题。 需要将项目的最低…

关于 打开虚拟机出现“...由VMware产品创建,但该产品与此版VMwareWorkstateion不兼容,因此无法使用” 的解决方法

文为原创文章&#xff0c;转载请注明原文出处 本文章博客地址&#xff1a;https://hpzwl.blog.csdn.net/article/details/133678951 红胖子(红模仿)的博文大全&#xff1a;开发技术集合&#xff08;包含Qt实用技术、树莓派、三维、OpenCV、OpenGL、ffmpeg、OSG、单片机、软硬结…

我的创业之路:我为什么选择 Angular 作为前端的开发框架?

我是一名后端开发人员&#xff0c;在上班时我的主要精力集中在搜索和推荐系统的开发和设计工作上&#xff0c;我比较熟悉的语言包括java、golang和python。对于前端技术中typescript、dom、webpack等流行的框架和工具也懂一些。目前&#xff0c;已成为一名自由职业者&#xff0…

通用监控视频web播放方案

业务场景 对接监控视频&#xff0c;实现海康大华等监控摄像头的实时画面在web端播放 方案一&#xff0c;使用 RTSP2webnode.jsffmpeg 说明&#xff1a;需要node环境&#xff0c;原理就是RTSP2web实时调用ffmpeg解码。使用单独html页面部署到服务器后&#xff0c;在项目中需要播…

【stm32芯片设置解惑】:stm32F103系列的开漏输出和推挽输出的区别

场景&#xff1a; 大家在开发stm32的时候&#xff0c;不管是标准库开发还是hal库开发&#xff0c;最基础的就是芯片引脚的某某设置&#xff0c;为什么这么设置&#xff1f;这样设置的好处是什么&#xff1f; 问题描述 — 开漏输出和推挽输出的用处和区别 什么是开漏输出&#x…

Android Studio修改模拟器AVD Manger目录

Android Studio修改虚拟机AVD Manger目录 1、在AS的设备管理器Device Manager中删除原来创建的所有虚拟机&#xff08;Android Virtual Device&#xff09;&#xff1b; 2、新建一个自定义的AVD目录&#xff0c;例如&#xff1a;D:\Android\AndroidAVD 3、在高级系统设置中增加…

ArcGIS中的镶嵌数据集与接缝线

此处介绍一种简单方法&#xff0c;根据生成的轮廓线来做镶嵌数据集的拼接。 一、注意修改相邻影像的上下重叠。注意修改ZOrder和每幅影像的范围。 二、修改新的镶嵌线并且导出影像文件。 三、还有其他方法和注意事项。

c++视觉--通道分离,合并处理,在分离的通道中的ROI感兴趣区域里添加logo图片

c视觉–通道分离&#xff0c;合并处理 通道分离: split()函数 #include <opencv2/opencv.hpp>int main() {// 读取图像cv::Mat image cv::imread("1.jpg");// 检查图像是否成功加载if (image.empty()) {std::cerr << "Error: Could not read the…

MyBatis-Plus演绎:数据权限控制,优雅至极!

&#x1f389;&#x1f389;欢迎来到我的CSDN主页&#xff01;&#x1f389;&#x1f389; &#x1f3c5;我是尘缘&#xff0c;一个在CSDN分享笔记的博主。&#x1f4da;&#x1f4da; &#x1f449;点击这里&#xff0c;就可以查看我的主页啦&#xff01;&#x1f447;&#x…

MFC扩展库BCGControlBar Pro v33.6 - 网格、报表控件功能升级

BCGControlBar库拥有500多个经过全面设计、测试和充分记录的MFC扩展类。 我们的组件可以轻松地集成到您的应用程序中&#xff0c;并为您节省数百个开发和调试时间。 BCGControlBar专业版 v33.6已正式发布了&#xff0c;此版本包含了对图表组件的改进、带隐藏标签的单类功能区栏…

Tomcat 9.0.41在IDEA中乱码问题(IntelliJ IDEA 2022.1.3版本)

1. 乱码的产生是由于编码和解码的编码表不一致引起的。 2. 排查乱码原因 2.1 在idea中启动Tomcat时控制台乱码排查 Tomcat输出日志乱码: 首先查看IDEA控制台&#xff0c;检查发现默认编码是GBK。 再查看Tomcat日志&#xff08;conf文件下logging.properties&#xff09;的默…

GaussDB数据库SQL系列-动态语句

目录 一、前言 二、构建动态SQL语句的基本步骤和注意事项 1、基本步骤 2、主要事项 三、GaussDB中执行动态查询语句&#xff08;示例&#xff09; 1、方式一&#xff1a;EXECUTE IMMEDIATE 2、方式二&#xff1a;OPEN FOR 四、GaussDB中的动态非查询语句&#xff08;示…

MathType2024苹果版数学公式编辑器

MathType苹果2024版是一款备受好评的数学公式编辑器&#xff0c;很多网友在编辑文本时对各种数学符号非常挠头&#xff0c;因为都不太好找&#xff0c;而这款软件能够在各种文档中加入复杂的数学公式和符号&#xff0c;并且与常见的文字处理软件和演示程序配合使用&#xff0c;…

华为云云耀云服务器L实例评测 | 实例使用教学之软件安装:华为云云耀云服务器环境下安装 RabbitMQ 集群

华为云云耀云服务器L实例评测 &#xff5c; 实例使用教学之软件安装&#xff1a;华为云云耀云服务器环境下安装 RabbitMQ 集群 介绍华为云云耀云服务器 华为云云耀云服务器 &#xff08;目前已经全新升级为 华为云云耀云服务器L实例&#xff09; 华为云云耀云服务器是什么华为云…

差分构造法推广:arc166_d

https://atcoder.jp/contests/arc166/tasks/arc166_d 首先肯定是这样子放&#xff1a; 考虑相邻之间的差&#xff0c;本质就是橙色区间减蓝色区间数量 区间数量越少显然越优&#xff0c;所以我们要么保留橙区间&#xff0c;要么保留紫区间&#xff0c;然后两两匹配 #include…

【linux进程(三)】进程有哪些状态?--Linux下常见的三种进程状态

&#x1f493;博主CSDN主页:杭电码农-NEO&#x1f493;   ⏩专栏分类:Linux从入门到精通⏪   &#x1f69a;代码仓库:NEO的学习日记&#x1f69a;   &#x1f339;关注我&#x1faf5;带你学更多操作系统知识   &#x1f51d;&#x1f51d; Linux进程 1. 前言2. 操作系统…