leetcode刷题(二)

92. 反转链表 II

反转从位置 m 到 n 的链表。请使用一趟扫描完成反转。

题解

两个思路:①不断调换指针②不断交换值。因为\(m\)可能等于1,所以要添加一个头节点(好写)

用循环比用while方便多了,还不容易出bug

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* reverseBetween(ListNode* head, int m, int n) {

        ListNode* root = new ListNode(0);    // 加一个头节点很方便
        root->next = head;

        ListNode* copy_head = root;

        for (int i = 1; i < m; ++i) root = root->next;
        /*  
        与for循环的功能一样,找出翻转位置的前一个节点
        注意:index++;不要放错位置!!!

        int index = 0;
        while(root->next != NULL) {
            if (index + 1 >= m) break;
            root = root->next;
            index++;
        }
        */
        head = root->next;
        for (int i = m; i < n; ++i) {
            ListNode* temp = root->next;

            root->next = head->next;
            head->next = head->next->next;
            root->next->next = temp;
        }

        return copy_head->next;
        /*
        同上面的for循环一样的功能
        ListNode* start = root;
        head = root->next;

        int cnt = 0;
        while(head->next != NULL) {
            ++cnt;
            if (cnt == n - m + 1) break;
            ListNode* temp = start->next;

            start->next = head->next;
            head->next = head->next->next;
            start->next->next = temp;
        }

        return copy_head->next;
        */
    }
};

765. 情侣牵手

N 对情侣坐在连续排列的 2N 个座位上,想要牵到对方的手。 计算最少交换座位的次数,以便每对情侣可以并肩坐在一起。 一次交换可选择任意两人,让他们站起来交换座位。

人和座位用 0 到 2N-1 的整数表示,情侣们按顺序编号,第一对是 (0, 1),第二对是 (2, 3),以此类推,最后一对是 (2N-2, 2N-1)。

这些情侣的初始座位 row[i] 是由最初始坐在第 i 个座位上的人决定的。

题解一

这道题蕴含了一个经常拿来出题的考点:对于任意一个排列,按照下标建边构成的图由一个个不相交的循环组成。举例来说:[3, 1, 0, 2],按照上述方式构图有两个循环:\(0 \rightarrow 3 \rightarrow 2 \rightarrow 0\)\(1 \rightarrow 1\)(自循环)。

先思考这样一个问题:Meeting with Aliens UVA - 10570,\(n\)个数的排列,一次操作选任意两个数进行交换,问使得排列变为升序或者降序的最少操作次数?

排列按照上述方式构图后如果只由一个循环组成,比如[2, 3, 1, 0]这样一个排列,那么所需交换次数最少为环中节点个数减一,既\(op_{min} = n - 1\)。如果由多个环构成,假设环的个数为\(t\),每个环的节点个数为\(cnt_i\),那么所需交换次数最少为\(\sum_{i = 0}^t (cnt_i - 1)\),整理哈,既\(op_{min} = n - t\)

有兴趣的童鞋可以尝试用数学归纳法证明单个循环的情况(黑体字部分),注意:两个环之间不需要交换操作,既只在环内进行交换操作。因为交换到另一个环后,最终又要交换回来,毕竟它在升序或者降序中的位置就在刚开始的环上。

对于这道题,很容易想到先把情侣的一人固定在当前位置上,然后交换另一个人来凑成情侣。复杂度\(O(n^2)\),空间复杂度\(O(1)\)

class Solution {
public:
    int minSwapsCouples(vector<int>& row) {
        int n = row.size();
        int ans = 0;
        
        for (int i = 0; i < n; i += 2) {
            if (judeg(row[i],  row[i + 1])) continue;
            for (int j = i + 2; j < n; j += 2) {
                if (judeg(row[j], row[j + 1])) continue;
                if (judeg(row[i], row[j])) {
                    swap(row[i + 1], row[j]);
                    ans++;
                    break;
                }
                else if (judeg(row[i], row[j + 1])) {
                    swap(row[i + 1], row[j + 1]);
                    ans++;
                    break;
                }
            }
        }

        return ans;
    }
    bool judeg(int a, int b) {
        if (a > b) swap(a, b);
        if (b - a > 1) return false;
        if (!(a & 1) && (b & 1)) return true;
        return false;
    }
};

第二个循环的作用是找它的配偶所在位置并交换,所以用一个数组记录一下,每个人的位置。时间复杂度\(O(n)\),空间复杂度\(O(n)\).

这叫有失有得,另外,学习优化代码可重要了

class Solution {
public:
    int minSwapsCouples(vector<int>& row) 
    {
        int n = row.size(), ans = 0;
        vector<int> pos(n, -1);

        for (int i = 0; i < n; ++i) pos[row[i]] = i;

        for (int i = 0; i < n; i += 2) {
            int wife = row[i] ^ 1;
            if (wife != row[i + 1]) {
                int id = pos[wife];
                swap(row[i + 1], row[id]);
                pos[row[id]] = id;
                ans++;
            }
        }
        return ans;
    }
};
posted @ 2020-04-24 12:12  天之道,利而不害  阅读(380)  评论(0编辑  收藏  举报