八大排序算法之交换排序
来源:互联网 发布:docker nginx负载均衡 编辑:程序博客网 时间:2024/06/11 19:54
上一篇博文中已经分享了八大排序算法中两种插入排序算法--直接插入排序和希尔排序。本文来继续介绍剩下的排序算法--交换排序,包括冒泡排序和快速排序。其中关于冒泡排序还会介绍其改进后的算法,而对于快速排序则会介绍递归和非递归两种形式。
说明:排序还是以从小到大为例。
一、冒泡排序
1.基本思想
冒泡排序算法是比较常用而且特别容易理解的排序算法。每一趟冒泡过程挑选出一个最大或最小的数,N个数只需要N-1趟就可以排好序。下面直接介绍它的排序过程,相信大家一看就懂。
2.排序过程
每一趟排序过程中,相邻两个元素之间相互比较,如果前面的元素比后面的元素值要大,则交换两者位置,直到交换到最后一个元素,此时最大的元素就放在了最后一个位置。具体过程如下:
3.代码实现
#include <stdio.h>void BubbleSort(int *pArray, int iLen);//冒泡排序void PrintArray(int *pArray, int iLen);void Swap(int *lhs, int *rhs);int main(void){ int iArray[] = {9, 7, 6, 5, 3 }; int iLen = sizeof(iArray) / sizeof(*iArray); //计算数组元素个数 PrintArray(iArray, iLen); ReQuickSorck(iArray, iLen); PrintArray(iArray, iLen); return 0;}void Swap(int *lhs, int *rhs){ int iTemp = *lhs; *lhs = *rhs; *rhs = iTemp;}void PrintArray(int *pArray, int iLen){ for (int iIndex = 0; iIndex < iLen; ++iIndex) { printf("%5d", pArray[iIndex]); } printf("\n");}void BubbleSort(int *pArray, int iLen){ int iCount; int iIndex; //控制循环趟数,N个数需要N-1趟 for (iCount = 0; iCount < iLen - 1; ++iCount) { //每次从第0个开始比较,这里的iLen-iCount-1是因为将上一个已经排好序的元素排除在本次排序过程之外,减少比较次数 for (iIndex = 0; iIndex < iLen - iCount - 1; ++iIndex) { if (pArray[iIndex] > pArray[iIndex + 1]) { Swap(&pArray[iIndex], &pArray[iIndex + 1]); } } }}
4.算法分析(时间复杂度、空间复杂度和稳定性)
(1)时间复杂度
从上面的代码中可以看出,嵌套了两层循环,而且循环次数为(N-1)+(N-2)+.......+1=(N^2-3N+2)/2,所以时间复杂度为O(N^2)。
(2)空间复杂度
在交换两个相邻元素的位置的Swap函数里面有一个临时变量iTemp,所以空间复杂度为O(1)。
(3)稳定性
稳定。
5.改进后的冒泡排序
以上程序有一个不好的地方,那就是对于最好的情况(数据原始就有序)的时间复杂度也为O(N^2)。对于改善这种情况,从上面的代码可以知道,出现这种情况的原因在于即使原始数据有序,但是每趟排序还是进行了两两比较。我们发现如果有一趟排序过程中,没有任何相邻的两个元素发生交换,则说明数据已经有序,没有必要再进行下一趟排序了。这是我们可以使用一个标志bFlag来标识这种状态,代码如下:
void BubbleSort_(int *pArray, int iLen)//改进后的冒泡排序{ int iCount; int iIndex; bool bFlag = true; //初始化标识 for (iCount = 0; iCount < iLen - 1 && bFlag; ++iCount) { bFlag = false; //置为false,如果下面的循环中没有交换元素,则说明数据已经有序 for (iIndex = 0; iIndex < iLen - 1 - iCount; ++iIndex) { if (pArray[iIndex] > pArray[iIndex + 1]) { bFlag = true; Swap(&pArray[iIndex], &pArray[iIndex + 1]); } } }}
二、快速排序
1.递归版快速排序
(1)基本思想
快速排序的过程分为两步,一是先取一个基准元素进行划分,二是根据找到基准元素的位置放入。一般取第一个元素为基准元素。具体过程是从右向左找第一个比基准小的元素,然后将该元素放入基准的位置;然后从左向右找第一个比基准大的元素,将该元素放入上一个找到的比基准小元素的位置;以此左右交替直到找到基准元素该放入的位置。
(2)排序过程
下面的图显示了一趟快速排序过程,当low和high相遇时说明一趟排序过程完成,然后将基准元素放入两者相遇的位置,进行下一趟排序。
(3)代码实现及运行结果
#include <stdio.h>#include <stdlib.h>void Swap(int *lhs, int *rhs);void PrintArray(int *pArray, int iLen);void QuickSort(int *pArray, int iLen);void Quick(int *pArray, int low, int high);int Partition(int *pArray, int low, int high);int main(void){ int iArray[] = { 9, 7 , 6,5, 3 }; int iLen = sizeof(iArray) / sizeof(*iArray); //计算数组元素个数 PrintArray(iArray, iLen); QuickSort(iArray, iLen); PrintArray(iArray, iLen); return 0;}void QuickSort(int *pArray, int iLen){ Quick(pArray, 0, iLen - 1);}//递归排序void Quick(int *pArray, int low, int high){ int pivot = Partition(pArray, low, high); if (pivot > low + 1) { Quick(pArray, low, pivot - 1); } if (pivot < high - 1) { Quick(pArray, pivot + 1, high); }}int Partition(int *pArray, int low, int high){ int iTemp = pArray[low]; while (low < high) { //从右向左找第一个比基准元素小的元素 while (low < high && pArray[high] >= iTemp) { --high; } if (low == high) //说明一次划分完成 { break; } else { pArray[low] = pArray[high]; } //从左向右找第一个比基准大的元素 while (low < high && pArray[low] <= iTemp) { ++low; } if (low == high) //说明一次划分完成 { break; } else { pArray[high] = pArray[low]; } } //将基准元素放入low和high相遇的位置 pArray[low] = iTemp; return low;//返回基准元素的位置,便于进行下一次划分}void Swap(int *lhs, int *rhs){ int iTemp = *lhs; *lhs = *rhs; *rhs = iTemp;}void PrintArray(int *pArray, int iLen){ for (int iIndex = 0; iIndex < iLen; ++iIndex) { printf("%5d", pArray[iIndex]); } printf("\n");}
(4)算法分析(时间复杂度、空间复杂度和稳定性)
a)时间复杂度
时间花费的时间主要用于划分过程,从Partition过程来看,整个函数执行完时,正好是把整个序列遍历了一遍。虽然是双层循环,但其复杂度为O(N)。因为这是一个递归的过程,而且每一次划分完以后将原序列一分为二(基准元素左边和基准元素右边),有点二叉树的感觉,所以会递归O(logN)次。将两者结合起来,整个算法的时间复杂度为O(NlogN)。
b)空间复杂度
如果单看一趟划分过程,则从上面代码可以看出来空间复杂度为O(1)。但是因为这是一个递归的过程,所以在上一次函数的临时变量没有释放时又进行了下一次划分。所以空间复杂度为O(logN)。
c)稳定性
不稳定。
2.非递归版快速排序
任何递归程序都可以借助栈进行非递归实现。直接上代码:
void NCQuickSort(int *pArray, int iLen){ int iLow = 0; int iHigh = iLen - 1; int *pStack = (int*)malloc(sizeof(int)*(2 * iLen - 1)); int iIndex = 0; pStack[iIndex++] = iLow; //注意这里是 pStack[iIndex++] = iHigh; while (iIndex > 0) { iHigh = pStack[--iIndex]; iLow = pStack[--iIndex]; int pivot = Partition(pArray, iLow, iHigh); if (pivot > iLow + 1) { pStack[iIndex++] = iLow; pStack[iIndex++] = pivot - 1; } if (pivot < iHigh - 1) { pStack[iIndex++] = pivot + 1; pStack[iIndex++] = iHigh; } }}
需要注意的一点是,栈的特点是先进后出,如果是先把low入栈,则先出栈的时high.
阅读全文
0 0
- 八大排序算法之交换排序
- 八大排序算法之交换排序
- 八大排序之交换排序
- 八大排序 -- 交换排序之【冒泡排序】
- 八大排序--交换排序 之 快速排序
- 排序算法之八大算法
- 八大排序算法之插入排序算法
- 排序算法之交换排序
- 排序算法之交换排序
- 排序算法之交换排序
- 排序算法之交换排序
- 排序算法之交换排序
- 排序算法之交换排序
- 算法之交换排序
- 八大排序算法之基数排序
- 数据结构之八大排序算法
- 八大算法之冒泡排序
- 八大排序之冒泡算法
- 矩阵快速幂 模板
- AndroidStudio如何引入so包
- docker 学习(1)
- HDU 6070 Dirt Ratio
- 排序之插入排序
- 八大排序算法之交换排序
- JNA的使用
- hdu 5047 Sawtooth
- 网页中分享功能
- 7.jdbc
- sed,awk笔记
- LCA
- java接口
- 谷歌访问助手(chrome版)安装方法