链表(Java)

链表介绍

链表是有序链表,在内存中存储方式如下:
链表的示意图

  • 链表是以节点的方式来存储,是链式存储
  • 每个节点包含data域,next域:指向下一个节点。
  • 如图发现链表的各个节点不一定是连续存储。
  • 链表分带头节点的链表和没有头节点的链表,根据实际的需求来确定。

单链表(带头结点)逻辑结构示意图如下:
在这里插入图片描述

单链表

应用实例,使用单链表实现增删改查操作

首先创建一个节点类,(注:因为待会的双向链表使用的也是这个节点类,所以此类中写了两个指针,但是单向链表只需要使用一个即可)

package com.athome.linkedlist;

/**
 * Description:
 * Author:江洋大盗
 * Date:2021/1/23 10:21
 */
public class Person {
    
    
    String name;//姓名
    int no;//编号
    Person next;//下一个节点
    Person pre;//上一个节点

    public Person(String name, int no) {
    
    
        this.name = name;
        this.no = no;
    }

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

再创建单向链表:

package com.athome.linkedlist;

import java.util.Stack;

/**
 * Description:
 * Author:江洋大盗
 * Date:2021/1/23 10:48
 */
public class SingleLinkedList {
    
    
    //定义头节点,不存放任何数据,指向单链表开始的位置
    private final Person head = new Person(null, 0);

    /**
     * 无序添加,即直接加入到链表末尾
     *
     * @param person 待添加的节点
     */
    public void add(Person person) {
    
    
        if (person == null) {
    
    
            return;
        }
        //定义辅助变量帮助遍历
        Person temp = head;
        while (temp.next != null) {
    
    
            temp = temp.next;
        }
        temp.next = person;
    }

    /**
     * 有序添加,按从小到大的顺序添加
     *
     * @param person 待添加的节点
     */
    public void addByNo(Person person) {
    
    
        if (person == null) {
    
    
            return;
        }
        //定义辅助变量帮助遍历
        boolean flag = false;//标识该位置是否存在数据
        Person temp = head;
        while (temp.next != null) {
    
    
            if (temp.next.no == person.no) {
    
    
                flag = true;
                break;
            }
            if (temp.next.no > person.no) {
    
    
                break;
            }
            temp = temp.next;
        }
        if (flag) {
    
    
            System.out.println("该位置已经存在数据");
        } else {
    
    
            person.next = temp.next;
            temp.next = person;
        }
    }

    /**
     * 打印链表
     */
    public void list() {
    
    
        //判断链表是否为空
        if (head.next == null) {
    
    
            System.out.println("链表为空");
            return;
        }
        //定义辅助变量帮助遍历
        Person temp = head.next;
        while (temp != null) {
    
    
            System.out.println(temp);
            temp = temp.next;
        }
    }

    /**
     * 修改节点
     *
     * @param person 要修改的数据
     */
    public void update(Person person) {
    
    
        //判断链表是否为空
        if (head.next == null) {
    
    
            System.out.println("链表为空");
            return;
        }
        //定义辅助变量帮助遍历
        Person temp = head.next;
        boolean flag = false;//标识该数据是否存在
        while (temp != null) {
    
    
            if (temp.no == person.no) {
    
    
                flag = true;
                break;
            }
            temp = temp.next;
        }
        if (flag) {
    
    
            //说明找到了节点
            temp.name = person.name;
        } else {
    
    
            System.out.println("没有找到要修改的节点");
        }
    }

    /**
     * 删除节点
     *
     * @param no 待删除的节点编号
     */
    public void delete(int no) {
    
    
        //判断链表是否为空
        if (head.next == null) {
    
    
            System.out.println("链表为空");
            return;
        }
        //定义辅助变量帮助遍历
        Person temp = head;
        boolean flag = false;//标识该数据是否存在
        while (temp.next != null) {
    
    
            if (temp.next.no == no) {
    
    
                flag = true;
                break;
            }
            temp = temp.next;
        }
        if (flag) {
    
    
            //说明找到了要删除的节点
            temp.next = temp.next.next;
        } else {
    
    
            System.out.println("没有找到要删除的节点");
        }
    }

    /**
     * @return 返回当前链表的有效节点个数
     */
    public int size() {
    
    
        //判断链表是否为空
        if (head.next == null) {
    
    
            return 0;
        }
        int count = 0;//计数器
        Person temp = head.next;
        while (temp != null) {
    
    
            count++;
            temp = temp.next;
        }
        return count;
    }

    /**
     * 查询倒数第k个节点
     *
     * @param k 到处第k个
     * @return 返回查询结果,若查询不到返回null
     */
    public Person findPerson(int k) {
    
    
        //判断链表是否为空
        if (head.next == null) {
    
    
            return null;
        }
        int curSize = size();
        //判断k值是否符合条件
        if (k < 0 || k > curSize) {
    
    
            return null;
        }
        Person temp = head.next;
        for (int i = 0; i < curSize - k; i++) {
    
    
            temp = temp.next;
        }
        return temp;
    }

    /**
     * 反转列表
     */
    public void reserve() {
    
    
        //如果链表中没有或只有一个元素,不需要反转
        if (head.next == null || head.next.next == null) {
    
    
            return;
        }
        //定义临时头节点,注意这个思想很重要
        Person tempHead = new Person(null, 0);
        Person temp = head.next;
        Person next;//帮助temp后移
        while (temp != null) {
    
    
            next = temp.next;
            //将头节点前面的元素拼接到临时头节点的末尾,完成反转
            temp.next = tempHead.next;
            tempHead.next = temp;
            //temp后移
            temp = next;
        }
        //反转完成之后使用真正的头节点取代临时头节点
        head.next = tempHead.next;
    }

    /**
     * 将链表反转输出,注意链表本身不受影响
     */
    public void reserveOut() {
    
    
        //如果链表中没有或只有一个元素,不需要反转
        if (head.next == null || head.next.next == null) {
    
    
            return;
        }
        //利用栈先进后出的特点完成反转输出
        Stack<Person> people = new Stack<>();
        Person temp = head.next;
        while (temp != null) {
    
    
            people.push(temp);
            temp = temp.next;
        }
        while (!people.empty()) {
    
    
            System.out.println(people.pop());
        }
    }
}

主方法中测试

package com.athome.linkedlist;

import java.util.Scanner;

/**
 * Description:
 * Author:江洋大盗
 * Date:2021/1/23 10:14
 */
public class LinkedListDemo {
    
    
    public static void main(String[] args) {
    
    
        Scanner scanner = new Scanner(System.in);
        boolean flag = true;
        SingleLinkedList singleLinkedList = new SingleLinkedList();
        while (flag) {
    
    
            System.out.println("--------单向链表测试---------");
            System.out.println("add:添加");
            System.out.println("delete:删除");
            System.out.println("update:更新");
            System.out.println("list:打印链表");
            System.out.println("find:反向查询");
            System.out.println("reverse:反转链表");
            System.out.println("reverseOut:反转输出链表");
            System.out.println("exit:退出程序");
            System.out.print("请输入您的选择:");
            String choice = scanner.next();
            switch (choice) {
    
    
                case "add":
                    System.out.print("请输入用户姓名:");
                    String name = scanner.next();
                    System.out.print("请输入用户编号:");
                    int no = scanner.nextInt();
                    singleLinkedList.addByNo(new Person(name, no));
                    break;
                case "delete":
                    System.out.print("请输入待删除用户编号:");
                    no = scanner.nextInt();
                    singleLinkedList.delete(no);
                    break;
                case "update":
                    System.out.print("请输入待修改用户编号:");
                    no = scanner.nextInt();
                    System.out.print("请输入修改后用户姓名:");
                    name = scanner.next();
                    singleLinkedList.update(new Person(name, no));
                    break;
                case "find":
                    System.out.print("请输入要查找倒数第几位用户:");
                    no = scanner.nextInt();
                    Person person = singleLinkedList.findPerson(no);
                    System.out.println(person);
                    break;
                case "list":
                    singleLinkedList.list();
                    break;
                case "reverse":
                    singleLinkedList.reserve();
                    break;
                case "reverseOut":
                    singleLinkedList.reserveOut();
                    break;
                case "exit":
                    flag = false;
                    break;
            }
        }
        System.out.println("程序结束");
    }
}

双向链表

应用实例:使用单向链表完成增删改查操作

创建双向链表(注:此处节点类使用的是 上面创建的Person节点类)

package com.athome.linkedlist;

/**
 * Description:
 * Author:江洋大盗
 * Date:2021/1/23 13:57
 */
public class DoubleLinkedList {
    
    
    //头节点
    private final Person head = new Person(null, 0);

