首页 > 其他分享 >双指针

双指针

时间:2024-10-07 22:03:11浏览次数:1  
标签:映射 nums int height vector include 指针

双指针

  • 同向指针
  • 快慢指针
  • 从两端向中间的指针
  • 其他

922. 按奇偶排序数组 II

#include <vector>

using namespace std;

class Solution {
public:
    // 时间复杂度 O(n),额外空间复杂度 O(1)
    vector<int> sortArrayByParityII(vector<int> &nums) {
        int even = 0;
        int odd = 1;
        while (even < nums.size() - 1 && odd < nums.size()) {
            // 找到偶数下标,但元素是奇数的位置
            while (even < nums.size() - 1 && ((nums[even] & 1) == 0)) even += 2;
            // 找到奇数下标,但元素是偶数的位置
            while (odd < nums.size() && ((nums[odd] & 1) != 0)) odd += 2;
            // 交换
            if (even < nums.size() - 1 && odd < nums.size()) swap(nums[even], nums[odd]);
        }
        return nums;
    }
};

287. 寻找重复数

  • 要求 不修改 数组 nums 且只用常量级 O(1) 的额外空间。
#include <vector>

using namespace std;

class Solution {
public:
    // 时间复杂度 O(n),额外空间复杂度 O(1)
    // 类似环形链表找入口节点,nums 数组类似静态链表
    int findDuplicate(vector<int> &nums) {
        int slow = 0, fast = 0;
        slow = nums[slow];
        fast = nums[nums[fast]];
        while (slow != fast) {
            // slow = slow.next
            slow = nums[slow];
            // fast = fast.next.next
            fast = nums[nums[fast]];
        }
        fast = 0;
        while (slow != fast) {
            slow = nums[slow];
            fast = nums[fast];
        }
        return slow;
    }
};

42. 接雨水

  • 按行积累
#include <vector>

using namespace std;

class Solution {
public:
    // 按行累积,每次累加当前行上能接多少水(超时)
    int trap(vector<int> &height) {
        int n = height.size();
        // 找最大高度
        int maxHeight = 0;
        for (const auto &item: height)
            maxHeight = max(maxHeight, item);

        int res = 0;
        // 每次找一层,一格一格的加
        for (int level = 1; level <= maxHeight; ++level) {
            int i = 0;
            // 找到第一个不低于当前 lever 的作为左边界
            while (i < n && height[i] < level) i++;
            // 找不到左边界,这层以及上面的层都接不了水
            if (i >= n) break;

            for (int water = 0; i < n; i++) {
                if (height[i] < level) {
                    // 已有左边界,并且比当前层低,说明这个格子 (i, lever) 可以放水
                    water++;
                } else if (height[i] >= level) {
                    // 找到大于或等于当前层的右边界,就把之前累积的水加到结果中,并清空 water
                    // 当前的右边界变成下一个左边界,在继续寻找下一个右边界
                    res += water;
                    water = 0;
                }
            }
        }
        return res;
    }
};
  • 按列积累
#include <vector>

using namespace std;

class Solution {
public:
    // 时间复杂度 O(n),额外空间复杂度 O(n)
    // 按列累积,每次累加当前列上能接多少水
    int trap(vector<int> &height) {
        int n = height.size();
        int res = 0;

        // 记录当前元素左边的最大值
        int leftMax = height[0];
        // 可以在后续累积雨水时遍历数组的操作中,用 leftMax 优化掉 leftMaxArr
        vector<int> leftMaxArr(n);
        // 更新每个元素左边的最大值
        for (int i = 1; i <= n - 2; ++i) {
            leftMaxArr[i] = leftMax;
            leftMax = max(leftMax, height[i]);
        }

        // 记录当前元素右边的最大值
        int rightMax = height[n - 1];
        vector<int> rightMaxArr(n);
        for (int i = n - 2; i >= 1; i--) {
            rightMaxArr[i] = rightMax;
            rightMax = max(rightMax, height[i]);
        }

        // 只有左边最高的和右边最高的,二者中的较小者比当年的列高,当前列才能接得住水
        for (int i = 1; i <= n - 2; ++i)
            res += max(0, min(leftMaxArr[i], rightMaxArr[i]) - height[i]);
        return res;
    }
};
  • 双指针优化掉 leftMaxArr、rightMaxArr(最优解)
