写leetcode遇到得链表问题总结

1.关于判断链表是否有环的问题

在解决这类问题上,我个人认为有两种办法:哈希及双指针
那先来看一下如何用哈希去解决这类问题
算法描述:遍历所有的链表的所有结点,将这些个结点全部存在hashset里面,直到下一个结点位null,则结束,如果当前结点已经存在于哈希表里,那么证明有环,反之就没有。
接下来看代码:
1.先定义一个链表类

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

2.再定义一个实例链表测试的主函数

   public static void main(String[] args) {
        ListNode listNode=new ListNode(3);
        ListNode listNode1=new ListNode(2);
        ListNode listNode2=new ListNode(0);
        ListNode listNode3=new ListNode(-4);
        listNode.next=listNode1;
        listNode1.next=listNode2;
        listNode2.next=listNode3;
        listNode3.next=listNode1;
        boolean b = hasCycle(listNode);
        System.out.println(b);

    }

那么实际的链表应该是这样的:


image.png

3.从这个链表实例出发可以看到我们调用了hasCycle()方法去判断是否有环,这里先用hashSet去实现。

public static boolean hasCycle1(ListNode head){
        //base判断
        if(head==null||head.next==null){
            return false;
        }
        //创建容器
        Set set=new HashSet();
        //循环出口
        while(head!=null){
            //判断如果包含结点,直接确定是循环连接
            if(set.contains(head)){
                return true;
            }else{
                //没有就往里添加
                set.add(head);
            }
            //结点依次往下
            head=head.next;
        }
        //找不到就返回fasle
        return false;
    }

4使用快慢指针去解题

 public static boolean hasCycle(ListNode head){
        //  base判断
        if(head==null||head.next==null){
            return false;
        }
        //指定快慢结点
        ListNode fast=head.next;
        ListNode slow=head;
        //循环出口
        while(fast!=null&&fast.next!=null){
            //如果快指针走过一圈回来碰到慢指针那肯定有环
            if(fast.equals(slow)){
                return true;
            }
            //快慢指针往下走
            fast=fast.next.next;
            slow=slow.next;
        }
        //找不到就返回fasle
        return false;
    }

2.关于链表反转的问题

两种方法:迭代和递归

1.首先看迭代
假设存在链表 1 → 2 → 3 → Ø,我们想要把它改成 Ø ← 1 ← 2 ← 3。

在遍历列表时,将当前节点的 next 指针改为指向前一个元素。由于节点没有引用其上一个节点,因此必须事先存储其前一个元素。在更改引用之前,还需要另一个指针来存储下一个节点。不要忘记在最后返回新的头引用!

public class ReverseListNode {
    public static void main(String[] args) {
        ReverseListNode reverseListNode = new ReverseListNode();
        ListNode l1=new ListNode(3);
        ListNode l2=new ListNode(2);
        ListNode l3=new ListNode(0);
        ListNode l4=new ListNode(-1);
        l1.next=l2;
        l2.next=l3;
        l3.next=l4;
        System.out.println(reverseListNode.reverseList(l1));




    }
    public static ListNode reverseList(ListNode head){
        //首先定义一个空指针,这个空指针定义是要在最头部的
        ListNode prev=null;
        //然后将curr指向head
       ListNode curr=head;
       //一步步迭代,出口就是curr=null的时候
        while(curr!=null){
            //先拿到curr的下一个指针
            ListNode temp=curr.next;
            //然后把当前的结点指向prev,即指向前一个结点
            curr.next=prev;
            //更新curr和prev的值,把prev往后移一位
            prev=curr;
            //再把当前结点往后移一位
            curr=temp;
            //就这样依次迭代,直到curr==null的时候跳出

        }
        //然后返回prev
        return prev;
    }
}

运行结果:

ListNode{val=-1, next=ListNode{val=0, next=ListNode{val=2, next=ListNode{val=3, next=null}}}}

Process finished with exit code 0

最终完成了反转。

1.再看递归
递归的解法很难理解,
递归的两个条件:

终止条件是当前节点或者下一个节点==null
在函数内部,改变节点的指向,也就是 head 的下一个节点指向 head 递归函数那句

head.next.next = head

很不好理解,其实就是 head 的下一个节点指向head。
递归函数中每次返回的 cur 其实只最后一个节点,在递归函数内部,改变的是当前节点的指向。

public static ListNode reverseList1(ListNode head){
       //先确定递归的终止条件
        if(head==null||head.next==null){
            return head;
        }
        //先拿到最后一个结点curr
        ListNode curr = reverseList1(head.next);
        //如果链表是 1->2->3->4->5,那么此时的cur就是5
        //而head是4,head的下一个是5,下下一个是空
        //所以head.next.next 就是5->4
        head.next.next=head;
        //防止链表循环,将head.next=null
        head.next=null;
        //每层递归函数都返回cur,也就是最后一个节点
        return curr;

    }

结果如下:

ListNode{val=-1, next=ListNode{val=0, next=ListNode{val=2, next=ListNode{val=3, next=null}}}}

Process finished with exit code 0

3.再来讨论一个很经典的约瑟夫问题吧,这曾经是我在恒生笔试的一道题,当时一脸懵,现在蹭热再好好讨论一下经典的约瑟夫问题


image.png
public class JosephTest {
    public static void main(String[] args) {
        Node first=null;
        Node pre=null;
        for (int i = 1; i <=41; i++) {
            //如果是第一个结点
            if(i==1){
                first=new Node(i,null);
                pre=first;
                continue;
            }
            //如果不是第一个结点
            Node newNode=new Node(i,null);
            //让前一个结点指向这个新节点
            pre.next=newNode;
            //然后再让pre指针往后移
            pre=newNode;
            //如果位最后一个结点,则最后一个结点位第一个结点
            if(i==41){
                pre.next=first;
            }
        }
        //count计数,模拟报数
        int count =0;
        //记录第一个结点
        Node n=first;
        //当前结点的前一个结点
        Node before=null;
        //循环结束的条件n==n.next
        while(n!=n.next){
            //模拟报数
            count++;
            //判断是不是3
            if(count==3){
                //删除
                before.next=n.next;
                //打印
                System.out.println(n.val);
                //重置
                count=0;
                //继续往下
                n=n.next;
            }else{
                //如果不是3.那么继续往下
                before=n;
                n=n.next;
            }
        }
        System.out.println("最后的元素"+n.val);


    }
}
class Node{
    int val;
    Node next;

    public Node(int val, Node next) {
        this.val = val;
        this.next = next;
    }

    @Override
    public String toString() {
        return "Node{" +
                "val=" + val +
                ", next=" + next +
                '}';
    }
}

最终结果:

你可能感兴趣的:(写leetcode遇到得链表问题总结)