    /**
     * 有序添加,从小到大的顺序添加
     *
     * @param person 待插入的节点
     */
    public void addByNo(Person person) {
    
    
        if (person == null) {
    
    
            return;
        }
        Person temp = head;
        boolean flag = false;
        while (temp.next != null) {
    
    
            if (temp.next.no == person.no) {
    
    
                flag = true;
                break;
            }
            if (temp.next.no > person.no) {
    
    
                break;
            }
            temp = temp.next;
        }
        if (flag) {
    
    
            System.out.println("该位置已经有值");
        } else {
    
    
            if (temp.next != null) {
    
    
                temp.next.pre = person;
                person.next = temp.next;
            }
            temp.next = person;
            person.pre = temp;
        }
    }

    /**
     * 删除节点
     *
     * @param no 待删除编号
     */
    public void delete(int no) {
    
    
        if (head.next == null) {
    
    
            return;
        }
        Person temp = head.next;
        boolean flag = false;
        while (temp != null) {
    
    
            if (temp.no == no) {
    
    
                flag = true;
                break;
            }
            temp = temp.next;
        }
        if (flag) {
    
    
            temp.pre.next = temp.next;
            if (temp.next != null)
                temp.next.pre = temp.pre;
        } else {
    
    
            System.out.println("找不到要删除的节点");
        }
    }

    /**
     * @param person 封装了待修改的数据
     */
    public void update(Person person) {
    
    
        if (head.next == null || person == null) {
    
    
            return;
        }
        Person temp = head.next;
        boolean flag = false;
        while (temp != null) {
    
    
            if (temp.no == person.no) {
    
    
                flag = true;
                break;
            }
            temp = temp.next;
        }
        if (flag) {
    
    
            temp.name = person.name;
        } else {
    
    
            System.out.println("找不到要修改的节点");
        }
    }

    /**
     * 打印双向链表
     */
    public void list() {
    
    
        if (head.next == null) {
    
    
            System.out.println("链表为空");
        }
        Person temp = head.next;
        while (temp != null) {
    
    
            System.out.println(temp);
            temp = temp.next;
        }
    }
}

主方法中测试

package com.athome.linkedlist;

import java.util.Scanner;

/**
 * Description:
 * Author:江洋大盗
 * Date:2021/1/23 14:18
 */
public class DoubleLinkedListDemo {
    
    
    public static void main(String[] args) {
    
    
        Scanner scanner = new Scanner(System.in);
        boolean flag = true;
        DoubleLinkedList doubleLinkedList = new DoubleLinkedList();
        while (flag) {
    
    
            System.out.println("--------单向链表测试---------");
            System.out.println("add:添加");
            System.out.println("delete:删除");
            System.out.println("update:更新");
            System.out.println("list:打印链表");
            System.out.println("exit:退出程序");
            System.out.print("请输入您的选择:");
            String choice = scanner.next();
            switch (choice) {
    
    
                case "add":
                    System.out.print("请输入用户姓名:");
                    String name = scanner.next();
                    System.out.print("请输入用户编号:");
                    int no = scanner.nextInt();
                    doubleLinkedList.addByNo(new Person(name, no));
                    break;
                case "delete":
                    System.out.print("请输入待删除用户编号:");
                    no = scanner.nextInt();
                    doubleLinkedList.delete(no);
                    break;
                case "update":
                    System.out.print("请输入待修改用户编号:");
                    no = scanner.nextInt();
                    System.out.print("请输入修改后用户姓名:");
                    name = scanner.next();
                    doubleLinkedList.update(new Person(name, no));
                    break;
                case "list":
                    doubleLinkedList.list();
                    break;
                case "exit":
                    flag = false;
                    break;
            }
        }
        System.out.println("程序结束");
    }
}

单项环形列表解决Josephu问题

Josephu问题介绍

Josephu问题为:设编号为1,2,…n的n个人围坐一圈,约定编号为k(1<=k<=n)的人从1开始报数,数到m的那个人出列,它的下一位又从1开始报数,数到m的那个人又出列,依次类推,直到所有人出列为止,由此产生一个出队编号的序列。

代码实现

节点类

package com.athome.linkedlist;

/**
 * Description:
 * Author:江洋大盗
 * Date:2021/1/23 14:25
 */
public class Boy {
    
    
    int id;//男孩编号
    Boy next;

