排序【HDU - 1106】

排序问题解析

这一题好像有那么点小复杂

题目

输入一行数字,如果我们把这行数字中的‘5’都看成空格,那么就得到一行用空格分割的若干非负整数(可能有些整数以‘0’开头,这些头部的‘0’应该被忽略掉,除非这个整数就是由若干个‘0’组成的,这时这个整数就是0)。
你的任务是:对这些分割得到的整数,依从小到大的顺序排序输出。

Time limit Memory limit OS Source
1000 ms 32768 kB Windows POJ

Input

输入包含多组测试用例,每组输入数据只有一行数字(数字之间没有空格),这行数字的长度不大于1000。
输入数据保证:分割得到的非负整数不会大于100000000;输入数据不可能全由‘5’组成。

Output

对于每个测试用例,输出分割得到的整数排序的结果,相邻的两个整数之间用一个空格分开,每组输出占一行。

Sample

input output
0051231232050775 0 77 12312320

问题链接HDU - 1106

问题描述

输入多组数据,一组数据包括一行数字,把其中的“5”当做空格,以这些空格为界,分割这串数字以得到一堆整数,将这些整数从小到大排序后逐个输出,每个输出之间加一个空格。

问题分析

首先需要输入多组数据,就需要用一个while来循环程序,当结束输入时退出循环,然后由于一开始输入的数据是一长串数字,为了不溢出,应选用字符串来储存,然后以5为界分割字符串,再将每个分割出来的字符串转为整型,然后进行排序,排序后输出结果。

AC通过的C++语言程序代码如下:

#include
#include
using namespace std;
void bubble_sort(int arr[], int len)
{
	int i, j, change = 1;
	for (i = 0; i < len - 1 && change != 0; i++)
	{
		change = 0;
		for (j = 0; j < len - 1 - i; j++)
			if (arr[j] > arr[j + 1])
			{
				swap(arr[j], arr[j + 1]);
				change = 1;
			}
	}
}
int main()
{
	string num[500];
	string snum;
	while (cin >> snum)
	{
		int len = 0;
		int start = 0;
		int n = 0;
		bool ndone = false;
		for (int i = 0; i < snum.size(); i++)
		{
			if (snum[i] != '5')
			{
				len++;
				if (ndone)
				{
					start = i;
					ndone = false;
				}
			}
			if (i != 0 && snum[i - 1] != '5' && snum[i] == '5')
			{
				num[n] = snum.substr(start, len);
				n++;
				ndone = true;
				len = 0;
			}
			else if (i == snum.size() - 1 && snum[i] != '5')
			{
				num[n] = snum.substr(start, len);
				n++;
			}
		}
		int *numl = new int[n];
		for (int i = 0; i < n; i++)
			numl[i] = stoi(num[i]);
		bubble_sort(numl, n);
		for (int i = 0; i < n - 1; i++)
			cout << numl[i] << " ";
		cout << numl[n - 1] << endl;
		delete numl;
	}
}

代码分析

一开始声明了一个用于存放string的输出和一个string,前者用于存放分割出来的字符串,后者用来存放用户输入的字符串。

string num[500];
string snum;

使用了while来保证可以输入多组数据且在结束输出时会退出循环。

while (cin >> snum)

在程序里,声明了四个变量,分别用于标示需要分割出来的字符串长度、开始分割位置的角标、分割出来的字符串要放到的位置,和是否已经给start(开始分割的位置)赋过值。

int len = 0;
int start = 0;
int n = 0;
bool ndone = false;

解释一下这些变量的默认值的用处,len一开始赋0以方便后面计算要分割的长度,而一开始分割的位置必然在第0个字符,所以start初始值赋0,然后第一个分割出来的字符串应该放在num数组的第0角标位置,所以n赋0。再由于start已经赋过初始值,所以ndone默认为false

然后进入到while循环,第一个部分是一个for循环,这个for循环用于检查所输入的字符串的每一个数字,遇到5时将该5到前一个5之间的数字都分割出来放进num数组,并将最后一组数字也分割出来放进num数组。

