首页 > 其他分享 >排序(一)插入排序,希尔排序,选择排序,堆排序,冒泡排序

排序(一)插入排序,希尔排序,选择排序,堆排序,冒泡排序

时间:2024-10-23 09:46:37浏览次数:6  
标签:end int 插入排序 冒泡排序 gap sizeof 排序 void

目录

一.排序

1.插入排序

2.希尔排序

3.选择排序

4.堆排序

5.冒泡排序

二.整体代码

1.Sort.h

2.Sort.c

3.test.c


一.排序

1.插入排序

插入排序基本思想:把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中,直到所有的记录插入完为 止,得到一个新的有序序列

实现过程单趟排序:将插入值与前一个数进行比较,如果大就直接插入到后面,如果小,将前一个数后移,继续与之前的比较,小的话插入空的位置,否则继续向前比较

        我们可以将每一个排序分为单趟和整体排序,了解单趟排序,对于整体排序,我们可以将它看作将数据一个一个的向里面插入

void InsertSort(int* a, int n)
{
	assert(a);
	for (int i = 0; i < n - 1; ++i)
	{
		//单趟排序,[0,end]有序,将end + 1向其中插入
		int end = i;
		int tmp = a[end + 1];
		while (end >= 0)
		{
			if (tmp < a[end])
			{
				a[end + 1] = a[end];
				--end;
			}
			else
			{
				break;
			}
		}
		//插入的值比数组内所有值都小
		a[end + 1] = tmp;
	}
}

插入排序特性总结:

1.时间复杂度:O(N^2),顺序时最好,逆序时最坏

2.空间复杂度:O(1)

3.稳定性:稳定

4.当排序的数组越接近有序,算法的时间效率越高

2.希尔排序

我们将希尔排序分为预排序(使数组接近有序),然后直接插入排序

预排序:把间距为gap的值分为一组,进行插入排序

以gap为3为例的图

        当gap的值越大时,前面大的值可以更快的到后面,后面小的值可以更快的到前面,但是gap越大,数组内数据越不接近有序,而当gap为1的,就相当于插入排序

// 希尔排序
void ShellSort(int* a, int n)
{
	//gap>1相当于预排序,让数组接近有序
	int gap = n;
	while (gap > 1)
	{
		gap = gap / 3 + 1;//+1保证了gap最后一次一定为1
		//gap==1相当于直接插入排序
		for (int i = 0; i < n - gap; ++i)
		{
			int end = i;
			int tmp = a[end + gap];
			while (end >= 0)
			{
				if (tmp < a[end])
				{
					a[end + gap] = a[end];
					end -= gap;
				}
				else
				{
					break;
				}
			}
			a[end + gap] = tmp;
		}
	}
}

希尔排序特性总结:

1.希尔排序是对插入排序的优化

2.当gap>1时为预排序,是为了让数组更接近有序,gap==1时数组已经基本有序,插入时就会较快.对于整个过程可以起到优化

3.稳定性:不稳定

4.gap的时间复杂度不好计算,因为gap的值不同

        在下图中我们可以看到对于一组数据插入排序和希尔排序的处理时间

我们可以看到希尔排序的处理速度相比于插入排序提升很大

3.选择排序

        选择排序:我们在begin和end的区间内寻找到最小值和最大值的下标,将最小值和最大值找到分别置于首和尾后,++begin,--end,缩小区间,继续寻找,直到搜索完整个区间

选择排序特性总结:

1.直接选择排序思考非常好理解,但是效率不是很好。实际中很少使用

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

3.空间复杂度:O(1)

4.稳定性:不稳定

我们可以看到选择排序的处理效率与直接插入排序差不多

4.堆排序

        堆排序(Heapsort)是指利用堆积树(堆)这种数据结构所设计的一种排序算法,它是选择排序的一种。它是通过堆来进行选择数据。需要注意的是排升序要建大堆,排降序建小堆

堆排序在前面已经讲解过,在此不多解释

//向下调整
//小堆向下调整前提:左右子树都为小堆
//大堆向下调整前提:左右子树都为大堆
void AdjustDown(int* a, int n, int root)
{
	int parent = root;//将根节点位置给父节点
	int child = 2 * parent + 1;//子节点下标为2*parent+1

	while (child < n)//保证下标不越界
	{
		//找出左右孩子小的那个
		if (child + 1 < n && a[child + 1] > a[child])
		{
			++child;
		}

		//若孩子的值大于父亲则交换,并将孩子下标给父亲,重新计算孩子下标
		if (a[child] > a[parent])
		{
			Swap(&a[child], &a[parent]);
			parent = child;
			child = 2 * parent + 1;
		}
		//如果孩子大,则说明为小堆,不需要向下调整
		else
		{
			break;
		}
	}
}

