Day04算法|24. 两两交换链表中的节点 19.删除链表的倒数第N个节点 02.07. 链表相交142.环形链表II

本文介绍了如何通过编程解决LeetCode中的链表问题,包括两两交换链表节点、删除链表倒数第N个节点以及检测环形链表的方法,涉及快慢指针、虚拟头结点和哈希集合的运用。
摘要由CSDN通过智能技术生成

24. 两两交换链表中的节点 

文章讲解: 代码随想录

題目:力扣题目链接

 两两交换

首先设置虚拟头结点->指向真正头结点,设置两个指针分别指向两个头结点,而交换分为三步

首先处理两两交换结点的头与尾的链接:

将pre指针指向结点2(cru.next),将cru.next(即结点1)指向结点三。

最后将二者进行交换,由二结点指向一节点。一个循环结束,移动指针。

class Solution {
    public ListNode swapPairs(ListNode head) {
        ListNode dhead=new ListNode(-1);
    	dhead.next=head;
    	ListNode cru=head;
    	ListNode pre=dhead;
    	while(cru!=null&&cru.next!=null) {
    		pre.next=cru.next;
    		cru.next=cru.next.next;
    		pre.next.next=cru;
    		pre=cru;;
    		cru=cru.next;
    	}
    	return dhead.next;
    }
}

 19.删除链表的倒数第N个节点

文章讲解:代码随想录

題目:力扣题目链接

思路:计算链表长度,需要两次循环,首先遍历一遍计算长度,再遍历一遍进行删除

class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {
        if (head == null) {
            return head;
        }
        ListNode dhead = new ListNode();
        dhead.next = head;
        ListNode pre = dhead;
        ListNode cru = head;
        int j = 0;
        while (cru != null) {
            j++;
            cru = cru.next;
        }
        cru = head;
        if (j - n >= j || j - n < 0)
            return head;
        for (int i = 0; i < j - n; i++) {
            cru = cru.next;
            pre = pre.next;
        }
        pre.next = cru.next;
        return dhead.next;
    }
}

官方解法一:双指针法:

代码如下:

class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {
        ListNode dummy = new ListNode(0, head);
        ListNode first = head;
        ListNode second = dummy;
        for (int i = 0; i < n; ++i) {
            first = first.next;
        }
        while (first != null) {
            first = first.next;
            second = second.next;
        }
        second.next = second.next.next;
        ListNode ans = dummy.next;
        return ans;
    }
}

官方解法二:栈

class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {
        ListNode dummy = new ListNode(0, head);
        Deque<ListNode> stack = new LinkedList<ListNode>();
        ListNode cur = dummy;
        while (cur != null) {
            stack.push(cur);
            cur = cur.next;
        }
        for (int i = 0; i < n; ++i) {
            stack.pop();
        }
        ListNode prev = stack.peek();
        prev.next = prev.next.next;
        ListNode ans = dummy.next;
        return ans;
    }
}
、

02.07. 链表相交

文章讲解:代码随想录

题目:力扣题目链接 

暴力解法:

两层循环,找到a链与b链相等起始节点:

class Solution {

    public Node2 getIntersectionNode(Node2 headA, Node2 headB) {
    	Node2 a=headA;
    	Node2 b=headB;
    	while(a!=null) {
    		while(b!=null){
    			if(a==b) {
                  return a;
    			}
                b=b.next;
    		}
            a=a.next;
            b=headB;
    	}
         return null;
    }

当a与b结点相等时,直接返回结点。

官方解法一:

求出两个链表的长度,并求出两个链表长度的差值,然后让curA移动到,和curB 末尾对齐的位置,如图:

面试题02.07.链表相交_2

此时我们就可以比较curA和curB是否相同,如果不相同,同时向后移动curA和curB,如果遇到curA == curB,则找到交点。

public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        ListNode curA = headA;
        ListNode curB = headB;
        int lenA = 0, lenB = 0;
        while (curA != null) { // 求链表A的长度
            lenA++;
            curA = curA.next;
        }
        while (curB != null) { // 求链表B的长度
            lenB++;
            curB = curB.next;
        }
        curA = headA;
        curB = headB;
        // 让curA为最长链表的头,lenA为其长度
        if (lenB > lenA) {
            //1. swap (lenA, lenB);
            int tmpLen = lenA;
            lenA = lenB;
            lenB = tmpLen;
            //2. swap (curA, curB);
            ListNode tmpNode = curA;
            curA = curB;
            curB = tmpNode;
        }
        // 求长度差
        int gap = lenA - lenB;
        // 让curA和curB在同一起点上(末尾位置对齐)
        while (gap-- > 0) {
            curA = curA.next;
        }
        // 遍历curA 和 curB,遇到相同则直接返回
        while (curA != null) {
            if (curA == curB) {
                return curA;
            }
            curA = curA.next;
            curB = curB.next;
        }
        return null;
    }

}

