目录
完整代码:
package list;
import java.util.Iterator;
/**
* \* (ง •_•)ง,加油
* \* User: ttmdawn
* \* Date: 2022/9/16
* \* Time: 11:12
* \* Description:单循环链表的基本操作
* \
*/
@SuppressWarnings("all")
public class CircularLinkedListWithHeadNodeUsingTail<T> implements Ilist<T>, Iterable<T> {
private Node<T> tail;//尾巴
private int size;
/**
* Node类
*
* @param <T>
*/
private static class Node<T> {
T data;
Node<T> next;
Node(T data, Node<T> next) {
this.data = data;
this.next = next;
}
public String toString() {//测试后面的tail有没有弄对
return String.valueOf(data);
}//测试用
}
public CircularLinkedListWithHeadNodeUsingTail() {
tail = new Node<>(null, null);// 尾结点
tail.next = tail;//循环起来
//此时的链表长度依旧为0
}
public boolean isEmpty() {
return size == 0;
}
public int size() {
return size;
}
public int indexOf(T x) {
int index = 0;
for (Node<T> p = tail.next.next; p != tail.next; p = p.next) {
if (x.equals(p.data))
return index;
++index;
}
return -1;
}
// 将索引号的合法性检查抽出作为函数
private void rangeCheckForAdd(int index) {// add用
if (index < 0 || index > size)
throw new IndexOutOfBoundsException(String.valueOf(index));
}
private void rangeCheck(int index) {// 其它函数用
if (index < 0 || index > size - 1)
throw new IndexOutOfBoundsException(String.valueOf(index));
}
public T get(int index) {
rangeCheck(index);
Node<T> p = tail.next.next;// 对比LinkListWithSize
for (int i = 0; i < index; i++) // i次
p = p.next;
return p.data;
}
/**
* add==>在最后要更改这个tail,使其一直指向末尾!
*
* @param index
* @param x
*/
public void add(int index, T x) {
rangeCheckForAdd(index);
Node<T> p = tail.next;
for (int i = 0; i < index; i++) // 找到index的前驱
p = p.next;
p.next = new Node<>(x, p.next);
if (p == tail) tail = p.next;//更改tail,使其一直指向末尾
++size;
}
public void clear() {
while (tail.next != tail) {
Node<T> q = tail.next;// q待删除结点
tail.next = q.next;// 将q从链表中移除
q.data = null;// 帮助GC
q.next = null;
}
size = 0;
}
/**
* remove
*
* @param index
* @return
*/
public T remove(int index) {
rangeCheck(index);
Node<T> p = tail.next;
for (int i = 0; i < index; i++) // 找到index的前驱
p = p.next;// p待删除结点的前驱
Node<T> q = p.next;// q待删除结点
if (q == tail) tail = p;//tail!!!
p.next = q.next;// 将待删除结点从链表中移除
T value = q.data; // q指向待删除结点
q.data = null;// 帮助GC
q.next = null;
--size;
return value;
}
/**
* mintofirst将最小元素移到最前面
* 基本思路:
* 使用链表操作,找到最小的结点的前驱结点,然后,调整链表,从链表中摘除最小结点,再把最小结点加入第1个位置
*/
public void mintofirst() {
Node<T> p = tail.next;//起始位置
Comparable<? super T> tmp = (Comparable<? super T>) p.next.data;
Node<T> t;
Node<T> min = p.next;//最小节点
Node<T> min_h = p;//最小节点的前驱节点
for (t = p.next; t != tail.next; t = t.next) {
if (tmp.compareTo(t.data) > 0) {
tmp = (Comparable<? super T>) t.data;
min = t;//找到最小的那个节点
}
}
for (t = p.next; t != min; t = t.next) {//找到最小的结点的前驱结点
min_h = min_h.next;
}
if (min == tail) {//解决在尾部的情况
Node<T> temp = tail;
tail = min_h;
tail.next = tail.next.next;
temp.next = tail.next.next;
tail.next.next = temp;
} else {//普通情况
min_h.next = min.next;
min.next = tail.next.next;
tail.next.next = min;
}
}
/**
* 合并
*
* @return
*/
public void merge(CircularLinkedListWithHeadNodeUsingTail<T> other) {
//剥离出带合并的有效节点
Node<T> p = other.tail.next.next;//第二个环形链表的第一个节点
Node<T> q = other.tail;//第二个环形链表的第二个节点
other.tail = other.tail.next;//将tail指向other的头节点
other.tail.next = other.tail;//自己循环,将剩下的待合并节点剥离出
//合并两个环形链表
q.next = tail.next;//将other原赖的尾节点连接到头节点
tail.next = p;
//更改尾指针的位置
tail = q;
//更改size大小
size = size + other.size;
other.size = 0;
}
public String toString() {
StringBuilder str = new StringBuilder();
for (T elem : this)// 利用了实现了的迭代器
str.append(elem + " ");
return str.toString();
}
public Iterator<T> iterator() {
return new Itr();
}
private class Itr implements Iterator<T> {
private Node<T> currentNode;
public Itr() {
currentNode = tail.next.next;//!!!
}
public boolean hasNext() {
return currentNode != tail.next;//!!!
}
public T next() {
T data = currentNode.data;
currentNode = currentNode.next;
return data;
}
}
}
分解部分:
图解:
属性:
private Node<T> tail;//尾巴
private int size;
使用tail(尾节点/尾指针),tail.next即为head头节点,此处的head为空,是一个伪节点,tail.next.next指向第一个元素。
tail为尾节点,永远指向最后一个元素。
Node类:
/**
* Node类
*
* @param <T>
*/
private static class Node<T> {
T data;
Node<T> next;
Node(T data, Node<T> next) {
this.data = data;
this.next = next;
}
public String toString() {//测试后面的tail有没有弄对
return String.valueOf(data);
}//测试用
}
构造方法:
public CircularLinkedListWithHeadNodeUsingTail() {
tail = new Node<>(null, null);// 尾结点
tail.next = tail;//循环起来
//此时的链表长度依旧为0
}
初始化时,tail为空,tail.next指向tail,形成循环
isEmpty、size、indexof、get方法:
public boolean isEmpty() {
return size == 0;
}
public int size() {
return size;
}
public int indexOf(T x) {
int index = 0;
for (Node<T> p = tail.next.next; p != tail.next; p = p.next) {
if (x.equals(p.data))
return index;
++index;
}
return -1;
}
public T get(int index) {
rangeCheck(index);
Node<T> p = tail.next.next;// 对比LinkListWithSize
for (int i = 0; i < index; i++) // i次
p = p.next;
return p.data;
}
add、clear方法:
/**
* add==>在最后要更改这个tail,使其一直指向末尾!
*
* @param index
* @param x
*/
public void add(int index, T x) {
rangeCheckForAdd(index);
Node<T> p = tail.next;
for (int i = 0; i < index; i++) // 找到index的前驱
p = p.next;
p.next = new Node<>(x, p.next);
if (p == tail) tail = p.next;//更改tail,使其一直指向末尾
++size;
}
public void clear() {
while (tail.next != tail) {
Node<T> q = tail.next;// q待删除结点
tail.next = q.next;// 将q从链表中移除
q.data = null;// 帮助GC
q.next = null;
}
size = 0;
}
add与后面的remove在执行时都要注意tail永远指向最后一个节点
链表增加或减少都要注意size的变化
remove方法:
/**
* remove
*
* @param index
* @return
*/
public T remove(int index) {
rangeCheck(index);
Node<T> p = tail.next;
for (int i = 0; i < index; i++) // 找到index的前驱
p = p.next;// p待删除结点的前驱
Node<T> q = p.next;// q待删除结点
if (q == tail) tail = p;//tail!!!
p.next = q.next;// 将待删除结点从链表中移除
T value = q.data; // q指向待删除结点
q.data = null;// 帮助GC
q.next = null;
--size;
return value;
}
清除不要忘记帮助GC,即清除内存
mintofirst将最小的元素移动到最前面:
/**
* mintofirst将最小元素移到最前面
* 基本思路:
* 使用链表操作,找到最小的结点的前驱结点,然后,调整链表,从链表中摘除最小结点,再把最小结点加入第1个位置
*/
public void mintofirst() {
Node<T> p = tail.next;//起始位置
Comparable<? super T> tmp = (Comparable<? super T>) p.next.data;
Node<T> t;
Node<T> min = p.next;//最小节点
Node<T> min_h = p;//最小节点的前驱节点
for (t = p.next; t != tail.next; t = t.next) {
if (tmp.compareTo(t.data) > 0) {
tmp = (Comparable<? super T>) t.data;
min = t;//找到最小的那个节点
}
}
for (t = p.next; t != min; t = t.next) {//找到最小的结点的前驱结点
min_h = min_h.next;
}
if (min == tail) {//解决在尾部的情况
Node<T> temp = tail;
tail = min_h;
tail.next = tail.next.next;
temp.next = tail.next.next;
tail.next.next = temp;
} else {//普通情况
min_h.next = min.next;
min.next = tail.next.next;
tail.next.next = min;
}
}
基本思路:
使用链表操作,找到最小的结点的前驱结点,然后,调整链表,从链表中摘除最小结点,再把最小结点加入第1个位置
merge将两个环形链表合并:
/**
* 合并
*
* @return
*/
public void merge(CircularLinkedListWithHeadNodeUsingTail<T> other) {
//剥离出带合并的有效节点
Node<T> p = other.tail.next.next;//第二个环形链表的第一个节点
Node<T> q = other.tail;//第二个环形链表的第二个节点
other.tail = other.tail.next;//将tail指向other的头节点
other.tail.next = other.tail;//自己循环,将剩下的待合并节点剥离出
//合并两个环形链表
q.next = tail.next;//将other原赖的尾节点连接到头节点
tail.next = p;
//更改尾指针的位置
tail = q;
//更改size大小
size = size + other.size;
other.size = 0;
}
思路看代码注释;
上课讲解视频:阿里云盘分享 (aliyundrive.com)
迭代器:
public String toString() {
StringBuilder str = new StringBuilder();
for (T elem : this)// 利用了实现了的迭代器
str.append(elem + " ");
return str.toString();
}
public Iterator<T> iterator() {
return new Itr();
}
private class Itr implements Iterator<T> {
private Node<T> currentNode;
public Itr() {
currentNode = tail.next.next;//!!!
}
public boolean hasNext() {
return currentNode != tail.next;//!!!
}
public T next() {
T data = currentNode.data;
currentNode = currentNode.next;
return data;
}
}