刷题Day2

1.移除链表元素

1.1在原链表上操作

先处理头结点,头结点处理的时候不需要考虑前面的节点,所以直接删掉就可以了,然后处理其他节点,这个时候要考虑前面的节点,所以当cur->next->val==val的时候,连接cur和cur->next->next就可以了;最后返回head

 ListNode* removeElements(ListNode* head, int val) {
        // 处理头节点需要删除的情况
        while (head != nullptr && head->val == val) {
            ListNode* temp = head;
            head = head->next;
            delete temp;
        }

        // 现在头节点的值不再等于val,开始处理剩下的节点
        ListNode* cur = head;
        while (cur != nullptr && cur->next != nullptr) {
            if (cur->next->val == val) {
                // 删除cur->next节点
                ListNode* temp = cur->next;
                cur->next =temp->next;
                delete temp;
            } else {
                // 移动cur指针
                cur = cur->next;
            }
        }

        return head;
    }

 如果在head前面添加一个虚拟头结点,就不需要对头结点进行额外的处理,头结点的处理和其他节点的处理是相同的

class Solution {
public:
    ListNode* removeElements(ListNode* head, int val) {
        // 创建一个虚拟头节点
        ListNode* dummy = new ListNode(0);
        dummy->next = head;
        
        ListNode* cur = head;
        ListNode* pre = dummy;  

        while (cur != nullptr) {
            if (cur->val == val) {
                pre->next = cur->next;  
                delete cur;  
                cur = pre->next;  
            } else {
                pre = cur;  
                cur = cur->next;  
            }
        }

        ListNode* result = dummy->next;
        delete dummy;  
        return result;
    }
};

1.2也可以进行递归,先判断头节点是否为val,如果是就是递归处理head->next,接下来对剩余的节点进行处理

class Solution {
public:
    ListNode* removeElements(ListNode* head, int val) {
        // 基础情况:空链表
        if (head == nullptr) {
            return nullptr;
        }

        // 递归处理
        if (head->val == val) {
            ListNode* newHead = removeElements(head->next, val);
            delete head;
            return newHead;
        } else {
            head->next = removeElements(head->next, val);
            return head;
        }
    }
};

2.实现带头节点的链表的增删改查

在定义时候就包括链表的size,方便未来的处理

class MyLinkedList {
public:
    struct LinkedNode {
        int val;
        LinkedNode* next;
        LinkedNode(int x) : val(x), next(nullptr) {}
    };
    MyLinkedList() {
        _dummyHead = new LinkedNode(0);
        _size = 0;
    }

    int get(int index) {
        if (index < 0 || index >= _size) {
            return -1;
        }
        LinkedNode* cur = _dummyHead->next;
        for (int i = 0; i < index; i++) {
            cur = cur->next;
        }
        return cur->val;
    }

  
    void addAtHead(int val) {
        addAtIndex(0, val); 
    }

 
    void addAtTail(int val) {
        addAtIndex(_size, val);  
    }

    void addAtIndex(int index, int val) {
        if (index > _size) {
            return; 
        }
        if (index < 0) {
            index = 0;  
        }
   
        LinkedNode* prev = _dummyHead;
        for (int i = 0; i < index; i++) {
            prev = prev->next;
        }
     
        LinkedNode* newNode = new LinkedNode(val);
      
        newNode->next = prev->next;
        prev->next = newNode;
        _size++;
    }

  
    void deleteAtIndex(int index) {
        if (index < 0 || index >= _size) {
            return; 
        }
       
        LinkedNode* prev = _dummyHead;
        for (int i = 0; i < index; i++) {
            prev = prev->next;
        }
       
        LinkedNode* toDelete = prev->next;
        prev->next = toDelete->next;
        delete toDelete; 
        _size--;
    }

private:
    LinkedNode* _dummyHead; 
    int _size;              
};

3.反转链表

太经典了,不需过多赘述,三个指针解决

ListNode* reverseList(ListNode* head) {
        if(head==nullptr){
            return head;
        }
        ListNode*cur=head;
        ListNode*pre=nullptr;
        ListNode*last=head->next;
        while(last!=nullptr){
            cur->next=pre;
            pre=cur;
            cur=last;
            last=last->next;
        }
        head=cur;
        cur->next=pre;
        return head;

    }

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

