二、链表
1、概念
数组在内存当中是一块连续的区域,每个元素之间的内存地址是相邻的。而链表在内存当中则不是连续的,每个元素之间通过指针来相互关联
如果链表内的元素只通过一个指针来指向它的下一个节点,则该链表是一个单向的链表;如果链表内的元素还有一个指针指向它的前节点,则该链表是一个双向链表
链表内部需要一个节点,用于标识链表的头部,该节点被称之为头节点。初始化链表时头节点可以指向一个默认的节点,该节点不能被访问或修改;头节点也可以为空,当添加元素时,它指向链表的第一个节点
2、示例
1)单向链表
class SingleLinkedList<T> {
private Node<T> head;
public SingleLinkedList() {
this.head = null;
}
public void add(T element) {
if (null == element) {
return;
}
Node<T> add = new Node<T>(element);
if (null == head) {
this.head = add;
return;
}
Node<T> node = this.head;
while (true) {
if (null == node.next) {
node.next = add;
break;
}
node = node.next;
}
}
public void remove(T element) {
if (null == head || null == element) {
return;
}
Node<T> node = this.head;
Node<T> prev = this.head;
boolean isHead = true;
while (true) {
if (node.element == element) {
if (isHead) {
this.head = head.next;
} else {
prev.next = node.next;
}
break;
}
if (null == node.next) {
break;
}
node = node.next;
if (isHead) {
isHead = false;
} else {
prev = prev.next;
}
}
}
@Override
public String toString() {
StringBuilder sBuilder = new StringBuilder("[");
if (null != head) {
Node<T> node = this.head;
while (true) {
sBuilder.append(node);
if (null == node.next) {
break;
}
node = node.next;
sBuilder.append(", ");
}
}
sBuilder.append("]");
return sBuilder.toString();
}
private static class Node<T> {
private T element;
private Node<T> next;
public Node(T element) {
this.element = element;
}
@Override
public String toString() {
return (null != element) ? element.toString() : "null";
}
}
}
2)双向链表
class DoubleLinkedList<T> {
private Node<T> head;
public DoubleLinkedList() {
this.head = null;
}
public void add(T element) {
if (null == element) {
return;
}
Node<T> add = new Node<T>(element);
if (null == head) {
this.head = add;
return;
}
Node<T> node = this.head;
while (true) {
if (null == node.next) {
node.next = add;
add.prev = node;
break;
}
node = node.next;
}
}
public void remove(T element) {
if (null == head || null == element) {
return;
}
Node<T> node = this.head;
if (node.element == element) {
this.head = head.next;
if (null != head) {
head.prev = null;
}
return;
}
node = node.next;
if (null == node) {
return;
}
while (true) {
if (node.element == element) {
node.prev.next = node.next;
if (null != node.next) {
node.next.prev = node.prev;
}
break;
}
if (null == node.next) {
break;
}
node = node.next;
}
}
@Override
public String toString() {
StringBuilder sBuilder = new StringBuilder("[");
if (null != head) {
Node<T> node = this.head;
while (true) {
sBuilder.append(node);
if (null == node.next) {
break;
}
node = node.next;
sBuilder.append(", ");
}
}
sBuilder.append("]");
return sBuilder.toString();
}
private static class Node<T> {
private T element;
private Node<T> prev;
private Node<T> next;
public Node(T element) {
this.element = element;
}
@Override
public String toString() {
return (null != element) ? element.toString() : "null";
}
}
}
3)有序链表
class SortedLinkedList<T> {
private Node<T> head;
public SortedLinkedList() {
this.head = null;
}
public void add(int index, T element) {
Node<T> add = new Node<T>(index, element);
if (null == head) {
this.head = add;
return;
}
Node<T> node = this.head;
Node<T> prev = this.head;
boolean isHead = true;
while (true) {
if (node.index > index) {
if (isHead) {
add.next = this.head;
this.head = add;
} else {
prev.next = add;
add.next = node;
}
break;
}
if (null == node.next) {
node.next = add;
break;
}
node = node.next;
if (isHead) {
isHead = false;
} else {
prev = prev.next;
}
}
}
public void remove(int index) {
if (null == head) {
return;
}
Node<T> node = this.head;
Node<T> prev = this.head;
boolean isHead = true;
while (true) {
if (node.index == index) {
if (isHead) {
this.head = head.next;
} else {
prev.next = node.next;
}
break;
}
if (null == node.next) {
break;
}
node = node.next;
if (isHead) {
isHead = false;
} else {
prev = prev.next;
}
}
}
@Override
public String toString() {
StringBuilder sBuilder = new StringBuilder("[");
if (null != head) {
Node<T> node = this.head;
while (true) {
sBuilder.append(node);
if (null == node.next) {
break;
}
node = node.next;
sBuilder.append(", ");
}
}
sBuilder.append("]");
return sBuilder.toString();
}
private static class Node<T> {
private final int index;
private T element;
private Node<T> next;
public Node(int index, T element) {
this.index = index;
this.element = element;
}
@Override
public String toString() {
StringBuilder sBuilder = new StringBuilder();
sBuilder.append(index).append("-");
if (null != element) {
sBuilder.append(element.toString());
} else {
sBuilder.append("null");
}
return sBuilder.toString();
}
}
}
3、链表的应用
1)移除倒数第n个节点
【1】思路
- 倒数第n个节点,即正数第
length - n + 1
个节点 - n不能超过链表元素的个数
【2】示例
class SingleLinkedList<T> {
private Node<T> head;
public SingleLinkedList() {
this.head = null;
}
public void reverseRemove(int n) {
if (null == head) {
return;
}
int size = size();
int index = size - n + 1; // 获取正数下标,从1开始
if (0 >= index) {
return;
}
Node<T> node = this.head;
Node<T> prev = this.head;
boolean isHead = true;
for (int i = index; i >= 1; i--) {
// 向后查找
if (1 == i) {
if (isHead) {
this.head = head.next;
} else {
prev.next = node.next;
}
break;
}
if (null == node.next) {
break;
}
node = node.next;
if (isHead) {
isHead = false;
} else {
prev = prev.next;
}
}
}
public void add(T element) {
Node<T> add = new Node<T>(element);
if (null == head) {
this.head = add;
return;
}
Node<T> node = this.head;
while (true) {
if (null == node.next) {
node.next = add;
break;
}
node = node.next;
}
}
public int size() {
if (null == head) {
return 0;
}
int size = 0;
Node<T> node = this.head;
while (true) {
size++;
if (null == node.next) {
break;
}
node = node.next;
}
return size;
}
@Override
public String toString() {
StringBuilder sBuilder = new StringBuilder("[");
if (null != head) {
Node<T> node = this.head;
while (true) {
sBuilder.append(node);
if (null == node.next) {
break;
}
node = node.next;
sBuilder.append(", ");
}
}
sBuilder.append("]");
return sBuilder.toString();
}
private static class Node<T> {
private T element;
private Node<T> next;
private Node(T element) {
this.element = element;
}
@Override
public String toString() {
return (null != element) ? element.toString() : "null";
}
}
}
2)从尾到头打印单向链表
【1】思路
- 破坏原有链表结构:反向遍历链表,保存元素到数组中,再遍历数组打印
- 不破坏原有链表结构:遍历链表,保存元素到
栈
(后进先出)中,再遍历栈打印
反向遍历链表:定义一个新的头节点,遍历链表,将新的头节点指向遍历出来的每一个元素,同时将遍历出来的每一个元素的下一个指针指向新的头节点所指向的节点
最后将原头节点指向新的头节点所指向的节点,新的头节点再指向空
head ==> A --> B --> C --> null
newHead ==> A --> null
newHead ==> B --> A --> null
newHead ==> C --> B --> A --> null
head ==> C --> B --> A --> null
newHead ==> null
【2】示例
class SingleLinkedList<T> {
private Node<T> head;
public SingleLinkedList() {
this.head = null;
}
public void add(T element) {
Node<T> add = new Node<T>(element);
if (null == head) {
this.head = add;
return;
}
Node<T> node = this.head;
while (true) {
if (null == node.next) {
node.next = add;
break;
}
node = node.next;
}
}
public void reverseBySelf() {
if (null == head || null == head.next) {
// 链表为空或只有一个节点
return;
}
Node<T> current = head.next;
Node<T> next = null;
Node<T> newHead = head;
newHead.next = null; // 将首节点的下一个节点指向空
while (null != current) {
next = current.next;
current.next = newHead;
newHead = current;
current = next;
}
current = null;
next = null;
this.head = newHead;
newHead = null;
}
public void reverseByStack() {
if (null == head || null == head.next) {
return;
}
java.util.Stack<Node<T>> stack = new java.util.Stack<Node<T>>();
Node<T> node = this.head;
while (true) {
stack.push(node);
if (null == node.next) {
break;
}
node = node.next;
}
Node<T> prev = stack.pop();
Node<T> newHead = prev;
newHead.next = null; // 将首节点的下一个节点指向空
while (!stack.isEmpty()) {
Node<T> temp = stack.pop();
temp.next = null;
prev.next = temp;
prev = temp;
}
prev = null;
this.head = newHead;
newHead = null;
}
@Override
public String toString() {
StringBuilder sBuilder = new StringBuilder("[");
if (null != head) {
Node<T> node = this.head;
while (true) {
sBuilder.append(node);
if (null == node.next) {
break;
}
node = node.next;
sBuilder.append(", ");
}
}
sBuilder.append("]");
return sBuilder.toString();
}
private static class Node<T> {
private T element;
private Node<T> next;
private Node(T element) {
this.element = element;
}
@Override
public String toString() {
return (null != element) ? element.toString() : "null";
}
}
}
3)约瑟夫环问题
【1】问题描述
一个封闭的环形数据结构,首尾相连。从指定位置开始,依次数指定个数,并将数到的元素移除。再从下一个元素开始,重复移除操作。直到剩余最后一个元素为止。问每次移除的元素是什么
【2】思路
使用单向链表来实现。将链表的尾节点指向头节点,即可构成一个封闭的环形数据结构
- 获取尾节点。遍历链表,哪个节点的下一个节点是头节点,则该节点就是尾节点
- 获取开始节点,以及开始节点的前一个结点。即移动头节点和尾节点相同的步数
- 找到需要移除的节点,移除。并将下一个节点设置为下一次的开始节点
【3】示例
class JosephusCircle {
private Node head;
public JosephusCircle(int length) {
if (1 > length) {
throw new IllegalArgumentException("长度不正确!");
}
Node node = null;
for (int i = 1; i <= length; i++) {
Node add = new Node(i);
if (1 == i) {
this.head = add;
this.head.next = this.head;
node = this.head;
} else {
node.next = add;
add.next = this.head;
node = add;
}
}
}
public void countDown(int begin, int step) {
if (0 >= begin || 0 >= step) {
throw new IllegalArgumentException("参数有误!");
}
Node tail = head;
while (head != tail.next) {
tail = tail.next;
}
Node first = head;
Node prev = tail;
for (int i = 1; i < begin; i++) {
// 从头节点开始,向后数指定个数
first = first.next;
prev = prev.next;
}
while (first != prev) {
// 剩余1个节点以上
for (int i = 1; i < step; i++) {
first = first.next;
prev = prev.next;
}
System.out.println(first);
first = first.next;
prev.next = first;
}
System.out.println("剩下:" + first);
}
private static class Node {
private final int element;
private Node next;
private Node(int element) {
this.element = element;
}
@Override
public String toString() {
return Integer.toString(element);
}
}
}