目录
剑指 Offer 48. 最长不含重复字符的子字符串(主从I型)
力扣 76. 最小覆盖子串(对象空间的主从II型,子空间的等距指针)
力扣 340. 至多包含 K 个不同字符的最长子串(主从II型)
一,双指针
有很多完全不同的题目,根据他们的解法,都可以归结为双指针的题目。
大概模型:
用指针1扫描数组1,指针2扫描数组2,两个指针一起扫描,每次只有1个指针往后挪,每次挪哪个取决于数组的值和要求的目标,
当2个数组都扫描完(有的问题是只需要一个数组扫描完)时,要求的目标就求出来了。
代码特性:
双指针的代码往往是两层循环,虽然实际只是线性扫描,但是如果写错了就可能变成暴力,甚至死循环。
PS:数组1和数组2有可能是同一个数组
二,非遗忘算法
参考《算法导论》中遗忘比较交换算法 常见排序算法_nameofcsdn的博客-CSDN博客
我们可以把双指针表述为,它是一个彻底的非遗忘算法,即扫描方式取决于数组的值,而一般的扫描数组的方法都属于(或几乎属于)遗忘算法。
三,3种主从型双指针
主从型双指针指的是,主指针的扫描方式是遗忘扫描,即从头到尾扫描一遍,
主指针每挪动一步,从指针都要判断是不挪动,还是挪动一步或者多步,但是从指针都不超过主指针。
PS:即使是搜索2个低位对称的数组,也有可能采用主从型双指针,比如查找两数之和的问题。
(1)退化型双指针:等距指针
假设往右是主指针,往下是从指针
(2)主从I型:主指针每次移动1步,从指针每次移动最多1步
典型代码:for里面套if
(3)主从II型:主指针每次移动1步,从指针每次移动0步,1步,或多步
折线不能越过对角线,表达了从指针都不超过主指针。
有些场景可能严格一点,要求从指针一直小于主指针,这些细节无关紧要。
典型代码:for里面套while
剑指 Offer 48. 最长不含重复字符的子字符串(主从I型)
题目:
给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。
示例 1:
输入: "abcabcbb"
输出: 3
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
示例 2:
输入: "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。
示例 3:
输入: "pwwkew"
输出: 3
解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。
请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。
代码:
class Solution {
public:
int lengthOfLongestSubstring(string s) {
if(s=="")return 0;
int ans=1,left=0;
for(int i=0;i<s.length();i++){
for(int j=left;j<i;j++)if(s[j]==s[i])left=j+1;
ans=max(ans,i-left+1);
}
return ans;
}
};
CSU 1343 Long Long(主从II型)
题目:
Description
现在有两个单调递增序列,第一个序列有N个整数,第二个序列有M个整数,现在你可以从第一个序列中选一个数x,然后从第二个序列中选一个数y,那么有多少种情况满足x+y<=K呢?
Input
输入包含多组数据。
对于每组测试数据,第一行包含两个整数N, M , K (1 <= N, M <= 105, 1 <= K <= 109),含义同上。接下来一行包含N个在[1, 109]范围内的整数,依次描述了第一个序列中的各个整数。再接下来一行包含M个在[1, 109]范围内的整数,依次描述了第二个序列中的各个整数。
Output
对于每组数据,输出有多少种情况满足x + y <= K。
Sample Input
2 3 5
3 7
3 5 7
4 5 6
1 2 3 4
1 2 3 4 5
4 5 9
1 2 3 4
1 2 3 4 5
Sample Output
0
14
20
#include <iostream>
#include<algorithm>
using namespace std;
int listn[100000];
int listm[100000];
int main()
{
ios_base::sync_with_stdio(false);
int n, m, k, temp;
while (cin >> n >> m >> k)
{
for(int i = 0; i < n; i++)cin >> listn[i];
for (int i = 0; i < m; i++)
{
cin >> temp;
listm[m - 1 - i] = k - temp;
}
long long sum = m;
sum *= n;
int key = 0;
for (int i = 0; i < n; i++)
{
while (listm[key] < listn[i] && key < m)key++; //双指针
sum -= key;
}
cout << sum << endl;
}
return 0;
}
力扣 30. 串联所有单词的子串
给定一个字符串 s
和一个字符串数组 words
。 words
中所有字符串 长度相同。
s
中的 串联子串 是指一个包含 words
中所有字符串以任意顺序排列连接起来的子串。
- 例如,如果
words = ["ab","cd","ef"]
, 那么"abcdef"
,"abefcd"
,"cdabef"
,"cdefab"
,"efabcd"
, 和"efcdab"
都是串联子串。"acdbef"
不是串联子串,因为他不是任何words
排列的连接。
返回所有串联子串在 s
中的开始索引。你可以以 任意顺序 返回答案。
示例 1:
输入:s = "barfoothefoobarman", words = ["foo","bar"]
输出:[0,9]
解释:因为 words.length == 2 同时 words[i].length == 3,连接的子字符串的长度必须为 6。
子串 "barfoo" 开始位置是 0。它是 words 中以 ["bar","foo"] 顺序排列的连接。
子串 "foobar" 开始位置是 9。它是 words 中以 ["foo","bar"] 顺序排列的连接。
输出顺序无关紧要。返回 [9,0] 也是可以的。
示例 2:
输入:s = "wordgoodgoodgoodbestword", words = ["word","good","best","word"]
输出:[]
解释:因为 words.length == 4 并且 words[i].length == 4,所以串联子串的长度必须为 16。
s 中没有子串长度为 16 并且等于 words 的任何顺序排列的连接。
所以我们返回一个空数组。
示例 3:
输入:s = "barfoofoobarthefoobarman", words = ["bar","foo","the"] 输出:[6,9,12] 解释:因为 words.length == 3 并且 words[i].length == 3,所以串联子串的长度必须为 9。 子串 "foobarthe" 开始位置是 6。它是 words 中以 ["foo","bar","the"] 顺序排列的连接。 子串 "barthefoo" 开始位置是 9。它是 words 中以 ["bar","the","foo"] 顺序排列的连接。 子串 "thefoobar" 开始位置是 12。它是 words 中以 ["the","foo","bar"] 顺序排列的连接。
提示:
1 <= s.length <= 104
1 <= words.length <= 5000
1 <= words[i].length <= 30
words[i]
和s
由小写英文字母组成
class Solution {
public:
vector<int> findSubstring(string s, vector<string>& words) {
int id = 0;
map<string, int>m;
for (auto &s : words)if (m[s] == 0)m[s] = ++id;
vector<int>num(id + 1);
for (auto &s : words)num[m[s]]++;
int len = words[0].length();
vector<int>ids;
vector<int>ans;
for (int i = 0; i < len; i++) {
vector<int>num2(id + 1);
ids.clear();
for (int j = i; j <= int(s.length()) - len; j += len) {
auto si = s.substr(j, len);
ids.push_back(m[si]);
}
for (int low = 0, high = 0; high < ids.size(); high++) {
num2[ids[high]]++;
while (num2[ids[high]] > num[ids[high]])--num2[ids[low++]];
if (high - low == words.size() - 1)ans.push_back(low*len + i);
}
}
return ans;
}
};
力扣 76. 最小覆盖子串(对象空间的主从II型,子空间的等距指针)
题目:
给你一个字符串 S、一个字符串 T,请在字符串 S 里面找出:包含 T 所有字母的最小子串。
示例:
输入: S = "ADOBECODEBANC", T = "ABC"
输出: "BANC"
说明:
如果 S 中不存这样的子串,则返回空字符串 ""。
如果 S 中存在这样的子串,我们保证它是唯一的答案。
思路:
首先记录t中每个字符出现的次数,比如假设字符A一共有3个,
然后扫描s,记录以当前字符结尾的包含3个A的最短子串长度,也就是说从这个字符往前第3个A的位置。
对于每个字符都有这么一个位置,即往前多少字符才有足够的字符,
取其中最大者就是所有字符都足够的最小长度。
实现方法一:只用数组
C语言AC代码:
#define MAX 1234567890
int getMin(int x[128])
{
int ans = MAX;
int i;
for (i = 0; i < 128; i++){
if (ans > x[i] && x[i] >= 0){
ans = x[i];
}
}
return ans;
}
char * minWindow(char * s, char * t){
int numOfLetter[128];//numOfLetter表示每个字符的数量
int startLocOfLetter[128];//startLocOfLetter表示每个字符的第一个出现位置
int startLoc, tempStartLoc;
int length = MAX;
bool haveLetter[128];//haveLetter表示每个字符是否出现过
int sumOfLetter = 0;//一共有多少不同的字符
int i;
for (i = 0; i < 128; i++){
numOfLetter[i] = 0;
startLocOfLetter[i] = -1;
haveLetter[i] = false;
}
char *tmp = t;
while (*tmp != '\0'){
numOfLetter[*tmp]++; //初始化numOfLetter
haveLetter[*tmp] = true;
tmp++;
}
for (i = 0; i < 128; i++){
if (haveLetter[i]){
sumOfLetter++;
}
}
for (i = 0; s[i] != '\0'; i++){
if (!haveLetter[s[i]]){
continue;
}
numOfLetter[s[i]]--;
if (numOfLetter[s[i]] < 0 || startLocOfLetter[s[i]] < 0){
while (true){
startLocOfLetter[s[i]]++; //滑动startLocOfLetter
if (s[startLocOfLetter[s[i]]] == s[i]){
break;
}
}
}
if (numOfLetter[s[i]] == 0){
sumOfLetter--; //每当一个字母的数量足够时,sumOfLetter减1
}
if (sumOfLetter <= 0){ //sumOfLetter小于0表示所有字母的个数已经足够
tempStartLoc = getMin(startLocOfLetter);
if (length > i - tempStartLoc + 1){
length = i - tempStartLoc + 1;
startLoc = tempStartLoc;
}
}
}
if (length == MAX){
length = 0;
}
static char *ans;
ans = (char*)malloc(sizeof(char)*(length + 1));
for (i = 0; i<length; i++){
ans[i] = s[i + startLoc];
}
ans[length] = '\0';
return ans;
}
C++语言AC代码:
#include<string>
#define MAX 1234567890
int getMin(int x[128])
{
int ans = MAX;
int i;
for (i = 0; i < 128; i++){
if (ans > x[i]&&x[i]>=0){
ans = x[i];
}
}
return ans;
}
class Solution {
public:
string minWindow(string s, string t) {
int numOfLetter[128];//numOfLetter表示每个字符的数量
int startLocOfLetter[128];//startLocOfLetter表示每个字符的第一个出现位置
int startLoc, tempStartLoc;
int length = MAX;
bool haveLetter[128];//haveLetter表示每个字符是否出现过
int sumOfLetter = 0;//一共有多少不同的字符
int i;
for (i = 0; i < 128; i++){
numOfLetter[i] = 0;
startLocOfLetter[i] = -1;
haveLetter[i] = false;
}
for (i = 0; i < t.length(); i++){
numOfLetter[t[i]]++; //初始化numOfLetter
haveLetter[t[i]] = true;
}
for (i = 0; i < 128; i++){
if (haveLetter[i]){
sumOfLetter++;
}
}
for (i = 0; s[i] != '\0'; i++){
if (!haveLetter[s[i]]){
continue;
}
numOfLetter[s[i]]--;
if (numOfLetter[s[i]] < 0 || startLocOfLetter[s[i]] < 0){
while (true){
startLocOfLetter[s[i]]++; //滑动startLocOfLetter
if (s[startLocOfLetter[s[i]]] == s[i]){
break;
}
}
}
if (numOfLetter[s[i]] == 0){
sumOfLetter--; //每当一个字母的数量足够时,sumOfLetter减1
}
if (sumOfLetter <= 0){ //sumOfLetter小于0表示所有字母的个数已经足够
tempStartLoc = getMin(startLocOfLetter);
if (length > i - tempStartLoc + 1){
length = i - tempStartLoc + 1;
startLoc = tempStartLoc;
}
}
}
if (length == MAX){
return "";
}
string ans = s.substr(startLoc, length);
return ans;
}
};
实现方法二:利用C++的队列
C++语言AC代码:
#include<string>
#include<queue>
#define MAX 1234567890
class Solution {
public:
string minWindow(string s, string t) {
int numOfLetter[128];//numOfLetter表示每个字符的数量
int startLocOfLetter[128];//startLocOfLetter表示每个字符的第一个出现位置
queue<int>loca[128];
int startLoc,tempStartLoc;
int length = MAX;
bool haveLetter[128];//haveLetter表示每个字符是否出现过
int sumOfLetter = 0;//一共有多少不同的字符
int i;
for (int i = 0; i < 128; i++){
numOfLetter[i] = 0;
startLocOfLetter[i] = -1;
haveLetter[i] = false;
while (!loca[i].empty()){
loca[i].pop();
}
}
for (i = 0; i < t.length(); i++){
numOfLetter[t[i]]++; //初始化numOfLetter
haveLetter[t[i]] = true;
}
for (i = 0; i < 128; i++){
if (haveLetter[i]){
sumOfLetter++;
}
}
for (i = 0; s[i] != '\0'; i++){
if (!haveLetter[s[i]]){
continue;
}
if (loca[s[i]].size() == numOfLetter[s[i]] - 1){
sumOfLetter--; //每当一个字母的数量足够时,sumOfLetter减1
}
loca[s[i]].push(i);
if (loca[s[i]].size() > numOfLetter[s[i]]){
loca[s[i]].pop();//保持数量
}
if (sumOfLetter <= 0){ //sumOfLetter小于0表示所有字母的个数已经足够
tempStartLoc = MAX;
for (int i = 0; i < 128; i++){
if (haveLetter[i] && tempStartLoc > loca[i].front()){
tempStartLoc = loca[i].front();
}
}
if (length > i - tempStartLoc + 1){
length = i - tempStartLoc + 1;
startLoc = tempStartLoc;
}
}
}
if (length == MAX){
return "";
}
string ans = s.substr(startLoc, length);
return ans;
}
};
实现方法三:利用C++的队列+堆
C++语言AC代码:
#include<string>
#include<set>
#include<queue>
#define MAX 1234567890
class Solution {
public:
string minWindow(string s, string t) {
int numOfLetter[128];//numOfLetter表示每个字符的数量
int startLocOfLetter[128];//startLocOfLetter表示每个字符的第一个出现位置
queue<int>loca[128];
set<int>sLoca;
int startLoc;
int length = MAX;
bool haveLetter[128];//haveLetter表示每个字符是否出现过
int sumOfLetter = 0;//一共有多少不同的字符
int i;
for (i = 0; i < 128; i++){
numOfLetter[i] = 0;
haveLetter[i] = false;
while (!loca[i].empty()){
loca[i].pop();
}
}
sLoca.clear();
for (i = 0; i < t.length(); i++){
numOfLetter[t[i]]++; //初始化numOfLetter
haveLetter[t[i]] = true;
}
for (i = 0; i < 128; i++){
if (haveLetter[i]){
sumOfLetter++;
}
}
for (i = 0; s[i] != '\0'; i++){
if (!haveLetter[s[i]]){
continue;
}
if (loca[s[i]].size() == numOfLetter[s[i]] - 1){
loca[s[i]].push(i);
sLoca.insert(loca[s[i]].front());
}
else{
loca[s[i]].push(i);
}
if (loca[s[i]].size() > numOfLetter[s[i]]){
sLoca.erase(loca[s[i]].front());
loca[s[i]].pop();//保持数量
sLoca.insert(loca[s[i]].front());
}
if (sumOfLetter == sLoca.size()){ //表示所有字母的个数已经足够
if (length > i - *sLoca.begin() + 1){
length = i - *sLoca.begin() + 1;
startLoc = *sLoca.begin();
}
}
}
if (length == MAX){
return "";
}
string ans = s.substr(startLoc, length);
return ans;
}
};
力扣 1040. 移动石子直到连续 II(主从II型)
在一个长度 无限 的数轴上,第 i 颗石子的位置为 stones[i]。如果一颗石子的位置最小/最大,那么该石子被称作 端点石子 。
每个回合,你可以将一颗端点石子拿起并移动到一个未占用的位置,使得该石子不再是一颗端点石子。
值得注意的是,如果石子像 stones = [1,2,5] 这样,你将 无法 移动位于位置 5 的端点石子,因为无论将它移动到任何位置(例如 0 或 3),该石子都仍然会是端点石子。
当你无法进行任何移动时,即,这些石子的位置连续时,游戏结束。
要使游戏结束,你可以执行的最小和最大移动次数分别是多少? 以长度为 2 的数组形式返回答案:answer = [minimum_moves, maximum_moves] 。
示例 1:
输入:[7,4,9]
输出:[1,2]
解释:
我们可以移动一次,4 -> 8,游戏结束。
或者,我们可以移动两次 9 -> 5,4 -> 6,游戏结束。
示例 2:
输入:[6,5,4,3,10]
输出:[2,3]
解释:
我们可以移动 3 -> 8,接着是 10 -> 7,游戏结束。
或者,我们可以移动 3 -> 7, 4 -> 8, 5 -> 9,游戏结束。
注意,我们无法进行 10 -> 2 这样的移动来结束游戏,因为这是不合要求的移动。
示例 3:
输入:[100,101,104,102,103]
输出:[0,0]
提示:
3 <= stones.length <= 10^4
1 <= stones[i] <= 10^9
stones[i] 的值各不相同。
思路:分类讨论
class Solution {
public:
int minMove(vector<int>& stones)
{
int ans=0,si=stones.size();
for(int i=0,j=0;i<si;i++) //双指针
{
while(j+1<si&&stones[j+1]-stones[i]<si)j++;
if(ans<j-i)ans=j-i;
}
return si-ans-1+(stones[si-2]-stones[0]==si-2 && stones[si-1]-stones[si-2]>2 || (stones[si-1]-stones[1]==si-2 && stones[1]-stones[0]>2));
}
int maxMove(vector<int>& stones)
{
int ans=0,si=stones.size();
if(stones[1]==stones[0]||stones[si-1]==stones[si-2])return stones[si-1]-stones[0]-si+1;
return max(stones[si-1]-stones[1],stones[si-2]-stones[0])-si+2;
}
vector<int> numMovesStonesII(vector<int>& stones) {
sort(stones.begin(),stones.end());
return vector<int>{minMove(stones),maxMove(stones)};
}
};
力扣 1989. 捉迷藏中可捕获的最大人数(主从II型)
You are playing a game of tag with your friends. In tag, people are divided into two teams: people who are "it", and people who are not "it". The people who are "it" want to catch as many people as possible who are not "it".
You are given a 0-indexed integer array team
containing only zeros (denoting people who are not "it") and ones (denoting people who are "it"), and an integer dist
. A person who is "it" at index i
can catch any one person whose index is in the range [i - dist, i + dist]
(inclusive) and is not "it".
Return the maximum number of people that the people who are "it" can catch.
Example 1:
Input: team = [0,1,0,1,0], dist = 3 Output: 2 Explanation: The person who is "it" at index 1 can catch people in the range [i-dist, i+dist] = [1-3, 1+3] = [-2, 4]. They can catch the person who is not "it" at index 2. The person who is "it" at index 3 can catch people in the range [i-dist, i+dist] = [3-3, 3+3] = [0, 6]. They can catch the person who is not "it" at index 0. The person who is not "it" at index 4 will not be caught because the people at indices 1 and 3 are already catching one person.
Example 2:
Input: team = [1], dist = 1 Output: 0 Explanation: There are no people who are not "it" to catch.
Example 3:
Input: team = [0], dist = 1 Output: 0 Explanation: There are no people who are "it" to catch people.
Constraints:
1 <= team.length <= 105
0 <= team[i] <= 1
1 <= dist <= team.length
题意:
给0和1做匹配,距离不能超过dist,问最多能选出多少组匹配。
class Solution {
public:
int catchMaximumAmountofPeople(vector<int>& t, int dist) {
int p0 = 0, ans = 0;
for (int p1 = 0; p1 < t.size(); p1++) {
if (!t[p1])continue;
while (p0 < p1 - dist)p0++;
while (t[p0]) {
p0++;
if (p0 == t.size())return ans;
}
if (p0 <= p1 + dist)ans++, p0++;
if (p0 == t.size())return ans;
}
return ans;
}
};
力扣 340. 至多包含 K 个不同字符的最长子串(主从II型)
给你一个字符串 s
和一个整数 k
,请你找出 至多 包含 k
个 不同 字符的最长子串,并返回该子串的长度。
示例 1:
输入:s = "eceba", k = 2 输出:3 解释:满足题目要求的子串是 "ece" ,长度为 3 。
示例 2:
输入:s = "aa", k = 1 输出:2 解释:满足题目要求的子串是 "aa" ,长度为 2 。
提示:
1 <= s.length <= 5 * 104
0 <= k <= 50
class Solution {
public:
int lengthOfLongestSubstringKDistinct(string s, int k) {
int ans=0,n=0;
map<char,int>m;
for(int i=0,j=0;j<s.length();j++){
if(++m[s[j]]==1)n++;
while(n>k){
if(--m[s[i++]]==0)n--;
}
ans=max(ans,j-i+1);
}
return ans;
}
};
力扣 1885. 统计数对(主从II型)
给你两个长度为 n
的整数数组 nums1
和 nums2
,找出所有满足 i < j
且 nums1[i] + nums1[j] > nums2[i] + nums2[j]
的数对 (i, j)
。
返回满足条件数对的 个数 。
示例 1:
输入:nums1 = [2,1,2,1], nums2 = [1,2,1,2] 输出:1 解释:满足条件的数对有 1 个:(0, 2) ,因为 nums1[0] + nums1[2] = 2 + 2 > nums2[0] + nums2[2] = 1 + 1
示例 2:
输入:nums1 = [1,10,6,2], nums2 = [1,4,1,5] 输出:5 解释:以下数对满足条件: - (0, 1) 因为 nums1[0] + nums1[1] = 1 + 10 > nums2[0] + nums2[1] = 1 + 4 - (0, 2) 因为 nums1[0] + nums1[2] = 1 + 6 > nums2[0] + nums2[2] = 1 + 1 - (1, 2) 因为 nums1[1] + nums1[2] = 10 + 6 > nums2[1] + nums2[2] = 4 + 1 - (1, 3) 因为 nums1[1] + nums1[3] = 10 + 2 > nums2[1] + nums2[3] = 4 + 5 - (2, 3) 因为 nums1[2] + nums1[3] = 6 + 2 > nums2[2] + nums2[3] = 1 + 5
提示:
n == nums1.length == nums2.length
1 <= n <= 105
1 <= nums1[i], nums2[i] <= 105
class Solution {
public:
long long countPairs(vector<int>&v1,vector<int>&v2) {
for (int i = 0; i < v1.size(); i++)v1[i] -= v2[i];
sort(v1.begin(), v1.end());
long long ans = 0;
int i = 0, j = v1.size() - 1;
for (; j >= 0; j--) {
while (v1[i] + v1[j] <= 0)if (++i > j)return ans;
ans += j - i;
}
return ans;
}
};
力扣 2009. 使数组连续的最少操作数(主从II型)
给你一个整数数组 nums
。每一次操作中,你可以将 nums
中 任意 一个元素替换成 任意 整数。
如果 nums
满足以下条件,那么它是 连续的 :
nums
中所有元素都是 互不相同 的。nums
中 最大 元素与 最小 元素的差等于nums.length - 1
。
比方说,nums = [4, 2, 5, 3]
是 连续的 ,但是 nums = [1, 2, 3, 5, 6]
不是连续的 。
请你返回使 nums
连续 的 最少 操作次数。
示例 1:
输入:nums = [4,2,5,3] 输出:0 解释:nums 已经是连续的了。
示例 2:
输入:nums = [1,2,3,5,6] 输出:1 解释:一个可能的解是将最后一个元素变为 4 。 结果数组为 [1,2,3,5,4] ,是连续数组。
示例 3:
输入:nums = [1,10,100,1000] 输出:3 解释:一个可能的解是: - 将第二个元素变为 2 。 - 将第三个元素变为 3 。 - 将第四个元素变为 4 。 结果数组为 [1,2,3,4] ,是连续数组。
提示:
1 <= nums.length <= 105
1 <= nums[i] <= 109
class Solution {
public:
int minOperations(vector<int>& nums) {
sort(nums.begin(),nums.end());
set<int>s;
map<int,int>m;
s.insert(nums[0]);
m[0]=1;
int ans=nums.size()-1;
for(int i=0,j=1;j<nums.size();j++){
s.insert(nums[j]);
m[j]=s.size();
while(nums[j]-nums[i]>=nums.size())i++;
ans=min(ans,(int)nums.size()-(m[j]-m[i-1]));
}
return ans;
}
};
力扣 881. 救生艇(主从II型)
给定数组 people
。people[i]
表示第 i
个人的体重 ,船的数量不限,每艘船可以承载的最大重量为 limit
。
每艘船最多可同时载两人,但条件是这些人的重量之和最多为 limit
。
返回 承载所有人所需的最小船数 。
示例 1:
输入:people = [1,2], limit = 3 输出:1 解释:1 艘船载 (1, 2)
示例 2:
输入:people = [3,2,2,1], limit = 3 输出:3 解释:3 艘船分别载 (1, 2), (2) 和 (3)
示例 3:
输入:people = [3,5,3,4], limit = 5 输出:4 解释:4 艘船分别载 (3), (3), (4), (5)
提示:
1 <= people.length <= 5 * 104
1 <= people[i] <= limit <= 3 * 104
class Solution {
public:
int numRescueBoats(vector<int>& v, int limit) {
sort(v.begin(),v.end());
int ans=0;
for(int i=0,j=v.size()-1;i<=j;j--,ans++)
{
if(v[i]+v[j]<=limit)i++;
}
return ans;
}
};
力扣 2495. 乘积为偶数的子数组数(主从I型)
给定一个整数数组 nums
,返回具有偶数乘积的 nums
子数组的数目。
子数组 是数组中连续的非空元素序列。
示例 1:
输入: nums = [9,6,7,13] 输出: 6 解释: 有6个子数组的乘积是偶数: - nums[0..1] = 9 * 6 = 54. - nums[0..2] = 9 * 6 * 7 = 378. - nums[0..3] = 9 * 6 * 7 * 13 = 4914. - nums[1..1] = 6. - nums[1..2] = 6 * 7 = 42. - nums[1..3] = 6 * 7 * 13 = 546.
示例 2:
输入: nums = [7,3,5] 输出: 0 解释: 没有乘积是偶数的子数组
提示:
1 <= nums.length <= 105
1 <= nums[i] <= 105
思路:
本质上也是主从I型的双指针问题,不过我这里的写法略有不同。
class Solution {
public:
long long evenProduct(vector<int>& nums) {
return ((long long)nums.size())*(nums.size()+1)/2-oddProduct(nums);
}
long long oddProduct(vector<int>& nums) {
int i=0;
long long ans=0;
while(true){
while(nums[i]%2==0){
i++;
if(i>=nums.size())return ans;
}
int j=i;
while(j+1<nums.size() && nums[j+1]%2)j++;
ans+=((long long)(j-i+1))*(j-i+2)/2;
i=j+1;
if(i>=nums.size())return ans;
}
return 0;
}
};
力扣 3132. 找出与数组相加的整数 II(主从II型)
给你两个整数数组 nums1
和 nums2
。
从 nums1
中移除两个元素,并且所有其他元素都与变量 x
所表示的整数相加。如果 x
为负数,则表现为元素值的减少。
执行上述操作后,nums1
和 nums2
相等 。当两个数组中包含相同的整数,并且这些整数出现的频次相同时,两个数组 相等 。
返回能够实现数组相等的 最小 整数 x
。
示例 1:
输入:nums1 = [4,20,16,12,8], nums2 = [14,18,10]
输出:-2
解释:
移除 nums1
中下标为 [0,4]
的两个元素,并且每个元素与 -2
相加后,nums1
变为 [18,14,10]
,与 nums2
相等。
示例 2:
输入:nums1 = [3,5,5,3], nums2 = [7,7]
输出:2
解释:
移除 nums1
中下标为 [0,3]
的两个元素,并且每个元素与 2
相加后,nums1
变为 [7,7]
,与 nums2
相等。
提示:
3 <= nums1.length <= 200
nums2.length == nums1.length - 2
0 <= nums1[i], nums2[i] <= 1000
- 测试用例以这样的方式生成:存在一个整数
x
,nums1
中的每个元素都与x
相加后,再移除两个元素,nums1
可以与nums2
相等。
class Solution {
public:
int minimumAddedInteger(vector<int>& nums1, vector<int>& nums2) {
sort(nums1.begin(),nums1.end());
sort(nums2.begin(),nums2.end());
int dif=nums2[nums2.size()-1]-nums1[nums1.size()-1];
if(ok(nums1,nums2,dif))return dif;
dif=nums2[nums2.size()-1]-nums1[nums1.size()-2];
if(ok(nums1,nums2,dif))return dif;
dif=nums2[nums2.size()-1]-nums1[nums1.size()-3];
return dif;
}
bool ok(vector<int>& nums1, vector<int>& nums2,int dif)
{
int s=0;
for(int i=0,j=0;j<nums2.size();i++,j++){
while(nums1[i]+dif<nums2[j]){
if(++i>=nums1.size())return false;
if(++s>2)return false;
}
if(nums1[i]+dif!=nums2[j])return false;
}
return true;
}
};
四,3种对称型双指针
对称型双指针指的是,2个指针的地位相同,没有严格的大小关系。
(1)对称I型
2个指针同时从起始位置开始同向移动,没有严格的大小关系
典型代码:1个for语句里2个循环变量
(2)对称II型
2个指针相向挪动,相遇而终,虽然有大小关系,但其实是退化的大小关系。
典型代码:while (i < j)
(3)对称III型
2个指针同时从起始位置开始反向移动,虽然有大小关系,但其实是退化的大小关系。
典型代码:i=j;while(i>=0 && j<v.size())if(...)i--,j++;
力扣 88. 合并两个有序数组(对称I型)
给你两个按 非递减顺序 排列的整数数组 nums1
和 nums2
,另有两个整数 m
和 n
,分别表示 nums1
和 nums2
中的元素数目。
请你 合并 nums2
到 nums1
中,使合并后的数组同样按 非递减顺序 排列。
注意:最终,合并后数组不应由函数返回,而是存储在数组 nums1
中。为了应对这种情况,nums1
的初始长度为 m + n
,其中前 m
个元素表示应合并的元素,后 n
个元素为 0
,应忽略。nums2
的长度为 n
。
示例 1:
输入:nums1 = [1,2,3,0,0,0], m = 3, nums2 = [2,5,6], n = 3 输出:[1,2,2,3,5,6] 解释:需要合并 [1,2,3] 和 [2,5,6] 。 合并结果是 [1,2,2,3,5,6] ,其中斜体加粗标注的为 nums1 中的元素。
示例 2:
输入:nums1 = [1], m = 1, nums2 = [], n = 0 输出:[1] 解释:需要合并 [1] 和 [] 。 合并结果是 [1] 。
示例 3:
输入:nums1 = [0], m = 0, nums2 = [1], n = 1 输出:[1] 解释:需要合并的数组是 [] 和 [1] 。 合并结果是 [1] 。 注意,因为 m = 0 ,所以 nums1 中没有元素。nums1 中仅存的 0 仅仅是为了确保合并结果可以顺利存放到 nums1 中。
提示:
nums1.length == m + n
nums2.length == n
0 <= m, n <= 200
1 <= m + n <= 200
-109 <= nums1[i], nums2[j] <= 109
进阶:你可以设计实现一个时间复杂度为 O(m + n)
的算法解决此问题吗?
class Solution {
public:
void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {
for(int i=m+n-1;i>=n;i--)nums1[i]=nums1[i-n];
int j=0,k=0;
for(int i=n;i<m+n&&j<n;){
if(nums1[i]>nums2[j])nums1[k++]=nums2[j++];
else nums1[k++]=nums1[i++];
}
while(j<n)nums1[k++]=nums2[j++];
}
};
力扣 161. 相隔为 1 的编辑距离(对称I型)
给定两个字符串 s 和 t,判断他们的编辑距离是否为 1。
注意:
满足编辑距离等于 1 有三种可能的情形:
往 s 中插入一个字符得到 t
从 s 中删除一个字符得到 t
在 s 中替换一个字符得到 t
示例 1:
输入: s = "ab", t = "acb"
输出: true
解释: 可以将 'c' 插入字符串 s 来得到 t。
示例 2:
输入: s = "cab", t = "ad"
输出: false
解释: 无法通过 1 步操作使 s 变为 t。
示例 3:
输入: s = "1203", t = "1213"
输出: true
解释: 可以将字符串 s 中的 '0' 替换为 '1' 来得到 t。
class Solution {
public:
bool isOneEditDistance(string s, string t) {
int ls = s.length(), lt = t.length();
if(s==t)return false;
if (ls - lt > 1 || ls - lt < -1)return false;
bool flag = true;
for (int i = 0, j = 0; i < ls && j < lt; i++, j++) {
if (s[i] == t[j])continue;
if (!flag)return false;
flag = false;
if (ls > lt)j--;
if (ls < lt)i--;
}
return true;
}
};
力扣 面试题 16.06. 最小差(对称I型)
给定两个整数数组a
和b
,计算具有最小差绝对值的一对数值(每个数组中取一个值),并返回该对数值的差
示例:
输入:{1, 3, 15, 11, 2}, {23, 127, 235, 19, 8} 输出:3,即数值对(11, 8)
提示:
1 <= a.length, b.length <= 100000
-2147483648 <= a[i], b[i] <= 2147483647
- 正确结果在区间
[0, 2147483647]
内
class Solution {
public:
int smallestDifference(vector<int>& a, vector<int>& b) {
sort(a.begin(), a.end());
sort(b.begin(), b.end());
int i = 0, j = 0;
long long ans = abs(((long long)(a[i])) - b[j]);
while (true) {
if (a[i] < b[j])i++;
else if (a[i] > b[j])j++;
else if (i == a.size() - 1)j++;
else if (j == b.size() - 1)i++;
else if (a[i + 1] < b[j + 1])i++;
else j++;
if (i >= a.size() || j >= b.size())break;
ans = min(ans, abs((long long)(a[i]) - b[j]));
}
return ans;
}
};
力扣 1198. 找出所有行中最小公共元素(对称I型)
给你一个 m x n
的矩阵 mat
,其中每一行的元素均符合 严格递增 。请返回 所有行中的 最小公共元素 。
如果矩阵中没有这样的公共元素,就请返回 -1
。
示例 1:
输入:mat = [[1,2,3,4,5],[2,4,5,8,10],[3,5,7,9,11],[1,3,5,7,9]] 输出:5
示例 2:
输入:mat = [[1,2,3],[2,3,4],[2,3,5]] 输出: 2
提示:
m == mat.length
n == mat[i].length
1 <= m, n <= 500
1 <= mat[i][j] <= 104
mat[i]
已按严格递增顺序排列。
class Solution {
public:
int smallestCommonElement(vector<vector<int>>& mat) {
auto v=mat[0];
for(int i=1;i<mat.size();i++){
v=smallestCommonElement(v,mat[i]);
}
return v.empty()?-1:v[0];
}
vector<int> smallestCommonElement(vector<int>& v1,vector<int>& v2) {
vector<int>ans;
for(int i=0,j=0;i<v1.size() && j<v2.size();)
{
if(v1[i]<v2[j])i++;
else if(v1[i]>v2[j])j++;
else ans.push_back(v1[i]),i++,j++;
}
return ans;
}
};
力扣 244. 最短单词距离 II(对称I型)
请设计一个类,使该类的构造函数能够接收一个字符串数组。然后再实现一个方法,该方法能够分别接收两个单词,并返回列表中这两个单词之间的最短距离。
实现 WordDistanc 类:
WordDistance(String[] wordsDict) 用字符串数组 wordsDict 初始化对象。
int shortest(String word1, String word2) 返回数组 worddict 中 word1 和 word2 之间的最短距离。
示例 1:
输入:
["WordDistance", "shortest", "shortest"]
[[["practice", "makes", "perfect", "coding", "makes"]], ["coding", "practice"], ["makes", "coding"]]
输出:
[null, 3, 1]
解释:
WordDistance wordDistance = new WordDistance(["practice", "makes", "perfect", "coding", "makes"]);
wordDistance.shortest("coding", "practice"); // 返回 3
wordDistance.shortest("makes", "coding"); // 返回 1
注意:
1 <= wordsDict.length <= 3 * 104
1 <= wordsDict[i].length <= 10
wordsDict[i] 由小写英文字母组成
word1 和 word2 在数组 wordsDict 中
word1 != word2
shortest 操作次数不大于 5000
class WordDistance {
public:
WordDistance(vector<string>& wordsDict) {
for (int i = 0; i < wordsDict.size(); i++) {
if(m[wordsDict[i]].empty() || i>*m[wordsDict[i]].rbegin())m[wordsDict[i]].push_back(i);
}
}
int shortest(string word1, string word2) {
return shortest(m[word1], m[word2]);
}
int shortest(vector<int>& v1, vector<int>& v2)
{
int i = 0, j = 0;
int ans = 1234567;
while (i < v1.size() && j < v2.size()) {
ans = min(ans, abs(v1[i] - v2[j]));
if (v1[i] < v2[j])i++;
else j++;
}
return ans;
}
map<string, vector<int>>m;
};
力扣 245. 最短单词距离 III(对称I型)
给定一个字符串数组 wordsDict 和两个字符串 word1 和 word2 ,返回这两个单词在列表中出现的最短距离。
注意:word1 和 word2 是有可能相同的,并且它们将分别表示为列表中 两个独立的单词 。
示例 1:
输入:wordsDict = ["practice", "makes", "perfect", "coding", "makes"], word1 = "makes", word2 = "coding"
输出:1
示例 2:
输入:wordsDict = ["practice", "makes", "perfect", "coding", "makes"], word1 = "makes", word2 = "makes"
输出:3
提示:
1 <= wordsDict.length <= 105
1 <= wordsDict[i].length <= 10
wordsDict[i] 由小写英文字母组成
word1 和 word2 都在 wordsDict 中
class Solution {
public:
int shortestWordDistance(vector<string>& wordsDict, string word1, string word2) {
for (int i = 0; i < wordsDict.size(); i++) {
if (m[wordsDict[i]].empty())m[wordsDict[i]].push_back(i);
else {
if (m2[wordsDict[i]])m2[wordsDict[i]] = min(m2[wordsDict[i]], i - *m[wordsDict[i]].rbegin());
else m2[wordsDict[i]] = i - *m[wordsDict[i]].rbegin();
if (i > *m[wordsDict[i]].rbegin())m[wordsDict[i]].push_back(i);
}
}
if (word1 != word2)return shortest(m[word1], m[word2]);
else return m2[word1];
}
int shortest(vector<int>& v1, vector<int>& v2)
{
int i = 0, j = 0;
int ans = 1234567;
while (i < v1.size() && j < v2.size()) {
ans = min(ans, abs(v1[i] - v2[j]));
if (v1[i] < v2[j])i++;
else j++;
}
return ans;
}
map<string, vector<int>>m;
map<string, int>m2;
};
力扣 11. 盛最多水的容器(对称II型)
题目:
给定 n 个非负整数 a1,a2,...,an,每个数代表坐标中的一个点 (i, ai) 。在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0)。找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。
说明:你不能倾斜容器,且 n 的值至少为 2。
图中垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在此情况下,容器能够容纳水(表示为蓝色部分)的最大值为 49。
示例:
输入: [1,8,6,2,5,4,8,3,7]
输出: 49
代码:
class Solution {
public:
int maxArea(vector<int>& height) {
int i = 0, j = height.size() - 1;
int ans = 0;
while (i < j)
{
ans = max(ans, (j - i)*min(height[i], height[j]));
if (height[i] < height[j])i++;
else j--;
}
return ans;
}
};
力扣 735. 小行星碰撞(对称III型)
对于数组中的每一个元素,其绝对值表示小行星的大小,正负表示小行星的移动方向(正表示向右移动,负表示向左移动)。每一颗小行星以相同的速度移动。
找出碰撞后剩下的所有小行星。碰撞规则:两个小行星相互碰撞,较小的小行星会爆炸。如果两颗小行星大小相同,则两颗小行星都会爆炸。两颗移动方向相同的小行星,永远不会发生碰撞。
示例 1:
输入:asteroids = [5,10,-5] 输出:[5,10] 解释:10 和 -5 碰撞后只剩下 10 。 5 和 10 永远不会发生碰撞。
示例 2:
输入:asteroids = [8,-8] 输出:[] 解释:8 和 -8 碰撞后,两者都发生爆炸。
示例 3:
输入:asteroids = [10,2,-5] 输出:[10] 解释:2 和 -5 发生碰撞后剩下 -5 。10 和 -5 发生碰撞后剩下 10 。
提示:
2 <= asteroids.length <= 104
-1000 <= asteroids[i] <= 1000
asteroids[i] != 0
思路:双指针
时间复杂度是O(n)
class Solution {
public:
vector<int> asteroidCollision(vector<int>& asteroids) {
vector<int>v,ans;
int id=-1,id1=-1;
for(int i=0;i<asteroids.size();i++){
if(asteroids[i]>0)id1=i;
if(i==asteroids.size()-1 || (asteroids[i]<0 && asteroids[i+1]>0)){
for(int j=id+1;j<=i;j++)v.push_back(asteroids[j]);
asteroidCollision(v,id1-i+v.size()-1,ans);
id=i;
}
}
for(auto x:v)ans.push_back(x);
return ans;
}
void asteroidCollision(vector<int>&v,int id1,vector<int>&ans){
int id2=id1+1;
while(id1>=0 && id2<v.size()){
if(v[id1]+v[id2]>0)id2++;
else if(v[id1]+v[id2]<0)id1--;
else id1--,id2++;
}
if(id1>=0){
v.resize(id1+1);
}
else{
for(int i=id2;i<v.size();i++)ans.push_back(v[i]);
v.clear();
}
}
};
力扣 1793. 好子数组的最大分数(对称III型)
给你一个整数数组 nums
(下标从 0 开始)和一个整数 k
。
一个子数组 (i, j)
的 分数 定义为 min(nums[i], nums[i+1], ..., nums[j]) * (j - i + 1)
。一个 好 子数组的两个端点下标需要满足 i <= k <= j
。
请你返回 好 子数组的最大可能 分数 。
示例 1:
输入:nums = [1,4,3,7,4,5], k = 3 输出:15 解释:最优子数组的左右端点下标是 (1, 5) ,分数为 min(4,3,7,4,5) * (5-1+1) = 3 * 5 = 15 。
示例 2:
输入:nums = [5,5,4,5,4,1,1,1], k = 0 输出:20 解释:最优子数组的左右端点下标是 (0, 4) ,分数为 min(5,5,4,5,4) * (4-0+1) = 4 * 5 = 20 。
提示:
1 <= nums.length <= 105
1 <= nums[i] <= 2 * 104
0 <= k < nums.length
五,2种退化型双指针
(1)退化I型:区间分割
区间分割指的是,像主从型一样每次主指针移动一步,从指针要么不移动,要么只能移到主指针当前位置。整体效果相当于把数组依次划分成若干区间。
PS:前面的从指针移动多步,指的是连续多次执行移动一步。这里是从指针直接跳变,位置确定且不需要访问中间数据。
(2)退化II型:退化对称型双指针
退化对称型双指针指的是,2个指针都是遗忘扫描,2个指针之间是解耦的。
这其中有一种特例:等距指针,即下图,也就是上文第一张图。
力扣 809. 情感丰富的文字(退化I型)
有时候人们会用重复写一些字母来表示额外的感受,比如 "hello" -> "heeellooo"
, "hi" -> "hiii"
。我们将相邻字母都相同的一串字符定义为相同字母组,例如:"h", "eee", "ll", "ooo"。
对于一个给定的字符串 S ,如果另一个单词能够通过将一些字母组扩张从而使其和 S 相同,我们将这个单词定义为可扩张的(stretchy)。扩张操作定义如下:选择一个字母组(包含字母 c
),然后往其中添加相同的字母 c
使其长度达到 3 或以上。
例如,以 "hello" 为例,我们可以对字母组 "o" 扩张得到 "hellooo",但是无法以同样的方法得到 "helloo" 因为字母组 "oo" 长度小于 3。此外,我们可以进行另一种扩张 "ll" -> "lllll" 以获得 "helllllooo"。如果 s = "helllllooo"
,那么查询词 "hello" 是可扩张的,因为可以对它执行这两种扩张操作使得 query = "hello" -> "hellooo" -> "helllllooo" = s
。
输入一组查询单词,输出其中可扩张的单词数量。
示例:
输入:
s = "heeellooo"
words = ["hello", "hi", "helo"]
输出:1
解释:
我们能通过扩张 "hello" 的 "e" 和 "o" 来得到 "heeellooo"。
我们不能通过扩张 "helo" 来得到 "heeellooo" 因为 "ll" 的长度小于 3 。
提示:
1 <= s.length, words.length <= 100
1 <= words[i].length <= 100
- s 和所有在
words
中的单词都只由小写字母组成。
这简直是阅读理解题,只要不是1个字母变成2个字母,或者字母变少,其他情况都算合法扩张。
class Solution {
public:
int expressiveWords(string s, vector<string>& words) {
vector<char>v(s.data(), s.data() + s.length());
auto vp = Fshr(v, false);
int ans = 0;
for (auto& s : words) {
vector<char>v2(s.data(), s.data() + s.length());
auto vp2 = Fshr(v2, false);
if (vp2.size() != vp.size())continue;
bool flag = true;
for (int i = 0; i < vp.size(); i++) {
if (vp[i].first != vp2[i].first || vp[i].second < vp2[i].second || (vp[i].second ==2&& vp2[i].second==1)) {
flag = false;
break;
}
}
if (flag)ans++;
}
return ans;
}
};
其中的Fshr在我的模板中,是用双指针实现的,属于区间分割。
力扣 795. 区间子数组个数(退化I型)
给你一个整数数组 nums
和两个整数:left
及 right
。找出 nums
中连续、非空且其中最大元素在范围 [left, right]
内的子数组,并返回满足条件的子数组的个数。
生成的测试用例保证结果符合 32-bit 整数范围。
示例 1:
输入:nums = [2,1,4,3], left = 2, right = 3 输出:3 解释:满足条件的三个子数组:[2], [2, 1], [3]
示例 2:
输入:nums = [2,9,2,5,6], left = 2, right = 8 输出:7
提示:
1 <= nums.length <= 105
0 <= nums[i] <= 109
0 <= left <= right <= 109
class Solution {
public:
long long numSubarrayBoundedMax(vector<int>& nums, int key) {
int low=0;
long long ans=0;
for(int i=0;i<=nums.size();i++){
if(i<nums.size() && nums[i]<=key)continue;
ans+=(long long)(i-low)*(i-low+1)/2;
low=i+1;
}
return ans;
}
int numSubarrayBoundedMax(vector<int>& nums, int left, int right) {
return numSubarrayBoundedMax(nums,right)-numSubarrayBoundedMax(nums,left-1);
}
};
力扣 3011. 判断一个数组是否可以变为有序(退化I型)
给你一个下标从 0 开始且全是 正 整数的数组 nums
。
一次 操作 中,如果两个 相邻 元素在二进制下数位为 1 的数目 相同 ,那么你可以将这两个元素交换。你可以执行这个操作 任意次 (也可以 0 次)。
如果你可以使数组变有序,请你返回 true
,否则返回 false
。
示例 1:
输入:nums = [8,4,2,30,15] 输出:true 解释:我们先观察每个元素的二进制表示。 2 ,4 和 8 分别都只有一个数位为 1 ,分别为 "10" ,"100" 和 "1000" 。15 和 30 分别有 4 个数位为 1 :"1111" 和 "11110" 。 我们可以通过 4 个操作使数组有序: - 交换 nums[0] 和 nums[1] 。8 和 4 分别只有 1 个数位为 1 。数组变为 [4,8,2,30,15] 。 - 交换 nums[1] 和 nums[2] 。8 和 2 分别只有 1 个数位为 1 。数组变为 [4,2,8,30,15] 。 - 交换 nums[0] 和 nums[1] 。4 和 2 分别只有 1 个数位为 1 。数组变为 [2,4,8,30,15] 。 - 交换 nums[3] 和 nums[4] 。30 和 15 分别有 4 个数位为 1 ,数组变为 [2,4,8,15,30] 。 数组变成有序的,所以我们返回 true 。 注意我们还可以通过其他的操作序列使数组变得有序。
示例 2:
输入:nums = [1,2,3,4,5] 输出:true 解释:数组已经是有序的,所以我们返回 true 。
示例 3:
输入:nums = [3,16,8,4,2] 输出:false 解释:无法通过操作使数组变为有序。
提示:
1 <= nums.length <= 100
1 <= nums[i] <= 28
class Solution {
public:
bool canSortArray(vector<int>& nums) {
int low=0;
for(int i=1;i<nums.size();i++){
if(FgetNum1(nums[i])==FgetNum1(nums[i-1]))continue;
sort(nums.begin()+low,nums.begin()+i);
low=i;
}
sort(nums.begin()+low,nums.end());
for(int i=1;i<nums.size();i++)if(nums[i]<nums[i-1])return false;
return true;
}
};
力扣 141. 环形链表(退化II型)
给定一个链表,判断链表中是否有环。
进阶:
你能用 O(1)(即,常量)内存解决此问题吗?
代码:
class Solution {
public:
bool hasCycle(ListNode *head) {
if (!head)return false;
ListNode *p1 = head, *p2 = head->next;
while (p1 != p2)
{
if (!p1)return false;
p1 = p1->next;
if (!p2)return false;
p2 = p2->next;
if (!p2)return false;
p2 = p2->next;
}
return true;
}
};
力扣 142. 环形链表 II(退化II型)
给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。注意,pos 仅仅是用于标识环的情况,并不会作为参数传递到函数中。
说明:不允许修改给定的链表。
进阶:
你是否可以使用 O(1) 空间解决此题?
示例 1:
输入:head = [3,2,0,-4], pos = 1
输出:返回索引为 1 的链表节点
解释:链表中有一个环,其尾部连接到第二个节点。
示例 2:
输入:head = [1,2], pos = 0
输出:返回索引为 0 的链表节点
解释:链表中有一个环,其尾部连接到第一个节点。
示例 3:
输入:head = [1], pos = -1
输出:返回 null
解释:链表中没有环。
提示:
链表中节点的数目范围在范围 [0, 104] 内
-105 <= Node.val <= 105
pos 的值为 -1 或者链表中的一个有效索引
class Solution {
public:
ListNode *getAnyoneInCycle(ListNode *head) {
if (!head)return NULL;
ListNode *p1 = head, *p2 = head->next;
while (p1 != p2)
{
if (!p1)return NULL;
p1 = p1->next;
if (!p2)return NULL;
p2 = p2->next;
if (!p2)return NULL;
p2 = p2->next;
}
return p1;
}
int getCycleLen(ListNode*head){
ListNode*p=getAnyoneInCycle(head);
if(!p)return 0;
ListNode*q=p->next;
int ans=1;
while(q!=p)q=q->next,ans++;
return ans;
}
ListNode *detectCycle(ListNode *head){
int len=getCycleLen(head);
if(len==0)return NULL;
ListNode *p=head;
while(len--)p=p->next;
while(head!=p)head=head->next,p=p->next;
return p;
}
};
力扣 125. 验证回文串(退化II型)
题目:
给定一个字符串,验证它是否是回文串,只考虑字母和数字字符,可以忽略字母的大小写。
说明:本题中,我们将空字符串定义为有效的回文串。
示例 1:
输入: "A man, a plan, a canal: Panama"
输出: true
示例 2:
输入: "race a car"
输出: false
解法一,过滤无关字符,然后再匹配
代码:
class Solution {
public:
bool isPalindrome(string s) {
char c;
vector<char>vs;
for (int i = 0; i < s.length(); i++)
{
c = s[i];
if (c >= 'A'&&c <= 'Z')c = c - 'A' + 'a';
if (c >= 'a'&&c <= 'z' || c >= '0'&&c <= '9')vs.insert(vs.end(), c);
}
for (int i = 0, j = vs.size() - 1; i < j;i++,j--)
{
if (vs[i] != vs[j])return false;
}
return true;
}
};
解法二,双指针,一边过滤一边匹配
代码:
class Solution {
public:
bool isPalindrome(string s) {
for(int i=0,j=s.length()-1;i<j;){
if(!isdigit(s[i])&&!isalpha(s[i])) {
i++;
continue;
}
if(!isdigit(s[j])&&!isalpha(s[j])) {
j--;
continue;
}
if(isdigit(s[i])||isdigit(s[j])) {
if (s[i] != s[j])return false;
i++, j--;
continue;
}
if(s[i]==s[j]||s[i]==s[j]+'A'-'a'||s[i]==s[j]+'a'-'A'){
i++, j--;
continue;
}
return false;
}
return true;
}
};
六,三指针
力扣 15. 三数之和
给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有满足条件且不重复的三元组。
注意:答案中不可以包含重复的三元组。
示例:
给定数组 nums = [-1, 0, 1, 2, -1, -4],
满足要求的三元组集合为:
[
[-1, 0, 1],
[-1, -1, 2]
]
思路:用三指针挪来挪去。
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
map<int,int>m;
for(int i=0;i<nums.size();i++)m[nums[i]]++;
vector<int>tmp(3);
vector<vector<int>>ans,ans2;
ans.reserve(nums.size()*nums.size());
tmp[0]=tmp[1]=tmp[2]=0;
if(m[0]>=3)ans2.push_back(tmp);
sort(nums.begin(),nums.end());
for(int i=2;i<nums.size();i++)if(nums[i]==nums[i-2])nums.erase(nums.begin()+i--);
for(int i=1;i<nums.size();i++)if(nums[i]==nums[i-1])
{
if(m[-nums[i]*2] && nums[i])tmp[0]=tmp[1]=nums[i],tmp[2]=-nums[i]*2,ans2.push_back(tmp);
nums.erase(nums.begin()+i--);
}
for(int i=0;i<nums.size();i++)
{
for(int j=i+1,k=nums.size()-1;j<k;)
{
if(nums[i]+nums[j]+nums[k]>0)k--;
else if(nums[i]+nums[j]+nums[k]<0)j++;
else
{
tmp[0]=nums[i],tmp[1]=nums[j],tmp[2]=nums[k];
ans.push_back(tmp);
k--,j++;
}
}
}
ans.resize(ans.size()+ans2.size());
copy(ans2.begin(),ans2.end(),ans.end()-ans2.size());
return ans;
}
};
力扣 259. 较小的三数之和
给定一个长度为 n
的整数数组和一个目标值 target
,寻找能够使条件 nums[i] + nums[j] + nums[k] < target
成立的三元组 i, j, k
个数(0 <= i < j < k < n
)。
示例 1:
输入: nums = [-2,0,1,3]
, target = 2
输出: 2
解释: 因为一共有两个三元组满足累加和小于 2:
[-2,0,1]
[-2,0,3]
示例 2:
输入: nums = []
, target = 0
输出: 0
示例 3:
输入: nums = [0]
, target = 0
输出: 0
提示:
n == nums.length
0 <= n <= 3500
-100 <= nums[i] <= 100
-100 <= target <= 100
class Solution {
public:
int threeSumSmaller(vector<int>& nums, int target) {
sort(nums.begin(),nums.end());
int ans=0;
for(int i=0;i<nums.size();i++)
{
for(int j=i+1,k=nums.size()-1;j<k;)
{
if(nums[i]+nums[j]+nums[k]>=target)k--;
else
{
ans+=k-j;
j++;
}
}
}
return ans;
}
};
CSU 1165 Nearest Numbers
题目:
Description
Given three integer sequences which have been sorted in ascending order,pick one integer from each sequence and we can get three integers.we want these three integers to be nearest to each other.Assuming these three integers as a,b,c, they are nearest to each other means d=(a-b)2+(b-c)2+(c-a)2 is the smallest.
Input
There are many test cases. For each test case,the first line are three integers la,lb,lc, they let you know the length of each sequence. The following three lines separately have la,lb,lc integers,give you the integers in each sequence. 1<=la,lb,lc<=10^6 and All integers in the sequences are in the range of [-10^9,10^9].
Output
For each test case, just print one integer : the smallest d as mentioned above.
Sample Input
10 10 10
1 2 3 4 5 6 7 8 9 10
2 3 6 8 10 12 14 16 18 20
3 5 7 9 11 12 14 16 18 20
Sample Output
0
AC代码:
#include<iostream>
#include<algorithm>
using namespace std;
long long x[3][100001];
long long f(int a, int b, int c)
{
long long r = 1;
r = (r << 63) - 1;
for (int i = 1, j = 1, k = 1; i <= x[a][0]; i++)
{
while (j < x[b][0] && x[a][i] >= x[b][j+1])j++;
while (k < x[c][0] && x[a][i] > x[c][k])k++;
r = min(r,(x[a][i] - x[b][j])*(x[a][i] - x[b][j]) + (x[b][j] - x[c][k])*(x[b][j] - x[c][k]) + (x[a][i] - x[c][k])*(x[a][i] - x[c][k]));
}
return r;
}
int main()
{
while (cin >> x[0][0] >> x[1][0] >> x[2][0])
{
for (int i = 0; i < 3; i++)for (int j = 1; j <= x[i][0]; j++)cin >> x[i][j];
long long r1 = min(f(0, 1, 2), f(0, 2, 1));
long long r2 = min(f(1, 0, 2), f(1, 2, 0));
long long r3 = min(f(2, 0, 1), f(2, 1, 0));
cout << min(min(r1, r2), r3) << endl;
}
return 0;
}