单链表反转,快慢指针解决链表的常见问题

单链表反转

可一直向下递归,找到最后一个节点;
然后让head节点的next指向最后一个节点,逐个向上返回,达到反转链表效果;

在这里插入图片描述

具体实现
在之前的单向链表OneDirectionLinked基础上,添加链表反转的方法;

//反转整个链表的方法;
public void reverseLinkList(){
    //首先对链表进行判空处理;
    if(isEmpty()){
        return;
    }
    reversePartList(head.next);
}
//底层方法
private ListNode reversePartList(ListNode node){
    //递归的结束点;到达最后一个节点;
    if(node.next==null){
        head.next = node;
        return node;
    }
    //若不是最后一个节点;递归处理;
    ListNode front = reversePartList(node.next);
    front.next = node;
    node .next = null;
    return node;
}

测试使用

public class Test2 {
    //测试
    public static void main(String[] args) {
        OneDirectionLinked<Integer> one = new OneDirectionLinked<>();
        //添加元素;
        one.addNodeLast(12);
        one.addNodeLast(13);
        one.addNodeLast(12);
        one.addNodeLast(14);
        one.addNodeLast(15);
        one.addNodeLast(16);
        //遍历;
        one.iterator().forEachRemaining(a-> System.out.print(a +"->"));
        System.out.println();
        //反转链表;
        one.reverseLinkList();
        one.iterator().forEachRemaining(a-> System.out.print(a +"->"));
        System.out.println();
        //12->13->12->14->15->16->
        //16->15->14->12->13->12->
    }
}

初探快慢指针

快慢指针这种策略,在很多题目中都见到过的,比如说要对一段螺旋链表进行排序查找元素;比如说要判断链表中是否有环,比如说需要计算链表的环入口;

快指针一次跳过两个节点,而慢指针一次跳一个节点;当快指针到达链表末尾时,慢指针也就到达链表的中心位置了;

需要注意的是,有的快慢指针模板中,快指针是从头结点开始,而有的快慢指针是从头指针的下一个节点开始;那么就需要注意了

这里就让fast指针从头结点开始;

/**
 * @author by @CSDN 小智RE0
 * @date 2021-12-23 12:09
 */
//快慢指针学习
public class Demo {
    //链表的节点;
    static class ListNode {
        //节点元素;
        String data;
        //下一个;
        ListNode next;

        //初始化;
        public ListNode(String data, ListNode next) {
            this.data = data;
            this.next = next;
        }
    }

    //测试调用;
    public static void main(String[] args) {
        //构造节点之间的连接;
        ListNode h = new ListNode("H", null);
        ListNode g = new ListNode("G", h);
        ListNode f = new ListNode("F", g);
        ListNode e = new ListNode("E", f);
        ListNode d = new ListNode("D", e);
        ListNode c = new ListNode("C", d);
        ListNode b = new ListNode("B", c);
        ListNode a = new ListNode("A", b);

        //调用快慢指针方法1;
        String s = fastAndSlowStart(a);
        System.out.println("慢指针最终指向节点->" + s);
        //慢指针最终指向节点->E
    }

    //学习快慢指针的基础用法1;
    public static String fastAndSlowStart(ListNode head) {
        if (head == null) return null;
        //快指针,慢指针定义;
        ListNode fast = head;
        ListNode slow = head;

        while (fast != null && fast.next != null) {
            //快指针一次移动两个位置;慢指针一次一个位置;
            slow = slow.next;
            fast = fast.next.next;
        }
        return slow.data;
    }
}

试着画图,看看快指针从头结点开始和快指针从头结点的下一个开始有什么区别;
其实看着都差不多的;

链表节点个数为奇数位;

在这里插入图片描述

(1)fast快指针若是从头结点开始,判断这是奇数位的链表; 就看最终快指针所在的节点值不是null即可;
(2)fast快指针若是从头结点的下一个开始;判断这是奇数位的链表;就看最终快指针所在的节点值为null即可

链表个数为偶数位;

在这里插入图片描述

(1)fast快指针若是从头结点开始,判断这是偶数位的链表; 就看最终快指针所在的节点值是null即可;
(2)fast快指针若是从头结点的下一个开始;判断这是偶数位的链表;就看最终快指针所在的节点值不是null即可

判断是否为环形链表问题;

方式1;链表快慢指针使用

在这里插入图片描述

public class HasCycle {
    //节点类;
    static class ListNode {
        int val;
        ListNode next;
        ListNode(int x) {
            val = x;
            next = null;
        }
    }
    
    //判断链表是否为环形链表;
    public boolean hasCycle(ListNode head) {
        //特殊情况判断;
        if(head == null || head.next == null) return false;
        //使用快慢指针;
        ListNode  slow = head;
        ListNode  fast = head;
        //快指针向后快速移动;
        while(fast!=null && fast.next!= null){
            fast = fast.next.next;
            slow = slow.next;
            if(slow == fast) return true;
        }
        return false;
    }
}

当然,也可使用hashset的不重复特性来完成这道题;

public boolean hasCycle(ListNode head) {
       //特殊情况判断;
        if(head == null || head.next == null) return false;
        //使用辅助结构解决;
        Set<ListNode> set = new HashSet<>();
        set.add(head);
        //实际上只需要判断是否添加过;
        ListNode curNode = head.next;
        while(curNode!=null){
            //判断是否重复;若添加时发现和之前的一样就说明为环形链表;
            if(set.contains(curNode)){
                return true;
            }
            
            set.add(curNode);
            curNode = curNode.next;
        }
        return false;
}

查询链表的环入口

比如说怎么得到这个环形链表的环入口

在这里插入图片描述

在解决上一个问题时,发现只要快慢指针相遇就说明为环形链表,
那么在断定这是一个环形链表时,这时的慢指针位置不一定就是入口位置;特殊情况时确实可能指向到入口位置;
可以再重新定义一个指针,步长为1,这时让辅助指针移动,同时慢指针也移动;
当他和慢指针相遇的位置时就说明,那个指向的节点就是环形链表的入口;

public class FindEntryCircularList {
    //节点类;
    static class ListNode {
        int val;
        ListNode next;

        ListNode(int x) {
            val = x;
            next = null;
        }
    }

    //使用指针法;
    public ListNode EntryNodeOfLoop1(ListNode pHead) {
        //排除特殊情况;
        if (pHead == null || pHead.next == null) return null;
        //定义快慢指针;
        ListNode fast = pHead;
        ListNode slow = pHead;
        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;

        //重新定义一个辅助指针;
        ListNode fu = pHead;
        //找到慢指针;
        while (fu != slow) {
            fu = fu.next;
            slow = slow.next;
        }
        return fu;
    }
}    

单链表的排序使用

例如有这样一段无序的单链表;

先用快慢指针;找到链表的中点附近;
在这里插入图片描述

import java.util.*;

/*
 * public class ListNode {
 *   int val;
 *   ListNode next = null;
 * }
 */

public class Solution {
    /**
     * 
     * @param head ListNode类 the head node
     * @return ListNode类
     */
    public ListNode sortInList (ListNode head) {
       //需要对单链表进行排序;
        if(head == null || head.next == null)
            return  head;
        //使用快慢指针,将链表排查完毕;
        ListNode fast = head.next;
        ListNode slow = head;
        while(fast!=null && fast.next!=null){
            //快指针移动块;
            slow = slow .next;
            fast = fast.next.next;
        }
        //分隔链表;这里的temp就是后一段分离出的链表头结点;
        ListNode temp = slow.next;
        slow.next = null;
        //这时的slow重置到初始的头结点,这一段就是分离的前一段链表;
        slow = head;
        //递归两部分链表;
        ListNode left = sortInList(slow);
        ListNode right = sortInList(temp);

        ListNode dummy = new ListNode(0);
        ListNode resNode = dummy;

        //合并两部分链表;
        while(left!=null && right!=null){
            if(left.val < right.val){
                dummy.next = left;
                left = left.next;
            }else{
                dummy.next = right;
                right = right.next;
            }
            dummy = dummy.next;
        }
        
        //把不为空的一段拼接上去;
        dummy.next = left!=null ? left: right;
        
        return resNode.next;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小智RE0

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值