单向链表是链表的一种,它由多个结点组成,每个结点都由一个数据域和一个指针域组成,数据域用来存储数据,指针域用来指向其后继结点。链表的头结点的数据域不存储数据,指针域指向第一个真正存储数据的结点。
单向链表的API设计:
package com.jinglan.basics;
public class LinkedList<T> {
private Node head; // 记录头结点
private int N; // 记录链表的长度
private class Node {// 结点类
T item;// 存储数据
Node next;// 下一个结点
public Node(T item, Node next) {
this.item = item;
this.next = next;
}
}
// 构造方法
public LinkedList() {
// 1、初始化头结点
this.head = new Node(null, null);// 头结点不存数据,默认情况下链表为空不指向下一结点
// 2、初始化元数个数
this.N = 0;
}
// 清空链表
/*
* 原理: 当头结点不指向任何结点并且链表中元数个数为0时 当前链表为空
*/
public void clear() {
head.next = null;
this.N = 0;
}
// 获取链表的长度
/*
* 原理: 长度即为元素个数 返回N即可
*/
public int length() {
return N;
}
// 判断链表是否为空
/*
* 原理: 如果链表中没有元素,那说明当前链表为空 所以只需要判断元素个数是否为0,为0则链表为空,不为0则链表不为空
*/
public boolean isEmpty() {
return N == 0;
}
// 获取指定位置i处的元素
/*
* 原理: 通过循环,从头结点开始往后找,依次找i次,就可以找到对应的元素。
*/
public T get(int i) {
Node n = head.next;// 记录头结点
for (int index = 0; index < i; index++) {
n = n.next;// 不断地把n向后变化
}
return n.item;// 返回第i个位置处存储的元素
}
// 向链表中添加元素t
public void insert(T t) {
// 尾插
/*
* 步骤: 找到当前链表的最后一个结点 创建新结点,保存元素 让当前最后一个结点指向新结点 元素的个数+1
*/
Node n = head;// 将n的值初始化为头结点
while (n.next != null) {// 如果n的下一个结点不为null,那就一直往下循环,直到为null时跳出循环,此时n记录的就是链表的尾结点
n = n.next;
}
Node newnode = new Node(t, null);// 创建新结点
n.next = newnode;// 让它作为新的尾结点
// 链表中的元素个数加一
N++;
}
// 向指定位置i处,添加元素t
public void insert(int i, T t) {
/*
* 原理 将i位置所代表的结点的前一个结点的后继改为新结点(要插入的结点 t) 然后再把新结点的后继改为i位置所代表的结点
*/
// 1、找出i位置的前一个结点
Node pre = head;// 默认i位置的前一个结点为头结点,然后通过循环去找到并修正
for (int index = 0; index <= i - 1; i++) {// i位置的前一个结点的位置就是i-1
pre = pre.next;// 找到了就跳出循环,并且此时pre记录的就是第i-1个结点
}
// 2、找到i位置的结点
Node curr = pre.next;// i位置的结点就是pre的下一个
// 3、创建新结点,并且新结点指向原来i位置的结点
Node newNode = new Node(t, curr);
// 4、原来i位置的前一个结点指向新结点即可
pre.next = newNode;
// 5、链表元素的个数加一
N++;
}
// 删除指定位置i处的元素,并返回被删除的元素
public T remove(int i) {
/*
* 原理: 将i位置所代表的结点的前一个结点直接指向i位置的后一个结点
*/
// 1、找到i位置的前一个结点
Node pre = head;// 默认i位置的前一个结点为头结点,然后通过循环去找到并修正
for (int index = 0; index <= i - 1; i++) {
pre = pre.next;// 找到了就跳出循环,并且此时pre记录的就是第i-1个结点
}
// 2、找到i位置的结点
Node curr = pre.next;
// 3、找到i位置的后一个结点
Node nextNode = curr.next;// i的前一个结点通过循环得出了是pre,pre.next是结点,pre.next.next是i的后一个结点
// 4、让i位置的前一个结点指向i位置的后一个结点
pre.next = nextNode;
// 5、链表元素的个数减一
N--;
return curr.item;
}
// 查找元素t在链表中第一次出现的位置
public int indexOf(T t) {
/*
* 原理: 遍历链表,找到之后拿出数据(item)并将它与t相比较,如果相同返回i位置,如果没有就继续找
*/
// 从头结点开始依次找到每一个结点取出item和t比较
Node n = head;
for (int i = 0; n.next != null; i++) {
n = n.next;
if (n.item.equals(t)) {
return i;
}
}
return -1;// 当前链表中不存在与t一样的元素
}
}