这是一道经典的面试题,下面是我的研究和举一反三,特整理如下:

分为三种情形:

(1)删除有序链表的重复节点,重复节点一个都不留

(2)删除有序链表的重复节点,重复节点只留一个

(3)删除无序链表的重复节点,重复节点只留一个

下面是相关节点的定义:

typedef struct ListNode {int val;struct ListNode *next;
} ListNode;ListNode* CreateListNode(int value){ListNode* pNode = (ListNode*)malloc(sizeof(ListNode));if(pNode == NULL){printf("failed to create ListNode\n");exit(-1);}   pNode->val = value;pNode->next = NULL;return pNode;
}      void ConnectListNodes(ListNode* pCurrent, ListNode* pNext){if(pCurrent == NULL || pNext == NULL){printf("Error to connect two nodes\n");exit(-1);}   pCurrent->next = pNext;
}      void DestroyListNode(ListNode* pNode){if(pNode != NULL){printf("-----delete list node [%d]-----\n", pNode->val);free(pNode);}
}

下面给出解读:

1)下面的代码针对删除有序单链表的重复节点,重复节点都删除。

ListNode* DeleteDuplication(ListNode* pHead){if(pHead == NULL)return NULL;//指向当前节点前最晚访问过的不重复节点;                                    ListNode* pPre = NULL;//指向当前正在处理的节点;ListNode* pCur = pHead;//指向当前节点后面的值相同的节点;ListNode* pNext = NULL;//临时存放节点ListNode* pNode = NULL;while(pCur != NULL){//下一个节点与当前节点相同if(pCur->next != NULL && pCur->next->val == pCur->val){pNext = pCur->next;//找出所有与当前节点值相同的节点,因为是排序过的链表,它们都在一切while(pNext != NULL && pNext->val == pCur->val){pNode = pNext->next;DestroyListNode(pNext);pNext = pNode;}   //现在要删除pCur->...->pNext->之间的所有节点//如果pCur是链表头,要更新链表头的位置if(pCur == pHead)pHead = pNext;elsepPre->next = pNext;DestroyListNode(pCur);pCur = pNext;}else{pPre = pCur;pCur = pCur->next;}   }   return pHead;
}

2)下面是有序链表,只留一个的

//删除有序链表中的重复节点,仅保留一个,使用2指针定位
//考虑到这里需要用到测试框架,这里必须使用二级指针
ListNode* DeleteDuplication(ListNode** pHead){if(pHead == NULL || *pHead == NULL)                                         return NULL;//指向当前正在处理的节点;ListNode* pCur = *pHead;//指向当前节点后面的值相同的节点;ListNode* pNext = NULL;//临时存放节点ListNode* pNode = NULL;while(pCur != NULL){//下一个节点与当前节点相同if(pCur->next != NULL && pCur->next->val == pCur->val){pNext = pCur->next;//找出所有与当前节点值相同的节点,因为是排序过的链表,它们都在一切while(pNext != NULL && pNext->val == pCur->val){pNode = pNext->next;DestroyListNode(pNext);pNext = pNode;}   //将当前节点指向后续不同值的首个节点pCur->next = pNext;}   pCur = pCur->next;}       return *pHead;
} 

3)下面是无序链表,只留一个的(注意:这里会兼容前面的两种情况)

//删除无序链表中的重复节点,仅保留一个,使用2指针定位
//考虑到这里需要用到测试框架,这里必须使用二级指针
ListNode* DeleteDuplication(ListNode** pHead){if(pHead == NULL || *pHead == NULL)return NULL; //指向当前正在处理的节点;ListNode* p = *pHead;//用于遍历p之后的节点;ListNode* q = NULL;ListNode* r = NULL;while(p != NULL){q = p;       //若后面有节点与当前节点相同,将其统统删除while(q->next != NULL){if(q->next->val == p->val){//保存需要删掉的节点r = q->next;//需要删掉的节点的前后节点相接q->next = r->next;DestroyListNode(r);}        else{    q = q->next;}        }            p = p->next; }                return *pHead;
}

下面是针对第三种情况编写的用户测试的截图:

下面是我针对第三种情况的源码实现:

