【双向链表练习】 - 删除链表重复结点

题目描述

描述
在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。
例如:链表1->2->3->3->4->4->5处理后为1->2->5


解题分析:

思路:
定义一前cur一后next指针,用于判断指针的内容是否相等。
用一个prev指针保存cur的前一个结点的地址,用于链接next。
如果cur和next不相同,三个指针往后移动,如果cur和next相同,则只移动next,cur和next仍相同继续只移动next,直到不相同,free掉重复的结点。

常规情况:1 -> 2 -> 3 -> 3 -> 4 -> 4 -> 5 -> NULL
在这里插入图片描述
代码实现:

struct ListNode
{
    
    
	int val;
	struct ListNode* next;
};
struct ListNode* deleteDuplication(struct ListNode* pHead)
{
    
    
	//当为空链表或者只要一个结点的链表时,直接返回
	if (pHead == NULL || pHead->next == NULL)
		return pHead;

	struct ListNode* prev = NULL;//保存cur前一个结点方便链接next
	struct ListNode* cur = pHead;
	struct ListNode* next = pHead->next;//cur和next比较

	while (next != NULL)
	{
    
    
		//cur和next不相同,三个指针往后移动
		if (cur->val != next->val)
		{
    
    
			prev = cur;
			cur = next;
			next = next->next;
        }
		else//如果cur和next相同,则只移动next,cur和next仍相同继续只移动next,
		{
    
    

			while (cur->val == next->val)//cur和next仍相同继续只移动next
			{
    
    //由于迭代前提是next不为空,这里不需要cur->val == next->val && next !=NULL
				next = next->next;
			}
			//走到这里,cur和next不相同,链接prev和next,删除结点。即free掉重复的结点
            prev->next = next;
			//释放,即迭代释放
			while (cur != next)
			{
    
    
				//free(cur)//如果直接释放cur,会找不到下一个需要释放的结点
				//保存当前结点,往后走,再释放当前结点
				struct ListNode* del = cur;//del将cur先保存起来
				cur = cur->next;
				free(del);

				//或者保存下一个结点,再释放掉前一个结点,再cur往后走
				//del = cur->next;
				//free(cur);
				//cur = del;
			}
			//cur、next迭代,cur在释放结点时cur = next, next往后走
			next = next->next;//等价于next = cur->next;
		}
	}
	return pHead;
}

特殊用例1:1 -> 1 -> 1 -> 3 -> 4 -> NULL 重复头结点
在这里插入图片描述
代码优化1:

struct ListNode* deleteDuplication(struct ListNode* pHead)
{
    
    
	//当为空链表或者只要一个结点的链表时,直接返回
	if (pHead == NULL || pHead->next == NULL)
		return pHead;

	struct ListNode* prev = NULL;
	struct ListNode* cur = pHead;
	struct ListNode* next = pHead->next;

	while (next != NULL)
	{
    
    
		//cur和next不相同,三个指针往后移动
		if (cur->val != next->val)
		{
    
    
			prev = cur;
			cur = next;
			next = next->next;
		}
		else//如果cur和next相同,则只移动next,cur和next仍相同继续只移动next,
		{
    
    
            while (cur->val == next->val)//cur和next仍相同继续只移动next
			{
    
    //由于迭代前提是next不为空,这里不需要cur->val == next->val && next !=NULL
				next = next->next;
			}
			//走到这里,cur和next不相同,链接prev和next,删除结点。即free掉重复的结点
			//特殊用例:头重复,删除重复结点,更新头
			if (prev != NULL)
				prev->next = next;
			else
				pHead = next;//更新头结点

			//释放,即迭代释放
			while (cur != next)
			{
    
    
				//free(cur)//如果直接释放cur,会找不到下一个需要释放的结点
      			//保存当前结点,往后走,再释放当前结点
				struct ListNode* del = cur;//将cur先保存起来
				cur = cur->next;
				free(del);
			}
			//迭代,next往后走
			next = next->next;
		}
	}
	return pHead;
}

特殊用例2:1 -> 2 -> 3 -> 3 -> 4 -> 5 -> 5 -> 5 -> NULL 重复尾节点
在这里插入图片描述
代码优化2:

struct ListNode* deleteDuplication(struct ListNode* pHead)
{
    
    
	//当为空链表或者只要一个结点的链表时,直接返回
	if (pHead == NULL || pHead->next == NULL)
		return pHead;

	struct ListNode* prev = NULL;
	struct ListNode* cur = pHead;
	struct ListNode* next = pHead->next;

