链表结构题

链表结构面试题


以下题目通用的链表结构类

package Link;

public class LinkNode {
    public int value;
    public LinkNode next;
    public LinkNode rand;
    public boolean isCopy;

    public LinkNode(){

    }

    public LinkNode(int value){
        this.value = value;
        this.next = null;
        this.isCopy = false;
    }

    public LinkNode(LinkNode node){
        this.next = node.next;
        this.value = node.value;
        this.rand = node.rand;
        this.isCopy = true;
    }

    public void addNode(LinkNode head, int value){
        LinkNode node = head;
        while(node != null) node = node.next;
        LinkNode tmp = new LinkNode(value);
        node.next = tmp;
    }

    public LinkNode createTree(int[] arr){
        LinkNode head = new LinkNode(arr[0]);
        LinkNode tmp = head;
        for(int i=1; i<arr.length; i++){
            LinkNode node = new LinkNode(arr[i]);
            tmp.next = node;
            tmp = tmp.next;
        }
        return head;
    }
}

题目一: 判断链表是否为回文结构

package Link;

public class LinkMain {

    public static void main(String[] args){
		//生成链表
        int[] arr = new int[]{1,2,3,4,3,2,1};
        LinkNode node = new LinkNode();
        LinkNode head = node.createTree(arr);
        LinkPractice linkPractice = new LinkPractice();

        /**
         * 题目一:判断单链表是否是回文结构
         * */
        //解法一:不考虑空间复杂度和时间复杂度
        boolean result = linkPractice.isPallindrome1(head);
        System.out.println("是否是回文:"+result);
        //解法二:反转链表,考虑空间复杂度
        boolean result2 = linkPractice.isPalindrome3(head);
        System.out.println("是否是回文:"+result2);
}
    
package Link;
import javax.swing.tree.TreeNode;
import java.util.HashMap;
import java.util.Stack;
public class LinkPractice {

    /**
     * 判断回文
     */
    //解法一:使用栈
    public boolean isPallindrome1(LinkNode head){
        LinkNode node = head;
        Stack<LinkNode> stack = new Stack<>();
        while(node != null) {
            stack.push(node);
            node = node.next;
        }
        node = head;
        while (node!= null){
            if(node.value != stack.pop().value) return false;
            node = node.next;
        }
        return true;
    }

    //方法二:使用反转链表方法
    public boolean isPalindrome3(LinkNode head){
        if(head == null || head.next == null) return true;
        LinkNode node1 = getMidNode(head); //node1为中点
        LinkNode node2 = node1.next;
        LinkNode node3 = null; //辅助结点
        node1.next = null;
        while (node2 != null){
            node3 = node2.next; //保存node2下一个结点
            node2.next = node1; //将node2的下一个结点指向node1
            node1 = node2;  //将node2给到node1,以进行下一次反转指向
            node2 = node3; //将node3给到node2,以进行下一次反转指向
        }
        node3 = node1;
        node2 = head;
        boolean res = true;
        while(node1 != null ||  node2 != null){
            if(node1.value != node2.value) {
                res = false;
                break; //后面要将链表还原,不可以直接返回
            }
            node1 = node1.next;
            node2 = node2.next;
        }
        //还原链表
        node1 = node3.next;
        node3.next = null;
        while (node1!=null){
            node2 = node1.next;
            node1.next = node3;
            node3 = node1;
            node1 = node2;
        }
        return res;
    }
    //快慢指针找中间位置 --链表
    public LinkNode getMidNode(LinkNode head){
        LinkNode node1 = head;
        LinkNode node2 = head;
        while(node2.next != null && node2.next.next != null){
            node1 = node1.next;
            node2 = node2.next.next;
        }
        return node1;
    }
}

题目二:将链表分为大于区,小于去和等于区

package Link;
public class LinkMain {

