<剑指OFFER18> 18_02_DeleteDuplicationNode删除重复节点
/******************************************************************* Copyright(c) 2016, Harry He All rights reserved. Distributed under the BSD license. (See accompanying file LICENSE.txt at https://github.com/zhedahht/CodingInterviewChinese2/blob/master/LICENSE.txt) *******************************************************************/ //================================================================== // 《剑指Offer——名企面试官精讲典型编程题》代码 // 作者:何海涛 //================================================================== // 面试题18(二):删除链表中重复的结点 // 题目:在一个排序的链表中,如何删除重复的结点?例如,在图3.4(a)中重复 // 结点被删除之后,链表如图3.4(b)所示。 #include <cstdio> #include "list.h" void DeleteDuplicationAnswer(ListNode** pHead) { if (pHead == nullptr || *pHead == nullptr) return; ListNode* pPreNode = nullptr; ListNode* pNode = *pHead; while (pNode != nullptr) { ListNode *pNext = pNode->m_pNext; bool needDelete = false; if (pNext != nullptr && pNext->m_nValue == pNode->m_nValue) needDelete = true; if (!needDelete) { pPreNode = pNode; pNode = pNode->m_pNext; } else { int value = pNode->m_nValue; ListNode* pToBeDel = pNode; while (pToBeDel != nullptr && pToBeDel->m_nValue == value) { pNext = pToBeDel->m_pNext; delete pToBeDel; pToBeDel = nullptr; pToBeDel = pNext; } if (pPreNode == nullptr) *pHead = pNext; else pPreNode->m_pNext = pNext; pNode = pNext; } } } /* 1. 头节点可能与后面的节点重复,头节点可能被删除 2. 从头遍历整个链表,如果当前节点和下一节点的值相同,删除 3. 删除后,当前节点的前一节点和后面值比当前节点的值大的节点相连 如果头节点为空,返回 while(当前节点不为空) { 如果找到重复节点,删除标志位为真 删除标志为假 { 循环下一节点 } 删除标志为真: { 删掉连续的重复节点,判断是不是空,防止到了尾节点 如果是从头节点开始重复了,重置新的头节点指针 如果不是的话,前一节点连接不重复的节点 重置当前节点的位置为pNext } } */ void DeleteDuplication(ListNode** pHead) { if (pHead == nullptr || *pHead == nullptr) return; /*如果头节点不是重复节点,那么链表的头节点地址是不变的,中间内容会变*/ /*pNode的作用是遍历链表,减掉重复节点,再把断的链表连起来*/ /*只要有了链表头节点地址就可以遍历整个链表,有了地址就可以访问和修改里面内容,这样是有效的*/ ListNode *pNode = *pHead; ListNode *pPre = nullptr; /*终止条件是pNode==nullptr,这时*pHead并没有变!!!*/ while (pNode != nullptr) { bool need_delete = false; ListNode *pNext = pNode->m_pNext; // need delete conditions if (pNode->m_pNext != nullptr &&/* 如果只有一个节点,则下一节点没有值*/ pNode->m_nValue == pNext->m_nValue) need_delete = true; // do not need delete if (!need_delete) { pPre = pNode; pNode = pNode->m_pNext; } else//need delete { ListNode *pToBeDeleted = pNode; int value = pNode->m_nValue; while (pToBeDeleted != nullptr && pToBeDeleted->m_nValue == value) { pNext = pToBeDeleted->m_pNext; delete pToBeDeleted; pToBeDeleted = nullptr; //?? pToBeDeleted = pNext; } /*从头节点开始就重复了,还没来得及给Pre节点赋值*/ if (pPre == nullptr) *pHead = pToBeDeleted;//重置头节点指针,不能使用pNode,作为null退出 else { pPre->m_pNext = pToBeDeleted; } /* 1 1 1 4 5 5*/ pNode = pToBeDeleted; /* pNode和pHead作用是不同的,pHead记录头节点地址,有它可以访问所有节点 pNode相当于迭代器,遍历每一节点,执行删除和连接操作。 */ } } } // ====================测试代码==================== void Test(char* testName, ListNode** pHead, int* expectedValues, int expectedLength) { if (testName != nullptr) printf("%s begins: ", testName); DeleteDuplication(pHead); int index = 0; ListNode* pNode = *pHead; while (pNode != nullptr && index < expectedLength) { if (pNode->m_nValue != expectedValues[index]) break; pNode = pNode->m_pNext; index++; } if (pNode == nullptr && index == expectedLength) printf("Passed.\n"); else printf("FAILED.\n"); } // 某些结点是重复的 void Test1() { ListNode* pNode1 = CreateListNode(1); ListNode* pNode2 = CreateListNode(2); ListNode* pNode3 = CreateListNode(3); ListNode* pNode4 = CreateListNode(3); ListNode* pNode5 = CreateListNode(4); ListNode* pNode6 = CreateListNode(4); ListNode* pNode7 = CreateListNode(5); ConnectListNodes(pNode1, pNode2); ConnectListNodes(pNode2, pNode3); ConnectListNodes(pNode3, pNode4); ConnectListNodes(pNode4, pNode5); ConnectListNodes(pNode5, pNode6); ConnectListNodes(pNode6, pNode7); ListNode* pHead = pNode1; int expectedValues[] = { 1, 2, 5 }; Test("Test1", &pHead, expectedValues, sizeof(expectedValues) / sizeof(int)); DestroyList(pHead); } // 没有重复的结点 void Test2() { ListNode* pNode1 = CreateListNode(1); ListNode* pNode2 = CreateListNode(2); ListNode* pNode3 = CreateListNode(3); ListNode* pNode4 = CreateListNode(4); ListNode* pNode5 = CreateListNode(5); ListNode* pNode6 = CreateListNode(6); ListNode* pNode7 = CreateListNode(7); ConnectListNodes(pNode1, pNode2); ConnectListNodes(pNode2, pNode3); ConnectListNodes(pNode3, pNode4); ConnectListNodes(pNode4, pNode5); ConnectListNodes(pNode5, pNode6); ConnectListNodes(pNode6, pNode7); ListNode* pHead = pNode1; int expectedValues[] = { 1, 2, 3, 4, 5, 6, 7 }; Test("Test2", &pHead, expectedValues, sizeof(expectedValues) / sizeof(int)); DestroyList(pHead); } // 除了一个结点之外其他所有结点的值都相同 void Test3() { ListNode* pNode1 = CreateListNode(1); ListNode* pNode2 = CreateListNode(1); ListNode* pNode3 = CreateListNode(1); ListNode* pNode4 = CreateListNode(1); ListNode* pNode5 = CreateListNode(1); ListNode* pNode6 = CreateListNode(1); ListNode* pNode7 = CreateListNode(2); ConnectListNodes(pNode1, pNode2); ConnectListNodes(pNode2, pNode3); ConnectListNodes(pNode3, pNode4); ConnectListNodes(pNode4, pNode5); ConnectListNodes(pNode5, pNode6); ConnectListNodes(pNode6, pNode7); ListNode* pHead = pNode1; int expectedValues[] = { 2 }; Test("Test3", &pHead, expectedValues, sizeof(expectedValues) / sizeof(int)); DestroyList(pHead); } // 所有结点的值都相同 void Test4() { ListNode* pNode1 = CreateListNode(1); ListNode* pNode2 = CreateListNode(1); ListNode* pNode3 = CreateListNode(1); ListNode* pNode4 = CreateListNode(1); ListNode* pNode5 = CreateListNode(1); ListNode* pNode6 = CreateListNode(1); ListNode* pNode7 = CreateListNode(1); ConnectListNodes(pNode1, pNode2); ConnectListNodes(pNode2, pNode3); ConnectListNodes(pNode3, pNode4); ConnectListNodes(pNode4, pNode5); ConnectListNodes(pNode5, pNode6); ConnectListNodes(pNode6, pNode7); ListNode* pHead = pNode1; Test("Test4", &pHead, nullptr, 0); DestroyList(pHead); } // 所有结点都成对出现 void Test5() { ListNode* pNode1 = CreateListNode(1); ListNode* pNode2 = CreateListNode(1); ListNode* pNode3 = CreateListNode(2); ListNode* pNode4 = CreateListNode(2); ListNode* pNode5 = CreateListNode(3); ListNode* pNode6 = CreateListNode(3); ListNode* pNode7 = CreateListNode(4); ListNode* pNode8 = CreateListNode(4); ConnectListNodes(pNode1, pNode2); ConnectListNodes(pNode2, pNode3); ConnectListNodes(pNode3, pNode4); ConnectListNodes(pNode4, pNode5); ConnectListNodes(pNode5, pNode6); ConnectListNodes(pNode6, pNode7); ConnectListNodes(pNode7, pNode8); ListNode* pHead = pNode1; Test("Test5", &pHead, nullptr, 0); DestroyList(pHead); } // 除了两个结点之外其他结点都成对出现 void Test6() { ListNode* pNode1 = CreateListNode(1); ListNode* pNode2 = CreateListNode(1); ListNode* pNode3 = CreateListNode(2); ListNode* pNode4 = CreateListNode(3); ListNode* pNode5 = CreateListNode(3); ListNode* pNode6 = CreateListNode(4); ListNode* pNode7 = CreateListNode(5); ListNode* pNode8 = CreateListNode(5); ConnectListNodes(pNode1, pNode2); ConnectListNodes(pNode2, pNode3); ConnectListNodes(pNode3, pNode4); ConnectListNodes(pNode4, pNode5); ConnectListNodes(pNode5, pNode6); ConnectListNodes(pNode6, pNode7); ConnectListNodes(pNode7, pNode8); ListNode* pHead = pNode1; int expectedValues[] = { 2, 4 }; Test("Test6", &pHead, expectedValues, sizeof(expectedValues) / sizeof(int)); DestroyList(pHead); } // 链表中只有两个不重复的结点 void Test7() { ListNode* pNode1 = CreateListNode(1); ListNode* pNode2 = CreateListNode(2); ConnectListNodes(pNode1, pNode2); ListNode* pHead = pNode1; int expectedValues[] = { 1, 2 }; Test("Test7", &pHead, expectedValues, sizeof(expectedValues) / sizeof(int)); DestroyList(pHead); } // 结点中只有一个结点 void Test8() { ListNode* pNode1 = CreateListNode(1); ConnectListNodes(pNode1, nullptr); ListNode* pHead = pNode1; int expectedValues[] = { 1 }; Test("Test8", &pHead, expectedValues, sizeof(expectedValues) / sizeof(int)); DestroyList(pHead); } // 结点中只有两个重复的结点 void Test9() { ListNode* pNode1 = CreateListNode(1); ListNode* pNode2 = CreateListNode(1); ConnectListNodes(pNode1, pNode2); ListNode* pHead = pNode1; Test("Test9", &pHead, nullptr, 0); DestroyList(pHead); } // 空链表 void Test10() { ListNode* pHead = nullptr; Test("Test10", &pHead, nullptr, 0); } int main(int argc, char* argv[]) { //Test1(); Test2(); //Test3(); //Test4(); //Test5(); //Test6(); //Test7(); //Test8(); //Test9(); //Test10(); return 0; }
/******************************************************************* Copyright(c) 2016, Harry He All rights reserved. Distributed under the BSD license. (See accompanying file LICENSE.txt at https://github.com/zhedahht/CodingInterviewChinese2/blob/master/LICENSE.txt) *******************************************************************/ //================================================================== // 《剑指Offer——名企面试官精讲典型编程题》代码 // 作者:何海涛 //================================================================== #include "list.h" #include <stdio.h> #include <stdlib.h> ListNode* CreateListNode(int value) { ListNode* pNode = new ListNode(); pNode->m_nValue = value; pNode->m_pNext = nullptr; return pNode; } void ConnectListNodes(ListNode* pCurrent, ListNode* pNext) { if(pCurrent == nullptr) { printf("Error to connect two nodes.\n"); exit(1); } pCurrent->m_pNext = pNext; } void PrintListNode(ListNode* pNode) { if(pNode == nullptr) { printf("The node is nullptr\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 != nullptr) { printf("%d\t", pNode->m_nValue); pNode = pNode->m_pNext; } printf("\nPrintList ends.\n"); } void DestroyList(ListNode* pHead) { ListNode* pNode = pHead; while(pNode != nullptr) { 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 = nullptr; if(*pHead == nullptr) { *pHead = pNew; } else { ListNode* pNode = *pHead; while(pNode->m_pNext != nullptr) pNode = pNode->m_pNext; pNode->m_pNext = pNew; } } void RemoveNode(ListNode** pHead, int value) { if(pHead == nullptr || *pHead == nullptr) return; ListNode* pToBeDeleted = nullptr; if((*pHead)->m_nValue == value) { pToBeDeleted = *pHead; *pHead = (*pHead)->m_pNext; } else { ListNode* pNode = *pHead; while(pNode->m_pNext != nullptr && pNode->m_pNext->m_nValue != value) pNode = pNode->m_pNext; if(pNode->m_pNext != nullptr && pNode->m_pNext->m_nValue == value) { pToBeDeleted = pNode->m_pNext; pNode->m_pNext = pNode->m_pNext->m_pNext; } } if(pToBeDeleted != nullptr) { delete pToBeDeleted; pToBeDeleted = nullptr; } }
/******************************************************************* Copyright(c) 2016, Harry He All rights reserved. Distributed under the BSD license. (See accompanying file LICENSE.txt at https://github.com/zhedahht/CodingInterviewChinese2/blob/master/LICENSE.txt) *******************************************************************/ //================================================================== // 《剑指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);
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix