C语言实现数组快速排序(含对算法的详细解释)

/* 

说明: 

代码参考过网上代码,但分析为个人原创,本贴重在说明快速排序算法的思想和运行过程。 

*/ 

代码部分: 

#include<stdio.h>
 #include<stdlib.h>
 void quickSort(int* arr,int startPos, int endPos)
 {
  int i, j;
  int key;
  key = arr[startPos];
  i = startPos;
  j = endPos;
  while (i<j)
  {
   while (arr[j] >= key && i<j)--j; //————1 从后往去前扫,直到找到一个a[j]<key或遍历完
  arr[i] = arr[j];
   while (arr[i] <= key && i<j)++i; //————2 从后往去前扫,直到找到一个a[i]>key或遍历完
  arr[j] = arr[i];
  }
  arr[i] = key;
  if (i - 1>startPos) quickSort(arr, startPos, i - 1); //————1 如果key前还有两个及以上的数,排key前的数(有一个的话自然就不用排了)
 if (endPos>i + 1) quickSort(arr, i + 1, endPos);//————2 如果key后还有两个及以上的数,排key后的数
}
int main()
 {
  int a[11], i;
  for (i = 0; i<11; i++)
   scanf_s("%d", &a[i]);
  quickSort(a, 0, 10);
  for (i = 0; i<11; i++)
   printf("%d ", a[i]);
  printf("\n");
  return 0;
 }  

