鏈表詳解(Java實現)


單鏈表

概述

功能:遍歷打印、添加到最后、按ID大小添加、根據ID進行修改、根據ID刪除節點

代碼及詳細注釋

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.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 +
                    '}';
        }
    }
}

雙向鏈表

概述

雙向鏈表,相比於單向鏈表,既能夠找前驅也能夠找后驅,本次實現是雙向鏈表的增刪改查

代碼

  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("未找到待更新結點");
        }
    }
}

循環單向鏈表與約瑟夫問題

概述

  1. 循環單向鏈表一般不設置頭結點,刪除結點需要把所有條件考慮清楚
  2. 約瑟夫問題:

n 個人圍成一圈,從第一個人開始報數,數到 m 的人出列,再由下一個人重新從 1 開始報數,數到 m 的人再出圈,依次類推,直到所有的人都出圈,請輸出依次出圈人的編號。

題目鏈接:https://www.luogu.com.cn/problem/P1996

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() {}
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM