Java实现单链表与循环链表

链表的引入

数组和链表的对比

  • 数组:

    访问数组时,其实是利用指针,即内存地址,直接访问对应内存地址中的数值,所以访问速度非常快。查找复杂度:O(1)

    添加元素时,需要移动大量元素,在内存中空出一个元素的空间,然后将要增加的元素放在其中。删除一个元素,同样需要移动大量元素去填掉被移动的元素。添加/删除元素的时间复杂度: O(n)

  • 链表:

    链表与数组相反,链表中的元素在内存中不是顺序存储的,而是通过存在元素中的指针联系到一起。第一个元素指向第二个,以此类推直到最后一个元素。所以查找链表中某一个元素就要从第一个元素开始找,一直到找到需要的元素。 查找复杂度:O(n)

    但是增加和删除一个元素对于链表数据结构就非常简单了,只要修改相应位置前后元素中的指针就可以了。复杂度:O(1)

    添加node:

    node.next = pre.next;
    pre.next = node;

    删除node:

    pre.next = node.next;

单链表

​ 单向链表是一种线性表,实际上是由节点(Node)组成的,一个链表拥有不定数量的节点。head为头节点,他不存放任何的数据,只是充当一个指向链表中真正存放数据的第一个节点的作用,而每个节点中都有一个next引用,指向下一个节点,就这样一节一节往下面记录,直到最后一个节点,其中的next指向null。

