问题描述:
给出一个不含重复数字的排列,求这些数字的所有排列按字典序排序后该排列的编号。其中,编号从1开始。
样例
样例 1:
输入:[1,2,4]
输出:1
样例 2:
输入:[3,2,1]
输出:6
思路:
-
按原始思路套,多层遍历,逐个比较,但是数字数量大的时候性能消耗大。
-
可研究规律,作如下算法:
- 设给定输入序列A,拷贝序列A至序列B(以vector装载)
- 从A中取出第一个数据a
- 对B排序
- 确认a在B中对应的下标
- 设B的大小为m,求出n*(m-1)!
- 然后从B中删除a
- 从A中取出下一个数据a
- 回到3,直到B的大小为1
- 最后将每次求出的值累加起来并加1,即所求的值
上述为何是n*(m-1)! 呢? 因为对于a来说,a对应B的下标n,说明有n个小于a的值存在,那么以这些小于a的值为头的都是排在a为头的前面的,每个值对应有(m-1)! 个排列,一共有n个,所以为n*(m-1)!。这些都需要统计出来。后续从B中删除a后又相当于重复上一轮流程。
class Solution {
public:
/**
* @param A: An array of integers
* @return: A long integer
*/
long long permutationIndex(vector<int> &A) {
// write your code here
// 拷贝一个临时副本
vector<int> B = A;
// 思路:
/*
对B排序,然后根据按顺序取A中的数据,每次取出一个A的数据a时,先对B排序,然后确认
a对应B的下表n,设B的大小为m,求出n*(m-1)!,然后删除B中的a,再排序,如此循环,最后将
结果累加并加1,即为所求值
*/
long long lRet = 0;
while(B.size() > 1)
{
// 从小到达排序
std::sort(B.begin(), B.end());
// 取得A中第一个数字在B中的下表
int i = 0;
auto iter = B.begin();
while(iter != B.end())
{
if(A[A.size() - B.size()] == *iter)
{
lRet += i * Calculate(B.size() - 1);
B.erase(iter);
break;
}
i++;
iter++;
}
}
return lRet + 1;
}
long long Calculate(size_t n)
{
long long llRet = 1;
while(n > 0)
{
llRet *= n;
n--;
}
return llRet;
}
};