你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

单链表之环形链表

[复制链接]
STMCU小助手 发布时间:2021-7-23 14:20
之前我的写文章《链表在STM32中的应用》
5 |& E% K) \, Q# c) W6 W
, A2 Q. J( m0 |
% G3 D/ _  s( M( G1 Z5 k
    不论在面试或者刷题过程中,很大概率都会遇到环形链表这种类型的题目,例如 leetcode 141. 环形链表 以及 leetcode 142. 环形链表 II 等,本文主要介绍通过快慢指针法来解决此类题型,以供大家参考。; {! L* D, r) q2 r% {- e

1 u: R+ Y+ o. M" q6 a2 y* {

; B7 w" l8 a5 X环形链表1 a* o9 y+ b, H0 b; S
环形链表大致样子如下图所示:) o& r* q$ G# ?/ \' J  L8 X
1.jpg
7 y% y2 J) j; x* _- w* g( R3 g. l7 ?+ {; j% w9 Y& _5 t8 `# u/ @
. {# J5 r; m/ n3 ?% C
快慢指针法" a2 N" D# @' y) c# m' u1 ^
判断链表是否是环形链表,一般通过快慢指针法。
8 b8 l) [# Y7 q5 b: a9 ]3 J  P: {# L8 [/ @+ g! ]; i$ |

5 w& F, [: }$ _! F: ~' t操作步骤- A) j8 {' s: X8 R) r2 G
一、分别定义两个均指向头节点的指针(fast/slow);
! B% n/ A  Z5 c! O0 b1 ]3 Z二、快指针每次走两步,慢指针每次走一步;
/ x6 y! ~8 r+ c0 N# }5 K三、如果链表存在环,则快慢指针一定会在环中相遇。* e* Z: t7 I8 V' V( E4 `0 x

4 c# S. b6 X4 k5 a6 s

' |# F) x) F4 ]' n! B7 J9 l: F8 J. p     如下图示,快慢指针行走的轨迹如下:
( s& M4 f9 ^3 t8 Nfaster:1--->3--->5--->7- ?. ^/ j+ x9 X( s& A& _
slower:1--->2--->3--->43 u( V$ i/ [6 Y
2.jpg
: U. _0 ^. N, A1 I* Ufaster:7--->50 j. {& D6 o0 ?8 ^7 v8 ?
slower:4--->58 u6 @" ?; U9 N, w
当他们同时到达节点 5 时,完美相遇。
% n/ X4 x- q: S* r4 |9 U4 Z" s" X 3.jpg
% b- i' H0 [) K* n* f! a3 `, o5 w/ _- e, [
$ @" q+ x8 v2 q9 [
举栗1:判断链表是否有环: Z! z3 }( f; Z" z" L4 N2 ?
4.jpg / b- \& ~- Y# b
题目

8 ?& q8 w! Y- E2 o3 a0 d+ i
Show me the Code6 K1 W: m: y' t& z1 f5 o
  1. <font face="微软雅黑" size="3">//  C 语言3 c) f0 k4 w8 Q
  2. bool hasCycle(struct ListNode *head) {' N! U5 i" y) g. u& A: z* @
  3.     /* 特判 */
    9 _6 [! O/ b) q5 `! X
  4.     if (head == NULL || head->next == NULL) {" S' S6 n; t" \
  5.         return false;5 y0 B: l7 ^9 Q( W8 W2 k' W! c
  6.     }  V2 I' ?& @9 |4 P
  7.     struct ListNode *slow = head, *fast = head;
    / S, }4 C  d% N7 B: ]% F
  8.     /* fast指针走过链表末端,说明链表无环*/! T7 }7 P, m9 ^& H& [
  9.     while((fast != NULL) && (fast->next != NULL)) {
    0 A( P- N0 W# I: r# P3 \' g8 G& c
  10.         slow = slow->next;
    4 L. |7 Z. ~2 B& w/ V# w
  11.         fast = fast->next->next;
    + h. r6 b  ]2 l! D! z
  12.         if(slow == fast) {+ Z& u( ^1 T7 i: u7 E
  13.             return true;8 {3 |( W+ [/ ^7 s, b4 b
  14.         }
    / a9 s; w- E4 T9 }; x
  15.     }' u3 D' [$ _+ T6 c) R. A" s# f
  16.     return false;7 R( C4 N2 G( `$ s& m( \$ p
  17. }</font>
复制代码
  1. <font face="微软雅黑" size="3"># python3
    / \9 p9 ?/ ?* q  V) F* Y, {( d' G
  2. class Solution:
    $ e" j8 U0 E% B$ K9 }
  3.     def hasCycle(self, head: ListNode) -> bool:7 _6 e1 J' l  U$ D0 N/ v
  4.         if not head or not head.next: return False     
    . n: c8 H5 ?1 N1 {9 K
  5.         slow, fast = head, head% Y! {+ u/ h2 F, ^+ c% _' X
  6.         while fast and fast.next:. K) L& S8 ?/ i/ e4 y- C
  7.             slow = slow.next$ J  _2 r- f- g( Q0 i
  8.             fast = fast.next.next
    " O0 J1 @+ ^7 m$ s0 q& O
  9.             if slow is fast: return True
    " ]8 Z# I0 h  n, u1 }$ Q
  10.         return False  </font>
复制代码
举例2:求入环的第一个节点
% V- d, }: t* K" s 5.jpg - _/ q; `# ~$ q+ @+ I
题目
& W3 k. w& X8 J8 n5 z4 g. q

/ Q6 e" e2 t2 _6 ^+ O思路  ^- ]- N2 s8 g
本题除了需要判断链表是否有环外,如果有环还要求入环的第一个节点,因此是上一个题目的升级版本,还是以快慢指针中举例的那个链表作为示例,下图将描述当链表有环的时候,如何求出入环的第一个节点,见下图示:" z, o- y7 U9 v4 |4 c* B" P
已判断链表有环
4 E" d  P7 w) r  h 7.jpg 2 z# p/ m0 ^* C
求入环的第一个节点, x# E  u7 v& z# U! u/ Y
让慢指针重新指向链表的头节点,并让快慢指针同时每次只走一步
* |4 K2 W, g4 F' `7 ?* B0 ~" T$ F7 Ifaster:5--->6--->7) n6 ]* T; \( H; w
slower:1--->2--->3
; x/ K' b, _9 v/ Y0 G 8.jpg ! A7 @: N! A" _1 L7 w. W
继续走
7 {6 }' R/ Z7 [faster:7--->4/ i* p8 s1 V0 a, A2 O; X! y
slower:3--->43 w8 C% F* N' F$ K% v- S- L* ~
当他们同时到达节点 4 时,再次完美相遇。
2 t+ x" X4 @/ H  [- q8 j 9.jpg 7 Q2 |2 i7 }  X6 F3 E6 H
% @$ D2 U# ^0 j1 a% Y6 t' w
' D7 c- n& B" p

8 a  D6 N) B8 a( Q* L7 i
" Z3 v8 Q6 i3 O: y! b& P
因此,由上面的图可知,当判断链表有环(快慢指针相遇)之后,再让快(或者慢)指针重新指向链表头节点,另外一个指针仍保持指向不变,然后让快/慢指针同时走,且每次只走一步,当他们再次相遇时,此时相遇的节点就是入环的第一个节点。, v8 B# i8 }4 m& ]8 K
4 N5 X' p% C% @$ d8 @& N
$ Q% C3 r3 G, B* U& K5 b5 x
Show me the Code( t- Y4 \8 p0 S
  1. <font face="微软雅黑" size="3">//  c 语言
    , s# i' ]. Q0 C7 H* g
  2. struct ListNode *detectCycle(struct ListNode *head) {
    : ^1 s. m1 `9 s' w1 P! H, G
  3.     /* 记录是否有环 */
    1 N1 W+ o9 y& T: H5 D
  4.     bool flag = false;  8 t" I% M! ~9 j0 t
  5.     struct ListNode *slow = head, *fast = head;
    3 Z7 [" V" D0 S& R! I0 W+ O
  6.     while (fast != NULL && fast->next != NULL) {0 B% m* c; i" i. u- V
  7.         slow = slow->next;0 D  |2 d6 K! q( K+ Z
  8.         fast = fast->next->next;
    2 {6 X9 K6 h  d6 p( _- S. X
  9.         /* 有环则更新 flag 的值,并跳出循环寻找入环第一个节点 */
    ' h" v( L; R" A3 m7 z4 q! v
  10.         if (slow == fast) {$ {* e4 K4 j+ R# b4 t3 T
  11.             flag = true;
    $ l- w$ P0 v3 b% E: Y7 F2 z. q
  12.             break;
    3 P9 ~, {$ w1 U5 ^! o3 \/ B2 V# |
  13.         }
    3 i. V' U8 H- E2 q9 a. J
  14.     }
    & v4 w; g2 w  p: z
  15.     /* 无环则直接返回空 */
    + {1 Y; F$ z' i. @" E
  16.     if (flag == false) {! R1 o1 n8 I" @) n  m4 A% r% A
  17.         return NULL;2 _. Z+ x2 a: J; ?+ y+ G8 e: g
  18.     }
    / O& U3 {" d. m' k2 U) }% t  N7 ]
  19.     /* 慢指针重新指向链表头节点,快指针仍指向相遇节点 */$ j0 C; R4 @  k& b, M+ j& B7 t
  20.     slow = head;
    $ s  j: R) \/ C; D
  21.     /* 快慢指针每次走一步,相遇节点即为入环第一个节点 */& Z4 h- G$ I/ e* n) B. Z% }  |9 F1 R; K
  22.     while (fast != slow) {" l! t3 `# k/ ~* ], h% y* {
  23.         fast = fast->next;& ?3 @0 Y' a" A& d! b* V+ n
  24.         slow = slow->next;" y9 e# j  }8 o& ?& j
  25.     }$ n/ o4 N& }2 ]* j
  26.     return slow;; R& e( J7 c% w2 m( f
  27. }</font>
复制代码
  1. <font face="微软雅黑" size="3">//  go 语言
    8 i; Z7 J( M; e! J# ^7 E5 a+ P# r
  2. " ^. H5 Y, ?9 z
  3. func detectCycle(head *ListNode) *ListNode {
    ( [, l3 l6 ?$ b! A# C9 e9 r2 R" z1 v8 l
  4.     slow, fast := head, head
    0 \. N8 E$ `" S; B" O+ \& @/ Z
  5.     for fast != nil && fast.Next != nil {
    , ]* p0 y, s3 ?1 P: w$ h  K4 E  r; h  x
  6.         fast, slow = fast.Next.Next, slow.Next6 \5 U  _; u7 w7 U9 B+ W# X
  7.         if fast == slow {
    1 B7 n3 ^2 K# c" u0 A  @- Y' k
  8.             break, I, _8 M, J4 X3 o2 s
  9.         }, r/ p; t" w  X  T& {
  10.     }7 L2 V$ U* Y' e+ J9 y
  11.     if fast == nil || fast.Next == nil {4 X* j, ?' ^: h* E% _0 J3 P
  12.         return nil, w: N3 a! {, h$ F( ~1 l
  13.     }5 W. _. L! K( _0 U+ Q
  14.     slow = head
    8 s" }( o$ }& z3 n( \
  15.     for fast != slow {8 d( Y3 g  ?" N/ K8 A
  16.         fast, slow = fast.Next, slow.Next* C5 Z+ x: j, G
  17.     }
    4 O# z! v% L; H! J# ?
  18.     return fast
    5 X5 H  o8 n' s% [7 u# W
  19. }</font>
复制代码
% }- d+ G$ T! b+ U

/ u$ p2 s3 _; Q7 n
6.jpg
赞 收藏 评论0 发布时间:2021-7-23 14:20

举报

0个回答
关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版