在 O(1) 时间删除链表结点(C 和 Python 实现)
(说明:本博客中的题目、题目详细说明及参考代码均摘自 “何海涛《剑指Offer:名企面试官精讲典型编程题》2012年”)
题目
给定单向链表的头指针和一个结点指针,定义一个函数在 O(1) 时间删除该结点.链表结点与函数的定义如下:
struct ListNode {
int m_nValue;
ListNode* m_pNext;
}; void DeleteNode(ListNode** pListHead, ListNode* pToBeDeleted);
算法设计思想
通常,在单向链表中,删除一个链表的结点,都会先从表头开始遍历整个链表,找到需要删除的结点的前一个结点,然后将这个结点的(指向下一个结点的)指针元素指向需要删除结点的下一个结点,最后把需要删除的结点删除.但此过程的平均时间复杂度为 O(n).
若要求以平均时间复杂度 O(1) 删除结点,则采取的是有些取巧的方法,其实质删除的是需要删除的结点中保存的元素值,而不是结点本身.此时,只需将需要删除的结点的下一个结点中保存的所有元素值赋值给需要删除的结点,然后删除需要删除的结点的下一个结点.需要注意,当需要删除的结点的下一个结点为空(NULL)时,这时只能采用按顺序遍历单向链表的方法,去寻找需要删除的结点的前一个结点,设置此结点的指向下一个结点的指针为空,再删除需要删除的结点,也就是前述的平均时间复杂度为 O(n) 的删除链表结点的方法,不过由于这是特例,并不影响这种方法的平均时间复杂度.这两种方法的示意图,如下图所示:
图中,(a) 表示初始链表;(b) 表示采用时间复杂度 O(n) 方法删除链表元素;(c) 表示采用时间复杂度 O(1) 方法删除链表元素.其中,结点 i 为需要删除的结点,结点 h 为结点 i 的前一个结点,结点 j 为结点 i 的后一个结点,当删除结点 i 后,字母 i 消失,其边框变为虚线框.
C 实现
/*
* Author: klchang
* Date: 2018.3.31
* Description: Delete elements of a linked list with the time complexity of O(1).
*/ #include <stdio.h>
#include <stdlib.h> // malloc, free // Type definition
typedef struct ListNode ListNode; struct ListNode {
int m_nValue;
ListNode* m_pNext;
}; // Function Declaration
ListNode* CreateNode(int value);
void PrintLinkedList(const ListNode* head);
void CreateLinkedList(ListNode** pHead);
void DeleteLinkedList(ListNode** pHead);
void DeleteNode(ListNode** pListHead, ListNode* pToBeDeleted); int main()
{
ListNode* head = NULL; CreateLinkedList(&head);
PrintLinkedList(head); DeleteNode(&head, head->m_pNext); // Delete the intermediate node
PrintLinkedList(head); DeleteNode(&head, head); // Delete the head node
PrintLinkedList(head); DeleteLinkedList(&head); return ;
} void CreateLinkedList(ListNode** pHead)
{
// Create the first node
*pHead = CreateNode(); ListNode *p1, *p2;
int i = ;
p1 = *pHead;
// Create Linked List 0 -> 1 -> ... -> 5 -> NULL
for (; i < ; ++ i) {
p2 = CreateNode(i);
p1->m_pNext = p2;
p1 = p2;
}
} void DeleteLinkedList(ListNode** pHead)
{
ListNode* pToBeDeleted; while (*pHead != NULL) {
pToBeDeleted = *pHead;
*pHead = (*pHead)->m_pNext;
// Release Node
free(pToBeDeleted);
}
} void PrintLinkedList(const ListNode* head)
{
if (head == NULL)
return; ListNode* pNode = (ListNode*)head;
for (; pNode != NULL; pNode = pNode->m_pNext) {
printf("%d -> ", pNode->m_nValue);
} printf("NULL\n");
} ListNode* CreateNode(int value)
{
ListNode* pNode = (ListNode*)malloc(sizeof(ListNode)); pNode->m_nValue = value;
pNode->m_pNext = NULL; return pNode;
} // O(1) Time Complexity
void DeleteNode(ListNode** pListHead, ListNode* pToBeDeleted)
{
if (pListHead == NULL || *pListHead == NULL || pToBeDeleted == NULL)
return; ListNode* pNode = pToBeDeleted;
if (pToBeDeleted->m_pNext != NULL) {
// Copy the contents of the next node to the node to be deleted
pToBeDeleted = pToBeDeleted->m_pNext;
pNode->m_nValue = pToBeDeleted->m_nValue;
pNode->m_pNext = pToBeDeleted->m_pNext;
} else if (pToBeDeleted == *pListHead) {
// Only have one node, i.e. the node needs to be deleted
*pListHead = NULL;
} else {
// The node needed to delete is the tail element of linked list
for (pNode = *pListHead; pNode != NULL; pNode = pNode->m_pNext)
if (pNode->m_pNext == pToBeDeleted)
break;
if (pNode == NULL) {
printf("Error: the node pointer to be deleted is not in the list!");
exit(-);
}
}
// Release the memory of pNode
free(pToBeDeleted);
}
Python 实现
#!/usr/bin/env python
# -*- coding: utf8 -*-
"""
# Author: klchang
# Date: 2018.3.31
# Description: Delete elements of a linked list in the time complexity of O(1).
""" class ListNode:
def __init__(self, value):
self.value = value
self.next_ = None # Copy the next node of the node to be deleted
# Time Complexity: O(1)
def delete_node(head, node_to_del):
if (head is None) or (node_to_del is None):
return head
if node_to_del.next_ is not None:
# Copy the content of the next node
next_node = node_to_del.next_
node_to_del.value = next_node.value
node_to_del.next_ = next_node.next_
del(next_node)
elif head == node_to_del:
# Only has one node
del(node_to_del)
return None
else:
# More than one node in the linked list, no next node
node = head
while node is not None:
if node.next_ == node_to_del:
break
node = node.next_
else:
print("The node to be deleted doesn't exist!")
if node is not None:
node.next_ = node_to_del.next_
del(node_to_del) return head def print_linked_list(head):
# Note: input param belongs to value pass
while head is not None:
print("%d -> " % head.value),
head = head.next_
print("None") def main():
# Create a linked list: 0 -> 1 -> 2 -> ... -> 5
head = ListNode(0)
node = head
for i in range(6):
node.next_ = ListNode(i)
node = node.next_
print_linked_list(head) # Delete a middle node with delete_node() function
head = delete_node(head, head.next_)
print_linked_list(head) # Delete the head node with delete_node() function
head = delete_node(head, head)
print_linked_list(head) if __name__ == '__main__':
main()
参考代码
1. targetver.h
#pragma once // The following macros define the minimum required platform. The minimum required platform
// is the earliest version of Windows, Internet Explorer etc. that has the necessary features to run
// your application. The macros work by enabling all features available on platform versions up to and
// including the version specified. // Modify the following defines if you have to target a platform prior to the ones specified below.
// Refer to MSDN for the latest info on corresponding values for different platforms.
#ifndef _WIN32_WINNT // Specifies that the minimum required platform is Windows Vista.
#define _WIN32_WINNT 0x0600 // Change this to the appropriate value to target other versions of Windows.
#endif
2. stdafx.h
// stdafx.h : include file for standard system include files,
// or project specific include files that are used frequently, but
// are changed infrequently
// #pragma once #include "targetver.h" #include <stdio.h>
#include <tchar.h> // TODO: reference additional headers your program requires here
3. stdafx.cpp
// stdafx.cpp : source file that includes just the standard includes
// DeleteNodeInList.pch will be the pre-compiled header
// stdafx.obj will contain the pre-compiled type information #include "stdafx.h" // TODO: reference any additional headers you need in STDAFX.H
// and not in this file
4. DeleteNodeInList.cpp
// DeleteNodeInList.cpp : Defines the entry point for the console application.
// // 《剑指Offer――名企面试官精讲典型编程题》代码
// 著作权所有者:何海涛 #include "stdafx.h"
#include "..\Utilities\List.h" 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();
ListNode* pNode2 = CreateListNode();
ListNode* pNode3 = CreateListNode();
ListNode* pNode4 = CreateListNode();
ListNode* pNode5 = CreateListNode(); ConnectListNodes(pNode1, pNode2);
ConnectListNodes(pNode2, pNode3);
ConnectListNodes(pNode3, pNode4);
ConnectListNodes(pNode4, pNode5); Test(pNode1, pNode3); DestroyList(pNode1);
} // 链表中有多个结点,删除尾结点
void Test2()
{
ListNode* pNode1 = CreateListNode();
ListNode* pNode2 = CreateListNode();
ListNode* pNode3 = CreateListNode();
ListNode* pNode4 = CreateListNode();
ListNode* pNode5 = CreateListNode(); ConnectListNodes(pNode1, pNode2);
ConnectListNodes(pNode2, pNode3);
ConnectListNodes(pNode3, pNode4);
ConnectListNodes(pNode4, pNode5); Test(pNode1, pNode5); DestroyList(pNode1);
} // 链表中有多个结点,删除头结点
void Test3()
{
ListNode* pNode1 = CreateListNode();
ListNode* pNode2 = CreateListNode();
ListNode* pNode3 = CreateListNode();
ListNode* pNode4 = CreateListNode();
ListNode* pNode5 = CreateListNode(); ConnectListNodes(pNode1, pNode2);
ConnectListNodes(pNode2, pNode3);
ConnectListNodes(pNode3, pNode4);
ConnectListNodes(pNode4, pNode5); Test(pNode1, pNode1); DestroyList(pNode1);
} // 链表中只有一个结点,删除头结点
void Test4()
{
ListNode* pNode1 = CreateListNode(); Test(pNode1, pNode1);
} // 链表为空
void Test5()
{
Test(NULL, NULL);
} int _tmain(int argc, _TCHAR* argv[])
{
Test1();
Test2();
Test3();
Test4();
Test5(); return ;
}
5. ../Utilities/List.h
// 《剑指Offer――名企面试官精讲典型编程题》代码
// 著作权所有者:何海涛 struct ListNode
{
int m_nValue;
ListNode* m_pNext;
}; __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);
6. ../Utilities/List.cpp
// Utilities.cpp : Defines the exported functions for the DLL application.
// // 《剑指Offer――名企面试官精讲典型编程题》代码
// 著作权所有者:何海涛 #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();
} 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;
}
}
7. 参考代码下载
项目 13_DeleteNodeInList 下载: 百度网盘
何海涛《剑指Offer:名企面试官精讲典型编程题》 所有参考代码下载:百度网盘
参考资料
[1] 何海涛. 剑指 Offer:名企面试官精讲典型编程题 [M]. 北京:电子工业出版社,2012. 99-102.
在 O(1) 时间删除链表结点(C 和 Python 实现)的更多相关文章
- 剑指Offer面试题:12.在O(1)时间删除链表结点
一.题目:在O(1)时间删除链表结点 题目:给定单向链表的头指针和一个结点指针,定义一个函数在O(1)时间删除该结点. 原文采用的是C/C++,这里采用C#,节点定义如下: public class ...
- 【面试题013】在O(1)时间删除链表结点
[面试题013]在O(1)时间删除链表结点 我们要删除结点i,我们可以把结点i的下一个结点j的内容复制到结点i,然后呢把结点i的指针指向结点j的下一个结点.然后在删除结点j. 1.如果结点i位于链表 ...
- 【Java】 剑指offer(17) 在O(1)时间删除链表结点
本文参考自<剑指offer>一书,代码采用Java语言. 更多:<剑指Offer>Java实现合集 题目 给定单向链表的头指针和一个结点指针,定义一个函数在O(1)时间删除 ...
- 《剑指offer》第十八题(在O(1)时间删除链表结点)
// 面试题18(一):在O(1)时间删除链表结点 // 题目:给定单向链表的头指针和一个结点指针,定义一个函数在O(1)时间删除该 // 结点. #include <iostream> ...
- 面试题18(一):在O(1)时间删除链表结点
// 面试题18(一):在O(1)时间删除链表结点 // 题目:给定单向链表的头指针和一个结点指针,定义一个函数在O(1)时间删除该 // 结点.链表结点与函数的定义如下: // struct Lis ...
- 第18题:在O(1)时间删除链表结点+删除链表中重复的节点
题目描述:题目描述在O(1)时间删除链表结点 给定单向链表的头指针和一个结点指针,定义一个函数在O(1)时间删除该结点. 考查创新编程能力. 思路: 1.如果从头到尾遍历,时间O(n) 2.如果将待删 ...
- 33.在O(1)时间删除链表结点[DeleteListNode]
[题目] 给定链表的头指针和一个结点指针,在O(1)时间删除该结点.链表结点的定义如下: C++ Code 123456 struct ListNode { int m_ ...
- 剑指Offer:面试题13——在O(1)时间删除链表结点
问题描述: 给定单向链表的头指针和一个结点指针,定义一个函数在O(1)时间删除该结点.链表结点与函数的定义如下: public class ListNode{ int value; ListNode ...
- 在O(1)时间删除链表结点
题目:给定单向链表的头指针和一个结点指针,定义一个函数在O(1)时间删除该结点. 链表结点与函数的定义如下: struct ListNode { int m_nValue; ListNode* m_p ...
- P99、面试题13:在o(1)时间删除链表结点
题目:给定单向链表的头指针和一个结点指针,定义一个函数在o(1)时间删除该结点.链表结点与函数的定义如下:struct ListNode{ int m_nValue; List ...
随机推荐
- 一款开源免费的办公套件系统:DzzOffice详细部署
一.系统环境 个人建议centos 7 系统 cat /etc/redhat-release CentOS Linux release (Core) 基本更新 yum update -y 基本优化 关 ...
- Linux下C++开发常用命令
本页面记录本人在Linux下进行C++开发时使用的常用命令,注意这里不包括比如ls,mv等linux命令,这里会持续更新.首先假设你只有一个源程序文件,叫vec.cpp,编译后的可执行程序叫vec(本 ...
- hibernate关联关系的crud2
hibernate关联关系的CRUD操作,解释都在注释里了,讲了fetchType.cascade. User类: package com.oracle.hibernate; import javax ...
- redis 常用的server的命令
- eclipse自定义代码注释
效果如下: 自定义注释的方法:
- FX4300超频4.7GHz
先贴出本人计算机配置: (本人cpu为fx4300,默认频率为3.8GHz) 谨记:超频有风险,很可能烧坏主板.cpu.内存等硬件,特别是增加主板电压时一定一次增加0.025V,不要一次增加太多,并且 ...
- js if和switch,==和===
今天改插件BoxScroll的时候,因为if里面的条件判断多于两个,于是立马想着改写switch.改到一半,忽然记起来JSHint等代码质量检测工具中的一个要求,用===替换==,不用不可靠的强制转型 ...
- XML的基本概念和Android下的使用
1. XML的基本概念 1. 什么是XML: 1). XML是指可扩展标记语言(eXtensible Markup Language),它是一种标记语言,很类似HTML.它被设计的宗旨是表示数据,而非 ...
- JS Date函数在safari中的问题
问题描述:在做Web的时候,在PC上用Chrome调试成功,但是在safari一测就出现了问题.经过debug发现是日期相关出现问题.查阅一些资料后发现,safari中对于JavaScript的Dat ...
- 解读MySQL的慢日志
完整的慢日志格式一般如下: # Time: :: # User@Host: db_user[db_database] @ localhost [] # Query_time: Rows_examine ...