写在前面
做算法题时最最重要的是清晰的思路,理清思路然后用代码实现。
链表
链表可以分为单项链表、双向链表、以及环形链表。它在内存中的存储格式如下:
由图可得:
- 链表是以节点存储数据,是链式存储。
- 每个节点包含data和next,data存放数据,next指向下一个节点。
- 链表的各个节点不一定是连续存储。
- 链表可以分为带头节点的链表和不带头节点的链表。
一、单向链表
单项链表最核心的思想是单向,可以理解为只能向一个方向(从头到尾)添加、遍历、读取数据。如图:
二、双向链表
几点说明:
- 单向链表查找的方向只能是一个方向,而双向链表可以向前或向后双向查找。
- 单向链表不能自我删除(无法使删除节点后的链表连接起来),需要靠辅助节点。而双向链表,则可以自我删除。所以删除单链表的节点时,总是找到temp,temp是待删除节点的前一个节点。
注意:
调用在链表中间添加、删除节点的方法时,当遍历到双向链表的最后一个节点,要注意pre指针和next指针的指向问题,否则会出现空指针异常。
例:
删除节点temp时,以下代码
temp.pre.next = temp.next; //构建向后的指针
temp.next.pre = temp.pre; //构建向前的指针
作用最后一个节点时,temp.next是一个null,null.pre就会出现空指针异常。
三、环形链表
这里用一个问题(约瑟夫环)引出环形链表。
Josephu问题:设编号为1,2,…n的n个人围坐一圈,约定编号为k(1<=k<=n)的人从1开始报数,数到m的那个人出列,它的下一位又从1开始报数,数到m的那个人又出列,依次类推,直到所有人出列为止,由此产生一个出队编号的序列。
用单向环形链表清楚直观的解决Josephu问题。
总思路:
先构成一个有n个结点的单循环链表,然后由k结点起从1开始计数,计到m时,对应结点从链表中删除,然后再从被删除结点的下一个结点又从1开始计数,直到最后一个结点从链表中删除算法结束。
构建单向环形链表思路:
- 先创建第一个节点, 让 first 指向该节点,并形成环形(注:first指针不能动,因为后面加入的节点要指向first,形成环形)
- 每当创建一个新的节点(boy节点),就把该节点,加入到已有的环形链表中即可。
- curBoy指针必须指向的是新加入的boy节点的前一个节点,因为是单向链表,只能单向添加节点(单向链表的特性)。
- curBoy.next = boy;
- boy.next = first;
遍历环形链表:
- 先让一个辅助指针(变量) curBoy,指向first节点。
- 然后通过一个while循环遍历环形链表即可,当curBoy.next == first 结束。
出圈思路:
3. 先创建一个辅助指针(变量) helper , 先指向环形链表的最后这个节点.
补充: 小孩报数前,先让 first 和 helper 移动 k - 1 次
4. 当小孩报数时,让first 和 helper 指针同时 的移动 m - 1 次
5. 这时就可以将first 指向boy的节点出圈。
出圈步骤:
- first = first .next (first指针后移)
- helper.next = first
原来first 指向的节点就没有任何引用,就会被回收
Talk is cheap. Show me the code.
package DataStructure.linkedlist