链表详解(Java实现)

二、链表

2.1 单链表

2.1.1 概述

功能:遍历打印、添加到最后、按ID大小添加、根据ID进行修改、根据ID删除节点

2.1.2 代码及详细注释

package linkedlist;

import linkedlist.pojo.User;

/**
 * @author: doudou
 * @date: Created in 2020/3/31
 * @description:
 * @version: 1.0
 */
public class UndirectionalLinkedList {
    // 头指针一开始就要初始化
    private Node head = new Node();

    /**
     * push方法,将元素放到链表的末尾
     * @param user
     */
    public void push(User user){
        Node node = new Node(user);
        Node tmp = head;
        while(true){
            if(tmp.next == null){
                break;
            }
            // 注意指针后移,防止死循环
            tmp = tmp.next;
        }
        tmp.next = node;
    }

    /**
     * 展示链表
     */
    public void show(){
        Node tmp = head.next;
        if (tmp == null) {
            System.out.println("链表为空!");
        }
        while(true){
            if(tmp == null){
                return;
            }
            System.out.println(tmp);
            tmp = tmp.next;
        }
    }

    /**
     * 按照id,升序插入到特定位置
     * 如果有重复的元素,则插入失败
     * @param user
     * @return
     */
    public boolean insert(User user) {
        Node node = new Node(user);
        Node tmp = head;
        boolean flag = false; // 默认插入失败
        while(true){
            if(tmp.next == null){
                flag = true;
                break;
            }
            if(node.user.getId() < tmp.next.user.getId()) {
                flag = true;
                break;
            }
            if(node.user.getId() == tmp.next.user.getId()) {
                break;
            }
            tmp = tmp.next;
        }
        if(flag) {
            node.next = tmp.next;
            tmp.next = node;
        }
        return flag;
    }

    /**
     * 根据user的id更新元素
     * @param user
     * @return
     */
    public boolean update(User user) {
        Node node = new Node(user);
        Node tmp = head.next;
        boolean flag = false; // 默认更新失败
        if(tmp == null){
            System.out.println("链表为空!");
            return flag;
        }
        while (true) {
            if (tmp == null) {
                break;
            }
            if (node.user.getId() == tmp.user.getId()) {
                flag = true;
                break;
            }
            tmp = tmp.next;
        }
        if(flag){
            tmp.user.setName(node.user.getName());
        }
        return flag;
    }

    /**
     * 根据id删除元素
     * @param id 
     * @return
     */
    public boolean delete(int id) {
        Node tmp = head;
        boolean flag = false; // 默认删除失败
        if(tmp.next == null) {
            System.out.println("链表为空!");
            return flag;
        }
        while(true) {
            if(tmp.next == null) {
                break;
            }
            if(tmp.next.user.getId() == id) {
                flag = true;
                break;
            }
            tmp = tmp.next;
        }
        if(flag) {
            tmp.next = tmp.next.next;
        }
        return flag;
    }

    /**
     * 内部类,作为节点,对外屏蔽
     */
    private class Node {

        private User user;
        private Node next;
        public Node(){}

        public Node(User user) {
            this.user = user;
        }

        @Override
        public String toString() {
            return "Node{" +
                    "user=" + user +
                    '}';
        }
    }
}

2.1.3 代码2.0版(完整版)

注意:完整版对show方法进行了改造,增加了入栈、出栈、单链表的翻转、反向打印、有序合并这5个方法

package linkedlist;

import linkedlist.pojo.User;

/**
 * @author: doudou
 * @date: Created in 2020/3/31
 * @description:
 * @version: 1.0
 */
public class UndirectionalLinkedList {
    // 头指针一开始就要初始化
    private Node head = new Node();

    /**
     * push方法,将元素放到链表的末尾
     * @param user
     */
    public void push(User user){
        Node node = new Node(user);
        Node tmp = head;
        while(true){
            if(tmp.next == null){
                break;
            }
            // 注意指针后移,防止死循环
            tmp = tmp.next;
        }
        tmp.next = node;
    }

    /**
     * 展示链表
     */
    private void _show(Node head){
        Node tmp = head.next;
        if (tmp == null) {
            System.out.println("链表为空!");
        }
        while(true){
            if(tmp == null){
                return;
            }
            System.out.println(tmp);
            tmp = tmp.next;
        }
    }