//堆排序
//1.升序建大堆
//2.降序建小堆
void HeapSort(int* a, int n)
{
	//建堆
	//假设树有N个节点,树高度:logN.时间复杂度:O(N)
	for (int i = (n - 1 - 1) / 2; i >= 0; --i)
	{
		AdjustDown(a, n, i);//向下调整
	}

	int end = n - 1;//最后一个叶的下标
	while (end > 0)
	{
		Swap(&a[0], &a[end]);

		AdjustDown(a, end, 0);
		--end;
	}
}

堆排序特性总结:

1. 堆排序使用堆来选数,效率就高了很多

2. 时间复杂度:O(N*logN)

3. 空间复杂度:O(1)

4. 稳定性:不稳定

5.冒泡排序

        冒泡排序:将键值较大的记录向序列的尾部移动,键值较小的记录向序列的前部移动,前后比较,大的就交换

// 冒泡排序
void BubbleSort(int* a, int n)
{
	assert(a);
	int end = n;
	while (end > 0)
	{
		int exchange = 0;
		for (int i = 1; i < end; ++i)
		{
			if (a[i - 1] > a[i])
			{
				Swap(&a[i - 1], &a[i]);
				exchange = 1;
			}
		}

		//如果一趟冒泡中没有发生交换,则说明已经有序,不需要冒泡
		if (exchange == 0)
		{
			break;
		}
	}
}

冒泡排序特性总结:

1. 冒泡排序是一种非常容易理解的排序

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

3. 空间复杂度:O(1)

4. 稳定性:稳定

二.整体代码

1.Sort.h

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

void PrintArray(int* a, int n);
void Swap(int* p1, int* p2);
void AdjustDown(int* a, int n, int root);
// 插入排序
void InsertSort(int* a, int n);

// 希尔排序
void ShellSort(int* a, int n);

// 选择排序
void SelectSort(int* a, int n);

// 堆排序
void AdjustDown(int* a, int n, int root);
void HeapSort(int* a, int n);

// 冒泡排序
void BubbleSort(int* a, int n);

2.Sort.c

#include "Sort.h"

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

// 插入排序
// 时间复杂度O(N^2),顺序有序时最好,逆序时最坏
//空间复杂度O(1)
void InsertSort(int* a, int n)
{
	assert(a);
	for (int i = 0; i < n - 1; ++i)
	{
		//单趟排序,[0,end]有序,将end + 1向其中插入
		int end = i;
		int tmp = a[end + 1];
		while (end >= 0)
		{
			if (tmp < a[end])
			{
				a[end + 1] = a[end];
				--end;
			}
			else
			{
				break;
			}
		}
		//插入的值比数组内所有值都小
		a[end + 1] = tmp;
	}
}

// 希尔排序
void ShellSort(int* a, int n)
{
	//gap>1相当于预排序,让数组接近有序
	int gap = n;
	while (gap > 1)
	{
		gap = gap / 3 + 1;//+1保证了gap最后一次一定为1
		//gap==1相当于直接插入排序
		for (int i = 0; i < n - gap; ++i)
		{
			int end = i;
			int tmp = a[end + gap];
			while (end >= 0)
			{
				if (tmp < a[end])
				{
					a[end + gap] = a[end];
					end -= gap;
				}
				else
				{
					break;
				}
			}
			a[end + gap] = tmp;
		}
	}
}

void Swap(int* p1, int* p2)
{
	int tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}

// 选择排序
void SelectSort(int* a, int n)
{
	assert(a);

	int begin = 0, end = n - 1;
	while (begin < end)
	{
		//在[bagin,end]之间找出最小数和最大数的下标
		int mini, maxi;
		mini = maxi = begin;
		for (int i = begin + 1; i <= end; ++i)
		{
			if (a[i] > a[maxi])
			{
				maxi = i;
			}

			if (a[i] < a[mini])
			{
				mini = i;
			}
		}
		Swap(&a[begin], &a[mini]);
		//如果maxi和begin位置重叠,maxi需要修正
		if (begin == maxi)
		{
			maxi = mini;
		}
		Swap(&a[end], &a[maxi]);

		++begin;
		--end;
	}
}


