1、了解链表的结构和优点;
2、知道链表的分类及其区别;
3、各种链表的代码实现。
1、链结点
是链表最基本的组成单元。在链表中我们的数据项是存储在链结点中的,每个链结点持有指向其他节点的指针,有了该指针,我们可以以某种顺序搜索到目标数据项的结点,进而获取到数据项。
2、链表
是由链结点构成的存储结构,链表的物理存储可以是非顺序和非连续的,这点和数组不同,但是我们链表是可以实现逻辑顺序的数据结构,因为链结点中可以包含相邻链结点的指针。链表的类型有:单链表、双端链表、双向链表以及有序链表等等。
3、对比数组优缺点
除非频繁需要通过下标随机访问数据(没有数组的随机访问性强),我们基本可以用链表代替数组。
(1)链表的灵活性高。数组的存储大小受限创建创建时的大小,而链表的存储是无限制的(局限于内存)。
(2)链表的插入元素方便。数组中间插入一个元素,该位置的后续元素需要向后移动,而链表只需要改变相邻结点的指针即可。
/**
* 单链表
*
*/
public class DefLinkList {
private Node first; // 链表的第一个节点
/**
* 初始化链表
*/
public DefLinkList() {
first = null;
}
/**
* 判断链表是否为空
*
* @return 空 true 非空 false
*/
public boolean isEmpty() {
return first == null;
}
/**
* 向链表头部位置插入元素
*
* @param data
*/
public void insertFirst(E data) {
Node node = new Node(data);
node.next = first;
first = node;
}
/**
* 删除链表的头部元素
*
* @param data
*/
public E deleteFirst() {
if (!isEmpty()) {
E temp = first.element;
first = first.next;
return temp;
}
return null;
}
/**
* 删除指定元素
*
* @param data
*/
public boolean delete(E e) {
if (isEmpty()) {
return false;
}
Node previous = first;
Node current = first;
// 为了防止空指针异常 ,元素为 null和非null的逻辑分开写
if (e == null) {
for ( ; current != null; previous = current, current = current.next) {
if (current.element == null) {
// 被删除的元素是头节点
if (current == first) {
first = current.next;
} else {
// 被删的元素非节点
previous.next = current.next;
}
return true;
}
}
} else {
for ( ; current != null; previous = current, current = current.next) {
// 注意判断的顺序,current.element有可能是存放的null元素
if (e.equals(current.element)) {
// 被删的元素是头节点
if (current == first) {
first = current.next;
} else {
// 被删的元素非头节点
previous.next = current.next;
}
return true;
}
}
}
return false;
}
/**
* 是否包含某元素
*
* @param e
* @return
*/
public boolean contains(E e) {
if (isEmpty()) {
return false;
}
if (e == null) {
for (Node temp = first; temp != null; temp = temp.next) {
if (temp.element == null) {
return true;
}
}
} else {
for (Node temp = first; temp != null; temp = temp.next) {
if (e.equals(temp.element)) {
return true;
}
}
}
return false;
}
/**
* 复写toString方法
*/
public String toString() {
if (isEmpty())
return "[]";
StringBuilder displayBuilder = new StringBuilder("[");
Node temp = first;
while (temp != null) {
displayBuilder.append(temp.element);
temp = temp.next;
if (temp == null) {
displayBuilder.append("]");
break;
}
displayBuilder.append(", ");
}
return displayBuilder.toString();
}
/**
* 结点 链表的组成元素
*/
private static class Node {
E element; // 节点里面存放的数据项
Node next; // 指向下一个节点的指针
public Node(E element) {
this.element = element;
}
}
}
/**
*双端队列
* @param
*/
public class DefDeque {
private Node first; // 链表的第一个元素
private Node last; // 链表的最后一个元素
/**
* 初始化链表
*/
public DefDeque() {
first = null;
last = null;
}
/**
* 链表头部插入
*
* @param e
*/
public void insertFirst(E e) {
Node current = new Node(e);
if (first == null) {
first = current;
last = current;
} else {
current.next = first;
first = current;
}
}
/**
* 链表尾部插入
*
* @param e
*/
public void insertLast(E e) {
Node current = new Node(e);
if (first == null) {
first = current;
last = current;
} else {
last.next = current;
last = current;
}
}
/**
* 判断链表是否为空
*
* @return
*/
public boolean isEmpty() {
return first == null;
}
/**
* 移除链表头部元素
*
* @return
*/
public E removeFirst() {
// 链表为空,返回null
if (isEmpty())
return null;
E data = first.data;
// 链表只有一个元素
if (first == last) {
first = last = null;
} else {
first = first.next;
}
return data;
}
/**
* 移除链表尾部元素
*
* @return
*/
public E removeLast() {
// 链表为空,返回null
if (isEmpty())
return null;
E data = last.data;
// 链表只有一个元素
if (first == last) {
first = last = null;
} else {
Node current = first;
Node previous = current;
// 找到链表的倒数第二个元素,双端链表就不用这么麻烦
while(current != last) {
previous = current;
current = current.next;
}
previous.next = null;
last = previous;
}
return data;
}
public boolean contains(E e) {
if (isEmpty()) {
return false;
}
// e是空和非空逻辑分开
if (e == null) {
for (Node current = first; current != null; current = current.next) {
if (current.data == e) {
return true;
}
}
} else {
for (Node current = first; current != null; current = current.next) {
if (e.equals(current.data)) {
return true;
}
}
}
return false;
}
/**
* 删除链表中包含数据项e的第一个结点(不是所有)
*
* @param e
* @return
*/
public boolean remove(E e) {
if(isEmpty()) {
return false;
}
Node current;
Node previous;
// e是空和非空逻辑分开
if (e == null) {
for(previous = current = first;current != null;previous = current, current = current.next) {
if(current.data == e) {
if(current == first) {
first = first.next;
}else {
previous.next = current.next;
if(current == last) {
last = previous;
}
}
return true;
}
}
} else {
for(previous = current = first;current != null;previous = current, current = current.next) {
if(e.equals(current.data)) {
if(current == first) {
first = first.next;
}else {
previous.next = current.next;
if(current == last) {
last = previous;
}
}
return true;
}
}
}
return false;
}
public String toString() {
if (isEmpty())
return "[]";
StringBuilder displayBuilder = new StringBuilder("[");
Node temp = first;
while (temp != null) {
displayBuilder.append(temp.data);
temp = temp.next;
if (temp == null) {
displayBuilder.append("]");
break;
}
displayBuilder.append(", ");
}
return displayBuilder.toString();
}
/**
* 结点 链表的组成元素
*
* @param
* 结点存放的数据项
*/
private static class Node {
private E data; // 节点存放的数据项
private Node next; // 指向下一个节点的指针
public Node(E e) {
data = e;
next = null;
}
}
}
/**
* 双向队列
* 特点:反向遍历链表很容易,可以很轻松地实现双端队列。
*/
public class DublyLinkList {
private Node first; // 链表头部指针
private Node last; // 链表尾部指针
/**
* 组成链表的结点
*
* @param
*/
private static class Node {
private E data;
private Node previous;
private Node next;
public Node(E e) {
this.data = e;
previous = null;
next = null;
}
}
/**
* 头部元素为空
*
* @return
*/
public boolean isEmpty() {
return first == null;
}
/**
* 头部插入元素
*
* @param data
*/
public void insertFirst(E data) {
Node newNode = new Node<>(data);
newNode.next = first;
// 如果是空链表
if (isEmpty()) {
last = newNode;
} else {
first.previous = newNode;
}
first = newNode;
}
/**
* 尾部插入元素
*
* @param data
*/
public void insertLast(E data) {
Node newNode = new Node<>(data);
// 如果是空链表
if (isEmpty()) {
first = newNode;
} else {
last.next = newNode;
newNode.previous = last;
}
last = newNode;
}
/**
* 删除头部元素
*
* @return
*/
public E removeFirst() {
// 空链表,返回null。正确的方法应该抛出异常
if (isEmpty()) {
return null;
}
E data = first.data;
Node next = first.next;
first.next = null;
first.data = null; // 帮助GC
first = next;
// 链表只有一个元素,需要修改尾部指针
if (next == null) {
last = null;
} else {
next.previous = null;
}
return data;
}
/**
* 删除尾部结点
*
* @return
*/
public E removeLast() {
// 空链表,返回null。正确的方法应该抛出异常
if (isEmpty()) {
return null;
}
E data = last.data;
Node previous = last.previous;
last.data = null; // GC
last.previous = null;
last = previous;
// 链表只有一个元素,需要修改头部指针
if (last == first) {
first = null;
} else {
previous.next = null;
}
return data;
}
/**
* 删除特定的元素
* PS:写完,看见linkedList的源码瞬间知道差距
* @param e
* @return
*/
public boolean remove(E e) {
if (isEmpty()) {
return false;
}
Node current = first;
if (e == null) {
while (current != null) {
if (current.data == e) {
// 删除的是链表头结点
if (current == first) {
first = current.next;
last = first;
//刪除的是链表尾部结点
}else if(current == last) {
last = current.previous;
last.next = null;
//刪除的是链表中间结点
} else {
current.previous.next = current.next;
current.next.previous = current.previous;
}
current.previous = null;
current.next = null;
current.data = null;
return true;
}
current = current.next;
}
} else {
while (current != null) {
if (e.equals(current.data)) {
// 删除的是链表头结点
if (current == first) {
first = current.next;
last = null;
//刪除的是链表尾部结点
} else if (current == last) {
last = current.previous;
last.next = null;
//刪除的是链表中间结点
} else {
current.previous.next = current.next;
current.next.previous = current.previous;
}
current.previous = null;
current.next = null;
current.data = null;
return true;
}
current = current.next;
}
}
return false;
}
/**
* 插入指定元素后面 插入的位置():头部、尾部或中间。
*
* @param data
*/
public boolean insertAfter(E key, E data) {
// 空链表,直接返回fasle
if (isEmpty()) {
return false;
}
Node newNode = new Node(data);
Node current = first;
// 防止空指针异常,代码逻辑分开
if (key == null) {
while (current != null && current.data != key) {
current = current.next;
}
// 如果插入的位置是链表尾部
if (current == last) {
last = newNode; // 修改尾部指针
newNode.next = null;
} else {
newNode.next = current.next; // 新插入的结点的next指向current.next
current.next.previous = newNode;// current.next结点的previous指向新结点
}
newNode.previous = current;
current.next = newNode;
} else {
while (current != null && !key.equals(current.data)) {
current = current.next;
}
if (current == last) {
last = newNode;
newNode.next = null;
} else {
newNode.next = current.next;
current.next.previous = newNode;
}
newNode.previous = current;
current.next = newNode;
}
return true;
}
/**
* 复写toString方法
*/
public String toString() {
if (isEmpty())
return "[]";
StringBuilder displayBuilder = new StringBuilder("[");
Node temp = first;
while (temp != null) {
displayBuilder.append(temp.data);
temp = temp.next;
if (temp == null) {
displayBuilder.append("]");
break;
}
displayBuilder.append(", ");
}
return displayBuilder.toString();
}
}