(2011.10.05) 01_sortproblem.cpp -- 简单的排序方法的探究(C++)

  半年没有使用过冒泡法和选择法了,也不是很记得怎么用,前几天,老师叫我随便写个排序出来,我就蒙了,太久没有写过啦,只记得大概的思路,于是就写了个很烂的自我感觉有错的冒泡法出来,这几天,趁着还算是有空,再次看书,思考,找回写简单排序的方法思路。

 

// 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);
}


 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值