链表工作原理
问题1:虚拟结点的使用,为什么要使用虚拟结点?
虚拟结点就是C数据结构中所说的头指针,它的next指针指向第一个结点(当然Java中是没有指针的);使用了虚拟结点,在删除第一个结点时不会误删导致整个链表丢失,有利于维护链表。同时,使用虚拟结点可以简化逻辑,头结点作为链表的第一个结点无论是在删除和添加上都需要特别对待,虚拟结点可以使头结点一般化,简化它的删除和添加操作。
问题2:链表中未使用数组,索引从何而来?
链表也是线性表,不论是逻辑顺序和存储顺序上,表中元素都是相邻的;所以我们可以从第一个结点开始到最后一个结点,给他指定一个索引来方便我们指定结点。
问题3:索引是如何在链表中进行移动的呢?
在链式存储结构中,我们通过next引用将结点连接起来,这样我们通过next引用的不断向后传递,索引随着next移动在链表中移动。
问题4:为什么使用内部类,来定义结点的数据结构?
在定义结点类时,不光使用内部类,同时利用 private 将结点类封装在链表中,将底层数据封装起来,留下接口给程序员使用,可以防止链表被破坏。
实现的方法
- isEmpty():判断链表是否为空
- contains(AnyType data):链表是否包含某个元素
- getSize():获得链表中存储元素个数
- getFirst():查询链表中的第一个元素
- get(int idx):根据给定索引查询链表中的元素
- getLast():查询链表中的最后一个元素
- addFirst():链表的头插法
- add(int idx, AnyType data):向指定索引位置插入元素
- addLast():链表的尾插法
- removeFirst():删除表头元素
- remove(int idx, AnyType data):删除指定索引位置的元素
- removeLast():删除表尾元素
- find():查找元素对应的索引值
具体代码实现
public class MyLinkedList <AnyType>{
private Node head;
private int size;
private class Node{
AnyType data;
Node next;
public Node() {
data = null;
next = null;
}
public Node(AnyType data, Node next) {
this.data = data;
this.next = next;
}
}
public MyLinkedList() {
head = new Node();
size = 0;
}
public boolean isEmpty() {
return head.next == null;
}
public int getSize() {
return size;
}
public boolean contains(AnyType data) {
if(isEmpty()) {
return false;
}
Node prev = head;
while(prev.next != null) {
prev = prev.next;
if(prev.data == data)
return true;
}
return false;
}
public AnyType getFirst() {
Node prev = head.next;
return prev.data;
}
public AnyType get(int idx) {
Node prev = getNode(idx);
return prev.data;
}
public AnyType getLast() {
return get(size-1);
}
public void addFirst(AnyType data) {
/*Node node = new Node(data, head.next);
head.next = node;
size++;*/
add(0, data);
}
public void add(int idx, AnyType data) {
Node prev;
if(idx == 0)
prev = head;
else
prev = getNode(idx - 1);
Node node = new Node(data, prev.next);
prev.next = node;
size++;
}
public void addLast(AnyType data) {
add(size, data);
}
public void removeFirst() {
/*if(isEmpty())
throw new IllegalArgumentException("空表无法进行删除操作");
Node outNode = head;
head = head.next;
outNode.next = null;
size--;*/
remove(0);
}
public void remove(int idx) {
Node prev;
if(idx == 0)
prev = head;
else
prev = getNode(idx - 1);
Node outNode = prev.next;
prev.next = outNode.next;
outNode.next = null;
size--;
}
public void removeLast() {
remove(size - 1);
}
public int find(AnyType data) {
Node prev = head.next;
int idx = 0;
while(prev != null) {
if(prev.data == data)
break;
prev = prev.next;
idx++;
}
return idx;
}
private Node getNode(int idx) {
if(idx >= size || idx < 0)
throw new IllegalArgumentException("不合法位置");
Node prev = head.next;
while(prev != null) {
if(idx-- == 0) {
break;
}
prev = prev.next;
}
return prev;
}
public String toString() {
StringBuilder strb = new StringBuilder();
strb.append("链表:");
strb.append("size = "+size+"\n");
strb.append("[");
Node prev = head;
while(prev.next != null) {
prev = prev.next;
strb.append(prev.data);
if(prev.next != null)
strb.append(',');
}
strb.append("]");
return strb.toString();
}
}