题目描述
你有一个长度为 n 的队伍,从左到右依次为 1~n,有 m 次插队行为,用数组 cutIn 进行表示,cutIn 的元素依次代表想要插队的人的编号,每次插队,这个人都会直接移动到队伍的最前方。你需要返回一个整数,代表这 m 次插队行为之后,有多少个人已经不在原来队伍的位置了。
示例1
输入:3,[3, 2, 3]
输出:2
说明:
初始队伍为 [1, 2, 3]
3 开始插队 [3, 1, 2]
2 开始插队 [2, 3, 1]
3 开始插队 [3, 2, 1]
所以2还在原来的尾置,3和1两个人已经不在原来的位置了。
思路
先来看一个例子15 [10, 4, 6, 5, 9, 7, 3, 3, 9, 10],我们来模拟一下插队过程。
第一次:10
第二次:4 10
第三次:6 4 10
第四次:5 6 4 10
第五次:9 5 6 4 10
第六次:7 9 5 6 4 10
第七次:3 7 9 5 6 4 10
第八次:3 7 9 5 6 4 10
第九次:9 3 7 5 6 4 10
第十次:10 9 3 7 5 6 4
最终位置:10 9 3 7 5 6 4 1 2 8 11 12 13 14 15
可以发现,队伍最终的排列是由最后一次插队决定的,然后没插队的人全部依次排在插队的人的后面。因此从后往前遍历cutIn数组,遇到重复的跳过。
判断是否还在原位置时,将队伍分为两部分计算:插队的人和没插队的人。对于插队的人,直接判断位置和序号是否匹配;设插队的人中最大序号为max,对于没插队的人,序号>max的一定还在原来队伍中的位置,<max的一定不在原来位置(结合生活实际,很好理解:我们在排队的时候,不太关心排在自己前面的人的位置变动,因为这不会影响到我们自己的等待时间,但是一旦排在自己后面的人插到自己前面,我们就会很不爽,因为这会造成自己的等待时间边长)
题目让求有多少个人已经不在原来队伍的位置,我们可以逆向思维计算有多少人还在原来的位置,然后减去这个值就能得到位置改变的总人数。
下图展示了例子中队伍的判断情况:
tips:
Set、HashMap等数据结构都可以处理重复值。HashMap的key值是编号这个没问题,对于value类型的选择,我直接用true/false来记录是否访问过;也可以使用Integer来记录编号为key的人在队伍中的最终位置。其实仔细观察会发现我们全程并不关心value值是多少(我们是通过map.containsKey()来判断是否重复的),也就是说HashMap完全可以退化成Set,不过实际提交代码时发现使用HashMap的程序运行时间更短,所以下面的代码还是用HashMap了。
JAVA代码
public class Solution { /** * 计算有多少个人最终不在自己原来的位置上 * @param n int整型 队伍总长 * @param cutIn int整型一维数组 依次会插队到最前方的人的编号 * @return int整型 */ public int countDislocation (int n, int[] cutIn) { if(cutIn.length == 0) return 0; HashMap<Integer, Boolean> visited = new HashMap<>(); int max = 0, same = 0, number = 1; for(int i = cutIn.length - 1; i >= 0; i--) { if(!visited.containsKey(cutIn[i])) { visited.put(cutIn[i], true); if(cutIn[i] == number++) same++; max = Math.max(max, cutIn[i]); } } return max - same; } }