C++数据结构与算法——双指针法

C++第二阶段------数据结构和算法,之前学过一点点数据结构,当时是基于Python来学习的,现在基于C++查漏补缺,尤其是树的部分。这一部分计划一个月,主要利用代码随想录来学习,刷题使用力扣网站,不定时更新,欢迎关注!

文章目录

一、移除元素(力扣27)

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

不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。

元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。

说明:

为什么返回数值是整数,但输出的答案是数组呢?

请注意,输入数组是以「引用」方式传递的,这意味着在函数里修改输入数组对于调用者是可见的。

你可以想象内部操作如下:

// nums 是以"引用"方式传递的。也就是说,不对实参作任何拷贝

int len = removeElement(nums, val);

// 在函数里修改输入数组对于调用者是可见的。

// 根据你的函数返回的长度, 它会打印出数组中 该长度范围内 的所有元素。

for (int i = 0; i < len; i++) {

print(nums[i]);

}

cpp 复制代码
class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        // 利用快慢指针进行删除
        // 1. 判断是否只有零个或一个元素,防止索引超出数组长度
        if(nums.size()==1){
            if(nums[0]==val){
                return 0;
            }
            else{
                return 1;
            }
        }
        if (nums.size()==0){
            return 0;
        }

        int slow =0;
        for(int fast=0;fast<nums.size();fast++){
            if(nums[fast]!=val){
                nums[slow] = nums[fast];
                slow++;
            }
        }
        return slow;
    }
};

二、反转字符串(力扣344)

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

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

示例 1:

输入:s = ["h","e","l","l","o"]

输出:["o","l","l","e","h"]

示例 2:

输入:s = ["H","a","n","n","a","h"]

输出:["h","a","n","n","a","H"]

提示:

1 <= s.length <= 105

s[i] 都是 ASCII 码表中的可打印字符

cpp 复制代码
class Solution {
public:
    void reverseString(vector<char>& s) {
        // 双指针解决
        int begin =0;
        int end = s.size()-1;
        while(begin<end){
            // 交换
            char temp = s[begin];
            s[begin] = s[end];
            s[end] = temp;
            begin++;
            end--;
        }
    }
};

三、替换数字(卡码网 54)

题目描述

给定一个字符串 s,它包含小写字母和数字字符,请编写一个函数,将字符串中的字母字符保持不变,而将每个数字字符替换为number。 例如,对于输入字符串 "a1b2c3",函数应该将其转换为 "anumberbnumbercnumber"。

输入描述

输入一个字符串 s,s 仅包含小写字母和数字字符。

输出描述

打印一个新的字符串,其中每个数字字符都被替换为了number

输入示例

a1b2c3

输出示例

anumberbnumbercnumber

提示信息

数据范围:

1 <= s.length < 10000。

cpp 复制代码
#include <iostream>
using namespace std;
 
 
int main() {
    string inputS;
    cin >> inputS;
    int count = 0;
    // 遍历原来数组,记录其中数字的个数
    for (int i = 0; i < inputS.length(); i++) {
        if (inputS[i] <= '9' && inputS[i]>='0') {
            count++;
        }
    }
    int FLength = inputS.length();
    // resize 原来的字符串
    inputS.resize(FLength + count * 5);
    // 替换原来字符串中的字符,从后向前替换
    int j = inputS.length()-1;
    for (int i = FLength - 1; j>i; i--,j--) {
        if (inputS[i] <= '9' && inputS[i]>='0') {
            // 是数字要替换
            inputS[j] = 'r';
            inputS[j-1] = 'e';
            inputS[j-2] = 'b';
            inputS[j-3] = 'm';
            inputS[j-4] = 'u';
            inputS[j-5] = 'n';
            j -= 5;
        }
        else {
            inputS[j] = inputS[i];
        }
    }
    cout << inputS << endl;
    system("pause");
    return 0;
 
}

四、翻转字符串里的单词(力扣151)

给定一个字符串,逐个翻转字符串中的每个单词。

示例 1:

输入: "the sky is blue"

输出: "blue is sky the"

示例 2:

输入: " hello world! "

输出: "world! hello"

解释: 输入字符串可以在前面或者后面包含多余的空格,但是反转后的字符不能包括。

示例 3:

输入: "a good example"

输出: "example good a"

解释: 如果两个单词间有多余的空格,将反转后单词间的空格减少到只含一个。

