一、前言
链表是一个线性的动态的数据结构,不需要处理固定容量问题,当时却丧失了随机访问的能力。链表数据存储在节点(Node)中,可以辅助组成其它的数据结构,如哈希表
二、实现
- 链表实现
/**
* 链表实现
* @param <E>
*/
public class LinkedList<E> {
/**
* 虚拟头节点
*/
private Node dummyHead;
/**
* 链表中元素个数
*/
private int size;
public LinkedList() {
dummyHead = new Node(null,null);
size = 0;
}
/**
* 获取链表中元素个数
* @return
*/
public int getSize() {
return size;
}
/**
* 判断链表是否为空
* @return
*/
public boolean isEmpty() {
return size == 0;
}
/**
* 在链表的index位置添加新的元素
* 在链表中不是一个常用的操作,练习用
* @param index 索引位置
* @param e 元素
*/
public void add(int index, E e) {
if (index < 0 || index > size) {
throw new IllegalArgumentException("add failed. illegal index.");
}
//找到插入位置的前一个节点
Node prev = dummyHead;
for (int i = 0; i < index; i++) {
prev = prev.next;
}
//插入元素
prev.next = new Node(e, prev.next);
size++;
}
/**
* 在链表头添加元素
* @param e 元素
*/
public void addFirst(E e) {
add(0, e);
}
/**
* 在链表末尾添加新的元素
* @param e 元素
*/
public void addLast(E e) {
add(size, e);
}
/**
* 获取链表指定位置的元素
* 非常用操作,练习用
* @param index 指定位置
* @return 元素
*/
public E get(int index) {
if (index < 0 || index >= size) {
throw new IllegalArgumentException("get failed, illegal index.");
}
Node cur = dummyHead.next;
for (int i = 0; i < index; i++) {
cur = cur.next;
}
return cur.e;
}
/**
* 获取链表第一个元素
* @return
*/
public E getFirst() {
return get(0);
}
/**
* 获取链表最后一个元素
* @return
*/
public E getLast() {
return get(size - 1);
}
/**
* 更新链表指定位置的元素
* 非常用操作,练习用
* @param index 指定位置
* @return 元素
*/
public void set(int index, E e) {
if (index < 0 || index >= size) {
throw new IllegalArgumentException("set failed, illegal index.");
}
Node cur = dummyHead.next;
for (int i = 0; i < index; i++) {
cur = cur.next;
}
cur.e = e;
}
/**
* 查找链表中是否存在元素e
* @param e 元素
* @return
*/
public boolean contains(E e) {
Node cur = dummyHead.next;
while (cur != null) {
if (cur.e.equals(e)) {
return true;
}
cur = cur.next;
}
return false;
}
/**
* 删除链表指定位置的元素
* 非常用操作,练习用
* @param index 指定位置
* @return 元素
*/
public E remove(int index) {
if (index < 0 || index >= size) {
throw new IllegalArgumentException("remove failed, illegal index.");
}
Node prev = dummyHead;
for (int i = 0; i < index; i++) {
prev = prev.next;
}
Node removeNode = prev.next;
prev.next = removeNode.next;
removeNode.next = null;
size--;
return removeNode.e;
}
/**
* 移除头部元素
* @return
*/
public E removeFirst() {
return remove(0);
}
public E removeLast() {
return remove(size - 1);
}
@Override
public String toString() {
StringBuilder res = new StringBuilder();
Node cur = dummyHead.next;
while (cur != null) {
res.append(cur + "->");
cur = cur.next;
}
res.append("NULL");
return res.toString();
}
/**
* 节点
*/
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();
}
}
}
三、链表的应用
1、力扣203问题:移除链表元素
- 方式一:遍历链表删除元素
/**
* 力扣203. 移除链表元素(https://leetcode-cn.com/)
* Definition for singly-linked list.
* 示例:
* 输入: 1->2->6->3->4->5->6, val = 6
* 输出: 1->2->3->4->5
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode removeElements(ListNode head, int val) {
//虚拟节点
ListNode dummyHead = new ListNode(-1);
dummyHead.next = head;
//遍历删除
ListNode prev = dummyHead;
while (prev.next != null) {
if (prev.next.val == val) {
ListNode removeNode = prev.next;
prev.next = removeNode.next;
removeNode.next = null;
} else {
prev = prev.next;
}
}
return dummyHead.next;
}
}
- 方式二:递归链表
/**
* 力扣203. 移除链表元素(https://leetcode-cn.com/)
* Definition for singly-linked list.
* 示例:
* 输入: 1->2->6->3->4->5->6, val = 6
* 输出: 1->2->3->4->5
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution2 {
/**
* 利用链表天然递归结构性质解决
* @param head 链表
* @param val 待删除值
* @return
*/
public ListNode removeElements(ListNode head, int val) {
//不可拆分的基本问题
if (head == null) {
return null;
}
//拆分为更小的问题
head.next = removeElements(head.next, val);
return head.val == val ? head.next : head;
}
}
四、复杂度分析
-
添加操作O(n)
- addLast(e) => o(n) : 没有链尾节点的指针,需要遍历列表
- addFirst(e) => O(1) : 有链首节点指针,无需遍历
- add(index,e) => O(n/2)=O(n) : 需要遍历链表找到合适位置
-
删除操作O(n)
- removeLast(e) => o(n) : 没有链尾节点的指针,需要遍历列表
- removeFirst(e) => O(1) : 有链首节点指针,无需遍历
- remove(index,e) => O(n/2)=O(n) : 需要遍历链表找到合适位置
-
修改操作O(n)
- set(index,e) => o(n) : 需要遍历链表找到合适位置
-
查找操作O(n)
- get(index) => o(n) : 需要从头遍历列表
- contains(e) => O(1) : 需要从头遍历列表
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。