|
1 | 1 | 专题-数据结构
|
2 | 2 | ===
|
3 | 3 |
|
4 |
| -- 数据结构相关基本是**现场面试**中出现频率最高的问题。因为现场面试的时间限制,更难的问题需要大量的思考时间,所以一般只要求需要阐述思路;而数据结构相关的问题,因为有很强的先验知识,通常要求**手写代码**。 |
5 |
| -- 本专题只收录**纯数据结构问题**,不包括数据结构的应用。 |
| 4 | +- 数据结构相关基本是**现场面试**中出现频率最高的问题。 |
| 5 | + - 因为现场面试的时间限制,更难的问题需要大量的思考时间,所以一般只要求需要阐述思路(比如动态规划); |
| 6 | + - 而**数据结构**相关的问题,因为有很强的先验知识,通常要求**手写代码**。 |
| 7 | +- 本专题只收录**基础数据结构相关问题**,不包括高级数据结构及数据结构的设计,比如线段树或者 LRU 缓存,这些问题可以参考[数据结构_Advanced](./专题-A-数据结构_Advanced)。 |
6 | 8 |
|
7 | 9 | Index
|
8 | 10 | ---
|
|
31 | 33 | - [链表快排](#链表快排)
|
32 | 34 | - [链表归并](#链表归并)
|
33 | 35 | - [链表插入排序](#链表插入排序)
|
| 36 | + - [链表选择排序](#链表选择排序) |
| 37 | + - [链表冒泡排序](#链表冒泡排序) |
34 | 38 | - [二维数组](#二维数组)
|
35 | 39 | - [二分查找](#二分查找)
|
36 | 40 | - [搜索二维矩阵 1](#搜索二维矩阵-1)
|
@@ -802,9 +806,10 @@ public:
|
802 | 806 | ```
|
803 | 807 |
|
804 | 808 | **思路**
|
805 |
| -- 与数组快排几乎一致,只是 partition 操作需要从左向右遍历 |
806 |
| -- 因为涉及指针,还是用 C++ 写比较方便 |
807 |
| -- 另外 LeetCode 讨论区反映 Python 可能会超时 |
| 809 | +- 与数组快排几乎一致,只是 partition 操作需要从左向右遍历; |
| 810 | +- 因为涉及指针,还是用 C++ 写比较方便; |
| 811 | +- 另外 LeetCode 讨论区反映 Python 可能会超时; |
| 812 | +- **时间复杂度**:最好 `O(NlogN)`,最坏 `O(N^2)` |
808 | 813 |
|
809 | 814 | **代码 1 - 只交换节点内的值**
|
810 | 815 | - 参考数组快排中的写法,这里选取**第一个元素**作为枢纽
|
@@ -935,6 +940,7 @@ public:
|
935 | 940 | - 归并排序比较适合链表,它可以保证了最好和最坏时间复杂度都是 `O(NlogN)`,而且它在数组排序中广受诟病的空间复杂度在链表排序中也从O(n)降到了 `O(1)`
|
936 | 941 | - 因为链表快排中只能使用第一个节点作为枢纽,所以不能保证时间复杂度
|
937 | 942 | - 还是使用 C++
|
| 943 | +- **时间复杂度**:最好/最坏 `O(NlogN)` |
938 | 944 |
|
939 | 945 | **C++**
|
940 | 946 | ```C++
|
@@ -1025,18 +1031,14 @@ public:
|
1025 | 1031 | - 插入排序的动画演示如上。从第一个元素开始,该链表可以被认为已经部分排序(用黑色表示)。
|
1026 | 1032 | 每次迭代时,从输入数据中移除一个元素(用红色表示),并原地将其插入到已排好序的链表中。
|
1027 | 1033 |
|
| 1034 | +**思路** |
| 1035 | +- 见代码注释 |
| 1036 | +- **时间复杂度**:最好/最坏 `O(N^2)` |
| 1037 | + |
1028 | 1038 | **代码 1 - 非原地**
|
1029 | 1039 | - 实际上,对链表来说,不存在是否原地的问题,不像数组
|
1030 | 1040 | - 这里所谓的非原地是相对数组而言的,因此下面的代码只针对链表,不适用于数组。
|
1031 | 1041 | ```C++
|
1032 |
| -/** |
1033 |
| - * Definition for singly-linked list. |
1034 |
| - * struct ListNode { |
1035 |
| - * int val; |
1036 |
| - * ListNode *next; |
1037 |
| - * ListNode(int x) : val(x), next(NULL) {} |
1038 |
| - * }; |
1039 |
| - */ |
1040 | 1042 | class Solution {
|
1041 | 1043 | public:
|
1042 | 1044 | ListNode* insertionSortList(ListNode* h) {
|
@@ -1112,6 +1114,87 @@ public:
|
1112 | 1114 | };
|
1113 | 1115 | ```
|
1114 | 1116 |
|
| 1117 | +### 链表选择排序 |
| 1118 | +> LeetCode/[147. 对链表进行插入排序](https://leetcode-cn.com/problems/insertion-sort-list/description/) |
| 1119 | +>> 注意:以下代码在[148. 排序链表](https://leetcode-cn.com/problems/sort-list/description/)也能 AC(要求时间复杂度 `O(NlogN)`) |
| 1120 | + |
| 1121 | +**思路** |
| 1122 | +- 见代码注释 |
| 1123 | +- **时间复杂度**:最好/最坏 `O(N^2)` |
| 1124 | + |
| 1125 | +**C++** |
| 1126 | +```C++ |
| 1127 | +class Solution { |
| 1128 | +public: |
| 1129 | + ListNode* sortList(ListNode* h) { |
| 1130 | + if (h == nullptr || h->next == nullptr) |
| 1131 | + return h; |
| 1132 | + |
| 1133 | + auto H = new ListNode(0); // 为了操作方便,添加一个头结点 |
| 1134 | + H->next = h; |
| 1135 | + |
| 1136 | + auto s = h; // 指向已经排好序的尾部 |
| 1137 | + ListNode* m; // 指向未排序部分的最小节点 min |
| 1138 | + ListNode* p; // 迭代器 |
| 1139 | + while (s->next) { |
| 1140 | + m = s; |
| 1141 | + p = s->next; |
| 1142 | + while (p) { // 寻找剩余部分的最小节点 |
| 1143 | + if (p->val < m->val) |
| 1144 | + m = p; |
| 1145 | + p = p->next; |
| 1146 | + } |
| 1147 | + |
| 1148 | + swap(s->val, m->val); // 交换节点内的值 |
| 1149 | + s = s->next; |
| 1150 | + } |
| 1151 | + |
| 1152 | + h = H->next; |
| 1153 | + delete H; |
| 1154 | + return h; |
| 1155 | + } |
| 1156 | +}; |
| 1157 | +``` |
| 1158 | +
|
| 1159 | +### 链表冒泡排序 |
| 1160 | +> LeetCode/[147. 对链表进行插入排序](https://leetcode-cn.com/problems/insertion-sort-list/description/) |
| 1161 | +
|
| 1162 | +**思路** |
| 1163 | +- 见代码注释 |
| 1164 | +- **时间复杂度**:最好 `O(N)`,最坏 `O(N^2)` |
| 1165 | +
|
| 1166 | +**C++** |
| 1167 | +- 以下代码不能 AC [148. 排序链表](https://leetcode-cn.com/problems/sort-list/description/) |
| 1168 | +```C++ |
| 1169 | +class Solution { |
| 1170 | +public: |
| 1171 | + ListNode* insertionSortList(ListNode* h) { |
| 1172 | + if (h == nullptr || h->next == nullptr) |
| 1173 | + return h; |
| 1174 | + |
| 1175 | + ListNode* q = nullptr; // 开始时指向尾节点 |
| 1176 | + ListNode* p; // 迭代器 |
| 1177 | + bool changed = true; |
| 1178 | + while (q != h->next && changed) { |
| 1179 | + changed = false; |
| 1180 | + p = h; |
| 1181 | + |
| 1182 | + // 把大的元素“冒泡”到尾部去 |
| 1183 | + while (p->next && p->next != p) { |
| 1184 | + if (p->val > p->next->val) { // 如果已经有序,则退出循环 |
| 1185 | + swap(p->val, p->next->val); |
| 1186 | + changed = true; |
| 1187 | + } |
| 1188 | + p = p->next; |
| 1189 | + } |
| 1190 | + q = p; |
| 1191 | + } |
| 1192 | + |
| 1193 | + return h; |
| 1194 | + } |
| 1195 | +}; |
| 1196 | +``` |
| 1197 | + |
1115 | 1198 |
|
1116 | 1199 | # 二维数组
|
1117 | 1200 |
|
|
0 commit comments