双向链表添加和删除 java

本文介绍了双向链表的概念,并对比了ArrayList和LinkedList的优缺点。重点讲解了Java中LinkedList作为双向链表的实现,展示了如何进行添加和删除操作。通过了解双向链表,可以更好地理解其在内存中的结构以及相对于单链表的特性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

为了更好地理解双向链表,先说几个相关的概念

List:特性:必须按照插入的顺序来保存元素

第一种实现:最常用的arrylist:结构类似于数组,所以访问元素的时候可以直接通过索引来访问任何位置的数据,但是当插入元素的时候默认是追加到数组的最后,那么数组当中原有的元素的位置不变 只是申请开辟了一块内存空间和新增加了一个索引,其他都没有变化,但是当向其他位置插入元素的时候,会先申请开辟一块内存空间和一个新索引 但是这个索引不是给新插入元素使用的,而是给数组当中最后一个元素使用的,新元素会插入到指定索引位置 代替原索引处的元素 并将该元素以及其后面的所有元素全部向后移动,所以这个是浪费时间的,而删除呢就是一样的原理,队尾删除很简单,其他位置删除。位于被删除元素后面的元素的位置全部向前移动。所以一样很浪费时间。所以该实现只是适用于随机访问元素或者遍历元素,因为他的底层是由数组来实现的。

第二种常用的实现是:linkedlist,它是基于链表实现的,链表有很多种
链表是一系列的节点组成的,这些点不需要在内存中相连
单链表是由一个头结点开始。然后依次插入新的节点。每个节点包含两个部分一个是数据的引用或者基本类型的数据 和下一个节点的存储地址。这样一个链表向外暴露的只是第一个头结点。所以只要知道头结点就可以直接找到剩下其余的节点。

单链表的内存结构如下图:
在这里插入图片描述
头结点不存储数据 其他节点存储的结构看下图 是数据加上下一个节点的地址
在这里插入图片描述

双向链表:双向链表的每一个结点都有一条指向其后继结点的next链和一条指向其前结点的pre链。双向链表既可以从第一项开始遍历也可以从最后一项开始往前遍历,双向链表可以用下图表示:
在这里插入图片描述
java的linkedlist实现了双向链表 看下linkedlist的用法

import java.util.ArrayList;
import java.util.LinkedList;
import org.junit.Before;

public class Test {
	 
	 public static LinkedList<String> dl;
	 public static ArrayList<String> l;
	 
	 @Before
	 public void init() {
		 dl = new LinkedList<String>();
		 dl.add("N1");
         dl.add("N2");
         dl.add("N3");
         dl.add("N4");
         dl.add("N5");
         
         l = new ArrayList<String>();
         l.add("N1");
         l.add("N2");
         l.add("N3");
         l.add("N4");
         l.add("N5");
	}
	 
	 @org.junit.Test
	 public void test() {
		 for (String str : dl) {
			System.out.println("双向链表:" + str);
		}
		 
		 for (String str : l) {
			 System.out.println("普通arrylist:" + str);
		}

	}
	 
	 @org.junit.Test
	 public void add() {
		// 双向链表独有的向链表头部添加元素
        dl.addFirst("N6");
        dl.addLast("N7");
	 }
	 
	 @org.junit.Test
     public void del() {
        dl.remove("N1");

        for (String str : dl) {
            System.out.println("双向链表:" + str);
        }
        l.remove("N1");
        for (String str : l) {
            System.out.println("普通arrylist:" + str);
        }

    }
	 
}

最后 是java实现双向链表的代码

public class DoubleLink<T> {
	 
	private class Node<T>{
		
		//节点值
		private T value;
		
		//前一个节点
		private Node<T> prev;
		
		//后一个节点
		private Node<T> prex;

		public Node(T value, Node<T> prev, Node<T> prex) {
			this.value = value;
			this.prev = prev;
			this.prex = prex;
		}
	}

	//链表长度
	private int size;
	
	//头结点
	private Node<T> head;
	
	public DoubleLink() {
		/**
		 * 头结点不存储值 并且头结点初始化时 就一个头结点。
		 * 所以头结点的前后节点都是自己
		 * 并且这个链表的长度为0;
		 */
		head = new Node<>(null, null, null);
		head.prev = head.prex;
		head = head.prex;
		size = 0;
	}
	
	public int getSize() {
		return this.size;
	}
	
	 /**
     * 判断链表的长度是否为空
     */
    public boolean isEmplty(){
        return size == 0;
    }
   /**
    * 判断索引是否超出范围
    */
    public void checkIndex(int index){
        if(index<0||index>=size){
            throw new IndexOutOfBoundsException();
        }
        return;
    }
    /**
     * 通过索引获取链表当中的节点
     * 
     */
    public Node<T> getNode(int index){
        /**
         * 检查该索引是否超出范围
         */
        checkIndex(index);
        /**
         * 当索引的值小于该链表长度的一半时,那么从链表的头结点开始向后找是最快的
         */
        if(index<size/2){
            Node<T> cur = head.prex;
            for(int i=0;i<index;i++){
                cur = cur.prex;
            }
            return cur;
        }
        /**
         * 当索引值位于链表的后半段时,则从链表的另端开始找是最快的
         */
        /**
         * 此
         */
        Node<T> cur = head.prev;
        int newIndex = size - (index+1);
        for(int i=0;i<newIndex;i++){
            cur = cur.prev;
        }
        return cur;
    }
    /**
     * 获取节点当中的值
     */
    public T getValue(Node<T> cur){
        return cur.value;
    }
    /**
     * 获取第一个节点的值
     */
    public T getFirst(){
        return getValue(getNode(0));
    }
    /**
     * 获取最后一个节点的值
     */
    public T getLast(){
        return getValue(getNode(size-1));
    }
    /**
     * 插入节点
     */
    public void inesert(int index,T value){
        //如果这次插入时 链表是空的
        if(index==0){
            //这个节点的
            Node<T> cur = new Node<T>(value, head, head.prex);
            head.prex.prev = cur;
            head.prex = cur;
            size++;
            return;
        }
        /**
         * 先根据给出的插入位置 找到该链表原来在此位置的节点
         */
        Node<T> node = getNode(index);
        /**
         *放置的位置的前一个节点就是原节点的前置节点 而后节点就是原节点
         */
        Node<T> cur = new Node<T>(value,node.prev,node);
        /**
         * 现将该位置也就是 原节点的前节点的后节点 赋值成为新节点
         * 然后将新节点的后置节点的值赋值成为原节点
         */
        node.prev.prex = cur;
        node.prev = cur;
        size++;
    }
    /**
     * 向表头插入数据
     */
    public void insertTo(T Value)
    {
        inesert(0,Value);
    }
    /**
     * 将元素插入到链表的尾部
     */
    public void insertTotatil(T vlaue){
        Node<T> cur = new Node<>(vlaue,head.prev, head);
       //head.prev 代表原来的尾部节点
        //遵循两个原则 一 新插入节点的前一个节点的后一个节点为新节点。新节点的后一个节点的前一个节点是新节点
        head.prev.prex = cur;
        head.prev = cur;
        size++;
    }
   /**
    * 删除节点的方法
    */
    public void del(int index){
        checkIndex(index);
        Node<T> cur = getNode(index);
        //记住此时的指针还没断开 赋值以后才相当于断开 
        cur.prev.prex = cur.prex;
        cur.prex.prev = cur.prev;
        size--;
        cur = null;
        return;
    }
   /**
    * 删除第一个节点
    */
   public void  delFirst(){
       del(0);
   }
   /**
    * 删除最后一个节点
    */
   public void delLast(){
       del(size-1);
   }
	 
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值