解析部分:
 /*
以数组  a[0] a[1]  a[2]  a[3]  a[4]  a[5]  a[6]  a[7]  a[8]  a[9]  a[10]
                 0    2     32    39    23    45    36    57    14    27    39      为例,说明核心代码的实现机制 

第一轮:
 首先进入quickSort(a, 0, 10); key=0,i=0,j=10,进入外层while,进入第一个内层while,由于0是数组中最小的,故j一直扫到头,j=0,arr[0] = arr[0]=0;
显然无法进入第二个内层while,由于i=j=0,结束外层while,执行a[0]=key=0;显然不进入第一个if,进入第二个if,执行quickSort(a, 1, 10);进行从a[1]到
a[10]的排序,第一轮结束。
 第二轮;
执行quickSort(a, 1, 10),key=2,i=1,j=10,进入外层while,进入第一个内层while,由于2是传入段中最小的,故j一直扫到头,j=1,arr[1] = arr[1]=2;显然
 无法进入第二个内层while,由于i=j=1,结束外层while,执行a[1]=key=2;显然不进入第一个if,进入第二个if,执行quickSort(a, 2, 10);进行从a[2]到
a[10]的排序,第二轮结束。
 第三轮:
 执行quickSort(a, 2, 10),key=32,i=2,j=10,进入外层while,进入第一个内层while,a[10]=39>key=32,--j,j变为9;a[9]=27<key=32,,退出第一个内层while,
 执行a[i]=a[2]=a[j]=a[9]=27,数组变为
                a[0] a[1]  a[2]  a[3]  a[4]  a[5]  a[6]  a[7]  a[8]  a[9]  a[10]
                  0    2     27    39    23    45    36    57    14    27    39
注意此时a[j](即a[9])的值存入a[i](即a[2])中,a[j]可以被再赋值,32呢?32怎么没了呢?注意32始终由key保存,不用担心。注意此时i=2,j=9;
 程序顺次执行,满足第二个内层while,进入,开始从左往右扫。a[2]=27<key=32,++i,i变为3(注意这是必然的,因为第一次的a[i]是由上次内层while的a[j]送给
 的,而送给的条件是a[j](即这里的a[i])<key);a[i]=a[3]=39>key=32,执行a[j](前边已经说过,此时a[j]=a[9]的值已保存到a[2]中,a[j]可修改)=a[9]=a[3]
 =39,数组变为
                a[0] a[1]  a[2]  a[3]  a[4]  a[5]  a[6]  a[7]  a[8]  a[9]  a[10]
                  0    2     27    39    23    45    36    57    14    39    39
注意此时a[i]=a[3]又变为可修改。
 注意此时i=3<j=9,未跳出外层while。
 继续执行第一个内层while,a[j]=a[9]=39>key=32,--j,j变为8(这也是必然的,道理同前边分析);a[j]=a[8]=14<key=32,退出第一个内层while,
 执行a[i]=a[3]=a[j]=a[8]=14,数组变为
                a[0] a[1]  a[2]  a[3]  a[4]  a[5]  a[6]  a[7]  a[8]  a[9]  a[10]
                  0    2     27    14    23    45    36    57    14    39    39
注意此时a[j]=a[8]又变为可修改。此时i=3,j=8;
程序顺次执行,满足第二个内层while,进入,开始从左往右扫。a[i]=a[3]=14<key=32,++i,i变为4(必然);a[i]=a[4]=23<key=32,++i,i变为5;a[i]=a[5]=
 45>key=32,退出第二个内层while,执行a[j]=a[8]=a[i]=a[5]=45,数组变为
                a[0] a[1]  a[2]  a[3]  a[4]  a[5]  a[6]  a[7]  a[8]  a[9]  a[10]
                  0    2     27    14    23    45    36    57    45    39    39
注意此时a[i]=a[5]可修改,且i=5,j=8,未跳出外层while。
 继续执行第一个内层while,a[j]=a[8]=45>key=32,--j,j变为7(必然);a[j]=a[7]=57>key=32,--j,j变为6;a[j]=a[6]=36>key=32,--j,j变为5注意到此时i=j=5,
 直接退出三个while。执行a[i]=a[5]=key=32,数组变为
                a[0] a[1]  a[2]  a[3]  a[4]  a[5]  a[6]  a[7]  a[8]  a[9]  a[10]
                  0    2     27    14    23    32    36    57    45    39    39
注意此时a[5]前的数都小于key=32,a[5]后的数都大于key=32,且startPos=2,endPos=10。
 显然两个if都满足,(这是人一次性判断的,计算机只能先判断第一个if,等程序再返回到本轮时再判断第二个if,我们一次性判断是为了说明方便)首先进入第一个
if,执行quickSort(a, 2, 4),排a[5]前面的a[2]到a[4](a[0],a[1]在第二轮后已排好),进入下一轮(第四轮),但第三轮未结束,因为计算机还并未判断第二个
if。
 第四轮:
 执行quickSort(a, 2, 4),key=a[2]=27,i=2,j=4,startPos=2,endPos=4。
 进入外层while,进入第一个内层while,a[j]=a[4]=23<key=27,执行a[i]=a[2]=a[j]=a[4]=23,数组变为
                a[0] a[1]  a[2]  a[3]  a[4]  a[5]  a[6]  a[7]  a[8]  a[9]  a[10]
                  0    2     23    14    23    32    36    57    45    39    39
此时a[j]=a[4]可修改,且i=2,j=4,程序顺次执行,进入第二个while,a[i]=a[2]=23<key=27,++i,i变为3(必然):a[i]=a[3]=14<key=27,++i,i变为4,注意到i=
 j=4,退出所有循环,执行a[i]=a[4]=key=27,数组变为
                a[0] a[1]  a[2]  a[3]  a[4]  a[5]  a[6]  a[7]  a[8]  a[9]  a[10]
                  0    2     23    14    27    32    36    57    45    39    39
此时i=j=4,startPos=2,endPos=4,显然满足第一个if不满足第二个if(key后已无数),故执行quickSort(a, 2, 3)(排a[4]前的),进入下一轮(第五轮),但
 第四轮未结束,因为计算机还并未判断第二个if。
 第五轮:
 执行quickSort(a, 2,3),key=a[2]=23,i=2,j=3,startPos=2,endPos=3。
 进入外层while,进入第一个内层while,a[j]=a[3]=14<key=23,执行a[i]=a[2]=a[j]=a[3]=14,数组变为
                a[0] a[1]  a[2]  a[3]  a[4]  a[5]  a[6]  a[7]  a[8]  a[9]  a[10]
                  0    2     14    14    23    32    36    57    45    39    39
此时a[j]=a[3]可修改,且i=2,j=3,程序顺次执行,进入第二个while,a[i]=a[2]=14<key=23,++i,i变为3(必然):注意到i=j=3,退出所有循环,执行a[i]=a[3]=
 key=23,数组变为
                a[0] a[1]  a[2]  a[3]  a[4]  a[5]  a[6]  a[7]  a[8]  a[9]  a[10]
                  0    2     14    23    27    32    36    57    45    39    39
此时i=j=3,startPos=2,endPos=3,显然不满足第一个if,再判断第二个,也不满足,故第五轮结束,返回到上一轮(第四轮)的第二个if处,前面已经分析过,不
 满足第二if,故第四轮结束,返回到上一轮(第三轮)的第二个if处(至此,到第三轮最终的a[i]=a[5]=key=32前的元素,都已排好),这次满足。执行
quickSort(a, 6, 10),排a[5]后的元素,进入下一轮(记为第四*轮,为的是与上面的第四轮区别,同时也为了体现两者的联系)。第三轮结束。
 第四*轮:
 执行quickSort(a, 6, 10),key=a[6]=36,i=6,j=10,startPos=6,endPos=10。
 此时a[j]=a[3]可修改,且i=2,j=3,程序顺次执行,进入第二个while,a[i]=a[2]=14<key=23,++i,i变为3(必然):注意到i=j=3,退出所有循环,执行a[i]=a[3]=
 a[j]=a[10]=39>key=36,--j,j变为9;a[j]=a[9]=39>key=36,--j,j变为8;a[j]=a[8]=45>key=36,--j,j变为7;a[j]=a[7]=57>key=36,--j,j变为6;注意到i=j=6,
 退出所有while,执行a[i]=a[6]=key=36,数组不变。此时i=j=6,startPos=6,endPos=10。显然不满足第一个if,满足第二个if,执行quickSort(a, 7, 10)(排
a[6]后的元素),进入第五*轮,第四*轮结束。
 第五*轮:
 执行quickSort(a, 7, 10),key=a[7]=57,i=7,j=10,startPos=7,endPos=10。
a[j]=a[10]=39<key=57,执行a[i]=a[7]=a[j]=a[10]=39,数组变为
                a[0] a[1]  a[2]  a[3]  a[4]  a[5]  a[6]  a[7]  a[8]  a[9]  a[10]
                  0    2     14    23    27    32    36    39    45    39    39
此时i=7,j=10,程序顺次执行,进入第二个内层while,a[i]=a[7]=39<key=57,++i,i变为8(必然);a[i]=a[8]=45<key=57,++i,i变为9;a[i]=a[9]=39<key=57,++i,
 i变为10;注意到i=j=10,退出所有while,执行a[i]=a[10]=key=57。数组变为
                a[0] a[1]  a[2]  a[3]  a[4]  a[5]  a[6]  a[7]  a[8]  a[9]  a[10]
                  0    2     14    23    27    32    36    39    45    39    57
此时i=j=10,startPos=7,endPos=10。显然满足第一个if,不满足第二个if(a[i]=a[10]后面已没有元素),执行quickSort(a, 7, 9)(排a[10]前面的元素),进入
 第六*轮,但未退出第五*轮,因为计算机并还未判断第二个if。
 第六*轮:
 执行quickSort(a, 7, 9),key=a[7]=39,i=7,j=9,startPos=7,endPos=9。
a[j]=a[9]=39=key=39,--j,j变为8;a[j]=a[8]=4>key=39,--j,j变为7;注意到i=j=7,退出所有while,执行a[i]=a[7]=key=39,数组不变。此时i=j=7,startPos=7,
 endPos=9。显然不满足第一个if(a[i]=a[7]前已无元素),满足第二个,执行quickSort(a, 8, 9)(排a[7]后面的元素),,进入第七*轮,第六*轮结束。
 第七*轮:
 执行quickSort(a, 8, 9),key=a[8]=45,i=8,j=9,startPos=8,endPos=9。
a[j]=a[9]=39<key=45,执行a[i]=a[8]=a[j]=a[9]=39,数组变为
                a[0] a[1]  a[2]  a[3]  a[4]  a[5]  a[6]  a[7]  a[8]  a[9]  a[10]
                  0    2     14    23    27    32    36    39    39    39    39
此时i=8,j=9,程序顺次执行,进入第二个内层while,a[i]=a[8]=39<key=45,++i,i变为9(必然);
 注意到i=j=9,退出所有while,执行a[i]=a[10]=key=57。数组变为
                a[0] a[1]  a[2]  a[3]  a[4]  a[5]  a[6]  a[7]  a[8]  a[9]  a[10]
                  0    2     14    23    27    32    36    39    39    45    57
此时i=j=9,startPos=8,endPos=9,显然不满足第一个if,再判断第二个,也不满足,第七*轮结束。程序回到第五*轮的第二个if,前面已经分析过,不满足,故第五*轮结束。
 至此,整个quickSort函数结束,数组已排好,如上所示。
*/ 

如果大家觉得分析太多,不想看,建议大家按照代码手动执行一下,就豁然开朗了。当然,过程中可以结合、参考在下的分析。 

代码已经过测试,在VS2013上成功运行! 

发此文有两大目的: 

1.和大家交流经验,供需要的人参考。 

2.在下菜鸟,代码中难免有不妥之处,恳求大神批评指正。您的批评就是在下提高的起点,对于您的批评,在下将不胜感激! 
时间: 2024-11-13 19:21:40

C语言实现数组快速排序(含对算法的详细解释)的相关文章

经典中的经典算法 动态规划(详细解释,从入门到实践,逐步讲解)

动态规划的重要性就不多说,直接进入正题 首先,我们看一下官方定义: 定义: 动态规划算法是通过拆分问题,定义问题状态和状态之间的关系,使得问题能够以递推(或者说分治)的方式去解决. 动态规划算法的基本思想与分治法类似,也是将待求解的问题分解为若干个子问题(阶段),按顺序求解子阶段,前一子问题的解,为后一子问题的求解提供了有用的信息.在求解任一子问题时,列出各种可能的局部解,通过决策保留那些有可能达到最优的局部解,丢弃其他局部解.依次解决各子问题,最后一个子问题就是初始问题的解. 基本思想与策略编

