class Solution
{
public:
void reverse(vector<int>& nums, int start, int end)
{
while (start < end)
{
swap(nums[start], nums[end]);
start += 1;
end -= 1;
}
}
void rotate(vector<int>& nums, int k)
{
// 防止被套圈
k %= nums.size();
// 先整体逆置
reverse(nums, 0, nums.size() - 1);
// 1. 将逆置到前面的逆置正常顺序
reverse(nums, 0, k - 1);
// 2. 将后面的也转换正常
reverse(nums, k, nums.size() - 1);
}
};
注意:第二个for循环中的 j 是从0遍历到 N(包括N),但实际上,当 j 等于 N 时,它并不与任何数组中的元素异或(因为数组索引是从0到N-1),但这并不影响结果,因为 N 与任何其他数字异或都会得到非零值(除非该数字也是 N,但这种情况不可能发生,因为数组中不可能有 N 这个元素)。
class Solution
{
public:
void reverse(vector<int>& nums, int start, int end)
{
while (start < end)
{
swap(nums[start], nums[end]);
start += 1;
end -= 1;
}
}
void rotate(vector<int>& nums, int k)
{
// 防止被套圈
k %= nums.size();
// 先整体逆置
reverse(nums, 0, nums.size() - 1);
// 1. 将逆置到前面的逆置正常顺序
reverse(nums, 0, k - 1);
// 2. 将后面的也转换正常
reverse(nums, k, nums.size() - 1);
}
};
经过这三次反转操作后,数组就被成功地旋转了 k 个位置。
这种方法的关键在于理解如何通过反转操作来重新排列数组中的元素。它避免了使用额外的空间,并且时间复杂度为 O(n),其中 n 是数组的长度。
class PalindromeList
{
public:
bool chkPalindrome(ListNode* A)
{
// 如果链表为空或者只有一个节点,那么它一定是回文的
if (A == nullptr || A->next == nullptr)
return true;
// 快慢指针找出中间节点,slow 指针指向链表中间位置(如果链表长度是奇数,则指向中间节点;如果是偶数,则指向中间两个节点的第一个)
ListNode* slow = A;
ListNode* fast = A;
while (fast != nullptr && fast->next != nullptr)
{
fast = fast->next->next;
slow = slow->next;
}
// 反转后半部分链表,prev 指针指向反转后的链表头部
ListNode* prev = nullptr;
ListNode* curr = slow;
while (curr != nullptr)
{
ListNode* temp = curr->next;
curr->next = prev;
prev = curr;
curr = temp;
}
// 检查回文,p1 指针指向链表头部,p2 指针指向反转后的链表头部
ListNode* p1 = A;
ListNode* p2 = prev;
while (p1 != nullptr && p2 != nullptr)
{
// 如果节点值不相等,则链表不是回文的
if (p1->val != p2->val)
{
return false;
}
p1 = p1->next;
p2 = p2->next;
}
// 链表是回文的
return true;
}
};
// 快慢指针找出中间节点,slow 指针指向链表中间位置(如果链表长度是奇数,则指向中间节点;如果是偶数,则指向中间两个节点的第一个)
ListNode* slow = A;
ListNode* fast = A;
while (fast != nullptr && fast->next != nullptr)
{
fast = fast->next->next;
slow = slow->next;
}
定义了两个指针 slow 和 fast,初始时它们都指向链表的头部。在循环中,fast 指针每次向前移动两步,而 slow 指针每次向前移动一步。当 fast 指针到达链表的末尾时,slow 指针就会指向链表的中间位置。
// 反转后半部分链表,prev 指针指向反转后的链表头部
ListNode* prev = nullptr;
ListNode* curr = slow;
while (curr != nullptr)
{
ListNode* temp = curr->next;
curr->next = prev;
prev = curr;
curr = temp;
}
就是将后半部分的链表每个节点
next
指向前一个节点,这样就形成了反转链表,链表头是翻转前的最后一个节点。 值得注意:前半部分的最后一个节点的next
还是指向翻转前的后半部分第一个节点(也就是后半部分反转后的最后一个节点),所以在后续进行判断两个链表是否相同的时候直接向后进行判断就可以。
// 检查回文,p1 指针指向链表头部,p2 指针指向反转后的链表头部
ListNode* p1 = A;
ListNode* p2 = prev;
while (p1 != nullptr && p2 != nullptr)
{
// 如果节点值不相等,则链表不是回文的
if (p1->val != p2->val)
{
return false;
}
p1 = p1->next;
p2 = p2->next;
}
// 链表是回文的
return true;
}
使用一个循环来比较两个指针所指向的节点值。如果发现任何一个节点值不相等,就意味着链表不是回文的,我们直接返回 false。如果循环完成后都没有发现不相等的节点值,那么链表就是回文的,我们返回 true。
class Solution
{
public:
ListNode* getIntersectionNode(ListNode* headA, ListNode* headB)
{
if (headA == nullptr || headB == nullptr)
{
return nullptr;
}
ListNode* pA = headA, * pB = headB;
// 如果有交点,那么在第一次相同的时候就会返回pa
while (pA != pB)
{
pA = pA == nullptr ? headB : pA->next;
pB = pB == nullptr ? headA : pB->next;
}
// 换道思想,如果两个链表长度不同,可以理解为补长度,
// 然后重新回到另一个链表的头的那个长度和和另一个指针指向剩余的长度相同,然后就可以达到一个相同的交点
return pA;
}
};
对于以下代码可能理解的时候存在一些误区,所以我作出以下思路整理:
while (pA != pB)
{
pA = pA == nullptr ? headB : pA->next;
pB = pB == nullptr ? headA : pB->next;
}
// 换道思想,如果两个链表长度不同,可以理解为补长度,
// 然后重新回到另一个链表的头的那个长度和和另一个指针指向剩余的长度相同,然后就可以达到一个相同的交点
return pA;