//description: 描述删除单链表的重复节点
//date: 2019-03-20#include <stdio.h>
#include <stdlib.h>
#include <time.h>typedef struct ListNode {int val;struct ListNode *next;
} ListNode;ListNode* CreateListNode(int value){ListNode* pNode = (ListNode*)malloc(sizeof(ListNode));if(pNode == NULL){printf("failed to create ListNode\n");exit(-1);}pNode->val = value;pNode->next = NULL;return pNode;
}void ConnectListNodes(ListNode* pCurrent, ListNode* pNext){if(pCurrent == NULL || pNext == NULL){printf("No necessary to connect two nodes\n");return;}pCurrent->next = pNext;
}void DestroyListNode(ListNode* pNode){if(pNode != NULL){printf("-----delete list node [%d]-----\n", pNode->val);free(pNode);}
}//在表头节点后面拼接n个随机元素的单链表
void CreateList(ListNode **pHead, int n){if(pHead == NULL || *pHead == NULL){printf("Invalid List header ptr\n");exit(-1);}int i;srand(time(0));ListNode *pNew, *pNode = *pHead;for(i=0; i<n; i++){pNew = (ListNode*)malloc(sizeof(ListNode));pNew->val = rand()%100 + 1;pNode->next = pNew;pNode = pNew;}pNode->next = NULL;
}void DestroyList(ListNode* pHead){ListNode *pNode = pHead;while(pNode != NULL){pHead = pNode->next;free(pNode);pNode = pHead;}
}void PrintList(ListNode* pHead){printf("------------print list begin-----------");ListNode* pNode = pHead;while(pNode != NULL){printf("%d ", pNode->val);pNode = pNode->next;}printf("------------print list end-------------");
}//==================业务函数定义到这里=============//删除无序链表中的重复节点,仅保留一个,使用2指针定位
//考虑到这里需要用到测试框架,这里必须使用二级指针
ListNode* DeleteDuplication(ListNode** pHead){if(pHead == NULL || *pHead == NULL)return NULL;//指向当前正在处理的节点;ListNode* p = *pHead;//用于遍历p之后的节点;ListNode* q = NULL;ListNode* r = NULL;while(p != NULL){q = p;//若后面有节点与当前节点相同,将其统统删除while(q->next != NULL){if(q->next->val == p->val){//保存需要删掉的节点r = q->next;//需要删掉的节点的前后节点相接q->next = r->next;DestroyListNode(r);}else{q = q->next;}}p = p->next;}return *pHead;
}//==================测试代码=======================
void Test(char* testName, ListNode** pHead, int* expectedValues, int expectedLength){if(testName != NULL)printf("%s begins:\n", testName);//======真正要干的活儿==========ListNode* pNode = DeleteDuplication(pHead);int idx = 0;while(pNode !=NULL && idx < expectedLength){if(pNode->val != expectedValues[idx])break;pNode = pNode->next;idx++;}if(pNode == NULL && idx == expectedLength)printf("%s Passed.\n", testName);elseprintf("%s FAILED.\n", testName);
}//--------------下面测试一些特例情况--------------//某些节点是重复的
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, 3, 4, 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[] = {1, 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;int expectedValues[] = {1};Test("Test4", &pHead, expectedValues, sizeof(expectedValues) / sizeof(int));//删除该单链表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;int expectedValues[] = {1, 2, 3, 4};Test("Test5", &pHead, expectedValues, sizeof(expectedValues) / sizeof(int));//删除该单链表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[] = {1, 2, 3, 4, 5};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);ListNode* pNode2 = CreateListNode(1);ConnectListNodes(pNode1, pNode2);ListNode* pHead = pNode1;int expectedValues[] = {1};Test("Test8", &pHead, expectedValues, sizeof(expectedValues) / sizeof(int));//删除该单链表DestroyList(pHead);
}//无序链表中某些节点是重复的
void Test9(){//构建一个单链表ListNode* pNode1 = CreateListNode(11);ListNode* pNode2 = CreateListNode(2);ListNode* pNode3 = CreateListNode(3);ListNode* pNode4 = CreateListNode(3);ListNode* pNode5 = CreateListNode(9);ListNode* pNode6 = CreateListNode(4);ListNode* pNode7 = CreateListNode(5);ListNode* pNode8 = CreateListNode(5);ListNode* pNode9 = CreateListNode(3);ConnectListNodes(pNode1, pNode2);ConnectListNodes(pNode2, pNode3);ConnectListNodes(pNode3, pNode4);ConnectListNodes(pNode4, pNode5);ConnectListNodes(pNode5, pNode6);ConnectListNodes(pNode6, pNode7);ConnectListNodes(pNode7, pNode8);ConnectListNodes(pNode8, pNode9);ListNode* pHead = pNode1;int expectedValues[] = {11, 2, 3, 9, 4, 5};Test("Test9", &pHead, expectedValues, sizeof(expectedValues) / sizeof(int));//删除该单链表DestroyList(pHead);
}//链表中只有一个节点
void Test10(){//构建一个单链表ListNode* pNode1 = CreateListNode(1);ConnectListNodes(pNode1, NULL);ListNode* pHead = pNode1;int expectedValues[] = {1};Test("Test10", &pHead, expectedValues, sizeof(expectedValues) / sizeof(int));//删除该单链表DestroyList(pHead);
}//空链表
void Test11(){ListNode* pHead = NULL;Test("Test11", &pHead, NULL, 0);
}int main(int argc, char** argv){Test1();Test2();Test3();Test4();Test5();Test6();Test7();Test8();Test9();Test10();Test11();return 0;
}

参考文献

[1].《剑指Offer名企面试官精讲典型编程题》第2版 面试题18-2

