单链表加强

本文围绕Java中的链表数据结构展开,介绍了双链表、循环单链表和循环双链表的基本概念。阐述了双链表在删除和插入操作上的优势,以及循环链表可通过任意节点遍历所有节点的特点。还详细说明了循环双链表的实现,包括数据成员、构造函数、增删操作等,并提及了性能相关内容。

DoubleLinkedList

双链表基本概念
  1. 双链表指的是节点有next 和prev 两个指针变量,前者指向前驱节点, 后者指向后继节点
  2. 单链表删除与插入一个节点需要找到这个节点的前驱节点, 双链表删除一个节点和插入一个节点只需要找到相应的这个节点即可
  3. 双链表其他操作与单链表一致, 事实上, 双链表是单链表的一种, 可以直接复用单链表的任何代码, 但是删除与插入代码使用prev会更简单
  4. 双链表如果再加上一个附加尾节点 , 功能将更好

对于双链表而言, 如果加上尾节点, 那么尾插和尾删都会变为O(1), 其他基本操作性能基本不变

实现代码略, 因为后面会实现双向循环链表时

java LinkedList 用的是没有哨兵节点的带head 和 end的双向链表

LoopLinkedList

循环单链表基本概念
  1. 循环单链表有头节点和尾节点, head 和 end
  2. 尾节点的next 始终指向头节点
  3. 通过链表中任意一个节点都可以遍历所有的节点
  4. 遍历终止条件是 i.next == end (有附加尾节点)或 i.next == head (有数据尾节点)

对于循环单链表而言, 其性能与单链表相同。这里只针对基本操作的性能,实际上, 会有许多其他操作, 使用循环单链表的性能会变得很好

LoopDoubleLinkedList

循环双链表基本概念
  1. 循环双链表相当于循环单链表加上双链表
  2. 节点具有next 和prev, 链表有head 和end , 并且 end.next = head, head.prev = end

循环双链表的性能兼具上面二者

实现循环双链表
数据成员与构造函数
package com.weinijuan;

public class LoopDoubleLinkedList<T> implements List<T>
{
    private class Node
    {
        private T data;
        private Node next;
        private Node prev;
        public Node(T data, Node next, Node prev)
        {
            this.data = data;
            this.next = next;
            this.prev = prev;
        }
    }
    private Node head;
    private Node end;
    private int size;
    public LoopDoubleLinkedList()
    {
        head = new Node(null, null, null);
        end = new Node(null, head, head);
        head.next = end;
        head.prev = end;
    }
}

增加add方法

在这里插入图片描述

文字描述:

  1. 找到对应位置的节点 (target), 在前半段就用next , 后半段就用prev
  2. new 一个节点, 它的next 是 target, 它的prev 是 target.prev
  3. 更改target. next 和 target.prev的指向
  4. 特别注意, add 的index的取值范围是[0, size], 0是最前面, size 是最后面。这一点不同于delete/set/get
  5. 根据不同情况选择是向后插入还是向前插入元素
    @Override
    public void add(int index, T data)
    {
        if (index < 0 || index > size)
        {
            throw new IndexOutOfBoundsException();
        }
        Node target ;
        if (index <= size/2)
        {
            target = head.next;
            for (int i = 0; i < index; i++)
            {
                target = target.next;
            }
            target.prev.next = new Node(data, target, target.prev);
            target.prev = target.prev.next;
        }
        else
        {
            target = end.prev;
            for (int i = 0; i < size - index; i++)
            {
                target = target.prev;
            }
            target.next = new Node(data, target.next, target);
            target.next.next.prev = target.next;
        }
        size++;
    }

    @Override
    public void addFirst(T data)
    {
        add(0, data);
    }

    @Override
    public void addLast(T data)
    {
        add(size, data);
    }
删除操作

在这里插入图片描述

上图中data2对应的节点的next 和prev 可以不用修改。

当target的前驱节点 指向 target的后继节点 , target节点的后继节点指向target的前驱节点, 则没人指向target节点, 那么它将被垃圾回收器回收

@Override
    public T delete(int index)
    {
        if (index < 0 || index >= size)
        {
            throw new IndexOutOfBoundsException();
        }
        Node target ;
        if (index < size/2)
        {
            target = head.next;
            for (int i = 0; i < index; i++)
            {
                target = target.next;
            }
        }
        else
        {
            target = end.prev;
            for (int i = 0; i < size - index - 1; i++)
            {
                target = target.prev;
            }
        }
        T retData = target.data;
        target.prev.next = target.next;
        target.next.prev = target.prev;
        size--;
        return  retData;
    }

set/get

核心都是找到对应位置的节点

@Override
public void set(int index, T data)
{
    if (index < 0 || index >= size)
    {
        throw new IndexOutOfBoundsException();
    }
    Node target ;
    if (index < size/2)
    {
        target = head.next;
        for (int i = 0; i < index; i++)
        {
            target = target.next;
        }
    }
    else
    {
        target = end.prev;
        for (int i = 0; i < size - index - 1; i++)
        {
            target = target.prev;
        }
    }
    target.data = data;
}

@Override
public T get(int index)
{
    if (index < 0 || index >= size)
    {
        throw new IndexOutOfBoundsException();
    }
    Node target ;
    if (index < size/2)
    {
        target = head.next;
        for (int i = 0; i < index; i++)
        {
            target = target.next;
        }
    }
    else
    {
        target = end.prev;
        for (int i = 0; i < size - index - 1; i++)
        {
            target = target.prev;
        }
    }
    return target.data;
}
size/traverse/getIndex

和单链表一摸一样

测试

使用单链表的测试文件即可

性能
性能总结

(双表示双链表性质, 循环为循环链表性质)

操作时间复杂度备注
addFirstO (1)
addLastO(1)
addO(N)单链表一半(双)
setO(N)单链表一半(双)
getO(N)单链表一半(双)
getIndexO(N)单链表一半(双)
traverseO(N)任意节点都可遍历(循环)
sizeO(1)
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值