问题说明:
因为是删除的是单链表中的重复元素,这和删除单链表中值为x的元素有区别。例如给出一组数:1 2 2 3。前者得到的结果是1 2 3;后者删除2,得到1 3。
算法思想:
本算法适用于事先所得到的单链表中元素是有序的,即元素值按大小排好序的。假设任意输入一组数,通过单链表的有序插入生成单链表,这样,重复的元素必然相邻。只要链表不为空,首结点肯定是不会动的。定义3个结点指针:ppre、pcur、pdel,pcur作为活动指针,初始指向第二个结点,而ppre初始指向pcur的前驱结点,pdel用于指向待删除的结点。用一个循环控制ppre和pcur向后偏移,当ppre指向的结点值和pcur指向的结点值相等,那么ppre的指向的位置保持不动,将pcur的指向先传递给pdel,然后pcur向后移一个位置,再将ppre指向的结点作为pcur指向的结点的前驱,这两个结点再次保持相邻,最后释放pdel指向结点的空间,达到删除的效果,如果后面还有和ppre指向的结点值相同的结点,便通过循环重复执行这些操作。循环执行的过程中ppre就相当于一个基准,不会发生移动。只有当ppre指向的结点值和此时pcur指向的结点值不相等时,ppre才会向后偏移一个位置,而pcur就指向ppre指向位置的下一个位置。
可以通过如下图理解ppre和pcur指向位置的轨迹变化:
案例效果:
链表中各结点之间的连接关系变化:
删除链表中的重复元素之后
完整代码如下:
#include<stdio.h>
#include<stdlib.h>
typedef struct linklist {
int data;
struct linklist *next;
}list,*plist;
void list_create(plist *pphead, plist *pptail, int key)
{
plist pnew = (plist)calloc(1, sizeof(list));
pnew->data = key;
if (NULL == *pphead)
{
*pphead = pnew;
*pptail = pnew;
}
else if (pnew->data <= (*pphead)->data)
{
pnew->next = *pphead;
*pphead = pnew;
}
else if (pnew->data > (*pphead)->data && pnew->data <= (*pptail)->data)
{
plist ppre=*pphead, pcur = (*pphead)->next;
while (pcur)
{
if (pnew->data > ppre->data && pnew->data <= pcur->data)
{
pnew->next = pcur;
ppre->next = pnew;
}
ppre = pcur;
pcur = pcur->next;
}
}
else {
(*pptail)->next = pnew;
*pptail = pnew;
}
}
void del_node(plist *pphead)
{
if (NULL == *pphead)
printf("linklist is empty\n");
else {
plist ppre=*pphead, pcur = (*pphead)->next,pdel;
while (pcur)
{
if (ppre->data == pcur->data)
{
pdel = pcur;
pcur = pdel->next;
ppre->next = pcur;
free(pdel);
}
else {
ppre = ppre->next;
pcur = ppre->next;
}
}
}
}
void list_print(plist phead)
{
while (phead)
{
printf("%d ", phead->data);
phead = phead->next;
}
printf("\n");
}
int main()
{
plist phead = NULL, ptail = NULL;
int key;
while (scanf("%d", &key) != EOF)
{
list_create(&phead, &ptail, key);
}
del_node(&phead, &ptail);
list_print(phead);
system("pause");
}