删除单链表中的重复节点(c语言版本)相关推荐

  1. 删除单链表中的重复节点

    删除单链表中的重复节点 一.题目描述 已知单链表L,写一算法,删除其中的重复节点.(更好的阅读体验,请访问程序员在旅途) 二.分析解答 2.1 知识点分析 本题主要考察链表的相关知识点,其中包括:单链 ...

  2. 用O(1)的时间复杂度删除单链表中的某个节点

    用O(1)的时间复杂度删除单链表中的某个节点 给定链表的头指针和一个结点指针,在O(1)时间删除该结点.链表结点的定义如下: struct ListNode {int m_nKey;ListNode* ...

  3. 老虎-删除排序链表中的重复节点

    题目:1-1-2-3-3-3-4-5-6-6-7,删除重复节点后返回2-4-5-7 def delete_duplicate(head):h = Node(0)k = hp = headif not ...

  4. python 链表倒数第k个节点_链表-删除单链表中倒数第k个节点

    题目 实现一个函数,一个可以删除单链表中倒数第k个节点 难度 简单 分析 本题比较简单,实现方法多种多样,这里提供一种方法 首先明确一点,在单链表中删除倒数第k个节点,需要找到他的前一个节点,让前一个 ...

  5. Leetcode 129求根节点到叶节点数字之和、104二叉树的最大深度、8字符串转换整数(atoi)、82删除排序链表中的重复元素II、204二分查找、94二叉树的中序遍历、144二叉树的前序遍历

    Top1:Leetcode 129求根节点到叶节点数字之和 官方题解:https://leetcode.cn/problems/sum-root-to-leaf-numbers/solution/qi ...

  6. 算法---删除排序链表中的重复元素 II

    删除排序链表中的重复元素 II 给定一个已排序的链表的头 head , 删除原始链表中所有重复数字的节点,只留下不同的数字 .返回 已排序的链表 . 输入:head = [1,2,3,3,4,4,5] ...

  7. 删除有序链表中的重复结点

    一,问题描述 请自己构造一个简单的有序单链表,然后实现删除链表中的重复结点.比如: 二,问题分析 首先要实现一个单链表,因此需要定义一个节点类Node.其次,实现向链表中添加结点的方法(使用尾插法)a ...

  8. 编写代码,移除未排序的链表中的重复节点

    2019独角兽企业重金招聘Python工程师标准>>> 解法一:如果不得使用临时缓冲区,该怎么解决? 要想移除链表中的重复节点,我们需要设法记录有哪些是重复的.这里只需要使用到一个简 ...

  9. leetcode 82. 删除排序链表中的重复元素 II

    难度:中等 频次:77 题目:给定一个已排序的链表的头 head , 删除原始链表中所有重复数字的节点,只留下不同的数字 .返回 已排序的链表 . 解题思路: 一次遍历 注意: 遍历时候,如果两个节点 ...

最新文章

  1. C#编程(五十三)----------字典DictionaryTKey,TValue
  2. 项目经理的超越(三)人际优先,做事上的超越
  3. SQL Server -- LIKE模糊查询
  4. android 之图文混排+GridView
  5. 1143 Lowest Common Ancestor (30 分)【难度: 中 / 知识点: 最低公共祖先 未完成】
  6. vc 消息与事件的区别
  7. Daily Scrum 2012/12/09
  8. 十月多媒体技术人聚会北京 LiveVideoStackCon 2018开启讲师/出品人招募
  9. 【刘汝佳可运行代码】Ordering Tasks UVA - 10305【两种解法】
  10. Spring框架中的单例Bean是线程安全的吗
  11. 【操作系统】请求调页
  12. 10 步让你成为更优秀的Coder
  13. Python nii文件转成tiff文件 以及遍历文件夹
  14. win7 下点击鼠标右键无法新建文件夹
  15. 微信小程序开发者工具获取不到坐标
  16. 爬虫笔记37:android控件ListView的讲解、fiddler的安装与使用、fiddler和模拟器的配合使用、案例(爬取豆果美食app)
  17. 在JavaScript中NaN为什么不等于NaN
  18. javaSE 打印流,PrintWriter,PrintStream。 打印到输出流(文件)中
  19. python 画老虎
  20. 使用 vue 开发 APICloud 应用的教程

热门文章

  1. oracle数据库 export,转:Oracle数据库的备份方法——使用export作为备份
  2. C++ 笔记(09)— 字符串(C 风格字符串、C++字符串 string)
  3. RabbitMQ 入门系列(10)— RabbitMQ 消息持久化、不丢失消息
  4. 第五届合肥工业大学宣城校区程序设计大赛题解
  5. 分享ShareSDK
  6. python-day05正则表达式
  7. 【原创】Cookie应用(二)
  8. UNICODE使用的一些知识和技巧
  9. usaco Riding the Fences(欧拉回路模板)
  10. 计算机wrod初级考试题及答案,计算机基础知识+Word基础知识+Excel基础知识试题答案解析.doc...