双向链表的原理

    对于普通的单向链表,如果在它的内部类中再加一个Node prev 属性(这属性代表向前的指针,指向它前面一个结点),然后在链表类中加一个Node tail指向它的尾部,就构成了双向链表。

    链表只能从头往尾遍历, 那我可以在Node内部类中再添加一个属性Node prev,这样Node内部类中就有了三个属性:E data保存数据,Node next 指向下一个结点, Node prev指向上一个结点。

    类似地,也可以来一个虚拟尾节点dummyTail,在链表类的构造方法中,分别令dummyHead指向虚拟头结点,dummyTail指向虚拟尾结点,然后令dummyHead.next = dummyTail, dummyTail.prev = dummyHead , 而dummyHead. prev = null, dummyTail.next = null   。   这样就构造好了初始的链表。

    然后向链表中增添或者删除一个结点,先判断这个索引是离头结点还是离尾节点更近,如果index < size/2,说明离头近,从头遍历,  如果index > size/2 说明离尾近,从尾遍历。 遍历的方法都是类似的,都是设置一个临时指针temp,如果离头近,那么我就 Node temp = dummyhead ,(循环 temp =temp.next ) ; 如果离尾近,我就Node temp = dummyTail,(循环 temp = temp.prev) .

    这样的话,添加头,添加尾  和   删除头,删除尾的时间复杂度都是1 .    这样的话,链表就对称了,意思是从头到尾进行操作,和从尾到头进行操作,并没有复杂度不同的问题。

    以下为代码

public class myLinkList<E> implements LinkList<E>{
 
 private class Node{
  E data;
  Node prev;
  Node next;
  Node(E data,Node prev,Node next)
  {
   this.data = data;
   this.prev = prev;
   this.next = next;
  }
  Node(E data)
  {
   this(data,null,null);
  }
  Node()
  {
   this(null,null,null);
  }
 }
 
 private Node dummyHead;
 private Node dummyTail;
 private int size;
 public myLinkList()
 {
  dummyHead = new Node();
  dummyTail = new Node();
  dummyHead.next = dummyTail;
  dummyTail.prev = dummyHead;
  size = 0;
 }
 @Override
 public int getSize() 
 {
  return size;
 }
 @Override
 public boolean isEmpty() 
 {
  return (size==0);
 }
 @Override
 public void addTo(int index, E elem) 
 {
  if((index < 0)||(index > size))
  {
   throw new NullPointerException("无法添加结点,索引不合法");
  }
  Node insert = new Node(elem);
  if(index < size/2)
  {
   Node temp = dummyHead;
   for(int i=0 ; i<index ; i++)
   {
    temp = temp.next;
   }
   Node afterInsert = temp.next;
   insert.next = afterInsert;
   insert.prev = temp;
   temp.next = insert;
   afterInsert.prev = insert;
   
  }
  else
  {
   Node temp = dummyTail;
   for(int i = size-1; i>=index ; i--)
   {
    temp = temp.prev;
   }
   Node beforeInsert = temp.prev;     
   insert.prev = beforeInsert;      
   insert.next = temp;     
   temp.prev = insert; 
   beforeInsert.next = insert;
   
  }
  size++;
 }
 @Override
 public void addFirst(E elem) 
 {
  addTo(0, elem);
 }
 @Override
 public void addLast(E elem) 
 {
  addTo(size, elem); 
 }
 @Override
 public void set(int index, E elem) 
 {
  if((index < 0)||(index >= size))
  {
   throw new NullPointerException("无法改变,索引不合法");
  }
  if(index <size/2)
  {
   Node temp = dummyHead.next;
   for(int i =0;i<index;i++)
   {
    temp = temp.next;
   }
   temp.data = elem;
  }
  else
  {
   Node temp = dummyTail.prev;
   for(int i=size-1;i>index;i--)
   {
    temp = temp.prev;
   }
   temp.data = elem;
  }
 }
 @Override
 public E get(int index) 
 {
  if((index < 0)||(index >= size))
  {
   throw new NullPointerException("无法获取到索引,索引不合法");
  }
  if(index <size/2)
  {
   Node temp = dummyHead.next;
   for(int i =0;i<index;i++)
   {
    temp = temp.next;
   }
   return temp.data;
  }
  else
  {
   Node temp = dummyTail.prev;
   for(int i=size-1;i>index;i--)
   {
    temp = temp.prev;
   }
   return temp.data;
  }
 }
 @Override
 public E getFirst() 
 {
  return get(0);
 }
 @Override
 public E getLast() 
 {
  return get(size-1);
 }
 @Override
 public int indexOf(E elem) 
 {
  Node temp = dummyHead.next;
  for(int i=0;i<size;i++)
  {
   if(temp.data.equals(elem)==true)
   {
    return i;
   }
   temp = temp.next;
  }
  return -1;
 }
 @Override
 public boolean contains(E elem) 
 {
  return (indexOf(elem)!=-1);
 }
 @Override
 public E deleteAt(int index) 
 {
  if((index < 0)||(index >= size))
  {
   throw new NullPointerException("无法删除结点,索引不合法");
  }
  E result = get(index);
  if(index < size/2)
  {
   Node temp = dummyHead;
   for(int i = 0; i<index ;i++)
   {
    temp = temp.next;
   }
   Node wannaDelete = temp.next;
   temp.next = wannaDelete.next;
   wannaDelete.next.prev = temp;
   wannaDelete.prev = null;
   wannaDelete.next = null;
  }
  else
  {
   Node temp = dummyTail;
   for(int i = size-1;i>index;i--)
   {
    temp = temp.prev;
   }
   Node wannaDelete = temp.prev;
   temp.prev = wannaDelete.prev;
   wannaDelete.prev.next = temp;
   wannaDelete.prev = null;
   wannaDelete.next = null;
  }
  size--;
  return result;
 }
 @Override
 public E deleteFirst() 
 {
  return deleteAt(0);
 }
 @Override
 public E deleteLast() 
 {
  return deleteAt(size-1);
 }
 @Override
 public void delete(E elem) 
 {
  if(contains(elem)==true)
  {
   deleteAt(indexOf(elem));
  }
 }
 @Override
 public void deleteAll(E elem) 
 {
  while(contains(elem)==true)
  {
   deleteAt(indexOf(elem));
  }
 }
 @Override
 public String toString() 
 {
  StringBuilder result = new StringBuilder("[");
  if(size ==0)
  {
   result.append("]");
   return result.toString();
  }
  for(int i =0; i <size;i++)
  {
   result.append(get(i));
   if(i<size-1)
   {
    result.append(", ");
   }
   else
   {
    result.append("]");
   }
  }
  return result.toString();
 }
}
interface LinkList<E>{ 
int getSize(); 
boolean isEmpty();
 void addTo(int index,E elem);
 void addFirst(E elem); 
 void addLast(E elem); 
 E get(int index); 
 E getFirst(); 
 E getLast(); 
 void set(int index,E elem);
 boolean contains(E elem);
 E deleteAt(int index);
 E deleteFirst();
 E deleteLast();
 int indexOf(E elem);
 void delete(E elem);
 void deleteAll(E elem);
         }

经过测试,我写的双向链表可以成功地进行数据的增删改查。

点赞