大话数据结构 | 排序 - 图1大话数据结构 | 排序 - 图2```c //非规范的冒泡排序,从头开始,一般是相邻比较,大的往后,一轮下来到靠后位置

include”stdio.h”

define MAXSIZE 10

void swap(int a,int b) { int t; t=a; a=b; b=t; } void Bubblesort(int *a) { for(int j=0;j<9;j++) { for(int i=1;i=MAXSIZE-j为一趟,内循环9=j趟为n-1 { if(a[i]>a[i+1]) //本身有序时,则比较次数为0,即0交换 //10数据比较9次,9个数据比较8次….9+8+7+…+1=n(n-1)/2 swap(&a[i],&a[i+1]); } } } int main() { int a[MAXSIZE+1]={0,1,4,5,6,7,8,9,10,2,3}; Bubblesort(a); for(int i=1;i<=MAXSIZE;i++) { printf(“%d “,a[i]); } }

  1. ```c
  2. //规范的冒泡排序,从头开始,一般是相邻比较,小的往上冒,一趟下来有一个小的产生
  3. #include"stdio.h"
  4. #define MAXSIZE 10
  5. typedef struct
  6. {
  7. int key;
  8. }Redtype;//一个Redtype其实就相当于一个int
  9. typedef struct
  10. {
  11. Redtype * r;//r[0]空出来
  12. int length;
  13. }Sqlist;
  14. void swap(int *a,int *b)
  15. {
  16. int t;
  17. t=*a;
  18. *a=*b;
  19. *b=t;
  20. }
  21. void Bubblesort(Sqlist *L)
  22. { for(int j=0;j<9;j++)
  23. {
  24. for(int i=L->length-1;i>=1;i--)//从末尾开始
  25. {
  26. if(L->r[i+1]<L->r[i]) //后一个比前一个小,则交换
  27. //10数据比较9次,9个数据比较8次....9+8+7+...+1=n(n-1)/2
  28. swap(&L->r[i],&L->r[i+1]);
  29. }
  30. }
  31. }
  32. //最好情况:有序:0交换,比较次数:未改进是9+8+7....+1,逆序:比较交换次数均是9+8+7+...+1
  33. int main()
  34. {
  35. int a[MAXSIZE+1]={0,1,4,5,6,7,8,9,10,2,3};
  36. Sqlist L;
  37. L.length=10;
  38. L.r=a;
  39. Bubblesort(&L);
  40. for(int i=1;i<=L.length;i++)
  41. {
  42. printf("%d ",L.r[i]);
  43. }
  44. }
  1. //最终版的冒泡排序
  2. #include<stdio.h>
  3. #define MAXSIZE 10
  4. typedef struct
  5. {
  6. int key;
  7. }Redtype;//一个Redtype其实就相当于一个int
  8. typedef struct
  9. {
  10. Redtype * r;//r[0]空出来
  11. int length;
  12. }Sqlist;
  13. void swap(Redtype *a,Redtype *b)
  14. {
  15. Redtype t;
  16. t=*a;
  17. *a=*b;
  18. *b=t;
  19. }
  20. void Bubblesort(Sqlist *L)
  21. {
  22. int flag=1;//先设置为1
  23. for(int j=0;j<L->length-1&&flag;j++)
  24. {
  25. flag=0;//没有交换,flag的值为0,提前终止
  26. for(int i=L->length-1;i>=1+j;i--)//从末尾开始,i>1+j理解上是为了减少没必要的排序
  27. {
  28. if(L->r[i+1].key<L->r[i].key) //后一个比前一个小,则交换
  29. {swap(&L->r[i],&L->r[i+1]);
  30. flag=1;//发生交换
  31. }
  32. }
  33. }
  34. }
  35. //改进后:最好正序,比较n-1次->O(n),交换0次,最坏9+8+7+...1->比较=交换=n-1+...1,时间复杂度为O(n^2)
  36. int main()
  37. {
  38. int a[MAXSIZE+1]={0,1,4,5,6,7,8,9,10,2,3};
  39. Sqlist L;
  40. L.length=10;
  41. L.r=(Redtype*)a;//左右类型一样
  42. Bubblesort(&L);
  43. for(int i=1;i<=L.length;i++)
  44. {
  45. printf("%d ",L.r[i]);
  46. }
  47. }
  48. //简单选择排序:每一次在n-i+1个,ri....rn个中找到合适的与之交换
  49. #include "stdio.h"
  50. #define MAXSIZE 10
  51. typedef struct
  52. {
  53. int key;
  54. }Redtype;
  55. typedef struct
  56. {
  57. Redtype * r;
  58. int length ;
  59. }Sqlist;
  60. void swap(Redtype *a,Redtype *b);//交换操作
  61. void Simplesort(Sqlist *L);//简单选择排序
  62. void SortPrint(Sqlist *L);
  63. int main()
  64. {
  65. Sqlist L;
  66. int a[MAXSIZE+1]={0,1,3,5,7,9,2,4,6,8,10};
  67. L.length=MAXSIZE;
  68. L.r=(Redtype*)a;
  69. Simplesort(&L);
  70. SortPrint(&L);
  71. }
  72. void swap(Redtype *a,Redtype *b)
  73. {
  74. Redtype t;
  75. t=*a;
  76. *a=*b;
  77. *b=t;
  78. }
  79. void Simplesort(Sqlist *L)
  80. { int j;
  81. for(j=1;j<=L->length-1;j++)//n个数据,交换n-1次
  82. { //寻找合适的位置,从n-i+1中寻找
  83. int min=j;//设置下标 1:2-10,2:3-7...9:10 9+8+7+6...+1 n*(n-1)/2
  84. int k;
  85. for(int i=j+1;i<=L->length;i++)
  86. {
  87. if(L->r[i].key<=L->r[min].key)
  88. {
  89. min=i;//记录找到的位置,更新最小值
  90. }
  91. }
  92. swap(&L->r[min],&L->r[j]);
  93. }
  94. }
  95. /*
  96. *n-i+1,最好和最坏:第i趟,n-i比较,总和n*(n-1)/2次
  97. *交换次数最好0次,最坏同比较
  98. *总复杂度O(n*2)
  99. */
  100. void SortPrint(Sqlist *L)
  101. {
  102. for(int i=1;i<=L->length;i++)
  103. {
  104. printf("%d ",L->r[i].key);
  105. }
  106. }
  107. //时间复杂度同冒泡排序,但是略优于冒泡排序
  108. //直接插入排序算法:哨兵 is important 作为辅助空间和判断依据
  109. #include"stdio.h"
  110. #define MAXSIZE 10
  111. typedef struct
  112. {
  113. int key;
  114. }Redtype;
  115. typedef struct
  116. {
  117. Redtype * r;
  118. int length;
  119. }Sqlist;
  120. void StrInsertsort(Sqlist * L);
  121. void LinkPrint(Sqlist *L);
  122. int main()
  123. {
  124. int a[MAXSIZE+1]={0,2,4,6,8,10,1,3,5,7,9};
  125. Sqlist L;
  126. L.r=(Redtype *)a;
  127. L.length=MAXSIZE;
  128. StrInsertsort(&L);
  129. LinkPrint(&L);
  130. return 0;
  131. }
  132. /*
  133. *第一个元素默认在有序表中,所以考虑r2...rn这些位置
  134. * 哨兵的作用为作为辅助空间和防止越界代替i>=0类似的写法;
  135. * 注意的地方是两个for的写法
  136. */
  137. void StrInsertsort(Sqlist *L)
  138. { int j;
  139. for(int i=2;i<=L->length;i++) //
  140. {
  141. L->r[0].key=L->r[i].key;
  142. if(L->r[i].key<L->r[i-1].key)
  143. {
  144. for( j=i-1;L->r[0].key<L->r[j].key;j--) //重要点。
  145. L->r[j+1]=L->r[j];
  146. L->r[j+1]=L->r[0];
  147. }
  148. }
  149. }
  150. /*
  151. *
  152. *时间复杂度分析:
  153. *最好:正序:n-1外循环,内循环比较1次,最终为n-1个1相加,移动0次
  154. *最坏:逆序:n-1趟,第k趟,移动k+2(哨兵的移动)次 3+4+5+...+n+1=(n+4)*(n-1)/2
  155. * 比较次数 ,第k趟,比较k+1(for2)次,2+3+...+n=(n+2)*(n-1)/2
  156. * 细节理解说明:for2:for( j=i-1;L->r[0].key<L->r[j].key;j--)
  157. * i-1;比较i次0与0也算一次
  158. * 综合考虑:根据概率相同原则:平均比较和移动次数均是n^2/4
  159. * O(n^2)
  160. * 同样的时间复杂度:直接插入排序比冒泡排序和简单选择排序更好一些
  161. * (一) 基本有序时,1+1+1+...kn(很少项较大),移动kn,整体较小
  162. * (二)n较小,L->length较小,对n^2的影响较大,此时优势明显
  163. */
  164. void LinkPrint(Sqlist *L)
  165. {
  166. for(int i=1;i<=L->length;i++)
  167. printf("%d ",L->r[i].key);
  168. }
  169. /*
  170. *Shellsort :希尔排序,优化后的直接插入排序,历史意义在于时间复杂度的突破
  171. *直接插入排序的优势在于在数据基本有序,数量较少时这两个条件下,优势明显
  172. *而希尔排序应创造这样的优势性条件,使数据变少和基本有序
  173. *变少的有效措施是分割数据,及分组,在组内内部直接插入排序,如何分割
  174. *在基本有序时在进行一次直接插入排序,基本有序,小的基本在前面,大的在后面,不大不小在中间
  175. *算法核心:采用跳跃分割策略,保证基本有序而非局部有序
  176. *
  177. */
  178. #include"stdio.h"
  179. #define MAXSIZE 10
  180. typedef struct
  181. {
  182. int key;
  183. }Redtype;
  184. typedef struct
  185. {
  186. Redtype *r;
  187. int length;
  188. }Sqlist;
  189. void Shellsort(Sqlist *L);
  190. void StrInsertsort(Sqlist *L,int gap);
  191. void SortPrint(Sqlist *L);
  192. void swap(Redtype *a,Redtype *b);
  193. int main()
  194. {
  195. Sqlist L;
  196. int b=MAXSIZE;
  197. int a[MAXSIZE+1]={0,9,8,7,6,5,1,2,3,4,10};
  198. L.r=(Redtype*)a;
  199. L.length=MAXSIZE;
  200. Shellsort(&L);
  201. SortPrint(&L);
  202. return 0;
  203. }
  204. void Shellsort(Sqlist *L)
  205. { int b=L->length/2;
  206. while(b)
  207. {
  208. StrInsertsort(L,b);
  209. b/=2;
  210. }
  211. }
  212. void StrInsertsort(Sqlist *L,int gap)
  213. { int i;
  214. //printf("%d %d ",L->r[6].key,L->r[8].key);
  215. for(i=1;i+gap<=L->length;i++)
  216. { if(L->r[i].key>L->r[i+gap].key)//交换的条件***
  217. swap(&(L->r[i]),&(L->r[i+gap]));
  218. }
  219. }
  220. //9,8,7,6,5,1,2,3,4,10
  221. /* gap=5
  222. *结尾=5:1,8,7,6,5,9,2,3,4,10
  223. * =6:1,2,7,6,5,9,8,3,4,10
  224. * =7:1,2,3,6,5,9,8,7,4,10
  225. * =8:1,2,3,4,5,9,8,7,6,10
  226. * =9:1,2,3,4,5,9,8,7,6,10
  227. * =10:1,2,3,4,5,9,8,7,6,10
  228. *
  229. * gap=2
  230. * 1,2,3,4,5,9,8,7,6,10
  231. * 1,2,3,4,5,7,8,9,6,10
  232. * 1,2,3,4,5,7,6,9,8,10
  233. *
  234. * gap=1
  235. * 1,2,3,4,5,6,7,9,8,10
  236. * 1,2,3,4,5,6,7,8,9,10
  237. */
  238. void swap(Redtype *a,Redtype *b)
  239. {
  240. Redtype t;
  241. t=*a;
  242. *a=*b;
  243. *b=t;
  244. }
  245. void SortPrint(Sqlist *L)
  246. {
  247. for(int i=1;i<=L->length;i++)
  248. printf("%d ",L->r[i].key);
  249. printf("\n");
  250. }
