之前有说过链表的首尾操作是十分快速,其实这句话不准确,对于单链表而言,只有一侧的操作快速,另一侧的操作依旧需要大量的移动指针(如果数据多的话);
双向链表与单向链表的区别:
1.增加了一个指针域保存上一个结点,内存占用大于单链表;
2.首尾两侧操作都很快速,而单链表只能保证一侧;
接下来实现一个双向链表:
1.定义MyList接口,提供线性表操作规范:
见作者的第一篇博客,《数据结构01-自定义ArrayList》
2.定义实现类,MyDLinkedList,便于与单链表区分,加入字母D:
package A01.LSQ;
import java.util.Iterator;
import java.util.NoSuchElementException;
public class MyDLinkedList<E> implements MyList<E>, Iterable<E>
{
// 测试方法
public static void main(String[] args)
{
MyDLinkedList<Integer> list = new MyDLinkedList<>();
list.add(4);
list.add(6);
list.add(9);
list.add(10);
list.set(0, -1);
for (Integer i:list)
{
System.out.println(i);
}
}
// 定义开始、结束的结点
private Node<E> bigen;
private Node<E> end;
// 链表长度
private int size;
public MyDLinkedList()
{
bigen = new Node<>(null, null, null);
end = new Node<>(null, bigen, null);
bigen.next = end;
size = 0;
}
public E get(int index)
{
return getNode(index).data;
}
private Node<E> getNode(int index)
{
return getNode(index, 0, size);
}
// 根据下标获取一个结点
private Node<E> getNode(int index, int low, int upper)
{
Node<E> p;
if (index < low || index > upper)
throw new IndexOutOfBoundsException("越界");
// 从首尾两侧寻找至目标结点最近的距离
if (index > size / 2)
{
p = bigen.next;
for (int i = 0; i < index; i++)
{
p = p.next;
}
} else
{
p = end;
for (int i = size; i > index; i--)
{
p = p.prev;
}
}
return p;
}
// 与以往不同,借用有参构造器来插入结点,使插入方法根据简洁易懂
public void addBefore(Node<E> p, E data)
{
Node<E> temp = new Node<>(data, p.prev, p);
temp.prev.next = temp;
p.prev = temp;
size++;
}
public E add(E data)
{
return add(size, data);
}
public E add(int index, E data)
{
addBefore(getNode(index), data);
return data;
}
// 倒序遍历
public void disPlay()
{
System.out.println("-----------");
Node<E> temp = end;
// 由于头结点存在,且data为null,所以要过滤掉头结点
while (temp.prev.prev!= null)
{
temp = temp.prev;
System.out.println(temp.data);
}
}
private class Node<E>
{
Node<E> next;
Node<E> prev;
E data;
public Node(E data, Node<E> prev, Node<E> next)
{
this.next = next;
this.prev = prev;
this.data = data;
}
}
@Override
public int size()
{
return size;
}
@Override
public boolean remove(int index)
{
if (index < 0 || index >= size)
throw new IndexOutOfBoundsException("越界");
remove(getNode(index));
return true;
}
@Override
public boolean remove(E data)
{
if (size == 0)
return false;
int conut = 0;
Node<E> temp = bigen;
while (temp.next != null)
{
temp = temp.next;
if (temp.data.equals(data))
{
remove(conut);
return true;
}
conut++;
}
return false;
}
@Override
public E set(int index, E data)
{
if (size == 0)
return null;
getNode(index).data = data;
return data;
}
private void remove(Node<E> p)
{
p.next.prev = p.prev;
p.prev.next = p.next;
size--;
}
@Override
public Iterator<E> iterator()
{
return new MyDLinkedList.MyLinkedListIterator();
}
private class MyLinkedListIterator implements Iterator<E>
{
private int current = 0;
@Override
public void remove()
{
MyDLinkedList.this.remove(--current);
}
@Override
public boolean hasNext()
{
return current < size;
}
@Override
public E next()
{
if (!hasNext())
throw new NoSuchElementException();
return get(current++);
}
}
}