【转载】经典10道c/c++语言经典笔试题(含全部所有参考答案)

经典10道c/c++语言经典笔试题(含全部所有参考答案) 1. 下面这段代码的输出是多少(在32位机上). char *p; char *q[20]; char *m[20][20]; int (*n)[10]; struct MyStruct { char dda; double dda1; int type ; }; MyStruct k; printf("%d %d %d %d %d",sizeof(p),sizeof(q),sizeof(m),sizeof(n),sizeof(

C语言几种常用的排序算法

/* ============================================================================= 相关知识介绍(所有定义只为帮助读者理解相关概念,并非严格定义): 1.稳定排序和非稳定排序    简单地说就是所有相等的数经过某种排序方法后,仍能保持它们在排序之前的相对次序,我们就 说这种排序方法是稳定的.反之,就是非稳定的.  比如:一组数排序前是a1,a2,a3,a4,a5,其中a2=a4,经过某种排序后为a1,a2,a4,a3,

学习C语言的数组

C语言的数组 数组声明的实例:int num[3];只要记下这个模板就好. 不建议使用变量定义数组,如果使用了变量定义数组,作为数组的元素的个数,不初始化的情况下是随机值,如果初始化会直接报错 注意:如果定义的同时进行初始化,那么元素的个数可以省略省略之后,初始化赋值几个数据,那么数组的长度就是几,也就是说数组将来就能存储几个数据.int scores[] = {1,3};//只有两个存储空间了如果没有进行初始化,就不能省略元素个数:    错误的:int scores[]; 可以通过[]索引指

C语言关于数组与指针内容小结

数组的基本概念 什么是数组:数组就是:数组是相同类型的元素的一个集合       类型说明符 数组名 [常量表达式]: 其中,类型说明符是任一种基本数据类型或构造数据类型.数组名是用户定义的数组标识符.方括号中的常量表达式表示数据元素的个数,也称为数组的长度.例如: int a[10]; /* 说明整型数组a,有10个元素 */ float b[10], c[20]; /* 说明实型数组b,有10个元素,实型数组c,有20个元素 */ char ch[20]; /* 说明字符数组ch,有20个元

C语言——字符数组

在C语言编程中,我们一般用一个字符数组来存放一个字符串.例如,我们想存储这个字符串“http://i.cnblogs.com”,这个字符串一共有20个字符,要存放下这个字符串,我们需要一个长度为21的字符数组.为什么是21个而不是20个呢?在C语言中,字符串数组默认以'\0'结尾,所以我们一共需要一个长度为21的字符数组来存储这个变量. unsigned char text[21] = “http://i.cnblogs.com”; 既然我们已经知道了如何存放一个字符串,那下面我们来讨论另外几个

C语言之数组名的含义

一:一维数组 int a[5]; a:就是数组名.a做左值时表示整个数组的所有空间(10×4=40字节),又因为C语言规定数组操作时要独立单个操作,不能整体操作数组,所以a不能做左值:a做右值表示数组首元素(数组的第0个元素,也就是a[0])的首地址(首地址就是起始地址,就是4个字节中最开始第一个字节的地址).a做右值等同于&a[0]; a[0]:表示数组的首元素,也就是数组的第0个元素.做左值时表示数组第0个元素对应的内存空间(连续4字节):做右值时表示数组第0个元素的值(也就是数组第0个元素

R语言实现︱局部敏感哈希算法(LSH)解决文本机械相似性的问题(二,textreuse介绍)

上一篇(R语言实现︱局部敏感哈希算法(LSH)解决文本机械相似性的问题(一,基本原理))讲解了LSH的基本原理,笔者在想这么牛气冲天的方法在R语言中能不能实现得了呢? 于是在网上搜索了一下,真的发现了一个叫textreuse的包可以实现这样的功能,而且该包较为完整,可以很好地满足要求. 现在的版本是 0.1.3,最近的更新的时间为 2016-03-28. 国内貌似比较少的用这个包来实现这个功能,毕竟R语言在运行大规模数据的性能比较差,而LSH又是处理大规模数据的办法,所以可能国内比较少的用R来执

【Go语言】【6】GO语言的数组

在<[4]GO语言类型和为类型增加方法>里说过GO语言除了基础类型(如int.float64.complex128等)之外,还有复合类型,其中就包含本文的数组.对于数组大家都不陌生,在C语言中可以这样声明一个一维数组:int arr[10],那么GO语言是怎么定义的呢? 一.数组的声明 1.数组的声明格式为var arrName [num]type,比如: var strArr [10]string     // 声明一个由10个字符串组成的一维字符串数组 var byteArr [32]by