|
28 | 28 | - [合并排序链表](#合并排序链表)
|
29 | 29 | - [两个链表的第一个公共节点](#两个链表的第一个公共节点)
|
30 | 30 | - [链表排序](#链表排序)
|
31 |
| - - [分隔链表(Partition List)](#分隔链表partition-list) |
32 |
| - - [链表快排(Sort List)](#链表快排sort-list) |
| 31 | + - [链表快排](#链表快排) |
| 32 | + - [链表归并](#链表归并) |
| 33 | + - [链表插入排序](#链表插入排序) |
33 | 34 | - [二维数组](#二维数组)
|
34 | 35 | - [二分查找](#二分查找)
|
35 | 36 | - [搜索二维矩阵 1](#搜索二维矩阵-1)
|
@@ -785,64 +786,7 @@ public:
|
785 | 786 | ## 链表排序
|
786 | 787 | > [链表排序(冒泡、选择、插入、快排、归并、希尔、堆排序) - tenos](https://www.cnblogs.com/TenosDoIt/p/3666585.html) - 博客园
|
787 | 788 |
|
788 |
| -### 分隔链表(Partition List) |
789 |
| -> LeetCode/[86. 分隔链表](https://leetcode-cn.com/problems/partition-list/description/) |
790 |
| - |
791 |
| -**问题描述** |
792 |
| -``` |
793 |
| -给定一个链表和一个特定值 x,对链表进行分隔,使得所有小于 x 的节点都在大于或等于 x 的节点之前。 |
794 |
| - |
795 |
| -你应当保留两个分区中每个节点的初始相对位置。 |
796 |
| - |
797 |
| -示例: |
798 |
| - 输入: head = 1->4->3->2->5->2, x = 3 |
799 |
| - 输出: 1->2->2->4->3->5 |
800 |
| -``` |
801 |
| -
|
802 |
| -**思路** |
803 |
| -- 链表快排的中间操作; |
804 |
| -- 新建两个链表,分别保存小于 x 和大于等于 x 的,最后拼接; |
805 |
| -- 因为要求节点的相对位置不变,所以这么写比较方便; |
806 |
| -- 一般来说,链表快排有两种写法:一种是交换节点内的值,一种是交换节点;该写法适用于后者。 |
807 |
| -- ~~如果是用在链表快排中,可以把头节点作为 x,最后把 x 插进 lo 和 hi 链表的中间;~~ |
808 |
| -- ~~这种写法不适合用在链表快排中,因为这里有拼接操作;~~ |
809 |
| -- ~~在实际链表快排中 partition 操作只对中间一部分执行,如果需要拼接,容易出错。~~ |
810 |
| -
|
811 |
| -**Python** |
812 |
| -```python |
813 |
| -# Definition for singly-linked list. |
814 |
| -# class ListNode: |
815 |
| -# def __init__(self, x): |
816 |
| -# self.val = x |
817 |
| -# self.next = None |
818 |
| -
|
819 |
| -class Solution: |
820 |
| - def partition(self, h, x): |
821 |
| - """ |
822 |
| - :type h: ListNode |
823 |
| - :type x: int |
824 |
| - :rtype: ListNode |
825 |
| - """ |
826 |
| - l = lo = ListNode(0) |
827 |
| - r = hi = ListNode(0) |
828 |
| - |
829 |
| - while h: |
830 |
| - if h.val < x: |
831 |
| - l.next = h # Python 中不支持 l = l.next = h 的写法,C++ 指针可以 |
832 |
| - l = l.next |
833 |
| - else: |
834 |
| - r.next = h # Python 中不支持 r = r.next = h 的写法,C++ 指针可以 |
835 |
| - r = r.next |
836 |
| - |
837 |
| - h = h.next |
838 |
| - |
839 |
| - r.next = None # 因为尾节点可能不小于 x,所以需要断开 |
840 |
| - l.next = hi.next |
841 |
| - |
842 |
| - return lo.next |
843 |
| -``` |
844 |
| - |
845 |
| -### 链表快排(Sort List) |
| 789 | +### 链表快排 |
846 | 790 | > LeetCode/[148. 排序链表](https://leetcode-cn.com/problems/sort-list/description/)
|
847 | 791 |
|
848 | 792 | **问题描述**
|
@@ -971,6 +915,201 @@ public:
|
971 | 915 | };
|
972 | 916 | ```
|
973 | 917 |
|
| 918 | +### 链表归并 |
| 919 | +> LeetCode/[148. 排序链表](https://leetcode-cn.com/problems/sort-list/description/) |
| 920 | +
|
| 921 | +**问题描述** |
| 922 | +``` |
| 923 | +在 O(n log n) 时间复杂度和常数级空间复杂度下,对链表进行排序。 |
| 924 | + |
| 925 | +示例 1: |
| 926 | + 输入: 4->2->1->3 |
| 927 | + 输出: 1->2->3->4 |
| 928 | +示例 2: |
| 929 | + 输入: -1->5->3->4->0 |
| 930 | + 输出: -1->0->3->4->5 |
| 931 | +``` |
| 932 | +
|
| 933 | +**思路** |
| 934 | +- 用快慢指针的方法找到链表中间节点,然后递归的对两个子链表排序,把两个排好序的子链表合并成一条有序的链表 |
| 935 | +- 归并排序比较适合链表,它可以保证了最好和最坏时间复杂度都是 `O(NlogN)`,而且它在数组排序中广受诟病的空间复杂度在链表排序中也从O(n)降到了 `O(1)` |
| 936 | + - 因为链表快排中只能使用第一个节点作为枢纽,所以不能保证时间复杂度 |
| 937 | +- 还是使用 C++ |
| 938 | +
|
| 939 | +**C++** |
| 940 | +```C++ |
| 941 | +/** |
| 942 | + * Definition for singly-linked list. |
| 943 | + * struct ListNode { |
| 944 | + * int val; |
| 945 | + * ListNode *next; |
| 946 | + * ListNode(int x) : val(x), next(NULL) {} |
| 947 | + * }; |
| 948 | + */ |
| 949 | + |
| 950 | +class Solution { |
| 951 | + ListNode* merge(ListNode *h1, ListNode *h2) { // 排序两个链表 |
| 952 | + if (h1 == nullptr) return h2; |
| 953 | + if (h2 == nullptr) return h1; |
| 954 | + |
| 955 | + ListNode* h; // 合并后的头结点 |
| 956 | + if (h1->val < h2->val) { |
| 957 | + h = h1; |
| 958 | + h1 = h1->next; |
| 959 | + } else { |
| 960 | + h = h2; |
| 961 | + h2 = h2->next; |
| 962 | + } |
| 963 | + |
| 964 | + ListNode* p = h; |
| 965 | + while (h1 && h2) { |
| 966 | + if (h1->val < h2->val) { |
| 967 | + p->next = h1; |
| 968 | + h1 = h1->next; |
| 969 | + } else { |
| 970 | + p->next = h2; |
| 971 | + h2 = h2->next; |
| 972 | + } |
| 973 | + p = p->next; |
| 974 | + } |
| 975 | + |
| 976 | + if (h1) p->next = h1; |
| 977 | + if (h2) p->next = h2; |
| 978 | + |
| 979 | + return h; |
| 980 | + } |
| 981 | + |
| 982 | +public: |
| 983 | + ListNode* sortList(ListNode* h) { |
| 984 | + if (h == nullptr || h->next == nullptr) |
| 985 | + return h; |
| 986 | + |
| 987 | + auto f = h, s = h; // 快慢指针 fast & slow |
| 988 | + while (f->next && f->next->next) { |
| 989 | + f = f->next->next; |
| 990 | + s = s->next; |
| 991 | + } |
| 992 | + f = s->next; // 中间节点 |
| 993 | + s->next = nullptr; // 断开 |
| 994 | + |
| 995 | + h = sortList(h); // 前半段排序 |
| 996 | + f = sortList(f); // 后半段排序 |
| 997 | + |
| 998 | + return merge(h, f); |
| 999 | + } |
| 1000 | +}; |
| 1001 | +``` |
| 1002 | + |
| 1003 | +### 链表插入排序 |
| 1004 | +> LeetCode/[147. 对链表进行插入排序](https://leetcode-cn.com/problems/insertion-sort-list/description/) |
| 1005 | +
|
| 1006 | +**问题描述** |
| 1007 | +``` |
| 1008 | +对链表进行插入排序。 |
| 1009 | +
|
| 1010 | +插入排序是迭代的,每次只移动一个元素,直到所有元素可以形成一个有序的输出列表。 |
| 1011 | +每次迭代中,插入排序只从输入数据中移除一个待排序的元素,找到它在序列中适当的位置,并将其插入。 |
| 1012 | +重复直到所有输入数据插入完为止。 |
| 1013 | +
|
| 1014 | +示例 1: |
| 1015 | + 输入: 4->2->1->3 |
| 1016 | + 输出: 1->2->3->4 |
| 1017 | +示例 2: |
| 1018 | + 输入: -1->5->3->4->0 |
| 1019 | + 输出: -1->0->3->4->5 |
| 1020 | +``` |
| 1021 | + |
| 1022 | +<div align="center"><img src="../_assets/Insertion-sort-example-300px.gif" height="" /></div> |
| 1023 | + |
| 1024 | +- 插入排序的动画演示如上。从第一个元素开始,该链表可以被认为已经部分排序(用黑色表示)。 |
| 1025 | +每次迭代时,从输入数据中移除一个元素(用红色表示),并原地将其插入到已排好序的链表中。 |
| 1026 | + |
| 1027 | +**代码 1 - 非原地** |
| 1028 | +- 实际上,对链表来说,不存在是否原地的问题,不像数组 |
| 1029 | +- 这里所谓的非原地是相对数组而言的,因此下面的代码只针对链表,不适用于数组。 |
| 1030 | +```C++ |
| 1031 | +/** |
| 1032 | + * Definition for singly-linked list. |
| 1033 | + * struct ListNode { |
| 1034 | + * int val; |
| 1035 | + * ListNode *next; |
| 1036 | + * ListNode(int x) : val(x), next(NULL) {} |
| 1037 | + * }; |
| 1038 | + */ |
| 1039 | +class Solution { |
| 1040 | +public: |
| 1041 | + ListNode* insertionSortList(ListNode* h) { |
| 1042 | + if (h == nullptr || h->next == nullptr) |
| 1043 | + return h; |
| 1044 | + |
| 1045 | + // 因为是链表,所以可以重新开一个新的链表来保存排序好的部分; |
| 1046 | + // 不存在空间上的问题,这一点不像数组 |
| 1047 | + auto H = new ListNode(0); |
| 1048 | + |
| 1049 | + auto pre = H; |
| 1050 | + auto cur = h; |
| 1051 | + ListNode* nxt; |
| 1052 | + while (cur) { |
| 1053 | + while (pre->next && pre->next->val < cur->val) { |
| 1054 | + pre = pre->next; |
| 1055 | + } |
| 1056 | + |
| 1057 | + nxt = cur->next; // 记录下一个要遍历的节点 |
| 1058 | + // 把 cur 插入 pre 和 pre->next 之间 |
| 1059 | + cur->next = pre->next; |
| 1060 | + pre->next = cur; |
| 1061 | + |
| 1062 | + // 重新下一轮 |
| 1063 | + pre = H; |
| 1064 | + cur = nxt; |
| 1065 | + } |
| 1066 | + |
| 1067 | + h = H->next; |
| 1068 | + delete H; |
| 1069 | + return h; |
| 1070 | + } |
| 1071 | +}; |
| 1072 | +``` |
| 1073 | +
|
| 1074 | +**代码 2 - 原地** |
| 1075 | +- 即不使用新链表,逻辑与数组一致 |
| 1076 | +```C++ |
| 1077 | +class Solution { |
| 1078 | +public: |
| 1079 | + ListNode* insertionSortList(ListNode* h) { |
| 1080 | + if (h == nullptr || h->next == nullptr) |
| 1081 | + return h; |
| 1082 | + |
| 1083 | + auto beg = new ListNode(0); |
| 1084 | + beg->next = h; |
| 1085 | + auto end = h; // (beg, end] 指示排好序的部分 |
| 1086 | + |
| 1087 | + auto p = h->next; // 当前待排序的节点 |
| 1088 | + while (p) { |
| 1089 | + auto pre = beg; |
| 1090 | + auto cur = beg->next; // p 将插入到 pre 和 cur 之间 |
| 1091 | + while (cur != p && p->val >= cur->val) { |
| 1092 | + cur = cur->next; |
| 1093 | + pre = pre->next; |
| 1094 | + } |
| 1095 | + |
| 1096 | + if (cur == p) { |
| 1097 | + end = p; |
| 1098 | + } else { |
| 1099 | + end->next = p->next; |
| 1100 | + p->next = cur; |
| 1101 | + pre->next = p; |
| 1102 | + } |
| 1103 | + p = end->next; |
| 1104 | + } |
| 1105 | + |
| 1106 | + h = beg->next; |
| 1107 | + delete beg; |
| 1108 | + return h; |
| 1109 | + } |
| 1110 | +}; |
| 1111 | +``` |
| 1112 | + |
974 | 1113 |
|
975 | 1114 | # 二维数组
|
976 | 1115 |
|
|
0 commit comments