一般而言,对于有序数组可以通过双指针法达到O(n + m)的时间复杂度。
这句话来自官方题解。反正我感觉有序的链表和数组都是一样的,用双指针就是了。虽然说删除链表中的元素更像是考察对链表这个数据结构本身的熟悉程度,但不管是对链表的添加和删除,我觉得都可以看成是快慢指针:慢指针指向当前元素,快指针指向需要添加或者删除的元素。
当然我并不觉得我这个实现很优雅,明明可以直接改一下链接的。
ListNode *deleteDuplicates(ListNode *head)
{
ListNode *slow = head;
ListNode *fast = head;
while (fast != nullptr)
{
if (slow != fast)
{
if (slow->val == fast->val)
{
slow->next = fast->next;
}
else
{
slow = fast;
}
}
fast = fast->next;
}
return head;
}
关于合并两个有序数组这个题,官方题解说最朴素的解法是合并后排序……但我竟然没想到这个解法,我第一反应就是类似于那个合并多项式的题,用双指针去做。好吧,其实最先想到的是类似于插入排序的解法,但是觉得还是双指针好一点。
void merge(vector<int> &nums1, int m, vector<int> &nums2, int n)
{
vector<int> res;
auto iter1 = 0;
auto iter2 = 0;
while (iter1 != m && iter2 != n)
{
auto val1 = nums1[iter1];
auto val2 = nums2[iter2];
if (val1 <= val2)
{
res.push_back(val1);
++iter1;
}
else
{
res.push_back(val2);
++iter2;
}
}
if (iter1 != m)
{
while (iter1 != m)
{
res.push_back(nums1[iter1]);
++iter1;
}
}
if (iter2 != n)
{
while (iter2 != n)
{
res.push_back(nums2[iter2]);
++iter2;
}
}
nums1.swap(res);
}
最后的交换有点取巧的意思,如果是数组就只能用memcpy了。这里用了一个额外的数组空间,我一直在想有没有什么不用额外空间的解法,但没想到。然后看了一下官方题解,说可以倒着用双指针,一时间惊为天人。仔细一想,反正题目要求就是修改nums1,而且给了挺大的空间,不用岂不是浪费。而且,这个做法还有一个好处,就是当循环结束之后,比正着用双指针少一个移动nums1剩下的元素的过程,只需要移动nums2的元素就行了。
void merge(vector<int> &nums1, int m, vector<int> &nums2, int n)
{
int p1 = m - 1;
int p2 = n - 1;
int p = m + n - 1;
while (p1 >= 0 && p2 >= 0)
{
int val1 = nums1[p1];
int val2 = nums2[p2];
if (val1 > val2)
{
nums1[p] = val1;
--p1;
}
else
{
nums1[p] = val2;
--p2;
}
--p;
}
if (p2 >= 0)
{
while (p2 >= 0)
{
nums1[p] = nums2[p2];
--p2;
--p;
}
}
}
这就是做数学题的时候用到的“正难则反”吧。