循环链表结构是什么?
你面前有一扇们,门上面有个数字0,打开门,进去,你又看到一扇门,门上面写着1,就这样你开了十扇门,你抬头一看,门上数字写着9,你打开了写着9的这扇门,里面还有一扇门,里面这扇门赫然写着数字0,你又回到了原点。可是你一直朝着一个方向 在走呀,难道这是灵异事件,不对,恭喜你发现了程序的美丽。有一天早晨,你起床刷牙时候忽然想看看牙齿,啊~ 你张开口,发现口里面还有一个你,同样张着嘴,发出啊~ 的声音,你嘴里的你的嘴里还有一个你在张着嘴发着啊~ 你瞬间惊醒了,原来是在做梦,想明白了这个,循环链表结构差不多就该明白了吧。如果你还不明白,看着你面前的东西,对,就是面前任何的东西,你就开始想,我在看着这个东西,我在看着看着这个东西的我,我在看着看着这个东西的看着这个东西的我。。。 明白了吧。
循环链表结构?
命运之蛇咬住了自己的尾巴,世界开启了一个新纪元。
命运之蛇吃掉了自己的尾巴,世界的寿命变短了。
命运之舌长大了,世界的寿命也因此变长了。
命运之蛇吃掉了自己,世界毁灭了。
开始单向循环链表结构的构建。
底层实现:对节点类进行操作。
单向循环链表实现了List接口
public interface List<E> {
public int getSize();
public boolean isEmpty();
public void add(int index,E e);
//表头添加元素
public void addFirst(E e);
//表尾添加元素
public void addLast(E e);
//在表中根据指定角标获取元素,指定角标大于0小于角标的个数size
public E get(int index);
//获取表头元素 返回值为表头元素 0
public E getFirst();
//获取表尾元素 返回值为表尾元素 size-1
public E getLast();
//修改线性表中指定index处的元素为E 没有返回值
public void set(int index,E e);
//判断线性表中是否包含指定元素e 参数为E 返回值为boolean
public boolean contains(E e);
//在线性表中获取元素角标 从前往后找 参数为E 返回值为int 返回数据角标
public int find(E e);
//在线性表中 根据指定角标 删除元素 并返回该元素 角标范围为:0至size-1
public E remove(int index);
//在线性表中 删除表头元素 返回表头元素
public E removeFirst();
//在线性表中 删除表尾元素 返回删除元素
public E removeLast();
//在线性表中根据元素删除指定元素
public void removeElement(E e);
//清空线性表 无返回值 无参数
public void clear();
public boolean equals(Object obj);
接着看实现:
public class LoopSingle001<E> implements List<E> {
// 首先构建一个节点类
private class Node {
private E data;
private Node next;
// 初始化
public Node() {
this(null, null);// 代表调用当前对象两个参数的构造方法
}
public Node(E data, Node next) {
this.data = data;
this.next = next;
}
}
// 节点构造好了,需要对头和为进行标记
private Node head;
private Node rear;
private int size;
// 对操作工具进行实例化
public LoopSingle001() {
head = null;
rear = null;
size = 0;
}
public int getSize() {
return size;
}
public boolean isEmpty() {
return head == null && rear == null && size == 0;
// 当头元素等于空 尾元素等于空 元素个数等于0 则链表为空
}
// 根据下标插入元素
public void add(int index, E e) {
// 对index进行判断
if (index < 0 || index > size) {
throw new IllegalArgumentException("插入角标非法!");
}
Node n = new Node(e, null); // 创建一个新节点,并让它的下一跳指向空,便于头插和尾插
// 非空判断
if (isEmpty()) {
// 当元素为空时,创建一个指针指向空的新元素
head = n; // 头指向新元素
rear = n; // 尾指向新元素
rear.next = head; // 尾的下一个指向头head
size = 1;
} else { // 当元素不为空时,
if (index == 0) {
// 当index==0时插哪里?插头
// 创建一个新节点用来保存e
n.next=head;
head = n;
rear.next = head;
size++;
} else if (index == size) {
// 当index==size;往size当前位置插入,也就是尾节点后移,往尾节点的原来位置插入
// 先获取当前尾节点的前一个节点 创建一个临时节点对象用来进行遍历
// Node p = head; //没有意义
// // 同理,获取index处的前一个节点
// for (int i = 0; i < index-2; i++) { //假设你有5个元素,你从0开始遍历,循环第4遍的时候就获得了5 你要插入5
// p = p.next;
// }
// n.next=p.next;
// p.next=n;
// size++;
//如果插入位置index==size 那么插向尾部的下一个节点
rear.next=n;
n.next=head;
rear=n;
size++;
}
else {
Node p = head;
// 同理,获取index处的前一个节点
for (int i = 0; i < index-1; i++) {
p = p.next;
}
p.next=n;
n.next=p.next;
size++;
}
}
}
// 表头添加元素
public void addFirst(E e) {
add(0, e);
}
// 表尾添加元素
public void addLast(E e) {
add(size, e);
}
// 在表中根据指定角标获取元素,指定角标大于0小于角标的个数size
public E get(int index) {
if (index < 0 || index > size) {
throw new IllegalArgumentException("角标非法!");
}
// 根据index获取节点,怎么获取?从头结点0开始向后遍历,直到index处,获取。
if (index == 0)
return head.data;
if (index == size)
return rear.data;
Node p = head;
for (int i = 0; i < index; i++) { //因为我们这里有个虚拟的下标是从0开始的所以这里不减1
p = p.next;
}
return p.data;
}
// 获取表头元素 返回值为表头元素 0
public E getFirst() {
return get(0);
}
// 获取表尾元素 返回值为表尾元素 size-1
public E getLast() {
return get(size);
}
// 修改线性表中指定index处的元素为E 没有返回值
public void set(int index, E e) {
if (index < 0 || index > size) {
throw new IllegalArgumentException("角标非法!");
}
Node p = head;
if (index == 1) { //当index=1 获取第0个节点
p.data = e;
}
if (index == size) {
rear.data = e;
}
for (int i = 0; i <= index; i++) {
p = p.next;
}
p.data = e;
}
// 判断线性表中是否包含指定元素e 参数为E 返回值为boolean
public boolean contains(E e) {
if (find(e) != -1)
return true;
return false;
}
// 在线性表中获取元素角标 从前往后找 参数为E 返回值为int 返回数据角标
public int find(E e) {
Node p = head;
if(p.data==e){
return 0;
}else if(p.data==rear.data){
return size;
}else{
for (int i = 0; i < size; i++) {
p=p.next;
if (p.data == e) return i;
}
}
return -1;
}
// 在线性表中 根据指定角标 删除元素 并返回该元素 角标范围为:0至size-1
public E remove(int index) {
if (index < 0 || index > size) {
throw new IllegalArgumentException("角标非法!");
}
E e = null;
Node p = head;
if (index == 0) {
e = head.data;
head=p.next;
rear.next = p.next;
size--;
return e;
} else if (index == size) {
for (int i = 0; i < index-2; i++) { //这里为什么要减2 因为减1获取到的是最后一个值, 第一次循环过后,p获取的是虚拟下标为1的值,0直接略过了
p = p.next;
}
e = rear.data;
p.next = rear.next;
rear=p;
size--;
return e;
} else {
for (int i = 0; i < index-2; i++) { //获取index的前一个元素 这里从虚拟下标0开始,获取第index个元素,先获取第index前一个元素,循环当中,循环次数要成index-2
p = p.next;
}
e = p.next.data;
p.next = p.next.next;
size--;
return e;
}
}
// 在线性表中 删除表头元素 返回表头元素
public E removeFirst() {
return remove(0);
}
// 在线性表中 删除表尾元素 返回删除元素
public E removeLast() {
return remove(size);
}
// 在线性表中根据元素删除指定元素
public void removeElement(E e) {
int index = find(e);
remove(index+2); //这里加一是因为,我们获取的是元素下标是从0开始计算,而我们删除时,是从1开始计算
}
// 清空线性表 无返回值 无参数
public void clear() {
head = null;
rear = null;
size = 0;
}
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("LoopSingle:size=" + getSize() + "\n");
if (isEmpty()) {
sb.append("[]");
} else {
sb.append('[');
Node p = head;
while (true) {
sb.append(p.data);
if (p.next == head) {
sb.append(']');
break;
} else {
sb.append(',');
}
p = p.next;
}
}
return sb.toString();
}
}