import java.util.Stack;

/**
 * 单向链表CURD
 */
public class SingleLinkedListDemo {

    public static void main(String[] args) {

        // 测试
        // 先创建节点
        HeroNode hero1 = new HeroNode(1, "宋江", "及时雨");
        HeroNode hero2 = new HeroNode(2, "卢俊义", "玉麒麟");
        HeroNode hero3 = new HeroNode(3, "吴用", "智多星");
        HeroNode hero4 = new HeroNode(4, "公孙胜", "入云龙");

        // 创建单向链表
        SingleLinkedList singleLinkedList = new SingleLinkedList();

        singleLinkedList.addByOrder(hero1);
        singleLinkedList.addByOrder(hero2);
        singleLinkedList.addByOrder(hero3);
        singleLinkedList.addByOrder(hero4);
//         显示数据
        singleLinkedList.list();
        System.out.println("-------------------------------");
        HeroNode herou = new HeroNode(2, "俊义", "玉麒麟");
        singleLinkedList.update(herou);
        singleLinkedList.list();
        System.out.println("-------------------------------");
        singleLinkedList.delete(4);
//        singleLinkedList.delete(3);
//        singleLinkedList.delete(2);
//        singleLinkedList.delete(1);
        singleLinkedList.list();
    }
}

/**
 * 单向链表
 * 说明:链表的各个节点不一定是连续的(链式存储)
 */
class SingleLinkedList {
    // 先初始化头节点,头节点不要动,不存放具体的数据
    public HeroNode head = new HeroNode(0, "", "");

    /**
     * 添加节点到单向链表(直接加到尾部)
     *
     * @param heroNode
     */
    public void add(HeroNode heroNode) {
        // head不能动,需要一个辅助遍历temp
        HeroNode temp = head;
        while (temp.next != null) {
            temp = temp.next;
        }
        // 循环结束,temp到达尾部,将下一个节点设为传入的参数
        temp.next = heroNode;
    }

    /**
     * 按照编号顺序插入
     *
     * @param heroNode
     */
    public void addByOrder(HeroNode heroNode) {
        // head不能动,需要一个辅助遍历temp
        HeroNode temp = head;
        boolean flag = false; // 标识添加的编号是否存在,默认为false
        while (true) {
            if (temp.next == null) { // 边界条件独立处理
                break;
            }
            if (temp.next.no > heroNode.no) { // temp后面插入
                break;
            } else if (temp.next.no == heroNode.no) { // 已经存在,无法加入
                flag = true;
                break;
            }
            temp = temp.next; // 移动指针
        }
        if (flag) { // 无法插入
            System.out.println("无法插入~");
        } else {
            heroNode.next = temp.next;
            temp.next = heroNode;
        }

    }

    /**
     * 修改节点数据
     *
     * @param heroNode
     */
    public void update(HeroNode heroNode) {
        if (head.next == null) { // 空链表
            System.out.println("链表为空~");
            return;
        }

        // 定义辅助变量,存储头节点
        HeroNode temp = head;
        boolean flag = false;
        while (true) {
            if (temp.next == null) { //遍历结束
                break;
            }
            if (temp.no == heroNode.no) { // 找到对应位置
                flag = true;
                break;
            }
            temp = temp.next;
        }

        if (flag) {
            temp.name = heroNode.name;
            temp.nickname = heroNode.nickname;
        } else
            System.out.println("未找到需修改的数据~");
    }

    /**
     * 删除节点
     *
     * @param no
     */
    public void delete(int no) {
        HeroNode temp = head;
        boolean flag = false; // 标志是否找到待删除节点
        while (true) {
            if (temp.next == null)
                break;
            if (temp.next.no == no) { // 找到待删除节点的前一个节点temp
                flag = true;
                break;
            }

            temp = temp.next;
        }

        if (flag) {
            temp.next = temp.next.next; // 跨越待删除的数据,待删除的数据会被JVM回收
        } else
            System.out.println("待删除的节点不存在~");

    }

    /**
     * 显示链表
     */
    public void list() {
        if (head.next == null) {
            System.out.println("链表为空~");
            return;
        }

        // 记录头节点
        HeroNode temp = head.next; // 头结点指示,实际数据从head.next开始
        while (temp != null) { // 到达尾部
            System.out.println(temp); // 输出数据
            temp = temp.next; // 指针后移
        }

    }