    public static void main(String[] args){
		//生成链表
        int[] arr = new int[]{1,2,4,3,2,1,3};
        LinkNode node = new LinkNode();
        LinkNode head = node.createTree(arr);
        LinkPractice linkPractice = new LinkPractice();

        /**
         * 题目二: 将一个链表分为大于区,小于区和等于区
         * */
        //解法一:使用数组partation
        head = linkPractice.listPartation1(head);
        printTreeNodeValue(head);
        //解法二:使用六个变量,保证无额外空间的使用
        head = linkPractice.listPartation2(head);
        printTreeNodeValue(head);
}
    
package Link;
import javax.swing.tree.TreeNode;
import java.util.HashMap;
import java.util.Stack;
public class LinkPractice {
	//解法一:使用数组做partation
    public LinkNode listPartation1(LinkNode head) {
        if (head == null || head.next == null) return head;
        LinkNode tmp = head;
        int length = 0,index=0;
        while(tmp != null){
            length++;
            tmp = tmp.next;
        }
        tmp = head;
        LinkNode[] arr = new LinkNode[length];
        while(tmp != null){
            arr[index] = tmp;
            tmp = tmp.next;
            arr[index].next = null;
            index++;
        }
        arr = Arraypartation(arr,0,length-1);
        head = arr[0];
        tmp = head;
        for(int i=1; i<arr.length; i++){
            tmp.next = arr[i];
            tmp = tmp.next;
        }
        return head;
    }
    //将数组做partatin
    public LinkNode[] Arraypartation(LinkNode[] arr, int L, int R){
        int less = L-1;
        int more = R;
        while(L<more){
            if(arr[L].value < arr[R].value) swap(arr,++less,L++);
            else if(arr[L].value > arr[R].value) swap(arr,--more,L);
            else L++;
        }
        swap(arr,more,R);
        return arr;
    }
    //数组元素交换
    public void swap(LinkNode[] arr, int index1, int index2){
        LinkNode node = arr[index1];
        arr[index1] = arr[index2];
        arr[index2] = node;
    }

    //解法二:使用六个变量,无须额外的空间
    public LinkNode listPartation2(LinkNode head){
        if(head == null || head.next == null) return head;
        LinkNode sh=null,st=null,eh=null,et=null,mh=null,mt=null,comparison=head,tmp=null;
        while (comparison.next!=null)  comparison=comparison.next;
        while (head != null) {
            tmp = head.next;
            head.next = null;
            if (head.value < comparison.value) { //小于区域
                if (sh == null) {
                    sh = head;
                    st = head;
                } else {
                    st.next = head;
                    st = head;
                }
            } else if (head.value == comparison.value) {  //等于区域
                if (eh == null) {
                    eh = head;
                    et = head;
                } else {
                    et.next = head;
                    et = head;
                }
            } else { 
                if (mh == null) {
                    mh = head;
                    mt = head;
                } else {
                    mt.next = head;
                    mt = head;
                }
            }
            head = tmp;
        }
        if(st!=null){
            st.next = eh;
            et = et==null?st:et; //如果没有等于区域,则事小区域去链接,如果有,则是等于区域去连街
        }
        if(et!=null){
            et.next = mh;
        }
        return sh!=null?sh:(eh!=null?eh:mh);
    }
}

题目三: 链表节点中,有一个rand指针,指向任意结点,复制该链表

package Link;

public class LinkMain {

    public static void main(String[] args){
		//创建链表
        LinkNode tmp = head.next;
        tmp.rand = tmp.next.next;
        tmp = tmp.next;
        tmp.rand = tmp.next.next;
        tmp = tmp.next;
        tmp.rand = head;

        //解法一: 使用map
        LinkNode copyNode = linkPractice.mapCopyLink(head);
        System.out.println("原链表");
        printTreeNode(head);
        System.out.println("复制链表");
        printTreeNode(copyNode);

        //解法二: 在原链表上进行复制
        LinkNode copyNode = linkPractice.copyLink(head);
        System.out.println("原链表");
        printTreeNode(head);
        System.out.println("复制链表");
        printTreeNode(copyNode);
}
    
package Link;
import javax.swing.tree.TreeNode;
import java.util.HashMap;
import java.util.Stack;
public class LinkPractice {
	//解法一: 使用map
    public LinkNode mapCopyLink(LinkNode head){
        if(head == null || head.next == null) return head;
        HashMap<LinkNode,LinkNode> linkMap = new HashMap<>();
        LinkNode tmp = head;
        while (tmp!=null){
            linkMap.put(tmp,new LinkNode(tmp));
            tmp = tmp.next;
        }
        tmp = head;
        while(tmp !=null){
            linkMap.get(tmp).next = linkMap.get(tmp.next);
            linkMap.get(tmp).rand = linkMap.get(tmp.rand);
            tmp = tmp.next;
        }
        return linkMap.get(head);
    }