#include <vector>

using namespace std;

class Solution {
public:
    // 时间复杂度 O(n),额外空间复杂度 O(1)
    int trap(vector<int> &height) {
        int n = height.size();
        int l = 1;
        int r = n - 2;
        // 左边最高
        int lMax = height[0];
        // 右边最高
        int rMax = height[n - 1];

        int res = 0;
        while (l <= r) {
            if (lMax <= rMax) {
                // 左边的最高柱子较低些
                res += max(0, lMax - height[l]);
                lMax = max(lMax, height[l++]);
            } else {
                // 右边的最高柱子较低些
                res += max(0, rMax - height[r]);
                rMax = max(rMax, height[r--]);
            }
        }
        return res;
    }
};
#include <vector>
#include <stack>

using namespace std;

class Solution {
public:
    int trap(vector<int> &height) {
        int res = 0;
        stack<int> stk;

        for (int i = 0; i < height.size(); i++) {
            // 当前高度大于栈顶高度,说明之前的地方有可能能接水
            // 持续出栈,直到栈顶高度大于等于当前高度或者栈为空
            while (!stk.empty() && height[i] > height[stk.top()]) {
                int top = height[stk.top()];
                stk.pop();
                if (stk.empty()) break;
                // 两个柱子间的距离,不包含两端
                int distance = i - stk.top() - 1;
                // height[stk.top()] 为左边界,height[i] 为右边界
                int smaller = min(height[stk.top()], height[i]);
                res += distance * (smaller - top);
            }
            stk.emplace(i);
        }
        return res;
    }
};

881. 救生艇

#include <vector>
#include <algorithm>

using namespace std;

class Solution {
public:
    // 时间复杂度 O(n * logn),因为有排序,额外空间复杂度 O(1)
    int numRescueBoats(vector<int> &people, int limit) {
        sort(people.begin(), people.end());
        int res = 0;
        for (int l = 0, r = people.size() - 1; l <= r; res++) {
            int sum = l == r ? people[l] : people[l] + people[r];
            // 如果没超重,最轻的可以和最重的共用一条船
            if (sum <= limit) l++;
            r--;
        }
        return res;
    }
};

11. 盛最多水的容器

#include <vector>
#include <algorithm>

using namespace std;

class Solution {
public:
    // 时间复杂度 O(n),额外空间复杂度 O(1)
    int maxArea(vector<int> &height) {
        int res = 0;
        for (int l = 0, r = height.size() - 1; l < r;) {
            int water = (r - l) * min(height[l], height[r]);
            res = max(res, water);
            // 每次把短板往中间靠,短板可能变长,总面积才可能变大
            // 如果移动长板,底一定变小,高度不会超过之前的那个短板,高只会原来越低,面积只会变小
            if (height[l] < height[r]) {
                l++;
            } else {
                r--;
            }
        }
        return res;
    }
};

475. 供暖器

#include <vector>
#include <algorithm>

using namespace std;

class Solution {
public:
    // 返回当前的地点 houses[i] 由 heaters[j] 来供暖是否是最优
    // 当前的地点 houses[i] 由 heaters[j] 来供暖,产生的半径是a
    // 当前的地点 houses[i] 由 heaters[j + 1] 来供暖,产生的半径是 b
    // 如果 a < b, 说明是最优,供暖不应该跳下一个位置
    // 如果 a >= b, 说明不是最优,应该跳下一个位置
    bool best(vector<int> &houses, vector<int> &heaters, int i, int j) {
        return j == heaters.size() - 1
               || abs(heaters[j] - houses[i]) < abs(heaters[j + 1] - houses[i]);
    }

