面试题13:在O(1)时间删除链表结点
题目:给定单向链表的头指针和一个结点指针,定义一个函数在O(1)时间删除该结点。链表结点与函数的定义如下:
struct ListNode
{
int m_nValue;
ListNode* m_pNext;
};
void DeleteNode(ListNode** pListHead, ListNode* pToBeDeleted)
预备知识:
算法执行时间需通过依据该算法编制的程序在计算机上运行时所消耗的时间来度量。而度量一个程序的执行时间通常有两种方法:
(1)事后统计的方法:很多计算机内部都有计时功能,不同算法的程序可通过一组或若干组相同的统计数据来分辨优劣。但是这样做有2个缺点:1)必须编制程序;2)所得时间依赖于计算机的硬件、软件等环境。
(2)事先分析估计的方法:一个使用高级程序语言编写的程序,在计算机上运行时消耗的时间取决于以下因素:1)依据的算法选用何种策略;2)问题规模,例如求100以内还是求1000以内的素数;3)书写的语言;4)编译程序所产生的机器代码的质量;5)机器执行指令的速度。
由此可见,使用绝对的时间单位衡量算法的效率是不合适的。我们需要撇开这些与计算机硬件、软件有关的因素,可以认为一个特定算法”运行工作量”的大小,只依赖于问题的规模(通常用整数量 n表示),或者说它是问题规模的函数。
一个算法由控制结构(顺序、分支和循环3钟)和原操作(指固有数据类型的操作)构成,算法时间取决于两者的综合效果。
为了便于比较同一问题的不同算法,通常的做法是:从算法中选取一种对于所研究的问题(或算法类型)来说是基本操作的原操作,以及该基本操作重复执行的次数作为算法的时间度量。
时间复杂度:
1)找出算法中的基本语句;2)计算基本语句的执行次数的数量级;3)把大O记号表示算法的时间性能。
空间复杂度:
头指针:头指针,它指向链表中的第一个节点。如下图:
头结点:有的时候,我们在单链表的第一个结点之前附设一个结点,称为“头结点”。头结点的数据域可以不存储任何信息,也可以存储链表长度信息,头结点的指针域存储指向第一个结点的指针(即第一个元素结点的存储位置)。此时,单链表的头指针指向头结点。如链表为空,则头结点的指针域为“空”。
链表的基本操作:创建结点;链接结点;输出单个结点;遍历结点;销毁链表;添加结点;移除结点
#include "stdafx.h"
#include "list.h"
#include <stdio.h>
#include <stdlib.h>
//创建链表结点
ListNode* CreateListNode(int value)
{
ListNode* pNode = new ListNode(); //结构体起始指针(头指针)
pNode->m_nValue = value;
pNode->m_pNext = NULL;
return pNode;
}
//连接链表结点
void ConnectListNodes(ListNode* pCurrent, ListNode* pNext)
{
if(pCurrent == NULL)
{
printf("Error to connect two nodes.\n");
exit(1);
}
pCurrent->m_pNext = pNext;
}
//输出链表结点
void PrintListNode(ListNode* pNode)
{
if(pNode == NULL)
{
printf("The node is NULL\n");
}
else
{
printf("The key in node is %d.\n", pNode->m_nValue);
}
}
//遍历链表并打印
void PrintList(ListNode* pHead)
{
printf("PrintList starts.\n");
ListNode* pNode = pHead;
while(pNode != NULL)
{
printf("%d\t", pNode->m_nValue);
pNode = pNode->m_pNext;
}
printf("\nPrintList ends.\n");
}
//销毁链表(每一个结点都要销毁)
void DestroyList(ListNode* pHead)
{
ListNode* pNode = pHead;
while(pNode != NULL)
{
pHead = pHead->m_pNext; //一个一个结点的销毁
delete pNode;
pNode = pHead;
}
}
//向链表尾部添加结点
void AddToTail(ListNode** pHead, int value)
{
ListNode* pNew = new ListNode();
pNew->m_nValue = value; //初始化
pNew->m_pNext = NULL;
if(*pHead == NULL)
{
*pHead = pNew;
}
else
{ //在结点的尾部添加
ListNode* pNode = *pHead;
while(pNode->m_pNext != NULL)
pNode = pNode->m_pNext;
pNode->m_pNext = pNew;
}
}
//移去结点
void RemoveNode(ListNode** pHead, int value)
{
if(pHead == NULL || *pHead == NULL)
return;
ListNode* pToBeDeleted = NULL;
if((*pHead)->m_nValue == value) //刚好等于这个值
{
pToBeDeleted = *pHead;
*pHead = (*pHead)->m_pNext; //指针改变
}
else
{
ListNode* pNode = *pHead;
while(pNode->m_pNext != NULL && pNode->m_pNext->m_nValue != value)
pNode = pNode->m_pNext; //遍历
if(pNode->m_pNext != NULL && pNode->m_pNext->m_nValue == value) //遍历找到了这个值
{
pToBeDeleted = pNode->m_pNext;
pNode->m_pNext = pNode->m_pNext->m_pNext; //指针改变
}
}
if(pToBeDeleted != NULL)
{
delete pToBeDeleted; //销毁
pToBeDeleted = NULL;
}
}
思路:
代码实现:
List.h
struct ListNode
{
int m_nValue;
ListNode* m_pNext;
};
//使用标识符:_declspec(dllimport)表明函数是从动态库中引入的 (孙鑫VC++ 712)
__declspec( dllexport ) ListNode* CreateListNode(int value);
__declspec( dllexport ) void ConnectListNodes(ListNode* pCurrent, ListNode* pNext);
__declspec( dllexport ) void PrintListNode(ListNode* pNode);
__declspec( dllexport ) void PrintList(ListNode* pHead);
__declspec( dllexport ) void DestroyList(ListNode* pHead);
__declspec( dllexport ) void AddToTail(ListNode** pHead, int value);
__declspec( dllexport ) void RemoveNode(ListNode** pHead, int value);
List.cpp
#include "stdafx.h"
#include "list.h"
#include <stdio.h>
#include <stdlib.h>
//创建链表结点
ListNode* CreateListNode(int value)
{
ListNode* pNode = new ListNode(); //结构体起始指针(头指针)
pNode->m_nValue = value;
pNode->m_pNext = NULL;
return pNode;
}
//连接链表结点
void ConnectListNodes(ListNode* pCurrent, ListNode* pNext)
{
if(pCurrent == NULL)
{
printf("Error to connect two nodes.\n");
exit(1);
}
pCurrent->m_pNext = pNext;
}
//输出链表结点
void PrintListNode(ListNode* pNode)
{
if(pNode == NULL)
{
printf("The node is NULL\n");
}
else
{
printf("The key in node is %d.\n", pNode->m_nValue);
}
}
//遍历链表并打印
void PrintList(ListNode* pHead)
{
printf("PrintList starts.\n");
ListNode* pNode = pHead;
while(pNode != NULL)
{
printf("%d\t", pNode->m_nValue);
pNode = pNode->m_pNext;
}
printf("\nPrintList ends.\n");
}
//销毁链表(每一个结点都要销毁)
void DestroyList(ListNode* pHead)
{
ListNode* pNode = pHead;
while(pNode != NULL)
{
pHead = pHead->m_pNext; //一个一个结点的销毁
delete pNode;
pNode = pHead;
}
}
//向链表尾部添加结点
void AddToTail(ListNode** pHead, int value)
{
ListNode* pNew = new ListNode();
pNew->m_nValue = value; //初始化
pNew->m_pNext = NULL;
if(*pHead == NULL)
{
*pHead = pNew;
}
else
{ //在结点的尾部添加
ListNode* pNode = *pHead;
while(pNode->m_pNext != NULL)
pNode = pNode->m_pNext;
pNode->m_pNext = pNew;
}
}
//移去结点
void RemoveNode(ListNode** pHead, int value)
{
if(pHead == NULL || *pHead == NULL)
return;
ListNode* pToBeDeleted = NULL;
if((*pHead)->m_nValue == value) //刚好等于这个值
{
pToBeDeleted = *pHead;
*pHead = (*pHead)->m_pNext; //指针改变
}
else
{
ListNode* pNode = *pHead;
while(pNode->m_pNext != NULL && pNode->m_pNext->m_nValue != value)
pNode = pNode->m_pNext; //遍历
if(pNode->m_pNext != NULL && pNode->m_pNext->m_nValue == value) //遍历找到了这个值
{
pToBeDeleted = pNode->m_pNext;
pNode->m_pNext = pNode->m_pNext->m_pNext; //指针改变
}
}
if(pToBeDeleted != NULL)
{
delete pToBeDeleted; //销毁
pToBeDeleted = NULL;
}
}
main.cpp
void DeleteNode(ListNode** pListHead, ListNode* pToBeDeleted)
{
if(!pListHead || !pToBeDeleted)
return;
// 要删除的结点不是尾结点
if(pToBeDeleted->m_pNext != NULL)
{
ListNode* pNext = pToBeDeleted->m_pNext;
pToBeDeleted->m_nValue = pNext->m_nValue;
pToBeDeleted->m_pNext = pNext->m_pNext;
delete pNext;
pNext = NULL;
}
// 链表只有一个结点,删除头结点(也是尾结点)
else if(*pListHead == pToBeDeleted)
{
delete pToBeDeleted;
pToBeDeleted = NULL;
*pListHead = NULL;
}
// 链表中有多个结点,删除尾结点
else
{
ListNode* pNode = *pListHead;
while(pNode->m_pNext != pToBeDeleted)
{
pNode = pNode->m_pNext;
}
pNode->m_pNext = NULL;
delete pToBeDeleted;
pToBeDeleted = NULL;
}
}
// ====================测试代码====================
void Test(ListNode* pListHead, ListNode* pNode)
{
printf("The original list is: \n");
PrintList(pListHead);
printf("The node to be deleted is: \n");
PrintListNode(pNode);
DeleteNode(&pListHead, pNode);
printf("The result list is: \n");
PrintList(pListHead);
}
// 链表中有多个结点,删除中间的结点
void Test1()
{
ListNode* pNode1 = CreateListNode(1);
ListNode* pNode2 = CreateListNode(2);
ListNode* pNode3 = CreateListNode(3);
ListNode* pNode4 = CreateListNode(4);
ListNode* pNode5 = CreateListNode(5);
ConnectListNodes(pNode1, pNode2);
ConnectListNodes(pNode2, pNode3);
ConnectListNodes(pNode3, pNode4);
ConnectListNodes(pNode4, pNode5);
Test(pNode1, pNode3);
DestroyList(pNode1);
}
// 链表中有多个结点,删除尾结点
void Test2()
{
ListNode* pNode1 = CreateListNode(1);
ListNode* pNode2 = CreateListNode(2);
ListNode* pNode3 = CreateListNode(3);
ListNode* pNode4 = CreateListNode(4);
ListNode* pNode5 = CreateListNode(5);
ConnectListNodes(pNode1, pNode2);
ConnectListNodes(pNode2, pNode3);
ConnectListNodes(pNode3, pNode4);
ConnectListNodes(pNode4, pNode5);
Test(pNode1, pNode5);
DestroyList(pNode1);
}
// 链表中有多个结点,删除头结点
void Test3()
{
ListNode* pNode1 = CreateListNode(1);
ListNode* pNode2 = CreateListNode(2);
ListNode* pNode3 = CreateListNode(3);
ListNode* pNode4 = CreateListNode(4);
ListNode* pNode5 = CreateListNode(5);
ConnectListNodes(pNode1, pNode2);
ConnectListNodes(pNode2, pNode3);
ConnectListNodes(pNode3, pNode4);
ConnectListNodes(pNode4, pNode5);
Test(pNode1, pNode1);
DestroyList(pNode1);
}
// 链表中只有一个结点,删除头结点
void Test4()
{
ListNode* pNode1 = CreateListNode(1);
Test(pNode1, pNode1);
}
// 链表为空
void Test5()
{
Test(NULL, NULL);
}
int _tmain(int argc, _TCHAR* argv[])
{
Test1();
Test2();
Test3();
Test4();
Test5();
return 0;
}