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

双向链表的一般情况:

增加节点:

删除节点:

双向链表的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. (Android+IOS)正在做一个新闻App,做的差不多了,听听大家的建议 (图)

    (Android+IOS)正在做一个新闻App,做的差不多了,听听大家的建议! 新闻采集器做好了,前端展示APP界面感觉还不是很好,还需要改进改进,希望发布(Android和IOS版本)前听听大家的建 ...

  2. 第3章—高级装配—bean的作用域

    bean的作用域 bean的默认作用域 Spring定义了多种作用域,可以基于这些作用域创建bean,包括: 单例(Singleton):在整个应用中,只创建bean的一个实例. 原型(Prototy ...

  3. jenkins创建构建任务

    构建项目类型 点击 Jenkins 首页 “创建一个新任务” 的链接, 输入任务名称 Jenkins 提供了六种类型的任务. 构建一个自由风格的软件项目 这是Jenkins的主要功能.Jenkins ...

  4. IE haslayout的理解与bug修复

    要想更好的理解 css, 尤其是 IE 下对 css 的渲染,haslayout 是一个非常有必要彻底弄清楚的概念.大多 IE下的显示错误,就是源于 haslayout 什么是 haslayout ? ...

  5. Mac 提交代码到Github

    然后在GitHub上创建版本库(Repository),在GitHub首页上,点击“Create a New Repository”,如下所示(为了便于后面演示,创建README.md这步暂不勾选): ...

  6. leetcode简单题目两道(5)

    Problem Given an integer (signed bits), write a function to check whether it . Example: Given num = ...

  7. 了解SSL必须要懂得密码技术

    要理解SSL就必须理解密码系统.消息摘要函数(单向或散列函数)和数字签名,这些技术是许多文献所讨论的主题(比如[AC96),提供了保密性.完整性和认证的基础. 密码系统 假设Alice想给她的银行发一 ...

  8. ES6常用七大特性

    ES6可谓是对JS语言的一个颠覆性改变,增加了Module改善JS一直被诟病的模块化.Promise解决异步函数的回调地狱.Class的面相对象编程... 在学习ES6的过程中,大家或多或少都有看过阮 ...

  9. height百分比失效

    heigh:100%失效 解决方案: 第一种 html, body { height: 100%; } 第二种 div { height: 100%; position: absolute; } 非定 ...

  10. [转]Web API OData V4 Keys, Composite Keys and Functions Part 11

    本文转自:https://damienbod.com/2014/09/12/web-api-odata-v4-keys-composite-keys-and-functions-part-11/ We ...