Skip to content

Commit

Permalink
feat: $147
Browse files Browse the repository at this point in the history
  • Loading branch information
lucifer committed Nov 20, 2020
1 parent 38632f0 commit 3597b8e
Show file tree
Hide file tree
Showing 4 changed files with 220 additions and 1 deletion.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ leetcode 题解,记录自己的 leetcode 解题之路。
目前更新了 200 多道题解,加上专题涉及的题目,差不多有 **300 道**

- [高频考题(简单 72 题)](./collections/easy.md)
- [高频考题(中等 114 题)](./collections/medium.md)
- [高频考题(中等 115 题)](./collections/medium.md)
- [高频考题(困难 30 题)](./collections/hard.md)

### 数据结构与算法的总结(21 篇)
Expand Down
1 change: 1 addition & 0 deletions SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@
* [0131. 分割回文串](problems/131.palindrome-partitioning.md)
* [0139. 单词拆分](problems/139.word-break.md)
* [0144. 二叉树的前序遍历](problems/144.binary-tree-preorder-traversal.md)
* [0147. 对链表进行插入排序](../problems/147.insertion-sort-list.md)
* [0150. 逆波兰表达式求值](problems/150.evaluate-reverse-polish-notation.md)
* [0152. 乘积最大子数组](problems/152.maximum-product-subarray.md)
* [0199. 二叉树的右视图](problems/199.binary-tree-right-side-view.md)
Expand Down
1 change: 1 addition & 0 deletions collections/medium.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
- [0131. 分割回文串](../problems/131.palindrome-partitioning.md)
- [0139. 单词拆分](../problems/139.word-break.md)
- [0144. 二叉树的前序遍历](../problems/144.binary-tree-preorder-traversal.md)
- [0147. 对链表进行插入排序](../problems/147.insertion-sort-list.md) 🆕
- [0150. 逆波兰表达式求值](../problems/150.evaluate-reverse-polish-notation.md)
- [0152. 乘积最大子数组](../problems/152.maximum-product-subarray.md)
- [0199. 二叉树的右视图](../problems/199.binary-tree-right-side-view.md)
Expand Down
217 changes: 217 additions & 0 deletions problems/147.insertion-sort-list.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,217 @@
<!-- 刚看了几个题解,写的不是很清楚,于是写下了这个。 -->

## 题目地址(147. 对链表进行插入排序)

https://leetcode-cn.com/problems/insertion-sort-list/

## 题目描述

对链表进行插入排序。

