LeetCode 刷题 -- Day 2

本文介绍了多个LeetCode链表相关题目,包括移除指定元素、设计链表结构、反转链表、两两交换节点、删除倒数第N个节点、链表相交以及环形链表的判断,展示了解决这些问题的代码思路和优化技巧。
摘要由CSDN通过智能技术生成

今日题目

题目难度备注
203. 移除链表元素 - 力扣(LeetCode)简单没有头结点创造头结点
707. 设计链表 - 力扣(LeetCode)中等单链表知识查漏补缺处
206. 反转链表 - 力扣(LeetCode)简单
24. 两两交换链表中的节点 - 力扣(LeetCode)中等成就感 + 0.5 (思维欠缺一丢丢)
19. 删除链表的倒数第 N 个结点 - 力扣(LeetCode)中等成就感 + 1
面试题 02.07. 链表相交 - 力扣(LeetCode)简单有更好的方法
142. 环形链表 II - 力扣(LeetCode)中等♥♥♥

题目:203. 移除链表元素

203. 移除链表元素 - 力扣(LeetCode)

一、源代码

 ListNode* removeElements(ListNode* head, int val) {
        ListNode* pre = new ListNode(0,head);   
        ListNode* p = pre;
        while(p->next != NULL){
            if(p->next->val == val){
                p->next = p->next->next;
            }else{
                p = p->next;
            }
        }
        return pre->next;
    }

二、代码思路

没有头结点就创造头结点,虚假头结点最好用 dummyHead 来定义,比 pre 有更好的可读性


题目:707. 设计链表

707. 设计链表 - 力扣(LeetCode)

一、源代码

private:
    struct ListNode{
        int val;
        ListNode* next;
        ListNode():val(0),next(NULL){}
        ListNode(int val):val(val),next(NULL){}
        ListNode(int val,ListNode* next):val(val),next(NULL){}
    };
    int len;
    ListNode* dummyhead; //链表虚拟头结点

public:
    MyLinkedList() {
        len = 0;
        dummyhead = new ListNode();
    }
    
    int get(int index) {
        if(index<0||index>len-1) return -1;
        ListNode* p = dummyhead->next;
        while(index--){
            p = p ->next;
        }
        return p->val;
    }
    
    void addAtHead(int val) {
        ListNode* p = new ListNode(val);
        p->next = dummyhead->next;
        dummyhead->next = p;
        len++;
    }
    
    void addAtTail(int val) {
        ListNode* p = new ListNode(val),*cur=dummyhead;
        while(cur->next) cur = cur->next;
        cur->next = p;
        len++;
    }
    
    void addAtIndex(int index, int val) {
        if(index<=0) addAtHead(val);
        else if(index==len) addAtTail(val);
        else{
            ListNode* p = new ListNode(val),*pre = dummyhead;
            while(index--) pre = pre->next;
            p->next = pre->next;
            pre->next = p;
            len++;
        }
    }
    
    void deleteAtIndex(int index) {
        if(index<0||index>len-1) return;
        ListNode* pre = dummyhead,*p = NULL;
        while(index--) pre = pre->next;
        p = pre->next;
        pre->next = p->next;
        delete p;
        len--;
    }
};

二、补充

    // 创建单链表
    ListNode *head = new ListNode,*p = head;
	for(int i=1;i<=n;i++){
		scanf("%d",&v);
		p->next = new ListNode(v);
		p = p->next;
	}

   //头插法
   p = q ->next;
   q->next = H->next;
   H->next = q;
   q = p;

   //尾插法
   ListNode *rear = H->next;
   while(){
    q = new ListNode(v);
    rear->next = q;
    rear = q;
   }
   rear->next = NULL;



题目:206. 反转链表

206. 反转链表 - 力扣(LeetCode)

ListNode* H = new ListNode(),*cur = head,*temp =NULL;
        while(cur!=NULL){
            temp = cur->next;
            cur->next = H->next;
            H->next = cur;
            cur = temp;
        }
        return H->next;

题目:24. 两两交换链表中的节点

