LeetCode和牛客网有关列表题目总结—Java

1.删除链表中等于给定值val的所有节点。

203. 移除链表元素 - 力扣(LeetCode)

class Solution {
    public ListNode removeElements(ListNode head, int val) {
        if(head==null) {
			return null;
		}
		ListNode cur=head.next;
		ListNode prev=head;
		while(cur!=null) {
			if(cur.val==val) {
                prev.next=cur.next;
            }else {
				prev.next=cur;
				prev=cur;
			}
            cur=cur.next;
		}
		if(head.val==val) {
			head=head.next;
		}
        return head;
    }
}

总结:首先我们在列表的题目时,先判断链表是否为空。这道题运用方法是双指针也称为快慢指针。当我们拿到这道题的时候,我们先想遇到给定值怎么办,删除这个节点还是采取其他的办法,

其次这是一个链表,直接跳过这个节点就相当于删除了。遇到不同的怎么办,不同的我们是不是得让这些不同的连在一起啊,所以就有了prev.next=cur.next。最后一单也是最重要的,当首节点也是给定值的时候,所以需要判断一下。因为在循环中只有cur的val进行判断了,而prev的val没有判断。

2.反转一个单链表

206. 反转链表 - 力扣(LeetCode)

class Solution {
    public ListNode reverseList(ListNode head) {
        if(head==null) {
            return null;
        }
        ListNode cur=head.next;
        head.next=null;
        while(cur!=null) {
            ListNode curNext=cur.next;
            cur.next=head;
            head=cur;
            cur=curNext;
        }
        return head;
    }
}

总结:一上来还是先判断这个链表是否为空。 我们在模拟实现一个链表的时候,头插法导致链表的插入顺序和显示出来的顺序是反过来的。所以这里可以运用头插法,先遍历链表,让每个节点都

都进行头插,在进行头插的时候,每个链表的下一个指向不是原来的指向,所以在进行头插之前,先保存下来这个节点的下一个指向也就是ListNode curNext=cur.next,头插完成时,再赋值给cur。

3.链表的中间节点

876. 链表的中间结点 - 力扣(LeetCode)

class Solution {
    public ListNode middleNode(ListNode head) {
        ListNode fast=head;
        ListNode slow=head;
        while(fast!=null&&fast.next!=null) {
            fast=fast.next.next;
            slow=slow.next;
        }
        return slow;
    }
}

 总结:这个链表不可能额为空,因为要返回中间的节点。运用的还是快慢指针,由此可见快慢指针多么重要,让fast一次走两步,slow一次走一步,退出循环的时候slow正好是中间节点。这时就有人问了,这是为什么?举个例子,有一段路程一个人的速度是另一个人的速度的两倍,当快的走完全程的时候,慢的正好走到中间,这里也是一样的。

这里还分情况讨论:当节点的数量为奇数时,如图

当节点的数量为偶数的时候,如图

4.合并两个有序链表

21. 合并两个有序链表 - 力扣(LeetCode) 

class Solution {
    public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
        if(list1==null&&list2!=null) {
            return list2;
        }
        if(list1!=null&&list2==null) {
            return list1;
        }
        if(list1==null&&list2==null) {
            return null;
        }
        ListNode head=null;
        ListNode cur=null;
        while(list1!=null&&list2!=null) {
            if(list1.val<list2.val) {
                if(head==null&&cur==null) {
                    head=list1;
                    cur=list1;
                } else {
                    cur.next=list1;
                    cur=cur.next;
                }
                    list1=list1.next;
            } else {
                if(cur==null&&head==null) {
                    head=list2;
                    cur=list2;
                } else {
                    cur.next=list2;
                    cur=cur.next;
                }
                list2=list2.next;
            }
        }
        if(list1==null) {
            cur.next=list2;
        }
        if(list2==null) {
            cur.next=list1;
        }
        return head;
    } 
}

总结:当其中一个为空,另一个就是已经合并好的,直接返回这个链表的头引用就行了。当两个链表都为空的时候,直接返回空。这道题采用的是小的进行尾插。我们在逻辑上开辟了一个新的链表,而物理上并没有让head来指向这个新链表的头部,cur来遍历新链表。而list1和list2分别遍历自己的链表head的指向无非就是这两个链表其中的一个,在第一次比较的时候就出现了,head等于list1或等于list2,而cur也需要这样赋值,之后就是来比较list1和list2的val值,小的链接在cur的后面最后可能出现的情况是,一个链表遍历完了,另一个链表没有遍历完,这里所说的两个链表是list1和list2,所以需要进行判断, 不为空的插入到cur后面。

5.链表的回文结构

链表的回文结构_牛客题霸_牛客网 (nowcoder.com)

public class PalindromeList {
    public boolean chkPalindrome(ListNode A) {
        // write code here
        ListNode head=A;
        ListNode fast=A;
        ListNode slow=A;
        while(fast!=null&&fast.next!=null) {
            fast=fast.next.next;
            slow=slow.next;
        }
        ListNode cur=slow.next;
        while(cur!=null) {
            ListNode curNext=cur.next;
            cur.next=slow;
            slow=cur;
            cur=curNext;
        }
        while(head!=slow) {
            if(head.val!=slow.val) {
                return false;
            }
            if(head.next==slow) {
                return true;
            }
            head=head.next;
            slow=slow.next;
        }
        return true;
    }
}