	while (next != NULL)
	{
    
    
		//cur和next不相同,三个指针往后移动
		if (cur->val != next->val)
		{
    
    
			prev = cur;
			cur = next;
			next = next->next;
		}
		else//如果cur和next相同,则只移动next,cur和next仍相同继续只移动next,
		{
    
    
			while (next != NULL && cur->val == next->val)//cur和next仍相同继续只移动next
			{
    
    //如果最后比较完了也没有找到不等于的结点,即next走到NULL,则结束比较
                //注意:cur->val == next->val && next != NULL不能这样,否则程序会崩,因为执行顺序的问题
				next = next->next;
            }
			//来到这里,1、可能next为空,2、也可能cur和next不相同

			//cur和next不相同或者next走到空,链接prev和next,删除结点。即free掉重复的结点
			//特殊用例:头重复,删除重复结点,更新头
			if (prev != NULL)
				prev->next = next;//这里可以处理next为空的情况,即prev不为空,next为空,删除重复的尾,prev链接到NULL
			else
				pHead = next;//更新头结点

            //判断next为空,释放重复的尾节点,是否会出问题
			//释放,即迭代释放
			while (cur != next)
			{
    
    
				//free(cur)//如果直接释放cur,会找不到下一个需要释放的结点
				//保存当前结点,往后走,再释放当前结点
				struct ListNode* del = cur;//将cur先保存起来
				cur = cur->next;
				free(del);
			}

			//迭代,next往后走,有问题,因此需要做一个判断
			//next = next->next;//如果直接使用这一行代码,当尾重复时,next已经走向了NULL,导致解引用空指针,发生错误

			//特殊用例:尾重复,删除重复结点,置为NULL
			if (next != NULL)
				next = next->next;
			else
				prev->next = NULL;//这里可以不做处理,因为prev链接next时,指向了NULL
		}
	}
	return pHead;
}

特殊用例3:1 -> 1 -> 1 -> 3 -> 3 -> 5 -> 5 -> 5 -> NULL
代码优化3:

struct ListNode* deleteDuplication(struct ListNode* pHead)
{
    
    
	//当为空链表或者只要一个结点的链表时,直接返回
	if (pHead == NULL || pHead->next == NULL)
		return pHead;

	struct ListNode* prev = NULL;
	struct ListNode* cur = pHead;
	struct ListNode* next = pHead->next;

	while (next != NULL)
	{
    
    
		//cur和next不相同,三个指针往后移动
		if (cur->val != next->val)
		{
    
    
			prev = cur;
			cur = next;
			next = next->next;
		}
		else//如果cur和next相同,则只移动next,cur和next仍相同继续只移动next,
		{
    
    
			while (next != NULL && cur->val == next->val)//cur和next仍相同继续只移动next
			{
    
    //如果最后比较完了也没有找到不等于的结点,即next走到NULL,则结束比较
                //注意:cur->val == next->val && next != NULL不能这样,否则程序会崩,因为执行顺序的问题
				next = next->next;
            }
			//来到这里,1、可能next为空,2、也可能cur和next不相同

			//cur和next不相同或者next走到空,链接prev和next,删除结点。即free掉重复的结点
			//特殊用例:头重复,删除重复结点,更新头
			if (prev != NULL)
				prev->next = next;//这里可以处理next为空的情况,即prev不为空,next为空,删除重复的尾,prev链接到NULL
			else
				pHead = next;//更新头结点

            //判断next为空,释放重复的尾节点,是否会出问题
			//释放,即迭代释放
			while (cur != next)
			{
    
    
				//free(cur)//如果直接释放cur,会找不到下一个需要释放的结点
				//保存当前结点,往后走,再释放当前结点
				struct ListNode* del = cur;//将cur先保存起来
				cur = cur->next;
				free(del);
			}

			//迭代,next往后走,有问题,因此需要做一个判断
			//next = next->next;//如果直接使用这一行代码,当尾重复时,next已经走向了NULL,导致解引用空指针,发生错误

			//特殊用例:尾重复,删除重复结点,置为NULL
			/*
			if (next != NULL)
				next = next->next;
			else
				prev->next = NULL;//这里可以不做处理,因为当prev为NULL时,导致空指针解引用
			*/
			if (next != NULL)
				next = next->next;
			else if(prev != NULL)//可能prev从始至终都是没有动,prev一直为NULL
				prev->next = NULL;
		}
	}
	return pHead;
}

猜你喜欢

转载自blog.csdn.net/qq_48163964/article/details/130134701
今日推荐