|
| 1 | + |
| 2 | +# 1. 两数相加 |
| 3 | + |
| 4 | +### 题目描述 |
| 5 | + |
| 6 | +> Leetcode:给定两个非空链表来表示两个非负整数。位数按照逆序方式存储,它们的每个节点只存储单个数字。将两数相加返回一个新的链表。 |
| 7 | +> |
| 8 | +>你可以假设除了数字 0 之外,这两个数字都不会以零开头。 |
| 9 | +
|
| 10 | +示例: |
| 11 | + |
| 12 | +``` |
| 13 | +输入:(2 -> 4 -> 3) + (5 -> 6 -> 4) |
| 14 | +输出:7 -> 0 -> 8 |
| 15 | +原因:342 + 465 = 807 |
| 16 | +``` |
| 17 | + |
| 18 | +### 问题分析 |
| 19 | + |
| 20 | +Leetcode官方详细解答地址: |
| 21 | + |
| 22 | + https://leetcode-cn.com/problems/add-two-numbers/solution/ |
| 23 | + |
| 24 | +> 要对头结点进行操作时,考虑创建哑节点dummy,使用dummy->next表示真正的头节点。这样可以避免处理头节点为空的边界问题。 |
| 25 | +
|
| 26 | +我们使用变量来跟踪进位,并从包含最低有效位的表头开始模拟逐 |
| 27 | +位相加的过程。 |
| 28 | + |
| 29 | + |
| 30 | + |
| 31 | +### Solution |
| 32 | + |
| 33 | +**我们首先从最低有效位也就是列表 l1和 l2 的表头开始相加。注意需要考虑到进位的情况!** |
| 34 | + |
| 35 | +```java |
| 36 | +/** |
| 37 | + * Definition for singly-linked list. |
| 38 | + * public class ListNode { |
| 39 | + * int val; |
| 40 | + * ListNode next; |
| 41 | + * ListNode(int x) { val = x; } |
| 42 | + * } |
| 43 | + */ |
| 44 | + //https://leetcode-cn.com/problems/add-two-numbers/description/ |
| 45 | +class Solution { |
| 46 | +public ListNode addTwoNumbers(ListNode l1, ListNode l2) { |
| 47 | + ListNode dummyHead = new ListNode(0); |
| 48 | + ListNode p = l1, q = l2, curr = dummyHead; |
| 49 | + //carry 表示进位数 |
| 50 | + int carry = 0; |
| 51 | + while (p != null || q != null) { |
| 52 | + int x = (p != null) ? p.val : 0; |
| 53 | + int y = (q != null) ? q.val : 0; |
| 54 | + int sum = carry + x + y; |
| 55 | + //进位数 |
| 56 | + carry = sum / 10; |
| 57 | + //新节点的数值为sum % 10 |
| 58 | + curr.next = new ListNode(sum % 10); |
| 59 | + curr = curr.next; |
| 60 | + if (p != null) p = p.next; |
| 61 | + if (q != null) q = q.next; |
| 62 | + } |
| 63 | + if (carry > 0) { |
| 64 | + curr.next = new ListNode(carry); |
| 65 | + } |
| 66 | + return dummyHead.next; |
| 67 | +} |
| 68 | +} |
| 69 | +``` |
| 70 | + |
| 71 | +# 2. 翻转链表 |
| 72 | + |
| 73 | + |
| 74 | +### 题目描述 |
| 75 | +> 剑指 offer:输入一个链表,反转链表后,输出链表的所有元素。 |
| 76 | +
|
| 77 | + |
| 78 | + |
| 79 | +### 问题分析 |
| 80 | + |
| 81 | +这道算法题,说直白点就是:如何让后一个节点指向前一个节点!在下面的代码中定义了一个 next 节点,该节点主要是保存要反转到头的那个节点,防止链表 “断裂”。 |
| 82 | + |
| 83 | +### Solution |
| 84 | + |
| 85 | + |
| 86 | +```java |
| 87 | +public class ListNode { |
| 88 | + int val; |
| 89 | + ListNode next = null; |
| 90 | + |
| 91 | + ListNode(int val) { |
| 92 | + this.val = val; |
| 93 | + } |
| 94 | +} |
| 95 | +``` |
| 96 | + |
| 97 | +```java |
| 98 | +/** |
| 99 | + * |
| 100 | + * @author Snailclimb |
| 101 | + * @date 2018年9月19日 |
| 102 | + * @Description: TODO |
| 103 | + */ |
| 104 | +public class Solution { |
| 105 | + |
| 106 | + public ListNode ReverseList(ListNode head) { |
| 107 | + |
| 108 | + ListNode next = null; |
| 109 | + ListNode pre = null; |
| 110 | + |
| 111 | + while (head != null) { |
| 112 | + // 保存要反转到头的那个节点 |
| 113 | + next = head.next; |
| 114 | + // 要反转的那个节点指向已经反转的上一个节点(备注:第一次反转的时候会指向null) |
| 115 | + head.next = pre; |
| 116 | + // 上一个已经反转到头部的节点 |
| 117 | + pre = head; |
| 118 | + // 一直向链表尾走 |
| 119 | + head = next; |
| 120 | + } |
| 121 | + return pre; |
| 122 | + } |
| 123 | + |
| 124 | +} |
| 125 | +``` |
| 126 | + |
| 127 | +测试方法: |
| 128 | + |
| 129 | +```java |
| 130 | + public static void main(String[] args) { |
| 131 | + |
| 132 | + ListNode a = new ListNode(1); |
| 133 | + ListNode b = new ListNode(2); |
| 134 | + ListNode c = new ListNode(3); |
| 135 | + ListNode d = new ListNode(4); |
| 136 | + ListNode e = new ListNode(5); |
| 137 | + a.next = b; |
| 138 | + b.next = c; |
| 139 | + c.next = d; |
| 140 | + d.next = e; |
| 141 | + new Solution().ReverseList(a); |
| 142 | + while (e != null) { |
| 143 | + System.out.println(e.val); |
| 144 | + e = e.next; |
| 145 | + } |
| 146 | + } |
| 147 | +``` |
| 148 | + |
| 149 | +输出: |
| 150 | + |
| 151 | +``` |
| 152 | +5 |
| 153 | +4 |
| 154 | +3 |
| 155 | +2 |
| 156 | +1 |
| 157 | +``` |
| 158 | + |
| 159 | +# 3. 链表中倒数第k个节点 |
| 160 | + |
| 161 | +### 题目描述 |
| 162 | + |
| 163 | +> 剑指offer: 输入一个链表,输出该链表中倒数第k个结点。 |
| 164 | +
|
| 165 | +### 问题分析 |
| 166 | + |
| 167 | +首先两个节点/指针,一个节点 node1 先开始跑,指针 node1 跑到 k-1 个节点后,另一个节点 node2 开始跑,当 node1 跑到最后时,node2 所指的节点就是倒数第k个节点。 |
| 168 | + |
| 169 | + |
| 170 | +### Solution |
| 171 | + |
| 172 | +```java |
| 173 | +/* |
| 174 | +public class ListNode { |
| 175 | + int val; |
| 176 | + ListNode next = null; |
| 177 | +
|
| 178 | + ListNode(int val) { |
| 179 | + this.val = val; |
| 180 | + } |
| 181 | +}*/ |
| 182 | + |
| 183 | +//时间复杂度O(n),一次遍历即可 |
| 184 | +//https://www.nowcoder.com/practice/529d3ae5a407492994ad2a246518148a?tpId=13&tqId=11167&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking |
| 185 | +public class Solution { |
| 186 | + public ListNode FindKthToTail(ListNode head,int k) { |
| 187 | + //如果链表为空或者k小于等于0 |
| 188 | + if(head==null||k<=0){ |
| 189 | + return null; |
| 190 | + } |
| 191 | + // 声明两个指向头结点的节点 |
| 192 | + ListNode node1 = head, node2 = head; |
| 193 | + //记录节点的个数 |
| 194 | + int count=0; |
| 195 | + //记录k值,后面要使用 |
| 196 | + int index=k; |
| 197 | + //p指针先跑,并且记录节点数,当node1节点跑了k-1个节点后,node2节点开始跑, |
| 198 | + //当node1节点跑到最后时,node2节点所指的节点就是倒数第k个节点 |
| 199 | + while(node1!=null){ |
| 200 | + node1=node1.next; |
| 201 | + count++; |
| 202 | + if(k<1){ |
| 203 | + node2=node2.next; |
| 204 | + } |
| 205 | + k--; |
| 206 | + } |
| 207 | + //如果节点个数小于所求的倒数第k个节点,则返回空 |
| 208 | + if(count<index) return null; |
| 209 | + return node2; |
| 210 | + |
| 211 | + } |
| 212 | +} |
| 213 | + |
| 214 | +``` |
| 215 | + |
| 216 | + |
| 217 | +# 4. 删除链表的倒数第N个节点 |
| 218 | + |
| 219 | +> Leetcode:给定一个链表,删除链表的倒数第 n 个节点,并且返回链表的头结点。 |
| 220 | +
|
| 221 | +**示例:** |
| 222 | + |
| 223 | +``` |
| 224 | +给定一个链表: 1->2->3->4->5, 和 n = 2. |
| 225 | +
|
| 226 | +当删除了倒数第二个节点后,链表变为 1->2->3->5. |
| 227 | +
|
| 228 | +``` |
| 229 | + |
| 230 | +**说明:** |
| 231 | + |
| 232 | +给定的 n 保证是有效的。 |
| 233 | + |
| 234 | +**进阶:** |
| 235 | + |
| 236 | +你能尝试使用一趟扫描实现吗? |
| 237 | + |
| 238 | +该题在 leetcode 上有详细解答,具体可参考 Leetcode. |
| 239 | + |
| 240 | +### 问题分析 |
| 241 | + |
| 242 | +我们注意到这个问题可以容易地简化成另一个问题:删除从列表开头数起的第 (L - n + 1)个结点,其中 LL 是列表的长度。只要我们找到列表的长度 L,这个问题就很容易解决。 |
| 243 | + |
| 244 | + |
| 245 | + |
| 246 | +### Solution |
| 247 | + |
| 248 | +**两次遍历法** |
| 249 | + |
| 250 | +首先我们将添加一个 **哑结点** 作为辅助,该结点位于列表头部。哑结点用来简化某些极端情况,例如列表中只含有一个结点,或需要删除列表的头部。在第一次遍历中,我们找出列表的长度 L。然后设置一个指向哑结点的指针,并移动它遍历列表,直至它到达第 (L - n) 个结点那里。我们把第 (L - n)个结点的 next 指针重新链接至第 (L - n + 2)个结点,完成这个算法。 |
| 251 | + |
| 252 | +```java |
| 253 | +/** |
| 254 | + * Definition for singly-linked list. |
| 255 | + * public class ListNode { |
| 256 | + * int val; |
| 257 | + * ListNode next; |
| 258 | + * ListNode(int x) { val = x; } |
| 259 | + * } |
| 260 | + */ |
| 261 | +// https://leetcode-cn.com/problems/remove-nth-node-from-end-of-list/description/ |
| 262 | +public class Solution { |
| 263 | + public ListNode removeNthFromEnd(ListNode head, int n) { |
| 264 | + // 哑结点,哑结点用来简化某些极端情况,例如列表中只含有一个结点,或需要删除列表的头部 |
| 265 | + ListNode dummy = new ListNode(0); |
| 266 | + // 哑结点指向头结点 |
| 267 | + dummy.next = head; |
| 268 | + // 保存链表长度 |
| 269 | + int length = 0; |
| 270 | + ListNode len = head; |
| 271 | + while (len != null) { |
| 272 | + length++; |
| 273 | + len = len.next; |
| 274 | + } |
| 275 | + length = length - n; |
| 276 | + ListNode target = dummy; |
| 277 | + // 找到 L-n 位置的节点 |
| 278 | + while (length > 0) { |
| 279 | + target = target.next; |
| 280 | + length--; |
| 281 | + } |
| 282 | + // 把第 (L - n)个结点的 next 指针重新链接至第 (L - n + 2)个结点 |
| 283 | + target.next = target.next.next; |
| 284 | + return dummy.next; |
| 285 | + } |
| 286 | +} |
| 287 | +``` |
| 288 | + |
| 289 | +复杂度分析: |
| 290 | + |
| 291 | +- 时间复杂度 O(L) :该算法对列表进行了两次遍历,首先计算了列表的长度 LL 其次找到第 (L - n)(L−n) 个结点。 操作执行了 2L-n2L−n 步,时间复杂度为 O(L)O(L)。 |
| 292 | + |
| 293 | +- 空间复杂度 O(1) :我们只用了常量级的额外空间。 |
| 294 | + |
| 295 | + |
| 296 | + |
| 297 | +**进阶——一次遍历法:** |
| 298 | + |
| 299 | +其实这种方法就和我们上面第四题找“链表中倒数第k个节点”所用的思想是一样的。**基本思路就是:** 定义两个节点 node1、node2;node1 节点先跑,node1节点 跑到第 n+1 个节点的时候,node2 节点开始跑.当node1 节点跑到最后一个节点时,node2 节点所在的位置就是第 (L-n ) 个节点(L代表总链表长度,也就是倒数第 n+1 个节点) |
| 300 | + |
| 301 | +```java |
| 302 | +/** |
| 303 | + * Definition for singly-linked list. |
| 304 | + * public class ListNode { |
| 305 | + * int val; |
| 306 | + * ListNode next; |
| 307 | + * ListNode(int x) { val = x; } |
| 308 | + * } |
| 309 | + */ |
| 310 | +public class Solution { |
| 311 | + public ListNode removeNthFromEnd(ListNode head, int n) { |
| 312 | + |
| 313 | + ListNode dummy = new ListNode(0); |
| 314 | + dummy.next = head; |
| 315 | + // 声明两个指向头结点的节点 |
| 316 | + ListNode node1 = dummy, node2 = dummy; |
| 317 | + |
| 318 | + // node1 节点先跑,node1节点 跑到第 n+1 个节点的时候,node2 节点开始跑 |
| 319 | + //当node1 节点跑到最后一个节点时,node2 节点所在的位置就是第 (L-n ) 个节点(L代表总链表长度,也就是倒数第 n+1 个节点) |
| 320 | + while (node1 != null) { |
| 321 | + node1 = node1.next; |
| 322 | + if (n < 0) { |
| 323 | + node2 = node2.next; |
| 324 | + } |
| 325 | + n--; |
| 326 | + } |
| 327 | + |
| 328 | + node2.next = node2.next.next; |
| 329 | + |
| 330 | + return dummy.next; |
| 331 | + |
| 332 | + } |
| 333 | +} |
| 334 | +``` |
| 335 | + |
| 336 | + |
| 337 | + |
| 338 | + |
| 339 | + |
| 340 | +# 5. 合并两个排序的链表 |
| 341 | + |
| 342 | +### 题目描述 |
| 343 | + |
| 344 | +> 剑指offer:输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。 |
| 345 | +
|
| 346 | +### 问题分析 |
| 347 | + |
| 348 | +我们可以这样分析: |
| 349 | + |
| 350 | +1. 假设我们有两个链表 A,B; |
| 351 | +2. A的头节点A1的值与B的头结点B1的值比较,假设A1小,则A1为头节点; |
| 352 | +3. A2再和B1比较,假设B1小,则,A1指向B1; |
| 353 | +4. A2再和B2比较 |
| 354 | +就这样循环往复就行了,应该还算好理解。 |
| 355 | + |
| 356 | +考虑通过递归的方式实现! |
| 357 | + |
| 358 | +### Solution |
| 359 | + |
| 360 | +**递归版本:** |
| 361 | + |
| 362 | +```java |
| 363 | +/* |
| 364 | +public class ListNode { |
| 365 | + int val; |
| 366 | + ListNode next = null; |
| 367 | +
|
| 368 | + ListNode(int val) { |
| 369 | + this.val = val; |
| 370 | + } |
| 371 | +}*/ |
| 372 | +//https://www.nowcoder.com/practice/d8b6b4358f774294a89de2a6ac4d9337?tpId=13&tqId=11169&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking |
| 373 | +public class Solution { |
| 374 | +public ListNode Merge(ListNode list1,ListNode list2) { |
| 375 | + if(list1 == null){ |
| 376 | + return list2; |
| 377 | + } |
| 378 | + if(list2 == null){ |
| 379 | + return list1; |
| 380 | + } |
| 381 | + if(list1.val <= list2.val){ |
| 382 | + list1.next = Merge(list1.next, list2); |
| 383 | + return list1; |
| 384 | + }else{ |
| 385 | + list2.next = Merge(list1, list2.next); |
| 386 | + return list2; |
| 387 | + } |
| 388 | + } |
| 389 | +} |
| 390 | +``` |
| 391 | + |
| 392 | + |
0 commit comments