Java 数据结构与算法 单链表

单链表通过结点存储数据,每一个数据都是一个结点

结点由当前数据的指针(这里可以理解为java的对象)、数据内容、下一个结点的指针三部分组成

 

单链表的结构如下图

来自于网络

看懂下面代码所需知识:java基础 

单链表结构类:Node

public class Node{
    public int id;
    public String value;

    public Node next;

    public Node(int id,String value){
        this.id = id;
        this.value = value;
    }

    @Override
    public String toString() {
        final StringBuilder sb = new StringBuilder("Node{");
        sb.append("id=").append(id);
        sb.append(", value='").append(value).append('\'');
        if(next != null)
            sb.append(", next.id=").append(next.id);
        else
            sb.append(", next.id=").append("null");
        sb.append('}');
        return sb.toString();
    }
}

单链表操作类:NodeOption

import java.util.Stack;

public class NodeOption {
    //尾部增加新的结点
    public void add(Node head,Node newNode){
        while(head.next != null){
            head = head.next;
        }
        head.next = newNode;
    }

    //删除指定结点
    public void delete(Node head,int delId){
        while (head.next != null){
            if(head.next.id == delId){
                head.next = head.next.next;
                return;
            }
            head = head.next;
        }
        System.out.println("未找到要删除的结点");
    }

    //修改替换指定结点
    public void update(Node head,Node updateNode){
        while (head.next != null){
            if(head.next.id == updateNode.id){
                updateNode.next =head.next.next;
                head.next = updateNode;
            }
            head = head.next;
        }
    }

    //打印链表
    public void display(Node head){
        while(head != null){
            System.out.println(head.toString());
            head = head.next;
        }

    }

    //查找倒数第k个链表 - 双指针实现
    public Node findback(Node head,int k){
        Node t1 = head;
        Node t2 = head;
        if(k > 0){
            for(int i = 0; i < k-1;i++){//t2先走k-1步
                if(t2.next == null){ //k比链表长
                    System.out.println("未找到,k不正确");return null;
                }
                t2 = t2.next;
            }
            while (t2.next!= null){//t1和t2一起走
                t1 = t1.next;
                t2 = t2.next;
            }
            return  t1;
        }
        System.out.println("未找到,k不正确");
        return null;//k<=0
    }

    //逆序打印链表,不改变结构 -使用栈实现
    public void backDisplay(Node head){
        Stack<Node> stack = new Stack<>();
        while (head != null){//不用head.next能保证最后一个元素入栈
            stack.push(head);
            head = head.next;
        }
        while (!stack.empty()){
            System.out.println(stack.pop());
        }
    }

    //反转链表并打印,头变尾,尾变头
    public void changeBackNode(Node head){
        Node reversed = null;//反转后的链表
        Node preNode = null;//前一个结点
        while (head != null){
            Node tempNext = head.next;//先保存下一个结点到一个临时遍历,用于移动
            if(tempNext == null)//传入的链表为null时到达最后一个结点
                reversed = head;
            head.next = preNode;//将【下一个结点】设置为【前一个结点】
            preNode = head;//每次遍历更新【前一个结点】

            head = tempNext;
        }

        //打印更改后的链表
        while (reversed!=null){
            System.out.println(reversed);
            reversed = reversed.next;
        }
    }

    //合并并打印两个有序链表 - 使用递归实现
    public Node merge(Node head1,Node head2){

        if(head1 == null) return head2;
        if(head2 == null) return head1;

        Node mergeNode = null;//保存合并后的链表

        if(head1.id <= head2.id){//就把head作为头结点
            mergeNode = head1;
            mergeNode.next = merge(head1.next,head2);
        }else{//就把head2作为头结点
            mergeNode = head2;
            mergeNode.next = merge(head1,head2.next);
        }
        return mergeNode;
    }

    //判断单链表是否有环 - 快慢双指针
    public boolean isLoop(Node head){
        if(head == null || head.next == null)//没有节点或者只有一个结点
            return false;
        Node fast = head;
        Node slow = head;
        while(true){//这里fast和slow初值一样,所以不能用while(fast!=slow)作为条件来遍历
            if(fast == null || fast.next == null)//遍历到尾部说明没有环
                return false;
            fast = fast.next.next;
            slow = slow.next;
            if(fast == slow) break;//相遇结束
        }
        return true;//相遇说明有环
    }

    //找到环的入口结点 - 快慢双指针,就是判断是否有环的基础上再用一个指针从头结点开始出发
    public Node findLoop(Node head){
        if(head == null || head.next == null)
            return null;
        Node fast = head;
        Node slow = head;
        while(true){
            if(fast == null || fast.next == null)
                return null;
            fast = fast.next.next;
            slow = slow.next;
            if(fast == slow) break;
        }
        //前面部分和查找环是一样的,目的是让fast和slow相遇
        //相遇后fast就可以不用了,将其回到头部,把slow留在原地
        //然后fast和slow同步移动,相遇则为入口节点(算法就是这么巧妙神奇)
        fast = head;
        while(fast != slow){
            fast = fast.next;
            slow = slow.next;
        }
        return fast;
    }
}

 测试类:Test

public class Test {
    public static void main(String[] args){
        NodeOption nodeoption = new NodeOption();
        //相关操作
//        Node headnode = new Node(0,"head Node");
//        init1(headnode);//初始化

//        Node newNode = new Node(6,"new Node");
//        Node updateNode = new Node(3,"update Node");

        //增删改
//        nodeoption.add(headnode,newNode);
//        nodeoption.delete(headnode,2);
//        nodeoption.update(headnode,updateNode);

        //遍历链表
//        nodeoption.display(headnode);

        //链表查询倒数第k个结点
//        System.out.println("查找结果:"+nodeoption.findback(headnode,2));

        //链表逆序打印
//        nodeoption.backDisplay(headnode);

        //链表反转
//        nodeoption.changeBackNode(headnode);

        //链表合并
//        Node headnodeA = new Node(1,"A链表heda node");
//        Node headnodeB = new Node(0,"B链表heda node");
//        init2(headnodeA,headnodeB);//初始化两个链表
//        Node megerNode = nodeoption.merge(headnodeA,headnodeB);
//        nodeoption.display(megerNode);

        //查找环的开始节点
        Node headnode = new Node(0,"可能有环的结点");
        init3(headnode);
        System.out.println("是否有环"+nodeoption.isLoop(headnode));
        System.out.println("查找结果:"+nodeoption.findLoop(headnode));
    }

    //初始化一个链表
    public static void init1(Node headnode){
        //初始化一个结点

        Node node1 = new Node(1,"node1");
        Node node2 = new Node(2,"node2");
        Node node3 = new Node(3,"node3");
        Node node4 = new Node(4,"node4");
        Node node5 = new Node(5,"node5");

        headnode.next = node1;
        node1.next = node2;
        node2.next = node3;
        node3.next = node4;
        node4.next = node5;

    }

    //初始化两个链表
    public static void init2(Node headnodeA,Node headnodeB){
        Node nodeA1 = new Node(1,"nodeA1");
        Node nodeA2 = new Node(3,"nodeA2");
        Node nodeA3 = new Node(5,"nodeA3");

        Node nodeB1 = new Node(2,"nodeB1");
        Node nodeB2 = new Node(4,"nodeB2");
        Node nodeB3 = new Node(6,"nodeB3");

        headnodeA.next = nodeA1;
        nodeA1.next = nodeA2;
        nodeA2.next = nodeA3;

        headnodeB.next = nodeB1;
        nodeB1.next = nodeB2;
        nodeB2.next = nodeB3;
    }

    /**
     * 初始化一个环形链表
     *  0 --> 1 --> 2 --> 3 --> 4 --> 5
     *                   👆           👇
     *                   👈👈👈👈👈👈👈👈
     */
    public static void init3(Node headNode){
        Node node1 = new Node(1,"node1");
        Node node2 = new Node(2,"node2");
        Node node3 = new Node(3,"node3");
        Node node4 = new Node(4,"node4");
        Node node5 = new Node(5,"node5");

        headNode.next = node1;
        node1.next = node2;
        node2.next = node3;
        node3.next = node4;
        node4.next = node5;
        node5.next = node3;
    }
}

注释中如有不解的或写错的希望留言指正

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值