常见操作
1.删除链表元素
如果我们想从单链表中删除现有结点 cur,可以分两步完成:
1.找到 cur 的上一个结点 prev 及其下一个结点 next ;
2.接下来链接 prev 到 cur 的下一个节点 next 。
1.双指针
1. 设置两个均指向头节点的指针,pre(记录待删除节点的前一节点)和 cur (记录当前节点);
遍历整个链表,查找节点值为 val 的节点,找到即删除该节点,否则继续查找。
2.找到,将当前节点的前一节点(之前最近一个值不等于 val 的节点(pre))连接到当前节点(cur)的下一个节点(即将 pre 的下一节点指向 cur 的下一节点:pre->next = cur->next)。
没找到,更新最近一个值不等于 val 的节点(即 pre = cur),并继续遍历(cur = cur->next)
struct ListNode* removeElements(struct ListNode* head, int val){
while (NULL != head && head->val == val) {
head = head->next;
}
struct ListNode* cur = head;
struct ListNode* pre = head;
while (cur != NULL) {
if (cur->val == val) {
pre->next = cur->next;
} else {
pre = cur;
}
cur = cur->next;
}
return head;
}
2.迭代
删除链表中给定值的节点,一般的步骤是:
遍历链表,找到所有值为 val 的节点;
将值为 val 的节点的上一个节点直接指向该节点的下一个节点(pre->next = pre->next->next)。
struct ListNode* removeElements(struct ListNode* head, int val){
while (NULL != head && head->val == val) {
head = head->next;
}
if (NULL == head) {
return head;
}
struct ListNode* pre = head;
while (pre->next != NULL) {
/* 找到值为 val 的节点,并将其删除 */
if (pre->next->val == val) {
pre->next = pre->next->next;
} else {
/* 没找到,则继续遍历查找 */
pre = pre->next;
}
}
return head;
}
3.虚拟头节点(哨兵)
可以通过在头节点前增加虚拟头节点,这样头节点就成了普通节点,不需要单独拎出来考虑,但是在返回的时候,返回的是虚拟头节点的下一节点而不是虚拟头节点。
struct ListNode* removeElements(struct ListNode* head, int val){
struct ListNode* dummyHead = malloc(sizeof(struct ListNode));
if (NULL == dummyHead) {
return NULL;
}
dummyHead->next = head;
struct ListNode* cur = dummyHead;
while (cur->next != NULL) {
if (cur->next->val == val) {
cur->next = cur->next->next;
} else {
cur = cur->next;
}
}
struct ListNode* retNode = dummyHead->next;
free(dummyHead);
return retNode;
}
2.插入元素(一定要malloc申请)
//插入index等于len,插入尾部,index从0开始
void myLinkedListAddAtIndex(MyLinkedList* obj, int index, int val)
{
if(index<0)return;
MyLinkedList* node = obj;
for(int i=0;i<index;i++)
{
if(node->next==NULL)
return;
else
node = node->next;
}
MyLinkedList* add = (MyLinkedList*)malloc(sizeof(MyLinkedList));
add->val = val;
if(node->next!=NULL)
add->next = node->next;
else //尾部插入
add->next = NULL;
node->next = add;
}
技巧
1.快慢指针
每次移动慢指针一步
,而移动快指针两步
经典问题:环形链表
第一次相遇检查成环(快指针比慢指针每次快一步),第二次相遇检查环节点(快慢指针步长相同,相遇时一定在环节点)
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode *detectCycle(struct ListNode *head) {
if(!head||!head->next)return NULL;
struct ListNode *fast=head;
struct ListNode *cur=head;
while(fast!=NULL&&fast->next!=NULL)
{
fast=fast->next->next;
cur=cur->next;
if(fast==cur)//相遇后,快指针从头开始
{
struct ListNode*cur=head;
while(cur!=fast){
cur=cur->next;
fast=fast->next;
}
return cur;//第二次相遇节点
}
}
return NULL;
}