    // 时间复杂度 O(n * logn),因为有排序,额外空间复杂度 O(1)
    int findRadius(vector<int> &houses, vector<int> &heaters) {
        sort(houses.begin(), houses.end());
        sort(heaters.begin(), heaters.end());

        int res = 0;
        // i 号房屋,j 号供暖器
        for (int i = 0, j = 0; i < houses.size(); i++) {
            while (!best(houses, heaters, i, j)) j++;
            res = max(res, abs(heaters[j] - houses[i]));
        }
        return res;
    }
};

41. 缺失的第一个正数

  • 原地映射:把值为 value 的元素映射到数组中下标为 value - 1 的位置
#include <vector>

using namespace std;

class Solution {
public:
    // 原地映射:把值为 value 的元素映射到数组中下标为 value-1 的位置
    // 优化了对映射后覆盖掉的值的处理
    int firstMissingPositive(vector<int> &nums) {
        int n = nums.size();
        for (int i = 0; i < n; ++i) {
            // 如果 nums[i] 也能映射到数组中,并且尚未映射过
            while (nums[i] > 0 && nums[i] <= n && nums[nums[i] - 1] != nums[i]) {
                // 把 nums[i] 映射到 nums[nums[i] - 1],nums[nums[i] - 1] 放在 nums[i]
                // 然后继续判断新的 nums[i] 是否也需要映射
                swap(nums[nums[i] - 1], nums[i]);
            }
        }
        // 遍历寻找第一个空缺
        for (int i = 0; i < n; ++i)
            if (nums[i] != i + 1)
                return i + 1;
        // 重新映射后,数组中没有空缺,说明缺失的第一个正数就是 n + 1
        return n + 1;
    }
};
  • 原地映射:把可以映射到的地方的元素改成负数
#include <vector>
#include <valarray>

using namespace std;

class Solution {
public:
    // 原地映射:把可以映射到的地方的元素改成负数,映射规则还是 value 映射到 nums[value-1]
    int firstMissingPositive(vector<int> &nums) {
        int n = nums.size();
        // 非正数改成 n + 1
        for (int i = 0; i < n; ++i)
            if (nums[i] <= 0)
                nums[i] = n + 1;

        for (int i = 0; i < n; ++i) {
            // 待映射的值
            int value = abs(nums[i]);
            // 如果可以映射到数组里,就把映射到的地方的元素改成负数
            if (value <= n) nums[value - 1] = -abs(nums[value - 1]);
        }
        for (int i = 0; i < n; ++i)
            if (nums[i] > 0) 
                return i + 1;
        return n + 1;
    }
};
  • 双指针
#include <vector>

using namespace std;

class Solution {
public:
    // 时间复杂度 O(n),额外空间复杂度 O(1)
    int firstMissingPositive(vector<int> &nums) {
        // [0, l) 为已经映射的区域,l 是待处理的位置
        int l = 0;
        // [r, ...) 为无法映射的区域
        // r 的第二个含义是这 r 个数最好的情况下能映射到 [0, r-1]
        // 当有一个数字无法映射的时候,就剩下 r-1 个待映射的,最好情况下能映射到 [0, r-2]
        int r = nums.size();
        // [l, r) 为待映射的区域

        while (l < r) {
            if (nums[l] == l + 1) {
                // 已经映射好了,已映射区域右扩
                l++;
            } else if (nums[l] <= l || nums[l] > r || nums[nums[l] - 1] == nums[l]) {
                // 把映射不了的数字或者重复出现的数字,与后面待映射的数字交换,无法映射的区域左扩
                // nums[l] <= l 说明 nums[l] 已经映射好了,已经映射的区域中有重复
                // nums[l] > r 说明无法映射
                // nums[nums[l] - 1] == nums[l] 也是说明 nums[l] 已经映射好了,待映射区域中有重复
                swap(nums[l], nums[--r]);
            } else {
                // 可以映射
                swap(nums[l], nums[nums[l] - 1]);
            }
        }
        return l + 1;
    }
};

