数据结构与算法--双向链表(Double Linked List)、单向环形链表(Circular Linked List)

此文章仅作为自己学习过程中的记录和总结,同时会有意地去用英文来做笔记,一些术语的英译不太准确,内容如有错漏也请多指教,谢谢!


一、双向链表-概述

-双向链表的基本组成结构:

  • Node:自定义的结点结构。
  • (Node) head:指向单链表头结点的“头指针”。

-自定义结点的基本组成结构:

  • 数据域:存放具有实际意义的数据。
  • “指针”域(next):存放一个指向下一结点的“指针”。
  • “指针”域(pre):存放一个指向上一结点的“指针”。【与单链表的区别】

数据结构与算法--双向链表(Double Linked List)、单向环形链表(Circular Linked List)_第1张图片
-优点:

  1. 遍历方向:单链表只能朝向一个方向遍历查找;而双向链表可以朝两个方向遍历查找。
  2. 结点的自我删除:单链表在进行删除操作时,无法实现自我删除,因此代码实现中我们都是通过遍历到目标结点的前一个结点时停下;而双向链表在进行删除操作时,可以实现自我删除。

-内容:

  • 构造方法。
  • add()【向链表中添加结点】
  • addByOrder()【向链表中按顺序(默认升序)添加结点】
  • update()【更新链表中已存在的某个结点的信息】
  • delete()【删除链表中已存在的的某个结点】
  • show()【打印出链表中所有元素的内容】

(关于单链表的属性、构造器、结点结构及基本方法,具体可见:数据结构与算法–单链表(Single Linked List))

(单链表相关的企业面试题,具体可见:数据结构与算法–单链表相关面试题)


二、双向链表-代码实现

  • Custom Node(自定义结点结构)
/*
 HeroNode2

 Zzay

 2021/01/20
 */
package com.zzay.linkedlist.impl;

/**
 * Define the node of the double linked list.
 *
 * @author Zzay
 * @version 2021/01/20
 */
public class HeroNode2 {
     

    private int no;

    private String name;

    private String nickname;

    protected HeroNode2 next;

    protected HeroNode2 pre; // The difference from single linked list

    public HeroNode2(int hNo, String hName, String hNickname) {
     
        no = hNo;
        name = hName;
        nickname = hNickname;
        next = null;
        pre = null;
    }

    @Override
    public String toString() {
     
        return "HeroNode1 [" +
                "number=" + no +
                ", name='" + name + '\'' +
                ", nickname='" + nickname + '\'' +
                ']';
    }

    public int getNo() {
     
        return no;
    }

    public String getName() {
     
        return name;
    }

    public String getNickname() {
     
        return nickname;
    }

    public void setName(String name) {
     
        this.name = name;
    }

    public void setNickname(String nickname) {
     
        this.nickname = nickname;
    }
}
  • Attributes and constructor
/*
 DoubleLinkedList

 Zzay

 2021/01/20
 */
package com.zzay.linkedlist.impl;


