双向链表实现
1.1 基本介绍
1、单向链表优缺点
- 单向链表,查找的方向只能是一个方向,而双向链表可以向前或者向后查找。
- 单向链表不能自我删除,需要靠辅助节点 ,而双向链表则可以自我删除。
2、双向链表基本介绍
双向链表也叫双向表,是链表的一种,它由多个结点组成,每个结点都由一个数据域和两个指针域组成,数据域(data)
用来存储数据,其中一个指针域(next)
用来指向其后继结点,另一个指针域用来指向前驱结点(prev指针)
。链表的头结点的数据域不存储数据,指向前驱结点的指针域值为null
,指向后继结点的指针域指向第一个真正存储数据的结点。
1.2 添加操作
1、思路分析
头部插入
-
新建插入节点
newNode
。 -
first
前驱指向newNode
。 -
newNode
后驱指向first
。 -
first
指向newNode
,这时候head
只是表示第二个节点,而head
需要表示第一个节点故改变指向。
尾部插入
- 新建插入节点
newNode
。 newNode
前驱指向last
。last
后驱指向newNode
。last
指向newNode
,这时候last
只是表示倒数第二个节点,而last
需要表示最后节点故指向newNode
。
中间插入
新建插入节点newNode
找到要插入newNode
的前一个节点preNode
。和后一个节点nextNode
newNode
后驱指向nextNode
,nextNode
前驱指向newNode
,此时newNode
和后面与链表已经联立,但是和前面处理分离状态。
preNode
后驱指向newNode
,newNode
前驱指向preNode
,此时插入完整操作完毕。
2、代码示例
链表类:DoubleLinkedList
package cn.linkedlist.demo03;
public class DoubleLinkedList<E> {
// 链表元素的数量
private int size;
// 声明头结点
private Node first;
// 声明尾节点
private Node last;
// 创建Node节点
private class Node{
// 存放内容
public E data;
// 指向链表的上一个节点
public Node prev;
// 指向下一个节点
public Node next;
// 构造方法
public Node() {
}
public Node(Node prev, E data, Node next) {
this.prev = prev;
this.data = data;
this.next = next;
}
@Override
public String toString(){
return data.toString();
}
}
// 初始化头结点
public DoubleLinkedList(){
first = null;
last = null;
size = 0;
}
/***
* 获取链表中的元素个数
* @return
*/
public int getSize(){
return size;
}
/***
* 返回链表是否为空
* @return
*/
public boolean isEmpty(){
return size == 0;
}
/***
* 根据链表的index位置添加新的元素e
* @param index
* @param data
*/
public void add(int index, E data){
// 调用方法
rangeCheckAdd(index);
if (index == size) { // 往最后面添加元素
addLast(data);
} else if(index == 0){
addLast(data);
} else {
// 新添加节点下一个元素
Node nextNode = node(index);
// 新添加节点的上一个元素
Node prevNode = nextNode.prev;
// 新添加节点
Node newNode = new Node (prevNode, data, nextNode);
// next节点的上一个prev指向新节点
nextNode.prev = newNode;
// prevNode节点的下一个next指向新节点
prevNode.next = newNode;
}
size++;
}
/***
* 在链表头添加新的元素e
* @param data
*/
public void addFirst(E data){
// 创建一个新节点
Node newNode = new Node(null, data, null);
if(isEmpty()){
last = newNode; // last -> newNode
}else {
first.prev = newNode; // first.prev->newNode
}
newNode.next = first; // newNode.next -> first;
first = newNode;
size++;
}
/***
* 在链表末尾添加新的元素e
* @param data
*/
public void addLast(E data){
// 创建一个新节点
Node newNode = new Node(null, data, null);
if(isEmpty()){
first = newNode; // first->newNode
}else{
last.next = newNode; // last指向的节点指向新节点
newNode.prev = last; // 新节点的前驱指向last指针
}
last = newNode; // last指针指向新节点
size++;
}
/**
* 获取index位置对应的节点对象
* @param index
* @return
*/
private Node node(int index) {
rangeCheck(index);
Node node;
if (index < (size >> 1)) {
node = first;
for (int i = 0; i < index; i++) {
node = node.next;
}
} else {
node = last;
for (int i = size - 1; i > index; i--) {
node = node.prev;
}
}
return node;
}
@Override
public String toString(){
StringBuilder res = new StringBuilder();
res.append("size=").append(size).append(", [");
// 定义一个指针变量
Node cur = first;
while(cur != null){
res.append(cur + "->");
cur = cur.next;
}
res.append("NULL");
res.append("]");
return res.toString();
}
// 索引值检查范围方法
private void rangeCheck(int index){
if(index < 0 || index >=size){
// 调用越界处理方法
outOfBounds(index);
}
}
// 添加方法索引检查范围
private void rangeCheckAdd(int index){
if(index < 0 || index >size){
// 调用越界处理方法
outOfBounds(index);
}
}
// 数组索引越界处理
private void outOfBounds(int index){
throw new IndexOutOfBoundsException("index:" + index + ", Size:" + size);
}
}
测试类:DoubleLikedListTest
package cn.linkedlist.demo03;
public class DoubleLinkedListDemo01 {
public static void main(String[] args) {
DoubleLinkedList<Integer> list = new DoubleLinkedList<>();
System.out.println("===链表头部插入===");
for(int i=0; i<5; i++){
list.addFirst(i);
System.out.println(list);
}
System.out.println("===链表尾部插入===");
list.addLast(12);
list.addLast(111);
list.addLast(123);
list.addLast(15);
System.out.println(list);
System.out.println("===链表中间插入===");
list.add(2, 23);
list.add(7, 66);
list.add(8, 39);
System.out.println(list);
}
}
3、代码示例
1.3 修改和查询操作
1、查询操作
链表类:DoubleLinkedList
/***
* 得链表的第index个位置的元素
* @param index
* @return
*/
public E get(int index){
return node(index).data;
}
/***
* 获得链表的第一个元素
* @return
*/
public E getFirst(){
return get(0);
}
/***
* 获得链表的最后一个元素
* @return
*/
public E getLast(){
return get(size - 1);
}
/***
* 查找链表中是否有元素e
* @param data
* @return
*/
public boolean contains(E data){
Node cur = first.next;
while(cur != null){
if(cur.data.equals(data))
return true;
cur = cur.next;
}
return false;
}
测试类:DoubleLikedListTest
package cn.linkedlist.demo03;
public class DoubleLinkedListDemo01 {
public static void main(String[] args) {
DoubleLinkedList<Integer> list = new DoubleLinkedList<>();
for(int i=0; i<5; i++){
list.addFirst(i);
}
list.addLast(12);
list.addLast(111);
list.addLast(123);
list.addLast(15);
list.add(2, 23);
list.add(7, 66);
list.add(8, 39);
System.out.println("===查找元素===");
Integer integer = list.get(2);
System.out.println("通过索引获取元素:" + integer);
Integer first = list.getFirst();
System.out.println("第一个链表元素:" + first);
Integer last = list.getLast();
System.out.println("最后链表元素:" + last);
boolean b = list.contains(23);
System.out.println("是否存在该元素:" + b);
}
}
2、执行结果
3、修改操作
链表类:DoubleLinkedList
/***
* 修改链表的第index(0-based)个位置的元素为e
* @param index
* @param data
*/
public void update(int index, E data){
// 调用索引检测方法
rangeCheck(index);
// 创建cur指针,指向虚拟头结点
Node cur = first;
for(int i = 0 ; i < index ; i ++) {
cur = cur.next;
}
cur.data = data;
}
测试类:DoubleLikedListTest
package cn.linkedlist.demo03;
public class DoubleLinkedListDemo01 {
public static void main(String[] args) {
DoubleLinkedList<Integer> list = new DoubleLinkedList<>();
for(int i=0; i<5; i++){
list.addFirst(i);
}
list.addLast(12);
list.addLast(111);
list.addLast(123);
list.addLast(15);
list.add(2, 23);
list.add(7, 66);
list.add(8, 39);
System.out.println("===修改节点元素===");
System.out.println("linkedList(修改前)" + list);
list.update(4, 38);
System.out.println("linkedList(修改后)" + list);
}
}
4、执行结果
1.4 删除操作
1、思路分析
因为是双向链表,因此可以实现自我删除某个节点,直接找到要删除的这个节点。
头结点删除
first
节点的后驱节点的前指针prev
改为null
。
first
节点指向first.next
,这样first
就指向需要的第一个节点。
尾节点删除
尾删除就是删除双向链表中的最后一个节点,也就是尾指针所指向的那个节点,思想和头删除的思想一致。
last.prev.next=null
尾节点的前一个节点(prev)
的后驱节点等于null
。
last = last.prev
尾节点指向它的前驱节点,此时尾节点由于步骤1next
已经为null
,完成删除。
中间删除
找到待删除节点deleteNode
的前驱节点preNode
,preNode.next
是要删除的节点
preNode.next.next.pre=preNode
,将待删除deleteNode
的后驱节点nextNode
的prev
指针指向preNode
,等价于nextNode.pre=preNode
。
preNode.next=preNode.next.next;
,此时deleteNode
被跳过成功删除。
2、代码示例
链表类:DoubleLinkedList
/***
* 从链表中删除index位置的元素, 返回删除的元素
* @param index
*/
public void remove(int index){
// 调用索引检测方法
rangeCheck(index);
// 条件判断
if(index == 0){
removeFirst();
}else if(index == size -1 ){
removeLast();
}else {
//删除位置的前一个元素
Node preNode = first;
for(int i=0; i < index-1; i++) {
preNode = preNode.next;
}
//要删除位置的元素
Node deleteNode = preNode.next;
//要删除元素的下一个元素
Node nextNode = deleteNode.next;
preNode.next = nextNode;
nextNode.prev = preNode;
size--;
}
}
/***
* 从链表中删除第一个元素, 返回删除的元素
*/
public void removeFirst(){
if (size == 1)
{
first = null;
last = null;
} else {
first =first.next;
}
size--;
}
/***
* 从链表中删除最后一个元素, 返回删除的元素
*/
public void removeLast(){
if (size == 1)
{
first = null;
last = null;
} else {
last.prev.next = null;
last = last.prev;
}
size--;
}
/***
* 从链表中删除元素e
* @param data
*/
public void removeElement(E data){
// 创建头结点
Node prev = first;
while(prev.next != null){
if(prev.next.data.equals(data))
break;
prev = prev.next;
}
if(prev.next != null){
Node delNode = prev.next;
prev.next = delNode.next;
delNode.next = null;
size --;
}
}
测试类:DoubleLikedListTest
package cn.linkedlist.demo03;
public class DoubleLinkedListDemo01 {
public static void main(String[] args) {
DoubleLinkedList<Integer> list = new DoubleLinkedList<>();
for(int i=0; i<5; i++){
list.addFirst(i);
}
list.addLast(12);
list.addLast(111);
list.addLast(123);
list.addLast(15);
list.add(2, 23);
list.add(7, 66);
list.add(8, 39);
System.out.println("===删除链表节点前====");
System.out.println(list);
System.out.println("===删除链表节点后====");
// 删除链表第一个节点
list.removeFirst();
System.out.println(list);
// 根据链表index位置的元素, 返回删除的元素
list.remove(2);
System.out.println(list);
// 删除链表最后一个节点
list.removeLast();
System.out.println(list);
// 删除链表中的元素
list.removeElement(39);
System.out.println(list);
}
}
3、执行结果