Java学习笔记:数据结构之线性表(单向链表)

目录


Java数据结构之线性表

  1. 链表结构

    链式存储结构是基于指针实现,我们把一个数据元素和一个指针成为节点

    链式存储结构是用指针把相互直接关联的结点(即直接前驱节点或直接后继节点)链接起来。 链式存储结构的线性表成为链表

  2. 链表类型

    根据链表的构造方式的不同可以分为:

    ​ 1. 单向链表

    ​ 2. 循环链表

    ​ 3. 双向链表


线性表的抽象数据类型

1、线性表的置空操作clear()
2、线性表判空操作:isEmpty()
3、求线性表的长度:size( )
4、取元素操作:get( i )
5、插入操作:insert( i, x )
6、删除操作:delete( i)

单向链表

1、基本概念

​ 单向链表(单链表)是链表的一种,其特点是链表的链接方向是单向的,对链表的访问要通过顺序读取从头部开始;链表是使用指针进行构造的列表;又称为结点列表,因为链表是由一个个结点组装起来的;其中每个结点都有指针成员变量指向列表中的下一个结点;

​ 链表是由结点构成,head指针指向第一个成为表头结点,而终止于最后一个指向NULL的指针。

2、图形理解

带有头结点

  1. 头结点是为了操作的统一与方便而设立的,放在第一个元素结点之前,其数据域一般无意义(当然有些情况下也可存放链表的长度、用做监视哨等等)。
  2. 有了头结点后,对在第一个元素结点前插入结点和删除第一个结点,其操作与对其它结点的操作统一了。
  3. 首元结点也就是第一个元素的结点,它是头结点后边的第一个结点。
  4. 头结点不是链表所必需的。
  5. 在线性表的链式存储结构中,头指针是指链表指向第一个结点的指针,若链表有头结点,则头指针就是指向链表头结点的指针。
  6. 头指针具有标识作用,故常用头指针冠以链表的名字。
  7. 无论链表是否为空,头指针均不为空。头指针是链表的必要元素。
  8. 单链表也可以没有头结点。

不带头结点

3、链表特性

​ 链表的链接方向是单向的

4、优缺点(对比线性表)
优点

​ 结点创建非常方便,普通的线性内存通常在创建的时候就需要设定数据的大小

​ 结点的删除非常方便,不需要像线性结构那样移动剩下的数据

​ 结点的访问方便,可以通过循环或者递归的方法访问到任意数据,但是平均的访问效率低于线性表

缺点

​ 只能从头到尾遍历。只能找到后继,无法找到前驱,也就是只能前进

优缺点总结

​ 适用于节点的增加删除

5、代码展示
5.1、基础代码
/** 保存该链表的头结点 */
private Node head;
/** 保存该链表的尾结点 */
private Node tail;
/** 保存该链表中已包含的节点数 */
private int size;

/** 内部类 */
private class Node {
    // 保存节点的数据
    private T date;
    // 指向下个节点的引用
    private Node next;
    // 无参构造器
    public Node() {

    }
    // 初始化构造器
    public Node(T date) {
        this.date = date;
    }

    public Node(T date, Node next) {
        this.date = date;
        this.next = next;
    }
}
5.2、插入数据
a、链表为空时

链表为空

/**
 * <p>以指定数据元素来创建链表,Ps.(当前链表为空)</p>
 *
 * @param element   数据
 * @author Kallen
 * @since 2020/12/22 13:52
*/
public void OneLink(T element) {
    head = new Node(element);
    // 只有一个节点,head、tail 都指向该节点
    tail = head;
    size++;
}
b、根据索引添加

根据索引添加

/**
 * <p>根据索引添加元素</p>
 *
 * @param element       数据
 * @param index         索引
 * @author Kallen
 * @since 2020/12/22 14:22
*/
public void insert(T element, int index) {
	
    // 索引小于0或大于当前链表最大索引长度
    if (index < 0 || index > size -1) {
        throw new IndexOutOfBoundsException("索引越界");
    }

    // 是否为空链表
    if (head == null) {
        OneLink(element);
    }else {
        // index是否为0
        if (index == 0) {
            addAtHead(element);
        }else {
            // 获取插入点的前一个节点
            Node prev = getNodeByIndex(index - 1);
            // 获取新节点
            Node newNode = new Node(element);
            // prev的next指向新节点
            prev.next = newNode;
            // 新节点的next指向原prev的next节点
            newNode.next = prev.next;
            size++;
        }
    }
}
c、尾插法

