算法思想一:遍历
解题思路:
可分为没插过队的和插过队的两种,最后的排队,没插过队的都在后面,插过队的最大值是个分界线。
没插过队的小于这个分界线最后一定不在原来的位置,大于的一定在原来位置,插过队的,我们从后往前看,删除重复的,就是排队序列
直接统计插过队的还在原来位置上的人数即可
代码展示:
Python版本
class Solution: def countDislocation(self , n , cutIn ): # write code here # 哈希表 cd = set() m = len(cutIn) if m == 0: return 0 # 插队的最大编号 res = max(cutIn) # 倒序遍历 for i in range(m-1,-1,-1): if cutIn[i] not in cd: # 如果当前人插队编号等于已插队的人数+1,则其还会处于原来位置 if cutIn[i]==len(cd)+1: # 去除还在原来位置的人 res-=1 # 记录已插队的人 cd.add(cutIn[i]) return res
复杂度分析
时间复杂度:N表示插入数组的长度,遍历插队数组时间
空间复杂的:哈希表占用空间
算法思想二:逆向思维
解题思路:
先来看一个例子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的一定不在原来位置(结合生活实际,很好理解:我们在排队的时候,不太关心排在自己前面的人的位置变动,因为这不会影响到我们自己的等待时间,但是一旦排在自己后面的人插到自己前面,我们就会很不爽,因为这会造成自己的等待时间边长)
题目让求有多少个人已经不在原来队伍的位置,我们可以逆向思维计算有多少人还在原来的位置,然后减去这个值就能得到位置改变的总人数
下图展示了例子中队伍的判断情况:
代码展示:
JAVA版本
import java.util.*; public class Solution { /** * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可 * * 计算有多少个人最终不在自己原来的位置上 * @param n int整型 队伍总长 * @param cutIn int整型一维数组 依次会插队到最前方的人的编号 * @return int整型 */ public int countDislocation (int n, int[] cutIn) { // write code here // 特殊情况 if(cutIn.length == 0) return 0; // 初始化 HashMap 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 记录遍历过的数字 visited.put(cutIn[i], true); // 判断插入人数恰好是该人原位置,则 same++ if(cutIn[i] == number++) same++; // 插队中找到最大的数,该数之前的位置都会变化,之后的都不会变化 max = Math.max(max, cutIn[i]); } } return max - same; } }
复杂度分析
时间复杂度:N表示插入数组的长度,遍历插队数组时间
空间复杂的:哈希表占用空间