1. 程式人生 > >【LeetCode】765. Couples Holding Hands 解題報告(Python)

【LeetCode】765. Couples Holding Hands 解題報告(Python)

題目描述:

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:

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

題目大意

有0~N-1這些數字的排列組成的一個數組,0和1,2和3,…,N-2和N-1分別是情侶,現在要讓情侶坐在一起,每次能交換兩個人的座位,求最少需要換多少次座位。

解題方法

這個題不愧是Hard題,雖然題目簡單,但是沒有任何思路。

看了Grandyang大神的做法,使用貪心策略。直接找出第偶數個位置是否和他的另一半在一起,是的話不用交換,否則找出另一半在哪裡,然後直接把現在和自己坐在一起的人與自己的另一半的位置交換即可。(不會證明)

原文是:

當我們暫時對如何用程式碼來解決問題沒啥頭緒的時候,一個很好的辦法是,先手動解決問題,意思是,假設這道題不要求你寫程式碼,就讓你按照要求排好序怎麼做。我們隨便舉個例子來說吧,比如:

[3 1 4 0 2 5]

我們如何將其重新排序呢?首先明確,我們交換數字位置的動機是要湊對兒,如果我們交換的兩個數字無法組成新對兒,那麼這個交換就毫無意義。來手動交換吧,我們兩個兩個的來看數字,前兩個數是3和1,我們知道其不成對兒,數字3的老相好是2,不是1,那麼怎麼辦呢?我們就把1和2交換位置唄。好,那麼現在3和2牽手成功,度假去了,再來看後面的:

[3 2 4 0 1 5]

我們再取兩數字,4和0,互不認識!4跟5有一腿兒,不是0,那麼就把0和5,交換一下吧,得到:

[3 2 4 5 1 0]

好了,再取最後兩個數字,1和0,兩口子,不用動!前面都成對的話,最後兩個數字一定成對。而且這種方法所用的交換次數一定是最少的。

使用了一個很高效的求另一半的方式,使用1異或。如果是偶數的話,最後位是0,‘異或’上1等於加了1,變成了可以的成對奇數。如果是奇數的話,最後位是1,‘異或’上1後變為了0,變成了可以的成對偶數。

最壞情況下的時間複雜度是O(N^2),空間複雜度是O(1)。

class Solution(object):
    def minSwapsCouples(self, row):
        """
        :type row: List[int]
        :rtype: int
        """
        res = 0
        n = len(row)
        for i in range(0, n - 1, 2):
            if row[i + 1] == (row[i] ^ 1):
                continue
            for j in range(i + 1, n):
                if row[j] == (row[i] ^ 1):
                    row[j], row[i + 1] = row[i + 1], row[j]
            res += 1
        return res

另外一個解法,是使用並查集。但是這個解法我沒有看懂,所以只貼上程式碼,不講解了。

class Solution(object):
    def minSwapsCouples(self, row):
        """
        :type row: List[int]
        :rtype: int
        """
        n = len(row)
        cnt = n / 2
        self.par = range(n)
        for i in range(0, n, 2):
            x = self.find(row[i] / 2)
            y = self.find(row[i + 1] / 2)
            if x != y:
                self.par[x] = y
                cnt -= 1
        return n / 2 - cnt
        
    def find(self, x):
        return x if self.par[x] == x else self.find(self.par[x])

參考資料:

日期

2018 年 10 月 1 日 —— 歡度國慶!