    public Boy(int id) {
    
    
        this.id = id;
    }

    @Override
    public String toString() {
    
    
        return "小孩{" +
                "编号:" + id +
                '}';
    }
}

环形列表解决约瑟夫问题

package com.athome.linkedlist;

/**
 * Description:
 * Author:江洋大盗
 * Date:2021/1/23 14:38
 */
public class CircleLinkedList {
    
    
    //头节点
    private Boy head = null;

    /**
     * 添加小孩
     *
     * @param nums 要添加的小孩数量
     */
    public void addBoy(int nums) {
    
    
        //如果人数小于1,直接结束方法
        if (nums < 1) {
    
    
            return;
        }
        //辅助指针帮助构建环形链表
        Boy temp = null;
        //循环构建环形链表
        for (int i = 1; i <= nums; i++) {
    
    
            Boy boy = new Boy(i);
            if (i == 1) {
    
    
                head = boy;
                head.next = head;
                temp = head;
            } else {
    
    
                temp.next = boy;
                boy.next = head;
                temp = boy;
            }
        }
    }

    /**
     * 打印列表
     */
    public void show() {
    
    
        //判断列表是否为空
        if (head == null) {
    
    
            System.out.println("当前链表为空");
            return;
        }
        //临时变量
        Boy temp = head;
        while (true) {
    
    
            System.out.println(temp);
            if (temp.next == head) {
    
    
                break;
            }
            temp = temp.next;
        }
    }

    /**
     * 解决小孩出圈问题
     *
     * @param startNum 开始报数的位置
     * @param countNum 报数的步长
     * @param nums     小孩的个数
     */
    public void outCircle(int startNum, int countNum, int nums) {
    
    
        if (startNum < 0 || head == null || startNum > nums) {
    
    
            System.out.println("参数有误");
            return;
        }
        //1.定义辅助变量,先将其移动到head节点的前一个位置
        Boy helper = head;
        while (helper.next != head) {
    
    
            helper = helper.next;
        }
        //2.小孩报数前先让helper和head前移startNum-1次
        for (int i = 0; i < startNum - 1; i++) {
    
    
            helper = helper.next;
            head = head.next;
        }
        //3.小孩报数时,让helper和head同时移动countNum-1次
        //循环出圈,直到圈中只剩下一个节点
        while (helper != head) {
    
    
            for (int i = 0; i < countNum - 1; i++) {
    
    
                helper = helper.next;
                head = head.next;
            }
            System.out.println("出圈者为:" + head);
            //将head出圈
            head = head.next;
            helper.next = head;
        }
        System.out.println("幸运儿:" + head);
    }

    public static void main(String[] args) {
    
    
        CircleLinkedList circleLinkedList = new CircleLinkedList();
        //测试addBoy方法
        circleLinkedList.addBoy(25);
        circleLinkedList.show();
        //测试outCircle方法
        circleLinkedList.outCircle(1, 2, 25);
    }
}

结语

只要能收获甜蜜,荆棘丛中也有蜜蜂忙碌的身影,未来的你一定会感谢现在努力的自己。

猜你喜欢

转载自blog.csdn.net/m0_52099927/article/details/113050552