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;
}