    /**
     * 链表长度
     *
     * @param heroNode
     * @return
     */
    public static int getLength(HeroNode heroNode) {
        if (heroNode.next == null) { // 静态方法中调用静态变量
            return 0;
        }
        int length = 0;
        HeroNode temp = heroNode;
        while (temp.next != null) {
            length++;
            temp = temp.next;
        }
        return length;
    }

    /**
     * 获取链表倒数第k个节点
     *
     * @param k
     * @return
     */
    public static HeroNode queryLastIndexNode(HeroNode heroNode, int k) {
        if (heroNode.next == null) {
            return null;
        }

        int size = getLength(heroNode); // size - k
        // 校验k
        if (k <= 0 || k > size) {
            return null;
        }

        HeroNode temp = heroNode;
        for (int i = 0; i < (size - k); i++) {
            temp = temp.next;
        }

        return temp.next;
    }

    /**
     * 反转链表
     *
     * @param heroNode
     * @return
     */
    public HeroNode reverseLinkedList(HeroNode heroNode) {
        if (heroNode.next == null || heroNode.next.next == null) {
            return heroNode; // 链表不变
        }

        // 定义一个辅助的指针,帮助遍历链表
        HeroNode temp = heroNode.next;
        HeroNode next = null; // 指向当前节点(temp)的下一个节点
        HeroNode reverseHead = new HeroNode(0, "", "");

        // 遍历原来的链表,每遍历一个链表,就将其取出,并放在新的链表reverseHead的最前端
        while (temp != null) {
            next = temp.next; // 存储当前节点的下一节点
            temp.next = reverseHead.next; // 将temp的下一个节点指向新链表reverseHead的最前端
            reverseHead.next = temp; // 将temp连接到新的链表上
            temp = next; // 最后移动指针
        }

        heroNode.next = reverseHead.next;
        return heroNode;
    }

    /**
     * 倒序打印单链表
     * 方法1:反转链表再遍历(破坏原链表结构)
     * 方法2:栈实现
     *
     * @param heroNode
     */
    public void reversePrintLinkedList(HeroNode heroNode) {
        if (heroNode.next == null) {
            return;
        }

        Stack<HeroNode> stack = new Stack<HeroNode>();
        HeroNode temp = heroNode.next;
        while (temp != null) {
            stack.push(temp); // 入栈
            temp = temp.next;
        }

        while (stack.size() > 0) {
            System.out.println(stack.pop()); // 出栈
        }
    }

    /**
     * 合并两个有序链表(升序)
     *
     * @param heroNode1
     * @param heroNode2
     * @return
     */
    public HeroNode mergeLinkedList(HeroNode heroNode1, HeroNode heroNode2) {
        if (heroNode1 == null) {
            return heroNode2;
        }
        if (heroNode2 == null) {
            return heroNode1;
        }
        SingleLinkedList singleLinkedList = new SingleLinkedList(); // 用于操作链表
        while (heroNode1 != null && heroNode2 != null) {
            if (heroNode1.no > heroNode2.no) {
                singleLinkedList.add(heroNode2); // 添加到全局变量head中
                heroNode2 = heroNode2.next;
            } else {
                singleLinkedList.add(heroNode1); // 添加到全局变量head中
                heroNode1 = heroNode1.next;
            }
        }
        while (heroNode1 == null) {
            singleLinkedList.add(heroNode2);
            heroNode2 = heroNode2.next;
        }
        while (heroNode2 == null) {
            singleLinkedList.add(heroNode1);
            heroNode1 = heroNode1.next;
        }
        return head;
    }
}

/**
 * 定义HeroNode,每个HeroNode对象就是一个节点
 */
class HeroNode {
    public int no;
    public String name;
    public String nickname;
    public HeroNode next; // 指向下一个节点(为null时为链表尾部)

    /**
     * 构造方法
     *
     * @param no       编号
     * @param name     姓名
     * @param nickname 昵称
     */
    public HeroNode(int no, String name, String nickname) {
        this.no = no;
        this.name = name;
        this.nickname = nickname;
    }

    /**
     * 重写toString()方法为了显示
     *
     * @return
     */
    @Override
    public String toString() {
        return "HeroNode{" +
                "no=" + no +
                ", name='" + name + '\'' +
                ", nickname='" + nickname + '\'' +
                '}';
    }
}