有了单向链表的基础,双向链表的实现就容易多了。

双向链表的一般情况:

增加节点:

删除节点:

双向链表的Java实现:

package com.liuhao.algorithm;  
  
public class DuLinkList<T> {  
  
    /** 
     * 内部类:链表中的一个节点 
     *  
     * @author liuhao data 节点中的数据 prev 指向前一个节点的引用 next 指向下一个节点的引用 
     */  
    private class Node {  
  
        private T data;// 保存的数据元素  
        private Node prev;// 指向上一个节点  
        private Node next;// 指向下一个节点  
  
        public Node() {  
        }  
  
        public Node(T data, Node prev, Node next) {  
            super();  
            this.data = data;  
            this.prev = prev;  
            this.next = next;  
        }  
    }  
  
    private Node header;// 头结点  
    private Node tail;// 尾节点  
    private int size;// 链表中元素个数  
  
    // 创建空链表  
    public DuLinkList() {  
        header = null;  
        tail = null;  
    }  
  
    // 已指定数据元素创建链表,只有一个元素  
    public DuLinkList(T element) {  
  
        header = new Node(element, null, null);  
        // 只有一个节点,header,tail都指向该节点  
        tail = header;  
        size++;  
    }  
  
    // 返回链表长度  
    public int length() {  
        return size;  
    }  
  
    // 获取指定位置的数据元素  
    public T get(int index) {  
        return this.getNodeByIndex(index).data;  
    }  
  
    // 获取指定位置的节点  
    private Node getNodeByIndex(int index) {  
  
        if (index < 0 || index > size - 1) {  
            throw new IndexOutOfBoundsException("索引超出线性表范围");  
        }  
  
        if (index < size / 2) {  
            Node current = header;  
            for (int i = 0; i < size / 2 && current != null; i++, current = current.next) {  
                if (i == index) {  
                    return current;  
                }  
            }  
        } else {  
            Node current = tail;  
            for (int i = size - 1; i >= size / 2 && current != null; i--, current = current.prev) {  
                if (i == index) {  
                    return current;  
                }  
            }  
        }  
        return null;  
    }  
  
    // 按值查询所在的位置  
    public int locate(T element) {  
        Node current = header;  
  
        for (int i = 0; i < size - 1 && current != null; i++, current = current.next) {  
            if (element.equals(current.data)) {  
                return i;  
            }  
        }  
  
        return -1;  
    }  
  
    // 向指定位置插入元素  
    public void insert(T element, int index) {  
        if (index < 0 || index > size) {  
            throw new IndexOutOfBoundsException("索引超出线性表范围");  
        }  
  
        if (header == null) {  
            this.add(element);  
        } else {  
            if (0 == index) {  
                this.addAtHead(element);  
            } else {  
                Node prev = this.getNodeByIndex(index - 1);// 获取插入节点的前一个节点  
                Node next = prev.next;// 待插索引处的节点  
                Node newNode = new Node(element, prev, next);// 新增节点,让它的prev指向之前的节点。next指向之后的节点  
  
                prev.next = newNode;// 之前的节点的next指向当前节点  
                next.prev = newNode;// 之后节点的prev指向当前节点  
  
                size++;  
            }  
        }  
    }  
  
    // 采用尾插法添加新节点  
    public void add(T element) {  
  
        // 若还是空表,则将header和tail都指向该元素即可  
        if (header == null) {  
            header = new Node(element, null, null);  
            tail = header;  
        } else {  
            // 创建信节点,prev指向tail  
            Node newNode = new Node(element, tail, null);  
            // 令tail的next指向新节点  
            tail.next = newNode;  
            tail = newNode;// 把新节点设为尾节点  
        }  
  
        size++;  
    }  
  
    // 采用头插发添加新节点  
    public void addAtHead(T element) {  
        Node newNode = new Node(element, null, header);  
        header.prev = newNode;  
        header = newNode;  
  
        // 如果插入之前是空表  
        if (tail == null) {  
            tail = header;  
        }  
  
        size++;  
    }  
  