/**
 * The implementations of a double linked list.
 * 

* CONTENTS: * (1) Add nodes. * (2) Update a node's information. * (3) Delete a node from the single linked list. (注意空指针) * (4) Show the contents of the single linked list. * * @author Zzay * @version 2021/01/20 */ public class DoubleLinkedList { // The head node of the double linked list. It does not store any practical data. private HeroNode2 head = new HeroNode2(0, "", ""); /** * Instantiate a double linked list without giving any nodes. */ public DoubleLinkedList() { } /** * Instantiate a double linked list with a given head node. * * @param head The given head node */ public DoubleLinkedList(HeroNode2 head) { this.head = head; } }

  • Methods
    /**
     * Add new node into the double linked list.
     *
     * @param newNode The new node to be added
     */
    public void add(HeroNode2 newNode) {
     
        HeroNode2 temp = head;
        while (temp.next != null) {
      // When the list gets to the end.
            temp = temp.next;
        }
        temp.next = newNode;
        newNode.pre = temp;
    }

    /**
     * Add new node into the list by the ascending order of attribute "no".
     */
    public void addByOrder(HeroNode2 newNode) {
     
        // Whether the place has already been occupied.
        boolean existed = false;
        HeroNode2 temp = head;
        while (temp.next != null) {
     
            if (temp.next.getNo() > newNode.getNo()) {
      // When we find the target place.
                break;
            } else if (temp.next.getNo() == newNode.getNo()) {
       // The No has already occupied by someone.
                existed = true;
                break;
            }
            temp = temp.next;
        }
        if (existed) {
     
            System.out.printf("There's already a hero with the no. of %d, cannot add into it...", newNode.getNo());
        } else {
     
            if (temp.next != null) {
     
                newNode.next = temp.next;
                temp.next.pre = newNode;
            }
            newNode.pre = temp;
            temp.next = newNode;
        }
    }
    
    /**
     * Update a node's information, according to the new node's "no".
     *
     * @param newNode The new node that will replace the previous node
     */
    public void update(HeroNode2 newNode) {
     
        if (head.next == null) {
     
            System.out.println("The list is empty, cannot update...");
            return;
        }
        HeroNode2 temp = head.next;
        boolean findNo = false;
        while (temp != null) {
     
            if (temp == null) {
      // When the list gets to the end.
                break;
            }
            if (temp.getNo() == newNode.getNo()) {
      // When we find the target node.
                findNo = true;
                break;
            }
            temp = temp.next;
        }
        if (findNo) {
     
            temp.setName(newNode.getName());
            temp.setNickname(newNode.getNickname());
        } else {
     
            System.out.printf("There's no hero with No %d in the list...\n", newNode.getNo());
        }
    }

    /**
     * Print all the nodes in the double linked list.
     */
    public void show() {
     
        if (head.next == null) {
      // If the list is empty.
            System.out.println("The list is empty...");
            return;
        }
        HeroNode2 temp = head.next;
        while (temp != null) {
     
            System.out.println(temp);
            temp = temp.next;
        }
    }

    /**
     * Delete an existing node from the double linked list according to the given "no".
     * (Tips: especially pay attention to whether the target node is the last node or not.)
     *
     * @param no The given attribute to be compared with so that we can find the target node
     */
    public void delete(int no) {
     
        if (head.next == null) {
     
            System.out.println("The list is empty, cannot delete...");
            return;
        }
        HeroNode2 temp = head.next;
        boolean findNo = false;
        while (temp != null) {
     
            if (temp.getNo() == no) {
      // When we find the target node.
                findNo = true;
                break;
            }
            temp = temp.next;
        }
        if (findNo) {
     
            temp.pre.next = temp.next;
            // If the node to be deleted is the last one of the list,
            // then we cannot assign a pointer to it, or there will be a null pointer.
            if (temp.next != null) {
     
                temp.next.pre = temp.pre;
            }
        } else {
     
            System.out.printf("There's no hero with No %d in the list...\n", no);
        }
    }

    public HeroNode2 getHead() {
     
        return head;
    }
  • Test
/*
 DoubleLinkedListDemo

 Zzay

 2021/01/20
 */
package com.zzay.linkedlist.demo;


import com.zzay.linkedlist.impl.DoubleLinkedList;
import com.zzay.linkedlist.impl.HeroNode2;

/**
 * CONTENTS:
 * (1) Add nodes.
 * (2) Add nodes by order (ascending).
 * (3) Update a node's information.
 * (4) Delete a node from the double linked list. (注意空指针)
 * (5) Show the contents of the double linked list.
 *
 * @author Zzay
 * @version 2021/01/20
 */
public class DoubleLinkedListDemo {
     

    public static void main(String[] args) {
     

        DoubleLinkedList doubleLinkedList = new DoubleLinkedList();

//        /* Add nodes into the double linked list. */
//        doubleLinkedList.add(new HeroNode2(1, "Jay", "Yellow Chocolate"));
//        doubleLinkedList.add(new HeroNode2(2, "Kobe Bryant", "Black Mamba"));
//        doubleLinkedList.add(new HeroNode2(3, "Dirk Nowitzki", "German Race Car"));
//        doubleLinkedList.add(new HeroNode2(4, "Paul Pierce", "Truth"));

        /* Add nodes into the single linked list by order (ascending). */
        doubleLinkedList.addByOrder(new HeroNode2(1, "Jay", "Yellow Chocolate"));
        doubleLinkedList.addByOrder(new HeroNode2(4, "Paul Pierce", "Truth"));
        doubleLinkedList.addByOrder(new HeroNode2(2, "Kobe Bryant", "Black Mamba"));
        doubleLinkedList.addByOrder(new HeroNode2(3, "Dirk Nowitzki", "German Race Car"));

        /* Update a node's information. */
        doubleLinkedList.update(new HeroNode2(4, "Vince Carter", "Half man Half amazing"));
//        doubleLinkedList.update(new HeroNode1(5, "James Harden", "The beard"));

        /* Delete a node from the double linked list according to the attribute "no". */
//        doubleLinkedList.delete(2);
//        doubleLinkedList.delete(5);

        /* Show the contents of the single linked list. */
        doubleLinkedList.show();
    }
}

特别注意:addByOrder()方法中添加结点和delete()方法中结点自我删除时,要判断所操作的结点是否为链表的最后一个结点。否则,可能会出现空指针的问题。】


三、单向环形链表-概述

-环形链表的基本组成结构:

  • Node:自定义的结点结构。
  • (Node) first:指向单链表头结点的“头指针”(可有也可无)。

-自定义结点的基本组成结构:

  • 数据域:存放具有实际意义的数据。
  • “指针”域(next):存放一个指向下一结点的“指针”。

数据结构与算法--双向链表(Double Linked List)、单向环形链表(Circular Linked List)_第2张图片
数据结构与算法--双向链表(Double Linked List)、单向环形链表(Circular Linked List)_第3张图片
-优点:

  1. 遍历方向:单链表只能朝向一个方向遍历查找;而双向链表可以朝两个方向遍历查找。
  2. 结点的自我删除:单链表在进行删除操作时,无法实现自我删除,因此代码实现中我们都是通过遍历到目标结点的前一个结点时停下;而双向链表在进行删除操作时,可以实现自我删除。

-内容:

  • 构造方法。
  • add()【向链表中添加结点】
  • show()【打印出链表中所有元素的内容】

环形链表的介绍主要是用来引出约瑟夫环问题


四、单向环形链表-代码实现

  • Custom Node(自定义结点结构)
/*
 BoyNode

 Zzay

 2021/01/20
 */
package com.zzay.linkedlist.impl.circularsinglelinkedlist;


/**
 * Define the node of the circular single linked list.
 *
 * @author Zzay
 * @version 2021/01/20
 */
public class BoyNode {
     

    // The number of the boy node.
    private int no;

    // The name of the boy node.
    private String name;

    // A reference to the next node of the current node.
    protected BoyNode next;

    /**
     * Instantiate the node, and initialize the attribute "no".
     *
     * @param no The number of the node
     * @param name The name of the node
     */
    public BoyNode(int no, String name) {
     
        this.no = no;
        this.name = name;
    }

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

    public int getNo() {
     
        return no;
    }

    public void setNo(int no) {
     
        this.no = no;
    }

    public BoyNode getNext() {
     
        return next;
    }

    public void setNext(BoyNode next) {
     
        this.next = next;
    }
}
  • Attributes, Constructor, Methods
/*
 CircularSingleLinkedList

 Zzay

 2021/01/20
 */
package com.zzay.linkedlist.impl.circularsinglelinkedlist;


/**
 * The implementations of a circular single linked list.
 * 

* CONTENTS: * (1) Add nodes. * (2) Add nodes by order (ascending). * (3) Update a node's information. * (4) Delete a node from the double linked list. (注意空指针) * (5) Show the contents of the double linked list. * * @author Zzay * @version 2021/01/20 */ public class CircularSingleLinkedList { // A reference that always points to the first node of the list. private BoyNode first = null; /** * Instantiate the CircularSingleLinkedList instance. */ public CircularSingleLinkedList() { } /** * Instantiate the CircularSingleLinkedList instance, * and initialize the reference "first". * * @param node The node to be the first node of the list */ public CircularSingleLinkedList(BoyNode node) { first = node; first.next = null; } /** * Add a new node into the list. * * @param newNode The node to be added into the list */ public void add(BoyNode newNode) { // Check if the attribute "no" is valid. if (newNode.getNo() < 1) { System.out.println("The No of the boy node is invalid..."); return; } // If the list is still empty, initialize the "first" node. if (first == null) { first = newNode; first.next = first; return; } // If the list is not empty, // traverse to the end of the list, and then add the new node into the list. BoyNode cur = first; while (cur.next != first) { // Traverse until finishing a whole loop. if (cur.getNo() == newNode.getNo()) { System.out.println("There's already a boy node with the same No as the given node, cannot add..."); return; } cur = cur.next; } cur.next = newNode; newNode.next = first; } /** * Print all the nodes' information. */ public void show() { if (first == null) { System.out.println("The list is empty, cannot show..."); return; } // Way1: by using "while" loop. BoyNode cur = first; while (true) { System.out.println(cur); if (cur.next == first) { // Finish a whole loop. break; } cur = cur.next; } // Way2: by using "for" loop. // System.out.println(first); // for (BoyNode cur = first.next; cur != first; cur = cur.next) { // System.out.println(cur); // } }

  • Test
/*
 CircularSingleLinkedListDemo

 Zzay

 2021/01/20
 */
package com.zzay.linkedlist.demo;


import com.zzay.linkedlist.impl.circularsinglelinkedlist.BoyNode;
import com.zzay.linkedlist.impl.circularsinglelinkedlist.CircularSingleLinkedList;

/**
 * @author Zzay
 * @version 2021/01/20
 */
public class CircularSingleLinkedListDemo {
     

    public static void main(String[] args) {
     

        CircularSingleLinkedList circularSingleLinkedList = new CircularSingleLinkedList();

        // Add nodes into the list.
        circularSingleLinkedList.add(new BoyNode(1, "Jay"));
        circularSingleLinkedList.add(new BoyNode(2, "Dirk"));
        circularSingleLinkedList.add(new BoyNode(3, "Kobe"));
        circularSingleLinkedList.add(new BoyNode(4, "Tracy"));

        // Show the contents of the list.
        circularSingleLinkedList.show();

    }
}

你可能感兴趣的:(java,数据结构与算法,数据结构,链表,单链表,java)