/*堆排序:历史溯源:发明堆排序的人同样发明了对这样的数据结构:heap chunk
 * 了解大根堆和小根堆的概念
 * 简单选择排序:在n个元素中选择最小的需要比较n-1次,其中造成很多无效的比较
 * 而堆排序可以记录下来,规避简单选择排序的缺点,改进后的选择排序
 * 利用大根堆的
 * 问题数目不是完全二叉树的如何更变为大根堆,必须奇数个
 */
#include"stdio.h"
#define MAXSIZE 9
typedef struct
{
 int key;
}Redtype;
typedef struct
{
Redtype *r;
int length;
}Sqlist;
void HeapAdjust(Sqlist *L,int s,int m); //s是其实下标,m是末尾下标
void Heapsort(Sqlist *L);
void SortPrint(Sqlist *L);
void swap(Redtype *a,Redtype *b);//交换记录
int main()
{ int a[MAXSIZE+1]={0,5,1,9,3,7,4,8,6,2};
  Sqlist L;
  L.r=(Redtype*)a;
  L.length=MAXSIZE;
  Heapsort(&L);
  SortPrint(&L);
  return 0;
}
void SortPrint(Sqlist *L)
{
  for(int i=1;i<=L->length;i++)
  printf("%d ",L->r[i].key);

}
//核心的思想,子树的根节点及孩子的大小的比较
void HeapAdjust(Sqlist *L,int s,int m)
{
  int j;//temp的用途在于完成根与孩子的交换,存储元素,j的话与之相关的是2*j,2*j+1,左右孩子
  Redtype temp;
  temp=L->r[s];//由于在堆的实际排序中根节点很有用,记录根节点
  for(j=2*s;j<=m;j++)//孩子不超过n的范围
  {
  if(j<m&&L->r[j].key<L->r[j+1].key)
    j++;//j记录的是孩子中较大的那一方的下标
  if(temp.key>=L->r[j].key)//跟两个孩子中较大的比较之后,如果是大根堆,则打断循环
   break;
   //否则的话就赋值(此时有两个一样的数)
   L->r[s]=L->r[j];//j的角标那一项比较大
   s=j;
  }
  L->r[s]=temp;//如果进行了赋值,那就要想办法改变赋值的影响:即将temp赋值给新的s的位置
  //完成交换,这算法有些复杂
}