设置一个头结点,有利于以交换操作,不需要考虑头结点的特殊情况

0->1->2->3->4->     

此时cur=0,我设置t1=1,t2=2;我先让cur->next指向2,然后让t1->next指向t2的next,最后让t2->next指向t1,注意,如果先更新t2的指针,就会导致断开

0->2-><-1        3->4->

ListNode* swapPairs(ListNode* head) {
        ListNode* dummyHead = new ListNode(0); 
        dummyHead->next = head;
        ListNode* cur = dummyHead;
       while(cur->next!=nullptr&&cur->next->next!=nullptr){
        ListNode*t1=cur->next;
        ListNode*t2=t1->next;
        cur->next=t2;
        t1->next=t2->next;
        t2->next=t1;
        cur=cur->next->next;
       }
       return dummyHead->next;
    }

5.删除倒数第n个节点

经典,快慢指针,快指针先走n个,然后一起走,最后指向的就是倒数第n个,画个图就明白了

ListNode* removeNthFromEnd(ListNode* head, int n) {
        ListNode* dummyhead=new ListNode(0);
        dummyhead->next=head;
        ListNode*fast=dummyhead;
        ListNode*slow=dummyhead;
       while(n--&&fast){
        fast=fast->next;
       }
        while(fast->next){
            fast=fast->next;
            slow=slow->next;
        }
        ListNode*temp=slow->next;
        slow->next=temp->next;
        delete temp;
        return dummyhead->next;
        

    }

5.链表相交

5.1朴素的方法,先知道两个链表的长度,然后让长链表与短的尾部对齐,然后一个一个比较就可以了。

注意!!!!!是比较指针是不是一个地址,而不是比较节点里面的值

   ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        ListNode*cura=headA;
        ListNode*curb=headB;
        int lengtha=0,lengthb=0;
        while(cura!=nullptr){
            cura=cura->next;
            lengtha++;
        }
         while(curb!=nullptr){
            curb=curb->next;
            lengthb++;
        }
        cura=headA;
        curb=headB;

        if(lengtha<lengthb){
            swap(lengtha,lengthb);
            swap(cura,curb);
        }
        int delta=lengtha-lengthb;
        while(delta--){
            
            cura=cura->next;

        }
        while(cura!=nullptr){
            if(cura==curb){
                return cura;
            }else{
                cura=cura->next;
                curb=curb->next;
            }
        }
        return nullptr;
    }

 5.2更简洁但是不好理解的方法

画个图举个例子:

链表A: A1 -> A2 -> A3 -> C1 -> C2 -> C3

                                         链表B:          B1 -> B2 -> C1 -> C2 -> C3

 pA从A1开始,pB从B1开始

初始位置

  • pA 指向 A1
  • pB 指向 B1

第 1 步:

  • pA 从 A1 走到 A2
  • pB 从 B1 走到 B2

第 2 步:

  • pA 从 A2 走到 A3
  • pB 从 B2 走到 C1

第 3 步:

  • pA 从 A3 走到 C1
  • pB 从 C1 走到 C2

第 4 步:

  • pA 从 C1 走到 C2
  • pB 从 C2 走到 C3

第 5 步:

  • pA 从 C2 走到 C3
  • pB 从 C3 走到 nullptr(B 链表到末尾了)

第 6 步:

  • pA 从 C3 走到 nullptr(A 链表到末尾了)
  • pB 由于变成了 nullptr,现在根据算法逻辑,它“跳转”到 headA,所以 pB 回到 A1

第 7 步:

  • pA 变成 nullptr 后,根据算法逻辑,“跳转”到 headB,所以 pA 回到 B1
  • pB 从 A1 走到 A2

第 8 步:

  • pA 从 B1 走到 B2
  • pB 从 A2 走到 A3

第 9 步:

  • pA 从 B2 走到 C1
  • pB 从 A3 走到 C1

此时,pApB 都在 C1,它们相等,说明找到了交点。在这一刻 pA == pB == C1

