Leedcode刷题——3 链表

注:以下代码均为c++

数据结构的定义与创建:

//数据结构的定义
struct ListNode {
    int val;
    ListNode *next;
    ListNode() : val(0), next(nullptr) {}
    ListNode(int x) : val(x), next(nullptr) {}
    ListNode(int x, ListNode *next) : val(x), next(next) {}
};
//尾插法建立链表
ListNode* createLink(vector<int> l){
    ListNode* head = new ListNode();
    ListNode* r = head, * s = head;
    for(auto a: l){
        s = new ListNode();
        s->val = a;
        r->next = s;
        r = s;
    }
    r->next = NULL;
    return head->next;
}
// 输出单链表的value
while(cur)
{
    cout << cur->val << " ";
    cur = cur->next;
}
cout << endl;

1. 删除链表的倒数第N个节点

在这里插入图片描述
思路:快慢指针
在这里插入图片描述

//注:这里的链表都没有头节点
ListNode* removeNthFromEnd(ListNode* head, int n) {
    ListNode* dummy = new ListNode(-1);  //因为有可能删除第一个节点,所以建一个头节点指向第一个节点,便于将第一个节点与其他节点统一处理。
    dummy->next = head;
    ListNode* first = dummy, *second = dummy;
    while(n--)  //first先走n步
        first = first->next;
    while(first->next){  //first走到头时,second走到倒数n+1个
        first = first->next;
        second = second->next;
    }
    second->next = second->next->next;
    return dummy->next;
}

2. 删除链表中的节点

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
思路:
在这里插入图片描述
“伪装”
我们知道要想删除当前节点必须知道它的上一节点,但我们只有当前要删除的节点地址,无法找到上一节点。
所以,我们将要删除的node伪装成下一个点,再将下一个点删掉。

void deleteNode(ListNode* node) {
    //node->val = node->next->val;
    //node->next = node->next->next;

    // 可以将上面两句话合并为一句话,因为链表的数据结构为结构体,其存储的val和next的地址是相邻的,
    // 我们将当前节点的val,next复制为下一节点的val,next,可以一起操作。
    *(node) = *(node->next);  //当前节点的地址 = 下一节点的地址
}

3. 删除排序链表中的重复元素

在这里插入图片描述
在这里插入图片描述
思路:
在这里插入图片描述

ListNode* deleteDuplicates(ListNode* head){
    ListNode* cur = head;
    while(cur){
        if(cur->next && cur->next->val == cur->val)
            cur->next = cur->next->next;
        else
            cur = cur->next;
    }
    return head;
}

4. 删除排序链表中的重复元素2

在这里插入图片描述
在这里插入图片描述

ListNode* deleteDuplicates(ListNode* head) {
    //设头节点
    ListNode* h = new ListNode();
    h->next = head;

    ListNode* first, *second;  //first指向不重复节点的尾部, second寻找下一个不重复节点
    first = h;
    second = first->next;

    while(second && second->next){
        if(second->val == second->next->val){
            while(second->next && second->val == second->next->val)
                second = second->next;
            second = second->next;
        }
        else{
            first->next = second;
            first = second;
            second = second->next;
        }
    }
    first->next = second;
    return h->next;
}

5. 旋转链表

在这里插入图片描述
在这里插入图片描述
思路:快慢指针
在这里插入图片描述

ListNode* rotateRight(ListNode* head, int k) {
    if(head == NULL)
        return head;
    //求n
    int n = 0;
    for(ListNode* p = head; p; p = p->next)
        n++;
    k = k % n;
    //定义快慢指针,快指针先走k步
    ListNode* first, *second;
    first = head;
    second = head;
    while(k--)
        second = second->next;
    //两指针同时走,直到快指针走到头
    while(second->next){
        first = first->next;
        second = second->next;
    }
    //旋转
    second->next = head;
    head = first->next;
    first->next = NULL;

    return head;
}

