冒泡排序算法及其两种优化

冒泡排序算法及其两种优化

1、排序方法

将被排序的记录数组R[1..n]垂直排列,每个记录R[i]看作是重量为R[i].key的气泡。根据轻气泡不能在重气泡之下的原则,从下往上扫描数组R:凡扫描到违反本原则的轻气泡,就使其向上"飘浮"。如此反复进行,直到最后任何两个气泡都是轻者在上,重者在下为止。

(1)初始

     R[1..n]为无序区。

(2)第一趟扫描

 
   从无序区底部向上依次比较相邻的两个气泡的重量,若发现轻者在下、重者在上,则交换二者的位置。即依次比较(R[n],R[n-1]),(R[n-1],R[n-2]),…,(R[2],R[1]);对于每对气泡(R[j+1],R[j]),若R[j+1].key<R[j].key,则交换R[j+1]和R[j]的内容。

 第一趟扫描完毕时,"最轻"的气泡就飘浮到该区间的顶部,即关键字最小的记录被放在最高位置R[1]上。

(3)第二趟扫描

     扫描R[2..n]。扫描完毕时,"次轻"的气泡飘浮到R[2]的位置上……

 最后,经过n-1 趟扫描可得到有序区R[1..n]

注意:

     第i趟扫描时,R[1..i-1]和R[i..n]分别为当前的有序区和无序区。扫描仍是从无序区底部向上直至该区顶部。扫描完毕时,该区中最轻气泡飘浮到顶部位置R[i]上,结果是R[1..i]变为新的有序区。

2、冒泡排序过程动画演示

 

3、冒泡排序算法

(1)分析

 因为每一趟排序都使有序区增加了一个气泡,在经过n-1趟排序之后,有序区中就有n-1个气泡,所以整个冒泡排序过程至多需要进行n-1趟排序。

具体算法:

[cpp] view plain copy

  1. //冒泡排序
  2. void BubbleSort1(int* arr, size_t size)
  3. {
  4. assert(arr);
  5. int i = 0, j = 0;
  6. for (i = 0; i < size - 1; i++)//一共要排序size-1次
  7. {
  8. for (j = 0; j < size - 1 - i; j++)//选出该趟排序的最大值往后移动
  9. {
  10. if (arr[j] > arr[j + 1])
  11. {
  12. int tmp = arr[j];
  13. arr[j] = arr[j + 1];
  14. arr[j + 1] = tmp;
  15. }
  16. }
  17. }
  18. }

(2)优化1(优化外层循环):

 若在某一趟排序中未发现气泡位置的交换,则说明待排序的无序区中所有气泡均满足轻者在上,重者在下的原则,因此,冒泡排序过程可在此趟排序后终止。为此,在下面给出的算法中,引入一个标签flag,在每趟排序开始前,先将其置为0。若排序过程中发生了交换,则将其置为1。各趟排序结束时检查flag,若未曾发生过交换则终止算法,不再进行下一趟排序。

具体算法:

[cpp] view plain copy

  1. //冒泡排序优化1
  2. void BubbleSort2(int* arr, size_t size)
  3. {
  4. assert(arr);
  5. int i = 0, j = 0;
  6. for (i = 0; i < size - 1; i++)//一共要排序size-1次
  7. {
  8. //每次遍历标志位都要先置为0,才能判断后面的元素是否发生了交换
  9. int flag = 0;
  10. for (j = 0; j < size - 1 - i; j++)//选出该趟排序的最大值往后移动
  11. {
  12. if (arr[j] > arr[j + 1])
  13. {
  14. int tmp = arr[j];
  15. arr[j] = arr[j + 1];
  16. arr[j + 1] = tmp;
  17. flag = 1;//只要有发生了交换,flag就置为1
  18. }
  19. }
  20. //判断标志位是否为0,如果为0,说明后面的元素已经有序,就直接return
  21. if (flag == 0)
  22. {
  23. return;
  24. }
  25. }
  26. }

4、算法分析

(1)算法的最好时间复杂度

 若文件的初始状态是正序的,一趟扫描即可完成排序。所需的关键字比较次数C和记录移动次数M均达到最小值:

C(min)=n-1

M(min)=0。

 冒泡排序最好的时间复杂度为O(n)。

(2)算法的最坏时间复杂度

 若初始文件是反序的,需要进行n-1趟排序。每趟排序要进行n-i次关键字的比较(1≤i≤n-1),且每次比较都必须移动记录三次来达到交换记录位置。在这种情况下,比较和移动次数均达到最大值:

C(max)=n(n-1)/2=O(n^2)

M(max)=3n(n-1)/2=O(n^2)

 冒泡排序的最坏时间复杂度为O(n^2)。

(3)算法的平均时间复杂度为O(n^2)

 虽然冒泡排序不一定要进行n-1趟,但由于它的记录移动次数较多,故平均时间性能比直接插入排序要差得多。

(4)算法稳定性

 冒泡排序是就地排序,且它是稳定的。

5、算法优化2(优化内层循环)

