【C语言之排序】-------六大排序

                                                          作者主页:作者主页

                                                          数据结构专栏:数据结构

                                                         创作时间 :2024年5月18日

前言:

今天我们就给大家带来几种排序的讲解,包括冒泡排序,插入排序,希尔排序,选择排序,堆排序,快速排序等等,在讲解之前我先给大家一个网站,用于查看各种排序的动图,这样有助于我们更加清晰的去了解各种排序:排序动图

冒泡排序:

首先我们来讲解一下冒泡排序,这是我们学习编程第一个接触到的排序,也是较为容易理解的一个排序,下面我们来看一下她是如何实现的。

这就是一个大概的冒泡排序的实现过程,其实就是每次都把最大的一个放到最后面,然后下一轮就不需要去管后面最大的那几个。

下面我们来看一下代码应如何实现:

#include<stdio.h>
#include<iostream>
#include<algorithm>
using namespace std;

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

int main() 
{
    int arr[] = { 23,235,13,123523,2342,2342,1,41523,3456,6547,546 };

    int n = sizeof(arr) / sizeof(arr[0]);
    for (int i = 0; i < n - 1; i++)
    {
        for (int j = 0; j < n - 1 - i; j++) 
        {
            if (arr[j] > arr[j + 1]) {  
                swap(arr[j], arr[j + 1]);
            }
        }
    }
    print(arr, n);

    return 0;
}

这就是冒泡排序实现的过程,这里我们就不过多的去说了,还是比较简单的。

时间复杂度:最坏情况:O(N^2)
      最好情况:O(N)
空间复杂度:O(1)

插入排序:

下面我们来看一下插入排序,插入排序的步骤大概是这样的。

1.从第一个元素开始,该元素可以认为已经被排序
2.取下一个元素tem,从已排序的元素序列从后往前扫描
3.如果该元素大于tem,则将该元素移到下一位
4.重复步骤3,直到找到已排序元素中小于等于tem的元素
5.tem插入到该元素的后面,如果已排序所有元素都大于tem,则将tem插入到下标为0的位置
6.重复步骤2~5

图像演示:

这个图像还是比较清晰易懂的,下面我们来看一下代码吧。

void InsertSort(int* arr, int n)
{
    for (int i = 0; i < n - 1; i++)
    {
        int end = i;
        int temp = arr[end + 1];
        while (end >= 0)
        {
            if (temp < arr[end])
            {
                arr[end + 1] = arr[end];
                end--;
            }
            else
            {
                break;
            }
        }
        arr[end + 1] = temp;
    }
}




插入排序其实就是我们从第一个开始,然后我们用tmp记录要被插入的元素,然后一直和前面的作比较,如果大于前面的,那么就结束,如果小于前面的,我们就令end+1的位置等于end,然后end--就可以了,最后再将temp放到不符合这个条件的位置,就完成了一轮插入排序,后面也是这样循环进行的。

时间复杂度:最坏情况下为O(N*N),此时待排序列为逆序,或者说接近逆序
      最好情况下为O(N),此时待排序列为升序,或者说接近升序。
空间复杂度:O(1)

希尔排序:

下面我们来讲一下希尔排序,这个排序其实是比较难懂的,但他的时间复杂度也是较为小的,下面我们看一下如何实现这个排序:

大致步骤:

1.先选定一个小于N的整数gap作为第一增量,然后将所有距离为gap的元素分在同一组,并对每一组的元素进行直接插入排序。然后再取一个比第一增量小的整数作为第二增量,重复上述操作…
2.当增量的大小减到1时,就相当于整个序列被分到一组,进行一次直接插入排序,排序完成。

代码:

void ShellSort(int* arr, int n)
{
    int gap = n;
    while (gap > 1)
    {
        gap /= 2;
        for (int i = 0; i < n - gap; i++)
        {
            int end = i;
            int temp = arr[end + gap];
            while (end >= 0)
            {
                if (temp < arr[end])
                {
                    arr[end + gap] = arr[end];
                    end -= gap;
                }
                else
                {
                    break;
                }
            }
            arr[end + gap] = temp;
        }
    }
}

时间复杂度平均:O(N^1.3)
空间复杂度:O(1)

选择排序:

思路:

每次从待排序列中选出一个最小值,然后放在序列的起始位置,直到全部待排数据排完即可。
实际上,我们可以一趟选出两个值,一个最大值一个最小值,然后将其放在序列开头和末尾,这样可以使选择排序的效率快一倍。

下面我们来看一下动图:

这里我们图片仅供参考,因为我们要用两个下标,一个最大值,一个最小值,以此来提高效率。

void SelectSort(int* arr, int n)
{
    int begin = 0, end = n - 1;
    while (begin < end)
    {
        int max = begin, min = begin;
        for (int i = begin; i <=end; i++)
        {
            if (arr[i] < arr[min])
            {
                min = i;
            }
            if (arr[i] > arr[max])
            {
                max = i;
            }
        }
        swap(arr[begin], arr[min]);
        //为防止最大值在begin位置被换走,我们还要加个判断
        if (begin == max)
        {
            max = min;
        }
        swap(arr[max], arr[end]);
        ++begin;
        --end;
    }
}

 时间复杂度:最坏情况:O(N^2)
      最好情况:O(N^2)
空间复杂度:O(1)

堆排序:

想要更好的了解堆排可以查看这篇博文:堆

在学习堆排之前,首先我们要知道社么是堆,堆其实就是一个二叉树,然后有大根堆和小根堆,然后他们分别长这样:

然后我们这里排序的思路就是,比如说大根堆,他的最大值一定在堆顶,然后我们就让他和最后一个数交换,然后不再去管他,再得到一个新的大根堆,继续进行这个操作,这样是不是就实现了呢。

然后这里我们首先还要建堆,然后我们升序的话就建大堆,直接把最大的放在最后一个,然后就不把他看作堆里的数据了。反之,降序就建小堆。下面我们先来看一下如何建堆。