6. 两两交换链表中的节点

在这里插入图片描述
在这里插入图片描述
思路:
在这里插入图片描述

//2 建立头节点,因为对第一个节点进行操作,所以设置头节点来处理较为容易
ListNode* swapPairs(ListNode* head){
    ListNode* dummy = new ListNode();
    dummy->next = head;
    ListNode* p = dummy;
    while(p->next && p->next->next){
        ListNode *a = p->next, *b = p->next->next;
        p->next = b;
        a->next = b->next;
        b->next = a;
        p = a;
    }
    return dummy->next;
}

7. 反转链表

在这里插入图片描述
在这里插入图片描述
思路:
请添加图片描述

ListNode* reverseList(ListNode* head) {
    if(head == NULL || head->next == NULL)
        return head;
    ListNode* p = head, *q = p->next, *t = q->next;
    while(t){
        q->next = p;
        p = q;
        q = t;
        t = t->next;
    }
    q->next = p;  //这一步容易遗忘,当t=NULL时没有进入循环,直接退出了,需要补充一句。
    head->next = NULL;
    head = q;  //也可以不要这一句,直接返回q
    return head;
}

8. 反转链表2

在这里插入图片描述
在这里插入图片描述
思路:
在这里插入图片描述

ListNode* reverseBetween(ListNode* head, int left, int right) {
    if(left == right)
        return head;

    int i;
    //建立头节点
    ListNode* dummy = new ListNode();
    dummy->next = head;
    head = dummy;
    //找到first和second的位置,first->next是第一个要翻转的点,second是最后一个要翻转的点。
    ListNode* first = head, *second;
    for(i = 1; i < left; i++)
        first = first->next;
    second = first->next;
    for(i = 1; i <= right - left; i++)
        second = second->next;
    //翻转
    ListNode* p = first->next, *q = p->next, *t = q->next;
    while(t != second->next){
        q->next = p;
        p = q;
        q = t;
        t = t->next;
    }
    q->next = p;
    first->next->next = t;
    first->next = second;

    return head->next;
}

9. 相交链表

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
思路:
在这里插入图片描述

ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
    ListNode* p = headA, *q = headB;
    while(p != q){  //注意这个while循环内部一定是if-else,这种结构的语句,不然会出现问题。
        if(p == NULL)
            p = headB;
        else
            p = p->next;
        if(q == NULL)
            q = headA;
        else
            q = q->next;
    }
    return p;
}

10. 环形链表2

在这里插入图片描述
在这里插入图片描述
思路1:哈希表(我看这道题的第一个想法)
一个非常直观的思路是:我们遍历链表中的每个节点,并将它记录下来;一旦遇到了此前遍历过的节点,就可以判定链表中存在环。借助哈希表可以很方便地实现。
思路2:快慢指针
(1)
快指针:一次走两步(红色)
慢指针:一次走一步(蓝色)
快慢指针一起走,假设当慢指针走到b点时,快指针走到c’点,那么快慢指针将在c点相遇。
在这里插入图片描述
(2)
快慢指针相遇后,将慢指针指向链表头部,快慢指针均一次走一步,快慢指针将在b点相遇,即环的起始点。
在这里插入图片描述

ListNode *detectCycle(ListNode *head) {
    ListNode *quick = head, *slow = head;
    //注意开始需要将quick和slow走起来,因为如果开始不走,quick=slow,无法进入下面while循环
    if(quick == NULL || (quick != NULL && quick->next == NULL))
        return NULL;
    quick = quick->next->next;
    slow = slow->next;

    while(quick != slow  && quick && quick->next){  //注意quick和quick->next需要判空
        quick = quick->next->next;
        slow = slow->next;
    }
    if(quick != slow)  //如果遇到空就说明不是环,返回NULL
        return NULL;
    slow = head;
    while(quick != slow){
        quick = quick->next;
        slow = slow->next;
    }
    return quick;
}
  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值