半年没有使用过冒泡法和选择法了,也不是很记得怎么用,前几天,老师叫我随便写个排序出来,我就蒙了,太久没有写过啦,只记得大概的思路,于是就写了个很烂的自我感觉有错的冒泡法出来,这几天,趁着还算是有空,再次看书,思考,找回写简单排序的方法思路。
// sortproblem.cpp -- 简单的排序方法的探究(C++)
/**
* 探究问题:
* 1. 如何使用algorithm中的sort函数对vector容器进行排序?
* 2. 如何使用插入排序法?
* 3. 如何使用冒泡排序法?
* 4. 如何使用选择排序法?
*/
#include <iostream>
#include <algorithm>
#include <vector>
#include <iomanip>
typedef std::vector<int> vi;
void printvector(vi, std::ostream&); // 对容器内各元素的输出
int main()
{
using std::cout;
// 创建一个含15个变量的数组并将其放入容器中
const int maxsize(15);
int a[maxsize] = {2, 4, 6, 8, 10, 12, 15, 14, 13, 11, 9, 7, 5, 3, 1};
vi test(&a[0], &a[maxsize]);
// 各种排序方法的函数声明 (排序后不影响原容器的数据)
void vectorSort(vi);
void insertSort(vi);
void bubbleSort(vi);
void selectionSort(vi);
void allSort(vi);
// 控制键入及界面格式控制
void menu(vi);
char entercontrol(const char & min, const char & max);
char choice;
void next();
// 开始选择功能函数
do
{
menu(test);
choice = entercontrol('1', '6');
switch(choice)
{
case '1': // 1. 容器中的sort算法排序
{
vectorSort(test);
next();
break;
}
case '2': // 2. 插入排序法
{
insertSort(test);
next();
break;
}
case '3': // 3. 冒泡排序法
{
bubbleSort(test);
next();
break;
}
case '4': // 4. 选择排序法
{
selectionSort(test);
next();
break;
}
case '5': // 5. 输出各种排序方法
{
allSort(test);
next();
break;
}
}// 结束switch语句的花括号
}while(choice != '6');
next();
return 0;
}
/* ***************************** 其它函数的定义 ******************************* */
void next() // 提示用户,进行下一步工作
{
std::cout << "\n测试已完成,";
system("pause");
system("cls");
return;
}
void menu(vi test) // 提示用户,选择功能
{
using std::cout;
cout << " ***************************** 排序方法的探究 *****************************\n";
// 对原容器进行输出
cout << "\n下面是对原容器的顺序输出:" << std::endl;
printvector(test, cout);
// 显示菜单
cout << "\n--> 该程序将可实现测试四种基本的排序方法:"
<< "\n 1. 容器中的sort算法排序\n 2. 插入排序法\n 3. 冒泡排序法\n 4. 选择排序法"
<< "\n 5. 一次性输出四种排序测试\n 6. 退出测试"
<< "\n请输入选择测试选项:";
return;
}
/**
* char entercontrol(const char, const char)对键入字符大小的控制函数。
* 可以通过形参传递最小值及最大值准确地返回一个闭区间内的字符。
* 第一个形参为常量char型,用于控制闭合区间的最小值;
* 第二个形参为常量char型,用于控制闭合区间的最大值;
*/
char entercontrol(const char & min, const char & max)
{
using std::cin;
char choice;
cin >> choice;
while (!cin || choice < min || choice > max)
{
std::cerr << "\n\n您的输入有误,请重新输入:";
cin.sync();
cin.clear();
cin >> choice;
}
return choice;
}
/**
* printvector 对容器进行输出
* 输出格式控制为每个元素间使用'-'字符间开,输出完毕后再输出一个回车。
*/
void printvector(vi test, std::ostream & o)
{
vi::iterator printout = test.begin();
o << "—|元素位置: ";
vi::size_type length(test.end() - test.begin() + 1);
vi::size_type pos(1);
while(pos != length)
{
o << std::setw(2) << std::setfill('0') << pos++;
if (pos != length)
o << "-";
if (pos == length)
o << "\n";
}
o << "—|元素数值: ";
while(printout != test.end())
{
o << std::setw(2) << std::setfill('0') << *printout++ ;
if (printout != test.end())
o << "-";
if (printout == test.end())
o << "\n";
}
return;
}
/*******************************************************************************
* void vectorSort(vi)函数
* 只接受一个vector<int>型的实参,用作排序的顺序容器。
* 使用头文件algorithm中的sort函数进行排序
* 下面针对sort函数的两种使用方法进行探究。
**********************************************************************************/
bool sorthelp(const int& a , const int& b)
{
return a > b;
}
void vectorSort(vi arr)
{
// 容器的复制使用及其它声明
using std::cout;
vi arr2(arr); // 再复制一个名为arr2的容器,用作第二次实验。
vi arr3(arr); // 再复制一个名为arr3的容器,用作第三次实验。
vi arr4(arr); // 再复制一个名为arr4的容器,用作第四次实验。
vi arr5(arr); // 再复制一个名为arr5的容器,用作第五次实验。
cout << "\n\n现在开始algorithm排序方法测试:\n\n";
/* 第一种方法,接受两个迭代器形参 */
// 升序 —— 整个容器
// 第一个参数放入begin(指向容器首位的迭代器)
// 第二个参数放入end(超出末端迭代器)
// sort函数将会使整个容器默认按照“升序”(<)的方式排列
cout << "\n\n1. 测试:正在尝试使用sort(arr.begin(), arr.end())语句排序:\n";
sort(arr.begin(), arr.end());
printvector(arr,std::cout);
cout << "结果:整个容器呈升序排序。";
// 降序 —— 整个容器
// 第一个参数放入rbegin(指向容器末位的反向迭代器)
// 第二个参数放入rend(超出首位反向迭代器)
// sort函数将会使整个容器默认按照“降序”(>)的方式排列
cout << "\n\n2. 测试:正在尝试使用sort(arr2.rbegin(), arr2.rend())语句排序:\n";
sort(arr2.rbegin(), arr2.rend());
printvector(arr2,std::cout);
cout << "结果:整个容器呈降序排序。";
// 升序 —— 指定数据段
// 第一个参数放入begin + 5 此时迭代器指向容器的第六个元素
// 第二个参数放入end - 3 此时迭代器指向容器的倒数第三个元素
// sort函数将会使容器的第六个元素至倒数第四个元素按照“升序”(<)的方式排列
cout << "\n\n3. 测试:正在尝试使用sort(arr3.begin() + 5, arr3.end() - 3)语句排序:\n";
sort(arr3.begin() + 5, arr3.end() - 3);
printvector(arr3,std::cout);
cout << "结果:容器的第六个元素至倒数第四个元素按升序排列。\n";
// 降序 —— 指定数据段
// 第一个参数放入rbegin + 5 此时迭代器指向容器的第六个元素
// 第二个参数放入rend - 3 此时迭代器指向容器的倒数第三个元素
// sort函数将会使容器的第四个元素至倒数第六个元素按照“升序”(<)的方式排列
cout << "\n\n4. 测试:正在尝试使用sort(arr4.rbegin() + 5, arr4.rend() - 3)语句排序:\n";
sort(arr4.rbegin() + 5, arr4.rend() - 3);
printvector(arr4,std::cout);
cout << "结果:容器的第四个元素至倒数第六个元素按升序排列。\n";
// 试验
// 因为看见能够反向排序,在想是不是也可以以end(),begin()的方式调用函数?
// sort(arr.end() - 1, arr.begin());
// 运行失败,还是乖乖地遵循sort函数的规则吧~
/* 小结:第一种方法,接受两个迭代器形参
* 该函数可以接收两个形参,但是得遵循第一个迭代器需比第二个迭代器小的规则
* 附:begin() < end(), rbegin() < rend();
*/
/* 第二种方法:接受三个参数的sort函数 */
// 升序 —— 第三个参数
// 第一二个参数分别放入.begin()和.end()的迭代器;
// 第三个参数的函数设置需要满足:1. bool函数,2. 两个与vector容器元素相对应的形参
// 当满足以上条件时,函数就能按照需求使用排序。
cout << "\n\n5. 测试:正在尝试使用sort(arr5.begin(), arr5.end(), sorthelp)语句排序:\n";
sort(arr5.begin(), arr5.end(), sorthelp);
printvector(arr5,std::cout);
cout << "结果:容器能按照第三个函数的设置要求排序。\n";
return;
}
/***************************************************************************************************************
* 插入排序法 void insertSort(vector<int> arr)
* 这里接受了一个vector<int>类型的参数,用作数组的排序。
* 0.插入排序
* 1.个人理解:
* 将一个数组中的每一个元素逐个提取,放到一个新的空数组上的原理差不多,如其名,插入的时候就进行排序。
* 2.从目标去看:
* 输入: n个数<a1, a2, ..., an>
* 输出: 重新排序,得到<a`1, a`2, a`3, ..., a`n>,并使得 a`1<a`2<a`3...a`n.
* 3.插入排序需要两个循环
* 第一个循环:功能可以看作是提取原数组的元素,并对数组元素的位置作出标记(提取元素的位置)
* 第二个循环:嵌套在第一个循环中,可以理解成是对刚刚第一个循环中提取出来的元素进行处理,使其放入“新”数组的正确位置。
***************************************************************************************************************************/
void insertSort(vi arr)
{
// 定义了三个int型变量,maxsize是用于取得原数组的长度,而i和j用作下面的两个循环中
// 注意:如果将j定义为size_type型,在程序运行的时候会出错
// 因为,size_type为无符号型,而运行的时候,当j为0时,使用--j会变为-1.
int maxsize(static_cast<int>(arr.size()));
int i;
int j;
printf("\n\n现在开始插入排序方法测试:\n");
// 这里开始第一个循环;
// 将i初始化为1,这是因为第一次可以直接使用第二个元素进行比较了,可以理解成新数组里面现在只有[0]的这一个数。
// 判断条件为小于最大数时,如果该数组有10个数,maxsize则为10,数组下标则到[9],而用小于号,刚刚可以碰到[10]就停止循环了。
for(i = 1;i < maxsize; ++i)
{
// 这里先定义一个变量保存要插入到新数组中的元素
vi::value_type temp = arr[i];
// 第二个循环
// 实现的功能:是“移位”,将元素插入到正确的位置。
// 初始化条件:初始化j为i - 1,这是因为要与前一个元素进行比较,假设原数组 1-2-3-4-5 (0代表为空)
// 则第一次进入循环时,2与1作比较,可以忽略其它元素,(将数组大小看作是i),
// 看作将2插入到1-0-0-0-0中,此时第二位为空,1如果符合比较条件,可以移动到第二位。
// 当2插入完成后,数组变为 1-2-0-0-0 或者 2-1-0-0-0,
// 然后,进行下一次循环,提取原数组的下一个元素进去这一个“新”数组。
// 判断条件:temp < arr[j];如上所示,当要升序(<)排列时,因为大的数字要在数组后面,初始化的j = i - 1,
// 将要插入的变量用<比较,若temp小于新数组的变量[j],则将新数组中前面一位的元素移到后面的位置去。继续前一位元素的比较。
// 可以想象成是将一个空位移动如例:(设此时i = 4, temp = 2)(要插入的元素是2)
// 数组情况:3-4-5-0-0 -> 3-4-0-5-0 -> 3-0-4-5-0 -> 0-3-4-5-0 ->(放入元素2于空位中) 2-3-4-5-0
// 判断条件:j递减,至到它少于0则停止。
for (j = i - 1; j >= 0 && temp < arr[j]; --j)
{
arr[j + 1] = arr[j];
}
// 此处,相当于刚刚列举的情况,最后一步,将元素放入到空位中,为什么是j+1?可以这样理解:
// 第一种理解,从前面的temp元素去想,前面temp是arr[i], 初始条件[j = i - 1],那么,
// 假如数组内的元素全都符合条件,正好temp元素要放在新数组的最后一位,也就是要将刚刚减去的1再加回来。 [j = j + 1]
// 第二种理解,直接从j的初始条件入手去想,要插入的位置是第i位,即第j+1位,将要插入的位置想像成一个气泡,
// 无论执行了循环多少次,这一个气泡还是在第j+1的位置处,这个气泡就是认定了要插入的位置。
// 第三种理解,从数学角度去想,需要插入的位置=原来的位置(i)-位移量,
// 位移量=原来的位置(i)-现在的位置(即:j+1),因为执行循环时,最后多执行了一次自减,这里要加回来。
// 即 需要插入的位置=原来的位置(i)-(原来的位置(i)-位移量(即:j+1) )最后还是等于j+1;
arr[j + 1] = temp;
}
std::cout << std::endl;
printvector(arr, std::cout);
return;
}
/**
* 冒泡法:
* bubbleSort()使用了两种的冒泡方法.
* 冒泡方法的主要思路是相邻之数两两比较,将最大或最小放至顶部,顶部再逐部往下压。
* 冒泡法从for语句上看,有一个很大的特点,maxsize总会放在其中一个for的初始值的位置或者判断的位置。
* 一般冒泡法比较难确认的就两个for语句了,下面程序中,用了两种的for语句
* 第一个 for (i = 0; i != maxsize; ++i){for (j = maxsize;j != i; --j)}
* 第二个 for (i = maxsize; i != 0; --i){for (j = 0; j != i; j++)}
* 第一个:外面的for的特点:首先外面层的for是使数组从下往上压的(将maxsize视为上[最大下标]),因为它递增,
* 决定了什么:从从下往上压,它可以决定了一个个的泡泡是沉到底部的,从下面累积至上面[最大下标],
* 判断条件:i != maxsize由于它判断至倒数第二个的时候,剩下最后一个元素的就是已经排序好的元素了,所以使用!=不用判断最后一个元素。
* 内嵌的for的目标:由刚刚外层for说明了,这里的for要完成的任务是将最值放至底部[最小下标]处,(可以是最大值也可以是最小值)
* 做法:若要升序排序,则将最小值放至底部,即,从下往下,两两比较,小值赋给[小下标]
* 判断条件:因此,这里的下标是从大到小,j的初值是maxsize,递减,相邻元素比较,直到遇上i。
* 第二个:外面的for的特点:与第一种方法刚刚相反,外面层的for是使数组从上往下压的(将maxsize视为上[最大下标]),因为它递减,
* 决定了什么:下标递减,决定了最值元素是从下比较至上的,内嵌for要完成的任务是将最值送到maxsize处。(看上去更像冒泡了)、
* 判断条件:当i比较的时候,不用对下标[0]进行排序,因为它是最后一个元素,最后一个排序的目标,但由于前面已经排好了,最后一个就不用排了。
* 举个例来说,当你排序排好了6个元素的时候,第7个元素也就默认被排好了。
* 内嵌的for的目标:跟第一个刚刚相反,这里就不讲了。
* 个人讲讲写冒泡法的方法:
* 1. 首先自己决定泡泡要怎么冒,是要从下标小的冒上下标大的,还是下标大的冒向下标小的;
* 2. 然后,根据泡泡的冒向,先写外层的循环语句,决定好顶部的下标及递增减的条件;
* 3. 其次,是根据外层循环语句给予的目标,写内嵌的循环语句,使最值放到冒向的地方。
* 举例:设有数组a[10],从大到小排序
* 1. 首先思想:泡泡往上冒,下标小的冒向大的;
* 2. 往上冒,也就是说,从上面会一直累积下来,比较的数值会越来越小,直到为零,for(int i = Length; i > 0; --i)
* 3. 内嵌的,因为往上冒的,所以使最值往上推,初值为最小值,for (int j = 0; j != i; ++j)
*/
void bubbleSort(vi arr)
{
vi arr2(arr);
int maxsize(static_cast<int>(arr.size()) - 1);
int i;
int j;
printf("\n\n现在开始冒泡排序方法测试:\n");
printf("\n1. 泡泡从上面跌到下面的方法:\n");
// 第一种类型 泡泡沉下底部
for (i = 0; i != maxsize; ++i) // 从下向上递增
{
for (j = maxsize;j != i; --j) // 从上往下浏览,将最小的放至最底
if( arr[j] < arr[j - 1])
{
vi::value_type temp = arr[j - 1];
arr[j - 1] = arr[j];
arr[j] = temp;
}
}
printvector(arr, std::cout);
printf("\n2. 泡泡从下面升到上面的方法:\n");
// 第二种类型 泡泡升上来
for (i = maxsize; i != 0; --i) // 从上往下递减
{
for (j = 0; j != i; j++) // 下往上递增,将最大放至顶层
{
if (arr2[j] > arr2[j + 1])
{
int temp = arr2[j];
arr2[j] = arr2[j + 1];
arr2[j + 1] = temp;
}
}
}
printvector(arr2, std::cout);
}
/************************************************************************************************************************
* 选择排序法:
* 这个方法的主要思想:记录,比较,选择,交换。(个人用四个词语概括一下)
* 时间代价:比较次数恒为n的平方,交换次数恒为n.
* 写程序思路:(与冒泡法相似)
* 1. (确定方向)冒泡法也是要遍历一次数组,将全部元素比较一次,而且,比较的时候,是将确定要上升或者下降的方向。
* 2. (记录临时最值及下标)临时保存此位置的数值,并将其作为最值与下一循环中的其他数值进行比较。
* 3. (进入内嵌循环,在指定范围内选择出最值下标并记录)通过将数组遍历,找出在该范围内的最值,并对最值的位置进行记录。
* 4. (交换最值,将最值放至指定位置)这里所说的指定位置,是刚刚开始第一次外层循环时所定的位置,而这个位置是每次循环不同的。
* 例如:
* 1. (确定方向)这里确定为从下标[小]的至下标[大]的顺序。for(i = 0; i < maxsize; ++i)
* 2. (记录临时最值及下标)此处,定位[i]为最值,进行记录,k = i; tempmin = arr[i];
* 3. (进入内嵌循环,在指定范围内选择出最值下标并记录)for (j = i + 1; j <= maxsize; ++j)
* 这里的j定义为i+1避免与arr[i]重复比较,循环条件是<=maxsize,此时j要到达数组末尾处对数组进行比较。
* if(arr[j] <= tempmin)使用if语句,总是记录最小值。
* 4. (交换最值,将最值放至指定位置)if (i != k)a[i]<->a[k]当i与k的数值不相同时,证明了k曾经作改动,需要交换数值,使第i位为最值。
***********************************************************************************************************************************/
void selectionSort(vi arr)
{
int maxsize(static_cast<int>(arr.size()) - 1);
vi::value_type tempmin;
int i;
int j;
int k;
printf("\n\n现在开始选择排序方法测试:\n");
for (i = 0; i < maxsize; ++i) // 从下标小的往下标大的方向记录
{
k = i; // 记录最值下标
tempmin = arr[i]; // 记录:最小值
for (j = i + 1; j <= maxsize; ++j)
{
if(arr[j] <= tempmin) // 比较:找出最小值的下标
{
k = j; // 选择:选出最值的下标
tempmin = arr[j];
}
}
if (i != k) // 交换:与最值交换
{
vi::value_type temp = arr[k];
arr[k] = arr[i];
arr[i] = temp;
}
}
printvector(arr, std::cout);
return;
}
void allSort(vi arr)
{
printf("\n\n现在使用四种方法测试排序:\n");
vectorSort(arr);
std::cout << "\n";
system("pause");
insertSort(arr);
std::cout << "\n";
system("pause");
bubbleSort(arr);
std::cout << "\n";
system("pause");
selectionSort(arr);
}