void Heapsort(Sqlist *L)
{
int i;
for(i=L->length/2;i>0;i--)//将一个无序列调整为大根堆,从n/2开始,比起小的均为分支节点
 HeapAdjust(L,i,L->length); //n
 //SortPrint(L);
 for(i=L->length;i>1;i--)
 {
 swap(&L->r[1],&L->r[i]);//堆顶与最后一个交换
 HeapAdjust(L,1,i-1);//生成数目减一的大根堆,从根节点开始调整

 }
}
//时间复杂度分析:主要操作为初始化堆和重建堆
/*  
 * 先来看初始化堆:堆的初始化主要是从最下层的分支节点与左右孩子比较,有必要时交换
 * 初始堆:最不利的情况是n/2*2=n次交换,时间复杂度O(n)
 * 在HeapAdjust中重建中时由于循环体j=2*j<=m,结合完全二叉树的性质即:从某一节点到根节点的距离log2i(向下取整)+1
 * j=2*j实际上和log2i+1循环次数相近,而Heapsort进行n-1的堆重建(伴随去n-1的堆顶记录),综合在重建堆时的时间复杂度为O(n*logn)
 */
void swap(Redtype *a,Redtype *b)
{
  Redtype t;
   t=*a;
   *a=*b;
   *b=t;
}