尾插法

/**
 * <p>采用尾插法插入新节点</p>
 *
 * @param element       数据
 * @author Kallen
 * @since 2020/12/22 14:28
*/
public void add(T element) {
    // 该链表是否为空表
    if (head == null) {
        OneLink(element);
    } else {
        // 新建Node结点
        Node newNode = new Node(element);
        // 当前"尾节点"的"指针"指向"新节点"
        tail.next = newNode;
        // 以"新节点"作为"尾结点"
        tail = newNode;
        size++;
    }
}
d、头插法
/**
 * <p>采用头插法插入新节点</p>
 *
 * @param element       元素
 * @author Kallen
 * @since 2020/12/22 14:16
*/
public void addAtHead(T element) {
    // 创建新节点,让新节点的next指向原head,并以新节点作为head
    head = new Node(element, head);
    // 如果插入之前是空链表
    if (tail == null) {
        tail = head;
    }
    size++;
}
5.3、查询数据
a、根据索引查询
/**
 * <p>根据指定索引获取元素</p>
 *
 * @param index         索引
 * @return {@link T}    数据
 * @author Kallen
 * @since 2020/12/22 13:55
*/
public T get(int index) {
    return getNodeByIndex(index).date;
}
b、根据指定元素查找索引
/**
 * <p>根据指定元素查找索引</p>
 *
 * @param element       元素
 * @return {@link int}  索引
 * @author Kallen
 * @since 2020/12/22 14:06
*/
public int locate(T element) {

    // 从头结点开始搜索
    Node current = head;

    for (int i = 0; i < size && current != null; i++, current = current.next) {
        if (current.date.equals(element)) {
            return i;
        }
    }
    return -1;
}
c、获取当前链表长度
/**
 * <p>获取链表长度</p>
 *
 * @return {@link int}  链表长度
 * @author Kallen
 * @since 2020/12/23 13:34
*/
public int size() {
    return size;
}
5.4、删除数据
a、根据索引删除
/**
 * <p>根据索引删除元素</p>
 *
 * @param value     索引
 * @author Kallen
 * @since 2020/12/22 14:34
*/
public void delete(int value) {
    if (value < 0 || value > size -1) {
        throw new IndexOutOfBoundsException("索引越界");
    }
    // 是否为head
    if (value == 0) {
        head = head.next;
    }else {
        // 获取指定删除节点的前一个节点
        Node prev = getNodeByIndex(value - 1);
        // prev的next节点指向要删除节点的next
        prev.next = prev.next.next;
    }
    size--;
}
b、删除最后一个元素
/**
 * <p>删除最后一个元素</p>
 *
 * @author Kallen
 * @since 2020/12/22 14:43
*/
public void remove() {
    delete(size -1);
}
5.5、判断是否为空
/**
 * <p>是否为空</p>
 *
 * @author Kallen
 * @since 2020/12/22 14:45
*/
public boolean isEmpty() {
    return size == 0;
}
5.6、清空链表
/**
 * <p>清空链表</p>
 *
 * @author Kallen
 * @since 2020/12/22 14:47
 */
public void clear() {
    // head、tail、size设置为空
    head = null;
    tail = null;
    size = 0;
}
5.7、私有方法
/**
 * <p>根据指定索引获取结点</p>
 *
 * @param index						索引
 * @return {@link OneLink<T>.Node} 	结点
 * @author Kallen
 * @since 2020/12/28 14:33
*/
private Node getNodeByIndex(int index) {

    // 判断指定位置是否合法
    if (index < 0 || index > size - 1) {
        throw new IndexOutOfBoundsException("索引越界");
    }

    // 从head节点开始
    Node current = head;
    for (int i = 0; i < size && current != null; i++, current = current.next) {
        if (i == index) {
            return current;
        }
    }
    return null;
}

你知道越多,不知道的越多,感谢各位人才的:点赞、收藏和评论,我们下期见!

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值