链表_算法笔记

删除单链表的重复节点

链接:面试题 02.01. 移除重复节点 力扣

题目描述:编写代码,移除未排序链表中的重复节点。保留最开始出现的节点。

示例:

 输入:[1, 2, 3, 3, 2, 1]
 输出:[1, 2, 3]

解题思路:

        思路一:定义两个指针current和p来逐个遍历链表,current元素依次和p比较,直到p为NULL为止,current向后移动一个。

        思路二:利用标记数组,标记当前值是否出现过。

代码实现

        方法一:

struct ListNode* removeDuplicateNodes(struct ListNode* head){
    if(head == NULL) return NULL;
    struct ListNode* current = head;
    while(current){
        struct ListNode* p = current;
        while(p->next){
            if(p->next->val == current->val){
                p->next = p->next->next;
            }else{
                p = p->next;
            }
        }
        current = current->next;
    }
    return head;
}

        方法二:

struct ListNode* removeDuplicateNodes(struct ListNode* head){
    if(head == NULL || head->next == NULL) return head;

    //题目描述val的值在0~20000之间,标记是否出现过
    int index[200001] = {0};
    index[head->val] = 1;
    struct ListNode* pre = head, *q = head->next;
    while(q){
        if(!index[q->val]){
            index[q->val] = 1;
            pre = q;
            q = q->next;
        }else{
            pre->next = q->next;
            q = pre->next;
        }
    }
    return head;
}

如何找出链表的倒数第K个元素?

链接:剑指 Offer 22. 链表中倒数第k个节点 力扣

题目描述:

        输入一个链表,输出该链表中倒数第k个节点。为了符合大多数人的习惯,本题从1开始计数,即链表的尾节点是倒数第1个节点。

        例如,一个链表有 6 个节点,从头节点开始,它们的值依次是 1、2、3、4、5、6。这个链表的倒数第 3 个节点是值为 4 的节点。

示例:

给定一个链表: 1->2->3->4->5, 和 k = 2.

返回链表 4->5

解题思路:

        思路:快慢指针,先让快指针走k步,然后两个指针同步走,当快指针走到头时,慢指针就是链表倒数第k个节点。

代码实现        

struct ListNode* getKthFromEnd(struct ListNode* head, int k){
    struct ListNode* slow = head;
    struct ListNode* fast = head;
    while(--k){
        fast = fast->next;
    }
    while(fast->next){
        slow = slow->next;
        fast = fast->next;
    }
    return slow;
}

如何找出链表的中间节点?

链接:876. 链表的中间结点力扣

题目描述:

        给你单链表的头结点 head ,请你找出并返回链表的中间结点。

        如果有两个中间结点,则返回第二个中间结点。

示例:

输入:head = [1,2,3,4,5]
输出:[3,4,5]
解释:链表只有一个中间结点,值为 3 


输入:head = [1,2,3,4,5,6]
输出:[4,5,6]
解释:该链表有两个中间结点,值分别为 3 和 4 ,返回第二个结点

解题思路:

        思路:双指针,定义快指针一次走两步,慢指针一次走一步。需要注意边界条件,“如果有两个中间结点,则返回第二个中间结点”,则有快指针前进条件:当前快慢指针下一节点均非空。

代码实现       

struct ListNode* middleNode(struct ListNode* head){
    struct ListNode* slow = head;
    struct ListNode* fast = head;
    while(fast && fast->next){
        slow = slow->next;
        fast = fast->next->next;
    }
    return slow;
}

反转链表

链接:剑指 Offer 24. 反转链力扣

题目描述:

        定义一个函数,输入一个链表的头节点,反转该链表并输出反转后链表的头节点。

示例:

输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL

解题思路:

        思路:反转链表实际就是重新指向结构体中的next指针,我们需要修改下一个节点的next指针指向前一个节点。所以,在遍历链表时我们要逐个修改链表的指针指向。这个题目也可以用递归来做, -直递归到链表的最后一个结点,该结点就是反转后的头结点,记作head.
        此后,每次函数在返回的过程中,让当前结点的下一个结点的next指针指向当前节点。同时让当前结点的next指针指向NULL ,从而实现从链表尾部开始的局部反转。当递归函数全部出栈后,链表反转完成。

代码实现

struct ListNode* reverseList(struct ListNode* head){
    struct ListNode* mid = head;
    struct ListNode* former = NULL;
    struct ListNode* latter = NULL;
    while(mid != NULL){
        latter = mid->next;
        mid->next = former;
        former = mid;
        mid = latter;
    }
    return former;
}

   递归解法 

