链表问题(去重,带环)

1.链表去重问题

1.1删除链表相同元素,使每个元素只出现一次

问题:给定一个已排序的链表的头 head , 删除所有重复的元素,使每个元素只出现一次 。返回 已排序的链表 。
在这里插入图片描述
思路:我们创建一个cur指针,使它指向链表的头结点,建立循环,循环条件是cur的下一个结点和下下个结点都不是NULL如果cur存的数字和cur的下个结点存的数字相同时候,把cur的下一个结点释放掉(防止内存泄漏),然后令cur的下一个结点为原来的下下个结点;如果cur存的数字和cur下个结点存的数字不一样时候,令cur = cur->next继续迭代即可。最后直接返回头结点即可。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
typedef struct ListNode ListNode;
struct ListNode* deleteDuplicates(struct ListNode* head) {
    ListNode* cur = head;
    while(cur && cur->next){
        if(cur->val != cur->next->val){
            cur = cur->next;
        }
        else{
            ListNode* del = cur->next;
            cur->next = cur->next->next;
        }
    }
    return head;
}
1.2删除链表相同元素,只留下不同数字

问题:给定一个已排序的链表的头 head , 删除原始链表中所有重复数字的节点只留下不同的数字 。返回 已排序的链表 。

在这里插入图片描述
思路:因为链表是排好序的,所以拥有重复数字的结点肯定连续。如果链表是空链表,我们直接返回空指针即可,然后首先我们要创造一个哑结点,使它指向链表的头结点,这样做是为了处理头结点被替换的情况(即头结点存的数字和它后面结点存的数字一样),然后创建一个指针cur指向链表的哑结点,令循环条件为cur的下一个结点和下下个结点的结点不为空结点,当它的下一个结点存的数字和下下个结点存的数字一样,我们就存下这个数字,如果cur的下一个结点存的数字是这个数字,我们就把它释放掉(防止内存泄漏),然后令cur的指向下下个结点,直到cur的下一个结点存的数字不是这个数字或者下一个结点是空结点;当cur的下一个结点存的数字和下下个结点存的数字不同时,我们直接令cur = cur->next继续迭代,

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
typedef struct ListNode ListNode;
struct ListNode* deleteDuplicates(struct ListNode* head) {
	
    ListNode* temp = (ListNode*)malloc(sizeof(ListNode));
    temp->next = head;
    ListNode* cur = temp;
    while(cur->next && cur->next->next){
		if(cur->next->val != cur->next->next->val){
			cur = cur->next;	
		}
		else{
			int same = cur->next->val;
			while(cur->next && cur->next->val == same){
				ListNode* del = cur->next;
				cur->next = cur->next->next;
				free(del);
			}
		}
	}
	ListNode* newHead = temp->next;
	free(temp);
	return newHead;
}

2.链表的环状问题

2.1链表是否存在环

问题:给你一个链表的头节点 head ,判断链表中是否有环。
如果链表中存在环 ,则返回 true 。 否则,返回 false 。
在这里插入图片描述

思路:用快慢指针来解决,即创建一个快指针和慢指针,快指针一下走两步,慢指针一下走两步,建立循环,循环条件是fast不为空而且它的下一个结点也不为空,当链表没有环时候,循环停止后返回false,当链表有环时候,快指针一定会和慢指针相遇,此时返回true。
但是为啥快慢指针为啥会相遇呢,为啥快指针要走两步呢,三步不行吗?
因为快指针每次走两步,慢指针走一步,它们的相对位移为1步,即每次快指针都追赶慢指针一步,所以如果链表有环时候,快指针一定会追上慢指针,但是如果快指针每次走3或4步时候,它们的相对位移不为1,即快指针每次不追赶慢指针一步,可能一下追赶很多步,这个时候有可能每次都跳到慢指针的前面,它们俩就会无法相遇。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
 typedef struct ListNode ListNode;
bool hasCycle(struct ListNode *head) {
    ListNode* slow = head;
    ListNode* fast = head;
    while(fast && fast->next){
    	slow = slow->next;
    	fast = fast->next->next;
    	if(slow == fast)
    		return true;
    }
    return false;
}
2.2环状链表的入环点

问题:给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
在这里插入图片描述
思路:创建快慢指针,快指针每次走两步,慢指针每次走一步,创建meet指针代表相遇结点,令它等于NULL,链表没有环,meet仍未空,如果链表有环时候,快慢指针会相遇,令meet指针指向相遇点, 再让meet和head结点同时走,它们俩的相遇点即为入环点。
思路证明:
在这里插入图片描述
设入环前的距离为L,环的长度为R,入环点到相遇点的距离为X,那么相遇点到入环点的距离为R - X,可以知道慢指针顶多走了环的一圈,因为当慢指针入环时候,快指针和它的距离顶多一个环的距离,快指针每次追赶慢指针一步,所以慢指针走了一个环的距离时候,快指针追赶了一个环的距离,所以它们在这或在这之前肯定就相遇了,所以慢指针顶多走了一圈,
由于快指针每次走两步,慢指针每次走两步,所以快指针走的距离是慢指针距离的两倍。即2*(L + X)= L + X + NR,N至少为1,化简可以得到L = NR - X,如果N等于1,相当于一个指针从链表头结点开始走,一个指针从相遇点开始走,它们俩会在入环点相遇,此时从相遇点开始走的指针走了R -X,当N不等于1时候,L = (N - 1)* R + R - X,相当于一个指针从链表头结点开始走,一个指针从相遇点开始走,它们俩会在入环点相遇,但这个时候从相遇点开始走的指针走了N - 1圈,然后再走了R - X,说明此时环非常小。所以一个指针从头开始走,另一个指针从相遇点开始走,它们一定会再入环点相遇。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
typedef struct ListNode ListNode;
struct ListNode *detectCycle(struct ListNode *head) {
    ListNode* slow = head;
    ListNode* fast = head;
    ListNode* meet = NULL;
    while(fast && fast->next){
    	slow = slow->next;
    	fast = fast->next->next;
    	if(slow == fast){
    		meet = fast;
			break;
    	}
    }
    ListNode* cur1 = head;
    ListNode* cur2 = meet;
    ListNode* ret = NULL;
    while(cur2){
    	if(cur1 == cur2){
    		ret = cur2;
    		break;
    	}
    	cur1 = cur1->next;
    	cur2 = cur2->next;
    }
    return ret;
}
  • 28
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值