Skip to content

Latest commit

 

History

History
153 lines (96 loc) · 4.91 KB

0765._Couples_Holding_Hands.md

File metadata and controls

153 lines (96 loc) · 4.91 KB

765. Couples Holding Hands

难度: Hard

刷题内容

原题连接

内容描述

N couples sit in 2N seats arranged in a row and want to hold hands. We want to know the minimum number of swaps so that every couple is sitting side by side. A swap consists of choosing any two people, then they stand up and switch seats.

The people and seats are represented by an integer from 0 to 2N-1, the couples are numbered in order, the first couple being (0, 1), the second couple being (2, 3), and so on with the last couple being (2N-2, 2N-1).

The couples' initial seating is given by row[i] being the value of the person who is initially sitting in the i-th seat.

Example 1:

Input: row = [0, 2, 1, 3]
Output: 1
Explanation: We only need to swap the second (row[1]) and third (row[2]) person.
Example 2:

Input: row = [3, 2, 0, 1]
Output: 0
Explanation: All couples are already seated side by side.
Note:

len(row) is even and in the range of [4, 60].
row is guaranteed to be a permutation of 0...len(row)-1.

解题方案

思路 1 - 时间复杂度: O(N)- 空间复杂度: O(N)******

证明过程参考Java/C++ O(N) solution using cyclic swapping

以下所有的group指的都是connected component

并查集的思想,by saying "resolving a group", we mean placing the elements at each index contained in the group to their expected positions at the end of the swapping process.

先assume一个论点,size为n的group需要n-1次swap来使得所有的元素放在它应该在的index上

  • Base case:如果是一个size 为1的group,我们只需要0次swap就可以让所有的元素放在它应该在的index上
  • Induction case:如果是一个size 为k+1的group,那么这个group就是... --> i --> p --> ... --> j --> q --> ....的形式, 假设i本应该在的位置是j所在的位置,j本应该在的位置是i所在的位置,那么我们将i和j互相换一下,就将这个size为 k+1的group分成了两个disjoint的group。 假设这两个group的size分别为k1和k2,那么我们一直分下去,可以一直分到base case的情况,也就是说size为k1的group需要k1-1次swap,size为k2的grouo 需要k2-1次swap,那么将整个size为k的group全部放到正确的位置上所需要的swap次数就是1 + (k1-1) + (k2-1),也就是k1+k2-1 ,而其中k1 + k2 = k,所以对于Induction case来说也满足这个论点

证毕

那么对于我们的N对couple来说,其实就是N个node,我们可以把作为input的row看成一个大家庭,其中有几个group我们可以通过并查集或者dfs算出来,设为m, 每做一次swap我们就可以增加一个group,最终我们的目的就是形成N个group,即N对couple,所以我们一共需要N-m次swap

并查集,beats 34.83%

class UnionFind(object):
    def __init__(self, n):  # 初始化uf数组和组数目
        self.count = n
        self.uf = [i for i in range(n)]

    def find(self, x):  # 判断节点所属于的组
        while x != self.uf[x]:
            self.uf[x] = self.uf[self.uf[x]]
            x = self.uf[x]
        return self.uf[x]

    def union(self, x, y):  # 连接两个节点
        x_root = self.find(x)
        y_root = self.find(y)
        if x_root == y_root:
            return
        self.uf[x_root] = y_root
        self.count -= 1
    
class Solution:
    def minSwapsCouples(self, row):
        """
        :type row: List[int]
        :rtype: int
        """
        N = len(row) // 2
        uf = UnionFind(N)
        for i in range(N):
            m, n = row[2*i], row[2*i+1]
            uf.union(m // 2, n // 2) # 说明这两对couple,即这两个node相互连接
        return N - uf.count

思路 2 - 时间复杂度: O(N)- 空间复杂度: O(N)******

看完上面的思路,发现直接用贪心就可以,就是直接从头到尾遍历,没配对成功就立马配对,执行相应swap操作.

因为每一次这样的swap都至少增加了一个connected component,所以肯定是最优的

beats 34.83%

class Solution:
    def minSwapsCouples(self, row):
        """
        :type row: List[int]
        :rtype: int
        """
        lookup = {} # people: his idx in row
        for idx, ppl in enumerate(row):
            lookup[ppl] = idx
        res = 0
        for i in range(0, len(row), 2):
            p1 = row[i]
            p2 = p1 + 1 if p1 % 2 == 0 else p1 - 1 # Find p1's couple
            if row[i+1] != p2: # p1's couple isn't next to p1
                res += 1
                s = row[i+1] # stranger
                lookup[s], lookup[p2] = lookup[p2], i+1 # swap p2 and cur stranger
                row[lookup[s]], row[i+1] = s, p2 # couple in right idx, Stranger is gone
        return res