十大经典排序算法总结(C语言版本)_c语言考研考试哪种排序好用(1)

算法效率详解博客:http://t.csdn.cn/DdQCx

1、冒泡排序

冒泡排序是一种简单的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果它们的顺序错误就把它们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。该排序算法之所以叫冒泡算法就是因为算法会每次把数值中最大的数值“浮动”到数组的最顶端,过程类似水中气泡的浮动。

1.1 算法描述

**1、**比较相邻的元素。如果第一个比第二个大,就交换它们两个;

**2、**对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对,这样在最后的元素应该会是最大的数;

**3、**针对所有的元素重复以上的步骤,除了最后一个;

4、重复步骤1~3,直到排序完成。

1.2 动图演示

1.3 算法实现

//冒泡排序算法
void BubbleSort(int arr[],int numSize)
{
	int temp = 0;        //交换的临时变量
	for(int i = numSize-1; i > 0 ; i--)	// 每次需要排序的长度
	{
		for(int j = 0; j < i; j++)
		{
			if(arr[j] > arr[j+1])    //保证较大的数值放在后面
			{
				temp = arr[j];
				arr[j] = arr[j+1];
				arr[j+1] = temp;
			}
		}
	}
}

算法结果:

**平均时间复杂度:**O(n^2);**空间复杂度:**O(1);**稳定性:**稳定

**适用场景:**冒泡排序思路简单,代码也简单,特别适合小数据的排序。但是,由于算法复杂度较高,在数据量大的时候不适合使用。

2、选择排序

选择排序是一种简单直观的排序算法,它也是一种交换排序算法,和冒泡排序有一定的相似度,可以认为选择排序是冒泡排序的一种改进。核心本质:就是选择出数组中最小/大的数值进行归位。

2.1 算法描述

**1、**在未排序序列中找到最小(大)元素,存放到排序序列的起始位置

**2、**从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。

**3、**重复第二步,直到所有元素均排序完毕。

2.2 动图演示

2.3 算法实现

//选择排序算法
void SelectSort(int arr[],int numSize)
{
	printf("选择排序算法\r\n");
	int temp,min = 0;	
	for(int i = 0; i < numSize; i++)
	{
		min = i;
		//寻找数组最小值
		for(int j = i+1; j < numSize; j++)
		{
			if(arr[min] > arr[j])
			{
				min = j;
			}
		}
		if(min != i)
		{
			temp = arr[i];
			arr[i] = arr[min];
			arr[min] = temp;
		}
	}
}

算法结果 :

**平均时间复杂度:**O(n^2);**空间复杂度:**O(1);**稳定性:**不稳定

**适用场景:**选择排序实现也比较简单,并且由于在各种情况下复杂度波动小,因此一般是优于冒泡排序的。在所有的完全交换排序中,选择排序也是比较不错的一种算法。但是,由于固有的O(n^2)复杂度,选择排序在海量数据面前显得力不从心。因此,它适用于简单数据排序。

3、插入排序

插入排序是一种简单直观的排序算法。它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。

3.1 算法描述

**1、**把待排序的数组分成已排序和未排序两部分,初始的时候把第一个元素认为是已排好序的。
**2、**从第二个元素开始,在已排好序的子数组中寻找到该元素合适的位置并插入该位置。
**3、**重复上述过程直到最后一个元素被插入有序子数组中。

3.2 动图演示

3.3 算法实现

//插入排序算法
void InsertSort(int arr[],int numSize)
{
	for(int i=1; i < numSize; i++)
	{
		int value = arr[i];
		int position=i;
		while(position > 0 && arr[position-1] > value)
		{
			arr[position] = arr[position-1];
			position--;
		}
		arr[position] = value;
	}
}

算法结果:

**平均时间复杂度:**O(n^2);**空间复杂度:**O(1);**稳定性:**稳定

**适用场景:**插入排序由于O(n^2)的复杂度,在数组较大的时候不适用。但是,在数据比较少的时候,是一个不错的选择,一般做为快速排序的扩充。

4、归并排序

归并排序是建立在归并操作上的一种有效的排序算法。该算法是采用分治法的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为2-路归并。

4.1 算法描述

**1、**将序列每相邻两个数字进行归并操作,形成ceil(n/2)个序列,排序后每个序列包含两/一个元素
**2、**若此时序列数不是1个则将上述序列再次归并,形成ceil(n/4)个序列,每个序列包含四/三个元素
**3、**重复步骤2,直到所有元素排序完毕,即序列数为1

4.2 动图演示

特别注意:归并算法的核心是划分归并,下方GIF仅为一种归并排序的措施(与本人写得代码有出入)。

