(2011.12.03) 08_堆排序(heapsort).cpp

 
 
// 08_堆排序(heapsort).cpp

/**
 * -> heapsort
 * 1. 所谓堆排序,就是指将一组数看作是一个二叉堆,将其排序。
 * 2. 实现堆排序:
 *    01. 刚刚开始时,数组只是一堆没有排序的随意数,按照题目要求(使用下滤法)选择建立最大堆还是最小堆,
 *        当选择了建立最大堆的时候,数组就会从小到大排序,当选择的是最小堆,数组则会从大到小排序。
 *    02. 为什么选择了最大堆反而会从小到大排序呢?这是因为实现堆排序还有很重要的第二步:
 *        通过设定数组的最末元素不断地与最值堆的首元素交换元素,将所需要的最值移动到元素的最后一位,
 *        所以,建立最大堆会变成从小到大排序,而建立最小堆会变成从大到小排序。
 **/


#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 heapsort(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;
		// 排序
		heapsort(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;
} 


/**
 * 子程序名称:heapsort
 * 子程序返回类型:void
 * 子程序入口参数:vector<double> &
 * 子程序功能:将vector<double>内部从小到大的顺序排序。
 **/
void heapsort(vector<double> & test)
{
	void percDown( vector<double> & test, int parent, int size);
	int i;

	// 下面第一个循环实现heapsort的第一步,将这个数组变为最大堆
	// 通俗一点讲,就是将全部父亲跟儿子的关系搞好,使父亲的值比儿子的值大,
	// 在这个循环中,使i的初值为 数组的总大小除以二,在最大堆里面的含义是:这是二叉树里面最右下角的父亲
	// 假设有数组a[15], 则下面是它的树的形式表示的关系
	//            0
	//         /     \
	//       1         2
	//      / \       /  \
	//     3  4      5    6
	//    /\  /\     /\   /\
	//   7 8 9 10  11 12 13 14 
	// 使初值为7,每一次i减1,使i从右往左,从下往上走,直到它走到0的位置为止,
	// 而在这个循环体当中,使用了上滤法,功能是找出一棵子树中的最值,并使这个最值放到父亲的位置
	// 在下面的这一个循环当中,每一次的最值都会放到i里面,到最后,这个循环结束时,这棵树就可以成为一查最大堆了。
	// 假如执行了三次循环,而那些数字又代表了数值的大小,那么, 它的结果将是:
	//            0
	//         /     \
	//       1         2
	//      / \       /  \
	//     3  10      12    14
	//    /\  /\     /\   /\
	//   7 8 9  4  11  5 13 6 
	// 假如执行了五次循环:
	//            0
	//         /     \
	//       10         14
	//      / \       /  \
	//     8  9      12  13
	//    /\  /\     /\   /\
	//   7 3 1  4  11  5 2  6 
	// 本次循环全部执行完的最终结果:
	//            14
	//         /     \
	//       10         13
	//      / \       /  \
	//     8  9      12   6
	//    /\  /\     /\   /\
	//   7 3 1  4  11  5 2  0 
	// 此时,最大堆形成,a[0]的元素为最大,整个数组相对有序。
	for (i  = static_cast<int>(test.size()) / 2 ; i >= 0; --i)
	{
		percDown(test, i, test.size());		// 下滤法,以i为父亲,使i的值在子树中最大
	}
	
	// 这里开始执行heapsort的第二步:目的是使数组最后以从小到大的方式排序
	// 实现这一功能的方法,可以理解成从堆中从尾部开始逐次删除元素,而这个元素在删除前会先与最大堆的最值的数值交换,
	// 即删除的是最值,然后把相对小的值,在数组尾部的值调到原来的首位,
	// 可以知道,接下来要进行很重要的一点是:再重新将刚刚删除的交换的元素再次排序,使最大堆的第一位的值始终保持最大。
	// 假如这里执行了第1步,交换元素,继续上面的例子:(1跟15的位置交换了)
	//            0
	//         /     \
	//       10         13
	//      / \       /  \
	//     8  9      12   6
	//    /\  /\     /\   /\
	//   7 3 1  4  11  5 2  14 
	// 假如这里执行了第2步,“删除”元素,然后使用下滤法:
	//            13
	//         /     \
	//       10         12
	//      / \       /  \
	//     8  9      11   6
	//    /\  /\     /\   /\
	//   7 3 1  4  0  5 2  [14]<-在使用下滤法时,这个元素已经被忽略不计算了,因为它的位置已经符合我们本来想实现的排序功能。
	// 继续看一次程序执行的步骤:
	// 执行第一步“交换”元素:
	//            2
	//         /     \
	//       10         12
	//      / \       /  \
	//     8  9      11   6
	//    /\  /\     /\   /\
	//   7 3 1  4  0  5  [13] [14]
	// 执行第二步“删除”元素:
	//            12
	//         /     \
	//       10         6
	//      / \       /   \
	//     8  9      11    2
	//    /\  /\     /\   /  \
	//   7 3 1  4  0  5  [13] [14]<-括住的元素,在使用下滤法时,可以当作是不存在了。
	// 就这样一直往下交换删除排序,最后,这个被要求排序的数组就可以从小到大排序了。
	for (i  = static_cast<int>(test.size()) - 1 ; i > 0; --i)
	{
		// 以下,实现该子循环中的交换功能,使最值与相应位置的值交换,并且交换的末位元素的值不再变动
		vector<double>::value_type temp = test[i];
		test[i] = test[0];
		test[0] = temp;
		// 这里控制percDown调入的是i就是限定了数组的大小,使已经“删除”的元素位置不再读取,这样就可以保持排序的继续实现
		percDown(test, 0, i);
	}
	return;
}

// 下滤法,调入参数有三:1. 需要过滤的数组,2. 需要比较的子树的父结点, 3. 该数组的大小
void percDown(vector<double> & test, int parent, int size)
{
	int child;
	// 建立新变量,临时保存[parent]的值
	vector<double>::value_type tempparent = test[parent];
	// 假如parent * 2 + 1 即儿子的值超出数组范围,则直接跳出循环
	// 该循环每执行一次,就会对比了一棵子树,假如该子树中还包含着多个左右儿子,则循环继续,
	// parent = child, 让parent下一级,直到为tempparent找到正确的位置.
	// 实际上,这个函数要实现的功能就是,把进来的[parent]当作是一个需要插入的元素,
	// 然后,为这个元素找到正确的位置。
	for (; parent * 2 + 1 < size; parent = child)
	{
		child = parent * 2 + 1;
		// 让child != size - 1 目的是让它不超出边界,假如执行了child + 1,这时会内存溢出
		// test[child] < test[child] + 1目的是找出两个儿子中的最大值,假如符合条件,应将儿子加1
		if (child != size - 1 && test[child] < test[child + 1])
		{
			++child;
		}
		// if语句的执行结果是让老爸比儿子的值大
		if (tempparent < test[child])
		{
			test[parent] = test[child];
		}
		else
		{
			break;
		}
	}
	// 最终将parent的值还原到相应的位置中,这方法的实现实际上跟插入排序有点类似的地方
	test[parent] = tempparent;
	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.03) 08_堆排序(heapsort).cpp)