Java - 数据结构 - 链表
1 单向链表

1.1 接口与抽象类
- 链表跟数组一样是线性表,那么就可以抽取线性表的共性
1.1.1 List接口
线性表公共的接口方法
点击查看代码
public interface List<E> {
static final int ELEMENT_NOT_FOUND = -1;
/**
* 清除所有元素
*/
void clear();
/**
* 元素的数量
* @return
*/
int size();
/**
* 是否为空
* @return
*/
boolean isEmpty();
/**
* 是否包含某个元素
* @param element
* @return
*/
boolean contains(E element);
/**
* 添加元素到尾部
* @param element
*/
void add(E element);
/**
* 获取index位置的元素
* @param index
* @return
*/
E get(int index);
/**
* 设置index位置的元素
* @param index
* @param element
* @return 原来的元素ֵ
*/
E set(int index, E element);
/**
* 在index位置插入一个元素
* @param index
* @param element
*/
void add(int index, E element);
/**
* 删除index位置的元素
* @param index
* @return
*/
E remove(int index);
/**
* 查看元素的索引
* @param element
* @return
*/
int indexOf(E element);
}
1.1.2 抽象父类
线性表的公共父类
点击查看代码
public abstract class AbstractList<E> implements List<E> {
/**
* 元素的数量
*/
protected int size;
/**
* 元素的数量
* @return
*/
public int size() {
return size;
}
/**
* 是否为空
* @return
*/
public boolean isEmpty() {
return size == 0;
}
/**
* 是否包含某个元素
* @param element
* @return
*/
public boolean contains(E element) {
return indexOf(element) != ELEMENT_NOT_FOUND;
}
/**
* 添加元素到尾部
* @param element
*/
public void add(E element) {
add(size, element);
}
protected void outOfBounds(int index) {
throw new IndexOutOfBoundsException("Index:" + index + ", Size:" + size);
}
protected void rangeCheck(int index) {
if (index < 0 || index >= size) {
outOfBounds(index);
}
}
protected void rangeCheckForAdd(int index) {
if (index < 0 || index > size) {
outOfBounds(index);
}
}
}
1.2
public class SingleLinkedList <E> extends AbstractList<E> {
//第一个节点
private Node<E> first;
//内部私有的节点
private static class Node<E>{
//元素
E element;
//下一个节点
Node next;
public Node(E element,Node<E> next){
this.element=element;
this.next=next;
}
}
}
1.3 查索引位置的元素(重要方法)
链表的增删改查功能,要先找到索引位置的元素,单向链表不像数组,数组可以通过下标直接访问索引位置的元素,单向链表要从第一个节点开始挨个找。
private Node<E> node(int index){
rangeCheck(index);
Node<E> node =first;
for (int i = 0 ;i<index;i++){
node=node.next;
}
return node;
}
1.4 clear
@Override
public void clear() {
first=null;
size=0;
}
1.5 add
1.6.1 在索引0加节点


1.6.2 在其他位置加节点

代码实现:
@Override
public void add(int index, E element) {
//索引0,没有上一个节点
if (index==0){
first=new Node<>(element,first);
}else {
//找到当前索引位置的上一个节点
Node<E> prev = node(index - 1);
prev.next=new Node<>(element,prev.next);
}
size++;
}
1.6 remove
1.6.1 删除索引0节点


1.6.2 删除其他节点(不是第一个)