(1)记住最后一次交换发生位置lastExchange的冒泡排序

  在每趟扫描中,记住最后一次交换发生的位置lastExchange,(该位置之后的相邻记录均已有序)。下一趟排序开始时,R[1..lastExchange-1]是无序区,R[lastExchange..n]是有序区。这样,一趟排序可能使当前无序区扩充多个记录,因此记住最后一次交换发生的位置lastExchange,从而减少排序的趟数。

具体算法:

[cpp] view plain copy

  1. //冒泡排序优化2
  2. void BubbleSort3(int* arr, size_t size)
  3. {
  4. assert(arr);
  5. int i = 0, j = 0;
  6. int k = size - 1,pos = 0;//pos变量用来标记循环里最后一次交换的位置
  7. for (i = 0; i < size - 1; i++)//一共要排序size-1次
  8. {
  9. //每次遍历标志位都要先置为0,才能判断后面的元素是否发生了交换
  10. int flag = 0;
  11. for (j = 0; j <k; j++)//选出该趟排序的最大值往后移动
  12. {
  13. if (arr[j] > arr[j + 1])
  14. {
  15. int tmp = arr[j];
  16. arr[j] = arr[j + 1];
  17. arr[j + 1] = tmp;
  18. flag = 1;//只要有发生了交换,flag就置为1
  19. pos = j;//循环里最后一次交换的位置 j赋给pos
  20. }
  21. }
  22. k = pos;
  23. //判断标志位是否为0,如果为0,说明后面的元素已经有序,就直接return
  24. if (flag == 0)
  25. {
  26. return;
  27. }
  28. }
  29. }

完整代码实现:

[cpp] view plain copy

  1. #include<iostream>
  2. using namespace std;
  3. #include<cassert>
  4. //冒泡排序
  5. void BubbleSort1(int* arr, size_t size)
  6. {
  7. assert(arr);
  8. int i = 0, j = 0;
  9. for (i = 0; i < size - 1; i++)//一共要排序size-1次
  10. {
  11. for (j = 0; j < size - 1 - i; j++)//选出该趟排序的最大值往后移动
  12. {
  13. if (arr[j] > arr[j + 1])
  14. {
  15. int tmp = arr[j];
  16. arr[j] = arr[j + 1];
  17. arr[j + 1] = tmp;
  18. }
  19. }
  20. }
  21. }
  22. //冒泡排序优化1
  23. void BubbleSort2(int* arr, size_t size)
  24. {
  25. assert(arr);
  26. int i = 0, j = 0;
  27. for (i = 0; i < size - 1; i++)//一共要排序size-1次
  28. {
  29. //每次遍历标志位都要先置为0,才能判断后面的元素是否发生了交换
  30. int flag = 0;
  31. for (j = 0; j < size - 1 - i; j++)//选出该趟排序的最大值往后移动
  32. {
  33. if (arr[j] > arr[j + 1])
  34. {
  35. int tmp = arr[j];
  36. arr[j] = arr[j + 1];
  37. arr[j + 1] = tmp;
  38. flag = 1;//只要有发生了交换,flag就置为1
  39. }
  40. }
  41. //判断标志位是否为0,如果为0,说明后面的元素已经有序,就直接return
  42. if (flag == 0)
  43. {
  44. return;
  45. }
  46. }
  47. }
  48. //冒泡排序优化2
  49. void BubbleSort3(int* arr, size_t size)
  50. {
  51. assert(arr);
  52. int i = 0, j = 0;
  53. int k = size - 1,pos = 0;//pos变量用来标记循环里最后一次交换的位置
  54. for (i = 0; i < size - 1; i++)//一共要排序size-1次
  55. {
  56. //每次遍历标志位都要先置为0,才能判断后面的元素是否发生了交换
  57. int flag = 0;
  58. for (j = 0; j <k; j++)//选出该趟排序的最大值往后移动
  59. {
  60. if (arr[j] > arr[j + 1])
  61. {
  62. int tmp = arr[j];
  63. arr[j] = arr[j + 1];
  64. arr[j + 1] = tmp;
  65. flag = 1;//只要有发生了交换,flag就置为1
  66. pos = j;//循环里最后一次交换的位置 j赋给pos
  67. }
  68. }
  69. k = pos;
  70. //判断标志位是否为0,如果为0,说明后面的元素已经有序,就直接return
  71. if (flag == 0)
  72. {
  73. return;
  74. }
  75. }
  76. }
  77. int main()
  78. {
  79. int arr[5] = { 5,4,3,2,1 };
  80. cout << "初始顺序为:";
  81. for (int i = 0; i < 5; i++)
  82. {
  83. cout << arr[i] << " ";
  84. }
  85. cout << endl;
  86. /*BubbleSort1(arr, 5);*/
  87. /*BubbleSort2(arr, 5);*/
  88. BubbleSort3(arr, 5);
  89. cout << "冒泡排序后顺序为:";
  90. for (int i = 0; i < 5; i++)
  91. {
  92. cout << arr[i] << " ";
  93. }
  94. cout << endl;
  95. system("pause");
  96. return 0;
  97. }

