以下代码片段包含各种remove()
方法,取自我用Java
编写的LinkedList
实现之一。
代码
LinkedList.java (部分)
private int size;
private LinkedListNode<T> head;
private LinkedListNode<T> end;
@Override
public T remove(int k) {
checkElementIndex(k);
LinkedListNode<T> preNode = null;
LinkedListNode<T> node = head;
while (k-- > 0) {
preNode = node;
node = node.next;
}
T result = (T) node.value;
removeNode(node, preNode);
return result;
}
@Override
public boolean removeValue(T v) {
LinkedListNode<T> preNode = null;
LinkedListNode<T> node = head;
while (true) {
if (node == null) return false;
if (node.getValue().compareTo(v) == 0) break;
preNode = node;
node = node.next;
}
removeNode(node, preNode);
return true;
}
@Override
public int removeAllValue(T v) {
int rc = 0;
LinkedListNode<T> preNode = null;
LinkedListNode<T> node = head;
while (true) {
if (node == null) return rc;
if (node.getValue().compareTo(v) == 0) {
rc++;
if (removeNode(node, preNode)) break;
continue;
}
preNode = node;
node = node.next;
}
return rc;
}
protected boolean removeNode(LinkedListNode node, LinkedListNode preNode) {
LinkedListNode nextNode = node.next;
boolean isEnd = (nextNode == null);
if (isEnd) {
if (preNode == null) {
head = null;
} else {
preNode.next = null;
}
end = preNode;
} else {
node.next = nextNode.next;
node.value = nextNode.value;
}
size--;
return isEnd;
}
@Override
public T removeHead() {
return remove(0);
}
@Override
public T removeEnd() {
return remove(size - 1);
}
LinkedListTest.java(部分)(单元测试,通过TestNG)
import org.testng.Assert;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
public class LinkedListTest {
private int n = 10;
private LinkedList<Integer> llist;
private LinkedList<Integer> dupEvenLlist;
@BeforeMethod
public void init() {
llist = new LinkedList();
Assert.assertTrue(llist.isEmpty());
LinkedList.appendRangeNum(llist, 0, n);
dupEvenLlist = new LinkedList();
LinkedList.appendRangeNum(dupEvenLlist, 0, n);
LinkedList.appendRangeNum(dupEvenLlist, 0, n, 2);
Assert.assertEquals(dupEvenLlist.size(), n + n / 2);
}
@Test
public void testRemoveByIndex() {
for (int i = 0; i < n; i++) {
Assert.assertEquals(llist.removeEnd().intValue(), n - 1 - i);
Assert.assertEquals(llist.size(), n - 1 - i);
}
Assert.assertTrue(llist.isEmpty());
}
@Test
public void testRemoveByValue() {
Assert.assertFalse(llist.removeValue(n));
for (int i = n - 1; i >= 0; i--) {
Assert.assertTrue(llist.removeValue(i));
Assert.assertEquals(llist.size(), i);
}
Assert.assertTrue(llist.isEmpty());
Assert.assertFalse(llist.removeValue(0));
for (int i = 0; i < n; i++) {
Assert.assertTrue(dupEvenLlist.removeValue(i));
}
Assert.assertFalse(dupEvenLlist.isEmpty());
Assert.assertEquals(dupEvenLlist.size(), n / 2);
}
@Test
public void testRemoveAllByValue() {
Assert.assertEquals(dupEvenLlist.removeAllValue(n), 0);
int remainSize = dupEvenLlist.size();
for (int i = 0; i < n; i++) {
int rc = dupEvenLlist.removeAllValue(i);
Assert.assertEquals(rc, i % 2 == 0 ? 2 : 1);
remainSize -= rc;
Assert.assertEquals(dupEvenLlist.size(), remainSize);
}
Assert.assertTrue(dupEvenLlist.isEmpty());
Assert.assertEquals(dupEvenLlist.removeAllValue(0), 0);
}
}
所有测试用例都将通过。
解释
方法:
步骤:
* 循环到目标节点,
* 对于每一步,
记录:
* 上一个节点,
* 这个节点,
* 获取目标节点的下一个节点,
* 获取目标节点的值,
作为返回值,
* 如果目标是尾部,
* 如果也是头部,
head = null;
* 如果不是头部,
preNode.next = null;
* end = preNode;
* 如果目标不是尾部,
用其下一个节点替换它,
逻辑:
* node.value = nextNode.value;
* node.next = nextNode.next;
* 返回之前跟踪的目标节点的值,
boolean removeValue(T v)
,根据值删除元素,仅删除第一次出现的元素(如果有)。
逻辑与按索引删除相似。
区别在于:
- 在初始搜索时,比较元素而不是循环到索引以找到目标。
- 返回布尔值表示是否已删除,而不是已删除的值。
int removeAllValue(T v)
,根据值删除所有元素,删除所有出现的元素。
类似于按值删除。
区别在于:
【while()内部】
- 它会搜索直到结束的所有出现。
- 删除一个出现后,它“继续”重新检查当前节点。
因为当前节点实际上已经被其下一个节点替换了。
- 如果删除的节点是末尾,则返回。
这依赖于removeNode()
的返回值。
- 它记录已删除出现的计数。
【返回值】
boolean removeNode(LinkedListNode node, LinkedListNode preNode)
,根据给定的preNode和node删除节点。
删除给定的节点(保证存在)和给定的前一个节点,可能为null。
返回值表示已删除节点是否为末尾,主要用于支持removeAllValue()
。
T removeHead()
,T removeEnd()
,删除头部/尾部。
只需调用按索引删除,并传递相应的索引0
和size - 1
即可。
提示:
LinkedList
表示链表,包含 size
、head
、end
三个字段和泛型类型 T
(节点中的值类型),它不是线程安全的。
checkElementIndex()
方法用于检查给定索引是否超出范围,如果超出范围则抛出异常。
LinkedListNode
表示链表中的节点,包含 value
和 next
两个字段。
复杂度
- 移除单个元素:
O(k)
- 根据值移除所有元素:
O(n)
其中: