美文网首页
链表(Java)

链表(Java)

作者: dlihasa | 来源:发表于2019-07-28 14:47 被阅读0次

    链表常见的操作:

    1、插入节点(头插、尾插)
    2、删除节点
    3、获取链表长度
    4、逆序打印
    5、反转链表
    6、判断链表是否有环
    7、判断链表是否相交
    8、如果相交,返回第一个开始相交的节点
    9、查找单链表中第K个节点
    10、寻找单链表的中间结点

    代码外加注释如下

    class LinkList{
        Node header;
        static class Node{
            int data;
            Node next;
            public Node(int data){
                this.data = data;
            }
        }
        
        /*
         * 头插法
         * 被插入node的next指向header,则成为第一个
         * 将header移动到头部
         */
        public void addNodeHead(int data){
            Node newNode = new Node(data);
            if(header==null){
                header = newNode;
            }else{
                newNode.next = header;
                header = newNode;
            }
        }
        
        /**
         * 尾插法
         */
        public void addNodeEnd(int data){
            Node newNode = new Node(data);
            if(header==null){
                header = newNode;
            }else{
                Node temp = header;
                while(temp.next!=null){
                    temp = temp.next;
                }
                temp.next = newNode;
            }
        }
    /**
         * 链表删除结点:
         * 把要删除结点的前结点指向要删除结点的后结点,即直接跳过待删除结点
         * @param index
         * @return
         */
        public boolean deleteNode(int index){
            if(index<1 || index>getLength(header)){//待删除结点不存在
                return false;
            }
            if(index == 1){//删除头结点
                header = header.next;
                return true;
            }
            Node preNode = header;
            Node curNode = preNode.next;
            int i = 1;
            while(curNode != null){
                if(i==index){//寻找到待删除结点
                    preNode.next = curNode.next;//待删除结点的前结点指向待删除结点的后结点
                    return true;
                }
                //当先结点和前结点同时向后移
                preNode = preNode.next;
                curNode = curNode.next;
                i++;
            }
            return true;
        }
        
        /**
         * 长度
         * @param header
         * @return
         */
        public int getLength(Node header){
            int num = 0;
            while(header!=null){
                num++;
                header = header.next;
            }
            return num;
        }
        
        /**
         * 从尾到头打印链表
         * 建栈
         */
        public void reversePrintLink(Node node){
            Stack<Node> stack = new Stack<Node>();
            while(node!=null){
                stack.push(node);
                node = node.next;
            }
            while(!stack.isEmpty()){
                System.out.print(stack.pop().data+" ");
            }
            System.out.println();
        }
        
        /**
         * 从尾到头打印链表
         * 递归
         */
        public void reversePrintLink1(Node node){
            if(node!=null){
                reversePrintLink1(node.next);
                System.out.print(node.data+" ");
            }
        }
        
        /**
         * 正序打印
         */
        public void printLink(Node node){
            while(node!=null){
                System.out.print(node.data+" ");
                node = node.next;
            }
        }
        
        /**
         * 反转链表
         */
        public void reverseLink(){
            Node curNode = header;
            Node preNode = null;
            Node nextNode = null;
            while(curNode!=null){
                nextNode = curNode.next;//保留下一个节点(因为要断开下面了)
                curNode.next = preNode;//反转指针到前面
                //移动节点为了继续下一次保存、断开、反转
                preNode = curNode;
                curNode = nextNode;
            }
            header = preNode;
        }
        
        /**
         * 创建有环链表只为测试用
         */
        public void createRing(){
            header = new Node(1);
            header.next = header;
        }
        
        /**
         * 是否有环
         * 设置快指针和慢指针,慢指针每次走一步,快指针每次走两步
         * 当快指针与慢指针相等时,就说明该链表有环
         */
        public boolean isRing(){
            if(header == null){
                return false;
            }
            Node slow = header;
            Node fast = header;
            if(fast.next!=null && fast.next.next!=null){
                slow = slow.next;
                fast = fast.next.next;
                if(slow == fast){
                    return true;
                }
            }
            return false;
        }
        
        /**
         * 创建添加节点的链表方法,为测试相交
         */
        public void add(Node node){
            if(node==null){
                return;
            }
            if(header==null){
                header = node;
            }else{
                node.next = header;
                header = node;
            }
        }
        
        /**
         * 两个链表是否相交
         * 两个链表相交,则它们的尾结点一定相同,比较两个链表的尾结点是否相同即可
         */
        public static boolean isCross(Node head1,Node head2){
            if(head1==null||head2==null){
                return false;
            }
            Node temp1 = head1;
            Node temp2 = head2;
            while(temp1.next!=null){
                temp1 = temp1.next;
            }
            while(temp2.next!=null){
                temp2 = temp2.next;
            }
            return temp1 == temp2;
        }
        
         /**
         * 如果链表相交,求链表相交的起始点:
         * 1、首先判断链表是否相交,如果两个链表不相交,则求相交起点没有意义
         * 2、求出两个链表长度之差:len=length1-length2
         * 3、让较长的链表先走len步
         * 4、然后两个链表同步向前移动,没移动一次就比较它们的结点是否相等,第一个相等的结点即为它们的第一个相交点
         */
        public static Node findFirstCrossPoint(LinkList linkedList1, LinkList linkedList2){
            //链表不相交
            if(!isCross(linkedList1.header,linkedList2.header)){
                return null;
            }else{
                int length1 = linkedList1.getLength(linkedList1.header);//链表1的长度
                int length2 = linkedList2.getLength(linkedList2.header);//链表2的长度
                Node temp1 = linkedList1.header;//链表1的头结点
                Node temp2 = linkedList2.header;//链表2的头结点
                int len = length1 - length2;//链表1和链表2的长度差
                
                if(len > 0){//链表1比链表2长,链表1先前移len步        
                    for(int i=0; i<len; i++){
                        temp1 = temp1.next;
                    }
                }else{//链表2比链表1长,链表2先前移len步
                    for(int i=0; i<-len; i++){
                        temp2 = temp2.next;
                    }
                }
                //链表1和链表2同时前移,直到找到链表1和链表2相交的结点
                while(temp1 != temp2){
                    temp1 = temp1.next;
                    temp2 = temp2.next;
                }
                return temp1;
            }
        }
        
        /**
         * 查找单链表中第K个节点
         * 第二种解法是快慢指针,主要思路就是使用两个指针,先让前面的指针走到正向第k个结点,
         * 后面的指针才走,这样前后两个指针的距离差是k-1,之后前后两个指针一起向前走,
         * 前面的指针走到最后一个结点时,后面指针所指结点就是倒数第k个结点
         */
        public Node getKNode(Node head,int k){
            if(k<0||k>getLength(header)){
                return null;
            }
            Node first = head;
            Node last = head;
            for(int i=1;i<k;i++){
                first = first.next;
            }
            while(first.next!=null){
                first = first.next;
                last = last.next;
            }
            return last;
        }
        
        /**
         * 寻找单链表的中间结点:
         * 方法一、先求出链表的长度,再遍历1/2链表长度,寻找出链表的中间结点
         * 方法二、:
         * 用两个指针遍历链表,一个快指针、一个慢指针,
         * 快指针每次向前移动2个结点,慢指针一次向前移动一个结点,
         * 当快指针移动到链表的末尾,慢指针所在的位置即为中间结点所在的位置 
         */
        public Node findMiddleNode(){
            Node slowPoint = header;
            Node quickPoint = header;
            //链表结点个数为奇数时,返回的是中间结点;链表结点个数为偶数时,返回的是中间两个结点中的前一个
            while(quickPoint.next != null && quickPoint.next.next != null){
                slowPoint = slowPoint.next;
                quickPoint = quickPoint.next.next;
            }
            return slowPoint;
        }
    
    }
    
    

    测试代码如下:

    
    public class LinkTest {
    
        public static void main(String[] args) {
            // TODO Auto-generated method stub
            //测试尾插法和递归从尾达到头打印
            System.out.println("---------测试尾插法和递归打印----------");
            LinkList mLink = new LinkList();
            mLink.addNodeEnd(1);
            mLink.addNodeEnd(2);
            mLink.addNodeEnd(3);
            System.out.println(mLink.getLength(mLink.header));
            mLink.reversePrintLink1(mLink.header);
            System.out.println();
            //测试头插法和栈从尾达到头打印
            System.out.println("---------测试头插法和栈打印----------");
            LinkList mLink1 = new LinkList();
            mLink1.addNodeHead(1);
            mLink1.addNodeHead(2);
            mLink1.addNodeHead(3);
            mLink1.addNodeHead(4);
            System.out.println(mLink1.getLength(mLink1.header));
            mLink1.reversePrintLink(mLink1.header);
            System.out.println();
            //测试反转
            System.out.println("---------测试反转----------");
            LinkList mLink2 = new LinkList();
            mLink2.addNodeEnd(5);
            mLink2.addNodeEnd(6);
            mLink2.addNodeEnd(7);
            mLink2.addNodeEnd(8);
            mLink2.reverseLink();
            mLink2.printLink(mLink2.header);
            System.out.println();
            //测试有环
            System.out.println("---------测试有环----------");
            LinkList mLink3 = new LinkList();
            mLink3.createRing();
            System.out.println("是否有环:"+mLink3.isRing());
            System.out.println("---------测试相交----------");
            LinkList mLink4 = new LinkList();
            LinkList mLink5 = new LinkList();
            LinkList.Node node = new LinkList.Node(0);
            mLink4.addNodeHead(1);
            mLink5.addNodeHead(1);
            mLink4.add(node);
            mLink5.add(node);
            mLink5.addNodeHead(2);
            System.out.println("是否相交:"+LinkList.isCross(mLink4.header,mLink5.header));
            System.out.println("相交的起始节点:"+LinkList.findFirstCrossPoint(mLink4, mLink5).data);
            System.out.println("---------测试返回倒数第k个节点----------");
            LinkList mLink6 = new LinkList();
            mLink6.addNodeEnd(1);
            mLink6.addNodeEnd(2);
            mLink6.addNodeEnd(3);
            mLink6.addNodeEnd(4);
            mLink6.addNodeEnd(5);
            mLink6.addNodeEnd(6);
            System.out.println(mLink6.getKNode(mLink6.header, 2).data);
        }
    
    }
    

    打印如下:

    ---------测试尾插法和递归打印----------
    3
    3 2 1 
    ---------测试头插法和栈打印----------
    4
    1 2 3 4 
    
    ---------测试反转----------
    8 7 6 5 
    ---------测试有环----------
    是否有环:true
    ---------测试相交----------
    是否相交:true
    相交的起始节点:0
    ---------测试尾插法和递归打印----------
    5
    

    相关博文:
    java实现单链表常见操作 ----方法思路标注详细
    面试中的Java链表---这个是一个面试题一个解
    https://www.jianshu.com/p/6ebedde370b0

    相关文章

      网友评论

          本文标题:链表(Java)

          本文链接:https://www.haomeiwen.com/subject/qpayrctx.html