线性表的Java实现--链式存储(双向链表)

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

双向链表的一般情况:

增加节点:

删除节点:

双向链表的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());
    }

}

  

时间: 2024-10-17 03:33:03

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

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

线性表的Java实现--链式存储(单向链表) 单向链表(单链表)是链表的一种,其特点是链表的链接方向是单向的,对链表的访问要通过顺序读取从头部开始. 链式存储结构的线性表将采用一组任意的存储单元存放线性表中的数据元素.由于不需要按顺序存储,链表在插入.删除数据元素时比顺序存储要快,但是在查找一个节点时则要比顺序存储要慢. 使用链式存储可以克服顺序线性表需要预先知道数据大小的缺点,链表结构可以充分利用内存空间,实现灵活的内存动态管理.但是链式存储失去了数组随机存取的特点,同时增加了节点的指针域,空

2、线性表的实现:链式存储、单链表

1 package ren.laughing.datastructure.baseImpl; 2 3 import ren.laughing.datastructure.base.List; 4 import ren.laughing.datastructure.base.Strategy; 5 import ren.laughing.datastructure.exception.OutOfBoundaryException; 6 /** 7 * 线性表的实现:链式存储结构:单链表 8 * @

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

前几章已经介绍到了顺序存储.链式存储 顺序存储:初始化.插入.删除.定位 链式存储:初始化.插入.删除.定位 顺序存储:初始化 strudt student{ int ID://ID char name[30];//姓名 char sex; //性别 int class;//班级 int age;//年龄 } student={"01",“zhangsan”,"m","201","20"}: 链式存储:初始化 //建立一个空链

数据结构学习笔记(二) 线性表的顺序存储和链式存储

线性表:由同类型数据元素构成有序序列的线性结构 -->表中元素的个数称为线性表的长度 -->没有元素时,成为空表 -->表起始位置称表头,表结束位置称表尾 顺序存储: 1 package test; 2 3 /** 4 * 线性表(数组) 5 * 6 */ 7 public class Test { 8 private static int m ; 9 private static int[] a; 10 public static void main(String[] args) {

线性表的顺序存储和链式存储

顺序存储是分配了一块连续的内存,把这块内存平均分为N份,每份存放一个线性表的单元.从内存利用角度讲,顺序存储需要的是一块连续的内存.特点是查找节点容易,插入. 删除节点比较耗时. 链式存储是分配了几个零散的非连续单元,从内存利用角度讲,它能充分利用那些碎片化的内存.特点是查找节点慢,但插入删除节点快. 怎么理解呢? 下文说的酒店均没有客户记录,就是没有登记某个人住在某个房间. 顺序存储就和某个酒店的一层房间,比如在第一层有100个房间,从1号房间按顺序一致排到底100号房间.你去找一个人,打开1

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

复杂的链式存储结构 循环链表 定义:是一种头尾相接的链表(即表中最后一个结点的指针域指向头结点,整个链表形成一个环) 优点:从表中任一节点出发均可找到表中其他结点 注意:涉及遍历操作时,终止条件是判断 p->next == L? 双向链表 定义:在单链表的每个结点离再增加一个指向直接前驱的指针域 prior,这样链表中就形成了有 两个方向不用的链,故称为双向链表 双向循环链表 定义: 和单链的循环表类似,双向链表也可以有循环表 ·让头节点的前驱指针指向链表的最后一个结点 ·让最后一个结点的后继指

Java实现链式存储的二叉树

二叉树的定义: 二叉树(BinaryTree)是n(n≥0)个结点的有限集,它或者是空集(n=0),或者由一个根结点及两棵互不相交的.分别称作这个根的左子树和右子树的二叉树组成. 二叉树的遍历方式主要有:先序遍历(NLR),中序遍历(LNR),后序遍历(LRN),和层次遍历. 注意: 由二叉树的先序序列和中序序列可以唯一地确定一颗二叉树: 由二叉树的后序序列和中序序列可以唯一地确定一颗二叉树: 由二叉树的层序序列和中序序列可以唯一地确定一棵二叉树: 但,由二叉树的先序序列和后序序列无法唯一地确定

Java实现线性表-顺序表示和链式表示

顺序表示和链式表示的比较: 1.读写方式:顺序表可以顺序存取,也可以随机存取:链表只能从表头顺序存取元素: 2.逻辑结构与物理结构:顺序存储时,逻辑上相邻的元素其对应的物理存储位置也相邻:链式存储时,逻辑上相邻的元素,其物理存储位置则不一定相邻: 3.查找.插入和删除操作: 按值查找,当线性表在无序的情况下,两者的时间复杂度均为o(n):而当顺序表有序时,可采用折半查找,此时时间复杂度为o(log n): 按位查找,顺序表支持随机访问,时间复杂度为o(1):而链表的平均时间复杂度为o(n). 顺

数据结构(四)——基于链式存储结构的线性表

数据结构(四)--基于链式存储结构的线性表 一.基于链式存储结构的线性表 1.链式存储的定义 链式存储为了表示数据元素与其直接后继元素间的逻辑关系,数据元素除了存储本身的信息外,还需要存储直接后继的信息.相连的数据元素之间在存储空间中不要求连续.链式存储的逻辑结构基于链式存储结构的线性表中,每个结点都包含数据域和指针域.数据域用于存储数据元素本身,指针域用于存储相邻结点的地址. 2.链表的定义 链表是基于链式存储结构的线性表链表的基本元素:头结点:链表中的辅助结点,包含指向第一个数据元素的指针数