官方解法二:栈

用两个栈来解决

public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        Deque<ListNode> st1 = new LinkedList<>();
        Deque<ListNode> st2 = new LinkedList<>();
        ListNode tmp1 = headA;
        ListNode tmp2 = headB;
        while(tmp1 != null){
            st1.push(tmp1);
            tmp1 = tmp1.next;
        }
        while(tmp2 != null){
            st2.push(tmp2);
            tmp2 = tmp2.next;
        }
        ListNode ans = null;
        while(!st1.isEmpty() && !st2.isEmpty()){
            ListNode a = st1.pop();
            ListNode b = st2.pop();
            
            if(a == b){
                ans = a;
            }
        }
        
        return ans;
    }
}

 官方解法三:双指针(数学解法)

public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        if (headA == null || headB == null) {
            return null;
        }
        ListNode pA = headA, pB = headB;
        while (pA != pB) {
            pA = pA == null ? headB : pA.next;
            pB = pB == null ? headA : pB.next;
        }
        return pA;
    }
}

原理:

若相交:

 不想交:

官方解法四:哈希集合

判断两个链表是否相交,可以使用哈希集合存储链表节点。

首先遍历链表 headA,并将链表 headA 中的每个节点加入哈希集合中。然后遍历链表 headB,对于遍历到的每个节点,判断该节点是否在哈希集合中:

如果当前节点不在哈希集合中,则继续遍历下一个节点;

如果当前节点在哈希集合中,则后面的节点都在哈希集合中,即从当前节点开始的所有节点都在两个链表的相交部分,因此在链表 headB 中遍历到的第一个在哈希集合中的节点就是两个链表相交的节点,返回该节点。

如果链表 headB中的所有节点都不在哈希集合中,则两个链表不相交,返回 null。

public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        Set<ListNode> visited = new HashSet<ListNode>();
        ListNode temp = headA;
        while (temp != null) {
            visited.add(temp);
            temp = temp.next;
        }
        temp = headB;
        while (temp != null) {
            if (visited.contains(temp)) {
                return temp;
            }
            temp = temp.next;
        }
        return null;
    }
}

 

142.环形链表II 

文章讲解:代码随想录

题目:力扣题目链接

我的思路:哈希集合

建立循环判断当前节点是否已在哈希集合中,若已经存在,则返回该节点。

class Solution {
	public Node3 detectCycle(Node3 head) {
		if(head==null||head.next==null)
		{
			return null;
		}
		Set<Node3> list=new HashSet<Node3>();
		Node3 cru=head;
		while(cru.next!=null) {
			list.add(cru);
			if(list.contains(cru.next)) {
				return cru.next;
			}
			else {
				cru=cru.next;
			}
		}
	    return null;
	}
}

官方思路:快慢指针

具体思路过于冗长,参照 文章讲解:代码随想录

快慢指针运动过程有如下图,核心思路:当快慢指针重逢时有等式:(x + y) * 2 = x + y + n (y + z)

public class Solution {
    public ListNode detectCycle(ListNode head) {
        ListNode slow = head;
        ListNode fast = head;
        while (fast != null && fast.next != null) {
            slow = slow.next;
            fast = fast.next.next;
            if (slow == fast) {// 有环
                ListNode index1 = fast;
                ListNode index2 = head;
                // 两个指针,从头结点和相遇结点,各走一步,直到相遇,相遇点即为环入口
                while (index1 != index2) {
                    index1 = index1.next;
                    index2 = index2.next;
                }
                return index1;
            }
        }
        return null;
    }
}

小技巧:

链表的功能实现通常用到

1.虚拟头结点:

链表的一大问题就是操作当前节点必须要找前一个节点才能操作。这就造成了,头结点的尴尬,因为头结点没有前一个节点了。每次对应头结点的情况都要单独处理,所以使用虚拟头结点的技巧,就可以解决这个问题。

2.双指针(进阶版:快慢指针)【巨好用!】

3.栈(引用的话)

        1)声明:

import java.util.Stack;

        2)创建:

Stack<ListNode> s = new Stack<ListNode>();

         3)函数:

表 Stack类的常用方法

方法描述
push(value)将给定的值压入栈的顶端
pop()删除并返回栈顶的值
peek()返回栈顶端的值,但是不将其从栈中删除
isEmpty()如果栈为空则返回true
size()返回栈中元素的个数

4.哈希集合

    1)声明:

import java.util.HashSet;
import java.util.Set;

        2)创建:

Set<ListNode> list=new HashSet<ListNode>();

         3)函数:

 方法名方法说明
add()往集合中添加元素
contains() 判断集合中是否存在某元素
remove()从集合中删除指定的元素
clear()清除集合中所有元素
isEmpty() 判断集合元素是否为空
Iterator()返回元素的迭代器
size() 获取集合中元素个数

  • 29
    点赞
  • 40
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值