-
算法-链表
简介:算法-链表
一、从尾到头打印链表
1、题目描述
输入:
{1,2,3}
返回值:
[3,2,1]
2、解题思路
使用递归
要逆序打印链表 1->2->3(3,2,1),可以先逆序打印链表 2->3(3,2),最后再打印第一个节点 1。而链表 2->3 可以看成一个新的链表,要逆序打印该链表可以继续使用求解函数,也就是在求解函数中调用自己,这就是递归函数。
1 import java.util.*;
2 public class Solution {
3 public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
4 ArrayList<Integer> ret = new ArrayList<>();
5 if (listNode != null) {
6 // 递归
7 ret.addAll(printListFromTailToHead(listNode.next));
8 ret.add(listNode.val);
9 }
10 return ret;
11 }
12 }
二、删除链表中重复的节点
1、题目描述
在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。 例如,链表1->2->3->3->4->4->5 处理后为 1->2->5
输入:
{1,2,3,3,4,4,5}
返回值:
{1,2,5}
2、解题思路
借助辅助头结点,可避免单独讨论头结点的情况。设置两个结点 pre 和 cur,当 cur 和 cur.next 值相等,cur 一直向前走,直到不等退出循环,这时候 cur 指的值还是重复值,调整 cur 和 pre 的指针再次判断。
1 /*
2 删除链表中重复的结点
3 */
4 public class Solution {
5 public ListNode deleteDuplication(ListNode pHead) {
6 /*
7 构建辅助头结点
8 对于链表而言,不保存的意思也就是跳过这一结点next指向下一个结点
9 */
10 ListNode head=new ListNode(-1);
11 head.next=pHead;
12 ListNode saveHead=head;//保存下来的结点
13 ListNode currentNode=pHead;
14 while(currentNode!=null){
15 if(currentNode.next!=null && currentNode.val==currentNode.next.val) {//有重复了,跳过
16 while (currentNode.next != null && currentNode.val == currentNode.next.val) {
17 currentNode = currentNode.next;//跳过这些相同结点
18 }
19 currentNode=currentNode.next;//有重复,一个都不保存
20 saveHead.next=currentNode;//保存
21 }
22 else {
23 //这里是没重复的
24 saveHead=currentNode;
25 currentNode=currentNode.next;
26 }
27 }
28 return head.next;
29 }
30 }
三、链表中倒数第K 个结点
1、题目描述
输入:
{1,2,3,4,5},1
返回值:
{5}
2、解题思路
在链表中:倒数的+顺数的长度等于链表总长度,所以可以设置两个指针,一个先走K 步,剩下的到链表的末尾要走的步数就是倒数第k个节点,需要从头开始走的步数。
1 import java.util.*;
2
3 /*
4 链表中倒数最后k 个结点
5 */
6 public class Solution {
7 public ListNode FindKthToTail (ListNode pHead, int k) {
8 ListNode first = pHead;
9 for(int i = 0; i < k; i++){
10 if(first == null){
11 return first;
12 }
13 first = first.next;
14 }
15 ListNode last = pHead;
16 while(first != null){
17 first = first.next;
18 last = last.next;
19 }
20 return last;
21 }
22 }
四、链表中环的入口结点
1、题目描述
输入:
{1,2},{3,4,5}
返回值:
3
说明:
返回环形链表入口节点,我们后台会打印该环形链表入口节点,即3
2、解题思路
使用双指针,一个快指针 fast 每次移动两个节点,一个慢指针 slow 每次移动一个节点。因为存在环,所以两个指针必定相遇在环中的某个节点上。
假设环入口节点为 y1,相遇所在节点为 z1。
假设快指针 fast 在圈内绕了 N 圈,则总路径长度为 x+Ny+(N-1)z。z 为 (N-1) 倍是因为快慢指针最后已经在 z1 节点相遇了,后面就不需要再走了。
而慢指针 slow 总路径长度为 x+y。
因为快指针是慢指针的两倍,因此 x+Ny+(N-1)z = 2(x+y)。
我们要找的是环入口节点 y1,也可以看成寻找长度 x 的值,因此我们先将上面的等值分解为和 x 有关:x=(N-2)y+(N-1)z。
上面的等值没有很强的规律,但是我们可以发现 y+z 就是圆环的总长度,因此我们将上面的等式再分解:x=(N-2)(y+z)+z。这个等式左边是从起点x1 到环入口节点 y1 的长度,而右边是在圆环中走过 (N-2) 圈,再从相遇点 z1 再走过长度为 z 的长度。此时我们可以发现如果让两个指针同时从起点 x1 和相遇点 z1 开始,每次只走过一个距离,那么最后他们会在环入口节点相遇。
1 /*
2 链表中环的入口结点
3 */
4 public class Solution {
5
6 public ListNode EntryNodeOfLoop(ListNode pHead) {
7 if(pHead == null || pHead.next == null){
8 return null;
9 }
10 ListNode slow = pHead;
11 ListNode fast = pHead;
12 do{
13 fast = fast.next.next;
14 slow = slow.next;
15 }while(slow != fast);
16 fast = pHead;
17 while(slow != fast){
18 slow = slow.next;
19 fast = fast.next;
20 }
21 return slow;
22 }
23 }
五、链表中环的入口结点
1、题目描述
输入:
{1,2,3}
返回值:
{3,2,1}
2、解题思路
递归
1 /*
2 反转链表
3 */
4 public class Solution {
5 public ListNode ReverseList(ListNode head) {
6 if(head == null || head.next == null){
7 return head;
8 }
9 ListNode next = head.next;
10 // 递归
11 ListNode newHead = ReverseList(next);
12 next.next = head;
13 head.next = null;
14 return newHead;
15 }
16 }