//向下调整
//小堆向下调整前提:左右子树都为小堆
//大堆向下调整前提:左右子树都为大堆
void AdjustDown(int* a, int n, int root)
{
	int parent = root;//将根节点位置给父节点
	int child = 2 * parent + 1;//子节点下标为2*parent+1

	while (child < n)//保证下标不越界
	{
		//找出左右孩子小的那个
		if (child + 1 < n && a[child + 1] > a[child])
		{
			++child;
		}

		//若孩子的值大于父亲则交换,并将孩子下标给父亲,重新计算孩子下标
		if (a[child] > a[parent])
		{
			Swap(&a[child], &a[parent]);
			parent = child;
			child = 2 * parent + 1;
		}
		//如果孩子大,则说明为小堆,不需要向下调整
		else
		{
			break;
		}
	}
}

//堆排序
//1.升序建大堆
//2.降序建小堆
void HeapSort(int* a, int n)
{
	//建堆
	//假设树有N个节点,树高度:logN.时间复杂度:O(N)
	for (int i = (n - 1 - 1) / 2; i >= 0; --i)
	{
		AdjustDown(a, n, i);//向下调整
	}

	int end = n - 1;//最后一个叶的下标
	while (end > 0)
	{
		Swap(&a[0], &a[end]);

		AdjustDown(a, end, 0);
		--end;
	}
}

// 冒泡排序
void BubbleSort(int* a, int n)
{
	assert(a);
	int end = n;
	while (end > 0)
	{
		int exchange = 0;
		for (int i = 1; i < end; ++i)
		{
			if (a[i - 1] > a[i])
			{
				Swap(&a[i - 1], &a[i]);
				exchange = 1;
			}
		}

		//如果一趟冒泡中没有发生交换,则说明已经有序,不需要冒泡
		if (exchange == 0)
		{
			break;
		}
	}
}

3.test.c

#include "Sort.h"

// 测试排序的性能对比
void TestOP()
{
	srand(time(0));
	const int N = 10000;
	int* a1 = (int*)malloc(sizeof(int) * N);
	int* a2 = (int*)malloc(sizeof(int) * N);
	int* a3 = (int*)malloc(sizeof(int) * N);
	int* a4 = (int*)malloc(sizeof(int) * N);
	int* a5 = (int*)malloc(sizeof(int) * N);
	
	for (int i = 0; i < N; ++i)
	{
		a1[i] = rand();
		a2[i] = a1[i];
		a3[i] = a1[i];
		a4[i] = a1[i];
		a5[i] = a1[i];
	}

	int begin1 = clock();
	InsertSort(a1, N);
	int end1 = clock();

	int begin2 = clock();
	ShellSort(a2, N);
	int end2 = clock();

	int begin3 = clock();
	SelectSort(a3, N);
	int end3 = clock();

	int begin4 = clock();
	HeapSort(a4, N);
	int end4 = clock();

	int begin5 = clock();
	BubbleSort(a5,  N);
	int end5 = clock();


	printf("InsertSort:%d\n", end1 - begin1);
	printf("ShellSort:%d\n", end2 - begin2);
	printf("SelectSort:%d\n", end3 - begin3);
	printf("HeapSort:%d\n", end4 - begin4);
	printf("BubbleSort:%d\n", end5 - begin5);
	

	free(a1);
	free(a2);
	free(a3);
	free(a4);
	free(a5);
}

void TestInsertSort()
{
	int a[] = { 3,2,4,6,7,9,11,1,0 };
	PrintArray(a, sizeof(a) / sizeof(int));
	InsertSort(a, sizeof(a) / sizeof(int));
	PrintArray(a, sizeof(a) / sizeof(int));
}

void TestShellSort()
{
	int a[] = { 9,8,7,6,5,4,3,2,1};
	PrintArray(a, sizeof(a) / sizeof(int));
	ShellSort(a, sizeof(a) / sizeof(int));
	PrintArray(a, sizeof(a) / sizeof(int));
}

void TestSelectSort()
{
	int a[] = {5,4,7,6,4,2,9,7,8 };
	PrintArray(a, sizeof(a) / sizeof(int));
	SelectSort (a, sizeof(a) / sizeof(int));
	PrintArray(a, sizeof(a) / sizeof(int));
}

void TestHeapSort()
{
	int a[] = { 1,8,4,7,6,3,9,12,6 };
	PrintArray(a, sizeof(a) / sizeof(int));
	HeapSort(a, sizeof(a) / sizeof(int));
	PrintArray(a, sizeof(a) / sizeof(int));
}

void TestBubbleSort()
{
	int a[] = { 7,8,1,14,6,10,2,12,9 };
	PrintArray(a, sizeof(a) / sizeof(int));
	BubbleSort(a, sizeof(a) / sizeof(int));
	PrintArray(a, sizeof(a) / sizeof(int));
}


