本文參考自《劍指offer》一書,代碼采用Java語言。
題目
給定單向鏈表的頭指針和一個結點指針,定義一個函數在O(1)時間刪除該結點。
思路
通常那樣從頭開始查找刪除需要的時間為O(n),要在O(1)時間刪除某結點,可以這樣實現:設待刪除結點i的下一個結點為j,把j的值復制到i,再把i的指針指向j的下一個結點,最后刪除j,效果就相當於刪除j。
注意特殊情況:1.當待刪除結點i為尾結點時,無下一個結點,則只能從頭到尾順序遍歷;2.當鏈表中只有一個結點時(即是頭結點,又是尾結點),必須把頭結點也設置為null。
本題有個缺陷:要求O(1)時間刪除,相當於隱藏了一個假設:待刪除的結點的確在表中
測試算例
1.功能測試(多個結點鏈表,刪除頭結點、中間結點和尾結點;單個結點鏈表)
2.特殊測試(頭結點或刪除結點為null)
完整Java代碼
(含測試代碼)
package _18;
/**
*
* @Description 面試題18(一):在O(1)時間刪除鏈表結點
*
* @author yongh
* @date 2018年9月18日 下午3:57:59
*/
//題目:給定單向鏈表的頭指針和一個結點指針,定義一個函數在O(1)時間刪除該
//結點。
//注:本題存在缺陷,要求O(1)時間,則無法確定待刪除結點的確在表中
public class DeleteNodeInList {
public class ListNode{
int val;
ListNode next;
public ListNode(int value,ListNode nextNode) {
val=value;
next=nextNode;
}
}
/**
* 返回值:頭結點
* 返回值不可以為void,否則頭結點無法刪除
* 即:函數中雖然令head=null,但返回到主程序后,
* head還是不變,所以令該函數返回值為ListNode
*/
public ListNode deleteNode(ListNode head,ListNode pToBeDeleted) {
if(head==null||pToBeDeleted==null)
return head;
//待刪除結點不是尾結點
if(pToBeDeleted.next!=null) {
ListNode nextNode=pToBeDeleted.next;
pToBeDeleted.val=nextNode.val;
pToBeDeleted.next=nextNode.next;
nextNode=null;
//只有一個結點(即是尾結點,又是頭結點)
}else if(head==pToBeDeleted) {
pToBeDeleted=null;
head=null;
//鏈表含多個結點,刪除尾結點
}else {
ListNode preNode=head;
while(preNode.next!=pToBeDeleted && preNode!=null) {
preNode=preNode.next;
}
if(preNode==null) {
System.out.println("無法找到待刪除結點!");
return head;
}
preNode.next=null;
pToBeDeleted=null;
}
return head;
}
//=========測試代碼==========
void test(ListNode head,ListNode PToBeDelete) {
System.out.println("============");
System.out.print("The original list is: ");
ListNode curr=head;
if(curr!=null) {
while(curr.next!=null) {
System.out.print(curr.val+",");
curr=curr.next;
}
System.out.println(curr.val);
}else {
System.out.println();
}
System.out.print("The node to be deleted is: ");
if(PToBeDelete!=null)
System.out.println(PToBeDelete.val);
else
System.out.println();
curr=deleteNode(head, PToBeDelete);
System.out.print("The result list is: ");
if(curr!=null) {
while(curr.next!=null) {
System.out.print(curr.val+",");
curr=curr.next;
}
System.out.println(curr.val);
}else {
System.out.println();
}
System.out.println("============");
}
/**
* 鏈表含多個結點,刪除頭結點
*/
void test1() {
ListNode p4=new ListNode(4, null);
ListNode p3=new ListNode(3, p4);
ListNode p2=new ListNode(2, p3);
ListNode p1=new ListNode(1, p2);
test(p1, p1);
}
/**
* 鏈表含多個結點,刪除中間結點
*/
void test2() {
ListNode p4=new ListNode(4, null);
ListNode p3=new ListNode(3, p4);
ListNode p2=new ListNode(2, p3);
ListNode p1=new ListNode(1, p2);
test(p1, p3);
}
/**
* 鏈表含多個結點,刪除尾結點
*/
void test3() {
ListNode p4=new ListNode(4, null);
ListNode p3=new ListNode(3, p4);
ListNode p2=new ListNode(2, p3);
ListNode p1=new ListNode(1, p2);
test(p1, p4);
}
/**
* 鏈表含一個結點,刪除結點
*/
void test4() {
ListNode p4=new ListNode(4, null);
test(p4, p4);
}
/**
* 鏈表為空
*/
void test5() {
test(null, null);
}
public static void main(String[] args) {
DeleteNodeInList demo = new DeleteNodeInList();
demo.test1();
demo.test2();
demo.test3();
demo.test4();
demo.test5();
}
}
============ The original list is: 1,2,3,4 The node to be deleted is: 1 The result list is: 2,3,4 ============ ============ The original list is: 1,2,3,4 The node to be deleted is: 3 The result list is: 1,2,4 ============ ============ The original list is: 1,2,3,4 The node to be deleted is: 4 The result list is: 1,2,3 ============ ============ The original list is: 4 The node to be deleted is: 4 The result list is: ============ ============ The original list is: The node to be deleted is: The result list is: ============
收獲
1.鏈表中刪除結點的方法中,雖然直接令head=null了,但在主函數中的head還是不變,因此要令刪除結點的返回值為ListNode,將返回值賦值給主函數中的head,這樣才能實現真正的刪除。
2.另一種情況可以令刪除函數返回值為void,只是需要定義一個頭結點head(1中的head相當於是第一個結點),這個頭結點中不存任何數據,僅僅起到指針的作用,第一個結點是頭結點的下一個結點,通過對head.next操作,能夠實現真正的刪除。
3.和鏈表有關的特殊情況:頭結點,尾結點,鏈表僅一個結點,null等。