代码实现
public E remove(int index) {
//取第一个节点
Node<E> node = first;
//删除第一个节点
if (index == 0) {
first = first.next;
} else {
//上一个节点
Node<E> prev = node(index - 1);
//当前节点
node = prev.next;
prev.next = node.next;
}
size--;
return node.element;
}
1.7 其余的方法
查,写法一,性能低
public int indexOf(E element) {
if (element == null){
for (int i = 0 ;i<size;i++){
if (node(i).element == null) return i;
}
}else {
for (int i=0;i<size;i++){
if (element.equals(node(i).element)) return i;
}
}
return ELEMENT_NOT_FOUND;
}
查,写法二
public int indexOf(E element) {
if (element == null){
Node node=first;
for (int i = 0 ;i<size;i++){
if (node.element == null) {
return i;
}
node=node.next;
}
}else {
Node node=first;
for (int i=0;i<size;i++){
if (element.equals(node.element)){
return i;
}
node=node.next;
}
}
return ELEMENT_NOT_FOUND;
}
获取和设置
@Override
public E get(int index) {
return node(index).element;
}
@Override
public E set(int index, E element) {
Node<E> node = node(index);
E old = node.element;
node.element=element;
return old;
}
2 双向链表

基本的
public class LinkedList<E> extends AbstractList<E> {
private Node<E> first;
private Node<E> last;
private static class Node<E> {
E element;
Node<E> prev;
Node<E> next;
public Node(Node<E> prev, E element, Node<E> next) {
this.prev = prev;
this.element = element;
this.next = next;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
if (prev != null) {
sb.append(prev.element);
} else {
sb.append("null");
}
sb.append("_").append(element).append("_");
if (next != null) {
sb.append(next.element);
} else {
sb.append("null");
}
return sb.toString();
}
}
@Override
public String toString() {
StringBuilder string = new StringBuilder();
string.append("size=").append(size).append(", [");
Node<E> node = first;
for (int i = 0; i < size; i++) {
if (i != 0) {
string.append(", ");
}
string.append(node);
node = node.next;
}
string.append("]");
return string.toString();
}
}
2.1 获取索引位置的节点
类似二分查找,
位于前面的元素从头节点开始查往中间查找,
位于后面的元素从尾部开始查往中间查找.
private Node<E> node(int index) {
rangeCheck(index);
if (index < (size >> 1)) {
Node<E> node = first;
for (int i = 0; i < index; i++) {
node = node.next;
}
return node;
} else {
Node<E> node = last;
for (int i = size - 1; i > index; i--) {
node = node.prev;
}
return node;
}
}
2.2 clear
@Override
public void clear() {
size = 0;
first = null;
last = null;
}
2.3 add
@Override
public void add(int index, E element) {
rangeCheckForAdd(index);
// size == 0
// index == 0
if (index == size) { // 往最后面添加元素
Node<E> oldLast = last;
last = new Node<>(oldLast, element, null);
if (oldLast == null) { // 这是链表添加的第一个元素
first = last;
} else {
oldLast.next = last;
}
} else {
Node<E> next = node(index);
Node<E> prev = next.prev;
Node<E> node = new Node<>(prev, element, next);
next.prev = node;
if (prev == null) { // index == 0
first = node;
} else {
prev.next = node;
}
}
size++;
}
3 单向循环链表
图示

只有一个节点

3.1
基础代码参考单向链表
只要改造add和remove就可以使单向链表成为单向循环链表
3.2 add
改造点,因为是单向循环,所以要考虑的情形是往索引0添加节点,其他的位置跟单向链表一样。
@Override
public void add(int index, E element) {
rangeCheckForAdd(index);
if (index == 0) {
Node<E> newFirst = new Node<>(element, first);
// 拿到最后一个节点
Node<E> last = (size == 0) ? newFirst : node(size - 1);
last.next = newFirst;
first = newFirst;
} else {
Node<E> prev = node(index - 1);
prev.next = new Node<>(element, prev.next);
}
size++;
}
3.3 remove
同理
@Override
public E remove(int index) {
rangeCheck(index);
Node<E> node = first;
if (index == 0) {
if (size == 1) {
first = null;
} else {
Node<E> last = node(size - 1);
first = first.next;
last.next = first;
}
} else {
Node<E> prev = node(index - 1);
node = prev.next;
prev.next = node.next;
}
size--;
return node.element;
}
4 双向循坏链表

浙公网安备 33010602011771号