(2011.11.26)03_折半插入排序.cpp

 
// 03_折半插入排序.cpp

/**
 * -> 编写原因:
 * 1. 前些天看了折半插入排序的相关资料,今天也写了一次作业题关于插入排序的
 *    现在,在7_a1.cpp的基础上,编写折半插入排序的代码。
 **/

/**
 * -> 折半插入排序思想分析:
 * 1. 跟名字差不多,也是插入排序的一种,只是跟普通的插入排序有点不同。
 * 2. 在普通的插入排序中,新的元素需要插入到已排好序的数组中时,是对数组中的元素逐个比较,得出位置。
 * 3. 折半插入排序也是一样需要将新元素插入到已排好序的数组中,只是这时不是对元素进行逐个比较,
 *    而是在插入之前先找到正确的位置,然后直接插入到那个位置当中。
 * 4. 所谓折半查找,也就是将一个数组分成一半来查找,准确地来说,是将数组分成两个一半来查找,
 *    假设有数组 0, 0, 0, 0, 0, 0 那么,现在的查找方式是,设定左右边界 -> |0, 0, 0 | 0, 0, 0 |<- .从中间开始与新元素比较。
 * 5. 刚刚刚那个 "|" 正是需要插入的位置了,假如发现左边或右边的元素符合比较条件,那么,它会让左边或右边的"|"移动位置至中间的"|"。
 *    从而使左边或者右边的"|"移到中间的"|"的左边一位或右边一位,如果数组较大,那么跳跃性就明显这就是折半插排序的主要思想了。
 * -> 使用折半插入排序的原因:
 * 1. 一般来说,一个算法出来,无非就是两个原因,1. 解决需要解决的问题, 2. 优化需要解决的问题
 *    在这个排序方法中,主要是优化原来的插入排序的问题,使排序的效率更高,平均情况下总比较次数约为n*n/4
 **/


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


/**
 * 子程序名称:HalfInsertSort
 * 子程序返回类型:void
 * 子程序入口参数:vector<double> &
 * 子程序功能:将vector<double>内部从小到大的顺序排序。
 **/
void HalfInsertSort(vector<double> & test)
{
	// 折半插入排序的空间代价相对插入排序大一点,需要多使用三个变量,
	// 三个增加的变量功能是记录在使用折半比较的算法时左边中间右边的数组元素下标
	int n = static_cast<int>(test.size());
	int i(1);
	int left;
	int right;
	int j(0);
	// 第一个最外层的循环,初始条件是i = 1,结束的条件是 i == n, 在数组中,n就相当于末端迭代器的位置,
	// 数组中只有n个数需要比较,到达下标n的时候,这个数不存在,因为,数组是从0下标开始读取数据的,
	// 这里将其初始化为1,主要跟下面的right的设定有关,
	// 假如将i初始化为1的话,整个循环就要有点小改动,要改为将right设定为i,需要插入的元素为i+1.
	for (i; i < n; ++i)
	{
		vector<double>::value_type temp = test[i];
		 
		left = 0;					// 这里将left初始化为0,设定它的左边界
		right = i - 1;				// right初始化为i-1, 因为需要插入的元素是[i],所以右边界只需设定为[i-1].

		// 下面这一整个循环的位置的主要作用是将left左边界的位置调至需要插入的位置,也就是说,left的位置就是temp的位置
		// 假如temp比中间的元素小,因为这个是从小到大的排序,所以要找到正确的temp的位置,还需要将temp的位置左移
		// 而这里将temp位置左移的方法就是将右边界调为中界的左一位,实行跨越式移动。
		// 按照这样的思路,考虑边界条件,当left == right时,会发生什么事,这时,temp的值已经与left的值相等了,
		// 然后,right还会执行一步减1,也就是说,这里if (temp <= test[middle])	right = middle - 1;	
		// 暗含了一个从这里跳出的条件,一旦执行了这一步,在下一次循环时,刚好不符合条件left <= right,程序下一步的执行,可以跳出循环体
		// 这就是说明了,left小于right时,还可以继续移动,left > right时,需要跳出, left == right时,为left>right作跳出准备
		// 还有一个问题,为什么left的位置就是temp的位置了呢?这个跟下面的移位功能的循环也有一定的关系,当然,最主要条件还是在这个循环中的if判断
		// 假如要将一个新元素插入到新数组中,而这新元素正好大于这数组中的全部元素,(如:现在新数组有六个元素:l 0 0 0 0 r 需插入[i])
		// 那么,它就会一直执行left的右移,直到left == right + 1,刚好执行下一条指令可以跳出. (插入时 0 0 0 0 0  r (li)li的位置都在同一个位置了)
		// 可以看出,差不多全部的初始条件的设置,都跟上下有关系,所以说,一个程序下来,整个总体的思路的设置是很重要的。
		while(left <= right)						// 以left为下次移位的基准
		{
			int middle = (left + right) / 2;		// 用中间元素与需要插入的元素比较
			if (temp <= test[middle])
			{ 
				right = middle - 1;	
			}
			else
			{
				left = middle + 1;
			}

		}

		for ( j = i - 1; j >= left; --j)		// 移位
		{
			test[j + 1] = test[j];
		}
		// 上面提到过,left的位置就是temp的位置,所以,这里,因为j在循环中出来的时候,数值多减了一个1,
		// 所以,在下面,如果需要用j的时候,还需要加上一个1
		// 下面一句也可写成test[j+1] = temp
	 	test[left] = temp;

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值