数据结构之双向链表

一、双向链表的定义

每个结点存在两个指针域,一个指向前驱节点的引用,一个指向后继结点引用。即可以通过该节点向前走,也可以向后走。总之,单节点能做的事,双向链表都能做。

在这里插入图片描述

二、双向链表的实现

  1. 创建双节点类
class DoubleNode{
    int data;
    //保存前驱节点的地址
    DoubleNode pre;
    //保存后继节点的地址
    DoubleNode next;
    //定义三个构造方法
    public DoubleNode(){}
    public DoubleNode(int data){
        this.data = data;
    }
    public DoubleNode(DoubleNode pre, int data, DoubleNode next) {
        this.pre = pre;
        this.data = data;
        this.next = next;
    }
}
  1. 创建双向链表类
public class DoubleLinkedList {
    //头节点的地址
    private DoubleNode head = null;
    //尾结点的地址
    private DoubleNode tail = null;
    //记录节点数
    private int size;
}

3.在双向链表的头部插入新结点

public void addFir(int data) {
    DoubleNode node = new DoubleNode(null,data,head);
    //链表中没有结点
    if (tail == null) {
        tail = node;
    } else {
        head.pre = node;
    }
    head = node;
    size ++;
}

在这里插入图片描述

4.在双向链表的尾部插入新结点

//和头插法类似
public void addTail(int data){
    DoubleNode node = new DoubleNode(tail,data,null);
    if (head == null) {
        head = node;
    } else {
        tail.next = node;
        node.pre = tail;
    }
    tail = node;
    size ++;
}
  1. 根据索引值去找到对应的节点
private DoubleNode node(int index) {
    //inde在前半部分,从前遍历
    if(index <= size / 2){
        DoubleNode x = head;
        for (int i = 0; i < index; i++) {
            x = x.next; 
        }
        return x;
    } else {
        //index在后半部分,从后遍历
        DoubleNode x = tail;
        for (int i = size - 1; i > index; i--) {
            x = x.next;
        }
        return x;
    }
}

6.在索引为index位置插入结点

public void addByIndex(int index, int data) {
    if (index < 0 || index > size) {
        System.out.println("输出错误");
        return;
    }
    if(index == 0){
        addFir(data);
    }
    else if (index == size) {
        addTail(data);
    } else{
        //从中间插入,找前驱
        DoubleNode pre = node(index - 1);
        DoubleNode newNode = new DoubleNode(pre,data,pre.next);
        pre.next.pre = newNode;
        pre.next = newNode;
        size ++;
    }
}

在这里插入图片描述

7.查找索引位置的结点,返回结点值

public int findByIndex(int index) {
    if(index < 0 || index >= size) {
        return -1;
    }
    DoubleNode ret = node(index);
    return ret.data;
}

8.修改索引位置的结点,返回旧的结点值

public int setByIndex(int index,int newVal) {
    if(index < 0 || index >= size) {
        return -1;
    }
    DoubleNode ret = node(index);
    int oldVal = ret.data;
    ret.data = newVal;
    return oldVal;
}

9.删除当前双向链表中的node节点

一共分为四种情况

  1. 无前驱结点有后继结点
  2. 有前驱结点无后继结点
  3. 有前驱结点有后继结点
  4. 无前驱结点无后继结点(只有一个结点)
    我们可以采用”分治思想“,先只管前驱节点的删除操作,在管后继结点的删除操作
public void ulink(DoubleNode node) {
    //前驱结点
    DoubleNode pre = node.pre;
    //后继结点
    DoubleNode successor = node.next;
    //无前驱节点,头节点就是下一个结点
    if(pre == null) {
        head = successor;
    } else {
        //有前驱节点
        pre.next = successor;
        node.pre = null;
    }
    //无后继结点,尾结点就是前一个结点
    if(successor == null) {
        tail = pre;
    } else {
        successor.pre = pre;
        node.next = null;
    }
}

10.根据索引index删除节点

public int removeByIndex(int index) {
    if(index < 0 || index >= size){
        return -1;
    }
    DoubleNode node = node(index);
    int oldVal = node.data;
    ulink(node);
    return oldVal;
}

11.删除头结点

public void removeFir(){ 
    ulink(node(0));
}

12.删除尾结点

public void removeTail(){
    ulink(node(size - 1));
}

13.删除出现第一个值为val的结点

public void removeOnceVal(int val) {
    //找到了循环就结束
    for (DoubleNode x = head; x!= null; x = x.next) {
        if (x.data == val){
            ulink(x);
            break;
        }
    }
}

14.删除链表中所有值为val的结点

public void removeAllVal(int val) {
    for(DoubleNode x = head; head != null;) {
        if(x.data == val){
            //暂存下一个结点的地址
            //因为ulink()方法会把x.next置空,找不到后继了
            DoubleNode successor = x.next;
            ulink(x);
            x = successor;
        } else {
            x = x.next;
        }
    }
}

15.链表的toString方法

public String toString(){
    String ret = "";
    for (DoubleNode x = head; x != null; x= x.next){
        ret += x.data;
        ret += "->";
    }
    ret += "NULL";
    return ret;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值