    /**
     * 展示链表
     */
    public void show(){
        this._show(this.head);
    }

    /**
     * 按照id,升序插入到特定位置
     * 如果有重复的元素,则插入失败
     * @param user
     * @return
     */
    public boolean insert(User user) {
        Node node = new Node(user);
        Node tmp = head;
        boolean flag = false; // 默认插入失败
        while(true){
            if(tmp.next == null){
                flag = true;
                break;
            }
            if(node.user.getId() < tmp.next.user.getId()) {
                flag = true;
                break;
            }
            if(node.user.getId() == tmp.next.user.getId()) {
                break;
            }
            tmp = tmp.next;
        }
        if(flag) {
            node.next = tmp.next;
            tmp.next = node;
        }
        return flag;
    }

    /**
     * 根据user的id更新元素
     * @param user
     * @return
     */
    public boolean update(User user) {
        Node node = new Node(user);
        Node tmp = head.next;
        boolean flag = false; // 默认更新失败
        if(tmp == null){
            System.out.println("链表为空!");
            return flag;
        }
        while (true) {
            if (tmp == null) {
                break;
            }
            if (node.user.getId() == tmp.user.getId()) {
                flag = true;
                break;
            }
            tmp = tmp.next;
        }
        if(flag){
            tmp.user.setName(node.user.getName());
        }
        return flag;
    }

    /**
     * 根据id删除元素
     * @param id
     * @return
     */
    public boolean delete(int id) {
        Node tmp = head;
        boolean flag = false; // 默认删除失败
        if(tmp.next == null) {
            System.out.println("链表为空!");
            return flag;
        }
        while(true) {
            if(tmp.next == null) {
                break;
            }
            if(tmp.next.user.getId() == id) {
                flag = true;
                break;
            }
            tmp = tmp.next;
        }
        if(flag) {
            tmp.next = tmp.next.next;
        }
        return flag;
    }

    /**
     * 翻转整个链表但是不改变原链表
     */
    private Node _reverse() {
        if(head.next == null || head.next.next == null) { // 1个或0个不翻转
            return head;
        }
        Node cur = head.next;
        Node reverseHead = new Node();
        while (cur != null) {
            Node next = cur.next;
            cur.next = reverseHead.next;
            reverseHead.next = cur;
            cur = next;
        }
        return reverseHead;
    }

    /**
     * 翻转
     */
    public void reverse() {
        Node node = this._reverse();
        head.next = node.next;
    }

    /**
     * 翻转打印
     */
    public void reversePrint() {
        if(head.next == null) {
            System.out.println("[]");
            return;
        }
        Node node = this._reverse();
        _show(node);

    }

    /**
     * 添加一个元素到链表的第一个位置,入栈
     * @param user 待入栈的元素
     */
    public void stackPush(User user) {
        Node node = new Node(user);
        node.next = head.next;
        head.next = node;
    }

    /**
     * 将链表的第一个元素出栈
     * @return
     */
    public User stackPop() {
        if (head.next == null) {
            return null;
        }
        Node res = head.next;
        head.next = res.next;

        // 复制user
        User user = new User();
        user.setName(res.user.getName());
        user.setId(res.user.getId());

        return user;
    }


    /**
     * 有序合并两个链表(两个链表都为从小到大排列的,合并后也为从小到大排列) O(n)
     * @param otherList 要与this合并的链表
     * @return 新的链表
     */
    public UndirectionalLinkedList mergeSorted(UndirectionalLinkedList otherList) {
        UndirectionalLinkedList newList = new UndirectionalLinkedList();
        if(otherList.head.next == null) {
            newList.head.next = this.head.next;
            return newList;
        }

        Node cur = this._reverse().next;
        Node otherCur = otherList._reverse().next;
        while (cur != null && otherCur != null) {
            User user = new User();
            if(cur.user.getId() > otherCur.user.getId()) {
                user.setId(cur.user.getId());
                user.setName(cur.user.getName());
                cur = cur.next;
            }else{
                user.setId(otherCur.user.getId());
                user.setName(otherCur.user.getName());
                otherCur = otherCur.next;
            }
            newList.stackPush(user);
        }

        while (cur != null) {
            User user = new User();
            user.setId(cur.user.getId());
            user.setName(cur.user.getName());
            cur = cur.next;
            newList.stackPush(user);
        }


        while (otherCur != null) {
            User user = new User();
            user.setId(otherCur.user.getId());
            user.setName(otherCur.user.getName());
            otherCur = otherCur.next;
            newList.stackPush(user);
        }

        return newList;
    }

    /**
     * 内部类,作为节点,对外屏蔽
     */
    private class Node {

        private User user;
        private Node next;
        public Node(){}

        public Node(User user) {
            this.user = user;
        }

        @Override
        public String toString() {
            return "Node{" +
                    "user=" + user +
                    '}';
        }
    }
}

2.2 双向链表

2.2.1 概述

双向链表,相比于单向链表,既能够找前驱也能够找后驱,本次实现是双向链表的增删改查

2.2.2 代码

  1. UserNode
package linkedlist.node;

import lombok.Data;

/**
 * @author: doudou
 * @date: Created in 2020/4/12
 * @description:
 * @version: 1.0
 */

@Data
public class UserNode {
    private Integer id;
    private String name;
    private UserNode next;
    private UserNode pre;

    public UserNode(Integer id, String name) {
        this.id = id;
        this.name = name;
    }

    public UserNode() {
    }

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

  1. 双向链表主体
package linkedlist.dual;

import linkedlist.node.UserNode;

/**
 * @author: doudou
 * @date: Created in 2020/4/12
 * @description: 带头结点的双向链表
 * @version: 1.0
 */
public class DualLinkedList {
    // 头结点
    private UserNode head = new UserNode();
    // 尾结点
    private UserNode tail = head;

    private int size = 0;

    /**
     * 获取双向链表有效结点的数量
     * @return 有效结点的数量
     */
    public int getSize() {
        return size;
    }

    /**
     * 判空
     * @return 如果为空,则返回为true
     */
    public boolean isEmpty() {
        return head == tail;
    }

    /**
     * 展示链表
     */
    public void show() {
        if (isEmpty()) {
            System.out.println("[]");
            return;
        }
        UserNode cur = head.getNext();
        while (cur != null) {
            System.out.println(cur);
            cur = cur.getNext();
        }
    }

    /**
     * 反向展示链表
     */
    public void reverseShow() {
        if (isEmpty()) {
            System.out.println("[]");
            return;
        }
        UserNode cur = tail;
        while (cur != head) {
            System.out.println(cur);
            cur = cur.getPre();
        }
    }

    /**
     * 增加新结点
     * @param newNode
     */
    public void add(UserNode newNode) {
        tail.setNext(newNode);
        newNode.setPre(tail);
        tail = newNode;
        size++;
    }


    /**
     * 根据ID删除结点,如果要删除的是最后一个结点,一定要小心
     * @param id 要删除结点的id
     */
    public void remove(int id) {
        if(isEmpty()) {
            System.out.println("链表为空,删除失败");
            return;
        }
        UserNode cur = head.getNext();
        boolean flag = false; // flag为false表示没有找到该id对应的结点
        while (cur != null) {
            if(cur.getId() == id) {
                flag =true;
                break;
            }
            cur = cur.getNext();
        }
        if(flag) {
            if(cur == tail) { // 如果要删除最后一个结点,需要特判
                tail = tail.getPre();
                tail.setNext(null);
                size--;
                return;
            }
            cur.getPre().setNext(cur.getNext()); // 当前结点前驱的后驱指向当前结点的后驱
            cur.getNext().setPre(cur.getPre()); // 当前结点后驱的前驱指向当前结点的前驱
            size--;
        }else {
            System.out.println("未找到ID="+id+"的结点");
        }
    }

    /**
     * 更新结点
     * @param newNode 利用id更新结点
     */
    public void update(UserNode newNode) {
        if(isEmpty()) {
            System.out.println("链表为空,无法进行更新操作");
            return;
        }
        UserNode cur = head.getNext();
        boolean flag = false; // flag为false表示没有找到该id对应的结点
        while (cur != null) {
            if(cur.getId() == newNode.getId()) {
                flag =true;
                break;
            }
            cur = cur.getNext();
        }
        if(flag) {
            cur.setName(newNode.getName());
        }else {
            System.out.println("未找到待更新结点");
        }
    }
}

2.3 循环单向链表与约瑟夫问题

2.3.1 概述

