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;
}
};