链表是非常常用的数据结构,常见的链表有单链表、双向链表和双向循环链表。
一个比一个复杂,但实际运用中,越往后越好用。
下面我们使用java分别实现:
一、单链表
单链表特点:
1.单链表的head结点指向第一个数据节点,存数据,没有tail结点
2.单链表的每个节点都有next指针指向下一个节点,但是没有指针指向前驱节点
/** * @Author : wangbin * @Date : 2/7/2022 11:17 AM * @Description: 单链表特点: * 1.单链表的head结点指向第一个数据节点,存数据,没有tail结点 * 2.单链表的每个节点都有next指针指向下一个节点,但是没有指针指向前驱节点 */ public class SinglyLinkedList<E> implements LinkedList<E> { private int size; private Node<E> head; public SinglyLinkedList() { } @Override public int size() { return size; } @Override public boolean isEmpty() { return size == 0; } @Override public boolean add(E e) { if (head == null) { head = new Node<>(e, null); size++; return true; } Node<E> cur = head; while (cur.next != null) { cur = cur.next; } cur.next = new Node<>(e, null); size++; return true; } @Override public boolean remove(E e) { Node<E> cur = head; Node<E> curPrev = head; while (!cur.item.equals(e) && cur.next != null) { curPrev = cur; cur = cur.next; } if (cur.next == null) { return false; } curPrev.next = cur.next; size--; return true; } @Override public E get(int index) { Node<E> curNode = getNode(index); return curNode.item; } private Node<E> getNode(int index) { checkIndex(index); Node<E> curNode = head; int curIdx = 0; while (curNode.next != null && curIdx != index) { curIdx++; curNode = curNode.next; } return curNode; } private Node<E> getPrevNode(int index) { checkIndex(index); Node<E> curNode = head; int curIdx = 0; while (curIdx != index - 1) { curIdx++; curNode = curNode.next; } return curNode; } @Override public E set(int index, E element) { checkIndex(index); Node<E> node = getNode(index); E oldElement = node.item; node.item = element; return oldElement; } @Override public void add(int index, E element) { if (!isPositionIndex(index)) { throw new IndexOutOfBoundsException(); } Node<E> prevNode = getPrevNode(index); Node<E> curNode = prevNode.next; prevNode.next = new Node<>(element, curNode); size++; } private boolean isPositionIndex(int index) { return index >= 0 && index <= size; } @Override public E remove(int index) { if (index == 0) { E item = head.item; head = head.next; size--; return item; } Node<E> prevNode = getPrevNode(index); Node<E> curNode = prevNode.next; prevNode.next = curNode.next; size--; return curNode.item; } @Override public int indexOf(E o) { int curIndex = 0; for (Node<E> curNode = head; curIndex < size; curNode = curNode.next) { if (curNode.item.equals(o)) { return curIndex; } curIndex++; } return -1; } @Override public Iterator<E> iterator() { return new Iterator<E>() { int nextIndex = 0; @Override public boolean hasNext() { return nextIndex < size; } @Override public E next() { return get(nextIndex++); } }; } private void checkIndex(int index) { if (index < 0 || index >= size) { throw new IndexOutOfBoundsException(); } } private static class Node<E> { E item; Node<E> next; public Node(E item, Node<E> next) { this.item = item; this.next = next; } } }
二、双向链表
双向链表的特点:
1.双向链表既有head节点,也有tail节点;
2.head节点指向第一个节点,head.prev==null;
3.tail节点指向最后一个节点,tail.next==null;
4.相比双向循环链表,双向链表没有使用哨兵,所以需要处理复杂的边界条件
/** * @Author : wangbin * @Date : 2/7/2022 11:18 AM * @Description: 双向链表的特点: * 1.双向链表既有head节点,也有tail节点; * 2.head节点指向第一个节点,head.prev==null; * 3.tail节点指向最后一个节点,tail.next==null; * 相比双向循环链表,双向链表没有使用哨兵,所以需要处理复杂的边界条件 */ public class DoublyLinkedList<E> implements LinkedList<E> { private int size = 0; private Node<E> head; private Node<E> tail; public DoublyLinkedList() { } @Override public int size() { return size; } @Override public boolean isEmpty() { return size == 0; } @Override public boolean add(E e) { if (head == null) { Node<E> newNode = new Node<>(e, null, null); this.head = newNode; this.tail = newNode; size++; } else { Node<E> curNode = head; while (curNode.next != null) { curNode = curNode.next; } Node<E> newNode = new Node<>(e, null, curNode); curNode.next = newNode; this.tail = newNode; size++; } return true; } @Override public boolean remove(E e) { for (Node<E> curNode = head; curNode != null; curNode = curNode.next) { if (curNode.item.equals(e)) { Node<E> prev = curNode.prev; Node<E> next = curNode.next; if (prev == null) { head = next; } else { prev.next = next; } if (next == null) { tail = prev; } else { next.prev = prev; } size--; return true; } } return false; } @Override public E get(int index) { checkIndex(index); if (index < size / 2) { Node<E> curNode = head; for (int curIndex = 0; curIndex < index; curIndex++) { curNode = curNode.next; } return curNode.item; } else { Node<E> curNode = tail; for (int curIndex = size - 1; curIndex > index; curIndex--) { curNode = curNode.prev; } return curNode.item; } } private void checkIndex(int index) { if (index < 0 || index >= size) { throw new IndexOutOfBoundsException(); } } @Override public E set(int index, E element) { checkIndex(index); Node<E> curNode = getNode(index); E item = curNode.item; curNode.item = element; return item; } private Node<E> getNode(int index) { checkIndex(index); if (index < size / 2) { Node<E> curNode = head; for (int curIndex = 0; curIndex < index; curIndex++) { curNode = curNode.next; } return curNode; } else { Node<E> curNode = tail; for (int curIndex = size - 1; curIndex > index; curIndex--) { curNode = curNode.prev; } return curNode; } } @Override public void add(int index, E element) { checkIndex(index); Node<E> oldNode = getNode(index); Node<E> prev = oldNode.prev; Node<E> newNode = new Node<>(element, null, null); if (prev == null) { head = newNode; } else { prev.setNext(newNode); newNode.setPrev(prev); } newNode.setNext(oldNode); oldNode.setPrev(newNode); size++; } @Override public E remove(int index) { checkIndex(index); Node<E> node = getNode(index); Node<E> next = node.next;//next和prev对象都有可能为null,所有下面需要进行判断 Node<E> prev = node.prev; E item = node.item; if (prev == null) { head = next; } else { prev.next = next; } if (next == null) { tail = prev; } else { next.prev = prev; } size--; return item; } @Override public int indexOf(E o) { int curIndex = 0; for (Node<E> curNode = head; curNode.next != null; curNode = curNode.next) { if (curNode.item.equals(o)) { return curIndex; } curIndex++; } return -1; } @Override public Iterator<E> iterator() { return new Iterator<E>() { private int nextIndex; @Override public boolean hasNext() { return nextIndex < size; } @Override public E next() { return get(nextIndex++); } }; } private static class Node<E> { E item; Node<E> next; Node<E> prev; public Node(E item, Node<E> next, Node<E> prev) { this.item = item; this.next = next; this.prev = prev; } public void setNext(Node<E> next) { this.next = next; } public void setPrev(Node<E> prev) { this.prev = prev; } } }
三、双向循环链表
双向循环链表特点:
1.有head节点,无tail节点
2.与单链表和双向链表不同,为简化操作,双向循环链表使用一个空哨兵节点作为head节点
3.head节点的next指针指向第一个节点,head节点的prev指针指向最后一个节点;
4.最后一个节点的next指针指向head节点,
5.判断是否为空。head==head.next
/** * @Author : wangbin * @Date : 2/7/2022 11:18 AM * @Description: 双向循环链表特点: * 1.有head节点,无tail节点 * 2.与单链表和双向链表不同,为简化操作,双向循环链表使用一个空哨兵节点作为head节点 * 3.head节点的next指针指向第一个节点,head节点的prev指针指向最后一个节点; * 4.最后一个节点的next指针指向head节点, * 5.判断是否为空。head==head.next */ public class CircularLinkedList<E> implements LinkedList<E> { private int size = 0; private Node<E> head; public CircularLinkedList() { this.head = new Node<>(null, null, null); this.head.prev = this.head; this.head.next = this.head; } @Override public int size() { return size; } @Override public boolean isEmpty() { return size == 0; } @Override public boolean add(E e) { if (head.next == head) { Node<E> newNode = new Node<>(e, null, null); this.head.next = newNode; this.head.prev = newNode; newNode.setNext(head); newNode.setPrev(head); size++; } else { Node<E> curNode = head; while (curNode.next != head) { curNode = curNode.next; } Node<E> newNode = new Node<>(e, head, curNode); curNode.next = newNode; head.prev = newNode; size++; } return true; } @Override public boolean remove(E e) { for (Node<E> curNode = head.next; curNode.item != null; curNode = curNode.next) { if (curNode.item.equals(e)) { Node<E> prev = curNode.prev; Node<E> next = curNode.next; prev.next = next; next.prev = prev; return true; } } return false; } @Override public E get(int index) { checkIndex(index); if (index < size / 2) { Node<E> curNode = head.next; for (int curIndex = 0; curIndex < index; curIndex++) { curNode = curNode.next; } return curNode.item; } else { Node<E> curNode = head.prev; for (int curIndex = size - 1; curIndex > index; curIndex--) { curNode = curNode.prev; } return curNode.item; } } private void checkIndex(int index) { if (index < 0 || index >= size) { throw new IndexOutOfBoundsException(); } } @Override public E set(int index, E element) { checkIndex(index); Node<E> curNode = getNode(index); E item = curNode.item; curNode.item = element; return item; } private Node<E> getNode(int index) { checkIndex(index); if (index < size / 2) { Node<E> curNode = head.next; for (int curIndex = 0; curIndex < index; curIndex++) { curNode = curNode.next; } return curNode; } else { Node<E> curNode = head.prev; for (int curIndex = size - 1; curIndex > index; curIndex--) { curNode = curNode.prev; } return curNode; } } @Override public void add(int index, E element) { checkIndex(index); Node<E> oldNode = getNode(index); Node<E> prev = oldNode.prev; Node<E> newNode = new Node<>(element, oldNode, prev); prev.setNext(newNode); oldNode.setPrev(newNode); size++; } @Override public E remove(int index) { checkIndex(index); Node<E> node = getNode(index); Node<E> next = node.next; Node<E> prev = node.prev; E item = node.item; prev.next = next; next.prev = prev; size--; return item; } @Override public int indexOf(E o) { int curIndex = 0; for (Node<E> curNode = head.next; curNode.next != head; curNode = curNode.next) { if (curNode.item.equals(o)) { return curIndex; } curIndex++; } return -1; } @Override public Iterator<E> iterator() { return new Iterator<E>() { private Node<E> curNode = head.next; @Override public boolean hasNext() { return curNode.item != null; } @Override public E next() { E item = curNode.item; curNode = curNode.next; return item; } }; } private static class Node<E> { E item; Node<E> next; Node<E> prev; public Node(E item, Node<E> next, Node<E> prev) { this.item = item; this.next = next; this.prev = prev; } public void setNext(Node<E> next) { this.next = next; } public void setPrev(Node<E> prev) { this.prev = prev; } } }