//向上调整(小根堆)
void AdjustUp(HPDataType* a, int child)
{
	int parent = (child - 1) / 2;
	while (child>0)
	{
		//if (a[child] < a[parent])//小于就换就相当于建小堆
		if (a[child] > a[parent])//大于就换就会变成大堆
		{
			std::swap(a[child], a[parent]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}
	}
}



//建堆
void HPPush(HP* php, HPDataType x)
{
	assert(php);

	if (php->size == php->capacity)
	{
		int newcapacity = php->capacity == 0 ? 4 : php->capacity * 2;
		HPDataType* tmp = (HPDataType*)realloc(php->a,sizeof(HPDataType) * newcapacity);
		if (tmp == NULL)
		{
			perror("realloc failed!!!");
			return;
		}
		php->a = tmp;
		php->capacity = newcapacity;
	}

	php->a[php->size++] = x;

	//向上调整
	AdjustUp(php->a, php->size - 1);
}

这就是c语言种建堆的一个过程,还是比较麻烦的。

然后有了堆之后,排序就比较简单了。

void HeapSort(int* a, int n)
{
	//首先建堆
	//升序:建大堆
	//降序:建小堆
	/*for (int i=0;i<n;i++)
	{
		AdjustUp(a, i);
	}*/
	//            求最后一个节点的父亲
	for (int i = (n - 1 - 1) / 2; i >= 0; i--)
	{
		AdjustDown(a, n, i);
	}

	int end = n - 1;
	///这里>0即可,因为=0时只剩下最后一个,就不再需要继续进行了
	while (end>0)//思路就是:比如我们升序排序,那么我们就利用大根堆,每次都将最大的那个数放在最顶上,然后将它和最后一个交换,然后让整体的大小--,那么最后一个就不再会受影响
	{
		std::swap(a[0], a[end]);
		AdjustDown(a, end, 0);
		--end;
	}


}

堆排最好的最坏的时间复杂度均为O(n*logn)

空间复杂度为O(1)

快速排序:

快排还是这几个排序种比较麻烦的有一个,但其实也是比较好用的一个

我们这里将会用三种方法来实现这个快速排序:

hoare版本(左右指针法):

思路:
1、选出一个key,一般是最左边或是最右边的。
2、定义一个begin和一个end,begin从左向右走,end从右向左走。(需要注意的是:若选择最左边的数据作为key,则需要end先走;若选择最右边的数据作为key,则需要bengin先走)。
3、在走的过程中,若end遇到小于key的数,则停下,begin开始走,直到begin遇到一个大于key的数时,将begin和right的内容交换,end再次开始走,如此进行下去,直到begin和end最终相遇,此时将相遇点的内容与key交换即可。(选取最左边的值作为key)
4.此时key的左边都是小于key的数,key的右边都是大于key的数
5.将key的左序列和右序列再次进行这种单趟排序,如此反复操作下去,直到左右序列只有一个数据,或是左右序列不存在时,便停止操作,此时此部分已有序

//快速排序   hoare版本(左右指针法)
void QuickSort(int* arr, int begin, int end)
{
	//只有一个数或区间不存在
	if (begin >= end)
		return;
	int left = begin;
	int right = end;
	//选左边为key
	int keyi = begin;
	while (begin < end)
	{
		//右边选小   等号防止和key值相等    防止顺序begin和end越界
		while (arr[end] >= arr[keyi] && begin < end)
		{
			--end;
		}
		//左边选大
		while (arr[begin] <= arr[keyi] && begin < end)
		{
			++begin;
		}
		//小的换到右边,大的换到左边
		swap(&arr[begin], &arr[end]);
	}
	swap(&arr[keyi], &arr[end]);
	keyi = end;
	//[left,keyi-1]keyi[keyi+1,right]
	QuickSort(arr, left, keyi - 1);
	QuickSort(arr,keyi + 1,right);
}

挖坑法:

递归:

挖坑法思路与hoare版本(左右指针法)思路类似
1.选出一个数据(一般是最左边或是最右边的)存放在key变量中,在该数据位置形成一个坑
2、还是定义一个L和一个R,L从左向右走,R从右向左走。(若在最左边挖坑,则需要R先走;若在最右边挖坑,则需要L先走)

//快速排序法  挖坑法
void QuickSort1(int* arr, int begin, int end)
{
	if (begin >= end)
		return;
	int left = begin,right = end;
	int key = arr[begin];
	while (begin < end)
	{
		//找小
		while (arr[end] >= key && begin < end)
		{
			--end;
		}
		//小的放到左边的坑里
		arr[begin] = arr[end];
		//找大
		while (arr[begin] <= key && begin < end)
		{
			++begin;
		}
		//大的放到右边的坑里
		arr[end] = arr[begin];
	}
	arr[begin] = key;
	int keyi = begin;
	//[left,keyi-1]keyi[keyi+1,right]
	QuickSort1(arr, left, keyi - 1);
	QuickSort1(arr, keyi + 1, right);
}
非递归:
//单趟排
int PartSort(int* arr, int begin, int end)
{
	int key = arr[begin];
	while (begin < end)
	{
		while (key <= arr[end] && begin < end)
		{
			--end;
		}
		arr[begin] = arr[end];
		while (key >= arr[begin] && begin < end)
		{
			++begin;
		}
		arr[end] = arr[begin];
	}
	arr[begin] = key;
	int meeti = begin;
	return meeti;
}

void QuickSortNoR(int* arr, int begin, int end)
{
	stack<int> st;
	//先入右边
	st.push(end);
	//再入左边
	st.push(begin);
	while (!st.empty())
	{
		//左区间
		int left = st.top();
		st.pop();
		//右区间
		int right = st.top();
		st.pop();
		//中间数
		int mid = PartSort(arr, left, right);
		//当左区间>=mid-1则证明左区间已经排好序了
		if (left < mid - 1)
		{
			st.push(mid - 1);
			st.push(left);
		}
		//当mid+1>=右区间则证明右区间已经排好序
		if (right > mid + 1)
		{
			st.push(right);
			st.push(mid + 1);
		}
	}
}

前后指针法:

思路:
1、选出一个key,一般是最左边或是最右边的。
2、起始时,prev指针指向序列开头,cur指针指向prev+1。
3、若cur指向的内容小于key,则prev先向后移动一位,然后交换prev和cur指针指向的内容,然后cur指针++;若cur指向的内容大于key,则cur指针直接++。如此进行下去,直到cur到达end位置,此时将key和++prev指针指向的内容交换即可。

经过一次单趟排序,最终也能使得key左边的数据全部都小于key,key右边的数据全部都大于key。

然后也还是将key的左序列和右序列再次进行这种单趟排序,如此反复操作下去,直到左右序列只有一个数据,或是左右序列不存在时,便停止操作

//快速排序法  前后指针版本
void QuickSort2(int* arr, int begin, int end)
{
	if (begin >= end)
		return;
	int cur = begin, prev = begin - 1;
	int keyi = end;
	while (cur != keyi)
	{
		if (arr[cur] < arr[keyi] && ++prev != cur)
		{
			swap(&arr[cur], &arr[prev]);
		}
		++cur;
	}
	swap(&arr[++prev],&arr[keyi]);
	keyi = prev;
	//[begin,keyi -1]keyi[keyi+1,end]
	QuickSort2(arr, begin, keyi - 1);
	QuickSort2(arr, keyi + 1, end);

}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

计算机网络ppt和课后题总结(下)

常用端口总结 计算机网络中&#xff0c;端口是TCP/IP协议的一部分&#xff0c;用于标识运行在同一台计算机上的不同服务。端口号是一个16位的数字&#xff0c;范围从0到65535。通常&#xff0c;0到1023的端口被称为“熟知端口”或“系统端口”&#xff0c;它们被保留给一些标准…

【Python数据类型的奥秘】:构建程序基石,驾驭信息之海

文章目录 &#x1f680;Python数据类型&#x1f308;1. 基本概念⭐2. 转化&#x1f44a;3. 数值运算&#x1f4a5;4. 数值运算扩展(math库常用函数) &#x1f680;Python数据类型 &#x1f308;1. 基本概念 整数&#xff08;int&#xff09;&#xff1a;整数是没有小数部分的数…

保研面试408复习 8——计算机网络(浏览器http)、离散数学(平面图)、操作系统、数据结构

文章目录 一、计算机网络1、从在浏览器输入网址到页面显示的过程1. 输入网址2. DNS 解析3. 建立TCP连接4. 发送HTTP请求5. 服务器处理请求并响应6. 浏览器处理响应7. 页面渲染 二、离散数学一、平面图1、平面图性质2、Kuratowski定理 三、操作系统四、数据结构 一、计算机网络 …

Unity3d使用3D WebView for Windows and macOS打开全景网页(720云)操作问题记录

问题描述 使用Unity3d内嵌网页的形式打开720云中的全景图这个功能&#xff0c;使用的是3D WebView for Windows and macOS插件&#xff0c;720云的全景图在浏览器上的操作是滑动鼠标滚轮推远/拉近全景图&#xff0c;鼠标左键拖拽网页可以旋转全景图内容。网页的打开过程是正常…

CSS函数:scale、scale3d函数的使用

CSS函数scale()主要是为了实现元素的放大和缩小效果&#xff0c;使用的是元素的变换效果。使用的是元素的转换属性&#xff1a;transform的&#xff0c;该函数可以实现指定X轴和Y轴的放大、缩小效果。除此之外&#xff0c;我们还可以通过如下两种方式实现指定方向的转换&#x…

C++结合OpenCV进行图像处理与分类

⭐️我叫忆_恒心&#xff0c;一名喜欢书写博客的在读研究生&#x1f468;‍&#x1f393;。 如果觉得本文能帮到您&#xff0c;麻烦点个赞&#x1f44d;呗&#xff01; 近期会不断在专栏里进行更新讲解博客~~~ 有什么问题的小伙伴 欢迎留言提问欧&#xff0c;喜欢的小伙伴给个三…

选择排序-Java版本

选择排序 算法的思想&#xff1a;java模拟 算法的思想&#xff1a; 每遍历一次就找一个最小的数 *外层 一共遍历 length-1次 总遍历次数符合等差数列 时间复杂度为O(n^2)内部查找 并 返回 数值 和 下标 java模拟 public static void selectSort(int[] arr) {for(int i 0;i<…

Flask 学习笔记 总结

python基础 服务端开发编程 第一个是赋值运算&#xff0c;第二是乘法&#xff0c;最后是一个是幂&#xff08;即a2&#xff09; a 2 a * 2 a ** 2 Python支持多重赋值&#xff1a; a, b, c 2, 3, 4 这句命令相当于&#xff1a; a 2 b 3 c 4 Python支持对字符串的灵活…

网络编程(一)

网络编程&#xff08;一&#xff09; 网络基础网络体系结构**OSI的7层模型**&#xff1a;&#xff08;理想化&#xff09;**每层的功能** **TCP/IP的4层模型**&#xff1a;&#xff08;在使用&#xff09;常见的协议IP地址IPV4分类A类&#xff08;第1位固定为0&#xff09;B类&…

10个令人惊叹的Python自动化脚本

大家好&#xff0c;Python凭借其简单和通用性&#xff0c;能够为解决每天重复同样的工作提供最佳方案。本文将介绍10个Python自动化脚本&#xff0c;可以帮助自动化完成任务&#xff0c;提高工作效率&#xff0c;它们可以成为项目运行中的便捷工具&#xff0c;可以收藏这些脚本…

conflicting types for 错误问题

操作系统真象还原中&#xff0c;第十一章出现的问题&#xff1a; 怎样编译都会出现一个conflicting types for ’xxx‘的错误 出现这个错误的原因&#xff1a; 头文件声明和定义参数稍有不同 头文件中声明 void Hanlder(const char * buf); 在定义时写作 void Hanlder(char…

C# WPF入门学习主线篇(六)—— TextBox常见属性和事件

欢迎回到C# WPF入门学习系列的第六篇。在前面的文章中&#xff0c;我们探讨了按钮&#xff08;Button&#xff09;的事件处理。今天&#xff0c;我们将继续学习另一个常用的WPF控件——TextBox。本文将介绍 TextBox 的常见属性和事件&#xff0c;并通过示例代码展示如何在实际应…

用这个AI工具,做公众号爆款图文,5分钟一篇10w+,居然这么简单!(附工具教程)

文章首发于公众号&#xff1a;X小鹿AI副业 大家好&#xff0c;我是程序员X小鹿&#xff0c;前互联网大厂程序员&#xff0c;自由职业2年&#xff0c;也一名 AIGC 爱好者&#xff0c;持续分享更多前沿的「AI 工具」和「AI副业玩法」&#xff0c;欢迎一起交流~ 之前X小鹿一直在各…

泵制造5G智能工厂工业物联数字孪生可视化,推进制造业数字化转型

泵制造5G智能工厂工业物联数字孪生可视化&#xff0c;推进制造业数字化转型。泵制造行业&#xff0c;作为工业领域的核心部分&#xff0c;更是急需通过技术创新实现生产流程的智能化和高效化。而5G智能工厂工业物联数字孪生可视化技术的出现&#xff0c;为泵制造业的数字化转型…

代码随想录算法训练营第四十四天 | 01背包问题理论基础、01背包问题滚动数组、416. 分割等和子集

背包问题其实有很多种&#xff0c;01背包是最基础也是最经典的&#xff0c;软工计科学生一定要掌握的。 01背包问题 代码随想录 视频讲解&#xff1a;带你学透0-1背包问题&#xff01;| 关于背包问题&#xff0c;你不清楚的地方&#xff0c;这里都讲了&#xff01;| 动态规划经…

YOLO10:手把手安装教程与使用说明

目录 前言一、YOLO10检测模型二、YOLO安装过程1.新建conda的环境 yolo10安装依赖包测试 总结 前言 v9还没整明白&#xff0c;v10又来了。而且还是打败天下无敌手的存在&#xff0c;连最近很火的RT-DETR都被打败了。那么&#xff0c;笑傲目标检测之林的v10又能持续多久呢&#…

2024第三届全国大学生数据分析大赛,有没有没有思路的朋友?

大家好呀&#xff0c;2024第三届全国大学生数据分析大赛准备开始咯&#xff0c;大家是不是没有思路呀。 本论文可以保证原创&#xff0c;保证高质量。绝不是随便引用一大堆模型和代码复制粘贴进来完全没有应用糊弄人的垃圾半成品论文。 比赛现在还能报名哈&#xff01;6-7才截…

图像背景去除工具:removebg

文章目录 简介面向不同用户价格 简介 removebg&#xff0c;就是remove background&#xff0c;是一款智能图片背景去除工具。 在免费使用时&#xff0c;用到的是本地的CPU。我第一次试用时&#xff0c;图片刚上传之后&#xff0c;电脑的帧率便直线下降&#xff0c;鼠标都拖不…

买视觉检测设备需要多少钱?

随着工业自动化的发展&#xff0c;其应用范围逐步提高&#xff0c;其中母子图像传感器、CMOS和CCD摄像机、DSP、ARM嵌入式技术、图像处理和模式识别技术的快速发展&#xff0c;有效地推动了视觉检测设备的发展。在机器视觉领域中&#xff0c;常见的就是视觉检测、视觉识别、视觉…

Win11中Yolo V10安装过程记录

1. 配置Anaconda环境&#xff1a; conda create -n yolov10 python3.9 conda activate yolov10 pip install -r requirements.txt pip install -e . 这里由于torch2.0.1太慢&#xff0c;单独用pytorch官网安装流程&#xff08;选择支持GPU版本&#xff09;&#xff1a; con…