数据结构基础java实现—双向链表

设计双向链表需要两个类

1. LinkedList类,包含头结点first,尾节点last,节点个数size,记录头结点和尾节点有助于理解和简化编程。提供最基本的方法为:在指定索引位置插入和删除数据。

2. Node类,包含一个泛型对象的引用item,一个前引用prev,一个后引用next,这个可以设置为内部静态类。


在指定索引位置插入和删除数据,需要通过node(int index)方法返回index位置上的Node数据一遍操作。在返回index位置上的node数据时,要考虑index与size的关系,从而判别是从前往后查找还是从后往前查找。

插入和删除数据时需要分三种情况考虑:头结点插入删除,尾节点插入删除,中间节点插入删除。每种情况均写成私有的方法,由public方法根据实际情况调用。

在public的方法中,凡是牵扯的index的操作都要对index做检查,防止下标越界,这个由checkIndex(int index)方法完成。需要注意的是插入数据时的下标检查与其他时候的下标检查不一致,因为插入数据时index是可以等于size的。

以下代码为个人所写代码:

package wenpq.util;

/**
 * @author wenpq
 * wenpq的linkedList
 */
public class LinkedList<E>{

	private int size = 0;
	private Node<E> first; // 头结点与尾节点的使用,排除了特殊情形简化编码
	private Node<E> last;
	
	// ------------------------------------------------构造函数
	public LinkedList() {}
	
	// ------------------------------------------------内部使用函数
	// 检查下标是否越界
    private void checkIndex(int index) {
        if (!(index >= 0 && index < size))
            throw new IndexOutOfBoundsException("Index: "+index+", Size: "+size);
    }
    
	// 返回index位置上的node
    Node<E> node(int index) {
    	// 右移1 和除2取整等价
    	if(index<(size>>1)){
    		// 从前向后 连接到指定节点
    		Node<E> x=first;
    		for(int i=0;i<index;i++)
    			x = x.next;
    		return x;
    	}else{
    		// 从后向前 连接到指定节点
    		Node<E> x=last;
    		for(int i=size-1;i>index;i--)
    			x = x.prev;
    		return x;
    	}
    }
    
    // 头节点前插入
	void linkFirst(E e){
		Node<E> f = first;
		Node<E> newNode = new Node<>(null,e,f);
		if (f==null){
			last = newNode;
		}else{
			f.prev = newNode;			
		}
		first = newNode;
		size++;
	}
	// 尾节点后插入
	void linkLast(E e){
		Node<E> l = last;
		Node<E> newNode = new Node<>(l,e,null);
		if(l==null){
			first = newNode;
		}else{
			l.next = newNode;
		}
		last = newNode;
		size++;
	}
	// 中间前节点插入  插入后 element位置就是index 故要插入原来位置之前
	void linkBefore(E e, Node<E> succ){
		Node<E> newNode = new Node<>(succ.prev,e,succ);
		succ.prev.next = newNode;
		succ.prev = newNode;
		size++;
	}
	// 头节点删除
    void unlinkFirst(Node<E> f) {
        final Node<E> next = f.next;
        f.item = null;
        f.next = null; // help GC
        first = next;
        if (next == null)
            last = null;
        else
            next.prev = null;
        size--;
    }
    // 尾节点删除
    void unlinkLast(Node<E> l) {
        final Node<E> prev = l.prev;
        l.item = null;
        l.prev = null; // help GC
        last = prev;
        if (prev == null)
            first = null;
        else
            prev.next = null;
        size--;
    }
    // 中间节点删除
    void unlink(Node<E> x) {
        final Node<E> next = x.next;
        final Node<E> prev = x.prev;
        
        prev.next = next;
        x.prev = null;
        next.prev = prev;
        x.next = null;
        x.item = null;
        size--;
    }
	
	
	// ------------------------------------------------可以继承自接口的一些操作(没有写完)
	public int size() {
		return size;
	}

	public boolean add(E e) {
		linkLast(e);
		return true;
	}

	public E get(int index) {
		checkIndex(index);
        return node(index).item;
	}

	public boolean set(int index, E element) {
		checkIndex(index);
        Node<E> x = node(index);
        x.item = element;
        return true;
	}

	public void add(int index, E element) {
		// 注意在add的时候index是可以等于size的
        if (!(index >= 0 && index <= size))
            throw new IndexOutOfBoundsException("Index: "+index+", Size: "+size);
        if (index == size){
        	linkLast(element);
        }else{
        	if(index ==0 ){
        		linkFirst(element);
        	}else 
                linkBefore(element, node(index));		
        }       
	}

	public void remove(int index) {
		checkIndex(index);
        if (index == size-1){
        	unlinkLast(node(index));
        }else{
        	if(index ==0 ){
        		unlinkFirst(node(index));
        	}else 
        		unlink(node(index));		
        }  		
	}


	/**
	 * 采用静态内部类 减少代码 较少动态创建开销
	 */
	private static class Node<E> {
		// 一个E类型引用
		// 一个前引用
		// 一个后引用
		Node<E> prev;
		E item;
		Node<E> next;
		public Node(Node<E> prev, E item, Node<E> next) {
			super();
			this.prev = prev;
			this.item = item;
			this.next = next;
		}
	}
}

以下为测试代码:

public class Test {

	public static void main(String[] args) {
	       LinkedList<String>  list = new LinkedList<String>();  
	        
	        list.add("one");  
	        list.add("two");  
	        list.add("three");        	          
	        list.add(0,"first");  
	        list.add(list.size(),"end");  
	        
	        list.set(1, "new one");
	        for(int i=0;i<list.size();i++){
	        	System.out.println(list.get(i));
	        }
	        
	        list.remove(3);
	        list.remove(3);	        
	        for(int i=0;i<list.size();i++){
	        	System.out.println(list.get(i));
	        }
	}
}



对比util的LinkedList的代码,标准库则做了更多考虑,在类申明时:

public class LinkedList<E>
    extends AbstractSequentialList<E>
    implements List<E>, Deque<E>, Cloneable, java.io.Serializable

之所以继承AbstractSequentialList ,是因为它实现了get(int index)、set(int index, E element)、add(int index, E element) 和 remove(int index)这些骨干性函数。降低了List接口的复杂度。这些接口都是随机访问List的,LinkedList是双向链表;既然它继承于AbstractSequentialList,就相当于已经实现了“get(int index)这些接口”。

java.io.Serializable用于声明该类的对象可以序列化,在socket通信和远程方法调用时会用到。实际上在源代码中头结点first,尾节点last,节点个数size这些都被transient关键字修饰,限定了不可能序列化的属性。

而在完成get,set,add方法时也不像上面代码那样分三种情况讨论,因为它调用的内部方法已经做了考虑。

对于remove方法,util源码返回了remove的对象,可能是因为考虑到同时要取数据燃耗删除数据的操作吧,目前还明白为什么要这么做,先留着,以后再来想。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值