cpp 复制代码
class Solution {
public:
    string reverseWords(string s) {
        // 去除空格
        removeSpace(s);
        // // 翻转整个句子
        reverse(s.begin(), s.end());
        // 翻转每个单词
        // 首先找到每个单词的位置
        int slow=0;
        for(int fast=0;fast<s.length();fast++){
            if(s[fast+1]==' ' || fast==s.length()-1){
                reverseWord(s,slow,fast);
                slow = fast+2;
            }
        }
        return s;
    }
    // 翻转每个单词
    void reverseWord(string& s, int begin, int end) {
        while (begin < end) {
            swap(s[begin], s[end]);
            begin++;
            end--;
        }
    }
    // 去除字符串中多余的空格
    void removeSpace(string& s) {
        // 快慢指针去除空格
        int slow = 0;
        for (int fast = 0; fast < s.length(); fast++) {
            if (s[fast] != ' ') {
                // 不为空格才进行下面的操作
                // 添加每个单词之间的空格,句子开头不加
                if (slow != 0) {
                    s[slow] = ' ';
                    slow++;
                }
                while (fast<s.size() && s[fast] != ' ') {
                    s[slow] = s[fast];
                    slow++;
                    fast++;
                }
            }
        }
    s.resize(slow);
    }

};

五、反转链表(力扣206)

cpp 复制代码
/**
 * Definition for singly-linked list.
 * 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) {}
 * };
 */
class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        if(head==NULL||head->next==NULL){
            // 只有一个结点
            return head;
        }
        // 定义两个指针,一个指前面的,一指后面的
        ListNode *pre = NULL;
        ListNode *cur = head;
        while(cur!=NULL){
            // 用一个值去接收cur
            ListNode *temp = cur->next;
            cur->next = pre;
            pre = cur;
            cur = temp;
        }
        return pre;
    }

};

六、删除链表的倒数第 N 个结点(力扣19)

cpp 复制代码
/**
 * Definition for singly-linked list.
 * 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) {}
 * };
 */
class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
        if (head==NULL){
            return head;
        }

        ListNode * dummyNode = new ListNode(0);
        dummyNode->next = head;
        // 使用快慢指针
        ListNode* fast = dummyNode;
        ListNode* slow = dummyNode;
        while(n--){
            // 快指针先走n个
            fast = fast->next;
        }
        while(fast!=NULL&&fast->next!=NULL){
            fast = fast->next;
            slow = slow->next;
        }
        // 删除元素
        ListNode * temp = slow->next;
        slow->next = slow->next->next;
        delete temp; // 删除
        return dummyNode->next;
    }
};

七、链表相交(力扣面试题 02.07)

