坚持就是胜利! 奥利给!
148. 排序链表 - medium
排序都是用自带sort,而且这还是第一次写链表的归并,所以慢的不行。。。
链表的归并需要注意,每次找到中点后都要让中点前一点的next = nullptr,不然没有递归出口。
//19.58
//20.48
//其实看了答案后才想到这并不是空间复杂度为O(1),因为递归要栈空间
class Solution {
void mergeSort(ListNode* &head) {
if(head == nullptr) return ;
if(head->next == nullptr) return ;
ListNode *slow, *fast, *curr;
fast = slow = curr = head;
while(fast) {
slow = curr;
curr = curr->next;
fast = fast->next;
if(fast) fast = fast->next;
}
/*都一样的
while(fast && fast->next) {
slow = curr;
curr = curr->next;
fast = fast->next->next;
}
*/
slow->next = nullptr;
mergeSort(head);
mergeSort(curr);
merge(head, curr);
}
void merge(ListNode* &head, ListNode* &slow) { //这里的slow就是上面的curr,
if(slow == nullptr) return ; //一开始写没有把中点前改成nullptr所以用了slow
ListNode *tmp = head, **cur = &head;
/*其实这里可以
ListNode header(-1);
ListNode *cur = &header;
那下面就没必要用(ListNode **)这么麻烦了
*/
while(tmp && slow) {
if(tmp->val > slow->val) {
*cur = slow;
slow = slow->next;
}
else {
*cur = tmp;
tmp = tmp->next;
}
cur = &((*cur)->next);
}
if(tmp == nullptr) {
*cur = slow;
}
else {
*cur = tmp;
}
}
public:
ListNode* sortList(ListNode* head) {
if(head == nullptr) return head;
mergeSort(head);
return head;
}
};
贴一个大佬写的递归归并排序,迭代版本看另一位大佬ivan_allen
class Solution {
public:
ListNode* sortList(ListNode* head) {
if (head == NULL || head->next == NULL)
{
return head;
}
ListNode* pmid;
ListNode* mid = head;
ListNode* trail = head;
while (trail && trail->next)
{
pmid = mid;
mid = mid->next;
trail = trail->next->next;
}
pmid->next = NULL;
return twoWayMerge(sortList(head), sortList(mid));
}
ListNode* twoWayMerge(ListNode* l1, ListNode* l2) {
ListNode header(-1);
ListNode *p = &header;
while (l1 && l2)
{
if (l1->val < l2->val)
{
p->next = l1;
l1 = l1->next;
}
else
{
p->next = l2;
l2 = l2->next;
}
p = p->next;
}
p->next = l1 == NULL ? l2 : l1;
return header.next;
}
};
作者:jiangtianyu007
链接:https://leetcode-cn.com/problems/sort-list/solution/kuai-man-zhi-zhen-er-lu-gui-bing-c-by-jiangtianyu0/
来源:力扣(LeetCode)
287. 寻找重复数
一. 修改数值
把nums[i] 作为索引,对对应索引的值乘 -1,这样当我们遇到某个数师负数时就可以知道我们已经修改过这个数了
//21.49
//22.03
class Solution {
public:
int findDuplicate(vector<int>& nums) {
for(int i = 0; i < nums.size(); i++) {
if(nums[abs(nums[i])-1] < 0) return abs(nums[i]);
else nums[abs(nums[i])-1] *= -1;
}
return -1;
}
};
二. 二分法查找法
类似于你有一个称,找出其中重量不同的小球。每次我们都取中值mid,然后看有多少个数大于mid,若超过一半则重复的数字必定在mid之后(mid+1),否则就在mid之前
class Solution {
int helper(vector<int> & nums, int small, int large) {
if(small == large) {
return small;
}
int cntr, cntl, mid;
cntl = cntr = 0;
mid = (small+large)/2;
for(int x : nums) {
if(x < small || x > large) continue;
if(x > mid) cntr++;
else cntl++;
} //要注意再递归时,究竟时mid+1,还是mid
return (cntl > cntr)? helper(nums, small, mid) : helper(nums, mid+1, large);
}
public:
int findDuplicate(vector<int>& nums) {
return helper(nums, 1, nums.size()-1);
}
};
三. 快慢指针
参考atbulbs在leetcode的答案,可以把索引看作指针,这样就会后多个位置指向同一个地方,相当于循环列表找相交点,可以在快慢指针第一次相遇时,从头多一个慢指针,两慢指针必定在列表中的循环处相遇(证明可以看atbulbs)
var findDuplicate = function(nums) {
let slowPointer = 0
let fastPointer = 0
while (true) {
slowPointer = nums[slowPointer]
fastPointer = nums[nums[fastPointer]]
if (slowPointer == fastPointer) {
let _slowPointer = 0
while (nums[_slowPointer] !== nums[slowPointer]) {
slowPointer = nums[slowPointer]
_slowPointer = nums[_slowPointer]
}
return nums[_slowPointer]
}
}
};
作者:atbulbs
链接:https://leetcode-cn.com/problems/find-the-duplicate-number/solution/qian-duan-ling-hun-hua-shi-tu-jie-kuai-man-zhi-z-3/
来源:力扣(LeetCode)