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
    评论
快速排序是一种常用的排序算法,它通过递归地将组分成较小和较大的两个子组来实现排序。下面是快速排序C语言递归实现: ```c #include <stdio.h> // 交换两个元素的值 void swap(int* a, int* b) { int temp = *a; *a = *b; *b = temp; } // 将组分成两部分,并返回分割点的索引 int partition(int arr[], int low, int high) { int pivot = arr[high]; // 选取最后一个元素作为基准值 int i = (low - 1); // 记录小于基准值的元素的索引 for (int j = low; j <= high - 1; j++) { // 如果当前元素小于等于基准值,则将其与小于基准值的元素交换位置 if (arr[j] <= pivot) { i++; swap(&arr[i], &arr[j]); } } swap(&arr[i + 1], &arr[high]); // 将基准值放到正确的位置上 return (i + 1); // 返回分割点的索引 } // 快速排序递归函数 void quickSort(int arr[], int low, int high) { if (low < high) { int pi = partition(arr, low, high); // 获取分割点的索引 // 对分割点左边的子进行递归排序 quickSort(arr, low, pi - 1); // 对分割点右边的子进行递归排序 quickSort(arr, pi + 1, high); } } // 打印组 void printArray(int arr[], int size) { for (int i = 0; i < size; i++) { printf("%d ", arr[i]); } printf("\n"); } int main() { int arr[] = {64, 25, 12, 22, 11}; int n = sizeof(arr) / sizeof(arr); printf("原始组:\n"); printArray(arr, n); quickSort(arr, 0, n - 1); printf("排序后的组:\n"); printArray(arr, n); return 0; } ``` 这段代码实现了快速排序算法递归版本。首先定义了一个`swap`函用于交换两个元素的值。然后定义了`partition`函,它选择最后一个元素作为基准值,将组分成两部分,并返回分割点的索引。接下来是`quickSort`函,它通过递归调用自身来对分割点左右两个子进行排序。最后,在`main`函中测试了快速排序算法
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值