冒泡排序及其代码优化(C语言实现)
定义
冒泡排序是从第一个元素开始,重复走访要排序的元素,如果两个元素位置相反,则进入交换,最后使得整个数组是一个有序数组。
冒泡排序(未优化)
算法思想
假设原数组为{3,1,7,8,2,5,3,6},数组为整型数组,数组长度为8。
根据冒泡排序的定义,我们从第一个元素开始,以数组长度为循环次数,开始进行比较:
-
比较 3 和 1
由于3>1,故交换位置:
-
比较 3 和 7
由于3<7,故不交换位置:
-
比较 7 和 8
由于7<8,故不交换位置:
-
比较 8 和 2
由于8>2,故交换位置:
-
比较 8 和 5
由于8>5,故交换位置:
-
比较 8 和 3
由于8>3,故交换位置:
-
比较 8 和 6
由于8>6,故交换位置:
此时,第一大轮循环比较结束,我们开始进行第二~N轮循环比较。
循环比较的过程为
当进行数组长度次大轮循环比较后,我们可以得到一个完全有序的数组,即
代码实现(未优化版)
#include<stdio.h>
//打印数组
void print(int a[], int length)
{
for (int i = 0; i < length; i++) {
printf(" %d", a[i]);
}
printf("\n");
}
void bubble1(int a[], int length)
{
//i,j为循环变量,temp为中间变量
int i, j, temp = 0;
//打印原数组
printf("原数组为:");
print(a, length);
//进行大轮循环
for (i = 0; i < length - 1; i++) {
//寻找每一次大循环内的最大值
for (j = 0; j < length - 1; j++) {
//判断是否需要交换位置
if (a[j] > a[j + 1]) {
//交换位置
temp = a[j];
a[j] = a[j + 1];
a[j + 1] = temp;
}
}
}
//打印排序后的数组
printf("冒泡排序后的数组为:");
print(a, length);
}
int main()
{
int a[8] = { 3,1,7,8,2,5,3,6 };
bubble1(a, 8);
}
优化比较次数
算法思想
我们观察下图:
通过观察,我们不难发现:每次比较后,我们都能固定数组中的一位元素,由于我们是增序排列,所以每次固定的一位元素都是靠右固定,并且,我们发现固定元素的位置与循环次数存在关系,即:固定元素位置(数组下标)=数组长度-循环次数,然而如果数组除第一个元素外,其他元素位置都是固定的,那么相等于数组的第一个元素位置也是固定的。所以,我们可以根据此原理进行优化。
代码实现(优化比较次数)
#include<stdio.h>
//打印数组
void print(int a[], int length)
{
for (int i = 0; i < length; i++) {
printf(" %d", a[i]);
}
printf("\n");
}
void bubble1(int a[], int length)
{
//i,j为循环变量,temp为中间变量
int i, j, temp = 0;
//打印原数组
printf("原数组为:");
print(a, length);
//进行大轮循环
for (i = 0; i < length - 1; i++) {
//寻找每一次大循环内的最大值
//length -1 -i -->优化了比较次数,使得固定的元素不再进行比较
for (j = 0; j < length - 1 - i; j++) {
//判断是否需要交换位置
if (a[j] > a[j + 1]) {
//交换位置
temp = a[j];
a[j] = a[j + 1];
a[j + 1] = temp;
}
}
}
//打印排序后的数组
printf("冒泡排序后的数组为:");
print(a, length);
}
int main()
{
int a[8] = { 3,1,7,8,2,5,3,6 };
bubble1(a, 8);
}
优化冒泡次数
算法思想
我们看另一个数组:
这个数组是有序的,但如果按上边的代码进行冒泡排序,我们不难发现,它还是正常的跑完了,没有在有序的时候进行跳出循环,浪费了很多资源。所以,我们能不能设置个变量,判断数组是否有序。如果判断到数组有序后,我们直接跳出循环,不再进行冒泡呢?
设想,如果设置个bool型变量"res",如果交换,则改变res值,如果不变,则说明了这次循环没有交换元素,此时数组是有序的。
代码实现(优化冒泡次数)
#include<stdio.h>
//打印数组
void print(int a[], int length)
{
for (int i = 0; i < length; i++) {
printf(" %d", a[i]);
}
printf("\n");
}
void bubble1(int a[], int length)
{
//i,j为循环变量,temp为中间变量
int i, j, temp = 0;
//打印原数组
printf("原数组为:");
print(a, length);
//进行大轮循环
for (i = 0; i < length - 1; i++) {
//定义判断有序标志
bool res = false;
//寻找每一次大循环内的最大值
for (j = 0; j < length - 1 - i; j++) {
//判断是否需要交换位置
if (a[j] > a[j + 1]) {
//交换位置
temp = a[j];
a[j] = a[j + 1];
a[j + 1] = temp;
//如果交换,则不有序-->res=true
res = true;
}
}
//如果res等于初始值,则说明元素未交换,数组已有序,结束冒泡排序
if (!res)
break;
}
//打印排序后的数组
printf("冒泡排序后的数组为:");
print(a, length);
}
int main()
{
int a[8] = { 1,2,3,4,5,6,7,8 };
bubble1(a, 8);
}
进一步优化比较次数
算法思想
根据“优化比较次数”代码思想,我们发现“固定元素位置(数组下标)=数组长度-循环次数”这个规律,接下来,我们观察下表:
能看到,当确定 8 的位置时,其实,我们不用在循环到”8前边那个位置,因为我们比较的时候,比较的是—— a[j] 和 a[j+1] 的关系,所以对于第一大轮循环
当我们确定到 8 的位置后,我们下一次循环可以只循环到 3 的位置,如果 3<6 ,则我们交换两者位置,把 3 的数组下标记录下来,继续进行下一次循环,如果 3>6 ,则直接进行下一次循环。当这轮循环进行结束后,最终记录下来的数组下标就是下一次循环比较的次数。
代码实现(进一步优化比较次数)
#include<stdio.h>
//打印数组
void print(int a[], int length)
{
for (int i = 0; i < length; i++) {
printf(" %d", a[i]);
}
printf("\n");
}
void bubble1(int a[], int length)
{
//i,j为循环变量,temp为中间变量
int i, j, temp = 0;
//记录比较次数
int n = length - 1;
//打印原数组
printf("原数组为:");
print(a, length);
//进行大轮循环
while (1) {
//设置比较结束位置
int last = 0;
//寻找每一次大循环内的最大值
for (j = 0; j < n; j++) {
//判断是否需要交换位置
if (a[j] > a[j + 1]) {
//交换位置
temp = a[j];
a[j] = a[j + 1];
a[j + 1] = temp;
last = j;
}
}
n = last;
//如果n==0,说明,未发生交换,数组有序,结束冒泡
if (n == 0)
break;
}
//打印排序后的数组
printf("冒泡排序后数组为:");
print(a, length);
}
int main()
{
int a[8] = { 3,1,7,8,2,5,3,6 };
bubble1(a, 8);
}