24. 两两交换链表中的节点 - 力扣(LeetCode)

一、源代码

ListNode* swapPairs(ListNode* head) {
        if(head==NULL ||head->next==NULL) return head;
        ListNode *newHead = head->next,*q,*p = head;
        while(p->next!=NULL){
            q = p->next;        
            p->next = q->next;
            q->next = p;
            p = p->next;     
        }
        return newHead;

报错

Line 4: Char 18: runtime error: member access within null pointer of type 'ListNode' (solution.cpp)
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior prog_joined.cpp:25:18

二、错误原因

1、语法错误,类似数组越界

当 p == NULL 的时候,还会去访问 p->next;如当while循环中的最后一句,p = p ->next = NULL时,下一次循环开始会访问 p->next;

2、逻辑错误

只考虑两两交换每队链表,没考虑把链表连起来,这样虽然链表两两交换了,当在逻辑上却被拆成很多段了

解决方法:用pre指针搭桥,连接各端

三、修改

ListNode* swapPairs(ListNode* head) {
        if(head==NULL ||head->next==NULL) return head;
        ListNode *newHead = head->next,*q,*p = head,*pre = new ListNode(0,head);
        while(p->next!=NULL){
            q = p->next;     //p,q 实现每队两两交换
            p->next = q->next;
            q->next = p;
            pre->next = q;   //pre用于链接各队
            pre = p;
            p = p->next;
            if(p==NULL){
                break;
            }
        }
        return newHead;
    }

题目:19. 删除链表的倒数第 N 个结点

19. 删除链表的倒数第 N 个结点 - 力扣(LeetCode)

一、源代码

 ListNode* removeNthFromEnd(ListNode* head, int n) {
        if(head->next == NULL)      //长度为1,直接返回NULL
            return NULL;
        ListNode *p = head, *q = head->next;
        int cnt = 1;
        while(q->next!=NULL){  //p一次走一步,q一次走两步,这样q到终点时,只走了一半,原本遍历链表O(n),现在O(n/2)
            cnt++;
            p = p->next;
            q = q->next->next;
            if(q==NULL){
                break;
            }
        }
        int len = (q==NULL)? cnt*2-1 :cnt*2;
        if(n < cnt){  //要删除的结点在p后面
             int k = len - cnt - n + 1;   //p要移动的次数
             ListNode *pre = p;
             while(k--){
                pre = p;
                p = p->next;
             }
             pre->next = p->next;
             delete p;
        }else{
            int k = len - n;
            ListNode *p = head,*pre = p;
            while(k--){
                pre = p;
                p = p->next;
            }
            if(n==len){   //删除表头
                return head->next;
            }
            pre->next = p->next;
             delete p;
        }
        return head;
    }

二、代码思路

1、若使用两趟扫描解决问题,固然简单,一趟得出链表长度,再把倒数第n个换算成正数第k个再扫描一次就行了

2、进阶难度是怎么仅使用一趟实现呢?想了很久,发现一种比两趟好一点的方法:用一趟是为了代码跑得快,那我定义两个指针 *fast 和 *slow,fast一次走两步,slow每次走一步,这样fast到底终点后,slow只走了一半。这样只用花一半的时间就能得到链表的长度len了,此时slow还在链表的中间,若 n < len 的话,slow 继续向前next,找到要删除的结点删除就行,这不就一趟实现了。若 n > len,那就把倒数第n 转换成 正数第k 个查找删除就行,最多也只用扫描半趟。

最后提交发现执行时间还是挺少的,但是代码肯定可以优化,写的太差了

tmp2EE6

三、代码优化

1、用 *fast 和 *slow 指针快速得到单链表长度,

源代码

        ListNode *p = head, *q = head->next;
        int cnt = 1;
        while(q->next!=NULL){ 
            cnt++;
            p = p->next;
            q = q->next->next;
            if(q==NULL){
                break;
            }
        }
        int len = (q==NULL)? cnt*2-1 :cnt*2;

修改代码

        ListNode *slow = head, *fast = head->next,;   //用fast、slow 更有可读性
        int cnt = 1;
        while(true){  
            if(fast==NULL || fast->next==NULL) break;  //把循环结束条件写一起好点
            cnt++;
            fast = fast->next->next;
            slow = slow->next;
        }
        int len = (fast == NULL)? cnt*2-1 : cnt*2;  

2、代码能共用则 共用

源代码

        if(n < cnt){  //要删除的结点在p后面
             int k = len - cnt - n + 1;   //p要移动的次数
             ListNode *pre = p;
             while(k--){
                pre = p;
                p = p->next;
             }
             pre->next = p->next;
             delete p;
        }else{
            int k = len - n;
            p = head,*pre = p;
            while(k--){
                pre = p;
                p = p->next;
            }
            pre->next = p->next;
             delete p;
        }

修改代码

            if(n == len){   //删除表头
                return head->next;
            }
            if(n > cnt) p = head;
            int k = (n < cnt)? len-cnt-n+1 : len - n;
            ListNode *pre = p;
            while(k--){
                pre = p;
                p = p->next;
             }
             pre->next = p->next;
             delete p;

题目:面试题 02.07. 链表相交

面试题 02.07. 链表相交 - 力扣(LeetCode)

一、源代码

ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        int lenA = 0,lenB = 0;
        ListNode *pa = headA,*pb = headB;
        while(pa!=NULL){   //求长度
            lenA++;
            pa = pa->next;
        }
        while(pb!=NULL){
            lenB++;
            pb = pb->next;
        }
        pa = headA;
        pb = headB;
        if(lenA < lenB){      //右对齐
            int k = lenB - lenA;
            while(k--){
                pb = pb ->next;
            }
        }else{
            int k = lenA - lenB;
            while(k--){
                pa = pa ->next;
            }
        }
        while(pa){   //返回第一个相同结点
            if(pa ==pb){
                return pa;
            }else{
                pa = pa->next;
                pb = pb->next;
            }
        }
        return NULL;
    }