//分割
for (int i = 0; i < snum.size(); i++)
{
	if (snum[i] != '5')
	{
		len++;
		if (ndone)
		{
			start = i;
			ndone = false;
		}
			}
	if (i != 0 && snum[i - 1] != '5' && snum[i] == '5')
	{
		num[n] = snum.substr(start, len);
		n++;
		ndone = true;
		len = 0;
	}
	else if (i == snum.size() - 1 && snum[i] != '5')
	{
		num[n] = snum.substr(start, len);
		n++;
	}
}

下面介绍各部分是怎样实现的

首先是第一个if:

if (snum[i] != '5')
	{
		len++;
		if (ndone)
		{
			start = i;
			ndone = false;
		}
	}

这个if是当当前检查的数不是5时执行的,它的作用是,如果当前数不是5,就将要分割的数长度加一(len++),然后如果start还处于未赋值的状态(这里状态用布尔变量表示),就将当前的i赋给start,并将start改为已赋值的状态。

然后是第二个if:

if (i != 0 && snum[i - 1] != '5' && snum[i] == '5')
{
	num[n] = snum.substr(start, len);
	n++;
	ndone = true;
	len = 0;
}

这个if的执行条件是当前检测的数遇到了5,而且i不等于0(即现在判断的不是第一个数),且上一个数不是5,这是为了避免当有连续多个5或者第一个数是5时仍然执行了赋值动作,这会导致程序num里赋进了空值,导致程序不能正常运行。

接下来赋值就是将字符串snum的从start开始为len长度的字符串赋值到num数组的第n项。这里涉及到了一个string类函数,那就是snum.substr(start, len),这个函数的含义即如上述。

进行完了赋值动作之后,n就要自增,以保证下一个分割出来的字符串储存在num数组的下一个位置。经过了这些,start的值就需要重新赋值,于是在这里将start重新改为未赋值的状态(ndone = true;),需要分割的长度也需要重新计算,于是将len赋值回0(len=0)。

而第二个if还有一个else if,也就是第二个if的条件达不到的时候执行的:

else if (i == snum.size() - 1 && snum[i] != '5')
{
	num[n] = snum.substr(start, len);
	n++;
}

这个else if的条件为当i等于输入的字符串的长度减1(snum.size() - 1),也就是当前检查的数是输入的数的最后一个,而且这个数不是5。大家可以想到,之前的分割动作的触发条件是遇到5,但有没有遇到的不是5也需要将前面的字符串分割出来的情况?那就是最后一串数字,因为最后一串数字不是以5结尾。

这里的分割操作和上面的if里面的分割操作方法基本相同,不同的是因为已经是最后一串数字,start和len就不用再重新赋值,也就可以不理,这里之所以n++,是为了方便下面使用n当做num长度的时候不会与数字是以5结尾的情况(该情况else if不会执行)有所不同。

分割完了字符串,就应该把分割出来的字符串都转为整型方便排序了

int *numl = new int[n];
for (int i = 0; i < n; i++)
	numl[i] = stoi(num[i]);

这里根据分割出来字符串的个数,动态创建了另一个存放整型数据的数组,然后利用stoi()函数将num里面的元素转为int类型存于数组numl中。

之后排序用到了一个自定义函数,这个函数是一个冒泡排序函数,关于冒泡排序,详见:经典排序算法(1)——冒泡排序算法详解,这里就不多做解释。

void bubble_sort(int arr[], int len)
{
	int i, j, change = 1;
	for (i = 0; i < len - 1 && change != 0; i++)
	{
		change = 0;
		for (j = 0; j < len - 1 - i; j++)
			if (arr[j] > arr[j + 1])
			{
				swap(arr[j], arr[j + 1]);
				change = 1;
			}
	}
}

然后就是输出结果了:

for (int i = 0; i < n - 1; i++)
	cout << numl[i] << " ";
cout << numl[n - 1] << endl;
delete numl;

这里依次输出了numl(有序)里面的每个元素加一个空格,这里为了防止最后一个数输出后多了一个空格导致PE错误,最后一个元素就单独输出了。最后用delete释放numl的空间。

你可能感兴趣的:(ACM训练)