转载请注明出处:http://blog.csdn.net/ns_code/article/details/21644083
题目:
Write code to remove duplicates from an unsorted linked list.
FOLLOW UP
How would you solve this problem if a temporary buffer is not allowed?
翻译:
写一个程序从一个未排序的链表中移除重复的项
另外,如果不允许使用临时缓存,你如何解决这个问题?
思路:
链表是未排序的,如果链表中的元素都是字符,很明显,跟前面字符串中一些判断或剔除重复字符的思想一样,最好的方法是用哈希思想,开辟一个bool数组来做映射,数组中对应位置的的元素值为true时,如果再次映射到该位置,则将链表中对应的字符删除。时间复杂度为O(n)。
但如果链表中的元素不是字符,而是整数呢,而且如果整数有正有负,或者最大的数很大,这样采取哈希的思想就不行了,有负值的时候可能会导致哈希数组越界,链表中元素的最大值很大的花,又会需要开辟很大的数组空间,而且可能会浪费掉数组中间的很多空间。
如果要进行原地删除呢?使用排序?例如快排、归并排序、堆排序等,这样咋一眼看,时间复杂度O(nlogn),但我们知道对链表来说,每查找一次元素所在的位置就需要遍历整个链表,而这几种比较高效的排序算法每一步排序操作都需要知道对应元素的位置,因此对链表进行这几种排序速度会很慢。
这样看来,如果要进行原地删除的话,只能采用时间复杂度为O(n*n)的方法了,即用两个指针,指针A首先指向第一个元素,指针B遍历后面的元素与A所指的元素比较,遇到与A所指元素相同的就删除,而后指针A后移,再进行同样的比较,直到指针A移到了最后一个位置。
实现代码:
/*
删除链表中重复的元素
*/
void remove(PNODE pHead)
{
if(!pHead)
return ;
PNODE p1;
PNODE p2;
PNODE p2_prior; //指向p2前面的一个元素
for(p1 = pHead->pNext ; p1 ; p1 = p1->pNext)
{
p2 = p1->pNext;
p2_prior = p1;
while(p2)
{
//如果当前元素需要删除,则在删除该元素后,p2需要跳过该元素
if(p1->data == p2->data)
{
p2_prior->pNext = p2->pNext;
free(p2);
p2 = p2_prior->pNext;
}
else
{ //如果当前元素不需要删除,则p2直接向后移动
p2_prior = p2;
p2 = p2->pNext;
}
}
}
}
完整代码(直接用以前写的单链表相关操作):
/***************************************************
题目描述:
不用额外的辅助空间,删除一个未排序的链表中重复的元素
Date:2014-03-20
****************************************************/
/**************************************************************************************************************
以下为操作链表的算法,该链表为单链表。
链表以头指针为索引,头指针指向头节点,头节点指向首节点,以此类推,直到尾节点。
头节点中不存放数据,只存放指向首节点的指针,
设置头节点的目的是为了方便对链表的操作,如果不设置头节点,而是直接由头指针指向首节点,
这样在对头指针后的节点进行插入删除操作时就会与其他节点进行该操作时有所不同,便要作为一种特殊情况来分析
**************************************************************************************************************/
#include<stdio.h>
#include<stdlib.h>
typedef struct Node
{
int data;
struct Node *pNext;
}NODE,*PNODE;
PNODE create_list();
void traverse_list(PNODE);
bool is_empty(PNODE);
int length_list(PNODE);
void sort_list(PNODE);
bool insert_list(PNODE,int,int);
bool delete_list(PNODE,int,int *);
void clear_list(PNODE);
void remove(PNODE);
int main(void)
{
int len;
PNODE pHead = NULL;
//创建链表并遍历输出
pHead = create_list();
traverse_list(pHead);
//求链表长度,并输出
len = length_list(pHead);
if(!is_empty(pHead))
printf("the length of the list is:%d\n",len);
remove(pHead);
printf("After removing duplicate, ");
traverse_list(pHead);
//清空链表,遍历输出(无数据输出)
clear_list(pHead);
printf("After cleared,");
traverse_list(pHead);
return 0;
}
/*
创建一个链表,并返回头指针
*/
PNODE create_list()
{
int val;
PNODE pHead =(PNODE)malloc(sizeof(NODE));
PNODE pCurrent = pHead;
pCurrent->pNext = NULL;
if(NULL == pHead)
{
printf("pHead malloc failed!");
exit(-1);
}
printf("Input first data(q to quit):");
while(scanf("%d",&val)==1)
{
PNODE pNew = (PNODE)malloc(sizeof(NODE));
if(NULL == pNew)
{
printf("pNew malloc failed!");
exit(-1);
}
pNew->data = val;
pCurrent->pNext = pNew;
pNew->pNext = NULL;
pCurrent = pNew;
printf("Input next data(q to quit):");
}
return pHead;
}
/*
遍历链表
*/
void traverse_list(PNODE pHead)
{
PNODE pCurrent = pHead->pNext;
printf("now dataes in the list are:\n");
while(pCurrent != NULL)
{
printf("%d ",pCurrent->data);
pCurrent = pCurrent->pNext;
}
printf("\n");
return ;
}
/*
判断链表是否为空
*/
bool is_empty(PNODE pNode)
{
if(NULL == pNode->pNext)
return true;
else
return false;
}
/*
求链表长度,即节点总数(不计入头节点)
*/
int length_list(PNODE pNode)
{
int count = 0;
PNODE pCurrent = pNode->pNext;
while(pCurrent != NULL)
{
count++;
pCurrent = pCurrent->pNext;
}
return count;
}
/*
选择法对链表排序
*/
void sort_list(PNODE pHead)
{
PNODE p,q;
int temp;
for(p=pHead->pNext;p!=NULL;p=p->pNext)
for(q=p->pNext;q!=NULL;q=q->pNext)
{
if(p->data>q->data)
{
temp = p->data;
p->data = q->data;
q->data = temp;
}
}
return ;
}
/*
在第pos个节点的后面插入一个新的节点,该节点中的数据为val
*/
bool insert_list(PNODE pHead,int pos,int val)
{
int i = 0;
PNODE p = pHead;
//i为0时,p指向第0个节点(这里指没有实际数据的头节点,不计入链表节点总数),
//i为1时,p指向第1个节点,i为几,p就指向第几个节点
while(p!=NULL && i<pos)
{
p = p->pNext;
i++;
}
//当pos的值大于链表长度时,便会出现这种情况
if(i>pos || p==NULL)
return false;
PNODE pNew = (PNODE)malloc(sizeof(NODE));
if(NULL == pNew)
{
printf("pNew malloc failed!");
exit(-1);
}
pNew->data = val;
pNew->pNext = p->pNext;
p->pNext = pNew;
return true;
}
/*
删除第pos个节点,并将删除的数据保存在pData指针所指向的位置
*/
bool delete_list(PNODE pHead,int pos,int *pData)
{
int i = 0;
PNODE p = pHead;
//p最终指向第pos个节点前面的节点
//如果下面两句分别改为while(p!=NULL && i<pos)和if(i>pos || p==NULL),则p最终指向第pos个节点,
//这样因为得不到第pos个节点前面的那个节点,因此无法将pos前后两个节点连结起来
while(p->pNext!=NULL && i<pos-1)
{
p = p->pNext;
i++;
}
//当pos的值大于链表长度时,便会出现这种情况
if(i>pos-1 || p->pNext==NULL)
return false;
PNODE q = p->pNext;
*pData = q->data;
p->pNext = p->pNext->pNext;
free(q);
q = NULL;
return true;
}
/*
清空链表,即使链表只剩下头节点(头节点中没有数据)
*/
void clear_list(PNODE pHead)
{
PNODE p = pHead->pNext;
PNODE r = NULL;
while(p != NULL)
{
r = p->pNext;
free(p);
p = r;
}
pHead->pNext = NULL;
return ;
}
/*
删除链表中重复的元素
*/
void remove(PNODE pHead)
{
if(!pHead)
return ;
PNODE p1;
PNODE p2;
PNODE p2_prior; //指向p2前面的一个元素
for(p1 = pHead->pNext ; p1 ; p1 = p1->pNext)
{
p2 = p1->pNext;
p2_prior = p1;
while(p2)
{
//如果当前元素需要删除,则在删除该元素后,p2需要跳过该元素
if(p1->data == p2->data)
{
p2_prior->pNext = p2->pNext;
free(p2);
p2 = p2_prior->pNext;
}
else
{ //如果当前元素不需要删除,则p2直接向后移动
p2_prior = p2;
p2 = p2->pNext;
}
}
}
}
测试结果: