数据结构学习3-单向链表、双向链表以及使用链表解决约瑟夫环

链表学习笔记

链表是一种使用十分频繁的数据结构,它的优点在于可以将大量的数据存储在分散的空间内,当需要插入或修改节点的时候,只需要修改节点之间的指针即可。
与之相反,数组的存储则需要连续的空间,当需要向数组中插入数据或者修改顺序的时候,需要对整个空间进行处理。
所以:我们常说 如果查找的比较频繁就使用数组,如果修改比较频繁那么建议使用链表

链表是以节点的方式来进行数据存储的,它分为带头节点的链表和不带头节点的链表

单向链表

每一个节点中都分为data域和next域 ,data域存放数据 ,next域指向下一个节点

next
next
next
head
node1
node2
node3
代码实现
public class SingleLinkedList {

    /**
     * 头节点中没有任何数据 它只是一个辅助节点
     */
    private SingleLinkedNode head;

    public SingleLinkedList() {
        head = new SingleLinkedNode(-1, null);
    }

    /**
     * 添加节点
     *
     * @param node
     */
    public void add(SingleLinkedNode node) {
        SingleLinkedNode temp = head;
        while (true) {
            if (temp.next == null) {
                break;
            }
            temp = temp.next;
        }
        temp.next = node;
    }

    /**
     * 按排序添加
     *
     * @param node
     */
    public void addByRank(SingleLinkedNode node) {
        SingleLinkedNode temp = head;
        while (true) {
            if (temp.next != null) {
                if (temp.next.rank > node.rank) {
                    node.next = temp.next;
                    temp.next = node;
                    break;
                } else {
                    temp = temp.next;
                }
            } else {
                temp.next = node;
                break;
            }
        }
    }

    /**
     * 移除节点
     *
     * @param rank
     */
    public SingleLinkedNode remove(int rank) {
        SingleLinkedNode temp = head;
        SingleLinkedNode removed = null;
        while (true) {
            if (temp.next != null) {
                if (temp.next.rank == rank) {
                    removed = temp.next;
                    temp.next = temp.next.next;
                    break;
                }
                // 往后面移动一位
                temp = temp.next;
            } else {
                // 不存在当前移除的key
                break;
            }
        }
        return removed;
    }

    public void print() {
        // 打印
        SingleLinkedNode lastNode = head.next;
        System.out.println("head");
        while (lastNode != null) {
            System.out.println("  -> " + lastNode);
            lastNode = lastNode.next;
        }
    }

    /**
     * 从尾部开始打印
     * 利用栈 先进后出的原则
     */
    public void printFromEnd() {
        Stack<String> stack = new Stack<>();
        SingleLinkedNode lastNode = head.next;
        while (lastNode != null) {
            stack.add("  -> " + lastNode);
            lastNode = lastNode.next;
        }
        System.out.println("printFromEnd");
        while (!stack.isEmpty()) {
            System.out.println(stack.pop());
        }
    }

    /**
     * 链表的有效长度
     *
     * @return
     */
    public int size() {
        int len = 0;
        SingleLinkedNode temp = head;
        while (temp.next != null) {
            len++;
            temp = temp.next;
        }
        return len;
    }

    /**
     * 从尾部开始查找到第n个节点
     *
     * @param index 下标都是从0开始计算
     * @return
     */
    public SingleLinkedNode lastIndexOf(int index) {
        // 先获取到真正的长度
        int size = size();
        if (index >= size) {
            throw new RuntimeException("out of bound:" + index + "[" + size + "]");
        }
        // 计算正向的下标
        int realIndex = size - 1 - index;
        SingleLinkedNode temp = head;
        int curIndex = -1;
        while (temp.next != null) {
            curIndex++;
            temp = temp.next;
            if (realIndex == curIndex) {
                return temp;
            }
        }
        return null;
    }

    /**
     * 反转
     * 原链表是 head - 1 -> 2 -> 5
     * 反转之后 head - 5 -> 2 -> 1
     * <p>
     * 思路:
     * tempHead -> 1
     * tempHead -> 2 -> 1
     * tempHead -> 5 -> 2 -> 1
     */
    public void reverse() {
        if (head.next == null) {
            return;
        }
        // 当前循环遍历的节点
        SingleLinkedNode cur = head.next;
        // 当前循环的节点的下一个节点
        SingleLinkedNode next;
        // 反转的临时头节点
        SingleLinkedNode reverseHead = new SingleLinkedNode(0, "");
        while (cur != null) {
            // 先获取到下一个节点
            next = cur.next;
            // 断开原本当前节点和下一个节点之间的关系 并构建新的关系
            // 这一句很巧妙 实现了将1挂在2的后面
            cur.next = reverseHead.next;
            // 将当前节点设置为反转链表的第一个节点
            reverseHead.next = cur;
            // 链表向后移动
            cur = next;

        }
        head.next = reverseHead.next;
    }

    /**
     * 合并2个有序的链表
     * @param another
     * @return
     */
    public SingleLinkedList merge( SingleLinkedList another){
        SingleLinkedNode aTemp = this.head.next;
        SingleLinkedNode bTemp = another.head.next;
        SingleLinkedList mergeList = new SingleLinkedList();
        SingleLinkedNode mergeNode = mergeList.head ;
        while(aTemp != null && bTemp != null){
            // 比较
            if(aTemp.rank > bTemp.rank){
                mergeNode.next = aTemp;
                aTemp = aTemp.next;
            }else{
                mergeNode.next = bTemp;
                bTemp = bTemp.next;
            }
            mergeNode = mergeNode.next;
        }
        if(aTemp == null){
            mergeNode.next = bTemp;
        }else {
            mergeNode.next = aTemp;
        }
        return mergeList;
    }
    static class SingleLinkedNode {

        private int rank;

        private String name;

        private SingleLinkedNode next;

        public SingleLinkedNode(int rank, String name) {
            this.rank = rank;
            this.name = name;
        }

        @Override
        public String toString() {
            return "SingleLinkedNode{" +
                    "rank=" + rank +
                    ", name='" + name + '\'' +
                    '}';
        }
    }
    public static void main(String[] args) {
        SingleLinkedList singleLinkedList = new SingleLinkedList();
        singleLinkedList.add(new SingleLinkedNode(1, "zhangsan"));
        singleLinkedList.add(new SingleLinkedNode(3, "lisi"));
        singleLinkedList.add(new SingleLinkedNode(5, "wangwu"));
        singleLinkedList.print();

        singleLinkedList.remove(4);
        singleLinkedList.print();
        singleLinkedList.addByRank(new SingleLinkedNode(4, "haha"));
        singleLinkedList.print();
        singleLinkedList.remove(4);
        singleLinkedList.print();

        System.out.println("size:" + singleLinkedList.size());

        System.out.println("\tlastIndexOf(2):" + singleLinkedList.lastIndexOf(2));
        System.out.println("\tlastIndexOf(1):" + singleLinkedList.lastIndexOf(1));
        System.out.println("\tlastIndexOf(0):" + singleLinkedList.lastIndexOf(0));

        System.out.println("reverse:");
        singleLinkedList.reverse();
        singleLinkedList.print();

        singleLinkedList.printFromEnd();

        SingleLinkedList singleLinkedList2 = new SingleLinkedList();
        singleLinkedList2.add(new SingleLinkedNode(8, "zhaoliu"));
        singleLinkedList2.add(new SingleLinkedNode(3, "songqi"));
        singleLinkedList2.add(new SingleLinkedNode(2, "songqi2"));

        System.out.println("merge:");
        singleLinkedList.merge(singleLinkedList2).print();
    }
}

双向链表

和单向链表不同的点在于双向链表除了data域和next域以外还有一个pre域,它指向当前节点的上一个节点
所以:双向链表可以自删除,可以反向查找

