赞
踩
目录
例如:对初始数组:[5, 1, 4, 2, 8, 0, 3]
冒泡排序(Bubble Sort)是计算机科学领域中最简单的排序算法之一,广泛应用于学术教学和初学者编程实践。它之所以得名“冒泡”,是因为数据交换的过程类似于水中气泡的上升,较小或较大的元素经过一系列交换后“浮”到数组的顶端。尽管在实际应用中由于其较低的效率并不常用,冒泡排序仍是理解和学习其他复杂排序算法的基础。本文将详细介绍C语言实现的冒泡排序算法及其扩展。
冒泡排序的核心思想是通过重复遍历要排序的数列,一次比较两个元素,如果它们的顺序错误就把它们交换过来。遍历数列的工作是重复进行的,直到没有再需要交换的元素,这表示数列已经排序完成。
根据其原理我们很快能写出一个可以对整形数列排序的程序,如下:
- #include <stdio.h>
-
- void bubbleSort(int arr[], int sz)
- {
- int i, j, temp;
- for (i = 0; i < sz-1; i++)
- {
- // 最后 i 个已经排序好了
- for (j = 0; j < sz-i-1; j++)
- {
- if (arr[j] > arr[j+1])
- {
- // 交换两个元素
- temp = arr[j];
- arr[j] = arr[j+1];
- arr[j+1] = temp;
- }
- }
- }
- }
-
- int main()
- {
- int arr[] = {64, 34, 25, 12, 22, 11, 90};
- int sz = sizeof(arr)/sizeof(arr[0]);
- bubbleSort(arr, sz);
- printf("排序后的数组: \n");
- for (int i = 0; i < sz; i++)
- printf("%d ", arr[i]);
- printf("\n");
- return 0;
- }
bubbleSort
函数接受一个整数数组 arr
和该数组的长度 n
作为参数。arr[j] > arr[j+1]
),我们就交换它们的位置。虽然冒泡排序不是很高效,但我们仍然可以对其进行一些优化,以减少排序过程中的比较和交换次数。
- void BubbleSort-2(int arr[], int sz) {
- int i, j, temp;
- int swapped;
- for (i = 0; i < sz - 1; i++) {
- swapped = 0;
- for (j = 0; j < sz - i - 1; j++) {
- if (arr[j] > arr[j + 1]) {
- temp = arr[j];
- arr[j] = arr[j + 1];
- arr[j + 1] = temp;
- swapped = 1;
- }
- }
- // 如果在这一轮排序中没有发生交换,则说明数组已经有序,可以结束排序
- if (swapped == 0)
- break;
- }
-
通过设置一个标志位swapped
,如果在某一轮排序结束后没有任何元素交换位置,那么可以提前结束排序,因为剩余的元素已经是有序的了。
第一趟排序:
swapped
设置为 1。第一趟排序结束后数组是 [2, 4, 3, 5, 7, 8],发生了交换,因此需要继续排序。
第二趟排序:
swapped
设置为 1。由于最后两个元素已经是排序好的,所以这趟排序只进行了三次比较。
第二趟排序结束后数组是 [2, 3, 4, 5, 7, 8],发生了交换,继续排序。
第三趟排序:
第三趟排序没有发生任何交换,swapped
仍然是 0,这意味着数组已经是有序的。根据优化冒泡排序的逻辑,排序操作在这里结束。
最终排序后的数组是 [2, 3, 4, 5, 7, 8],此时排序已完成。
通过这个例子,你可以看到,尽管初始数组部分无序,但是在第三趟排序之后,我们检测到没有发生交换,从而得知数组已经是有序的,无需进行额外的排序,这就是 optimizedBubbleSort
函数中引入 swapped
变量的优势所在。
优化后的冒泡排序可以在最好的情况(数组已经有序)下达到 O(n) 的时间复杂度,而平均和最坏的情况时间复杂度仍然是 O(n^2)。这种优化虽然在最坏情况下并不改善时间复杂度,但在最好情况下或者数组已经接近排序完成的情况下,可以大大减少排序所需的时间。
鸡尾酒排序(Cocktail Sort),也被称为定向冒泡排序。这个算法是冒泡排序的一个变体,它在每轮迭代中会在数组的两个方向上进行排序
- void cocktailSort(int arr[], int sz)
- {
- int i, left = 0, right = sz - 1, temp;
- int swapped = 1;
- while (swapped)
- {
- swapped = 0;
- for (i = left; i < right; ++i)
- {
- if (arr[i] > arr[i + 1])
- {
- temp = arr[i];
- arr[i] = arr[i + 1];
- arr[i + 1] = temp;
- swapped = 1;
- }
- }
- if (!swapped)
- {
- break;
- }
- swapped = 0;
- --right;
- for (i = right - 1; i >= left; --i)
- {
- if (arr[i] > arr[i + 1])
- {
- temp = arr[i];
- arr[i] = arr[i + 1];
- arr[i + 1] = temp;
- swapped = 1;
- }
- }
- ++left;
- }
- }
我们按步骤使用鸡尾酒排序来排序这个数组
正向遍历(从左到右):
此时,最大的元素8已到达它正确的位置。
反向遍历(从右到左):
end
现在指向数组的倒数第二个元素,因为最后一个元素8已经在正确的位置。
经过第一轮鸡尾酒排序:[1, 4, 5, 2, 3, 0, 8]
重复这个过程:接下来我们继续从左到右,然后是从右到左,交替进行,每次遍历结束,start
会增加1,end
会减少1,缩小未排序的范围。
在几轮迭代之后,数组将完全有序:
完全排序后的数组:[0, 1, 2, 3, 4, 5, 8]
在这个排序过程中,你可以看到鸡尾酒排序是如何在数组的两端迭代地进行排序,每次正向遍历将最大的元素置于其最终位置,反向遍历则是将最小的元素置于其最终位置。这种排序方式适合于大部分元素已经接近于其最终位置的情况,在这种场景下,鸡尾酒排序的效率通常会比传统的冒泡排序要好。
在探讨排序算法时,我们不得不提及C语言标准库中的qsort
函数,这是一个灵活且强大的快速排序实现。qsort
可以对数组进行排序,无论其元素是什么类型,只要提供了一个比较函数。
qsort
函数概述在C语言中,qsort
函数是快速排序算法的实现,定义在stdlib.h
头文件中。其函数原型如下:
- void qsort(void *base, size_t nmemb, size_t size,
- int (*compar)(const void *, const void *));
这里,base
是指向待排序数组的第一个对象的指针,nmemb
是数组中的元素数量,size
是每个元素的大小,而compar
是一个函数指针,指向一个比较两个元素的函数。这个比较函数必须返回一个整数,表示两个元素的相对顺序。
由于qsort
是基于快速排序算法,它在效率上通常优于简单排序算法,如冒泡排序。其平均时间复杂度为O(n log n),是处理大型数据集的理想选择。
而对于冒泡排序,我门或许可以借鉴函数qsort
函数对上面的程序进行优化,使冒泡排序可以同样对不同的数据进行排序。
下面是实现的代码
- int cmp_int(const void* p1, const void* p2)
- {
- return *(int*)p1 - *(int*)p2;
- }
- void Swap(char* buf1, char*buf2, size_t width)
- {
- int i = 0;
- for (i = 0; i < width; i++)
- {
- char tmp = *buf1;
- *buf1 = *buf2;
- *buf2 = tmp;
- buf1++;
- buf2++;
- }
- }
- //接收一个指向数据首地址的void*类型指针、数组中元素的数量、每个元素的大小以及一个比较函数指针。
- void bubble_sort2(void* base, size_t sz, size_t width, int (*cmp)(const void*p1, const void*p2))
- {
- for (int i = 0; i < sz - 1; i++)
- {
- //每一趟冒泡排序的过程
- for (int j = 0; j < sz - 1 - i; j++)
- {
- if(cmp((char*)base + j * width, (char*)base + (j + 1) * width)>0)
- {
- Swap((char*)base + j * width, (char*)base + (j + 1) * width, width);
- }
- }
- }
- }
-
- int main()
- {
- //设计和实现bubble_sort2(),这个函数能够排序任意类型的数据
- int arr[] = { 3,1,5,7,9,2,4,0,8,6 };
- int sz = sizeof(arr) / sizeof(arr[0]);
- bubble_sort2(arr, sz, sizeof(arr[0]), cmp_int);
- for (int i = 0; i < sz; i++)
- {
- printf("%d ", arr[i]);
- }
- printf("\n");
- return 0;
- }
cmp_int
函数
cmp_int
函数是一个比较函数,用于冒泡排序中比较两个整数的大小。它接受两个const void*
类型的指针参数,强转为int*
类型后进行比较,返回它们之间的差值。如果第一个数较大,返回正值;如果第二个数较大,返回负值;如果相等,返回零。
Swap
函数
Swap
函数用于交换两个数据项。由于冒泡排序需要交换元素位置,Swap
通过操作字节(char
类型数据),而不是具体类型的数据,这个函数能够交换任何大小和类型的数据,只要传递正确的字节宽度。在这个函数内,使用了一个循环来交换每一个字节,从而实现任意类型数据的交换。
通过这种方式,我们将冒泡排序实现通用化了。这个函数接受任意类型的数组和元素大小,使之类似于qsort
。
完
以上如有写错的地方请多多指教。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。