运行结果:

初始顺序为:5 4 3 2 1

冒泡排序后顺序为:1 2 3 4 5

请按任意键继续. . .

时间: 2024-10-13 02:15:27

冒泡排序算法及其两种优化的相关文章

【总结】冒泡排序及冒泡排序的两种优化

------------------------------------------------------------------------------------------------------ 冒泡排序(bubble sort)算法的运作如下:从前往后一次比较相邻的两个元素,如果第二个比第一个元素小,则交换这两个元素,一直到与最后一个元素比较完成,这样最大的元素就放到了最后:这样重复比较(n-1)次即可完成排序. -----------------------------------

冒泡排序及两种优化方式

冒泡排序是最常用的小型数据排序方式,下面是用C语言实现的,及其两种优化方式. 第一种优化方式是设置一个标记位来标记是否发生了交换,如果没有发生交换就提前结束: 第二种优化方式是记录最后放生交换的位置,作为下一趟比较结束的位置. #include <stdio.h> /* * 打印数组 * */ void printArray(int arr[], int n) { int i = 0; for (i = 0; i < n; ++i) { printf("%d ", a

冒泡排序算法以及它的优化方案

一.什么是冒泡排序? 冒泡排序(Bubble Sort)是一种最为基础的交换排序,相信学过C语言的,都接触过这种排序算法. 这篇文章重点应该放在优化上面. 二.冒泡排序的实现思想: 将数组里面相邻的元素两两比较,根据大小来交换元素位置,举个栗子: 这里有一个数组array[4, 6, 5, 8, 9, 3, 2, 1, 7], 首先4和6比较,4小于6,位置不变,接下来6和5比较,6大于5,所以6和5的位置对调,数组变成[4, 5, 6, 8, 9, 3, 2,1, 7],由于6和5位置对调,接

hdu3572--Task Schedule(最大流+两种优化方法,dinic)

Task Schedule Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 3651    Accepted Submission(s): 1271 Problem Description Our geometry princess XMM has stoped her study in computational geometry t

算法—比较两种排序算法:选择排序和插入排序

现在我们已经实现了两种排序算法,我们很自然地想知道选择排序和插入排序哪种更快.这里我们第一次用实践说明我们解决这个问题的办法. 性质:对于随机排序的无重复主键的数组,插入排序和选择排序的运行时间是平方级别的,两者之比应该是一个较小的常数. 例证:这个结论在过去的半个世纪中已经在许多不同类型的计算机上经过了验证.在1980年本书第一版完成之时插入排序就比选择排序快一倍,现在仍然是这样,尽管那时这些算法将10万条数据排序需要几个小时而现在只需要几秒钟.在你的计算机上插入排序也比选择排序快一些吗?可以

冒泡排序--两种优化方式

原始版本 void bubble_sort(int arr[],int n){ int tmp; for (int i = 0; i < n; ++i) { for (int j = 0; i < n; ++j) { if (arr[j] < arr[j+1]) { tmp = arr[j]; arr[j] = arr[j+1]; arr[j+1] = tmp; } } } } 优化版本一 如果内层循环没有进行交换,说明后面的元素已经有序,则不需要继续循环.因此,我们可以设置一个标记来标

冒泡算法的两种方法(java描述)

(端午放假今天终于回来了,打起鸡血继续) 之前学c的时候学过一种简单的冒泡算法,比较好理解,今天在书上看到了另一种,有些复杂,看了好一会在弄明白. 以数组num[]={8,3,5,4,2,6}为例: 先说以前学的那种方法: 1 int num[]={8,3,5,4,2,6}; 2 for(int i=0;i<num.length;i++){ 3 for(int j=0;j<num.length-i-1;j++ ){ 4 if(num[j]>num[j+1]){ 5 int temp=nu

关于兔子算法的两种方法

// 有一对幼兔,幼兔1个月后长成小兔,小兔1个月后长成成兔并生下一对幼兔,//问几年后有多少对兔子,幼兔.小兔.成兔对数分别是多少.// x            y      z//1 幼兔 1 小兔 0 成兔 0//2 幼兔 0 小兔 1 成兔 0 // 3 幼兔 1 小兔 0 成兔 1// 4 幼 兔 1 小兔 1 成兔 1// 5 幼兔 2 小兔 1 成兔 2//6 幼兔 3 小兔 2 成兔 3// 7 幼兔 5 小兔 3 成兔 5////当前月份幼兔 = 上个月的成兔+上月小兔//当

算法:两种方式(递归/循环)实现二分查找

程序由Golang实现,代码如下: 1.使用递归实现二分查找 //使用递归进行二分查找 func binarySearchTest() { //二分查找的数组必须是已经排好序的 nums := []int{1, 3, 5, 6, 7, 10, 12, 15, 17, 18, 19, 20, 21} value := 2 var index int = searchIndex(nums, value) fmt.Println("index:", index) } //在nums中查找元素