    // 删除指定索引处的元素  
    public T delete(int index) {  
  
        if (index < 0 || index > size - 1) {  
            throw new IndexOutOfBoundsException("索引超出线性表范围");  
        }  
  
        Node del = null;  
  
        if (index == 0) {  
            del = header;  
            header = header.next;  
            header.prev = null;  
        } else {  
            Node prev = this.getNodeByIndex(index - 1);// 获取索引处之前的节点  
            del = prev.next;// 获取索引处的节点  
  
            // 让之前的节点的next指向下一个节点  
            prev.next = del.next;  
  
            // 有可能删除的是最后一个元素,若直接调用next.prev可能会出错  
            if (del.next != null) {  
                del.next.prev = prev;  
            }  
  
            //若删除的是最后一个元素,那么就要重置tail;  
            tail = prev;  
              
            del.prev = null;  
            del.next = null;  
  
        }  
        size--;  
        return del.data;  
    }  
  
    // 删除最后一个元素  
    public T remove() {  
        return this.delete(size - 1);  
    }  
  
    // 判断是否为空  
    public boolean isEmpty() {  
        return size == 0;  
    }  
  
    // 清空线性表  
    public void clear() {  
        header = null;  
        tail = null;  
        size = 0;  
    }  
  
    public String toString() {  
        if (size == 0) {  
            return "[]";  
        } else {  
            StringBuilder sb = new StringBuilder("[");  
            for (Node current = header; current != null; current = current.next) {  
                sb.append(current.data.toString() + ", ");  
            }  
            sb.append("]");  
  
            int len = sb.length();  
  
            // 删除多余的“,”和空格  
            return sb.delete(len - 3, len - 2).toString();  
        }  
    }  
}  

测试代码:

package com.liuhao.test;

import org.junit.Test;

import com.liuhao.algorithm.DuLinkList;

public class DuLinkListTest {

    @Test
    public void test() {
       
        //测试构造函数
        DuLinkList<String> duList = new DuLinkList("好");
        System.out.println(duList);
       
        //测试添加元素
        duList.add("ni");
        duList.add("没");
        System.out.println(duList);
       
        //在头部添加
        duList.addAtHead("五月");
        System.out.println(duList);
       
        //在指定位置添加
        duList.insert("摩卡", 2);
        System.out.println(duList);
       
        //获取指定位置处的元素
        System.out.println("第2个元素是(从0开始计数):" + duList.get(2));
       
        //返回元素索引
        System.out.println("摩卡在的位置是:" + duList.locate("摩卡"));
        System.out.println("moka所在的位置:" + duList.locate("moka"));
       
        //获取长度
        System.out.println("当前线性表的长度:" + duList.length());
       
        //判断是否为空
        System.out.println(duList.isEmpty());
       
        //删除最后一个元素
        duList.remove();
        System.out.println("调用remove()后:" + duList);
       
        //获取长度
        System.out.println("当前线性表的长度:" + duList.length());
       
        //删除指定位置处元素
        duList.delete(3);
        System.out.println("删除第4个元素后:" + duList);
       
        //获取长度
        System.out.println("当前线性表的长度:" + duList.length());
       
        //清空
        duList.clear();
        System.out.println(duList);
       
        //判断是否为空
        System.out.println(duList.isEmpty());
    } }

  

