数据结构和算法之单向链表五:两个链表的第一个相遇节点以及倒序输出问题
我们在刚遇到这个问题的时候可能最迅速的想法就是把两个链表进行遍历,第一个链表next一下,对第二个链表就进行遍历,查找是否有相同的节点,如果有,那么就是交点,没有就不是。这样可不可以实现我们的功能,答案是当然可以,没有一丝一毫的问题,那么我们可以思考一下这个思路,它所需要的时间花销是不是应该为O(M*N),如果当我们的链表具有足够的长度的时候,这个算法无疑是失败的,极其的糟糕透顶。我们可以思考一个问题,就是我们在接触链表开始就提到的思路。可不可以通过使用两个指针来解决我们认为比较复杂得问题。我们知道这是一个单向链表,那么如果要是在拥有交点的前提下,必然可以说的是它们两个链表的在交点的后半部分是不是应该相等的,那么它们的差别是不是应该在交点的前半部分进行体现。也就是说,如果我们去除掉它们相差的节点数目,那么是不是同时进行后移,它们必然同时到达他们的交点处。这样我们分析一下,第一次遍历两个链表需要消费O(N)+O(M)的时间,第二次在进行遍历的时候所需的时间花销更是缩小无数,这样对于我们来说无疑是一个成功可行的思路。代码如下:
public Node getSameNode(Node head1,Node head2){ //判断两个链表是否有空链表的情况存在 if(head1 == null || head2 == null){ return null; } //让两个链表进行遍历 Node current = head1; int size1= 1; while(current.next != null){ size1++; current = current.next; } current = head2; int size2 = 1; while(current != null){ size2++; current = current.next; } //进行判断减除较长链表的多余节点 int size3 = size1 - size2; if(size3 > 0){ //让head1进行前行 for(int i = 0;i < size3;i++){ head1 = head1.next; } //让两个节点同时前行,找到交点 while(head1 != head2 && head1.next != null && head2.next != null){ head1 = head1.next; head2 = head2.next; if(head1 == head2){ return head1; } } }else{ //让head2进行前行 for(int i = 0;i < Math.abs(size3);i++){ head2 = head2.next; } //同时前行 while(head1 != head2 && head.next != null && head2.next != null){ head1 = head1.next; head2 = head2.next; if(head1 == head2){ return head2; } } } return null; }
这个算法的核心思想就在于分析如果两个链表相交时所具有的的特点,那就是一个Y型,这对于我们解决问题就可以去除很多不一致的地方。
接下来我们再顺便介绍两种关于链表倒序输出的方式:开启我们对于栈的认识,接下来我们需要进行的就是栈的部分
一:利用自定义的栈Stack类进行:为什么要用栈,我们知道单向链表只能从前往后进行,但是我们需要的是从后往前进行输出,有没有后进先出的感觉,所以我们就是用栈进行操作。
public void reversePrint(Node head){ //判断链表是否为空 if(head == null){ throw new Expection("链表为空"); } Stack<Node> stack = new Stack<>(); //对链表进行压栈 while(head != null){ stack.push(head); head = head.next; } //进行弹出 while(stack.size() > 0){ System.out.println(stack.pop().date); } }
二:利用系统的栈进行输出,其实系统栈的应用大部分都是递归的使用:代码优雅简洁,但是有一个比较尴尬的问题,如果链表数据量过于巨大,有栈溢出的风险
pubic void reversePrint(Node head){ //判断链表是否为空 if(head == null){ return; } reversePrint(head.next); System.out.println(head.date); }