删除单链表中的重复节点(c语言版本)
生活随笔
收集整理的這篇文章主要介紹了
删除单链表中的重复节点(c语言版本)
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
這是一道經典的面試題,下面是我的研究和舉一反三,特整理如下:
分為三種情形:
(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语言版本)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 使用最小堆使用优先级队列(c语言版本)
- 下一篇: 快速排序的两种实现方法(c语言版本)