二、优化思路

因为是公共结点,即找到第一个相同结点就行,那就可以用vector这类的容器存储链表A中的结点,再从头遍历链表B,在容器中出现的第一个b结点就是答案。但是vector不能用 find() 方法,可以用无序容器unordered_set 来实现

三、优化代码

ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        unordered_set<ListNode *> visited;
        ListNode *temp = headA;
        while (temp != nullptr) {
            visited.insert(temp);
            temp = temp->next;
        }
        temp = headB;
        while (temp != nullptr) {
            if (visited.count(temp)) { //  count返回元素在集合中出现的次数
                return temp;
            }
            temp = temp->next;
        }
        return nullptr;
    }

题目:142. 环形链表 II

142. 环形链表 II - 力扣(LeetCode)

一、思维缺陷

知道用 *fast 和 *slow 指针来判断是否有环,但是不知道怎么找出环入口结点

其实就是数学问题,找关系

tmp8867

设链表共有a+b个结点,a是链表头部到环入口之间的结点数,b是环中的结点数

fast 走的步数是 slow 步数的 2 倍,即 f=2s ①
fast 比 slow 多走了 n 个环的长度,即 f=s+nb ②

所以 ① - ② 得 s = nb,f = 2nb;

再令fast 重新指向头部结点,此时 f = 0,s = nb
fast 和 slow 每轮各走一步
则 当 fast 走了 a 步时,f = a,s = nb + a,两者相遇,并同时指向链表入口

二、参考答案

ListNode *detectCycle(ListNode *head) {
        if(head==NULL || head->next==NULL) return NULL;
        int flag = 0;
        ListNode *fast = head, *slow = head;
        while(true){   //跑圈圈,q每次多走一步,有环必能追上
            if(fast==NULL || fast->next==NULL) return NULL;
            fast = fast->next->next;
            slow = slow->next;
            if(fast == slow) break;
        }
        fast = head;
        while(slow !=fast){
            slow = slow->next;
            fast = fast->next;
        }
        return fast;
    }
  • 58
    点赞
  • 40
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值