// 06_快速排序(QuickSort).cpp
/**
* -> quicksort
* 1. 快速排序是内排序中的一种交换排序的方法。
* 2. 快速排序使用了一种经典的分治思想。
* 3. 快速排序是传说中最快的一种已知的排序算法,它的平均运行时间是O(NlongN)
* 4. 快速排序最基本的思想:
* 01. 通过选取一个pivot枢纽元,
* 02. 以pivot为基准将数组中的元素划分为两个子数组
* 03. 这两个子数组的特征是,像一棵二叉树一样,一棵是存放大于pivot的元素,另一棵则存放小于pivot的元素
* 04. 通过不断地这样循环选取pivot,不断地划分二叉树,最后会形成一个有序的数组(相当于中序遍历这棵二叉树)
* 5. 其中汲及到的问题有:
* 01. 如何选取pivot枢纽元(三数中值分割法)
* 02. 如何划分子数组(i,j 右左移动划分子数组)
**/
#include <iostream>
#include <vector>
#include <conio.h> // _getch();
using std::cin; // using 声明
using std::cout;
using std::endl;
using std::vector;
// ________________________ 主函数 _______________________________
int main()
{
void InsertArr(vector<double> & test);
void quicksort(vector<double> & test);
void ShowArr(vector <double> & test);
bool testagain(true);
char testagainjudge;
vector<double> testArr; // 用于测试的数组
do
{
cout << "------------------------- 现在开始数组的插入排序测试 ---------------------------\n";
cout << " -> 说明:该测试共分为三个步骤:输入 -> (系统内部)排序 -> 输出显示.\n"
<< "-> 注意:在输入时,请按任意字母结束输入。\n";
// 插入
InsertArr(testArr);
ShowArr(testArr);
cout << endl;
// 排序
quicksort(testArr);
ShowArr(testArr);
cout << endl;
cout << "-> 如需重新测试,请按字符'a',否则请按任意键退出...";
testagainjudge = _getch();
if (testagainjudge == 'a')
{
cin.sync();
testArr.clear();
testagain = true;
system("cls");
}
else
{
testagain = false;
}
}while (testagain);
return 0;
}
/**
* 子程序名称:InsertArr
* 子程序返回类型:void
* 子程序入口参数:vector<double> &
* 子程序功能:由用户设定N个数值,并由用户输入这N个数值,程序将其保存入vector<double>入口参数处。
**/
void InsertArr(vector<double> & test)
{
cout << "-> 请输入需要输入的数值个数:";
unsigned int n;
cin >> n;
cout << "-> 现在开始数值输入(请以空格间开):";
for ( unsigned int i = 0; i < n; ++i)
{
double value;
cin >> value;
while(cin.fail())
{
cin.sync();
cin.clear();
cin >> value;
}
test.push_back(value);
}
cout << "-> 输入操作完成.\n";
return;
}
/**
* 子程序名称:swap
* 子程序返回类型: double &
* 子程序入口参数:double & a, double & b ,调用两个double型的引用值
* 子程序功能: 使a与b的数值交换。(因为下面多处需要使用这一功能,所以定义这个函数)
**/
void swap(double & a, double & b)
{
double temp = a;
a = b;
b = temp;
return;
}
/**
* 子程序名称:median3
* 子程序返回类型:double & (引用方式返回本次对比得出的pivot)
* 子程序入口参数:vector<double> &(数组的入口指针), int left(需要用到的数组左值位置), int right(需要用到的数组右值位置)
* 子程序功能:执行三数中值分割。
* 0. 比较array的[left], [right], [middle = (right + left) / 2],
* 1. 使最小值放在a[left], 最大值放在a[right], 次大值[middle]放在[right - 1]处
* 2. 令quicksort()函数中的i 初始化为[left + 1], j 初始化为 [right - 2]
* (优点) 3. arr[left]可以作为j的警戒标记,a[right - 1]作为i的警戒标记
**/
double & median3 (vector<double> & test, int left, int right)
{
int middle( (left + right) / 2); // 求出中值的位置
// 目标是使最小值在left,最大值在right, 要比较的参数有三个,先确定一个再确定另外两个
// 下面的程序,先确定第一个参数, left,通过分别与另外两个参数比较,可以得出最小值放在left
// 最后只需要将剩下两个数比较,就可以得出次大值和最大值了。
if (test[left] > test[middle]) // left与middle比较
swap(test[left], test[middle]); // 较小值放在left
if (test[left] > test[right]) // left与middle比较
swap(test[left], test[right]); // 较小值放在right
if (test[right] < test[middle]) // middle与right比较
swap(test[right], test[middle]); // 最大值放在right
swap(test[middle], test[right - 1]); // 将pivot(枢纽元放在right - 1)处
return test[right - 1];
}
/**
* 子程序名称:quicksort
* 子程序返回类型:void
* 子程序入口参数:vector<double> &
* 子程序功能:将vector<double>内部从小到大的顺序排序。
**/
void quicksort(vector<double> & test)
{
void quicksort(vector<double> & test, int left, int right);
quicksort(test, 0, test.size() - 1);
return;
}
void quicksort(vector<double> & test, int left, int right)
{
if (left >= right) return;
double pivot = median3(test, left, right); // 设定枢纽元,使用三数中值分割的方法,将数组中三个数的值排序好
// 经过上一次的函数调用以后,现 在test数组中,a[left]相对a[right],a[right - 1]为最小值,a[right]相对为最大值
// 现在可以将这种方法的实现想像成一棵树,而pivot就是这棵树的树根,将数组中全部的数一次次的分下去,
// 相对树根小的子树放在左儿子处,相对树根大的子树放在右儿子处,这样一层层分下去,最后得到一个排好序的数组。
// 假如本来有数 (0)2, (1)3, (2)5, (3)21, (4)9, (5)6, (6)54, (7)7, (8)1, (9)4,调用了刚刚上面的median3函数后,
// 数组将会 (0)2_比_, (1)3, (2)5, (3)21, (4)1_比_换_, (5)6, (6)54, (7)7, (8)4_换_, (9)9_比_, 序号为 0, 8, 4的数将会改变,按从小到大的顺序存放
// 而这将会变成一棵树根为4的树,最后使用中序遍历这棵树,就是数组所排列出来的顺序啦。
// 4
// / \
// m<4 n>4
int i = left; // 设定 i 为左值
int j = right - 1; // 设定 j 为右值
if (i >= j) return;
// 先说分割目的:把所有的相对pivot枢钮元小的元素移到数组的左边,把所有的相对pivot枢钮元大的元素称移到数组的右边。
// 然后,现在设定i和j的值,分别为左值,右值,可以理解成i经过的数就是小于pivot的数,而j经过的数就是大于pivot的数
// 刚刚的数组是 (0)2_比_, (1)3, (2)5, (3)21, (4)1_比_换_, (5)6, (6)54, (7)7, (8)4_换_, (9)9_比_
// 现在,将i 放在0,将j 放在right - 1处,至于为什么不将j设定为right - 2呢,这样不是更加符合与pivot的思路吗?这跟下面的算法有关
// 现在的数组 (0)2_比_i, (1)3, (2)5, (3)21, (4)1_比_换_, (5)6, (6)54, (7)7, (8)4_换_j, (9)9_比_
// 设定i, j 为左右值的目的:将数组分割,i一直向右移动,直至遇上大于pivot的值,(不符合源分割目标)停止移动。
// j 一直向左移动,直至遇上小于pivot的值,(不符合源分割目标)停止移动。
// 当ij都停止移动后,将ij的值交换,那么,就可以将相对大小的位置放到正确的位置了。
// (相对小,左边,相对大,右边)
// 接下来这个循环的目的是实现分割,通过不断地循环一个个元素的比较,交换,到最后i>=j时跳出循环
while(i < j) // 当ij仍符合i<j 的条件时,继续寻找下一元素
{
while( test[++i] < pivot){} // 将左边小于枢钮元的元素过滤掉,找出大于pivot的元素
while( test[--j] > pivot){} // 将右边大于枢钮元的元素过滤掉,找出小于pivot的元素
if (i < j) // 当i仍小于j的时候,将i,j 位置对应的值交换
swap(test[i], test[j]); // 使大于pivot的元素放在j的一侧,而小于pivot的元素放在i的一侧
}
// 现在用数组来模拟刚刚的过程
// 原:(0)2_i_left, (1)3, (2)5, (3)21, (4)1, (5)6, (6)54, (7)7, (8)4_j_right-1, (9)9_right
// 寻找大于4的i, 小于4的j
// next: (0)2_left, (1)3, (2)5_i, (3)21, (4)1_j, (5)6, (6)54, (7)7, (8)4_right-1, (9)9_right
// 找到后,交换:
// next: (0)2_left, (1)3, (2)1_i, (3)21, (4)5_j, (5)6, (6)54, (7)7, (8)4_right-1, (9)9_right
// 交换后,继续寻找大于4的i, 小于4的j:
// next: (0)2_left, (1)3, (2)1, (3)21_i_j, (4)5, (5)6, (6)54, (7)7, (8)4_right-1, (9)9_right
// 可以看出,此时已经满足跳出条件了,当i = j 或者i>j的时候即要跳出循环,而现在,对于分割的目标,
// 有点小缺陷,就是(3)与(8)的值,如果此时将数组看为一棵树,
// 那么,分割线应该在中间的,很明显,现在的中线应在(3)的位置,
// 而(3)就是i的位置,i跳出时候的位置了,所以,此时,应该对该数组调整,i与right - 1的位置交换
swap(test[i], test[right - 1]);
// 交换后数组: (0)2_left, (1)3, (2)1, (3)4_i_j, (4)5, (5)6, (6)54, (7)7, (8)21_right-1, (9)9_right
// 实现了这一步以后,如何进行下一轮的比较分割呢?
// 现在的问题思路还是跟刚刚开始快速排序时候一样的,首先,需要确定枢钮元pivot,然后再进行分割。
// 而这个确定枢钮元的步骤已经在这个函数里面思考了,也就是说,我们只需要重复调用这个函数,
// 而调用这个函数的时候,我们只需要考虑的是它的入口,它需要调入一个左边界与右边界,
// 好吧,开始对数组进行分割,在下一次对比当中,树根已经不需要理会了,所以分割成两个子数组时,
// 左边的子数组,左边界,依然是 left,右边界,i-1
// 右边的子数组, 左边界,i + 1,右边界,right
quicksort(test, left, i -1); // 排列左数组
quicksort(test, i + 1, right); // 排列右数组
return;
}
/**
* 子程序名称:ShowArr
* 子程序返回类型:void
* 子程序入口参数:vector<double> &
* 子程序功能:遍历并显示vector<double>&。
**/
void ShowArr(vector <double> & test)
{
cout << "-> 现在开始显示确认刚刚所输入的数组顺序:\n";
cout << "-> ";
vector<double>::const_iterator be(test.begin());
vector<double>::const_iterator en(test.end());
while ( be != en)
{
cout << *be++ << " ";
}
return;
}
(2011.12.01) 06_快速排序(QuickSort).cpp
最新推荐文章于 2023-03-10 11:40:58 发布