双向循环链表实现
1.1 基本介绍
双向循环链表就是在双线链表的基础上首尾相连(第一个节点的prev指向最后一个节点,最后一个节点的next指向第一个节点)。
1.2 添加操作
1、思路分析
头部插入
当整个链表都为空时,添加操作。
头结点和尾节点都指向自己。
当链表不为空时,添加操作
先把当前头节点的上一跳地址给新元素的上一跳
然后让新节点的后驱指针指向head
结点,再让head
的前驱指针指向新元素。
更新head
结点,让head
结点指向新结点,更新tail
结点,让tail
的下一跳重新指向head
。
尾部插入
将当前tail
的下一跳给新节点的下一跳
让tail
的下一跳指向新结点,新结点的上一跳指向tail
。
tail
重新指向新结点。
直接让head
的上一跳重新指向tail
。
中间插入
1、离头部比较近
定义两个指针p
,q
,找到要插入节点前驱,让p
的上一跳指向新节点,让新节点的下一跳指向p
。
让q
的上一跳指向新节点,让新节点的下一跳指向q
。
2、离尾部比较近
让q
的下一跳指向新节点,让新节点的上一跳指向q
。
让p
的上一跳指向新节点,让新节点的下一跳指向p
。
2、代码示例
链表类: LinkedList
package cn.linkedlist.demo05;
import java.util.Iterator;
public class LinkedList<E> implements List<E>{
// 创建Node节点
private class Node {
// 数据域
E data;
// 指向直接前驱的指针
Node prev;
// 指向直接后继的指针
Node next;
// 构造函数
public Node() {
this(null, null, null);
}
public Node(E data) {
this(data, null, null);
}
public Node(E data, Node prev, Node next) {
this.data = data;
this.prev = prev;
this.next = next;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
if (prev != null) {
sb.append(prev.data);
} else {
sb.append("null");
}
sb.append("->").append(data).append("->");
if (next != null) {
sb.append(next.data);
} else {
sb.append("null");
}
return sb.toString();
}
}
// 链表元素的数量
private int size;
// 声明头结点
private Node head;
// 声明尾节点
private Node tail;
// 初始化头结点
public LinkedList() {
head = null;
tail = null;
size = 0;
}
public LinkedList(E[] arr) {
for (E e : arr) {
add(e);
}
}
//默认向表尾添加元素
@Override
public void add(E element) {
add(size, element);
}
//在链表当中指定索引index处添加一个元素
@Override
public void add(int index, E element) {
if (index < 0|| index > size) {
throw new ArrayIndexOutOfBoundsException("add index out of bounds");
}
// 创建新的结点对象
Node node = new Node(element);
// 链表为空
if(isEmpty()){
head = node;
tail = node;
tail.next = head;
head.prev = tail;
}else if(index == 0){ // 在链表头部添加元素
// 头结点的上一跳指向新节点的上一跳
node.prev = head.prev;
node.next = head;
head.prev = node;
head = node;
tail.next = head;
}else if(index == size){ // 在链表尾部添加元素
node.next = tail.next;
tail.next = node;
node.prev = tail;
tail = node;
head.prev = tail;
}else{
// 在链表中添加元素
Node p,q; // 定义两个指针变量
if(index <= size / 2){
p = head;
for(int i =0; i < index -1 ; i++){
p = p.next;
}
q = p.next;
p.next = node;
node.prev = p;
q.prev = node;
node.next = q;
}else{
p = tail;
for(int i=size -1; i > index; i--){
p = p.prev;
}
q = p.prev;
q.next = node;
node.prev = q;
p.prev = node;
node.next = p;
}
}
size++;
}
@Override
public int size() {
return size;
}
//查找元素在链表中第一次出现的索引
@Override
public int indexOf(E element) {
if(isEmpty()){
return -1;
}
Node p = head;
int index = 0;
while (!p.data.equals(element)){
p = p.next;
index++;
if(p == head){
return -1;
}
}
return index;
}
//在链表中判断是否包含元素element
@Override
public boolean contains(E element) {
return indexOf(element) != -1;
}
@Override
public boolean isEmpty() {
return size== 0 && head == null && tail == null;
}
@Override
public void clear() {
head = null;
tail = null;
size = 0;
}
@Override
public String toString() {
StringBuilder res = new StringBuilder();
res.append("size=").append(size).append(", [");
Node node = head;
for (int i = 0; i < size; i++) {
if (i != 0) {
res.append(", ");
}
res.append(node);
node = node.next;
}
res.append("]");
return res.toString();
}
@Override
public Iterator<E> iterator() {
return new DoubleCircleLinkedListIterator();
}
class DoubleCircleLinkedListIterator implements Iterator<E>{
private Node cur = head;
private boolean flag = true;
@Override
public boolean hasNext() {
if(isEmpty()){
return false;
}
return flag;
}
@Override
public E next() {
E ret = cur.data;
cur = cur.next;
if(cur == head){
flag = false;
}
return ret;
}
}
}
测试类:LinkedListDemo
package cn.linkedlist.demo05;
public class LinkedListDemo {
public static void main(String[] args) {
LinkedList<Integer> list = new LinkedList<>();
System.out.println("===链表头部插入===");
// System.out.println(list);
list.add(0, 1);
list.add(0, 2);
list.add(0, 3);
System.out.println(list);
System.out.println("===链表尾部插入===");
list.add(list.size(), 4);
list.add(list.size(), 5);
list.add(list.size(), 6);
System.out.println(list);
}
}
3、执行结果
1.3 删除操作
1、思路分析
删除头结点
node
节点为head.next
,最终node
是新的头结点。
head
的下一跳置空。
head
的上一跳,给node
的上一跳。
head
的上一跳置空!!head
重新指向node
。
最后让尾指针下一跳重新指向head
即可。
删除尾节点
node
为tail.pre
前驱,最终node
是新的尾结点。
让tail
的上一跳置空。
tail
的下一跳给node
的下一跳,tail
的下一跳置空。
tail
重新指向node
。
head
的上一跳重新指向tail
。
删除中间节点
1、离头部比较近
定义三个指针,p
是要删除节点的前驱,q
是要删除节点,r
是要删除节点的后继,p指针移动到要删除节点的前驱。
让p
的下一跳直接指向r
,让r
的上一跳重新指向p
。
让q
的上一跳和q
的下一跳直接置空。
删除成功。
2、离尾部比较近
定义三个节点指针,p
是要删除节点的前驱,q
是要删除节点,R
是要删除节点的后继。
让R
的上一跳指向p
,让p
的下一跳指向R
,让q
的两边同时置空!!!
最后删除成功!!!
2、代码示例
链表类: LinkedList
//删除链表中指定的元素element
@Override
public void remove(E element) {
int index = index0f(element);
if(index != -1){
remove(index);
}
}
//删除链表中指定角标处index的元素
@Override
public E remove(int index) {
if (index < 0|| index > size) {
throw new ArrayIndexOutOfBoundsException("remove index out of bounds");
}
// 定义ret变量
E ret = null;
Node node;
// 当链表只剩一个元素
if(size ==1){
ret = head.data;
head = null;
tail = null;
// 删除表头
}else if(index == 0){
ret = head.data;
node = head.next;
head.next = null;
node.prev = head.prev;
head.prev = null;
head = node;
tail.next = head;
// 删除表尾
}else if(index == size -1){
ret = tail.data;
node = tail.prev;
tail.prev = null;
node.next = tail.next;
tail.next = null;
tail = node;
head.prev = tail;
}else{
// 删除链表中间的某一个元素
Node p, q, r;
if(index <= size / 2){
p = head;
for(int i=0; i < index-1; i++){
p = p.next;
}
q = p.next;
ret = q.data;
r = q.next;
p.next = r;
r.prev = p;
q.next = null;
q.prev = null;
}else{
p = tail;
for(int i = size -1; i > index + 1; i--){
p = p.prev;
}
q = p.prev;
ret = q.data;
r = q.prev;
r.next = p;
p.prev = r;
q.next = null;
q.prev = null;
}
}
size --;
return ret;
}
测试类:LinkedListDemo
package cn.linkedlist.demo05;
public class LinkedListDemo {
public static void main(String[] args) {
LinkedList<Integer> list = new LinkedList<>();
System.out.println("===链表头部插入===");
//System.out.println(list);
list.add(0, 1);
list.add(0, 2);
list.add(0, 3);
System.out.println(list);
System.out.println("===链表尾部插入===");
list.add(list.size(), 4);
list.add(list.size(), 5);
list.add(list.size(), 6);
System.out.println(list);
System.out.println("==删除元素==");
System.out.println(list.remove(3));
System.out.println(list);
}
}
3、执行结果
1.4 修改和获取操作
1、代码示例
链表类: LinkedList
//获取链表中指定索引处的元素
@Override
public E get(int index) {
if (index < 0|| index > size) {
throw new ArrayIndexOutOfBoundsException("get index out of bounds");
}
// 获取头
if(index == 0){
return head.data;
}else if(index == size -1){
// 获取尾部
return tail.data;
}else{
// 获取中间
Node p = head;
for (int i = 0; i < index; i++) {
p = p.next;
}
return p.data;
}
}
// 修改链表中指定索引index的元素为element
@Override
public E set(int index, E element) {
if (index < 0|| index > size) {
throw new ArrayIndexOutOfBoundsException("set index out of bounds");
}
E ret = null;
// 获取头
if(index == 0){
// 修改头
ret = head.data;
head.data = element;
}else if(index == size -1){
// 修改尾部元素
ret = tail.data;
tail.data = element;
}else{
// 修改中间
Node p = head;
for (int i = 0; i < index; i++) {
p = p.next;
}
ret = p.data;
p.data = element;
}
return ret;
}
测试类:LinkedListDemo
package cn.linkedlist.demo05;
public class LinkedListDemo {
public static void main(String[] args) {
LinkedList<Integer> list = new LinkedList<>();
System.out.println("===链表头部插入===");
//System.out.println(list);
list.add(0, 1);
list.add(0, 2);
list.add(0, 3);
System.out.println(list);
System.out.println("===链表尾部插入===");
list.add(list.size(), 4);
list.add(list.size(), 5);
list.add(list.size(), 6);
System.out.println(list);
System.out.println("==删除元素==");
System.out.println(list.remove(3));
System.out.println(list);
System.out.println("===更新元素===");
System.out.println(list.set(2, 66));
System.out.println(list);
System.out.println("===获取元素===");
System.out.println(list.get(3));
}
}
2、执行结果