链表(二)

目录

1.如何从尾到头输出单链表

2.如何寻找单链表的中间节点

3.如何检测一个链表是否有环

4.如何在不知道头指针的情况下删除指定节点

5.如何判断两个链表是否相交


1.如何从尾到头输出单链表

看到这个问题,最直接地想法就是先反转链表,再输出,在上一篇博客有实现这个方法,但需要额外的操作。

还有一种方法,就是利用栈。每经过一个节点,把该节点压入栈,遍历完链表后,从栈顶开始输出节点值。但此方法需要维护一个额外的空间,实现起来略显麻烦。

是否还有更高效的方法?我们想到了栈,自然也就想到递归本质上就是一个栈结构,可以用递归来实现。每访问一个节点,先递归输出它后面的节点,再输出该节点自身,达到从尾到头输出链表的效果。

public class printReversely {
    public static void printListReversely(Node p){
        if(p!=null){
            printListReversely(p.next);
            System.out.println(p.data);
        }
    }

    public static void main(String[] args) {
        Node a = new Node(1);
        Node b = new Node(2);
        Node c = new Node(3);
        Node d = new Node(4);
        Node e = new Node(5);
        a.next=b;
        b.next=c;
        c.next=d;
        d.next=e;
        e.next=null;
        printListReversely(a);
    }
}

2.如何寻找单链表的中间节点

最容易想到的方法就是先求解单链表长度length,然后遍历length/2距离可查找到中间节点。但这种方法需要遍历两次链表。

如果是双向链表,可以利用两个指针一个从头到尾遍历,一个从尾到头遍历,当两个指针相遇,就找到中间元素。如果是单链表也可以采用双指针的方式实现查找中间节点:

具体步骤就是两个指针同时从头开始遍历,一个指针一次一步,另外一个指针一次两步,快指针先到达尾部,慢指针恰好到达链表中部。其中当链表长度为奇数,慢指针指向即是中间节点,若为偶数,慢指针指向以及慢指针下一节点都是中间节点。

public class SearchMid {
    public static Node SearchMidNode(Node head){
        Node p = head;
        Node q = head;
        while(p != null && p.next != null && p.next.next != null){
            p = p.next.next;
            q = q.next;
        }
        return q;
    }

    public static void main(String[] args) {
        Node a = new Node(1);
        Node b = new Node(2);
        Node c = new Node(3);
        Node d = new Node(4);
        Node e = new Node(5);
        a.next=b;
        b.next=c;
        c.next=d;
        d.next=e;
        e.next=null;
        System.out.println(SearchMidNode(a).data);
    }
}

结果: 3

3.如何检测一个链表是否有环

定义两个指针fast和slow,slow每次前进一步,fast每次前进两步,两个指针同时移动,快指针每次都要和慢指针比较,直到快指针等于慢指针,就证明是带环的单链表;如果fast先行到NULL,则为无环链表。

public class IsLoopChs {
    public static boolean IsLoop(Node head){
        Node fast = head;
        Node slow = head;
        if(fast == null)
            return false;
        while(fast != null && fast.next != null){
            fast = fast.next.next;
            slow = slow.next;
            if(fast == slow)
                return true;
        }
        return !(fast == null || fast.next == null);
    }

    public static void main(String[] args) {
        Node a = new Node(1);
        Node b = new Node(2);
        Node c = new Node(3);
        Node d = new Node(4);
        Node e = new Node(5);
        a.next=b;
        b.next=c;
        c.next=d;
        d.next=e;
        e.next=a;
        System.out.println(IsLoop(a));
    }

}

结果:true

4.如何在不知道头指针的情况下删除指定节点

一般分两种情况讨论:

1)若待删除节点为尾节点,则无法删除,因为删除后无法使其前驱节点的next置为空

2)若待删除节点不是尾节点,则可以将后继节点值赋给当前节点,然后删除后继节点。

public class DeleteNode {
    public static boolean deleteNode(Node p){
        if(p == null || p.next == null)
            return false;
        p.data = p.next.data;
        p.next=p.next.next;
        return true;
    }

    public static void main(String[] args) {
        Node a = new Node(1);
        Node b = new Node(2);
        Node c = new Node(3);
        Node d = new Node(4);
        Node e = new Node(5);
        a.next=b;
        b.next=c;
        c.next=d;
        d.next=e;
        e.next=null;
        System.out.println(deleteNode(e));
        System.out.println(deleteNode(d));
        Node chs = a;
        while(chs!=null){
            System.out.println(chs.data);
            chs=chs.next;
        }
    }
}

结果:

5.如何判断两个链表是否相交

首先需要明确如果两个链表相交,那么一定有相同的尾节点,不可能出现X型交叉,因为每个节点只有一个指针域。因此可以分别遍历两个链表,记录尾节点,如果尾节点相同,那么两个链表相交。

public class IsIntersect {
    public static boolean isIntersect(Node h1,Node h2){
        if(h1==null||h2==null)
            return false;
        Node tail=h1;
        while(tail.next!=null)
            tail=tail.next;
        Node tail2=h2;
        while(tail2.next!=null)
            tail2=tail2.next;
        return tail==tail2;
    }

    public static void main(String[] args) {
        Node a = new Node(1);
        Node b = new Node(2);
        Node c = new Node(3);
        Node d = new Node(4);
        Node e = new Node(5);
        a.next=b;
        b.next=c;
        c.next=null;
        d.next=b;
        e.next=null;
        System.out.println(isIntersect(a,d));
        System.out.println(isIntersect(a,e));
    }
}

结果:

引申:如果两个链表相交,如何找到它们相交的第一个节点?

可以先计算两个链表的长度len1和len2(假设len1>len2),对长链表先遍历(len1-len2)个节点,此时距离它们相交的节点距离相同,同时开始遍历,直到遇到相同的节点。注意查找相交节点的前提是先判断是否相交。

    public static Node getFirstMeetNode(Node h1,Node h2){
        if(h1==null||h2==null)
            return null;
        Node tail=h1;
        int len = 1;
        while(tail.next!=null){
            tail=tail.next;
            len++;
        }
        Node tail2=h2;
        int len2=1;
        while(tail2.next!=null){
            tail2=tail2.next;
            len2++;
        }
        if(tail!=tail2)
            return null;
        Node t1=h1;
        Node t2=h2;
        if(len>len2){
            int d=len-len2;
            while(d!=0){
                t1=t1.next;
                d--;
            }
        }
        else{
            int d=len2-len;
            while(d!=0){
                t2=t2.next;
                d--;
            }
        }
        while (t1!=t2){
            t1=t1.next;
            t2=t2.next;
        }
        return t1;
    }

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值