数据结构-链表

链表

链表以结点的方式来存储元素

每个结点包含data域和next域(指向下一个结点)

链表在内存中不一定连续存储

链表分为有头节点和没有头结点的,根据实际需求来确定

1. 单向链表

import java.util.Stack;

public class SingleLinkedList<E> {
    // 先初始化一个头结点,头结点不存放具体数据
    Node<E> head = new Node<>(null, null);

    // 定义结点
    private static class Node<E> {
        E item;
        Node<E> next;

        public Node(E item, Node<E> next) {
            this.item = item;
            this.next = next;
        }
    }

    // 判断链表是否为空
    public boolean isEmpty() {
        return head.next == null;
    }

    // 头插法添加结点
    public void addFirst(E item) {
        Node<E> first = head.next;
        // 创建一个结点,其next指向第一个结点
        Node<E> node = new Node<>(item, first);
        // head的next指向新创建的结点
        head.next = node;
    }

    // 尾插法添加结点
    public void addLast(E item) {
        Node<E> tmp = head;
        // 若当前结点的next为空,则说明是尾结点,退出循环
        while (tmp.next != null) {
            tmp = tmp.next;
        }
        tmp.next = new Node<>(item, null);
    }

    // 获取单链表中结点数量
    public int size() {
        int size = 0;
        Node<E> tmp = head;
        // 若当前结点的next为空,则说明是尾结点,退出循环
        while (tmp.next != null) {
            tmp = tmp.next;
            size++;
        }
        return size;
    }

    // 获取第index个结点
    private Node<E> getNode(int index) {
        if (index < 0 || index >= size())
            return null;
        Node<E> tmp = head.next;
        while (tmp != null && index > 0) {
            tmp = tmp.next;
            index--;
        }
        return tmp;
    }

    // 获取第index个结点的值
    public E get(int index) {
        Node<E> node = getNode(index);
        if (node == null)
            return null;
        return node.item;
    }

    // 获取指定元素的索引,若不存在,则返回-1
    public int indexOf(Object o) {
        int index = 0;
        if (o != null) {
            Node<E> tmp = head.next;
            while (tmp != null) {
                if (o.equals(tmp.item))
                    return index;
                index++;
                tmp = tmp.next;
            }
        }
        return -1;
    }

    // 在指定索引处插入元素
    public boolean insert(int index, E element) {
        // 获取插入位置的前驱结点
        Node<E> prev = getNode(index - 1);
        if (prev == null)
            return false;
        // 创建待插入结点,使其next指向prev的下一个
        Node<E> ele = new Node<>(element, prev.next);
        // 让prev的下一个指向待插入结点
        prev.next = ele;
        return true;
    }

    // 删除指定索引元素
    public E remove(int index) {
        Node<E> del;
        if (index == 0) {
            del = head.next;
            head.next = del.next;
            return del.item;
        }
        // 获取删除位置的前驱结点
        Node<E> prev = getNode(index - 1);
        if (prev == null)
            return null;
        del = prev.next;
        // 让prev的next指向待删除结点后面的结点
        prev.next = del.next;
        return del.item;
    }

    // 删除指定的第一个结点
    public E remove(Object o) {
        return remove(indexOf(o));
    }

    // 单链表反转
    public void reverse() {
        // 如果为空,或只有一个元素,则不需要反转
        if (isEmpty() || head.next.next == null)
            return;
        Node reverseHead = new Node(0, null);
        // cur指向第一个结点
        Node cur = head.next;
        while (cur != null) {
            // 在head中,每次摘掉第一个结点cur
            head.next = cur.next;
            // 将摘掉的cur采用头插法,添加到reverseHead
            cur.next = reverseHead.next;
            reverseHead.next = cur;
            // 让cur重新指向当前的第一个结点
            cur = head.next;
        }
        head = reverseHead;
    }

    // 遍历链表
    public void show() {
        // 判断链表是否为空
        if (isEmpty())
            return;
        // 头结点不能动,因此找一个辅助变量
        Node tmp = head.next;
        while (tmp != null) {
            System.out.println(tmp.item);
            tmp = tmp.next;
        }
    }

    // 递归逆序/后序遍历
    private void postorderTraversal(Node node) {
        if (node == null)
            return;
        postorderTraversal(node.next);
        System.out.println(node.item);
    }

    // 调用递归逆序/后序遍历
    public void inverseShowByRecursion() {
        postorderTraversal(head.next);
    }

    // 使用栈逆序遍历
    public void inverseShowByStack() {
        if (isEmpty())
            return;
        Stack<Node> stack = new Stack<>();
        Node cur = head.next;
        // 先前序遍历原链表,将遍历出的每一个结点入栈
        while (cur != null) {
            stack.push(cur);
            cur = cur.next;
        }
        // 依次出栈
        while (!stack.empty()) {
            System.out.println(stack.pop().item);
        }
    }
}

2. 双向链表

public class DoubleLinkedList<E> {
    // 先初始化一个头结点,头结点不存放具体数据
    public Node head = new Node(0, null, null);

    // 定义结点
    private static class Node<E> {
        E item;
        Node<E> next;
        Node<E> prev;

        public Node(E item, Node<E> next, Node<E> prev) {
            this.item = item;
            this.next = next;
            this.prev = prev;
        }
    }

    // 判断链表是否为空
    public boolean isEmpty() {
        return head.next == null;
    }

    // 添加数据到双链表尾部
    public void addLast(E item) {
        Node tmp = head;
        // 若当前结点的next为空,则说明是尾结点,退出循环
        while (tmp.next != null) {
            tmp = tmp.next;
        }
        // 创建一个结点,使其prev为tmp;并将其连接到tmp后面
        tmp.next = new Node(item, null, tmp);
    }

    // 删除第一个指定的date结点,返回是否删除成功
    public boolean remove(E item) {
        // 判断链表是否为空
        if (isEmpty())
            return false;
        // tmp指向待删除结点
        Node tmp = head.next;
        // 遍历链表,找待删除结点
        while (tmp != null) {
            // 如果找到
            if (tmp.item == item) {
                // 让tmp的前一个结点的next,指向tmp的后一个结点
                tmp.prev.next = tmp.next;
                // 如果是最后一个结点,就不需要执行
                if (tmp.next != null)
                    // 让tmp的后一个结点的prev,指向tmp的前一个结点
                    tmp.next.prev = tmp.prev;
                return true;
            }
            tmp = tmp.next;
        }
        return false;
    }

    // 遍历双向链表
    public void show() {
        // 判断链表是否为空
        if (isEmpty()) {
            System.out.println("空链表");
            return;
        }
        // 头结点不能动,因此找一个辅助变量
        Node tmp = head.next;
        while (tmp != null) {
            System.out.println(tmp.item);
            tmp = tmp.next;
        }
    }
}

3. 循环单链表解决Josephus问题

据说著名犹太历史学家Josephus有过以下的故事:在罗马人占领乔塔帕特后,39个犹太人与Josephus及他的朋友躲到一个洞中,39个犹太人决定宁愿死也不要被人抓到,于是决定了一个自杀方式,41个人排成一个圆圈,由第1个人开始报数,每报数到第3人该人就必须自杀,然后再由下一个重新报数,直到所有人都自杀身亡为止。然而Josephus和他的朋友并不想遵从,Josephus要他的朋友先假装遵从,他将朋友与自己安排在第16个与第31个位置,于是逃过了这场死亡游戏。

class CircleSingleLinkedList {
    // 先创建一个first结点
    public Node first = null;
    // 结点数量
    public int size;

    // 定义结点
    private static class Node<E> {
        E item;
        Node<E> next;

        public Node(E item, Node<E> next) {
            this.item = item;
            this.next = next;
        }
    }

    // 添加数据,构成不带头结点的单向环形链表
    public boolean addSum(int size) {
        if (size < 1)
            return false;
        this.size = size;
        Node<Integer> curNode = null;
        for (int i = 1; i <= size; i++) {
            // 创建结点,使其next指向first
            Node<Integer> node = new Node<>(i, null);
            // 如果是第一个结点
            if (i == 1) {
                // 让next指向自身,即自身构成一个环
                node.next = node;
                first = node;
                curNode = node;
            } else {
                // 让next指向first,构成环
                node.next = first;
                curNode.next = node;
                curNode = curNode.next;
            }
        }
        return true;
    }

    // 根据s,计算出圈顺序,star为从哪一个开始,offset为偏移量
    public void goOutOrder(int star, int offset) {
        if (first == null || star < 1 || star > size) {
            System.out.println("参数有误");
            return;
        }
        // 将firs移动到star
        for (int i = 0; i < star - 1; i++) {
            first = first.next;
        }
        Node<Integer> firstPre = first;
        // 将firstPre移动到firs前面
        while (true) {
            if (firstPre.next == first)
                break;
            firstPre = firstPre.next;
        }

        while (firstPre != first) {
            // 让firstPre和first移动offs - 1次
            for (int i = 0; i < offset - 1; i++) {
                first = first.next;
                firstPre = firstPre.next;
            }
            // 这时first指向的结点就是该出圈的结点
            System.out.print(first.item + "-->");
            first = first.next;
            firstPre.next = first;
        }
        // 当firstPre == first是,就是最后一个结点
        System.out.println(first.item);
    }
}

public class Josephus {
    public static void main(String[] args) {
        CircleSingleLinkedList csll = new CircleSingleLinkedList();
        System.out.println("约瑟夫问题,从1开始,数到第3个人出列:");
        csll.addSum(41);
        // 3-->6-->9-->12-->15-->18-->21-->24-->27-->30-->
        // 33-->36-->39-->1-->5-->10-->14-->19-->23-->28-->
        // 32-->37-->41-->7-->13-->20-->26-->34-->40-->8-->
        // 17-->29-->38-->11-->25-->2-->22-->4-->35-->16-->31
        csll.goOutOrder(1, 3);
    }
}
weixin073智慧旅游平台开发微信小程序+ssm后端毕业源码案例设计 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。
python017基于Python贫困生资助管理系统带vue前后端分离毕业源码案例设计 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值