如果两个链表根本就不相交,最后它们都会遍历到 nullptr,然后 pApB 都会变成 nullptr,两者相等,返回 nullptr 表示没有交点。

ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
    if (!headA || !headB) return nullptr;

    ListNode* pA = headA;
    ListNode* pB = headB;

    // 当 pA 和 pB 相等的时候,或者都指向同一个节点,或者都是 nullptr
    while (pA != pB) {
        pA = (pA == nullptr) ? headB : pA->next;
        pB = (pB == nullptr) ? headA : pB->next;
    }

    return pA; // 也可以 return pB,二者相等
}

6.环形链表

6.1最简单的思路就是用哈希表记录一下,然后看看节点有没有经历过,一旦发现经历过,直接返回,要是走到最后也没有,就说明没有环,直接返回空指针

ListNode *detectCycle(ListNode *head) {
        ListNode*cur=head;
        unordered_set<ListNode*> visited;
        while(cur!=nullptr){
            if(visited.count(cur)){
                return cur;
            }
            visited.insert(cur);
            cur=cur->next;
        }
        return nullptr;
    }

6.2快慢指针

快指针每次走两次,慢指针每次走一次,如果有环,一定会在环中相遇,接下来就是解决如何求出入环处的问题了,假设环外是a,慢指针走了b被追上,环还剩下c的距离,这样可以得到快慢指针分别走的距离

慢指针:a+b

快指针:a+n(b+c)+b=a+(n+1)b+nc。

又因为快指针一定走的是慢指针的二倍,得到等式;

a+(n+1)b+nc=2(a+b)⟹a=c+(n−1)(b+c)

观察推导结果,发现,在head处设置一个ptr指针,如果让慢指针走c步+n圈, ptr正好走过a步到达入环处,此处借用力扣的图示进行解释

 所以得到最终代码

 ListNode *detectCycle(ListNode *head) {
        ListNode*fast=head;
        ListNode*slow=head;
        while(fast!=nullptr){
           slow=slow->next;
           if(fast->next==nullptr){
            return nullptr;
           }
           fast=fast->next->next;
           if(fast==slow){
            ListNode*ptr=head;
            while(ptr!=slow){
                ptr=ptr->next;
                slow=slow->next;
            }return ptr;
           }
        }
        return nullptr;
    }

今天的学习就结束啦!!!给大家推荐一个网站代码随想录 很适合初学者进行系统性的学习和刷题,有详细的讲解以及相关习题推荐。

### 关于蓝桥杯竞赛中的C语言回文字符串练习及其解法 对于蓝桥杯竞赛中涉及的回文字符串问,在C语言环境下可以通过多种方式实现。下面提供一种常见的解决方案,该方案不仅适用于比赛环境下的快速编写需求,同时也能够帮助理解如何高效处理此类逻辑。 #### 定义与初步准备 首先定义什么是回文串——即正读反读均相同的序列。为了简化输入输出过程并提高程序可移植性,这里假设已经通过标准输入获取了一个不含空白符的纯字母数字组成的字符串[^1]。 #### 实现思路 核心在于构建两个指针分别指向待检测字符串首尾两端并向中间靠拢比较对应位置上的字符是否相等直到相遇为止;如果过程中遇到不匹配的情况则立即返回不是回文的结果;反之当循环结束仍未发现差异时说明给定字符串确实构成回文特性。 ```c #include <stdio.h> #include <string.h> int isPalindrome(char str[]) { int i = 0; int j = strlen(str) - 1; // 获取字符串长度减去终止符'\0'的位置 while(i < j){ if(str[i++] != str[j--]) return 0; // 若有任意一对不同,则非回文 } return 1; // 所有对比项相同视为回文 } void main(){ char testStr[]="level"; if(isPalindrome(testStr)){ printf("%s 是回文。\n",testStr); }else{ printf("%s 不是回文。\n",testStr); } } ``` 上述代码展示了基本框架以及具体操作细节,其中`isPalindrome()`函数负责执行实际判定工作而`main()`部分用于测试验证目的。 #### 进一步优化建议 考虑到可能存在的大小写字母混杂情况或是其他特殊符号干扰因素,在正式比赛中应当先对原始数据做适当预处理比如统一转换成小写形式或者忽略掉所有非字母字符后再进行后续分析以确保准确性[^2]。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值