代码随想录:双指针法

移除元素

27. 移除元素 - 力扣(LeetCode) (leetcode-cn.com)

给你一个数组 nums 和一个值 val,你需要原地移除所有数值等于 val 的元素,并返回移除后数组的新长度

  • 不要使用额外的数组空间,$O(1) $额外空间
  • 元素的顺序可以改变。
  • 你不需要考虑数组中超出新长度后面的元素。

思路:快慢指针即可做

参考之前题目的移除元素写出解答:

class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        int slowIndex = 0;
        for (int fastIndex = 0; fastIndex < nums.size(); fastIndex++) {
            if (val != nums[fastIndex]) {
                nums[slowIndex++] = nums[fastIndex];
            }
        }
        return slowIndex;
    }
};

细心的张同学发现这里还学到了python装饰器@classmethod的使用,Day 26:Python 中的装饰器是什么? - PiaYie - 博客园 (cnblogs.com)

反转字符串

344. 反转字符串 - 力扣(LeetCode) (leetcode-cn.com)

编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 s 的形式给出。

不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。

老题目了

void reverseString(vector<char>& s) {
        for(int i=0,j=s.size()-1;i<s.size()/2;i++,j--){
            swap(s[i],s[j]);
        }
    }

  

剑指Offer 05.替换空格

剑指 Offer 05. 替换空格 - 力扣(LeetCode) (leetcode-cn.com)

请实现一个函数,把字符串 s 中的每个空格替换成"%20"。

思路:统计空格个数。重新设置长度,从后往前原地修改字符串,复杂度$O(n)$

class Solution {
public:
    string replaceSpace(string s) {
        int count=0;
        int oldSize = s.size();
        for(int i=0;i<oldSize;i++){
            if(s[i]==' '){
                count++;
            }
        }
        //重新修改大小
        s.resize(s.size()+2*count);
        //从后向前修改-双指针
        int newSize = s.size();
        for(int i=oldSize-1,j=newSize-1;i<j;i--,j--){
            if(s[i]!=' '){
                s[j]=s[i];
            }else{
                s[j]='0';
                s[j-1]='2';
                s[j-2]='%';
                j-=2;
            }
        }
        return s;
    }
};

翻转字符串里的单词

151. 翻转字符串里的单词 - 力扣(LeetCode) (leetcode-cn.com)

这道题在字符串里做过,思路:去除多余空格,反转整个字符串,反转每个单词中的所有字符

去除多余空格函数里,只有遇到不是空格,才用

s[slowIndex]=s[fastIndex]

  存下来。

class Solution {
public:
    void removeExtraSpaces(string& s) {
        int slowIndex = 0, fastIndex = 0; // 定义快指针,慢指针
        // 去掉字符串前面的空格
        while (s.size() > 0 && fastIndex < s.size() && s[fastIndex] == ' ') {
            fastIndex++;
        }
        for (; fastIndex < s.size(); fastIndex++) {
            // 去掉字符串中间部分的冗余空格
            if (fastIndex - 1 > 0
                    && s[fastIndex - 1] == s[fastIndex]
                    && s[fastIndex] == ' ') {
                continue;
            } else {
                s[slowIndex++] = s[fastIndex];
            }
        }
        if (slowIndex - 1 > 0 && s[slowIndex - 1] == ' ') { // 去掉字符串末尾的空格
            s.resize(slowIndex - 1);
        } else {
            s.resize(slowIndex); // 重新设置字符串大小
        }
    }

    // 反转字符串s中左闭又闭的区间[start, end]
    void reverse(string& s, int start, int end) {
        for (int i = start, j = end; i < j; i++, j--) {
            swap(s[i], s[j]);
        }
    }


    string reverseWords(string s) {
        removeExtraSpaces(s);
        //反转字符串
        reverse(s,0,s.size()-1);
        //反转每个单词,判断空格位置
        for(int i=0;i<s.size();i++){
            int j=i;
            while(j<s.size() && s[j]!=' ') j++;
            reverse(s,i,j-1);
            //更新要反转的单词开始位置
            i = j;
        }
        return s;

    }

};