next
next
next
pre
pre
pre
head
node1
node2
node3
代码实现
public class DoubleLinkedList {


    private DoubleLinkedNode head;

    public DoubleLinkedList() {
        head = new DoubleLinkedNode(-1, "头节点");
    }

    public void add(DoubleLinkedNode node) {
        // 找到最后一个节点
        DoubleLinkedNode temp = head;
        while (temp.next != null) {
            temp = temp.next;
        }
        // 双向绑定
        temp.next = node;
        node.pre = temp;
    }

    public void addByRank(DoubleLinkedNode node) {
        DoubleLinkedNode temp = head;
        while (temp.next != null) {
            if (temp.next.rank > node.rank) {
                break;
            }
            temp = temp.next;
        }
        if (temp.next != null) {
            temp.next.pre = node;
            node.next = temp.next;
        }
        // 双向绑定
        temp.next = node;
        node.pre = temp;
    }

    /**
     * 移除节点
     *
     * @param rank
     */
    public DoubleLinkedNode remove(int rank) {
        DoubleLinkedNode temp = head;
        DoubleLinkedNode removed = null;
        while (true) {
            if (temp.next != null) {
                if (temp.next.rank == rank) {
                    removed = temp.next;
                    DoubleLinkedNode preNode = removed.pre;
                    DoubleLinkedNode nextNode = removed.next;
                    preNode.next = nextNode;
                    if (nextNode != null) {
                        nextNode.pre = preNode;
                    }
                    break;
                }
                // 往后面移动一位
                temp = temp.next;
            } else {
                // 不存在当前移除的key
                break;
            }
        }
        return removed;
    }


    /**
     * 打印数据
     */
    public void print() {
        System.out.println("head:");
        DoubleLinkedNode temp = head;
        while (temp.next != null) {
            System.out.println("\t -> " + temp.next);
            temp = temp.next;
        }
        System.out.println();
    }


    /**
     * 从尾部开始打印
     */
    public void printFromEnd() {
        // 首先找到最后一个节点
        DoubleLinkedNode temp = head;
        // 向后查找
        while (temp.next != null) {
            temp = temp.next;
        }
        System.out.println("printFromEnd");
        // 向前查找
        while (temp.pre != null) {
            System.out.println("\t -> " + temp);
            temp = temp.pre;
        }

    }

    /**
     * 链表的有效长度
     * @return
     */
    public int size() {
        int len = 0;
        DoubleLinkedNode temp = head;
        while (temp.next != null) {
            len++;
            temp = temp.next;
        }
        return len;
    }

    /**
     * 从尾部开始查找到第n个节点
     *
     * @param index 下标都是从0开始计算
     * @return
     */
    public DoubleLinkedNode lastIndexOf(int index) {
        // 先获取到最后一个节点
        DoubleLinkedNode temp = head;
        int len = 0;
        while (temp.next != null) {
            temp = temp.next;
            len++;
        }
        if (index >= len) {
            throw new RuntimeException("out of bound:" + index + "[" + len + "]");
        }
        // 向前找
        while (index > 0) {
            temp = temp.pre;
            index--;
        }
        return temp;
    }

    /**
     * 反转
     */
    public void reverse() {
        if(head.next == null){
            return;
        }
        // 首先需要一个临时的反转节点
        DoubleLinkedNode reverseHead = new DoubleLinkedNode(-1, "");
        // 循环原链表
        DoubleLinkedNode cur = head.next;
        while(cur!= null){
            // 先获取到下一个节点
            DoubleLinkedNode next = cur.next;
            // 反转pre和当前节点的关系
            cur.pre = cur;
            // 处理next的关系
            cur.next = reverseHead.next;
            // 将当前节点绑到新的头节点上
            reverseHead.next = cur;
            // 向后移动
            cur = next;
        }
        // 重新绑定头节点
        head.next = reverseHead.next;
        reverseHead.next.pre = head;
    }

    static class DoubleLinkedNode {

        private int rank;
        private String name;

        private DoubleLinkedNode pre;
        private DoubleLinkedNode next;

        public DoubleLinkedNode(int rank, String name) {
            this.rank = rank;
            this.name = name;
        }

        @Override
        public String toString() {
            return "DoubleLinkedNode{" +
                    "rank=" + rank +
                    ", name='" + name + '\'' +
                    '}';
        }
    }

    public static void main(String[] args) {
        DoubleLinkedList doubleLinkedList = new DoubleLinkedList();
        doubleLinkedList.add(new DoubleLinkedNode(1, "zhangsan"));
        doubleLinkedList.add(new DoubleLinkedNode(3, "lisi"));
        doubleLinkedList.add(new DoubleLinkedNode(5, "wangwu"));
        doubleLinkedList.print();

        doubleLinkedList.remove(3);
        doubleLinkedList.print();

        doubleLinkedList.addByRank(new DoubleLinkedNode(4, "zhaoliu"));
        doubleLinkedList.print();

        doubleLinkedList.printFromEnd();

        System.out.println("size:" + doubleLinkedList.size());

        System.out.println("\tlastIndexOf(2):" + doubleLinkedList.lastIndexOf(2));
        System.out.println("\tlastIndexOf(1):" + doubleLinkedList.lastIndexOf(1));
        System.out.println("\tlastIndexOf(0):" + doubleLinkedList.lastIndexOf(0));

        System.out.println("reverse:");
        doubleLinkedList.reverse();
        doubleLinkedList.print();

    }
}

使用不带头节点的单向环形链表实现约瑟夫环

单向循环链表

如图所示,不带头节点的单线环形链表中 最后一个节点的next域会指向第一个节点,所以我们可以通过这个数据结构实现约瑟夫环

next
next
next
张三
李四
王五
约瑟夫环

在这里插入图片描述

代码实现
public class Josephu {

    static class Children {

        private Child first;

        public Children(int nums) {
            addChild(nums);
        }

        public void addChild(int nums) {
            first = new Child(1);
            Child temp = first;
            for (int i = 2; i <= nums; i++) {
                Child child = new Child(i);
                temp.next = child;
                temp = child;
            }
            temp.next = first;
        }

        public void print() {
            System.out.println("head:");
            Child temp = first;
            if (temp != null) {
                System.out.println("\t -> " + temp);
                while (temp.next != first) {
                    temp = temp.next;
                    System.out.println("\t -> " + temp);
                }
            }
        }

        public void calculate(int num) {
            System.out.println("calculate split:" + num);
            int size = size();
            int splitSpace = (num - 1) % size;
            if(splitSpace == 0){
                splitSpace = size;
            }
            Child cur = first;
            Child next;
            int curIndex = 0;
            while (size > 1) {
                curIndex++;
                if (curIndex == splitSpace) {
                    // 需要移除下一个
                    next = cur.next;
                    System.out.println("\t -> "+next);
                    cur.next = next.next;
                    // 长度-1
                    size--;
                    // 重新计算间隔位置
                    splitSpace = (num - 1) % size;
                    if(splitSpace == 0){
                        splitSpace = size;
                    }
                    // 给当前节点当作最后一个
                    curIndex = 0;
                }
                cur = cur.next;
            }
            System.out.println("\t -> "+cur);
        }


        public int size() {
            if (first == null) {
                return 0;
            }
            int len = 1;
            Child temp = first;
            while (temp.next != first) {
                temp = temp.next;
                len++;
            }
            return len;
        }
    }


    static class Child {

        int no;
        Child next;
        public Child(int no) {
            this.no = no;
        }

        @Override
        public String toString() {
            return "Child{" +
                    "no=" + no +
                    '}';
        }
    }

    public static void main(String[] args) {
        Children children = new Children(5);
        children.print();
        // 1 2 3 4 5 的结果应该是 2 -> 4 -> 1 -> 5 -> 3
        children.calculate(7);

    }
}
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值