对于普通的单向链表,如果在它的内部类中再加一个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);
}经过测试,我写的双向链表可以成功地进行数据的增删改查。
随时随地看视频