    //解法二: 在原链表里面复制进行操作
    public LinkNode copyLink(LinkNode head){
        if(head == null || head.next == null) return head;
        LinkNode tmp = head,node = null,copyNode = null;
        //在自身复制出每个结点成为自己的下一个结点
        while (tmp !=null){
            node = tmp.next;
            tmp.next = null;
            copyNode = new LinkNode(tmp.value);
            copyNode.isCopy = true;
            tmp.next = copyNode;
            tmp.next.next = node;
            tmp = node;
        }
        //调整复制链表的rand属性
        tmp = head;
        node = null;
        copyNode = null;
        while (tmp!=null){
            copyNode = tmp.next;
            node = tmp.next.next;
            copyNode.rand = tmp.rand != null ? tmp.rand.next : null;
            tmp=node;
        }
        //将原链表和复制链表拆分
        LinkNode copyNodeHead = head.next;
        tmp = head;
        node = null;
        copyNode = null;
        while (tmp != null){
            node = tmp.next.next;
            copyNode = tmp.next;
            copyNode.next = node != null ? node.next:null;
            tmp = node;
        }
        return copyNodeHead;
    }
}

题目四:一个链表上面从某一个结点开始进入循环,求入环结点

package Link;

public class LinkMain {

    public static void main(String[] args){
		//创建链表
        LinkNode tmp = head,interLoop = null;
        interLoop = tmp.next.next;
        while (tmp.next!=null) tmp = tmp.next;
        tmp.next = interLoop;

        //解法一: 使用集合
        LinkNode result = linkPractice.getInterLoopNodeByMap(head);
        System.out.println("入环结点是:"+result.value);

        //解法二: 使用快慢指针
        LinkNode result = linkPractice.getInterLoopNdoe(head);
        System.out.println("入环结点是:"+result.value);
}
    
package Link;
import javax.swing.tree.TreeNode;
import java.util.HashMap;
import java.util.Stack;
public class LinkPractice {
	//解法一: 使用栈
    public LinkNode getInterLoopNodeByMap(LinkNode head){
        if(head == null || head.next == null) return head;
        LinkNode tmp = head;
        HashMap<LinkNode,LinkNode> nodeMap = new HashMap<>();
        boolean flat=false;
        while(flat == false){
            if(nodeMap.containsKey(tmp)) {
                flat = true;
            }
            else {
                nodeMap.put(tmp,tmp);
                tmp =tmp.next;
            }
        }
        return tmp;
    }

    //解法二: 使用快慢指针
    public LinkNode getInterLoopNdoe(LinkNode head){
        if(head == null || head.next == null) return null;
        LinkNode slow = head.next;
        LinkNode quick = head.next.next;
        while (slow != quick){
            if(quick.next == null || slow == null) return null;
            slow = slow.next;
            quick = quick.next.next;
        }
        quick = head;
        while (quick != slow){
            quick = quick.next;
            slow = slow.next;
        }
        return quick;
    }
}

题目五: 有俩条不确定是否有环的链表,请判断俩条链表是否相交,如果相交,返回相交的第一结点

package Link;

public class LinkMain {

