剑指offer 45:把数组排成最小的数

题目描述

输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。例如输入数组{3,32,321},则打印出这三个数字能排成的最小数字为321323。

我的解法1:暴力法

直接遍历所有可能的结果,找出最小值。具体流程是先用二维数组储存所有可能的排列结果,然后遍历所有结果找出最小值,最后把这个最小值转成string即可。但有一点要注意的是在求和的时候如果变量是int型,可能会溢出,所以要定义double型,最后转成string的时候要截掉小数点和小数点之后的字符。此方法时间和空间开销都很大,不建议。

代码见下:

class Solution {
public:
	// 计算位数
	int countNum(int value)
	{
		int count = 1;
		while (value > 9)
		{
			value /= 10;
			count++;
		}
		return count;
	}

	string PrintMinNumber(vector<int> numbers) {
        if (numbers.size() == 0) return "";
        
		vector<vector<int> > vec2d;
		unordered_set<int> s;
		vector<int> vec;
		getAllOrders(numbers, vec2d, vec, s);

		double sum_min = 0;
		for (int j = 0; j < vec2d[0].size(); j++)
		{
			sum_min = sum_min * pow(10, countNum(vec2d[0][j])) + vec2d[0][j];
		}

		for (int i = 0; i < vec2d.size(); i++)
		{
			double curSum = 0;
			for (int j = 0; j < vec2d[i].size(); j++)
			{				
				curSum = curSum * pow(10, countNum(vec2d[i][j])) + vec2d[i][j];
			}

			if (curSum < sum_min) sum_min = curSum;
		}

		string str = to_string(sum_min);
		string str_out;
		for (int i = 0; i < str.size(); i++)
		{
			if (str[i] == '.') break;
			str_out.push_back(str[i]);

		}
		return str_out;
	}

	// 得到所有的排列,结果存在二维数组中
	void getAllOrders(const vector<int> &numbers, vector<vector<int> > &vec2d, vector<int> &vec, unordered_set<int> s)
	{
		if (vec.size() == numbers.size())
		{
			vec2d.push_back(vec);
			return;
		}

		for (int i = 0; i < numbers.size(); i++)
		{
			unordered_set<int> sCur = s;
			vector<int> vecCur = vec;

			if (sCur.find(i) == sCur.end())
			{
				vecCur.push_back(numbers[i]);
				sCur.insert(i);
				getAllOrders(numbers, vec2d, vecCur, sCur);
			}
		}
	}
};

我的解法2:自定义比较规则+排序

我的解法2用的排序,只是在比较大小时是对两个字符串比较,要自定义比较规则。我的思路是这样的:比如比较字符串a(342)和字符串b(3423),先一位位比较,然而直到a遍历结束二者都一样大。我们需要继续比较,要在a后面补位,那么补的值是多少呢?

以此例分析,要比较的两个拼成的数分别是ab和ba,即3423423和3423342,比较这两个数的大小即满足题意。比较过程:从第一位开始,3和3等,4和4等,2和2等,3和3等,4大于3,因此结果出来了。

由上述分析可知,a如果遍历到末尾了仍未分出大小,后面要依次补上b的元素,而因为b的前几位和a的所有位相等,所以补上b的元素等价于补上a的元素。因此遍历a和b字符串比较大小的过程如下:3=3,4=4,2=2,a遍历到末尾仍未分出大小,a的指针重新回到a[0],继续比较a和b,然后当b遍历到末尾仍未分出结果,b同理指针回到b[0]。当满足此条件时不管二者是否相等都要停止比较:遍历次数等于a的size加上b的size的时候,因为两个数拼起来位数就是这么多。

关于排序可用任意算法,我的解法用的快排,其他所有排序算法均可。

代码见下:

class Solution2 {
public:
	string PrintMinNumber(vector<int> numbers) 
	{
		string s;
		vector<string> vStr = vecIntToStr(numbers);
		quickSort(vStr, 0, vStr.size() - 1);

		for (int i = 0; i < vStr.size(); i++)
		{
			s += vStr[i];
		}

		return s;
	}

	// int转string
	vector<string> vecIntToStr(vector<int> numbers)
	{
		vector<string> vStr;
		for (int i = 0; i < numbers.size(); i++)
		{
			vStr.push_back(to_string(numbers[i]));
		}
		return vStr;
	}

	// 自定义比较逻辑
	bool isBiggerOrEqual(string a, string b)
	{
		int i = 0, j = 0;
		int aSize = a.size(), bSize = b.size();
		int sizeMax = aSize + bSize;

		for(int i = 0; i < sizeMax; i++)
		{
			// 判断
			if (a[i % aSize] > b[i % bSize]) return true;
			if (a[i % aSize] < b[i % bSize]) return false;
		}

		return true;
	}

	// 快排
	void quickSort(vector<string> &vStr, int left, int right)
	{
		if (left >= right) return;
		
		int i = left, j = right;
		string pivot = vStr[left];
		while (i < j)
		{
			while (i < j && isBiggerOrEqual(vStr[j], pivot))
				j--;
			if (i < j)
				vStr[i++] = vStr[j];

			while (i < j && isBiggerOrEqual(pivot, vStr[i]))
				i++;
			if (i < j)
				vStr[j--] = vStr[i];
		}
		vStr[i] = pivot;

		quickSort(vStr, left, i - 1);
		quickSort(vStr, i + 1, right);
	}
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值