![](https://tva1.sinaimg.cn/large/0081Kckwly1gkvig9vromg308c050q55.gif)

```
插入排序的动画演示如上。从第一个元素开始,该链表可以被认为已经部分排序(用黑色表示)。
每次迭代时,从输入数据中移除一个元素(用红色表示),并原地将其插入到已排好序的链表中。
 
插入排序算法:
插入排序是迭代的,每次只移动一个元素,直到所有元素可以形成一个有序的输出列表。
每次迭代中,插入排序只从输入数据中移除一个待排序的元素,找到它在序列中适当的位置,并将其插入。
重复直到所有输入数据插入完为止。
 
示例 1:
输入: 4->2->1->3
输出: 1->2->3->4
示例 2:
输入: -1->5->3->4->0
输出: -1->0->3->4->5
```

## 思路

这道题属于链表题目中的修改指针。看过我链表专题的小伙伴应该熟悉我解决这种链表问题的套路了吧?

关于链表的题目,我总结了四个技巧**虚拟头****快慢指针****穿针引线****先穿再排后判空**,这道题我们使用到了两个,分别是**虚拟头****先穿再排后判空**。这四个技巧的具体内容可以查看我的[《链表专题》](https://lucifer.ren/blog/2020/11/08/linked-list/)

链表问题首先我们看返回值,如果返回值不是原本链表的头的话,我们可以使用虚拟节点。

此时我们的代码是这样的:

```py
class Solution:
def insertionSortList(self, head: ListNode) -> ListNode:
ans = ListNode(float("-inf"))
# do domething
return ans.next
```

接着,我们理清算法整体框架,把细节留出来。

我们的算法应该有两层循环,外层控制当前需要插入的元素,内层选择插入的位置。

此时我们的代码是这样的:

```py
class Solution:
def insertionSortList(self, head: ListNode) -> ListNode:
ans = ListNode(float("-inf"))

def insert(to_be_insert):
# 选择插入的位置,并插入

while head:
insert(head)
head = head.next
return ans.next
```

而选择插入位置的代码也不难,只需要每次都从头出发遍历,找到第一个大于 to_be_insert 的,在其前面插入即可(如果有的话):

```py
# ans 就是上面我提到的虚拟节点
ans = cur
while cur.next and cur.next.val < to_be_insert.val:
cur = cur.next
```

而链表插入是一个基本操作,不多解释,直接看代码:

```py
to_be_insert.next = cur.next
cur.next = to_be_insert
```

于是完整代码就呼之欲出了:

```py
class Solution:
def insertionSortList(self, head: ListNode) -> ListNode:
ans = ListNode(float("-inf"))

def helper(inserted):
cur = ans
while cur.next and cur.next.val < inserted.val:
cur = cur.next
inserted.next = cur.next
cur.next = inserted

while head:
helper(head)
head = head.next
return ans.next
```

到这里还没完,继续使用我的第二个技巧**先穿再排后判空**。由于这道题没有穿,我们直接排序和判空即可。

next 改变的代码一共两行,因此只需要关注这两行代码即可:

```py
inserted.next = cur.next
cur.next = inserted
```

inserted 的 next 改变实际上会影响外层,解决方案很简单,**留个联系方式**就好。这我在上面的文章也反复提到过。

```py
class Solution:
def insertionSortList(self, head: ListNode) -> ListNode:
ans = ListNode(float("-inf"))

def insert(to_be_insert):
# 选择插入的位置,并插入
# 这里 to_to_insert 的 next 会被修改,进而影响外层的 head

while head:
# 留下联系方式
next = head.next
insert(head)
# 使用联系方式更新 head
head = next
return ans.next

```

> 不熟悉这个技巧的看下上面提到的我写的链表文章即可。
如果你上面代码你会了,将 insert 代码整个复制出来就变成大部分人的解法了。不过我还是建议新手按照我的这个模式一步步来,稳扎稳打,不要着急。

![](https://tva1.sinaimg.cn/large/0081Kckwly1gkvie3jcz4j315h0dp428.jpg)

## 代码

代码支持:Python3,Java,JS

Python Code:

```py
class Solution:
def insertionSortList(self, head: ListNode) -> ListNode:
ans = ListNode(float("-inf"))

while head:
next = head.next
cur = ans
while cur.next and cur.next.val < head.val:
cur = cur.next
head.next = cur.next
cur.next = head
head = next
return ans.next
```

Java Code:

```java
class Solution {
public ListNode insertionSortList(ListNode head) {
ListNode ans = new ListNode(-1);
while( head != null ){
ListNode next = head.next;
ListNode cur = ans;
while(cur.next != null && cur.next.val < head.val ){
cur = cur.next;
}
head.next = cur.next;
cur.next = head;
head = next;
}

return ans.next;
}
}
```

JS Code:

```js
var insertionSortList = function (head) {
ans = new ListNode(-1);
while (head != null) {
next = head.next;
cur = ans;
while (cur.next != null && cur.next.val < head.val) {
cur = cur.next;
}
head.next = cur.next;
cur.next = head;
head = next;
}

return ans.next;
};
```

**复杂度分析**

- 时间复杂度:$O(N^2)$,其中 N 为链表长度。
- 空间复杂度:$O(1)$。

大家对此有何看法,欢迎给我留言,我有时间都会一一查看回答。更多算法套路可以访问我的 LeetCode 题解仓库:https://github.com/azl397985856/leetcode 。 目前已经 38K star 啦。大家也可以关注我的公众号《力扣加加》带你啃下算法这块硬骨头。

0 comments on commit 3597b8e

Please sign in to comment.