双向链表

在上一篇:单向链表中,可以发现单向链表有一些缺点
  • 单向链表,查找的方向只能是一个方向,只能从前向后查找
  • 单向链表不能自我删除,需要先找到被删除节点的前一个节点front,使用front节点来辅助删除,不方便
双向链表就能够解决这些问题
  1. 如图1,双向链表包括两个指针域,previous(简写prev)、next
    图1.双向链表的节点
图1.双向链表的节点
  1. 因为双向链表有两个指针域,所以内存结构如图2
    图2.双向链表的内存结构

    图2.双向链表的内存结构

  2. 常见操作

  • 链表尾部插入新节点原理示意图
    尾部插入新节点
尾部插入新节点
  • 链表中部插入新节点原理示意图
    中部插入新节点
    中部插入新节点
// 先找到A节点
A.next.prev = C;
C.next = A.next;
A.next = C;
C.prev = A;
  • 删除节点原理示意图
    删除节点
删除节点
C.next.prev = C.prev;
C.prev.next = C.next;

代码示例

1.因为链表由一个一个的节点组成,因此需要定义一个节点类(Node)。假设属性包括id、name(这些是节点的数据域),和prev、next(指针域)。
Node类

/**
 * 定义一个Node类,一个Node对象就是一个节点
 */
class Node2 {
    private Node prev; // 指向前一个节点
    private int id;    // id,唯一
    private String name;    // 姓名
    private Node next;  // 指向后一个节点
    // 省略setter、getter、构造器
}

2.定义一个DubboLinkedList 双向链表类,DubboLinkedList 类包括一个成员变量head,是链表的头结点
DubboLinkedList 类

/**
 * 定义DubboLinkedList类表示双向链表
 */
class DubboLinkedList {
    // 头结点,头结点不保存数据
    Node head = new Node();

    //  获取链表的最后那个节点
    public Node getLast() {
        // temp变量来保存链表的最后那个节点
        Node temp = head;
        while (temp.getNext() != null) {
            temp = temp.getNext();
        }
        // 循环结束时,temp就是最后那个节点
        return temp;
    }

    // 获取链表的第一个节点(不是头结点)
    public Node getFirst() {
        return head.getNext();
    }

    // 根据id查找指定节点
    public Node find(int id) {
        Node temp = head.getNext();
        while (temp != null) {
            // 本示例中约定id相同就是同一个节点
            if (temp.getId() == id) {
                break;
            }
            // temp后移
            temp = temp.getNext();
        }
        return temp;
    }


    // 正序遍历链表
    public void list() {
        // 判空
        if (head.getNext() == null) {
            System.out.println("链表为空,无数据");
            return;
        }
        Node temp = head.getNext();
        while (temp != null) {
            System.out.println(temp);
            // temp后移
            temp = temp.getNext();
        }
    }

    // 倒序遍历链表
    public void reverseOrderList() {
        // 判空
        if (head.getNext() == null) {
            System.out.println("链表为空,无数据");
            return;
        }
        // 先得到最后那个节点
        Node temp = getLast();
        while (temp != null) {
            System.out.println(temp);
            temp = temp.getPrev();
            if (temp.getPrev() == null) {
                // 如果temp.getPrev()==null,说明当前temp是头结点,不打印头结点
                break;
            }
        }
    }

    // 添加新节点到链表尾部
    public void append(Node node) {
        Node last = getLast();
        // 添加新节点
        last.setNext(node);
        node.setPrev(last);
    }

    // 插入节点到指定节点后
    public void insertAfter(Node node, Node newNode) {
        // 先根据id找到这个节点
        Node beforeNode = find(node.getId());
        // 插入节点
        beforeNode.getNext().setPrev(newNode);
        newNode.setNext(beforeNode.getNext());
        beforeNode.setNext(newNode);
        newNode.setPrev(beforeNode);
    }

    // 删除指定节点,并返回被删除节点
    public Node delete(Node node) {
        if (head.getNext() == null) {
            System.out.println("链表为空");
            return null;
        }

        // 找到被删除节点
        Node deleteNode = find(node.getId());
        if (deleteNode == null) {
            System.out.println("没有找到指定节点");
            return deleteNode;
        }

        //删除节点
        deleteNode.getPrev().setNext(deleteNode.getNext());
        //如果被删除的节点不是最后那个节点才执行, 因为最后的节点的next指针为null,不判断可能产生空指针异常
        if (deleteNode.getNext() != null) {
            deleteNode.getNext().setPrev(deleteNode.getPrev());
        }
        return deleteNode;
    }

    // 修改节点
    public boolean update(Node node) {
        // 先找到节点
        Node updateNode = find(node.getId());
        if (updateNode == null) {
            return false;
        }
        updateNode.setName(node.getName());
        return true;
    }

}

3.测试

public class DubboLinkedListDemo {
    public static void main(String[] args) {
        // 创建链表
        DubboLinkedList dubboLinkedList = new DubboLinkedList();
        // 创建节点
        Node node1 = new Node(1, "周星驰");
        Node Node = new Node(2, "刘德华");
        Node node3 = new Node(3, "古天乐");
        Node node4 = new Node(4, "张家辉");
        Node node5 = new Node(5, "尼古拉斯赵四");
        // 添加节点
        dubboLinkedList.append(node1);
        dubboLinkedList.append(Node);
        dubboLinkedList.append(node3);
        dubboLinkedList.append(node4);
        dubboLinkedList.append(node5);
        // 正序遍历链表
        System.out.println("正序遍历链表:");
        dubboLinkedList.list();
        System.out.println("倒序遍历链表:");
        dubboLinkedList.reverseOrderList();

        // 在id为2的节点后面添加一个新节点
        dubboLinkedList.insertAfter(new Node(2, null), new Node(6, "范迪塞尔"));
        System.out.println("在id为2的节点后添加新节点后遍历:");
        dubboLinkedList.list();

        dubboLinkedList.delete(new Node(5, null));
        System.out.println("删除id为5的节点后遍历:");
        dubboLinkedList.list();

        // 查找id为4的节点
        Node node = dubboLinkedList.find(4);
        System.out.println("查找id为4的节点:" + node);

        //修改id为6的节点
        dubboLinkedList.update(new Node(6,"尼古拉斯赵四"));
        System.out.println("修改id为6的节点:");
        dubboLinkedList.list();

    }
}

4.控制台打印

正序遍历链表:
Node{id=1, name='周星驰'}
Node{id=2, name='刘德华'}
Node{id=3, name='古天乐'}
Node{id=4, name='张家辉'}
Node{id=5, name='尼古拉斯赵四'}
倒序遍历链表:
Node{id=5, name='尼古拉斯赵四'}
Node{id=4, name='张家辉'}
Node{id=3, name='古天乐'}
Node{id=2, name='刘德华'}
Node{id=1, name='周星驰'}
在id为2的节点后添加新节点后遍历:
Node{id=1, name='周星驰'}
Node{id=2, name='刘德华'}
Node{id=6, name='范迪塞尔'}
Node{id=3, name='古天乐'}
Node{id=4, name='张家辉'}
Node{id=5, name='尼古拉斯赵四'}
删除id为5的节点后遍历:
Node{id=1, name='周星驰'}
Node{id=2, name='刘德华'}
Node{id=6, name='范迪塞尔'}
Node{id=3, name='古天乐'}
Node{id=4, name='张家辉'}
查找id为4的节点:Node{id=4, name='张家辉'}
修改id为6的节点:
Node{id=1, name='周星驰'}
Node{id=2, name='刘德华'}
Node{id=6, name='尼古拉斯赵四'}
Node{id=3, name='古天乐'}
Node{id=4, name='张家辉'}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值