代码实现(已定义Node head

  • 其他功能

    /**
    * 获取链表长度
    * @Author: Wang Rui
    * @Date: 2020/10/29
    */
    int length;
    public int getLength(){
    length = 0;
    if(head == null)
    return 0;
    Node temp = head;
    while (temp != null){
    length++;
    temp = temp.next;
    }
    return length;
    }
    /**
    *获取指定位置的节点
    * @Author: Wang Rui
    * @Date: 2020/10/29
    */
    public Node getNode(int index){
    if(index < 0 && index >= getLength()){
    return null;
    }
    Node temp = head;//从头遍历找
    for(int i = 0; i < getLength(); i++, temp = temp.next){
    if(i == index){
    return temp;
    }
    }
    return null;
    }
    /**
    *判断链表是否为空
    * @Author: Wang Rui
    * @Date: 2020/10/29
    */
    public boolean isEmpty(){
    if(head == null)
    return true;
    else
    return false;
    }
    /**
    *打印链表
    * @Author: Wang Rui
    * @Date: 2020/10/29
    */
    public void printLink(){
    Node temp = head;
    while (temp != null){
    System.out.print(temp.val + " ");
    temp = temp.next;
    }
    }
  • 创建节点

    public class Node {
    public int val;
    public Node next;

    public Node() {
    }

    public Node(int val) {
    this.val = val;
    }
    }
  • 增加节点

    public void addNode(Node node){
    if(isEmpty()){
    head = node;
    }
    Node temp = head;
    while (temp.next != null){
    temp = temp.next;
    }
    temp.next = node;
    node.next = null;
    int l = getLength();
    l++;
    }
  • 插入节点

    /**
    *在指定位置插入节点
    * @Author: Wang Rui
    * @Date: 2020/10/29
    */
    public void insert(int index,Node node){
    if (index < 0 && index >= getLength()) {
    return;
    }
    if(index == 0){
    Node temp = head;
    head = node;
    node.next = temp;
    }
    //1 2 3 4 ,length=4
    if(index > 0 && index < length - 1){
    Node pre = getNode(index - 1);
    node.next = pre.next;
    pre.next = node;
    }
    }
  • 删除节点

    /**
    *删除指定位置的节点
    * @Author: Wang Rui
    * @Date: 2020/10/29
    */
    public Node delete(int index) {
    if (index < 0 && index >= getLength()) {
    return null;
    }
    //删除头节点
    if (index == 0) {
    head = head.next;
    length--;
    return head;
    }
    //删除尾节点
    if(index == getLength() - 1){
    Node newEndNode = getNode(getLength() - 2);
    newEndNode.next = getNode(index).next;
    length--;
    return head;
    }
    //删除指定位置
    if(index != 0 && index != getLength() - 1){
    getNode(index - 1).next = getNode(index + 1);
    length--;
    return head;
    }
    return null;
    }

循环链表

单链表的尾结点指向 NULL,而循环链表的尾结点指向头结点,构成环状。

代码实现:

  • 其他功能:

    /**
    * 获取指定位置的节点
    * @Author: Wang Rui
    * @Date: 2020/10/26
    */
    public Node getNodeByIndex(int index){
    if(index < 0 || index > length){
    return null;
    }
    //从头节点开始遍历链表查找
    Node curr = head;
    for (int i = 0; i<length; i++,curr = curr.next)
    if (i == index)
    return curr;
    return null;
    }
    /**
    * 获取指定位置元素的值
    * @Author: Wang Rui
    * @Date: 2020/10/26
    */
    public int getVal(int index){
    if(index < 0 && index >= length)
    throw new RuntimeException("没有找到此元素!");
    return getNodeByIndex(index).val;
    }
    /**
    * 打印链表全部节点
    * @Author: Wang Rui
    * @Date: 2020/10/26
    */
    public void show(){
    if(length > 0){
    Node temp = head;
    for(int i = 0; i < length - 1; i++){
    System.out.print(temp.val+"->");
    temp = temp.next;
    }
    System.out.println(getVal(length - 1));
    System.out.println("length = "+length);
    }
    }
  • 添加节点

    /**
    * 尾部添加元素
    * @Author: Wang Rui
    * @Date: 2020/10/26
    */
    public Node add(Node node){
    if(length == 0){
    head = node;
    head.next = head;
    }
    else{
    Node temp = head;
    //head后还有节点,继续向后
    while(temp.next != head){
    temp = temp.next;
    }
    //找到目前最后一个元素,它指向head。在它后面插入node
    temp.next = node;
    node.next = head;
    }
    length++;
    return node;
    }
    /**
    * 指定位置添加元素
    * @Author: Wang Rui
    * @Date: 2020/10/26
    */
    public Node insert(int index, Node newNode){
    if(index < 0 && index >= length){
    return null;
    }
    //index = 0,将newNode设为头节点
    if(index == 0){
    newNode.next = head;
    head = newNode;
    }
    else{
    //将newNode插入到链表中,先获取前一个和后一个节点
    Node pre = getNodeByIndex(index - 1);
    newNode.next = pre.next;
    pre.next = newNode;

    }
    length++;
    return newNode;
    }
  • 删除节点

    /**
    * 删除头节点
    * @Author: Wang Rui
    * @Date: 2020/10/26
    */
    public Node deleteHead(){
    //链表中只有一个元素
    if(length == 1){
    head = null;
    }
    if(length > 1){
    head = head.next;
    getNodeByIndex(length - 1).next = head;
    length--;
    }
    return head.next;
    }
    /**
    * 删除尾节点
    * @Author: Wang Rui
    * @Date: 2020/10/26
    */
    public Node deleteTail(){
    //链表中只有一个元素
    if(length == 1){
    head = null;
    }
    if(length > 1){
    getNodeByIndex(length - 2).next = head;
    length--;
    }
    return head;
    }
    /**
    * 删除指定位置节点
    * @Author: Wang Rui
    * @Date: 2020/10/26
    */
    public Node deleteIndex(int index){
    if(index < 0 && index >= length){
    throw new RuntimeException("没有找到该删除元素!");
    }
    if(index != 0 && index != length -1){
    getNodeByIndex(index - 1).next = getNodeByIndex(index + 1);
    length--;
    }
    return head;
    }

summary: 原理大概都懂,代码实现上有些细节要注意。讨论头节点是否为null;给定某个位置index,讨论index是否在合理范围内。

------ 本文结束感谢您的阅读 ------