全排列 & 康托展开

# 去重全排列

复制代码
#include<bits/stdc++.h>  
using namespace std;  
int num[100],cnt;
void fun(int pos,int n);
int judge(int l,int r);
int main()  
{  
  int i,n;
  scanf("%d",&n);
  for(i=1;i<=n;i++) scanf("%d",&num[i]);
  fun(1,n);
  system("pause");
  return 0;
}
int judge(int l,int r)
{
  for(;l<r;l++)
   if(num[l]==num[r]) return 1;
  return 0;
}
void fun(int pos,int n)
{
  if(pos==n+1)
  {
    cnt++;
    for(int i=1;i<=n;i++)
     printf("%d%c",num[i],i==n?'\n':' ');
    return ;
  }
  for(int i=pos;i<=n;i++)
  {
    if(judge(pos,i)) continue;
    swap(num[pos],num[i]);
    fun(pos+1,n);
    swap(num[pos],num[i]);
  }
}
复制代码

# 康托

## 定义

康托展开是一个全排列到一个自然数的双射,常用于构建哈希表时的空间压缩。 康托展开的实质是计算当前排列在所有由小到大全排列中的顺序,因此是可逆的。

## 康托展开

令X为全排列中的次序,num[]为原数组,n为全排列中数字的个数,为num[j] (j>i)小于num[i]的个数。

 

例:有(1,2,3,4,5)5个数的全排列中,计算34152的康托展开值。

通过定义可知,a[5] = 2 (1<3,2<3),a[4] = 2 (1<4,2<4),a[3] = 0 (无),a[2] = 1 (2<5),a[1] = 0 (无)。

又因为 n = 5,则X=2*4! + 2*3! + 0*2! + 1*1! + 0*0! = 61

所以在全排列中比34152小的有61个,即34152在全排列中排第62位(61+1=62)。

## 逆康托展开

因为康托展开是一个全排列到一个自然数的双射,所以可以通过61计算出34152。

,得出

例:

                       61/4! = 2...13  则a[5] = 2,num[5]=3

                       13/3! = 2...1  则a[5] = 2,num[4]=4

                       1/2! = 0...1  则a[5] = 0,num[4]=1

                       1/1! = 1...0  则a[5] = 1,num[2]=5

                       则num[1]为剩下的数,num[1]=2

 所以前面有61个排列,即第62个全排列为34152。

# 下一个全排列

26458173的下一个全排列为26458317。

步骤:

      ①从后往前找到第一个 的位置 (即样例中为1<7)

      ②从后往前找到第一个大于的数 x ,交换  和 x的位置 ( =1,x=3),得到26458371

      ③将位置 i 以后的数反转,得到26458317。

上一个全排列的求法与下一个全排列类似,自行解决。😒

posted @   Vivid-BinGo  阅读(265)  评论(2编辑  收藏  举报
编辑推荐:
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 上周热点回顾(3.3-3.9)
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
点击右上角即可分享
微信分享提示