C语言学习基本排序--快速排序(Quicksort分割+递归)

快速排序算法

算法简介

&#8194 递归经常作为分治法(divide-and-conquer)的结果自然地出现。这种称为分治法的设计方法把一个大问题划分成多个较小的问题,然后采用相同的算法分别解决这些小问题。分治法的经典示例是流行的排序算法————快速排序(quicksort)。
1.快速排序是一种交换排序
快速排序尤其适用于对大数据的排序,它的高速和高效无愧于“快速”两个字。虽然说它是“最常用”的,可对于初学者而言,用它的人却非常少。因为虽然很快,但它也是逻辑最复杂、最难理解的算法,因为快速排序要用到递归和函数调用。
2.基本思想:

选取一个基准数;

然后将大于和小于基准的元素分别放置于基准数两边;

继续分别对按此方法分治基准数的两侧,直至整个序列有序。

  1. 注意问题

基准数的选择(通常选取头元素或者尾元素)

元素的查找(一组一组的查找需要交换的元素)

递归调用(终止递归的临界条件)

基准数的归位(查找相遇的位置和基准元素互换)

  1. 元素查找的先后顺序(以升序为例):

如果基准数选取为a[low],那么必须先从高位high查找到小于基准的数,然后再从低位low寻找大于基准的数,交换;

如果基准数选取为a[high],那么必须先从低位low查找到大于基准的数,然后再从高位high寻找小于基准的数,交换;

原因:

当两侧查找相遇的时候,需要将基准数和相遇点元素的值交换以正确归位基准数;

也就是若基准数=a[low]的这种情况,相遇点的元素值必须小于基准值,

如果先从低位low查找大于基准的数,最终停止的元素大于基准值的话,就会导致归位失败。

C程序

  1. 先开发一个名为quicksort的递归函数,此函数采用快速排序算法对数组元素进行排序,为了测试函数,将由main函数往数组中读入10个元素,调用quicksort函数对该数组进行排序然后显示数组中元素:
    Enter 10 numbers to be sorted:
    In sorted order:
    qsort.c
/* Sorts an array of integers using Quicksort algorithm*/
#include<stdio.h>
#define N 10
void quicksort(int a[], int low, int high);
int split(int a[], int low, int high); //声明2个函数,void放在函数开头表示返回任意类型;
int main(void)   //void放在括号内,表示main函数参数为任意类型
{
	int a[N], i;
	printf("Enter %d numbers to be sorted:", N);
	for (i = 0; i < N; i++)
		scanf("%d", &a[i]);
	quicksort(a, 0, N - 1);     //调用递归函数
	printf("In sorted order:");
	for (i = 0; i < N; i++)
		printf("%d", a[i]);
	printf("\n");
	return 0;
}
void quicksort(int a[], int low, int high)  //递归函数
{
	int middle;  
	if (low >= high) return; //保证执行if(low < high)
	middle = split(a, low, high);//找到存放基准值的正确位置;
	quicksort(a, low, middle - 1);//对基准值左边区间进行递归排序;
	quicksort(a, middle + 1, high);//对基准值右边区间进行递归排序;
}
int split(int a[], int low, int high)//排序,也可以是char a[],下标是整型变量就行;
{
	int part_element = a[low];//把左边第一个元素作为分割元素存到part_element;

	for (;;) {
		while (low < high &&part_element <= a[high])//如果是左边第一个作为分割基准,就应该先比较最右边;
			high--;                    //a[high]大于等于基准,就向左移动一位,继续比较,直到小于基准跳到下一语句;
		if (low >= high) break;
		a[low++] = a[high];           //把从右边得到的小于基准的值放在a[low]的空位上,并low向右移动一位,继续比较,这句话是在if条件里的;直接交换省去了swap函数

		while (low < high &&a[low] <= part_element)   //根据移动的low从左边和基准比较
			low++;
		if (low >= high) break;
		a[high--] = a[low];      //如果从左边的值大于基准,就把该值放到a[high]空位上,此时的high不是最右端了,前面high--;直到满足break条件
	}
	a[high] = part_element;//到这一句时,low=high的,把基准放到正确位置,基准左边皆小于它,右边大于它;
	return high;//返回这个正确的位置,进行递归;
}

2.本程序已经对基础的快速排序做了改进,即:省去了swap交换函数,a[low++] = a[high],相当于先将基准位置固定,等最后再把它放进数组来。

快速排序实际上是冒泡排序的一种改进,是冒泡排序的升级版。这种改进就体现在根据分割序列的基准数,跨越式地进行交换。正是由于这种跨越式,使得元素每次移动的间距变大了,而不像冒泡排序那样一格一格地“爬”。快速排序是一次跨多格,所以总的移动次数就比冒泡排序少很多。

但是快速排序也有一个问题,就是递归深度的问题。调用函数要消耗资源,递归需要系统堆栈,所以递归的空间消耗要比非递归的空间消耗大很多。而且如果递归太深的话会导致堆栈溢出,系统会“撑”不住。快速排序递归的深度取决于基准数的选择,比如下面这个序列:

5 1 9 3 7 4 8 6 2

5 正好处于 1~9 的中间,选择 5 作基准数可以平衡两边的递归深度。可如果是:

1 5 9 3 7 4 8 6 2

选择 1 作为基准数,那么递归深度就全部都加到右边了。如果右边有几万个数的话则系统直接就崩溃了。所以需要对递归深度进行优化。怎么优化呢?就是任意取三个数,一般是取序列的第一个数、中间数和最后一个数,然后选择这三个数中大小排在中间的那个数作为基准数,这样起码能确保获取的基准数不是两个极端。

前面讲过,快速排序一般用于大数据排序,即数据的个数很多的时候(不是指数的值很大)。如果是小规模的排序,就用前面讲的几种简单排序方式就行了,不必使用快速排序。
3.非递归快速排序
虽然快速排序本质是递归算法,并且递归格式的快速排序是最容易理解的,但是实际上若去掉递归会更加高效———用栈代替递归
每次用递归的时候均入栈 、每次运算的时候均出栈、 栈为空则完成排序
可参考博客:[https://www.cnblogs.com/JoZSM/p/9768781.html]

本文参考了《C语言程序设计现代方法》第2版P146

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值