核心思路是并查集:
#用例子来解释
构建一个数组 f = [0,1,2,...,1000001],因为题目范围是0~1000000
3
2 1 1走一次
构建获取输入的数组arr = list(map(int,input().split())),arr = [2,1,1,3,4]
step1: arr[0],即:2进来,比对f[arr[0]] == 2即看f[2] == 2,发现一样则说明第一次进,则说明该值不用改变,
直接返回原值后arr[0] = 2,然后f[arr[0]] = find(arr[0] + 1),即f[2] = find(3),由于此时find(3)还没有,故式子其实是f[2] = 3,此时的索引从2变为3,这个3就是表示下一个数的索引位置。这表示下一次找2会直接索引到3
step2:arr[1],即:1进来,比对f[arr[1]] == 1即看f[1] == 1,发现一样则说明是初始的,是第一次进,则说明该值不用改变,
直接返回原值后arr[1] = 1,然后f[arr[1]] = find(arr[1] + 1),即f[1] = find(2),再进入函数后发现f[2] == 2不成立,则说明已经该2索引被占用。且此时f[2] = 3
f[2] = find(f[2])即f[2] = find(3)继续找,由于此时f[3] == 3还没有变化说明该索引可以使用,故式子其实是f[2] = 3,这个3就是表示下一个数的索引位置。得到了结果f[2] = 3 -> find[2] = 3 -> f[1] = find(2) -> f[1] = 3,最后索引从1变为了3,这表示下一次只要是找2,1都会直接索引到3
step3:arr[2],即:1再进来,比对f[arr[1]] == 1即看f[1] == 1,发现不一样则说明是多次进,则执行f[x] = find(f[x])即得f[1] = find(f[1])
又由step2得到,f[1] = 3,即f[1] = find(3),再递归进入函数后发现f[3] == 3成立,则说明已经该3索引可以使用,即得f[1] = 3
返回主函数有arr[2] = 3,更新下一个索引f[arr[2]] = find[arr[2] + 1]即:f[3] = find[4],由于此时f[4] == 4成立,可以表示下一个数的索引位置.
#初始一个并查集数组
f=[]
for i in range(0,1000002):
f.append(i)
#f = [0,1,2,...,1000001]
#寻找插入点,以下例子用2 1 1 3 4来解释
def find(x):
if f[x] == x:#如果这两个数相等,说明该数第一次出现,不用修改直接返回,例如2,1第一次进直接返回2,1
return x
else:#该数x出现过,且f[x]对应着下一个索引位置,例如1第二次进,则由下面'*'号行得,f[1] = find(2) = 3
f[x] = find(f[x])
return f[x]
#输入值,并把值提取到arr中
n = int(input())
arr = list(map(int,input().split()))
#开始查找合更新
for i in range(0,n):
arr[i] = find(arr[i])#查找arr[i]的插入点,例如2此时有arr[0] = 2
f[arr[i]] = find(arr[i]+1)#**********
#输出结果
for j in range(0,n):
print(arr[j],end=" ")
# 5
# 2 1 1 3 4
#对并查集不是很了解的可以参考,以下c++实现的代码https://blog.csdn.net/qq_17807067/article/details/127081554