内部排序算法2(交换排序)
基本思路
两两比较待排序的元素,交换不满足顺序要求的那些元素对,直到所有元素的次序都满足顺序要求为止。
起泡排序
基本思想
设待排序元素序列中的元素个数为n,首先比较第n-2个元素和第n-1个元素,如果发生逆序(即前一个大于后一个),则将两个元素交换;然后对第n-3和第n-2个元素(可能是刚交换过来的)做同样的处理;重复此过程,直到处理完第0个和第1个元素,至此称为一趟起泡,结果将最小的元素交换到待排序元素序列的第一个位置,其他元素也都像排序的最终位置移动。下一趟排序时,前一趟确定的最小元素不在参加比较,待排序序列减少一个元素,一趟起泡的结果又把序列中最小的元素排到序列的第一个位置,……,这样做多n-1次起泡就能把所有的元素排好序。
算法实现
//起泡排序头文件
#pragma once
#include<iostream>
typedef int DataType;
class BuBBleSort
{
public:
BuBBleSort(int length);
void create();
void print();
void sort();
~BuBBleSort();
private:
int len;
DataType *data;
};
BuBBleSort::BuBBleSort(int length)
{
len = length;
data = new DataType[len];
}
inline void BuBBleSort::create()
{
std::cout << "请输入要排序的序列: " << std::endl;
for (int i = 0; i < len; i++)
{
int temp;
std::cin >> temp;
data[i] = temp;
}
std::cout << "输入完毕" << std::endl;
}
inline void BuBBleSort::print()
{
for (int i = 0; i < len; i++)
{
std::cout << data[i] << " ";
}
std::cout << std::endl;
}
inline void BuBBleSort::sort()
{
for (int i = 0; i < len; i++)
{
int exchange = 0; //检查是否存在逆序,若无逆序,则意味着已经排好序了,退出循环
for (int j = len - 1; j > i; --j)
{
if (data[j] < data[j - 1])
{
exchange = 1;
int temp = data[j];
data[j] = data[j - 1];
data[j - 1] = temp;
}
}
if (exchange == 0)
{
return;
}
std::cout << "第 " << i + 1 << "次起泡排序: ";
print();
}
}
BuBBleSort::~BuBBleSort()
{
delete[] data;
}
//起泡排序main文件
using namespace std;
#include "bubbleSort.h"
int main() {
BuBBleSort insertSort(10); //排序的序列中有十个元素
insertSort.create(); //输入十个数
insertSort.sort(); //排序
system("pause");
}
结果
算法分析
时间复杂度
起泡排序算法的排序码比较次数和元素移动次数均受待排序元素的初始排序影响。最好的情况是待排序元素序列已经有序,只需要一趟起泡,算法就可以结束了,需要n-1次比价和0次的移动。最坏的情况是待排序元素为逆序,需要进行n-1趟起泡,其排序码需要 ∑n−1i=0(n−i) = n(n−1)2 次比较和 ∑n−1i=13(n−i) = 3n(n−1)2 次移动。
空间复杂度
起泡排序算法的空间复杂度为O(1),只需要一个用于数据交换的工作单元和一个控制排序过程结束的标志变量。
算法的稳定性
起泡排序算法是稳定的。
快速排序算法
思想
任取待排序元素序列中的某个元素(例如取第一个元素)作为轴点,按照该元素的排序码大小将整个元素序列划分为左右两个子序列:左侧子序列中所有元素的排序码都小于轴点元素的排序码,右侧子序列中所有元素的排序码都大于或者等于轴点元素的排序码,轴点元素则排在这两个子序列中间(这也是该元素最终应安放的位置)。然后分别对这两个子序列重复施行上述方法,直到所有的元素都排在相应位置上为止。
图示
图片来自:http://www.cnblogs.com/surgewong/p/3381438.html
算法实现
//快速排序头文件
#pragma once
#include<iostream>
typedef int DataType;
class QuickSort
{
public:
QuickSort(int length);
void create();
void print(int pos);
void sort();
~QuickSort();
private:
int len;
DataType *data;
int partition(int low, int high);
void sortHelp(int low, int high);
};
QuickSort::QuickSort(int length)
{
len = length;
data = new DataType[len];
}
inline void QuickSort::create()
{
std::cout << "请输入要排序的序列: " << std::endl;
for (int i = 0; i < len; i++)
{
int temp;
std::cin >> temp;
data[i] = temp;
}
std::cout << "输入完毕" << std::endl;
}
inline void QuickSort::print(int pos)
{
for (int i = 0; i < len; i++)
{
if (i == pos)
{
std::cout << "<" << data[pos] << "> "; //用特殊标记输出轴点
}
else
{
std::cout << data[i] << " ";
}
}
std::cout << std::endl;
}
inline void QuickSort::sort()
{
sortHelp(0, len - 1);
}
QuickSort::~QuickSort()
{
delete[] data;
}
inline int QuickSort::partition(int low, int high) //一次划分
{
DataType temp = data[low]; //记录轴点的值
while (low < high)
{
while (low < high && data[high] >= temp) //从后往前寻找比轴点要小的值
{
--high;
}
if (low < high)
{
data[low++] = data[high]; //交换
}
while (low < high && data[low] <= temp) //从前往后找比轴点要大的值
{
++low;
}
if (low < high)
{
data[high--] = data[low]; //交换
}
}
data[low] = temp; //当low等于high的时候,该坐标就是轴点的位置。
return low;
return 0;
}
inline void QuickSort::sortHelp(int l, int h)
{
if (l < h)
{
int pos = partition(l, h);
print(pos);
sortHelp(l, pos - 1);
sortHelp(pos + 1, h);
}
}
//快速排序main文件
using namespace std;
#include"QuickSort.h"
int main() {
QuickSort insertSort(8); //排序的序列中有十个元素
insertSort.create(); //输入十个数
insertSort.sort(); //排序
system("pause");
}
算法分析
时间复杂度
如果每次划分对一个元素定位后,该元素的左侧子序列与右侧子序列的长度相同,则下一步将是对两个长度减半的子序列进行排序,这是最理想的情况。在n个元素的序列中,对一个元素定位所需的时间为O(n)。若设T(n)是对n个元素的序列进行排序所需的时间,而且每次对一个元素正确定位后,正好把序列划分为长度相等的两个子序列,此时,总的计算时间为:
T(n)<=cn+2T(n/2)<=cn+2(cn/2+2T(n/4))=2cn+4T(n/4)<=2cn+4(cn/4+2T(n/8))=3cn+8T(n/8)……<=cnlog2n+nT(1)=O(nlog2n)
实验结果表明:就平均计算时间而言,快速排序是我们所讨论的所有内排序方法中最好的一个。
因为每次都选用序列的第一个元素作为比较的轴点元素,在最坏的情况,即待排序元素序列已经按其排序码从小到大排好序的情况下,每次划分只得到一个比上一次少一个元素的子序列。这样,必须经过n-1趟才能把所有元素定位,而且第i趟需要经过n-i次比较,才能找到第i个元素的安放位置,总的排序码比较次数将达到
∑ni=1(n−i)=n(n−1)2
, 其排序速度退化到简单排序的水平。
空间复杂度
由于快速排序算法是递归的,需要有一个栈来存放每层递归调用时的指针和参数,理想情况下为 ⌈log2n+1⌉ 。要求存储的开销为 log2n ,但是在最坏的情况下占用的附加存储将达O(n)。
快速排序算法的适用性
对于n较大的平均情况而言,快速排序是“快速的”,但是当n和很小的时候,这种排序方法往往比其它简单排序方法还慢。
算法的稳定性
快速排序算法是一种不稳定的排序方法。
改进建议
当初始排列全是正序或者全是逆序的时候,快速排序算法会得到一个空子序列和一个有n-1个元素的子序列。当对这个有n-1个元素的子序列进行划分后,又会得到一个空子序列和一个有n-2个元素的子序列,如此重复,快速排序将做n-1趟,排序码比较次数达 (n−1)+(n−2)+(n−3)+...+1=n(n−1)2 ,比简单排序还慢。所以现在需要一个可靠的改进算法,方法是先取序列第一个、中间一个和最后一个元素,三者中选排序码值居中的元素,把它交换到序列第一个位置作为轴点,再执行快速排序。随机排列情况快速排序的时间性能很好,如果每次划分都能把待排序序列分为等长的两个子序列,排序码的比较次数和元素移动次数都达到 O(nlog2n) 。
注:本文参考书籍《数据结构精讲与习题详解—考研辅导与答疑解惑》,殷人昆编著,清华大学出版社。