题目:给定一个头指针和一个结点指针,定义一个函数在O(1)时间删除该结点。链表的定义如下:
class LNode { // 单链表的存储结构
int value;
LNode next;
public LNode() {
}
public LNode(int value) {
this.value = value;
this.next = null;
}
public LNode(int value, LNode next) {
this.value = value;
this.next = next;
}
}
删除结点的两种方法分析:
方法1:在删除结点i之前,先从链表的表头结点开始遍历到i之前的一个结点h,把h的next指向i的下一个结点j,再删除i结点。这个时间复杂度为O(n),显然不满足题目的要求。
方法2:把j结点的内容复盖给i结点,接下来再把结点i的next指向j的下一个结点之后删除结点j。这个不用遍历链表上结点i之前的结点,只有当i结点是尾结点的时候才需要遍历,整体的时间复杂度为O(1),显然是满足要求的。
对于链表的从尾到头遍历,上节已经讲了。
下面为完整代码:
package LinkList;
import java.util.Stack;
public class DeleteNode {
public LNode head = new LNode();// 创建空的头节点
public void insertHead(int value) { // 插入节点,从头开始
LNode link = new LNode(value); // 创建一个带value值的节点
link.next = head.next;
head.next = link;
}
// 两种删除链表结点的方法
public void deleteNode0(LNode pToBeDelete) {// 在删除结点pToBeDelete之前,先从链表的头结点开始遍历到其之前的结点Before
if (head == null || pToBeDelete == null)
return;
LNode beforeDelete = head;
while (beforeDelete.next.value != pToBeDelete.value) {// 找到要删除的结点之前的那个结点
beforeDelete = beforeDelete.next;
}
if (pToBeDelete.next == null)
beforeDelete.next = null;
else
beforeDelete.next = pToBeDelete.next; // 将pToBeDelete前面的结点beforeDelete直接指向pToBeDelete后面的结点
}
public void deleteNode1(LNode pToBeDelete) {// 第二种方法
if (head == null || pToBeDelete == null)
return;
if (pToBeDelete.next != null) { // 如果pToBeDelete后面的结点不为空
LNode afterNode = pToBeDelete.next;// 将afterNode指向pToBeDelete的下一个结点
pToBeDelete.value = afterNode.value;// 将afterNode的值赋给pToBeDelete
pToBeDelete.next = afterNode.next;// 删除afterNode结点
} else if (head.next == pToBeDelete && pToBeDelete.next == null) {// 只有一个结点pToBeDelete
head.next = null;
pToBeDelete = null;
} else if (pToBeDelete.next == null) {// pToBeDelete是尾结点时,需要从头开始遍历,找到尾结点之前的那个结点,将其下一个指向的结点赋为NULL
LNode beforeDelete = head;
while (beforeDelete.next != pToBeDelete) {// 找到要删除的结点之前的那个结点
beforeDelete = beforeDelete.next;
}
beforeDelete.next = null;
}
}
// 从尾到头打印链表方法一:用递归
public void printNode1(LNode node) {
if (node != null) {
printNode1(node.next);
System.out.print(node.value + " ");
}
}
// 从尾到头打印链表方法二:用栈
public void printNode2(LNode node) {
Stack<LNode> stack = new Stack<LNode>();
// node=node.next;
while (node != null) {
stack.push(node);
node = node.next;
}
while (!stack.isEmpty()) {
LNode pnode = stack.pop();
System.out.print(pnode.value + " ");
}
}
public static void main(String[] args) {
DeleteNode deleteNode = new DeleteNode();
for (int i = 0; i < 10; i++) {
deleteNode.insertHead(i);
}
LNode node = deleteNode.head.next;
deleteNode.printNode1(node);// 从尾到头打印链表,方法一
System.out.println();
deleteNode.printNode2(node);// 从尾到头打印链表,方法二
System.out.println();
LNode pToBeDelete = deleteNode.head.next.next.next;// 要删除的结点
System.out.println(pToBeDelete.value);
deleteNode.deleteNode0(pToBeDelete);// 用第一种方法删除
deleteNode.printNode1(node);
System.out.println();
System.out.println(pToBeDelete.value);
deleteNode.deleteNode1(pToBeDelete);// 用第二种方法删除
deleteNode.printNode2(node);
}
}
运行结果为:
0 1 2 3 4 5 6 7 8 9
0 1 2 3 4 5 6 7 8 9
7
0 1 2 3 4 5 6 8 9
7
0 1 2 3 4 5 6 8 9
因为
LNode pToBeDelete = deleteNode.head.next.next.next;// 要删除的结点
的固定值为7,所以第二次在删除时,发现没有这个几点。
将main代码改为:
public static void main(String[] args) {
DeleteNode deleteNode = new DeleteNode();
for (int i = 0; i < 10; i++) {
deleteNode.insertHead(i);
}
LNode node = deleteNode.head.next;
deleteNode.printNode1(node);// 从尾到头打印链表,方法一
System.out.println();
deleteNode.printNode2(node);// 从尾到头打印链表,方法二
System.out.println();
LNode pToBeDelete = node.next.next;// 要删除的结点
System.out.println(pToBeDelete.value);
deleteNode.deleteNode0(pToBeDelete);// 用第一种方法删除
deleteNode.printNode1(node);
System.out.println();
<span style="color:#ff6666;">LNode pToBeDelete1 = node.next.next;// 要删除的结点
System.out.println(pToBeDelete1.value);</span>
deleteNode.deleteNode1(pToBeDelete1);// 用第二种方法删除
deleteNode.printNode2(node);
}
运行结果为:
0 1 2 3 4 5 6 7 8 9
0 1 2 3 4 5 6 7 8 9
7
0 1 2 3 4 5 6 8 9
6
0 1 2 3 4 5 8 9