代码的划分与归并流程图;

4.3 算法实现

//归并排序算法
void MergeSort(int arr[],int numSize)
{
	//分配一个辅助数组
	int *tempArr = (int *)malloc(numSize * sizeof(int));
	if(tempArr)
	{
		msort(arr, tempArr, 0, numSize-1);
		free(tempArr);
	}
	else
	{
		printf("ERROR:Failed to allocate memory\r\n");
	}
}

//归并排序算法:1、数组划分
void msort(int arr[],int tempArr[], int left, int right)
{
	//如果只有一个元素,那么就不需要继续划分
	//只有一个元素的区域,本身就是有序的,只需要归并即可
	if(left < right)
	{
		//寻找中间点
		int mid = (left + right)/2;
		//递归划分左半区域
		msort(arr, tempArr, left, mid);
		//递归划分右半区域
		msort(arr, tempArr, mid+1, right);
		//合并已经排序的部分
		merge(arr, tempArr, left, mid, right);
	}
}

//归并排序算法:2、数组归并
void merge(int arr[],int tempArr[],int left,int mid,int right)
{
	//标记左半区第一个未排序的元素
	int l_pos = left;
	//标记右半区第一个未排序的元素
	int r_pos = mid+1;
	//临时数组元素的下标
	int pos = left;
	
	//合并
	while(l_pos <= mid && r_pos <= right)
	{
		if(arr[l_pos] < arr[r_pos])
		{
			tempArr[pos++] = arr[l_pos++];
		}
		else
		{
			tempArr[pos++] = arr[r_pos++];
		}
	}
	
	//合并左半区剩余的元素
	while(l_pos <= mid)
	{
		tempArr[pos++] = arr[l_pos++];
	}
	
	//合并右半区剩余的元素
	while(r_pos <= right)
	{
		tempArr[pos++] = arr[r_pos++];
	}
	
	//把临时数组中合并后的元素复制到原来的数组
	while(left <= right)
	{
		arr[left] = tempArr[left];
		left++;
	}
}

算法结果:

**平均时间复杂度:**O(nlogn);**空间复杂度:**O(n);**稳定性:**稳定

**适用场景:**归并排序在数据量比较大的时候也有较为出色的表现(效率上),但是,其空间复杂度O(n)使得在数据量特别大的时候(例如,1千万数据)几乎不可接受。而且,考虑到有的机器内存本身就比较小,因此,采用归并排序一定要注意。

5、快速排序

快速排序是一个知名度极高的排序算法,其对于大数据的优秀排序性能和相同复杂度算法中相对简单的实现使它注定得到比其他算法更多的宠爱(面试高频的排序算法)。

5.1 算法描述

**1、**从数列中挑出一个元素,称为"基准"(pivot),
**2、**重新排序数列,所有比基准值小的元素摆放在基准前面,所有比基准值大的元素摆在基准后面(相同的数可以到任何一边)。在这个分区结束之后,该基准就处于数列的中间位置。这个称为分区(partition)操作。
**3、**递归地(recursively)把小于基准值元素的子数列和大于基准值元素的子数列排序。

5.2 动图演示

5.3 算法实现

//快速排序算法
void QuickSort(int arr[],int numSize)
{
	qsort(arr,0,numSize);
}

void qsort(int arr[], int low, int high)
{
  if(low >= high)			//两个索引标号相碰
     return;
  int pivot = partition(arr, low, high);      //将数组分为两部分
  qsort(arr, low, pivot-1);                   //递归排序左子数组
  qsort(arr, pivot+1, high);                  //递归排序右子数值
}

int partition(int arr[], int low, int high)
{
  int pivot = arr[low];     //基准
  while (low < high){
     while (low < high && arr[high] >= pivot) --high;
     arr[low]=arr[high];             //交换比基准大的数值到左端
     while (low < high && arr[low] <= pivot) ++low;
     arr[high] = arr[low];           //交换比基准小的数值到右端
  }
  //扫描完成,基准到位
  arr[low] = pivot;
  //返回的是基准的位置
  return low;
}

算法结果:

**平均时间复杂度:**O(nlogn);**空间复杂度:**O(logn);**稳定性:**不稳定

**适用场景:**快速排序在大多数情况下都是适用的,尤其在数据量大的时候性能优越性更加明显。但是在必要的时候,需要考虑下优化以提高其在最坏情况下的性能。

6、希尔排序

**在希尔排序出现之前,计算机界普遍存在“排序算法不可能突破O(N^2)”的观点。**希尔排序是第一个突破O(n2)的排序算法,它是简单插入排序的改进版。希尔排序的提出,主要基于以下两点:

1、插入排序算法在数组基本有序的情况下,可以近似达到O(n)复杂度,效率极高。
2、但插入排序每次只能将数据移动一位,在数组较大且基本无序的情况下性能会迅速恶化。

**希尔排序核心:**插入算法的升级,通过分组迭代的插入排序缩短排序时间

6.1 算法描述

先将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序,具体算法描述:
**1、**选择一个增量序列t1,t2,…,tk,其中ti>tj,tk=1;
**2、**按增量序列个数k,对序列进行 k 趟排序;
**3、**每趟排序,根据对应的增量ti,将待排序列分割成若干长度为m 的子序列,分别对各子表进行直接插入排序。仅增量因子为1 时,整个序列作为一个表来处理,表长度即为整个序列的长度。

6.2 动图演示

6.3 算法实现

//希尔排序
void HillSort(int arr[],int numSize)
{
	//定义增量和步长
	int gap,i,j,k,temp;
	//通过改变组距持续进行插入排序
	for(gap = numSize/2; gap >= 1; gap = gap/2)
	{
		//每个组别进行插入排序
		for(i = 0; i < gap; i++)
		{
			for(j = i + gap; j < numSize; j += gap)		//每个组别中的数据
			{
				//标记需要插入排序的数值
				temp = arr[j];
				for(k = j -gap; arr[k] > temp && k >=0 ; k = k-gap)		//当前gap组别下,前一个数值大于现在这个数值
				{
					arr[k+gap] = arr[k];		//将数值后移一位
				}
				//将插入的数值放入正确位置
				arr[k+gap] = temp;
			}
		}
		
	}
}

算法结果:

**平均时间复杂度:**O(nlogn);**空间复杂度:**O(1);**稳定性:**不稳定

**适用场景:**希尔排序虽然快,但是毕竟是插入排序,其数量级并没有后起之秀——快速排序O(nlogn)快。在大量数据面前,希尔排序不是一个好的算法。但是,中小型规模的数据完全可以使用它。

7、堆排序

堆排序(Heapsort)是指利用堆积树(堆)这种数据结构所设计的一种排序算法,它是选择排序的一种。可以利用数组的特点快速定位指定索引的元素。堆排序就是把最大堆堆顶的最大数取出,将剩余的堆继续调整为最大堆,再次将堆顶的最大数取出,这个过程持续到剩余数只有一个时结束。

7.1 算法描述

7.1.1 什么是堆

堆是具有以下性质的完全二叉树:每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆;或者每个结点的值都小于或等于其左右孩子结点的值,称为小顶堆。如下图:

同时,我们对堆中的结点按层进行编号,将这种逻辑结构映射到数组中就是下面这个样子

该数组从逻辑上讲就是一个堆结构,我们用简单的公式来描述一下堆的定义就是:

大顶堆:arr[i] >= arr[2i+1] && arr[i] >= arr[2i+2]

小顶堆:arr[i] <= arr[2i+1] && arr[i] <= arr[2i+2]

7.1.2 堆排序基本思想及步骤

**堆排序的基本思想是:**将待排序序列构造成一个大顶堆,此时,整个序列的最大值就是堆顶的根节点。将其与末尾元素进行交换,此时末尾就为最大值。然后将剩余n-1个元素重新构造成一个堆,这样会得到n个元素的次小值。如此反复执行,便能得到一个有序序列了。

步骤一 构造初始堆。将给定无序序列构造成一个大顶堆(一般升序采用大顶堆,降序采用小顶堆)。

1.假设给定无序序列结构如下

img

2.此时我们从最后一个非叶子结点开始(叶结点自然不用调整,第一个非叶子结点 arr.length/2-1=5/2-1=1,也就是下面的6结点),从左至右,从下至上进行调整。

img

4.找到第二个非叶节点4,由于[4,9,8]中9元素最大,4和9交换。

img

这时,交换导致了子根[4,5,6]结构混乱,继续调整,[4,5,6]中6最大,交换4和6。

img

此时,我们就将一个无需序列构造成了一个大顶堆。

步骤二 将堆顶元素与末尾元素进行交换,使末尾元素最大。然后继续调整堆,再将堆顶元素与末尾元素交换,得到第二大元素。如此反复进行交换、重建、交换。

a.将堆顶元素9和末尾元素4进行交换

img

b.重新调整结构,使其继续满足堆定义

img

c.再将堆顶元素8与末尾元素5进行交换,得到第二大元素8.

img

后续过程,继续进行调整,交换,如此反复进行,最终使得整个序列有序

img

再简单总结下堆排序的基本思路:

**1、**将无需序列构建成一个堆,根据升序降序需求选择大顶堆或小顶堆;