线性表的Java实现--链式存储(双向链表)的更多相关文章

  1. 线性表的Java实现--链式存储(单向链表)

    单向链表(单链表)是链表的一种,其特点是链表的链接方向是单向的,对链表的访问要通过顺序读取从头部开始. 链式存储结构的线性表将采用一组任意的存储单元存放线性表中的数据元素.由于不需要按顺序存储,链表在 ...

  2. 数据结构导论 四 线性表的顺序存储VS链式存储

    前几章已经介绍到了顺序存储.链式存储 顺序存储:初始化.插入.删除.定位 链式存储:初始化.插入.删除.定位 顺序存储:初始化 strudt student{ int ID://ID char nam ...

  3. 算法与数据结构(一) 线性表的顺序存储与链式存储(Swift版)

    温故而知新,在接下来的几篇博客中,将会系统的对数据结构的相关内容进行回顾并总结.数据结构乃编程的基础呢,还是要不时拿出来翻一翻回顾一下.当然数据结构相关博客中我们以Swift语言来实现.因为Swift ...

  4. 线性表的顺序存储和链式存储的实现(C)

    //线性表的顺序存储 #include <stdio.h>typedef int DataType;#define MaxSize 15//定义顺序表typedef struct { Da ...

  5. c数据结构 -- 线性表之 复杂的链式存储结构

    复杂的链式存储结构 循环链表 定义:是一种头尾相接的链表(即表中最后一个结点的指针域指向头结点,整个链表形成一个环) 优点:从表中任一节点出发均可找到表中其他结点 注意:涉及遍历操作时,终止条件是判断 ...

  6. 线性表的顺序存储和链式存储c语言实现

    一.线性表的顺序存储 typedef int ElemType;typedef struct List { ElemType *data;//动态分配 ,需要申请空间 int length; }Lis ...

  7. Java实现链式存储的二叉查找树(递归方法)

    二叉查找树的定义: 二叉查找树或者是一颗空树,或者是一颗具有以下特性的非空二叉树: 1. 若左子树非空,则左子树上所有节点关键字值均小于根节点的关键字: 2. 若右子树非空,则右子树上所有节点关键字值 ...

  8. Java实现链式存储的二叉树

    二叉树的定义: 二叉树(BinaryTree)是n(n≥0)个结点的有限集,它或者是空集(n=0),或者由一个根结点及两棵互不相交的.分别称作这个根的左子树和右子树的二叉树组成. 二叉树的遍历方式主要 ...

  9. 线性表->链式存储->双向链表

    文字描述 之前的链表(单链表.循环链表)的链式存储结构中只有一个指示直接后继的指针域.由此,从某个结点出发只能顺指针往后寻查其他结点.若要寻查结点的直接前驱,则需从表头指针出发.即单链表中,NextE ...

随机推荐

  1. js 多个异步 的并发控制

    今天在群里看到一个人发的面试题: 1,请实现如下的函数,可以批量请求数据,所有的URL地址在urls参数中,同时可以通过max参数 控制请求的并发度.当所有的请求结束后,需要执行callback回调. ...

  2. 静态类和静态方法,抽象类和抽象方法,new关键字,值类型和引用类型,接口

    静态类和静态方法:静态成员是与类相关,而非实例相关:普通类中的静态成员:1.只能通过类名访问.2.静态方法中只能访问静态成员,或通过对象访问实例成员.3.多个对象共享同一个成员.静态类(一般用作工具类 ...

  3. MySQL设置远程连接服务器

    默认情况下,mysql只允许本地登录,如果要开启远程连接,则需要修改/etc/mysql/my.conf文件. 一.修改/etc/mysql/my.conf找到bind-address = 127.0 ...

  4. maven开发项目中遇到的问题 StartChild.call at java.util.concurrent.FutureTask.run

    这个原因是没有配置 pom中没有配置 <scope>provided</scope>  导致冲突 正确的配置文件如下 <project xmlns="http: ...

  5. H5+百度地图定位

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  6. Oracle 12c 创建用户

    本文转载自 http://blog.itpub.net/29047826/viewspace-1453577/,侵删.   12c推出了可插拔库,管理用户也分为两种:本地用户和公用用户.本地用户是指在 ...

  7. Oracle 汉字转拼音触发器

    --函数GetHzFullPY(string)用于获取汉字字符串的拼音 --select GetHzFullPY('中华人民共和国') from dual; --返回:ZhongHuaRenMinGo ...

  8. TCP网络参数优化

    TCP连接的状态 TCP连接过程是状态的转换,促使状态发生转换的因素包括用户调用.特定数据包以及超时等,具体状态如下所示: CLOSED:初始状态,表示没有任何连接. LISTEN:Server端的某 ...

  9. getSqlMapClientTemplate().insert()方法的返回值问题

    insert方法的返回值     今天碰到一个问题,就是关于ibatis的insert方法的返回值的问题.在网上找了很多例子,自己也亲自试了一下. 最后得出结论:insert方法返回的是在表中插入记录 ...

  10. R语言常用命令集合

    help.start()//打开帮助文档 q()//推出函数 ls()//返回处于现在名空间的对象名称 rm()//清楚对象:rm(list=ls())清除所有内存数据 gc()//垃圾回收数据 sq ...