虚拟头节点是什么?
虚拟头节点就是,在原本第一个节点之前,增加一个节点。即在链表为空时,仅有一个节点。这样操作的好处时,不需要在添加和删除的时候判断是否待删除或者待添加的索引为0
使用虚拟头节点有什么好处
我们先来对比以下,使用和未使用
对于添加元素方法
未使用添加元素
在理解上图后,要明白一个点,我们想在index插入节点,那么prev必须指向index的前一个节点!!,所以说,我们对于add方法内,要判断index是否为0
但是加上了虚拟头节点呢???,加上虚拟头节点后,我们从虚拟头节点的位置开始移动,那么只需要比没有移动时需要移动的次数大1即可,其他操作并没有区别
public void add(int index,E e){
if (index < 0 || index > size){
throw new IllegalArgumentException("Add failed");
}
if(index==0){
Node e = new Node(e);
e.next=head;
head=e;
}else{
Node prev = head;
for (int i = 0; i < index-1; i++) {
prev = prev.next;
}
Node node = new Node(e);
node.next = prev.next;
prev.next = node;
size++;
}
}
在使用虚拟头节点后,就不需要判断了
public void add(int index,E e){
if (index < 0 || index > size){
throw new IllegalArgumentException("Add failed");
}
Node prev = dummyHead;
for (int i = 0; i < index; i++) {
prev = prev.next;
}
Node node = new Node(e);
node.next = prev.next;
prev.next = node;
size++;
}
删除节点
这里的prev还是需要指向index前一个元素的
下面直接写带有虚拟头节点的删除方法
public E delete(int index){
if (index < 0 || index >= size){
throw new IllegalArgumentException("Remove failed");
}
// 初始
Node prev = dummyHead.next;
for (int i = 0; i < index-1; i++) {
prev = prev.next;
}
Node retNode = prev.next;
prev.next = retNode.next;
retNode.next = null;
size--;
return retNode.e;
}
使用Java创建带有虚拟头节点的链表
public class Link<E> {
// 增删改查都是On
// 只对表头操作增删查是o1
private class Node{
public E e;
public Node next;
public Node(E e ,Node next){
this.e = e;
this.next = next;
}
public Node(E e) {
this(e,null);
}
public Node(){
this(null,null);
}
@Override
public String toString() {
return super.toString();
}
}
private Node dummyHead;
private int size;
public Link(){
dummyHead = new Node(null,null);
size = 0;
}
public int getSize(){
return size;
}
public boolean isEmpty(){
return size == 0;
}
public void addFirst(E e){
add(0,e);
}
public void add(int index,E e){
if (index < 0 || index > size){
throw new IllegalArgumentException("Add failed");
}
Node prev = dummyHead;
// 直到 index + 1个
for (int i = 0; i < index; i++) {
prev = prev.next;
}
Node node = new Node(e);
node.next = prev.next;
prev.next = node;
size++;
}
public void addLast(E e){
add(size,e);
}
public E get(int index){
if (index < 0 || index >= size){
throw new IllegalArgumentException("Get failed");
}
Node cur = dummyHead.next;
for (int i = 0; i < index; i++) {
cur = cur.next;
}
return cur.e;
}
public E getFirst(){
return get(0);
}
public E getLast(){
return get(size-1);
}
public void set(int index,E e){
if (index < 0 || index >= size){
throw new IllegalArgumentException("Set failed");
}
Node cur = dummyHead.next;
for (int i = 0; i < index; i++) {
cur = cur.next;
}
cur.e = e;
}
public boolean contains(E e){
Node cur = dummyHead.next;
while(cur != null){
if (cur.e.equals(e)){
return true;
}
cur = cur.next;
}
return false;
}
public E delete(int index){
if (index < 0 || index >= size){
throw new IllegalArgumentException("Remove failed");
}
// 初始
Node prev = dummyHead.next;
for (int i = 0; i < index-1; i++) {
prev = prev.next;
}
Node retNode = prev.next;
prev.next = retNode.next;
retNode.next = null;
size--;
return retNode.e;
}
@Override
public String toString() {
StringBuffer res = new StringBuffer();
Node cur = dummyHead.next;
while(cur!=null){
res.append(cur.e+"->");
cur = cur.next;
}
res.append("NULL");
return res.toString();
}
}