**2、**将堆顶元素与末尾元素交换,将最大元素"沉"到数组末端;

**3、**重新调整结构(每次长度递减1),使其满足堆定义,然后继续交换堆顶元素与当前末尾元素,反复执行调整+交换步骤,直到整个序列有序。

7.2 动图演示

7.3 算法实现

//堆排序
void swap(int *a, int *b) 
{
    int temp = *b;
    *b = *a;
    *a = temp;
}

void max_heapify(int arr[], int start, int end) 
{
    // 建立父节点指针和子节点指针
    int dad = start;
    int son = dad * 2 + 1;
    while (son <= end) { // 若子节点指针在范围内才做比较
        if (son + 1 <= end && arr[son] < arr[son + 1]) // 先比较子节点的大小,选择大的
            son++;
        if (arr[dad] > arr[son]) //如果父节点大于子节点代表调整结束,直接跳出函数
            return;
        else { // 否则交换父子节点内容,继续比较
            swap(&arr[dad], &arr[son]);
            dad = son;
            son = dad * 2 + 1;
        }
    }
}
## 最后

**自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。**

**深知大多数Java工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。**

**因此收集整理了一份《2024年嵌入式&物联网开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。**

![img](https://img-blog.csdnimg.cn/img_convert/a9e60f59611965f7be864dc3119ff0f4.png)

![img](https://img-blog.csdnimg.cn/img_convert/fbac4d0b695c961e0060dc76caac9153.jpeg)

![img](https://img-blog.csdnimg.cn/img_convert/0ef6cbbd10e70ea4b8add56b70971a55.png)

 ![img](https://img-blog.csdnimg.cn/img_convert/3bd284a06d5a201ffea4f98e61d44d06.png)

![img](https://img-blog.csdnimg.cn/img_convert/87a663957d319960f39a4708f7d4779e.png)

![img](https://img-blog.csdnimg.cn/img_convert/5ee525db7375d8ecbcea24472e14ea9e.png)

![](https://img-blog.csdnimg.cn/img_convert/e7e7623f6755c2ccc3fbbfba1e743b11.png)

 

**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上嵌入式&物联网开发知识点,真正体系化!**

[**如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!**](https://bbs.csdn.net/topics/618654289)

**由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新**!!


一直到现在。**

**深知大多数Java工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。**

**因此收集整理了一份《2024年嵌入式&物联网开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。**

[外链图片转存中...(img-JxujRJm3-1715680804227)]

[外链图片转存中...(img-nVVU4w2f-1715680804228)]

[外链图片转存中...(img-RESAQkbX-1715680804228)]

 [外链图片转存中...(img-4ZWuzCRo-1715680804229)]

[外链图片转存中...(img-WniJ83S2-1715680804229)]

[外链图片转存中...(img-eBB6O6Yr-1715680804229)]

[外链图片转存中...(img-hhC2sx8l-1715680804230)]

 

**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上嵌入式&物联网开发知识点,真正体系化!**

[**如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!**](https://bbs.csdn.net/topics/618654289)

**由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新**!!


  • 19
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
快速排序是一种高效的排序算法,它采用分治的思想,通过将数组分为较小和较大的两部分,递归地对这两部分进行排序,最终使整个数组有序。下面是用 C 语言实现快速排序的一种算法。 快速排序的基本思路是选择一个基准元素,将数组划分为左右两个部分,左部分比基准元素小,右部分比基准元素大,然后对左右两个部分分别进行递归排序。 具体实现如下: 1. 定义一个函数 `quick_sort`,用于排序整个数组; 2. 在函数内部定义三个变量:`left`、`right`、`pivot`; 3. `left` 表示数组的左边界,`right` 表示数组的右边界; 4. `pivot` 用于存储基准元素; 5. 如果 `left` 大于等于 `right`,则返回; 6. 将 `pivot` 设置为数组的第一个元素; 7. 记录下 `pivot` 的位置 `index`; 8. 从 `left+1` 开始遍历数组,如果找到比 `pivot` 大的元素,则将其与 `pivot` 交换,并停止遍历; 9. 从 `right` 开始遍历数组,如果找到比 `pivot` 小的元素,则将其与 `pivot` 交换,并停止遍历; 10. 继续上述步骤,直到 `left` 和 `right` 相交; 11. 将 `pivot` 与 `index` 所在位置的元素交换; 12. 分别对左右两个部分递归调用 `quick_sort` 函数。 这样,通过不断地划分和排序,最终可以使整个数组有序。 以上就是用 C 语言实现快速排序的基本思路和步骤,希望对您理解快速排序算法有所帮助。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值