struct ListNode* reverseList(struct ListNode* head){
    if(head == NULL || head->next == NULL) return head;
    else{
        struct ListNode* mid = head;
        struct ListNode* latter = mid->next;
        head = reverseList(latter);
        latter->next = mid;
        mid->next = NULL;
        return head;
    }
}

环形链表

链接:141. 环形链表力扣

题目描述:

给你一个链表的头节点 head ,判断链表中是否有环。

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos 不作为参数进行传递 。仅仅是为了标识链表的实际情况。

如果链表中存在环 ,则返回 true 。 否则,返回 false 。

示例:

输入:head = [3,2,0,-4], pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个节点。

解题思路:

        思路:反转链表实际就是重新指向结构体中的next指针,我们需要修改下一个节点的next指针指向前一个节点。所以,在遍历链表时我们要逐个修改链表的指针指向。这个题目也可以用递归来做, -直递归到链表的最后一个结点,该结点就是反转后的头结点,记作head.
        此后,每次函数在返回的过程中,让当前结点的下一个结点的next指针指向当前节点。同时让当前结点的next指针指向NULL ,从而实现从链表尾部开始的局部反转。当递归函数全部出栈后,链表反转完成。

代码实现 

        维护哈希表查询(cpp)        

class Solution {
public:
    bool hasCycle(ListNode *head) {
        unordered_set<ListNode *> seem;
        while(head){
            if(seem.count(head)) return true;
            else{
                seem.insert(head);
                head = head->next;
            }
        }
        return false;
    }
};

        快慢指针(c)

bool hasCycle(struct ListNode *head) {
    struct ListNode *slow = head;
    struct ListNode *fast = head;
    while(fast != NULL && fast->next != NULL){
        if(fast->next == slow)  return true;
        fast = fast->next->next;
        slow = slow->next;
    }
    return false;
}

单链表相交,如何求交点?

链接:面试题 02.07. 链表相交力扣

题目描述:

给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回 null 。

图示两个链表在节点 c1 开始相交

题目数据 保证 整个链式结构中不存在环。

注意,函数返回结果后,链表必须 保持其原始结构 。 

示例:

输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,0,1,8,4,5], skipA = 2, skipB = 3
输出:Intersected at '8'
解释:相交节点的值为 8 (注意,如果两个链表相交则不能为 0)。
从各自的表头开始算起,链表 A 为 [4,1,8,4,5],链表 B 为 [5,0,1,8,4,5]。
在 A 中,相交节点前有 2 个节点;在 B 中,相交节点前有 3 个节点。

解题思路:

        思路:根据题意,首先有,交点必在两单链表上,如下图所示:

        无论是否BD≠AB,都有AD+BD+DC = BD+ AD+DC,即AC + BD = BC + AD

        则有,每条链表遍历自身后再遍历另一条链表,必然于交点重合。 

代码实现 

struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
    struct ListNode *A = headA;
    struct ListNode *B = headB;
    while(A!=B){
        if(A == NULL)   A = headB;
        else A = A->next;
        if(B == NULL)   B = headA;
        else B = B->next;        
    }
    return A;
}

用普通算法实现两个有序链表的合并

链接:21. 合并两个有序链表​​​​​​​​​​​​​​力扣

题目描述:

将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 

提示:

  1. 两个链表的节点数目范围是 [0, 50]
  2. -100 <= Node.val <= 100
  3. l1 和 l2 均按 非递减顺序 排列

示例:

输入:l1 = [1,2,4], l2 = [1,3,4]
输出:[1,1,2,3,4,4]

解题思路:

方法一:非递归
        两个链表是排序过得,所以我们只需要同时遍历两个链表,比较链表元素的大小,将小的连接到新的链表中即可。后遍历完的链表直接连接到新链表末端即可。
方法二:递归
        在两表都不为空的情况下,如果一个表当前value更小,把该表的next修改为两表当前较小值。

代码实现 

方法一:非递归

struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2){
    if(list1 == NULL) return list2;
    if(list2 == NULL) return list1;
    //注意此步分配内存
    struct ListNode* head = (struct ListNode*)malloc(sizeof(struct ListNode));
    struct ListNode* cur = head;
    while(list1 && list2){
        if(list1->val > list2->val){
            cur-> next = list2;
            list2 = list2->next;
        }else{
            cur-> next = list1;
            list1 = list1->next;            
        }
        cur = cur->next;         
    }
    cur->next = (list1 == NULL? list2:list1);
    return head->next;
}

方法二:递归

struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2){
    if(list1 == NULL) return list2;
    if(list2 == NULL) return list1;

    if(list1->val < list2->val){
        list1->next = mergeTwoLists(list1->next,list2);
        return list1;
    }else{
        list2->next = mergeTwoLists(list2->next,list1);
        return list2;        
    }
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值