Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Myfeature #1

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 69 additions & 0 deletions practive/leetcode/0148.sort-list/148.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/*
* 在 O(nlogn) 时间复杂度和常数级空间复杂度下,对链表进行排序。
* */

struct ListNode {
int val;
ListNode* next;
ListNode(int x)
:val(x)
,next(nullptr)
{}
};

/*
* 参考:Sort List——经典(链表中的归并排序) https://www.cnblogs.com/qiaozhoulin/p/4585401.html
* 归并排序法:在动手之前一直觉得空间复杂度为常量不太可能,因为原来使用归并时,都是 O(N)的,
* 需要复制出相等的空间来进行赋值归并。对于链表,实际上是可以实现常数空间占用的(链表的归并
* 排序不需要额外的空间)。利用归并的思想,递归地将当前链表分为两段,然后merge,分两段的方
* 法是使用 fast-slow 法,用两个指针,一个每次走两步,一个走一步,知道快的走到了末尾,然后
* 慢的所在位置就是中间位置,这样就分成了两段。merge时,把两段头部节点值比较,用一个 p 指向
* 较小的,且记录第一个节点,然后 两段的头一步一步向后走,p也一直向后走,总是指向较小节点,
* 直至其中一个头为NULL,处理剩下的元素。最后返回记录的头即可。
* 主要考察3个知识点,
* 知识点1:归并排序的整体思想
* 知识点2:找到一个链表的中间节点的方法
* 知识点3:合并两个已排好序的链表为一个新的有序链表
* */

class Solution {
public:
ListNode* sortList(ListNode* head) {
if (head == nullptr || head->next == nullptr) {
return head;
}
ListNode* left = head, *middle = left, *right = head;
while(right && right->next) {
right = right->next->next;
middle = left;
//跳出循环前,left 为中间节点,跳出后往后移动一位,指向 middle 后面的链表
left = left->next;
}
//将链表分成两部分,前一部分以 middle 结尾,应该置空,后一阶段以 left 开头
middle->next = nullptr;
//分别对 head、left 链表进行排序(合并两个有序链表)
return merge(sortList(head), sortList(left));
}
private:
//merge list
ListNode* merge(ListNode* l1, ListNode* l2) {
if(l1 == nullptr) {
return l2;
}
if(l2 == nullptr) {
return l1;
}
if(l1->val <= l2->val) {
l1->next = merge(l1->next, l2);
return l1;
} else {
l2->next = merge(l1, l2->next);
return l2;
}
}
};

/*
* 执行用时 : 68 ms, 在Sort List的C++提交中击败了96.75% 的用户
* 内存消耗 : 12.7 MB, 在Sort List的C++提交中击败了55.93% 的用户
* */
70 changes: 70 additions & 0 deletions practive/leetcode/0148.sort-list/sort-list.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package problem0148

/*
-----------------------------
Sort a linked list in O(n log n) time using constant space complexity.

Example 1:

Input: 4->2->1->3
Output: 1->2->3->4
Example 2:

Input: -1->5->3->4->0
Output: -1->0->3->4->5
--------------------------
*/

// ListNode 是题目预定义的数据类型
type ListNode = kit.ListNode

func sortList(head *ListNode) *ListNode {
if head == nil || head.Next == nil {
return head
}
left, right := split(head)
return merge(sortList(left), sortList(right))
}
// 从中间位置,切分 list
func split(head *ListNode) (left, right *ListNode) {
// head.Next != nil
// 因为, sortList 已经帮忙检查过了
// fast 的变化速度是 slow 的两倍
// 当 fast 到达末尾的时候,slow 正好在 list 的中间
slow, fast := head, head
var slowPre *ListNode
for fast != nil && fast.Next != nil {
slowPre, slow = slow, slow.Next
fast = fast.Next.Next
}
// 斩断 list
slowPre.Next = nil
// 使用 slowPre 是为了确保当 list 的长度为 2 时,会均分为两个长度为 1 的子 list
left, right = head, slow
return
}
// 把已经排序好了的两个 list left 和 right
// 进行合并
func merge(left, right *ListNode) *ListNode {
// left != nil , right != nil

// 因为, sortList 已经帮忙检查过了

cur := &ListNode{}
headPre := cur

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ineffectual assignment to headPre (from ineffassign)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这个测试代码怎么弄啊

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

你还写 Go 代码?

他的意思是:对 headPre 的无效分配(来自 ineffassign lint)

for left != nil && right != nil {
// cur 总是接上较小的 node
if left.Val < right.Val {
cur.Next, left = left, left.Next
} else {
cur.Next, right = right, right.Next
}
cur = cur.Next
}
// 处理 left 或 right 中,剩下的节点
if left == nil {
cur.Next = right
} else {
cur.Next = left
}
}