Java语言描述--双链表的设计与实现--数据结构

数据结构--双链表的设计与实现

首先了解其定义:
双链表:每个结点中设置两个指针成员,分别用于指向前驱结点和后继结点,这样的链表简称双向链表。与单链表类似不过多了一个指针成员。 需要了解单链表与指针成员,可参考浏览我的

上一期博客,其中对单链表进行了完整的解剖。

了解了双链表的概念,接下来见识它的模型:
双链表表与单链表一样,同样有头结点;头结点不存放data数据。但各结点之间为双向指向。
相应图解为:

在这里插入图片描述
这里为什么要画椭圆,仅根据我个人的理解,在单链表一期中已有介绍。这里做简单描述:1.各实例化对象默认拥有对应构造方法内的所有内容;2.当你往构造方法内加内容时,对应实例化对象所拥有的内容也会增多,所以我这里采用椭圆来理解各结点。
了解了双链表的结构,那么如何设计建立双链表呢?与单链表相同,有两种方法:

一.头插法建立双链表表:这里我画了一张图来理解

在这里插入图片描述
同样,了解了原理设计代码:

//用头插法建立双链表
    public void CreateHDList(Object[] a){
        DoubleList1<Object> s;
        for(int i=0;i<a.length;i++){
            s = new DoubleList1<>(a[i]);
            s.next = head.next;
            if(head.next!=null)
                head.next.prior = s;
            head.next = s;
            s.prior = head;
        }
    }

为什么这里要s.next = head.next;(为了保证插入的结点之间为双向指向),当插入第一个结点时这个语句本来是没有必要的,因为在构造结点时,s.next本来就是指向为空的。但是当继续插入结点时,就必须添加这条语句了,因为下一个结点插入时,需要该语句改变其引用,若没有该语句,则下一个结点s1与结点s达不到双向指向,读者可自行进行推导验算。这里无需置最后一个结点的next指向null,因为它本来就是指向null的。
二.尾插法建立双链表表:同样先附图理解

在这里插入图片描述

与头插法不同的是,在进行建表的过程中,中间结点t的指向是变化的。t是一个中间变量,在一个数据插入之后,t指向下一个结点;
第一种情况:当所有元素插入完之后,需将双链表收尾。所以将t置空,没有元素要插入了,t结点也就没有用了,将其置空释放内存空间。
第二种情况:若最后一个结点在插入前其next已有指向,但我们并不想让其进入该双链表,所以将其置空。

了解了建表方法,接下来正式进入双链表的功能设计阶段
1.创建一个DoubleList1类:构造结点内部结构
相关代码为:

//创建DoubleList类,生产结点
class DoubleList1<Object>{
    Object data;
    DoubleList1<Object> prior;
    DoubleList1<Object> next;
    //创建头结点结构
    public DoubleList1(){
        prior = null;
        next = null;
    }
    //创建普通结点结构
    public DoubleList1(Object d){
        data = d;
        prior = null;
        next = null;
    }
}

代码的具体用法在注释中已经说明。

2.创建一个DoubleList2类:创建双链表以及实现双链表的增删改查
相关代码为:以下方法均在此类中

  2.1:创建一个头结点
//创建头结点
    DoubleList1<Object> head = new DoubleList1<>();
  2.2:头插法建表
//用头插法建立双链表
    public void CreateHDList(Object[] a){
        DoubleList1<Object> s;
        for(int i=0;i<a.length;i++){
            s = new DoubleList1<>(a[i]);
            s.next = head.next;
            if(head.next!=null)
                head.next.prior = s;
            head.next = s;
            s.prior = head;
        }
    }
  2.3:尾插法建表
//用尾插法建立双链表
    public void CreatLDList(Object[] a){
        DoubleList1<Object> s;
        DoubleList1<Object> t;
        t = head;
        for (int i = 0; i < a.length; i++) {
            s = new DoubleList1<Object>(a[i]);
            t.next = s;
            s.prior = t;
            t = s;
        }
        t.next = null;
    }

尾插法和头插法上面已经进行讲解,这里不再赘述。
在实现增删改查前:

  2.4:获取双链表的长度
//在实现双链表增删改查之前,先求出双链表的长度
//也可使用前驱结点prior进行求size,这里我们采用后驱结点next进行求size
    public int size(){
        DoubleList1<Object> p = head;
        int count = 0;
        while(p.next!=null){
            count++;
            p = p.next;
        }
        return count;
    }

从这里可以大致看出,双链表的头结点索引为-1,size为0;头结点下一结点索引为0,size为1。

  2.5:获取双链表中某个位置对应的的结点
//在实现双链表增删改查之前,先求出双链表某个元素的对应结点
    //也可使用前驱结点prior进行求位置结点,这里我们采用后驱结点next进行求位置结点
    public DoubleList1<Object> geti(int i){
        DoubleList1<Object> p = head;
        int j = -1;     //这里设计了一个细节,默认设置双链表索引从-1开始,即头结点索引为-1,且头结点不存放数据
        while(j<i){
            j++;
            p = p.next;
        }
        return p;
    }

从这里发现,我们自己定义了双链表的索引。头结点head索引为-1,存放数据的第一个结点s索引为0。这与数组类似,首元素下标为0。双链表同样如此,即首个存放数据的结点索引为0。(这是由我们自己定义的)

  2.6:实现双链表的增操作
//实现元素的增操作,实现在指定位置插入,若直接插入,调用上面两个方法即可
    public void Insert(int i,Object e){
        if(i<0||i>size()){
            throw new IllegalArgumentException("位置i不在有效范围之内!");
        }
        DoubleList1<Object> s = new DoubleList1<>(e);
        DoubleList1<Object> p = geti(i-1);
        s.next = p.next;             //在其后插入
        if(p.next!=null){            //若后面结点不为空,则双向指向
            p.next.prior = s;
        }
        p.next = s;
        s.prior = p;
    }
  2.7:实现双链表的删操作
//实现元素的删操作,删除指定位置元素
    public void Delete(int i){
        if(i<0||i>size()-1){
            throw new IllegalArgumentException("位置i不在有效范围之内!");
        }
        DoubleList1<Object> p = geti(i);
        if(p.next!=null){
            p.next.prior = p.prior;
        }
        p.prior.next = p.next;
    }
  2.8:实现双链表的改操作
//实现元素的改操作,修改指定位置的元素
    public void SetElem(int i,Object e){
        if(i<0||i>size()-1){
            throw new IllegalArgumentException("位置i不在有效范围之内!");
        }
        DoubleList1<Object> p = geti(i);
        p.data = e;
    }
  2.9:实现双链表的查操作
    2.9.1:查找指定元素并返回
//实现元素的查操作,查找指定位置的元素
    public Object getElem(int i){
        DoubleList1<Object> p = head;
        int j = -1;
        while(j<i){
            j++;
            p = p.next;
        }
        return p.data;
    }
    2.9.1:查找双链表全部元素并返回
//将双链表中全部元素转换为字符串并返回
    public String ToString(){
        String ans = "";
        DoubleList1<Object> p = head.next;
        while(p!=null){
            ans+=p.data+" ";
            p=p.next;
        }
        return ans;
    }

3.创建一个DoubleList主类:实现结构与表现相分离
相关代码为:(功能测试)

public class DoubleList {
    public static void main(String[] args) {
        Integer[] a = {1,2,3,4,5};
        DoubleList2<Integer> doubleList2 = new DoubleList2<>();
        doubleList2.CreateHDList(a);
        System.out.println(doubleList2.getElem(3));
        System.out.println(doubleList2.ToString());
        DoubleList2<Integer> doubleList3 = new DoubleList2<>();
        doubleList3.CreatLDList(a);
        System.out.println(doubleList3.ToString());
    }
}

所有代码为:

//创建DoubleList类,生产结点
class DoubleList1<Object>{
    Object data;
    DoubleList1<Object> prior;
    DoubleList1<Object> next;
    //创建头结点结构
    public DoubleList1(){
        prior = null;
        next = null;
    }
    //创建普通结点结构
    public DoubleList1(Object d){
        data = d;
        prior = null;
        next = null;
    }
}
//利用DoubleList1创建对象,创造链表结构,实现链表基本功能
class DoubleList2<Object>{
    //创建头结点
    DoubleList1<Object> head = new DoubleList1<>();
   //用头插法建立双链表
    public void CreateHDList(Object[] a){
        DoubleList1<Object> s;
        for(int i=0;i<a.length;i++){
            s = new DoubleList1<>(a[i]);
            s.next = head.next;
            if(head.next!=null)
                head.next.prior = s;
            head.next = s;
            s.prior = head;
        }
    }
    //用尾插法建立双链表
    public void CreatLDList(Object[] a){
        DoubleList1<Object> s;
        DoubleList1<Object> t;
        t = head;
        for (int i = 0; i < a.length; i++) {
            s = new DoubleList1<Object>(a[i]);
            t.next = s;
            s.prior = t;
            t = s;
        }
        t.next = null;
    }
    //在实现双链表增删改查之前,先求出双链表的长度
    //也可使用前驱结点prior进行求size,这里我们采用后驱结点next进行求size
    public int size(){
        DoubleList1<Object> p = head;
        int count = 0;
        while(p.next!=null){
            count++;
            p = p.next;
        }
        return count;
    }
    //在实现双链表增删改查之前,先求出双链表某个元素的对应结点
    //也可使用前驱结点prior进行求位置结点,这里我们采用后驱结点next进行求位置结点
    public DoubleList1<Object> geti(int i){
        DoubleList1<Object> p = head;
        int j = -1;     //这里设计了一个细节,默认设置双链表索引从-1开始,即头结点索引为-1,且头结点不存放数据
        while(j<i){
            j++;
            p = p.next;
        }
        return p;
    }
    //实现元素的增操作,实现在指定位置插入,若直接插入,调用上面两个方法即可
    public void Insert(int i,Object e){
        if(i<0||i>size()){
            throw new IllegalArgumentException("位置i不在有效范围之内!");
        }
        DoubleList1<Object> s = new DoubleList1<>(e);
        DoubleList1<Object> p = geti(i-1);
        s.next = p.next;             //在其后插入
        if(p.next!=null){            //若后面结点不为空,则双向指向
            p.next.prior = s;
        }
        p.next = s;
        s.prior = p;
    }
    //实现元素的删操作,删除指定位置元素
    public void Delete(int i){
        if(i<0||i>size()-1){
            throw new IllegalArgumentException("位置i不在有效范围之内!");
        }
        DoubleList1<Object> p = geti(i);
        if(p.next!=null){
            p.next.prior = p.prior;
        }
        p.prior.next = p.next;
    }
    //实现元素的改操作,修改指定位置的元素
    public void SetElem(int i,Object e){
        if(i<0||i>size()-1){
            throw new IllegalArgumentException("位置i不在有效范围之内!");
        }
        DoubleList1<Object> p = geti(i);
        p.data = e;
    }
    //实现元素的查操作,查找指定位置的元素
    public Object getElem(int i){
        DoubleList1<Object> p = head;
        int j = -1;
        while(j<i){
            j++;
            p = p.next;
        }
        return p.data;
    }
    //将双链表中全部元素转换为字符串并返回
    public String ToString(){
        String ans = "";
        DoubleList1<Object> p = head.next;
        while(p!=null){
            ans+=p.data+" ";
            p=p.next;
        }
        return ans;
    }

}
public class DoubleList {
    public static void main(String[] args) {
        Integer[] a = {1,2,3,4,5};
        DoubleList2<Integer> doubleList2 = new DoubleList2<>();
        doubleList2.CreateHDList(a);
        System.out.println(doubleList2.getElem(3));
        System.out.println(doubleList2.ToString());
        DoubleList2<Integer> doubleList3 = new DoubleList2<>();
        doubleList3.CreatLDList(a);
        System.out.println(doubleList3.ToString());
    }
}

相关结果为:
在这里插入图片描述
这里我没有试验所有方法,读者可自行拷贝尝试。
这里使用泛型创建类及成员变量data,是方便各种类型插入,读者可自行替换成包装类类型。实现了基本功能之后,读者可自行设计案例做题。每个方法代码中都有对应注释内容。

若觉得内容稍可,请留下你们的

在这里插入图片描述
,若有疑问,欢迎下方评论区留言讨论!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值