链表(Java)

2019-07-28  本文已影响0人  dlihasa

链表常见的操作:

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

上一篇下一篇

猜你喜欢

热点阅读