我们知道顺序表中删除中间元素的时间复杂度是 O ( N ) O(N) O(N),单向不带头非循环链表中如果知道想删除一个结点,我们必须知道这个结点的前一个节点,否则的话,时间复杂度也是 O ( N ) O(N) O(N),本片博客介绍两种取巧的删除方法。
顺序表中删除元素
- 我们知道,顺序表中删除中间元素,都是将后面的元素向前移动一个位置,来覆盖要删除的元素,这里,我们可以采用一种更取巧的方法来删除中间元素。
- 如果我们要删除顺序表中指定下标的元素,我们可以将要删除的元素和最后一个元素的值进行交换,然后,将顺序表的size减1即可。这时候加粗样式时间复杂度就是 O ( 1 ) O(1) O(1)。
- 但是这种方法会破坏顺序表中元素的顺序,所以不推荐使用,但是在一些特殊场景可以考虑这种思想。
下面看代码,我们实现一个简单的顺序表。
public class SeqList {
private int[] arr = new int[10];
private int size = 0;
// 删除指定下标元素
public void remove(int index) {
if (index < 0 || index >= this.size) {
return;
}
swap(this.size - 1, index);
--this.size;
}
private void swap(int i, int j) {
int temp = this.arr[i];
this.arr[i] = this.arr[j];
this.arr[j] = temp;
}
// 简单的插入用来演示,暂时不考虑扩容问题
public void add(int index, int val) {
if (index < 0 || index > this.size) {
return;
}
for (int i = this.size; i > index; --i) {
this.arr[i] = this.arr[i - 1];
}
this.arr[index] = val;
++this.size;
}
public void display() {
System.out.print("[");
for (int i = 0; i < this.size; ++i) {
System.out.print(this.arr[i]);
if (i == this.size - 1) {
break;
}
System.out.print(", ");
}
System.out.println("]");
}
}
下面,我们写一个测试代码:
public class Test {
public static void main(String[] args) {
SeqList list = new SeqList();
list.add(0, 11);
list.add(1, 12);
list.add(2, 13);
list.add(3, 14);
list.add(4, 15);
list.display();
// 删除下标为1的元素
list.remove(1);
list.display();
}
}
单向不带头非循环链表删除结点
- 对于单向不带头非循环链表来说,如果想要删除中间的某个节点,如果我们知道这个结点的前一个节点,那么删除操作的时间复杂度就是 O ( 1 ) O(1) O(1)。
- 但是如果我们知道要删除结点的引用,我们必须去遍历链表,找到该节点的前一个节点,就可以删除该结点,但是这时候,时间复杂度就是 O ( N ) O(N) O(N)。
- 其实我们也可以以 O ( 1 ) O(1) O(1)的时间复杂度来删除结点,我们将当前结点的值拷贝到下一个结点,然后去删除当前结点的下一个结点,这时候时间复杂度就是 O ( 1 ) O(1) O(1),这种方法不适用于删除最后一个结点。
下面看代码,我们来实现一个简单的链表。
public class LinkedList {
private Node head = null;
// 根据一个结点的引用,删除该节点
public void remove(Node node) {
if (node == null || node.next == null) {
return;
}
node.val = node.next.val;
node.next = node.next.next;
}
// 写一个头插来方便演示
public void addFirst(int val) {
Node node = new Node(val);
node.next = this.head;
this.head = node;
}
// 写一个获取结点引用的方法,便于演示
public Node search(int val) {
for (Node cur = this.head; cur != null; cur = cur.next) {
if (cur.val == val) {
return cur;
}
}
return null;
}
public void display() {
System.out.print("[");
for (Node cur = this.head; cur != null; cur = cur.next) {
System.out.print(cur.val);
System.out.print(" -> ");
}
System.out.println("null]");
}
}
class Node {
public int val;
public Node next = null;
public Node(int val) {
this.val = val;
}
}
我们来写一段测试代码来验证一下:
public class Test {
public static void main(String[] args) {
LinkedList list = new LinkedList();
list.addFirst(5);
list.addFirst(4);
list.addFirst(3);
list.addFirst(2);
list.addFirst(1);
list.display();
Node toRemove = list.search(3);
list.remove(toRemove);
list.display();
}
}