几道常见的链表算法题
<section id="nice" style="font-size: 16px; padding-right: 10px; padding-left: 10px; word-break: break-word; overflow-wrap: break-word; line-height: 1.25; font-family: Optima-Regular, Optima, PingFangTC-Light, PingFangSC-light, PingFangTC-light; letter-spacing: 2px; background-image: linear-gradient(90deg, rgba(50, 0, 0, 0.05) 3%, rgba(0, 0, 0, 0) 3%), linear-gradient(360deg, rgba(50, 0, 0, 0.05) 3%, rgba(0, 0, 0, 0) 3%); background-size: 20px 20px; background-position: center center;"><h2 style="font-weight: bold; font-size: 22px; margin-top: 20px; margin-right: 10px; margin-bottom: 0px;"><span style="font-size: 18px; display: inline-block; padding-left: 10px; border-left: 5px solid rgb(145, 109, 213);">几道常见的链表算法题</span></h2>
<h2 style="font-weight: bold; font-size: 22px; margin-top: 20px; margin-right: 10px; margin-bottom: 0px;"><span style="font-size: 18px; display: inline-block; padding-left: 10px; border-left: 5px solid rgb(145, 109, 213);">1. 两数相加</span></h2>
<h3 style="margin-top: 30px; margin-bottom: 15px; font-size: 16px; font-weight: bold; text-align: center;"><span style="border-bottom: 2px solid #d89cf6;">题目描述</span></h3>
<blockquote style="border-top: none; border-right: none; border-bottom: none; border-image: initial; font-size: 0.9em; overflow: auto; border-left-width: 3px; color: rgb(106, 115, 125); padding-top: 10px; padding-bottom: 10px; padding-left: 20px; margin-bottom: 20px; margin-top: 20px; border-left-color: rgb(216, 156, 246); background: rgb(244, 238, 255);">
<p style="padding-top: 8px; padding-bottom: 8px; font-size: 14px; word-spacing: 2px; margin-top: 0px; margin-bottom: 0px; color: black; line-height: 26px;">Leetcode:给定两个非空链表来表示两个非负整数。位数按照逆序方式存储,它们的每个节点只存储单个数字。将两数相加返回一个新的链表。</p>
<p style="padding-top: 8px; padding-bottom: 8px; font-size: 14px; word-spacing: 2px; margin-top: 0px; margin-bottom: 0px; color: black; line-height: 26px;">你可以假设除了数字 0 之外,这两个数字都不会以零开头。</p>
</blockquote>
<p style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; font-size: 14px; word-spacing: 2px;">示例:</p>
<pre><code style="overflow-x: auto; padding: 16px; color: rgb(171, 178, 191); background: rgb(40, 44, 52); display: -webkit-box; font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace; border-radius: 0px;">输入:(2 -> 4 -> 3) + (5 -> 6 -> 4)<br>输出:7 -> 0 -> 8<br>原因:342 + 465 = 807<br></code></pre>
<h3 style="margin-top: 30px; margin-bottom: 15px; font-size: 16px; font-weight: bold; text-align: center;"><span style="border-bottom: 2px solid #d89cf6;">问题分析</span></h3>
<p style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; font-size: 14px; word-spacing: 2px;">Leetcode 官方详细解答地址:</p>
<p style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; font-size: 14px; word-spacing: 2px;">https://leetcode-cn.com/problems/add-two-numbers/solution/</p>
<blockquote style="border-top: none; border-right: none; border-bottom: none; border-image: initial; font-size: 0.9em; overflow: auto; border-left-width: 3px; color: rgb(106, 115, 125); padding-top: 10px; padding-bottom: 10px; padding-left: 20px; margin-bottom: 20px; margin-top: 20px; border-left-color: rgb(216, 156, 246); background: rgb(244, 238, 255);">
<p style="padding-top: 8px; padding-bottom: 8px; font-size: 14px; word-spacing: 2px; margin-top: 0px; margin-bottom: 0px; color: black; line-height: 26px;">要对头结点进行操作时,考虑创建哑节点 dummy,使用 dummy->next 表示真正的头节点。这样可以避免处理头节点为空的边界问题。</p>
</blockquote>
<p style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; font-size: 14px; word-spacing: 2px;">我们使用变量来跟踪进位,并从包含最低有效位的表头开始模拟逐
位相加的过程。</p>
<h3 style="margin-top: 30px; margin-bottom: 15px; font-size: 16px; font-weight: bold; text-align: center;"><span style="border-bottom: 2px solid #d89cf6;">Solution</span></h3>
<p style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; font-size: 14px; word-spacing: 2px;"><strong style="color: rgb(145, 109, 213);">「我们首先从最低有效位也就是列表 l1 和 l2 的表头开始相加。注意需要考虑到进位的情况!」</strong></p>
<pre><code style="overflow-x: auto; padding: 16px; color: rgb(171, 178, 191); background: rgb(40, 44, 52); display: -webkit-box; font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace; border-radius: 0px;"><span style="color: #5c6370; font-style: italic; line-height: 26px;">/**<br> * Definition for singly-linked list.<br> * public class ListNode {<br> * int val;<br> * ListNode next;<br> * ListNode(int x) { val = x; }<br> * }<br> */</span><br> <span style="color: #5c6370; font-style: italic; line-height: 26px;">//https://leetcode-cn.com/problems/add-two-numbers/description/</span><br><span style="line-height: 26px;"><span style="color: #c678dd; line-height: 26px;">class</span> <span style="color: #e6c07b; line-height: 26px;">Solution</span> </span>{<br><span style="line-height: 26px;"><span style="color: #c678dd; line-height: 26px;">public</span> ListNode <span style="color: #61aeee; line-height: 26px;">addTwoNumbers</span><span style="line-height: 26px;">(ListNode l1, ListNode l2)</span> </span>{<br> ListNode dummyHead = <span style="color: #c678dd; line-height: 26px;">new</span> ListNode(<span style="color: #d19a66; line-height: 26px;">0</span>);<br> ListNode p = l1, q = l2, curr = dummyHead;<br> <span style="color: #5c6370; font-style: italic; line-height: 26px;">//carry 表示进位数</span><br> <span style="color: #c678dd; line-height: 26px;">int</span> carry = <span style="color: #d19a66; line-height: 26px;">0</span>;<br> <span style="color: #c678dd; line-height: 26px;">while</span> (p != <span style="color: #c678dd; line-height: 26px;">null</span> || q != <span style="color: #c678dd; line-height: 26px;">null</span>) {<br> <span style="color: #c678dd; line-height: 26px;">int</span> x = (p != <span style="color: #c678dd; line-height: 26px;">null</span>) ? p.val : <span style="color: #d19a66; line-height: 26px;">0</span>;<br> <span style="color: #c678dd; line-height: 26px;">int</span> y = (q != <span style="color: #c678dd; line-height: 26px;">null</span>) ? q.val : <span style="color: #d19a66; line-height: 26px;">0</span>;<br> <span style="color: #c678dd; line-height: 26px;">int</span> sum = carry + x + y;<br> <span style="color: #5c6370; font-style: italic; line-height: 26px;">//进位数</span><br> carry = sum / <span style="color: #d19a66; line-height: 26px;">10</span>;<br> <span style="color: #5c6370; font-style: italic; line-height: 26px;">//新节点的数值为sum % 10</span><br> curr.next = <span style="color: #c678dd; line-height: 26px;">new</span> ListNode(sum % <span style="color: #d19a66; line-height: 26px;">10</span>);<br> curr = curr.next;<br> <span style="color: #c678dd; line-height: 26px;">if</span> (p != <span style="color: #c678dd; line-height: 26px;">null</span>) p = p.next;<br> <span style="color: #c678dd; line-height: 26px;">if</span> (q != <span style="color: #c678dd; line-height: 26px;">null</span>) q = q.next;<br> }<br> <span style="color: #c678dd; line-height: 26px;">if</span> (carry > <span style="color: #d19a66; line-height: 26px;">0</span>) {<br> curr.next = <span style="color: #c678dd; line-height: 26px;">new</span> ListNode(carry);<br> }<br> <span style="color: #c678dd; line-height: 26px;">return</span> dummyHead.next;<br>}<br>}<br></code></pre>
<h2 style="font-weight: bold; font-size: 22px; margin-top: 20px; margin-right: 10px; margin-bottom: 0px;"><span style="font-size: 18px; display: inline-block; padding-left: 10px; border-left: 5px solid rgb(145, 109, 213);">2. 翻转链表</span></h2>
<h3 style="margin-top: 30px; margin-bottom: 15px; font-size: 16px; font-weight: bold; text-align: center;"><span style="border-bottom: 2px solid #d89cf6;">题目描述</span></h3>
<blockquote style="border-top: none; border-right: none; border-bottom: none; border-image: initial; font-size: 0.9em; overflow: auto; border-left-width: 3px; color: rgb(106, 115, 125); padding-top: 10px; padding-bottom: 10px; padding-left: 20px; margin-bottom: 20px; margin-top: 20px; border-left-color: rgb(216, 156, 246); background: rgb(244, 238, 255);">
<p style="padding-top: 8px; padding-bottom: 8px; font-size: 14px; word-spacing: 2px; margin-top: 0px; margin-bottom: 0px; color: black; line-height: 26px;">剑指 offer:输入一个链表,反转链表后,输出链表的所有元素。</p>
</blockquote>
<h3 style="margin-top: 30px; margin-bottom: 15px; font-size: 16px; font-weight: bold; text-align: center;"><span style="border-bottom: 2px solid #d89cf6;">问题分析</span></h3>
<p style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; font-size: 14px; word-spacing: 2px;">这道算法题,说直白点就是:如何让后一个节点指向前一个节点!在下面的代码中定义了一个 next 节点,该节点主要是保存要反转到头的那个节点,防止链表 “断裂”。</p>
<h3 style="margin-top: 30px; margin-bottom: 15px; font-size: 16px; font-weight: bold; text-align: center;"><span style="border-bottom: 2px solid #d89cf6;">Solution</span></h3>
<pre><code style="overflow-x: auto; padding: 16px; color: rgb(171, 178, 191); background: rgb(40, 44, 52); display: -webkit-box; font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace; border-radius: 0px;"><span style="color: #c678dd; line-height: 26px;">public</span> <span style="line-height: 26px;"><span style="color: #c678dd; line-height: 26px;">class</span> <span style="color: #e6c07b; line-height: 26px;">ListNode</span> </span>{<br> <span style="color: #c678dd; line-height: 26px;">int</span> val;<br> ListNode next = <span style="color: #c678dd; line-height: 26px;">null</span>;<br><br> ListNode(<span style="color: #c678dd; line-height: 26px;">int</span> val) {<br> <span style="color: #c678dd; line-height: 26px;">this</span>.val = val;<br> }<br>}<br></code></pre>
<pre><code style="overflow-x: auto; padding: 16px; color: rgb(171, 178, 191); background: rgb(40, 44, 52); display: -webkit-box; font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace; border-radius: 0px;"><span style="color: #5c6370; font-style: italic; line-height: 26px;">/**<br> *<br> * <span style="color: #c678dd; line-height: 26px;">@author</span> Snailclimb<br> * <span style="color: #c678dd; line-height: 26px;">@date</span> 2018年9月19日<br> * <span style="color: #c678dd; line-height: 26px;">@Description</span>: TODO<br> */</span><br><span style="color: #c678dd; line-height: 26px;">public</span> <span style="line-height: 26px;"><span style="color: #c678dd; line-height: 26px;">class</span> <span style="color: #e6c07b; line-height: 26px;">Solution</span> </span>{<br><br> <span style="line-height: 26px;"><span style="color: #c678dd; line-height: 26px;">public</span> ListNode <span style="color: #61aeee; line-height: 26px;">ReverseList</span><span style="line-height: 26px;">(ListNode head)</span> </span>{<br><br> ListNode next = <span style="color: #c678dd; line-height: 26px;">null</span>;<br> ListNode pre = <span style="color: #c678dd; line-height: 26px;">null</span>;<br><br> <span style="color: #c678dd; line-height: 26px;">while</span> (head != <span style="color: #c678dd; line-height: 26px;">null</span>) {<br> <span style="color: #5c6370; font-style: italic; line-height: 26px;">// 保存要反转到头的那个节点</span><br> next = head.next;<br> <span style="color: #5c6370; font-style: italic; line-height: 26px;">// 要反转的那个节点指向已经反转的上一个节点(备注:第一次反转的时候会指向null)</span><br> head.next = pre;<br> <span style="color: #5c6370; font-style: italic; line-height: 26px;">// 上一个已经反转到头部的节点</span><br> pre = head;<br> <span style="color: #5c6370; font-style: italic; line-height: 26px;">// 一直向链表尾走</span><br> head = next;<br> }<br> <span style="color: #c678dd; line-height: 26px;">return</span> pre;<br> }<br><br>}<br></code></pre>
<p style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; font-size: 14px; word-spacing: 2px;">测试方法:</p>
<pre><code style="overflow-x: auto; padding: 16px; color: rgb(171, 178, 191); background: rgb(40, 44, 52); display: -webkit-box; font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace; border-radius: 0px;"> <span style="line-height: 26px;"><span style="color: #c678dd; line-height: 26px;">public</span> <span style="color: #c678dd; line-height: 26px;">static</span> <span style="color: #c678dd; line-height: 26px;">void</span> <span style="color: #61aeee; line-height: 26px;">main</span><span style="line-height: 26px;">(String[] args)</span> </span>{<br><br> ListNode a = <span style="color: #c678dd; line-height: 26px;">new</span> ListNode(<span style="color: #d19a66; line-height: 26px;">1</span>);<br> ListNode b = <span style="color: #c678dd; line-height: 26px;">new</span> ListNode(<span style="color: #d19a66; line-height: 26px;">2</span>);<br> ListNode c = <span style="color: #c678dd; line-height: 26px;">new</span> ListNode(<span style="color: #d19a66; line-height: 26px;">3</span>);<br> ListNode d = <span style="color: #c678dd; line-height: 26px;">new</span> ListNode(<span style="color: #d19a66; line-height: 26px;">4</span>);<br> ListNode e = <span style="color: #c678dd; line-height: 26px;">new</span> ListNode(<span style="color: #d19a66; line-height: 26px;">5</span>);<br> a.next = b;<br> b.next = c;<br> c.next = d;<br> d.next = e;<br> <span style="color: #c678dd; line-height: 26px;">new</span> Solution().ReverseList(a);<br> <span style="color: #c678dd; line-height: 26px;">while</span> (e != <span style="color: #c678dd; line-height: 26px;">null</span>) {<br> System.out.println(e.val);<br> e = e.next;<br> }<br> }<br></code></pre>
<p style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; font-size: 14px; word-spacing: 2px;">输出:</p>
<pre><code style="overflow-x: auto; padding: 16px; color: rgb(171, 178, 191); background: rgb(40, 44, 52); display: -webkit-box; font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace; border-radius: 0px;">5<br>4<br>3<br>2<br>1<br></code></pre>
<h2 style="font-weight: bold; font-size: 22px; margin-top: 20px; margin-right: 10px; margin-bottom: 0px;"><span style="font-size: 18px; display: inline-block; padding-left: 10px; border-left: 5px solid rgb(145, 109, 213);">3. 链表中倒数第 k 个节点</span></h2>
<h3 style="margin-top: 30px; margin-bottom: 15px; font-size: 16px; font-weight: bold; text-align: center;"><span style="border-bottom: 2px solid #d89cf6;">题目描述</span></h3>
<blockquote style="border-top: none; border-right: none; border-bottom: none; border-image: initial; font-size: 0.9em; overflow: auto; border-left-width: 3px; color: rgb(106, 115, 125); padding-top: 10px; padding-bottom: 10px; padding-left: 20px; margin-bottom: 20px; margin-top: 20px; border-left-color: rgb(216, 156, 246); background: rgb(244, 238, 255);">
<p style="padding-top: 8px; padding-bottom: 8px; font-size: 14px; word-spacing: 2px; margin-top: 0px; margin-bottom: 0px; color: black; line-height: 26px;">剑指 offer: 输入一个链表,输出该链表中倒数第 k 个结点。</p>
</blockquote>
<h3 style="margin-top: 30px; margin-bottom: 15px; font-size: 16px; font-weight: bold; text-align: center;"><span style="border-bottom: 2px solid #d89cf6;">问题分析</span></h3>
<blockquote style="border-top: none; border-right: none; border-bottom: none; border-image: initial; font-size: 0.9em; overflow: auto; border-left-width: 3px; color: rgb(106, 115, 125); padding-top: 10px; padding-bottom: 10px; padding-left: 20px; margin-bottom: 20px; margin-top: 20px; border-left-color: rgb(216, 156, 246); background: rgb(244, 238, 255);">
<p style="padding-top: 8px; padding-bottom: 8px; font-size: 14px; word-spacing: 2px; margin-top: 0px; margin-bottom: 0px; color: black; line-height: 26px;"><strong style="color: rgb(145, 109, 213);">「链表中倒数第 k 个节点也就是正数第(L-K+1)个节点,知道了只一点,这一题基本就没问题!」</strong></p>
</blockquote>
<p style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; font-size: 14px; word-spacing: 2px;">首先两个节点/指针,一个节点 node1 先开始跑,指针 node1 跑到 k-1 个节点后,另一个节点 node2 开始跑,当 node1 跑到最后时,node2 所指的节点就是倒数第 k 个节点也就是正数第(L-K+1)个节点。</p>
<h3 style="margin-top: 30px; margin-bottom: 15px; font-size: 16px; font-weight: bold; text-align: center;"><span style="border-bottom: 2px solid #d89cf6;">Solution</span></h3>
<pre><code style="overflow-x: auto; padding: 16px; color: rgb(171, 178, 191); background: rgb(40, 44, 52); display: -webkit-box; font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace; border-radius: 0px;"><span style="color: #5c6370; font-style: italic; line-height: 26px;">/*<br>public class ListNode {<br> int val;<br> ListNode next = null;<br><br> ListNode(int val) {<br> this.val = val;<br> }<br>}*/</span><br><br><span style="color: #5c6370; font-style: italic; line-height: 26px;">// 时间复杂度O(n),一次遍历即可</span><br><span style="color: #5c6370; font-style: italic; line-height: 26px;">// https://www.nowcoder.com/practice/529d3ae5a407492994ad2a246518148a?tpId=13&tqId=11167&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking</span><br><span style="color: #c678dd; line-height: 26px;">public</span> <span style="line-height: 26px;"><span style="color: #c678dd; line-height: 26px;">class</span> <span style="color: #e6c07b; line-height: 26px;">Solution</span> </span>{<br> <span style="line-height: 26px;"><span style="color: #c678dd; line-height: 26px;">public</span> ListNode <span style="color: #61aeee; line-height: 26px;">FindKthToTail</span><span style="line-height: 26px;">(ListNode head, <span style="color: #c678dd; line-height: 26px;">int</span> k)</span> </span>{<br> <span style="color: #5c6370; font-style: italic; line-height: 26px;">// 如果链表为空或者k小于等于0</span><br> <span style="color: #c678dd; line-height: 26px;">if</span> (head == <span style="color: #c678dd; line-height: 26px;">null</span> || k <= <span style="color: #d19a66; line-height: 26px;">0</span>) {<br> <span style="color: #c678dd; line-height: 26px;">return</span> <span style="color: #c678dd; line-height: 26px;">null</span>;<br> }<br> <span style="color: #5c6370; font-style: italic; line-height: 26px;">// 声明两个指向头结点的节点</span><br> ListNode node1 = head, node2 = head;<br> <span style="color: #5c6370; font-style: italic; line-height: 26px;">// 记录节点的个数</span><br> <span style="color: #c678dd; line-height: 26px;">int</span> count = <span style="color: #d19a66; line-height: 26px;">0</span>;<br> <span style="color: #5c6370; font-style: italic; line-height: 26px;">// 记录k值,后面要使用</span><br> <span style="color: #c678dd; line-height: 26px;">int</span> index = k;<br> <span style="color: #5c6370; font-style: italic; line-height: 26px;">// p指针先跑,并且记录节点数,当node1节点跑了k-1个节点后,node2节点开始跑,</span><br> <span style="color: #5c6370; font-style: italic; line-height: 26px;">// 当node1节点跑到最后时,node2节点所指的节点就是倒数第k个节点</span><br> <span style="color: #c678dd; line-height: 26px;">while</span> (node1 != <span style="color: #c678dd; line-height: 26px;">null</span>) {<br> node1 = node1.next;<br> count++;<br> <span style="color: #c678dd; line-height: 26px;">if</span> (k < <span style="color: #d19a66; line-height: 26px;">1</span>) {<br> node2 = node2.next;<br> }<br> k--;<br> }<br> <span style="color: #5c6370; font-style: italic; line-height: 26px;">// 如果节点个数小于所求的倒数第k个节点,则返回空</span><br> <span style="color: #c678dd; line-height: 26px;">if</span> (count < index)<br> <span style="color: #c678dd; line-height: 26px;">return</span> <span style="color: #c678dd; line-height: 26px;">null</span>;<br> <span style="color: #c678dd; line-height: 26px;">return</span> node2;<br><br> }<br>}<br></code></pre>
<h2 style="font-weight: bold; font-size: 22px; margin-top: 20px; margin-right: 10px; margin-bottom: 0px;"><span style="font-size: 18px; display: inline-block; padding-left: 10px; border-left: 5px solid rgb(145, 109, 213);">4. 删除链表的倒数第 N 个节点</span></h2>
<blockquote style="border-top: none; border-right: none; border-bottom: none; border-image: initial; font-size: 0.9em; overflow: auto; border-left-width: 3px; color: rgb(106, 115, 125); padding-top: 10px; padding-bottom: 10px; padding-left: 20px; margin-bottom: 20px; margin-top: 20px; border-left-color: rgb(216, 156, 246); background: rgb(244, 238, 255);">
<p style="padding-top: 8px; padding-bottom: 8px; font-size: 14px; word-spacing: 2px; margin-top: 0px; margin-bottom: 0px; color: black; line-height: 26px;">Leetcode:给定一个链表,删除链表的倒数第 n 个节点,并且返回链表的头结点。</p>
</blockquote>
<p style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; font-size: 14px; word-spacing: 2px;"><strong style="color: rgb(145, 109, 213);">「示例:」</strong></p>
<pre><code style="overflow-x: auto; padding: 16px; color: rgb(171, 178, 191); background: rgb(40, 44, 52); display: -webkit-box; font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace; border-radius: 0px;">给定一个链表: 1->2->3->4->5, 和 n = 2.<br><br>当删除了倒数第二个节点后,链表变为 1->2->3->5.<br><br></code></pre>
<p style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; font-size: 14px; word-spacing: 2px;"><strong style="color: rgb(145, 109, 213);">「说明:」</strong></p>
<p style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; font-size: 14px; word-spacing: 2px;">给定的 n 保证是有效的。</p>
<p style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; font-size: 14px; word-spacing: 2px;"><strong style="color: rgb(145, 109, 213);">「进阶:」</strong></p>
<p style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; font-size: 14px; word-spacing: 2px;">你能尝试使用一趟扫描实现吗?</p>
<p style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; font-size: 14px; word-spacing: 2px;">该题在 leetcode 上有详细解答,具体可参考 Leetcode.</p>
<h3 style="margin-top: 30px; margin-bottom: 15px; font-size: 16px; font-weight: bold; text-align: center;"><span style="border-bottom: 2px solid #d89cf6;">问题分析</span></h3>
<p style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; font-size: 14px; word-spacing: 2px;">我们注意到这个问题可以容易地简化成另一个问题:删除从列表开头数起的第 (L - n + 1)个结点,其中 L 是列表的长度。只要我们找到列表的长度 L,这个问题就很容易解决。</p>
<h3 style="margin-top: 30px; margin-bottom: 15px; font-size: 16px; font-weight: bold; text-align: center;"><span style="border-bottom: 2px solid #d89cf6;">Solution</span></h3>
<p style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; font-size: 14px; word-spacing: 2px;"><strong style="color: rgb(145, 109, 213);">「两次遍历法」</strong></p>
<p style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; font-size: 14px; word-spacing: 2px;">首先我们将添加一个 <strong style="color: rgb(145, 109, 213);">「哑结点」</strong> 作为辅助,该结点位于列表头部。哑结点用来简化某些极端情况,例如列表中只含有一个结点,或需要删除列表的头部。在第一次遍历中,我们找出列表的长度 L。然后设置一个指向哑结点的指针,并移动它遍历列表,直至它到达第 (L - n) 个结点那里。<strong style="color: rgb(145, 109, 213);">「我们把第 (L - n)个结点的 next 指针重新链接至第 (L - n + 2)个结点,完成这个算法。」</strong></p>
<pre><code style="overflow-x: auto; padding: 16px; color: rgb(171, 178, 191); background: rgb(40, 44, 52); display: -webkit-box; font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace; border-radius: 0px;"><span style="color: #5c6370; font-style: italic; line-height: 26px;">/**<br> * Definition for singly-linked list.<br> * public class ListNode {<br> * int val;<br> * ListNode next;<br> * ListNode(int x) { val = x; }<br> * }<br> */</span><br><span style="color: #5c6370; font-style: italic; line-height: 26px;">// https://leetcode-cn.com/problems/remove-nth-node-from-end-of-list/description/</span><br><span style="color: #c678dd; line-height: 26px;">public</span> <span style="line-height: 26px;"><span style="color: #c678dd; line-height: 26px;">class</span> <span style="color: #e6c07b; line-height: 26px;">Solution</span> </span>{<br> <span style="line-height: 26px;"><span style="color: #c678dd; line-height: 26px;">public</span> ListNode <span style="color: #61aeee; line-height: 26px;">removeNthFromEnd</span><span style="line-height: 26px;">(ListNode head, <span style="color: #c678dd; line-height: 26px;">int</span> n)</span> </span>{<br> <span style="color: #5c6370; font-style: italic; line-height: 26px;">// 哑结点,哑结点用来简化某些极端情况,例如列表中只含有一个结点,或需要删除列表的头部</span><br> ListNode dummy = <span style="color: #c678dd; line-height: 26px;">new</span> ListNode(<span style="color: #d19a66; line-height: 26px;">0</span>);<br> <span style="color: #5c6370; font-style: italic; line-height: 26px;">// 哑结点指向头结点</span><br> dummy.next = head;<br> <span style="color: #5c6370; font-style: italic; line-height: 26px;">// 保存链表长度</span><br> <span style="color: #c678dd; line-height: 26px;">int</span> length = <span style="color: #d19a66; line-height: 26px;">0</span>;<br> ListNode len = head;<br> <span style="color: #c678dd; line-height: 26px;">while</span> (len != <span style="color: #c678dd; line-height: 26px;">null</span>) {<br> length++;<br> len = len.next;<br> }<br> length = length - n;<br> ListNode target = dummy;<br> <span style="color: #5c6370; font-style: italic; line-height: 26px;">// 找到 L-n 位置的节点</span><br> <span style="color: #c678dd; line-height: 26px;">while</span> (length > <span style="color: #d19a66; line-height: 26px;">0</span>) {<br> target = target.next;<br> length--;<br> }<br> <span style="color: #5c6370; font-style: italic; line-height: 26px;">// 把第 (L - n)个结点的 next 指针重新链接至第 (L - n + 2)个结点</span><br> target.next = target.next.next;<br> <span style="color: #c678dd; line-height: 26px;">return</span> dummy.next;<br> }<br>}<br></code></pre>
<p style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; font-size: 14px; word-spacing: 2px;"><strong style="color: rgb(145, 109, 213);">「复杂度分析:」</strong></p>
<ul style="margin-top: 8px; margin-bottom: 8px; padding-left: 25px; font-size: 15px; list-style-type: circle;">
<li><section style="margin-top: 5px; margin-bottom: 5px; line-height: 26px; color: rgb(1, 1, 1); font-size: 14px;"><strong style="color: rgb(145, 109, 213);">「时间复杂度 O(L)」</strong> :该算法对列表进行了两次遍历,首先计算了列表的长度 LL 其次找到第 (L - n)(L−n) 个结点。 操作执行了 2L-n2L−n 步,时间复杂度为 O(L)O(L)。</section></li><li><section style="margin-top: 5px; margin-bottom: 5px; line-height: 26px; color: rgb(1, 1, 1); font-size: 14px;"><strong style="color: rgb(145, 109, 213);">「空间复杂度 O(1)」</strong> :我们只用了常量级的额外空间。</section></li></ul>
<p style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; font-size: 14px; word-spacing: 2px;"><strong style="color: rgb(145, 109, 213);">「进阶——一次遍历法:」</strong></p>
<blockquote style="border-top: none; border-right: none; border-bottom: none; border-image: initial; font-size: 0.9em; overflow: auto; border-left-width: 3px; color: rgb(106, 115, 125); padding-top: 10px; padding-bottom: 10px; padding-left: 20px; margin-bottom: 20px; margin-top: 20px; border-left-color: rgb(216, 156, 246); background: rgb(244, 238, 255);">
<p style="padding-top: 8px; padding-bottom: 8px; font-size: 14px; word-spacing: 2px; margin-top: 0px; margin-bottom: 0px; color: black; line-height: 26px;">链表中倒数第 N 个节点也就是正数第(L-N+1)个节点。</p>
</blockquote>
<p style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; font-size: 14px; word-spacing: 2px;">其实这种方法就和我们上面第四题找“链表中倒数第 k 个节点”所用的思想是一样的。<strong style="color: rgb(145, 109, 213);">「基本思路就是:」</strong> 定义两个节点 node1、node2;node1 节点先跑,node1 节点 跑到第 n+1 个节点的时候,node2 节点开始跑.当 node1 节点跑到最后一个节点时,node2 节点所在的位置就是第 (L-n ) 个节点(L 代表总链表长度,也就是倒数第 n+1 个节点)</p>
<pre><code style="overflow-x: auto; padding: 16px; color: rgb(171, 178, 191); background: rgb(40, 44, 52); display: -webkit-box; font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace; border-radius: 0px;"><span style="color: #5c6370; font-style: italic; line-height: 26px;">/**<br> * Definition for singly-linked list.<br> * public class ListNode {<br> * int val;<br> * ListNode next;<br> * ListNode(int x) { val = x; }<br> * }<br> */</span><br><span style="color: #c678dd; line-height: 26px;">public</span> <span style="line-height: 26px;"><span style="color: #c678dd; line-height: 26px;">class</span> <span style="color: #e6c07b; line-height: 26px;">Solution</span> </span>{<br> <span style="line-height: 26px;"><span style="color: #c678dd; line-height: 26px;">public</span> ListNode <span style="color: #61aeee; line-height: 26px;">removeNthFromEnd</span><span style="line-height: 26px;">(ListNode head, <span style="color: #c678dd; line-height: 26px;">int</span> n)</span> </span>{<br><br> ListNode dummy = <span style="color: #c678dd; line-height: 26px;">new</span> ListNode(<span style="color: #d19a66; line-height: 26px;">0</span>);<br> dummy.next = head;<br> <span style="color: #5c6370; font-style: italic; line-height: 26px;">// 声明两个指向头结点的节点</span><br> ListNode node1 = dummy, node2 = dummy;<br><br> <span style="color: #5c6370; font-style: italic; line-height: 26px;">// node1 节点先跑,node1节点 跑到第 n 个节点的时候,node2 节点开始跑</span><br> <span style="color: #5c6370; font-style: italic; line-height: 26px;">// 当node1 节点跑到最后一个节点时,node2 节点所在的位置就是第 (L-n ) 个节点,也就是倒数第 n+1(L代表总链表长度)</span><br> <span style="color: #c678dd; line-height: 26px;">while</span> (node1 != <span style="color: #c678dd; line-height: 26px;">null</span>) {<br> node1 = node1.next;<br> <span style="color: #c678dd; line-height: 26px;">if</span> (n < <span style="color: #d19a66; line-height: 26px;">1</span> && node1 != <span style="color: #c678dd; line-height: 26px;">null</span>) {<br> node2 = node2.next;<br> }<br> n--;<br> }<br><br> node2.next = node2.next.next;<br><br> <span style="color: #c678dd; line-height: 26px;">return</span> dummy.next;<br><br> }<br>}<br></code></pre>
<h2 style="font-weight: bold; font-size: 22px; margin-top: 20px; margin-right: 10px; margin-bottom: 0px;"><span style="font-size: 18px; display: inline-block; padding-left: 10px; border-left: 5px solid rgb(145, 109, 213);">5. 合并两个排序的链表</span></h2>
<h3 style="margin-top: 30px; margin-bottom: 15px; font-size: 16px; font-weight: bold; text-align: center;"><span style="border-bottom: 2px solid #d89cf6;">题目描述</span></h3>
<blockquote style="border-top: none; border-right: none; border-bottom: none; border-image: initial; font-size: 0.9em; overflow: auto; border-left-width: 3px; color: rgb(106, 115, 125); padding-top: 10px; padding-bottom: 10px; padding-left: 20px; margin-bottom: 20px; margin-top: 20px; border-left-color: rgb(216, 156, 246); background: rgb(244, 238, 255);">
<p style="padding-top: 8px; padding-bottom: 8px; font-size: 14px; word-spacing: 2px; margin-top: 0px; margin-bottom: 0px; color: black; line-height: 26px;">剑指 offer:输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。</p>
</blockquote>
<h3 style="margin-top: 30px; margin-bottom: 15px; font-size: 16px; font-weight: bold; text-align: center;"><span style="border-bottom: 2px solid #d89cf6;">问题分析</span></h3>
<p style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; font-size: 14px; word-spacing: 2px;">我们可以这样分析:</p>
<ol style="margin-top: 8px; margin-bottom: 8px; padding-left: 25px; font-size: 15px;">
<li><section style="margin-top: 5px; margin-bottom: 5px; line-height: 26px; color: rgb(1, 1, 1); font-size: 14px;">假设我们有两个链表 A,B;</section></li><li><section style="margin-top: 5px; margin-bottom: 5px; line-height: 26px; color: rgb(1, 1, 1); font-size: 14px;">A 的头节点 A1 的值与 B 的头结点 B1 的值比较,假设 A1 小,则 A1 为头节点;</section></li><li><section style="margin-top: 5px; margin-bottom: 5px; line-height: 26px; color: rgb(1, 1, 1); font-size: 14px;">A2 再和 B1 比较,假设 B1 小,则,A1 指向 B1;</section></li><li><section style="margin-top: 5px; margin-bottom: 5px; line-height: 26px; color: rgb(1, 1, 1); font-size: 14px;">A2 再和 B2 比较
就这样循环往复就行了,应该还算好理解。</section></li></ol>
<p style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; font-size: 14px; word-spacing: 2px;">考虑通过递归的方式实现!</p>
<h3 style="margin-top: 30px; margin-bottom: 15px; font-size: 16px; font-weight: bold; text-align: center;"><span style="border-bottom: 2px solid #d89cf6;">Solution</span></h3>
<p style="padding-top: 8px; padding-bottom: 8px; line-height: 26px; font-size: 14px; word-spacing: 2px;"><strong style="color: rgb(145, 109, 213);">「递归版本:」</strong></p>
<pre><code style="overflow-x: auto; padding: 16px; color: rgb(171, 178, 191); background: rgb(40, 44, 52); display: -webkit-box; font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace; border-radius: 0px;"><span style="color: #5c6370; font-style: italic; line-height: 26px;">/*<br>public class ListNode {<br> int val;<br> ListNode next = null;<br><br> ListNode(int val) {<br> this.val = val;<br> }<br>}*/</span><br><span style="color: #5c6370; font-style: italic; line-height: 26px;">//https://www.nowcoder.com/practice/d8b6b4358f774294a89de2a6ac4d9337?tpId=13&tqId=11169&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking</span><br><span style="color: #c678dd; line-height: 26px;">public</span> <span style="line-height: 26px;"><span style="color: #c678dd; line-height: 26px;">class</span> <span style="color: #e6c07b; line-height: 26px;">Solution</span> </span>{<br><span style="line-height: 26px;"><span style="color: #c678dd; line-height: 26px;">public</span> ListNode <span style="color: #61aeee; line-height: 26px;">Merge</span><span style="line-height: 26px;">(ListNode list1,ListNode list2)</span> </span>{<br> <span style="color: #c678dd; line-height: 26px;">if</span>(list1 == <span style="color: #c678dd; line-height: 26px;">null</span>){<br> <span style="color: #c678dd; line-height: 26px;">return</span> list2;<br> }<br> <span style="color: #c678dd; line-height: 26px;">if</span>(list2 == <span style="color: #c678dd; line-height: 26px;">null</span>){<br> <span style="color: #c678dd; line-height: 26px;">return</span> list1;<br> }<br> <span style="color: #c678dd; line-height: 26px;">if</span>(list1.val <= list2.val){<br> list1.next = Merge(list1.next, list2);<br> <span style="color: #c678dd; line-height: 26px;">return</span> list1;<br> }<span style="color: #c678dd; line-height: 26px;">else</span>{<br> list2.next = Merge(list1, list2.next);<br> <span style="color: #c678dd; line-height: 26px;">return</span> list2;<br> }<br> }<br>}<br></code></pre>
</section>
收藏(1255)
分享
相关标签: