代码随想录:双指针法

这一章类似于复习。
一天1-2题
要求:
1.使用c++中的debug语句判断。
2.调用库函数时尽量知道其原理。

1.移除元素

这个算是复习,但是我的第一反应也想不起来。
1.我首先想到的是:把要删除的元素移到尾部。
2.后来复习代码随想录的讲解之后,使用代码随想录的想法,使用的是while循环。
3.代码随想录中,for循环写的效果非常好,非常精简。

我的想法:
class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        int len = nums.size();
        int slowIndex = 0;
        int fastIndex = len - 1;
        while (slowIndex<=fastIndex) {
            if (nums[slowIndex] == val)
            {
                while(nums[slowIndex] == nums[fastIndex])
                {
                    if (fastIndex ==slowIndex) {
                        return slowIndex;
                    }
                    fastIndex--;
                }
                nums[slowIndex++] = nums[fastIndex--];
            }
            else {
                slowIndex++;
            }
        }
        return slowIndex;
    }
};
我回顾代码随想录的想法后:
class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        int len = nums.size();
        int slowIndex = 0;
        int fastIndex = 0;
        while(fastIndex<len) {
            if(nums[fastIndex] == val) {
                nums[slowIndex] = nums[fastIndex++];
            }
            else {
                nums[slowIndex++] = nums[fastIndex++];
            }
        }
        return slowIndex;
    }
};
代码随想录的写法:
class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        int len = nums.size();
        int slowIndex = 0;
        for(int fastIndex = 0; fastIndex < len; fastIndex++) {
            if (nums[fastIndex] != val) {
                nums[slowIndex++] = nums[fastIndex];
            }
        }
        return slowIndex;
    }
};

2.反转字符串

这个就比较简单了。

class Solution {
public:
    void reverseString(vector<char>& s) {
        for (int left = 0, right = s.size() - 1; left < s.size() / 2; left++, right--) {
            swap(s[left],s[right]);
        }
    }
};

3.替换数字

很简单:不过单个字符应该用单引号括起来,不能用双引号。

#include <iostream>
using namespace std;
int main() {
    string inputs;
    cin >> inputs;//遇到空格会自动结束
    int len  = inputs.size();
    int nums{ 0 };
    for (int i = 0; i < len; i++) {
        if (inputs[i] >= '0' && inputs[i] <= '9') {
            nums++;
        }
    }
    //修改总字符串长度,C++的string有这个功能
    inputs.resize(nums * 5 + len);
    int tailIndex = inputs.size() - 1; 
    //字符串长度修改成功
    for (int i = len-1; i >= 0; i--) {
        if (inputs[i] >= '0' && inputs[i] <= '9') {
            inputs[tailIndex--] = 'r';
            inputs[tailIndex--] = 'e';
            inputs[tailIndex--] = 'b';
            inputs[tailIndex--] = 'm';
            inputs[tailIndex--] = 'u';
            inputs[tailIndex--] = 'n';
        }
        else {
            inputs[tailIndex--] = inputs[i];
        }
    }
    cout << inputs;
    return 0;
}

4.反转字符串中的单词

难点还是删除多余的空格这部分比较麻烦,不好理解。
两个思路:
1.是将连续空格放在while中处理
2.将连续字符串放在while中处理(这种简洁很多)

class Solution {
public:
    string reverseWords(string s) {
    //两个思路,先清除所有空格,再缩减尺寸,再整体反转,再遇到空格反转
    //第一步:清除空格
        int len = s.size();
        int updateIndex{ 0 };
        //用双指针法
        for (int i = 0; i < len; i++) {
            if (s[i] != ' ') {
                if(updateIndex > 0) s[updateIndex++] = ' ';
                while (s[i] != ' '&& i < len) {
                    s[updateIndex++] = s[i++];
                }
            }
        }
        //缩减长度
        s.resize(updateIndex);
        //反转
        reverse(s.begin(),s.end());
        //发现空格即反转
        int start{0};
        for(int i = 0; i < s.size(); i++) {
            if (s[i] == ' ') {
                reverse(s.begin() + start,s.begin() + i);
                start = i + 1;
            }
            if (i == s.size() - 1) {
                reverse(s.begin() + start,s.end());
            }
        }
        return s;
    }
};