标签:映射,nums,int,height,vector,include,指针
From: https://www.cnblogs.com/sprinining/p/18450731

相关文章

  • c++指针传递与引用传递
    c不支持引用传递的!在C++中,指针传递和引用传递是两种常用的参数传递方式,它们各自有不同的特点和适用场景。下面是两者之间的主要区别:1.语法和使用指针传递定义和调用:函数参数是一个指针类型,调用时需要传递变量的地址。解引用:在函数内部需要使用解引用操作符*来访问指针......
  • C语言中指针解引用
    在32位的操作系统中,指针占据4个字节。在64位的操作系统中,指针占据8个字节。以下程序都是在32位操作系统中运行的。1.指针解引用程序运行结果为12345600进入调试,可以看到变量a在内存占4个字节,a的地址为0x008FF794,在内存中的存储方式为小端存储方式。当程序走完*p=0这一行......
  • C++函数指针详解
    概述本文详细介绍了C/C++中的普通函数和类的成员函数的指针。结合C++代码示例讲解了函数指针作为其他函数的输入、返回值以及typedef如何提高代码可读性的实用技巧。对于类的成员函数(方法)指针,则分为静态和非静态两种情况。最后总结了普通函数、类的非静态成员函数、类的静态成员......
  • C语言 函数指针
    概念在C语言中,函数指针是一种特殊的指针类型,它指向的是函数而不是普通的数据变量。函数在内存中有其入口地址,函数指针就是用来存储这个地址的变量。函数指针的定义函数指针的定义形式如下:返回值类型(*指针变量名)(参数类型列表);例如,定义一个指向返回值为int,参数为int......
  • C++指针等于地址加偏移量
    概述本文通过c++示例代码演示指针的加减法运算及对“指针=地址+偏移量”的理解。研究示例1.首先来检查各种变量类型所占的内存大小#include<iostream>usingnamespacestd;intmain(){ cout<<sizeof(char)<<endl;//1Byte cout<<sizeof(short)<<e......
  • 理解C语言之深入理解指针(四)
    目录1.回调函数是什么?2.qsort使⽤举例2.1使⽤qsort函数排序整型数据2.2使⽤qsort排序结构数据3.qsort函数的模拟实现1.回调函数是什么?        回调函数就是⼀个通过函数指针调⽤的函数。        如果你把函数的指针(地址)作为参数传递给另⼀个......
  • C++中指针和数组相关的运算符优先级
    概述本文深入介绍了与指针和数组相关的运算符优先级,利用代码示例展示了当左结合和右结合运算符同时存在时的结合方式,同时也演示了如何使用()来强制人为指定结合顺序。指针、数组相关的运算符优先级下表展示了相关运算符的优先级,有4个级别,同级别内的运算符按照结合性依次调用。......
  • 空指针处理方案
    空指针的bug处理出现A.xxxx的时候要考虑A如果为null的空指针异常//误报,此时first报空指针风险,但是不可能for(inti=0;i<size;i++){TreeNodefirst=queue.pollFirst();if(first.left!=null){queue.offerLast(first.left);} //处理前:re......
  • JS数组指针prev、current、next的实现方式,涉及是否删除当前元素的情况分析
    背景由于业务,需要做一个循环切换的轮播图效果,循环展示列表中的每个item,但是由于切换(从左往右移动,遇到末尾则跳到开头)的过程中可能会删掉当前元素,所以需要更新下标后再切换。由于涉及到几个临界条件,这里列出来处理方式,以便后续参考。代码这里给出的简化过后的代码:<template>......
  • 【C语言】手把手带你拿捏指针(完)(指针笔试、面试题解析)
    文章目录一、sizeof和strlen的对⽐1.sizeof2.strlen3.sizeof与strlen对比二、数组和指针笔试解析1.一维数组2.字符、字符串数组和字符指针代码1代码2代码3代码4代码5代码63.二维数组4.总结三、指针运算笔试题解析代码1代码2代码3代码4代码5代码6一、sizeof和strl......