总结:这道题看着很难,但是经过分析过后一点也不难。在进行回文结构的判断的时候,我们先想到的是让一个引用在前面一个引用在后面,分别进行遍历。但是这是一个单链表如何进行反着遍历呢?

1.先找到这个链表的中间节点。

这个方法在上面的题目说过

2.利用中间节点再该表中间节点的指向

所谓的改变指向就是将一个节点不指向后面而是指向前面节点,其实就是反转链表,上面题目也说过。

3.然后分别从头和从尾部进行判断这个链表是否为回文结构。 

当我们完成后半段链表的反转的时候slow是指向在最后一个节点的

奇数个节点的情况

 偶数个节点的数量

 

偶数个节点情况比较特殊一点,当我们判断完val值是否相等的时候,还要判断head.next==slow

如果相等返回直接返回true。因为当前面都没有返回的时候,此时正好head.next==slow而且他们两个所指向的val值还想等。

6.相交链表

 160. 相交链表 - 力扣(LeetCode)

public class Solution {
    public ListNode getIntersectionNode(ListNode head1, ListNode head2) {
        //相交的链表是下一个next域相同,并不是val值相等
        //先让长的走差值步,之后再一起走。
        int len1=0;
        int len2=0;
        ListNode pl=head1;
        ListNode ps=head2;
        while(pl!=null) {
            len1++;
            pl=pl.next;
        }
        while(ps!=null) {
            len2++;
            ps=ps.next;
        }
        int len=len1-len2;
        pl=head1;
        ps=head2;
        if(len1-len2<0) {
            pl=head2;
            ps=head1;
            len=len2-len1;
        }
        while(len!=0) {
            pl=pl.next;
            len--;
        }
        while(pl!=ps) {
            pl=pl.next;
            ps=ps.next;
        }
        //如果他们两个走到空的时候说明没有相遇,返回空,此时pl和ps正好为空。
        return pl;
    } 
}

 总结:先补充一个小知识,相交链表一定是Y字型的,而不是X字型的,因为相交链表是两个链表的其中一个节点的next域相同。如图:

 

整体思路就是:分别遍历自己的链表,之后让长的链表走差值步,之后在一起走。如果,指向相同即返回这个指向,如果没有相交的节点,此时pl==null,返回pl正好代表null。

还有一点:随机认定一个长的,当len结果出来后再修改数据。 

7.环形链表

141. 环形链表 - 力扣(LeetCode)判断一个链表是否存在有环。

public class Solution {
    public boolean hasCycle(ListNode head) {
        //有没有环,看快慢指针是否能够相遇
        //这里最好一个走一步,一个走两步,如果一个走的步数较多,会进行跳过另一个
        ListNode fast=head;
        ListNode slow =head;
        while(fast!=null&&fast.next!=null) {
            fast=fast.next.next;
            slow=slow.next;
            if(fast==slow) {
                return true;
            }
        }
        return false;
    }
}

总结:还是快慢指针。让一个走两步,一个走一步。为什么不让一个多走几步呢?因为判断一个链表是否有环是通过fast和slow是否相遇来判断的。链表上的相遇跟我们平时的相遇不一样,fast一次走的步数多的话,会跳过这个slow,导致相遇的机会变少,也有可能导致无法相遇,所以一次走两步是最好的(fast)。 还要判断空指针的异常问题

8.环形链表Ⅱ 

142. 环形链表 II - 力扣(LeetCode)

 结论:让一个指针从链表的起始位置开始遍历链表,同时让一个指针从相遇点的位置开始绕环,两个指针每次均走一步,最终肯定会在入口点的位置相遇。

public class Solution {
    public ListNode detectCycle(ListNode head) {
        //结论就是,快的在相遇点到入环的第一个节点与慢的在进入环的路程相等
        //也就是L等于x
        //L=(N-1)C+Y说明环很小,fast走了很多圈
        //慢的不可能进行套圈的,因为他们两个最大的距离为一个圈的长度,慢的不可能在环里走的一圈的,如果走一圈,
        //快的走两圈,满足这种情况只有快的和慢的在入环口开始一起走,这种情况下,快的和慢的已经相遇了。

        //先让他们两个相遇
        //之后再让慢的从头开始走,相遇便是入环口

        if(head==null) {
            return null;
        }
        ListNode fast=head;
        ListNode slow=head;
        while(fast!=null&&fast.next!=null) {
            fast=fast.next.next;
            slow=slow.next;
            if(fast==slow) {
                break;
            }
        }
        if(fast==null||fast.next==null) {
            return null;
        }
        slow=head;
        while(fast!=slow) {
            fast=fast.next;
            slow=slow.next;
        }
        return fast;
    }
}

结论推导:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值