5.翻转链表

这次是第二次做,顺序循环可以做到,但是两个递归方法确实不好理解,以后还得加深这道题的印象,值得反复。尤其是两个递归方法。
警醒:以后遇到可顺序循环的地方努力尝试去用递归算法。思路比较特殊,值得掌握学习。

顺序循环版本:
class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        //1.正常顺序 2.递归 3.递归
        ListNode* pre = nullptr;
        ListNode* temp;
        while (head!=nullptr) {
            temp = head;
            head = head->next;
            temp->next = pre;
            pre = temp;
        }
        return pre;
    }
};
递归版本1:
递归书写先考虑终止条件再按顺序编写
class Solution {
public:
    ListNode* reverse(ListNode* prev, ListNode* head) {
        //1.正常顺序 2.递归 3.递归
        //终止条件
        if(head == nullptr){
            return prev;
        }
        ListNode* temp;
        temp = head;
        head = head->next;
        temp->next = prev;
        prev= temp;
        return reverse(prev, head);
    }
    ListNode* reverseList(ListNode* head) {
        //1.正常顺序 2.递归 3.递归
        return reverse(nullptr,head);
    }
};
递归版本3:直接从末尾向前反转,不好理解,但是思路非常简洁。
class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        //1.正常顺序 2.递归 3.递归
        //终止条件:
        if(head == nullptr && head -> next = nullptr) {
            return head;
        }
        ListNode* newhead = reverseList(head->next);
        head->next->next = head;
        head->next = nullptr;
        return newhead;
    }
};

6.删除链表倒数第n个节点

特别要考虑的点是:while (fastNode -> next != nullptr) 以及while (fastNode != nullptr)这两个边缘条件要考虑清楚。

class Solution {
public:
	ListNode* removeNthFromEnd(ListNode* head, int n) {
		ListNode* dummyNode = new ListNode();
        dummyNode->next = head;
        ListNode* slowNode = dummyNode;
        ListNode* fastNode = dummyNode;
        ListNode* deleteNode = nullptr;
        while (n--) {
            fastNode = fastNode -> next;
        }
        while (fastNode -> next != nullptr) {
            fastNode = fastNode->next;
            slowNode = slowNode->next;
        }
        deleteNode = slowNode -> next;
        slowNode->next = slowNode->next->next;
        delete deleteNode;
        head = dummyNode->next;
        delete dummyNode;
        return head;
	}
};

7.链表相交

思路很妙。

class Solution {
public:
	ListNode* getIntersectionNode(ListNode* headA, ListNode* headB) {
		//代码随想录中的思路太妙了
		int headALen{ 0 }, headBLen{ 0 };
        ListNode* aList = headA;
        ListNode* bList = headB;
        while (aList != nullptr) {
            headALen++;
            aList= aList -> next;
        }
        while (bList != nullptr) {
            headBLen++;
            bList = bList -> next;
        }
        int cha = abs(headALen - headBLen);
        ListNode* searchNodeLong = headALen >= headBLen ? headA : headB;
        ListNode* searchNodeShort = headALen >= headBLen ? headB : headA;
        while (cha--) {
            searchNodeLong = searchNodeLong -> next;
        } 
        while (searchNodeLong != nullptr) {
            if (searchNodeLong == searchNodeShort) {
                return searchNodeLong;
            }
            searchNodeLong=searchNodeLong -> next;
            searchNodeShort = searchNodeShort -> next;
        }
        return NULL;
    }
};

8.环形链表II

重点关注一下这道题,比较复杂。

