以下代码 sn-p 包含各种remove() 方法,取自我的LinkedList 实现之一,用Java 编写。
代码
LinkedList.java (部分)
private int size; // node count,
private LinkedListNode<T> head; // first node,
private LinkedListNode<T> end; // last node,
/**
* Remove by index.
*
* @param k index, start from 0,
* @return value of removed node, or null if not removed,
*/
@Override
public T remove(int k) {
checkElementIndex(k);
// find target node, and remember previous node,
LinkedListNode<T> preNode = null;
LinkedListNode<T> node = head;
while (k-- > 0) {
preNode = node;
node = node.next;
}
T result = (T) node.value; // keep return value,
removeNode(node, preNode); // remove
return result;
}
/**
* Remove by value, only remove the first occurrence, if any.
*
* @param v
* @return whether removed,
*/
@Override
public boolean removeValue(T v) {
// find target node, and remember previous node,
LinkedListNode<T> preNode = null;
LinkedListNode<T> node = head;
while (true) {
if (node == null) return false;// not found,
if (node.getValue().compareTo(v) == 0) break; // value found,
preNode = node;
node = node.next;
}
removeNode(node, preNode); // remove
return true;
}
/**
* Remove by value, remove all occurrences.
*
* @param v
* @return count of nodes removed,
*/
@Override
public int removeAllValue(T v) {
int rc = 0;
// find target node, and remember previous node,
LinkedListNode<T> preNode = null;
LinkedListNode<T> node = head;
while (true) {
if (node == null) return rc; // reach end,
if (node.getValue().compareTo(v) == 0) { // value found,
rc++;
if (removeNode(node, preNode)) break; // remove, break if it's end,
continue; // recheck this node, since it become the next node,
}
preNode = node;
node = node.next;
}
return rc;
}
/**
* Remove given node, which guarantee to exists. Also reduce the size by 1.
*
* @param node node to delete,
* @param preNode previous node, could be null,
* @return indicate whether removed node is end,
*/
protected boolean removeNode(LinkedListNode node, LinkedListNode preNode) {
LinkedListNode nextNode = node.next; // next node,
boolean isEnd = (nextNode == null);
if (isEnd) { // target is end,
if (preNode == null) { // target is also head,
head = null;
} else { // target is not head, thus preNode is not null,
preNode.next = null;
}
end = preNode;
} else { // target is not end,
// replace target with next node,
node.next = nextNode.next;
node.value = nextNode.value;
}
size--; // reduce size by 1,
return isEnd;
}
/**
* Remove head node,
*
* @return
*/
@Override
public T removeHead() {
return remove(0);
}
/**
* Remove end node,
*
* @return
*/
@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;
/**
* LinkedList test.
*
* @author eric
* @date 1/28/19 6:03 PM
*/
public class LinkedListTest {
private int n = 10;
private LinkedList<Integer> llist; // linked list,
private LinkedList<Integer> dupEvenLlist; // linked list, with duplicated even values,
@BeforeMethod
public void init() {
// init llist,
llist = new LinkedList(); // create linked list,
Assert.assertTrue(llist.isEmpty());
LinkedList.appendRangeNum(llist, 0, n); // append range,
// init dupEvenLlist,
dupEvenLlist = new LinkedList(); // create linked list,
LinkedList.appendRangeNum(dupEvenLlist, 0, n); // append range,
LinkedList.appendRangeNum(dupEvenLlist, 0, n, 2); // append range, again, with step as 2 (only even numbers),
Assert.assertEquals(dupEvenLlist.size(), n + n / 2);
}
// non-remove related test cases ... are deleted,
// remove(k) - remove by index,
@Test
public void testRemoveByIndex() {
for (int i = 0; i < n; i++) {
Assert.assertEquals(llist.removeEnd().intValue(), n - 1 - i); // remove by end, in turn it remove by index,
Assert.assertEquals(llist.size(), n - 1 - i);
}
Assert.assertTrue(llist.isEmpty());
}
// remove(v) - remove by value,
@Test
public void testRemoveByValue() {
Assert.assertFalse(llist.removeValue(n)); // not exists,
for (int i = n - 1; i >= 0; i--) {
Assert.assertTrue(llist.removeValue(i)); // remove by value,
Assert.assertEquals(llist.size(), i);
}
Assert.assertTrue(llist.isEmpty());
Assert.assertFalse(llist.removeValue(0)); // empty,
// remove from list with duplicated value,
for (int i = 0; i < n; i++) {
Assert.assertTrue(dupEvenLlist.removeValue(i));
}
Assert.assertFalse(dupEvenLlist.isEmpty());
Assert.assertEquals(dupEvenLlist.size(), n / 2);
}
// removeAll(v) - remove all occurrences by value,
@Test
public void testRemoveAllByValue() {
Assert.assertEquals(dupEvenLlist.removeAllValue(n), 0); // not exists,
int remainSize = dupEvenLlist.size();
for (int i = 0; i < n; i++) {
int rc = dupEvenLlist.removeAllValue(i); // remove all by value,
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); // empty,
}
}
所有测试用例都会通过。
说明
方法:
脚步:
* 循环到目标节点,
* 对于每一步,
记录:
* 前一个节点,
* 这个节点,
* 获取目标节点的下一个节点,
* 获取目标节点的值,
稍后作为返回值,
* 如果目标是结束,
* 如果也是头,
头=空;
* 如果不是头,
preNode.next = null;
* 结束 = 前节点;
* 如果目标没有结束,
用下一个节点替换它,
逻辑:
* 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。
删除保证存在的给定节点,给定的前一个节点可能为空。
返回值表示移除节点是否结束,主要用于支持removeAllValue()。
T removeHead(), T removeEnd(), 删除头/尾。
只需按索引调用remove,并传递相应的索引0 和size - 1。
提示:
-
LinkedList 表示链表,有字段size、head、end 和泛型类型T(用于节点中的值类型),它不是线程安全的。
-
checkElementIndex() 方法,检查给定的索引,如果超出范围则抛出异常。
-
LinkedListNode,表示链表中的节点。带有字段value、next。
复杂性
地点: