链表的特点
- 数据存储在节点(Node)中
- 优点:真正的动态,不需要处理固定容量的问题
- 缺点:丧失了随机访问的能力
数组和链表的对比
- 数组最大的优点:支持快速查询
- 链表最大的优点:动态
实现
public class LinkedList<E> {
private class Node {
public E e;
public Node next;
public Node(E e, Node next) {
this.e = e;
this.next = next;
}
public Node(E e) {
this(e, null);
}
public Node() {
this(null, null);
}
@Override
public String toString() {
return e.toString();
}
}
//使用虚拟头节点 使代码更优雅
private Node dummyHead;
int size;
public LinkedList() {
dummyHead = new Node();
size = 0;
}
//获取链表中的元素个数
public int getSize() {
return size;
}
//返回链表是否为空
public boolean isEmpty() {
return size == 0;
}
//在链表指定索引添加新的元素e
public void add(int index, E e) {
if (index < 0 || index > size) throw new IllegalArgumentException("Add fail,Illegal index");
Node pre = dummyHead;
for (int i = 0; i < index; i++) {
pre = pre.next;
}
pre.next = new Node(e, pre.next);
//等价于
//Node node = new Node(e);
//node.next = pre.next;
//pre.next = node;
size++;
}
//在链表头添加新的元素e
public void addFirst(E e) {
add(0, e);
}
//在链表尾部添加新的元素e
public void addLast(E e) {
add(size, e);
}
//获取链表的第index个位置的元素
public E get(int index) {
if (index < 0 || index > size) throw new IllegalArgumentException("Add fail,Illegal index");
Node cur = dummyHead.next;
for (int i = 0; i < index; i++) {
cur = cur.next;
}
return cur.e;
}
//获取链表的第一个元素
public E getFirst() {
return get(0);
}
//获取链表的最后一个元素
public E getLast() {
return get(size - 1);
}
//修改链表的第index个位置的元素为e
public void set(int index, E e) {
if (index < 0 || index > size) throw new IllegalArgumentException("Add fail,Illegal index");
Node cur = dummyHead.next;
for (int i = 0; i < index; i++) {
cur = cur.next;
}
cur.e = e;
}
//查找链表中是否有元素e
public boolean contains(E e) {
Node cur = dummyHead.next;
while (cur != null) {
if (cur.e.equals(e))
return true;
cur = cur.next;
}
return false;
}
//从链表中删除index位置的元素,放回删除的元素
public E remome(int index) {
if (index < 0 || index > size) throw new IllegalArgumentException("Add fail,Illegal index");
Node pre = dummyHead;
for (int i = 0; i < index; i++) {
pre = pre.next;
}
Node delNode = pre.next;
pre.next = delNode.next;
delNode.next = null;
size--;
return delNode.e;
}
//从链表中删除第一个元素
public E remomeFirst() {
return remome(0);
}
//从链表中删除最后个元素
public E remomeLast() {
return remome(size - 1);
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
Node cur = dummyHead.next;
while (cur != null) {
sb.append(cur).append("-->");
cur = cur.next;
}
sb.append("NULL");
return sb.toString();
}
}
测试结果
public static void main(String[] args) {
LinkedList<Integer> linkedList = new LinkedList<>();
for (int i = 0; i < 5; i++) {
linkedList.addFirst(i);
System.out.println(linkedList.toString());
}
linkedList.add(2, 8);
System.out.println(linkedList.toString());
linkedList.remome(2);
System.out.println(linkedList.toString());
linkedList.remomeFirst();
System.out.println(linkedList.toString());
linkedList.remomeLast();
System.out.println(linkedList.toString());
}
//0-->NULL
//1-->0-->NULL
//2-->1-->0-->NULL
//3-->2-->1-->0-->NULL
//4-->3-->2-->1-->0-->NULL
//4-->3-->8-->2-->1-->0-->NULL
//4-->3-->2-->1-->0-->NULL
//3-->2-->1-->0-->NULL
//3-->2-->1-->NULL
链表的时间复杂度分析
操作 | 时间复杂度 |
---|---|
addFirst(E e) | O(1) |
addLast(E e) | O(n) |
add(int index, E e) | O(n/2) = O(n) |
remomeFirst() | O(1) |
remomeLast() | O(n) |
remome(int index) | O(n/2) = O(n) |
contains(E e) | O(n) |
使用链表实现栈
public interface Stack<E> {
int getSize();
boolean isEmpty();
void push(E e);
E pop();
//栈顶元素
E peek();
}
public class LinkedListStack<E> implements Stack<E> {
private LinkedList<E> list;
public LinkedListStack() {
list = new LinkedList<>();
}
@Override
public int getSize() {
return list.getSize();
}
@Override
public boolean isEmpty() {
return list.isEmpty();
}
@Override
public void push(E e) {
list.addFirst(e);
}
@Override
public E pop() {
return list.remomeFirst();
}
@Override
public E peek() {
return list.getFirst();
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("Stack: top ");
sb.append(list);
return sb.toString();
}
}
测试结果
public static void main(String[] args) {
LinkedListStack<Integer> stack = new LinkedListStack<>();
for (int i = 0; i < 5; i++) {
stack.push(i);
System.out.println(stack);
}
stack.pop();
System.out.println(stack);
}
Stack: top 0-->NULL
Stack: top 1-->0-->NULL
Stack: top 2-->1-->0-->NULL
Stack: top 3-->2-->1-->0-->NULL
Stack: top 4-->3-->2-->1-->0-->NULL
Stack: top 3-->2-->1-->0-->NULL
使用链表实现队列
之前实现的链表,表首添加元素的时间复杂度为O(1),删除元素的时间复杂度为O(n)。
对链表进行优化,使删除的时间复杂度为O(1)。
public interface Queue<E> {
int getSize();
boolean isEmpty();
void enqueue(E e);
E dequeue();
E getFront();
}
public class LinkedListQueue<E> implements Queue<E> {
private class Node {
public E e;
public Node next;
public Node(E e, Node next) {
this.e = e;
this.next = next;
}
public Node(E e) {
this(e, null);
}
public Node() {
this(null, null);
}
@Override
public String toString() {
return e.toString();
}
}
private Node head, tail;
private int size;
public LinkedListQueue() {
head = null;
tail = null;
size = 0;
}
@Override
public int getSize() {
return size;
}
@Override
public boolean isEmpty() {
return size == 0;
}
@Override
public void enqueue(E e) {
//tail为空的时候意味着head也为空 队列没有元素
if (tail == null) {
tail = new Node(e);
head = tail;
} else {
tail.next = new Node(e);
tail = tail.next;
}
size++;
}
@Override
public E dequeue() {
if (isEmpty()) throw new IllegalArgumentException("Cannot dequeue from a empty queue");
//拿到head节点 将head节点指向下一个元素 将返回的节点的next指空
Node retNode = head;
head = head.next;
retNode.next = null;
//如果head节点为空 说明队列为空 head和tail都为空
if (head == null)
tail = null;
size--;
return retNode.e;
}
@Override
public E getFront() {
if (isEmpty()) throw new IllegalArgumentException("Queue is empty");
return head.e;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("Queue: front ");
Node cur = head;
while (cur != null) {
sb.append(cur + "-->");
cur = cur.next;
}
sb.append("NULL tail");
return sb.toString();
}
}
测试结果
public static void main(String[] args) {
LinkedListQueue<Integer> queue = new LinkedListQueue();
for (int i = 0; i < 10; i++) {
queue.enqueue(i);
System.out.println(queue);
if (i % 3 == 2) {
queue.dequeue();
System.out.println(queue);
}
}
}
Queue: front 0-->NULL tail
Queue: front 0-->1-->NULL tail
Queue: front 0-->1-->2-->NULL tail
Queue: front 1-->2-->NULL tail
Queue: front 1-->2-->3-->NULL tail
Queue: front 1-->2-->3-->4-->NULL tail
Queue: front 1-->2-->3-->4-->5-->NULL tail
Queue: front 2-->3-->4-->5-->NULL tail
Queue: front 2-->3-->4-->5-->6-->NULL tail
Queue: front 2-->3-->4-->5-->6-->7-->NULL tail
Queue: front 2-->3-->4-->5-->6-->7-->8-->NULL tail
Queue: front 3-->4-->5-->6-->7-->8-->NULL tail
Queue: front 3-->4-->5-->6-->7-->8-->9-->NULL tail