    public static void main(String[] args){
        //创建链表
        LinkNode tmp1 = head,interLoop = null;
        interLoop = tmp1.next.next;
        while (tmp1.next!=null) tmp1 = tmp1.next;
        tmp1.next = interLoop;

        LinkNode tmp2 = new LinkNode(3);
        tmp2.next = new LinkNode(4);
        tmp2.next.next = interLoop.next;
        LinkNode linkNode = linkPractice.getIntersectNode(head,tmp2);
        System.out.println("相交结点是:"+linkNode.value);
}
    
package Link;
import javax.swing.tree.TreeNode;
import java.util.HashMap;
import java.util.Stack;
public class LinkPractice {
	/**
     * 判断链表是否相交
     * */
    public LinkNode getIntersectNode(LinkNode head1,LinkNode head2){
        if(head1 == null || head2 == null || head1.next == null || head2.next == null)return null;
        LinkNode linkNode = null;
        //获取入环结点
        LinkNode interLoop1 = getInterLoopNdoe(head1);
        LinkNode interLoop2 = getInterLoopNdoe(head2);
        if(interLoop1 == null && interLoop2 == null){
            linkNode = noLoop(head1,head2);
        }
        if(interLoop1 != null && interLoop2 != null){
            linkNode = bothLoop(head1,interLoop1,head2,interLoop2);
        }
        return linkNode;
    }
    //获取入环结点
    public LinkNode getInterLoopNdoe(LinkNode head){
        if(head == null || head.next == null) return null;
        LinkNode slow = head.next;
        LinkNode quick = head.next.next;
        while (slow != quick){
            if(quick.next == null || slow == null) return null;
            slow = slow.next;
            quick = quick.next.next;
        }
        quick = head;
        while (quick != slow){
            quick = quick.next;
            slow = slow.next;
        }
        return quick;
    }
    //当俩条链表均没有环时判断是否相交
    public LinkNode noLoop(LinkNode head1,LinkNode head2){
        if(head1 == null || head2 == null || head1.next == null || head2.next == null)return null;
        //判断是否相交
        LinkNode tmp1 = head1;
        LinkNode tmp2 = head2;
        int difference = 0;
        while (tmp1 != null) {
            tmp1 = tmp1.next;
            difference++;
        }
        while (tmp2 != null) {
            tmp2 = tmp2.next;
            difference--;
        }
        if(tmp1 != tmp2) return null; //不相交
        else {
            LinkNode cur = difference > 0 ? tmp1 : tmp2;
            LinkNode tmp = cur == tmp1 ? tmp2 : tmp1;
            difference = Math.abs(difference);
            for (int i=0; i<difference; i++) cur = cur.next; //先走完长度差,不会在这段距离内相交
            while (cur != tmp){
                cur = cur.next;
                tmp = tmp.next;
            }
            return cur;
        }
    }

    //当俩个链表都有环时
    public LinkNode bothLoop(LinkNode head1,LinkNode interNode1,LinkNode head2,LinkNode interNode2){
        if(head1 == null || head2 == null || head1.next == null || head2.next == null)return null;
        if (interNode1 == interNode2){ //入环前相交
            //判断是否相交
            LinkNode tmp1 = head1;
            LinkNode tmp2 = head2;
            int difference = 0;
            while (tmp1 != interNode1) {
                tmp1 = tmp1.next;
                difference++;
            }
            while (tmp2 != interNode2) {
                tmp2 = tmp2.next;
                difference--;
            }
            if(tmp1 != tmp2) return null; //不相交
            else {
                LinkNode cur = difference > 0 ? head1 : head2;
                LinkNode tmp = cur == head1 ? head2 : head1;
                difference = Math.abs(difference);
                for (int i=0; i<difference; i++) cur = cur.next; //先走完长度差,不会在这段距离内相交
                while (cur != tmp){
                    cur = cur.next;
                    tmp = tmp.next;
                }
                return cur;
            }
        }else { //相交 则任意一个入环结点都可以
            LinkNode cur = interNode1.next;
            while (cur != interNode1){
                if (cur == interNode2) return cur;
                cur = cur.next;
            }
            return null; //不相交
        }
    }
}
posted @ 2022-02-19 01:00  彬哙  阅读(33)  评论(0)    收藏  举报