反转链表

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

题意:反转一个单链表。

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

思路,双指针or递归法,参考:代码随想录:链表 - PiaYie - 博客园 (cnblogs.com) and 数据结构入门 - PiaYie - 博客园 (cnblogs.com)

双指针解法:

class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        //节点的值不变,只是改变next的指向
        ListNode* pre = nullptr;
        ListNode* cur = head;
        ListNode* temp = new ListNode();
        while(cur){
            temp = cur->next;
            cur->next = pre;
            pre = cur;
            cur = temp;
        }
        return pre;
    }
};

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

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

给一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。

进阶:使用一趟扫描实现 $O(n)$算法。

使用快慢指针,如果要删除的节点在中间,当快指针指向结尾的null时候,慢指针与快指针相隔n+1个节点,把慢指针slow->next删除即可

使用虚拟头节点+双指针方法:

class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
        ListNode* dummyHead = new ListNode(0);
        dummyHead->next = head;
        ListNode* fast = dummyHead;
        ListNode* slow = dummyHead;
        while(n-- && fast != nullptr){
            fast = fast->next;
        }
        fast = fast->next;
        while(fast != nullptr){
            slow = slow->next;
            fast = fast->next;
        }
        slow->next = slow->next->next;
        return dummyHead->next;
    }
};

链表相交

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

思路:如果两个人在部分or全部相交的跑道,速度不一样,那么他们一定会在相交的跑道上相遇:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        ListNode* A = headA, * B = headB;
        while (A != B) {//当没有相遇时候,会得到A->null == null<-B
            A = A != nullptr ? A->next : headB;
            B = B != nullptr ? B->next : headA;
        }
        return A;
    }
};

环形链表II

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

给定一个链表的头节点  head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。

不允许修改 链表。

思路:快慢指针,快指针走两步,慢指针走一步,并且一定会在慢指针进入第一圈环内相遇:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
        ListNode* fast = head;
        ListNode* slow = head;
        while(fast != NULL && fast->next != NULL){
            fast = fast->next->next;
            slow = slow->next;
            if(fast == slow){
                //如果相遇了  从head到相遇点之间,查找那个严格意义上的入口节点
                ListNode* node1 = fast;
                ListNode* node2 = head;
                while(node1 != node2){
                    node1 = node1->next;
                    node2 = node2->next;
                }
                return node1;
            }
        }

        
        return NULL;
    }
};

为什么相遇之后,执行下面的代码就会走到环的入口?

//如果相遇了  从head到相遇点之间,查找那个严格意义上的入口节点
                ListNode* node1 = fast;
                ListNode* node2 = head;
                while(node1 != node2){
                    node1 = node1->next;
                    node2 = node2->next;
                }
                return node1;
    

 x+y = n(z+y)

如果快指针只是转了一圈,n=1,那么 x=z,相遇点就是入口节点

如果快指针转了不止一圈,n>1,那么 x=nz+(n-1)y = (n-1)(z+y) + z,设置nodel1和nodel2之后,可能🙄相遇节点就不是第一个入口节点了,但是他还是入口节点

三数之和 and 四数之和

15. 三数之和 - 力扣(LeetCode) (leetcode-cn.com)

给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有和为 0 且不重复的三元组。

注意:答案中不可以包含重复的三元组

18. 四数之和 - 力扣(LeetCode) (leetcode-cn.com)

给你一个由 n 个整数组成的数组 nums ,和一个目标值 target 。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]] (若两个四元组元素一一对应,则认为两个四元组重复)

思路:hash表不是很好去重,选择双指针方法去重,参考:代码随想录:哈希表 - PiaYie - 博客园 (cnblogs.com)

posted @ 2022-02-24 14:11  PiaYie  阅读(42)  评论(0编辑  收藏  举报