int main()
{
	TestInsertSort();
	TestShellSort();
	TestSelectSort();
	TestHeapSort();
	TestBubbleSort();
	TestOP();
}

标签:end,int,插入排序,冒泡排序,gap,sizeof,排序,void
From: https://blog.csdn.net/w200514/article/details/143173801

相关文章

  • 冒泡排序
    一、冒泡排序的介绍冒泡排序简单的来说就是将第一个数与第二个数进行比较,如果第一个数大就进行交换,这样比到最后就是这组数中最大数,下面将使用动画演示来展示冒泡排序https://visualgo.net/en/sorting这是一个可视化界面,可以详细的查看相关的排序动画。二、代码进行演示接下......
  • python中的字典排序--sorted()
    字典的排序:在学习python的时候,了解到相比于列表,字典是一个无序的数据结构,一般都不对其进行排序的。但是要想对字典进行排序,是可以通过sorted()函数进行操作的!关于字典的排序,下面从键key和值value进行代码的运行和分析:【先看代码和执行结果,后面会进行详细的解析】#先定义一......
  • 排序算法 —— 快速排序(理论+代码)
    目录1.快速排序的思想2.快速排序的实现hoare版挖坑法前后指针法快排代码汇总3.快速排序的优化三数取中小区间优化三路划分4.快速排序的非递归版本5.快速排序总结1.快速排序的思想快速排序是一种类似于二叉树结构的排序方法。其基本思想为从待排序序列中任取一个......
  • echarts根据数据动态生成饼图的个数,并排序
    动态生成个数functioninitPurchaseContract(){//获取数据的keysletkeys=Object.keys(dataPurchaseContract[0]);lettotalCharts=keys.length-1;//饼图数量//动态计算行数和列数(使布局接近正方形)letcols=3;//列数letrows......
  • 必学排序算法——归并排序
    目录前言一、什么是归并排序二、归并排序步骤三、算法思想四、归并排序复杂度分析五、优化方案六、算法图解七、c++代码模板八、经典例题1.排序数组代码题解2.排序链表代码题解九、结语前言归并排序算法是必须掌握的一种基础算法,在一些比较出名的竞赛acm、蓝桥杯,......
  • 关于如何排序使得最终的答案最优的总结
    关于如何排序使得最终的答案最优的总结例题LuoguP1012CF2024C分析就以先CF2024C来展开,题意是给定\(N\)个二元组,确定一个可行的排列使得最后的序列逆序对个数最少,注意二元组内部不可以交换顺序Solution1详情见“CF980Review”中对这道题的解法,这里不多赘述了。只......
  • mongodb 查询条件,查询逻辑对照表,逻辑运算符,正则表达式匹配查询,排序,分页/巧分页,更新操
    mongodb查询条件,查询逻辑对照表,逻辑运算符,正则表达式匹配查询,排序,分页/巧分页,更新操作符,更新单个/多个文档,删除文档,批量插入,$type操作符,内嵌文档和数组查找修改1.条件查询SQLMQLa=1{a:1}a<>1{a:{$ne:1}}a>1{a:{$gt:1}}a>=1{a:{$gte:1}}a<1{a:{$lt......
  • 大模型驱动的电商个性化搜索结果重排序
    《大模型驱动的电商个性化搜索结果重排序》摘要随着电商行业的快速发展,个性化搜索成为了提升用户体验和提升转化率的关键因素。本文将探讨大模型驱动的电商个性化搜索结果重排序技术,分析大模型在电商搜索中的价值和应用原理。文章将详细介绍大模型的基础知识、电商搜索的......
  • 2023 ICPC Seoul Regional A. Apricot Seeds(Pjudge【NOIP Round #7】冒泡排序)
    题意一个序列,Q次询问一个区间[l,r],进行k轮冒泡后,求子区间[x,y]的和。(N<=1e6,Q<=5e5)冒泡定义为:fori=1ton-1:ifa[i]>a[i+1]:swap(a[i],a[i+1])考场想法:经典转01。110111000111000111111011100011100011111+1011100011100011111+1101100......
  • 基数排序1111
    相对于其他排序,基数排序不是将两个数作比较,通过将他们的个,十,百,千等位存入对应的桶中,桶可以为10个,对应着0-9,还有一点如果数字的最大位不同,那需将其他小于最大位的数前面补0,保持与最大位的数的位一致。进入的顺序和出去的顺序是一致的013021011052062043个0123456021,011052,06......