在 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 0;
}

void CreateLinkedList(ListNode** pHead)
{
    // Create the first node
    *pHead = CreateNode(0);
    
    ListNode *p1, *p2;
    int i = 1;
    p1 = *pHead;
    // Create Linked List 0 -> 1 -> ... -> 5 -> NULL
    for (; i < 6; ++ 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(-1);
        }
    }
    // 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(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;
}

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(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;
    }
}

7. 参考代码下载

项目 13_DeleteNodeInList 下载: 百度网盘

何海涛《剑指Offer:名企面试官精讲典型编程题》 所有参考代码下载:百度网盘

 

参考资料

[1] 何海涛. 剑指 Offer:名企面试官精讲典型编程题 [M]. 北京:电子工业出版社,2012. 99-102.

 

posted @ 2018-03-31 16:20  klchang  阅读(475)  评论(0编辑  收藏  举报