  1. 循环单向链表一般不设置头结点,删除结点需要把所有条件考虑清楚
  2. 约瑟夫问题:

n 个人围成一圈,从第一个人开始报数,数到 m 的人出列,再由下一个人重新从 1 开始报数,数到 m 的人再出圈,依次类推,直到所有的人都出圈,请输出依次出圈人的编号。

题目链接:https://www.luogu.com.cn/problem/P1996

2.3.2 AC代码

import java.util.Scanner;

/**
 * @author: doudou
 * @date: Created in 2020/4/12
 * @description:
 * @version: 1.0
 */
public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int m = sc.nextInt();
        CircleLinkedList circleLinkedList = new CircleLinkedList();
        circleLinkedList.exitFromJosepCircle(n,m);
    }
}

class CircleLinkedList {
    private JosephNode head = null;
    private JosephNode tail = null;
    private int size = 0;

    /**
     * 获取循环单向链表的长度
     * @return
     */
    public int getSize() {
        return size;
    }

    /**
     * 判空
     * @return
     */
    public boolean isEmpty() {
        return head == null;
    }

    /**
     * 打印循环链表
     */
    public void show() {
        if(isEmpty()) {
            System.out.println("[]");
            return;
        }
        JosephNode cur = head;
        while (true) {
            System.out.println(cur);
            if (cur == tail) {
                break;
            }
            cur = cur.getNext();
        }
    }


    /**
     * 增加新结点
     * @param node 新增加的结点
     */
    public void add(JosephNode node) {
        if(head == null) {
            head = node;
            tail = head;
            tail.setNext(head);
            size++;
            return;
        }
        tail.setNext(node);
        tail = node;
        tail.setNext(head);
        size++;
    }

    /**
     * 清空链表
     */
    public void clear() {
        size = 0;
        head = null;
        tail = null;
    }

    /**
     * 根据ID删除结点
     * @param id
     */
    public void delete(int id) {
        if(isEmpty()) {
            System.out.println("该链表为空,不能删除");
            return;
        }

        if(size == 1) { // 如果只有一个结点
            clear();
            return;
        }

        if (head.getId() == id && size > 1) { // 如果要删除第一个结点,特判
            head = head.getNext();
            tail.setNext(head);
            size--;
            return;
        }

        JosephNode cur = head;
        JosephNode cur4next = head.getNext();
        boolean flag = false; // 默认代表没找到结点
        while (true) {
            if(cur4next.getId() == id) {
                flag = true;
                break;
            }
            if(cur4next == tail) {
                break;
            }
            cur = cur.getNext();
            cur4next = cur4next.getNext();
        }
        if(flag) { // 找到了
            if(cur4next == tail) {
                tail = cur;
            }
            cur.setNext(cur4next.getNext());
            size--;
        }else {
            System.out.println("不存在待删除的结点");
        }
    }

    /**
     * 生成约瑟夫环
     * @param n 1-n
     */
    public void constructJosepCircle(int n) {
        for(int i=1;i<=n;i++) {
            JosephNode josephNode = new JosephNode(i);
            add(josephNode);
        }
    }

    /**
     * 打印出圈顺序
     * @param n 1-n
     * @param m 数到m出圈
     */
    public void exitFromJosepCircle(int n,int m) {
        clear();
        constructJosepCircle(n);
        JosephNode cur = head;
        while (size > 0) {
            for(int i=0;i<m-1;i++) {
                cur = cur.getNext();
            }
            System.out.printf("%d ",cur.getId());
            delete(cur.getId());
            if(size > 0) {
                cur = cur.getNext();
            }
        }
    }
}

class JosephNode {
    private int id;
    private JosephNode next;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public JosephNode getNext() {
        return next;
    }

    public void setNext(JosephNode next) {
        this.next = next;
    }

    @Override
    public String toString() {
        return "JosephNode{" +
                "id=" + id +
                '}';
    }

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

    public JosephNode() {}
}

猜你喜欢

转载自www.cnblogs.com/doubest/p/12910479.html
今日推荐