class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
		//用快慢指针的跟踪方法
        ListNode* fast = head;
        ListNode* slow = head;
        while(fast != nullptr && fast->next != nullptr) {
            fast = fast->next->next;
            slow = slow->next;
            if(fast == slow) {
                ListNode* index1 = fast;
                ListNode* index2 = head;
                while (index1!=index2) {
                    index1 = index1->next;
                    index2 = index2->next;
                }
                return index1;
            }
        }
        return nullptr;
    }
};

9.三数之和

二刷这道题的时候,还是没能独立解决掉,依然用到的是代码随想录的想法:
重难点:
1.去重(第一个数字,第二个数字,第三个数字)
2.&&和||的写法:当&&的前半部分为0,则直接为0;当||前半部分为1,则直接为1.
举例:
第一种:while (left < right && nums[left] == nums[left + 1] )
第二种:while (nums[left] == nums[left + 1]&&left < right )//堆栈溢出风险
第二种写法是有堆栈溢出的风险的。
很重要的一道题,非常值得多,反复思考,编写。

class Solution {
public:
	vector<vector<int>> threeSum(vector<int>& nums) {  //三数之和,输入是一个数组,输出是一组三元组
		vector<vector<int>> result;
        sort(nums.begin(),nums.end());//升序排序
        for (int i = 0; i < nums.size()-2; i++) {
            if (nums[i] > 0) {
                //不用再继续找下去了
                return result;//还得修改
            }
            if (i > 0 && nums[i] == nums[i-1]) {
                continue;
            }
            int left = i + 1;
            int right = nums.size() - 1;
            while (left < right) {
                if (nums[i] + nums[left] + nums[right] > 0) {
                    right--;
                }
                else if (nums[i] + nums[left] + nums[right] < 0) {
                    left++;
                }
                else {
                    result.push_back({nums[i], nums[left], nums[right]});
                    while (left < right && nums[left] == nums[left + 1] ) {
                        left++;
                    }
                    while (left < right && nums[right] == nums[right - 1]) {
                        right--;
                    }
                    left++;
                    right--;
                }
            } 
        }
        return result;	
	}
};

10.四数之和

跟三数之和是一个思路,唯一 要多考虑的是target可以为负数。
重要的思路:1.left和right的设定,两端往中间夹得方式效率是最高的。2.去重的思路很重要

class Solution {
public:
	vector<vector<int>> fourSum(vector<int>& nums, int target) {
        //跟三数之和有异曲同工之妙
        vector<vector<int>> result;
        //先对整体进行排序
        sort(nums.begin(), nums.end());
        if (nums.size() < 4) {
            return result;
        }//这个判断是用来处理短数组的,虽然下面的for循环已经把这种情况包含在内了,这段代码其实可以不加的
        for (int i = 0; i < nums.size(); i++) {//注意nums.size()不要再去减去某值了,减去某值必须要考虑到数组长度。
            if (target >= 0 && nums[i] > target) {
                return result;
            }
            if (i > 0 && nums[i] == nums[i - 1]) {
                continue;
            }
            for (int j = i + 1; j < nums.size(); j++) {
                if(target - nums[i] >= 0 && nums[j] > target - nums[i]) {
                    break;
                }
                if( j > i + 1 && nums[j] == nums[j - 1]) {
                    continue;
                }
                int left = j + 1;
                int right = nums.size() - 1;
                while (left < right) {
                    if (static_cast<long>(nums[i]) + nums[j] + nums[left] + nums[right] > target) {
                        right--;
                    }
                    else if (static_cast<long>(nums[i]) + nums[j] + nums[left] + nums[right] < target) {
                        left++;
                    }
                    else {
                        result.push_back({nums[i], nums[j], nums[left], nums[right]});
                        left++;
                        right--;
                        while (left < right && nums[left] == nums[left - 1]) {
                            left++;
                        }
                        while (left < right && nums[right] == nums[right + 1]) {
                            right--;
                        }
                    }
                }
            }
        }
        return result;
	}
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值