cpp 复制代码
/**
 * 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) {
        // 实际上是找相等的结点,不是值相等,而是 是同一个结点
        // 将两个链表右对齐
        // 求两个链表的长度
        if(headA==NULL||headB==NULL){
            return NULL;
        }
        int lenA =1;
        int lenB =1;
        ListNode * curA = headA;
        ListNode * curB = headB;
        while(curA->next!=NULL){
            lenA++;
            curA = curA->next;
        }
        while(curB->next!=NULL){
            lenB++;
            curB = curB->next;
        }
        // 找最短的链表
        int minLen =min(lenA,lenB);
        // AB需要移动的长度
        int needA= lenA-minLen;
        int needB = lenB - minLen;
        // 右对齐两个节点
        while(needA--){
            headA = headA->next;
        }
        while(needB--){
            headB = headB->next;
        }
        // 比较之后的链表是否相同
        while(headA!=NULL||headB!=NULL){
            if(headA== headB){
                return headA;
            }
            else{
                headA = headA->next;
                headB = headB ->next;
            }
        }
        return NULL;
    }
};

八、环形链表 II(力扣142)

cpp 复制代码
/**
 * 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 * first = head;
        ListNode * slow = head;
        while(first!=NULL&&first->next!=NULL){
            first = first->next->next;
            slow = slow->next;
            if(first==slow){
                // 此时相遇
                ListNode * index1 = first;
                ListNode * index2 = head;
                // 找入环口
                while(index1!=index2){
                    index1 = index1->next;
                    index2 = index2->next;
                }
                return index1;
            }
        }
        return NULL;

    }
};

九、三数之和(力扣15)

给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != j、i != k 且 j != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请

你返回所有和为 0 且不重复的三元组。

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

示例 1:

输入:nums = [-1,0,1,2,-1,-4]

输出:[[-1,-1,2],[-1,0,1]]

解释:

nums[0] + nums[1] + nums[2] = (-1) + 0 + 1 = 0 。

nums[1] + nums[2] + nums[4] = 0 + 1 + (-1) = 0 。

nums[0] + nums[3] + nums[4] = (-1) + 2 + (-1) = 0 。

不同的三元组是 [-1,0,1] 和 [-1,-1,2] 。

注意,输出的顺序和三元组的顺序并不重要。

示例 2:

输入:nums = [0,1,1]

输出:[]

解释:唯一可能的三元组和不为 0 。

示例 3:

输入:nums = [0,0,0]

输出:[[0,0,0]]

解释:唯一可能的三元组和为 0 。

cpp 复制代码
class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        // 先对数组排序
        sort(nums.begin(),nums.end());
        // 定义结果数组
        vector<vector<int>> result;
        // 剪枝操作
        if(nums[0]>0){
            return {}; // 第一个元素都大于0了,说明不可能加起来等于0,直接返回空
        }
        for(int i=0;i<nums.size();i++){
            // 定义两个指针
            int begin = i+1;
            int end = nums.size()-1;
            // 去重操作
            if(i>0&&nums[i]==nums[i-1]){
                // 当前的i和i-1的元素是一样的
                continue; // 跳过当前循环
            }
            while(begin<end){
                if((nums[i]+nums[begin]+nums[end])==0){
                    result.push_back({nums[i],nums[begin],nums[end]});
                    while(begin<end&&nums[begin]==nums[begin+1]){
                        begin++;
                    }
                    while(begin<end&&nums[end]==nums[end-1]){
                        end--;
                    }
                    begin++;
                    end--;
                }
                else if((nums[i]+nums[begin]+nums[end])>0){
                    end--;
                }
                else{
                    begin++;
                }

            }


        }
        return result;
    }
};

十、四数之和(力扣18)

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

0 <= a, b, c, d < n

a、b、c 和 d 互不相同

nums[a] + nums[b] + nums[c] + nums[d] == target

你可以按 任意顺序 返回答案 。

示例 1:

输入:nums = [1,0,-1,0,-2,2], target = 0

输出:[[-2,-1,1,2],[-2,0,0,2],[-1,0,0,1]]

示例 2:

输入:nums = [2,2,2,2,2], target = 8

输出:[[2,2,2,2]]

提示:

1 <= nums.length <= 200

-109 <= nums[i] <= 109

-109 <= target <= 109

cpp 复制代码
class Solution {
public:
    vector<vector<int>> fourSum(vector<int>& nums, int target) {
        // 先排序
        sort(nums.begin(),nums.end());
        // 定义最终返回数组
        vector<vector<int>> result;
        // 类似于三数之和,多了一层循环
        for (int i=0;i<nums.size();i++){
            // 剪枝
            if(nums[0]>=0&&nums[0]>target){
                break;
            }
            // 去重
            if(i>0&&nums[i]==nums[i-1]){
                continue;
            }
            for(int j=i+1;j<nums.size();j++){
                // 剪枝
                if(nums[i]+nums[j]>target&&nums[j]>=0){
                    break;
                }
                // 去重
                if(j>i+1&&nums[j]==nums[j-1]){
                    continue;
                }
                // 类似于三数之和
                int begin = j+1;
                int end = nums.size()-1;
                while(begin<end){
                    if((long) nums[i]+nums[j]+nums[begin]+nums[end]==target){
                        // 找到符合要求的数组,记录
                        result.push_back({nums[i],nums[j],nums[begin],nums[end]});
                        while(begin<end&&nums[begin]==nums[begin+1]){
                            begin++;
                        }
                        while(begin<end&&nums[end]==nums[end-1]){
                            end--;
                        }
                        begin++;
                        end--;
                    }
                    else if((long)nums[i]+nums[j]+nums[begin]+nums[end]>target){
                        end--;
                    }
                    else{
                        begin++;
                    }
                }
            }
        }
        return result;
    }
};
相关推荐
金灰3 分钟前
HTML5--裸体回顾
java·开发语言·前端·javascript·html·html5
菜鸟一皓3 分钟前
IDEA的lombok插件不生效了?!!
java·ide·intellij-idea
爱上语文6 分钟前
Java LeetCode每日一题
java·开发语言·leetcode
bug菌29 分钟前
Java GUI编程进阶:多线程与并发处理的实战指南
java·后端·java ee
程序猿小D42 分钟前
第二百六十九节 JPA教程 - JPA查询OrderBy两个属性示例
java·开发语言·数据库·windows·jpa
良月澪二1 小时前
CSP-S 2021 T1廊桥分配
算法·图论
极客先躯2 小时前
高级java每日一道面试题-2024年10月3日-分布式篇-分布式系统中的容错策略都有哪些?
java·分布式·版本控制·共识算法·超时重试·心跳检测·容错策略
夜月行者2 小时前
如何使用ssm实现基于SSM的宠物服务平台的设计与实现+vue
java·后端·ssm
程序猿小D2 小时前
第二百六十七节 JPA教程 - JPA查询AND条件示例
java·开发语言·前端·数据库·windows·python·jpa
wangyue42 小时前
c# 线性回归和多项式拟合
算法