LeetCode算法之链表总结--删除类操作,回环链表

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_18870127/article/details/82625747

      链表是数据结构中较为基础的一种数据结构,其在我们日常的开发中用的比较多,关于链表的一些基础知识,我在我之前的博客中已经介绍过,并且也做了一些简单的实现,然而这个博客主要是讲链表的应用问题,具体体现为在LeetCode上面与链表相关的题目做了一些总结,如果错误,还希望各位不吝,给予指正。链表相关知识可参考此链接

闲话不多说直接上题目进行分析:

第一类,改变单个链表的结构的,这一类以删除为核心操作的:

例如:LeetCode 删除链表中的节点:第203 和 237题;第203题是就爱那个连表中的含有给定值的链表中的元素进行删除,而237则是直接指定要删除的节点,所以首先要分清楚干什么,一个是通过元素的值判断,另一个则是直接指定了要删除的节点。

对于203题的解:

class Solution {
    public ListNode removeElements(ListNode head, int val) {
        
        ListNode dummyHead = new ListNode(-1);
        dummyHead.next = head;
        ListNode pre = dummyHead;
        while(pre.next != null){
            if(pre.next.val == val){
                pre.next = pre.next.next;
            }else
                pre = pre.next;
        }
        return dummyHead.next;
        
    }
}

对于207题的解则较为直接:

class Solution {
    public void deleteNode(ListNode node) {
        node.val = node.next.val;
        node.next = node.next.next;
    }
}

再入第83题的删除链表中的重复元素,这个需要删除重复的元素:

class Solution {
    public ListNode deleteDuplicates(ListNode head) {
        ListNode now=head;
        if(now==null)return head;

        while(now.next!=null){
            if(now.val==now.next.val){
                now.next=now.next.next;
            }
            else{
                now=now.next;
            }
        }
        return head;
    }
}

再如第19题:删除倒数第n个节点,其实就是一个利用指针进行寻址然后进行删除操作的过程:所以我们可以使用多个指针进行操作。在使用指针进行操作的时候不要忘记判断边界。核心点有两个,一个是找到要删除元素的前驱元素,另一个要点就是删除操作:

class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {
        
        if( n < 1 || head == null){
            return head;
        }
        
        ListNode dummyHead = new ListNode(0);
        ListNode prev = dummyHead, curr = dummyHead;
        dummyHead.next = head;
        
        //核心1:寻找要删除的元素的前驱
        while(n >= 0 && curr != null){
            curr = curr.next;
            n--;
        }
        
        while(curr != null){
            prev = prev.next;
            curr = curr.next;
        }
        
        //核心2:进行删除操作
        prev.next = prev.next.next;
        return dummyHead.next;
    }
}

可以看到,核心的内容是在删除操作的基础上,增加边界判断,然后确定好指针位置就即可。同时,如LeetCode第·82 删除排序链表中的重复元素:此题的核心是,把含有重复元素的元素全部删去,也就是说,一旦出现重复,就将此值为val的全部删去:

class Solution {
    public ListNode deleteDuplicates(ListNode head) {
        if (head == null || head.next == null) {
            return head;
        }
        
        ListNode dummy = new ListNode(0);
        dummy.next = head;
        head = dummy;

        while (head.next != null && head.next.next != null) {
            if (head.next.val == head.next.next.val) {
                int val = head.next.val;
                // val 记录的是重复元素的值,借用下面的while删除元素
                while (head.next != null && head.next.val == val) {
                    head.next = head.next.next;
                }            
            } else {
                head = head.next;
            }
        }
        
        return dummy.next;
    }
}

回环链表类:会换链表类说到本质,还是对指针的控制,判定是不是回环链表也是有技巧的,例如,平时链表我们是一个一个节点走的,但是,我们可以在判断回环链表时借助回环链表的特性,可以得到按照两倍的速度走,必然后重新碰到,如果遇到了,则说明这个是一个回环链表:

例如LeetCode第141. 环形链表,判断连表中是不是有回环:

public class Solution {
    public boolean hasCycle(ListNode head) {
        
        ListNode slow = head;
        ListNode fast = head;
        
        while(fast != null && fast.next != null){
            fast = fast.next.next;
            slow = slow.next;
            if(fast == slow){
                return true;
            }
        }
        
        return false;
    }
}

 再例如,在上面题目基础上加点佐料的第142. 环形链表 II题目中,给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。我们需要改变的地方在判断期待起点的地方,如果不是很明白可以参考下图:

在上图中我们假如fast和slow都从1开始,那么运行过程如下:

fast->3; slow ->2;

fast->5; slow ->3;

fast->7; slow ->4;

fast->3; slow ->5;

fast->5; slow ->6;

fast->7; slow ->7; 此时相遇了,所以我们只需要将fast移回开端,然后同时使得fast 和 slow共同移动就可以了:

slow = slow.next;

fast = fast.next;

如果相遇则,说明是起始位置,在图中,则表示为:

fast ->1;操作完成后同时开始移动fast 和 slow;

fast->2;

slow->2;

此时,节点而即为环形链表的开始节点,之后在上个问题代码的基础上增加发现的这个条件,或者是判据:

public class Solution {
    public ListNode detectCycle(ListNode head) {
        ListNode fast = head;
        ListNode slow = head;
        
        while(fast != null && fast.next != null){
            slow = slow.next;
            fast = fast.next.next;
            if(fast == slow){
                //操作一:返回head节点
                fast = head;
                //判断是否有环形链表的开始节点
                while(fast != slow){
                    fast = fast.next;
                    slow = slow.next;
                }
                return slow;
            }
        }
        return null;
    }
}

链表算法的题,还有其他类型,具体的可参考后续的博客,同时也希望能够批评指正,给予更好的建议。共同交流,共同进步。

猜你喜欢

转载自blog.csdn.net/qq_18870127/article/details/82625747