双指针夹逼定理
977. 有序数组的平方
给你一个按 非递减顺序 排序的整数数组 nums,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。
示例 1:
输入:nums = [-4,-1,0,3,10]
输出:[0,1,9,16,100]
解释:平方后,数组变为 [16,1,0,9,100]
排序后,数组变为 [0,1,9,16,100]
示例 2:
输入:nums = [-7,-3,2,3,11]
输出:[4,9,9,49,121]
提示:
1 <= nums.length <= 104
-104 <= nums[i] <= 104
nums 已按 非递减顺序 排序
进阶:
请你设计时间复杂度为 O(n) 的算法解决本问题
题解
//原题本来就是排序的,只不过有负数;
//kk是从后往前的下标;
//从前往后插
class Solution {
public:
vector<int> sortedSquares(vector<int>& nums) {
vector<int> n1(nums.size());
int rr=nums.size()-1;
int ll=0; int kk=nums.size()-1;
while(ll<=rr)
{
if(abs(nums[ll])>abs(nums[rr])){n1[kk]=(nums[ll]*nums[ll]);ll++;kk--;}
if(abs(nums[ll])<=abs(nums[rr])){n1[kk]=(nums[rr]*nums[rr]);rr--;kk--;}
}
return n1;
}
};
283. 移动零
给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。
示例:
输入: [0,1,0,3,12]
输出: [1,3,12,0,0]
说明:
必须在原数组上操作,不能拷贝额外的数组。
尽量减少操作次数。
题解
class Solution {
public:
void moveZeroes(vector<int>& nums) {
int n=nums.size();
int a=0;//aa是从前往后的下标;
for(int i=0;i<n;i++)//先把非0元素移向前,用a 记录
if(nums[i]!=0)
nums[a++]=nums[i];
for(int i=a;i<n;i++)//后面都为0
nums[i]=0;
}
};
167. 两数之和 II - 输入有序数组
给定一个已按照 非递减顺序排列 的整数数组 numbers ,请你从数组中找出两个数满足相加之和等于目标数 target 。
函数应该以长度为 2 的整数数组的形式返回这两个数的下标值。numbers 的下标 从 1 开始计数 ,所以答案数组应当满足 1 <= answer[0] < answer[1] <= numbers.length 。
你可以假设每个输入 只对应唯一的答案 ,而且你 不可以 重复使用相同的元素。
示例 1:
输入:numbers = [2,7,11,15], target = 9
输出:[1,2]
解释:2 与 7 之和等于目标数 9 。因此 index1 = 1, index2 = 2 。
示例 2:
输入:numbers = [2,3,4], target = 6
输出:[1,3]
示例 3:
输入:numbers = [-1,0], target = -1
输出:[1,2]
提示:
2 <= numbers.length <= 3 * 104
-1000 <= numbers[i] <= 1000
numbers 按 非递减顺序 排列
-1000 <= target <= 1000
仅存在一个有效答案
题解
class Solution {
public:
vector<int> twoSum(vector<int>& numbers, int target) {
int n=numbers.size();
int a=0,b=n-1;
while(a<b)
{
if(numbers[a]+numbers[b]>target) b--;
else if(numbers[a]+numbers[b]==target) return vector<int>{a+1,b+1};
else a++;
}
return vector<int>{-1, -1};
}
};
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 码表中的可打印字符
题解
class Solution {
public:
void reverseString(vector<char>& s) {
int n=s.size();
for(int i=0;i<n/2;i++){
swap(s[i],s[n-i-1]);
}
}
};
557. 反转字符串中的单词 III
给定一个字符串,你需要反转字符串中每个单词的字符顺序,同时仍保留空格和单词的初始顺序。
示例:
输入:“Let’s take LeetCode contest”
输出:“s’teL ekat edoCteeL tsetnoc”
提示:
在字符串中,每个单词由单个空格分隔,并且字符串中不会有任何额外的空格。
题解
class Solution {
public:
string reverseWords(string s) {
int n=s.size();int a=0,b=-1;
for(int i=0;i<n;i++)
{
if(s[i]==' '||i==n-1)//寻找单个单词
{
b=i-1;
if(i==n-1) b=i;
while(a<b)//旋转单词内部
{
swap(s[a],s[b]);
a++;
b--;
}
a=i+1;
}
}
return s;
}
};
189. 旋转数组
给定一个数组,将数组中的元素向右移动 k 个位置,其中 k 是非负数。
进阶:
尽可能想出更多的解决方案,至少有三种不同的方法可以解决这个问题。
你可以使用空间复杂度为 O(1) 的 原地 算法解决这个问题吗?
示例 1:
输入: nums = [1,2,3,4,5,6,7], k = 3
输出: [5,6,7,1,2,3,4]
解释:
向右旋转 1 步: [7,1,2,3,4,5,6]
向右旋转 2 步: [6,7,1,2,3,4,5]
向右旋转 3 步: [5,6,7,1,2,3,4]
示例 2:
输入:nums = [-1,-100,3,99], k = 2
输出:[3,99,-1,-100]
解释:
向右旋转 1 步: [99,-1,-100,3]
向右旋转 2 步: [3,99,-1,-100]
提示:
1 <= nums.length <= 2 * 104
-231 <= nums[i] <= 231 - 1
0 <= k <= 105
题解
class Solution {
public:
void rotate(vector<int>& nums, int k) {
int n=nums.size();
if(n==1)
return;
k=k%n;//经过总结规律。刚开大于n时,相当取n的余;
int a=0,b=n-1;//翻转全部。
while(a<b){ swap(nums[a],nums[b]);a++;b--;}
a=0;b=k-1;//翻转前半部分。
while(a<b){ swap(nums[a],nums[b]);a++;b--;}
a=k;b=n-1;//翻转后半部分
while(a<b){ swap(nums[a],nums[b]);a++;b--;}
}
};
11. 盛最多水的容器
给你 n 个非负整数 a1,a2,…,an,每个数代表坐标中的一个点 (i, ai) 。在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0) 。找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。
说明:你不能倾斜容器。
示例 1:
输入:[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 a=0,b=height.size()-1;
int max=0;
while(a<b)
{
int min1=min(height[a],height[b]);
if(max<min1*(b-a))
max=min1*(b-a);
if(height[a]<height[b]) a++;//经常使用if else if else
else b--;
}
return max;
}
//若向内 移动短板 ,水槽的短板 min(h[i], h[j])min(h[i],h[j]) 可能变大,因此下个水槽的面积 可能增大 。
//若向内 移动长板 ,水槽的短板 min(h[i], h[j])min(h[i],h[j]) 不变或变小,因此下个水槽的面积 一定变小 。
};
16. 最接近的三数之和
给定一个包括 n 个整数的数组 nums 和 一个目标值 target。找出 nums 中的三个整数,使得它们的和与 target 最接近。返回这三个数的和。假定每组输入只存在唯一答案。
示例:
输入:nums = [-1,2,1,-4], target = 1
输出:2
解释:与 target 最接近的和是 2 (-1 + 2 + 1 = 2) 。
提示:
3 <= nums.length <= 10^3
-10^3 <= nums[i] <= 10^3
-10^4 <= target <= 10^4
题解
class Solution {
public:
int threeSumClosest(vector<int>& nums, int target) {
int mini=10000,min1;
int n=nums.size();
sort(nums.begin(),nums.end());
for(int i=0;i<n-2;i++)//一个一个来
{
int a=i+1;int b=n-1;
while(a<b)//循环遍历后面
{
if(abs(nums[a]+nums[b]+nums[i]-target)<mini)
{
mini=abs(nums[a]+nums[b]+nums[i]-target);//寻找最接近的
min1=nums[a]+nums[b]+nums[i];//最接近的
}
if(nums[a]+nums[b]+nums[i]<target)a++;//常规三情况
else if(nums[a]+nums[b]+nums[i]>target)b--;
else return target;
}
}
return min1;
}
};
##面试题 01.05. 一次编辑
字符串有三种编辑操作:插入一个字符、删除一个字符或者替换一个字符。 给定两个字符串,编写一个函数判定它们是否只需要一次(或者零次)编辑。
示例 1:
输入:
first = “pale”
second = “ple”
输出: True
示例 2:
输入:
first = “pales”
second = “pal”
输出: False
题解
class Solution {
public:
bool oneEditAway(string first, string second) {
int m=first.size(),n=second.size();
// if(first.empty()&&second.size()==1) return true;
// if(second.empty()&&first.size()==1) return true;
if(n>m)swap(first,second);
if(first.size()-second.size()>1)//两字符串长度相差二
return false;
else if(first.size()==second.size())//两个字符串一样长
{
int sum=0;
for(int i=0;i<n;i++)
if(first[i]!=second[i])
{ sum++; if(sum>1)return false;}
return true;
}
else//两字符串长度相差一。
{
int a=0,b=n-1;
while(a<=b&&first[a]==second[a]) a++;//从前往后遍历,
while(a<=b&&first[b+1]==second[b]) b--;//从后往前便利,如果错误之处有两个,则A必小于B,如果只有一次错误,则A等于B加一。
return a>b;
}
}
};
面试题 16.16. 部分排序
给定一个整数数组,编写一个函数,找出索引m和n,只要将索引区间[m,n]的元素排好序,整个数组就是有序的。注意:n-m尽量最小,也就是说,找出符合条件的最短序列。函数返回值为[m,n],若不存在这样的m和n(例如整个数组是有序的),请返回[-1,-1]。
示例:
输入: [1,2,4,7,10,11,7,12,6,7,16,18,19]
输出: [3,9]
提示:
0 <= len(array) <= 1000000
题解
class Solution {
public:
vector<int> subSort(vector<int>& array) {
int n=array.size();
if(n==1||n==0)//刚刚只有一个或没有元素的时候,我们直接返回{-1,-1}说明他就不需要再排序
return {-1,-1};
int i=1,j=n-2;//相当于我们先找出开始混乱的地方。但没有混乱的地方不一定排不排。初始第一个元素和最后一个元素是有序的。逐渐加入游戏的队伍。
while(i<n){if(array[i]<array[i-1])break;i++;}i--;if(i==n-1)return {-1,-1};//从左往右找出开始混乱的地方,但如果他没有混乱那就直接返回
while(j>=0){if(array[j]>array[j+1])break;j--;}j++;//从右往左咋说开始混乱的地方
int max1=array[0];int min1=array[n-1];//找出混乱的最大值和最小
for(int k=i;k<=j;k++)
{
if(array[k]<min1)min1=array[k];
if(array[k]>max1)max1=array[k];
}
int m=i+1,m2=j-1;//首先给个默认值
for(int k=0;k<=i;k++)//确定左端开始排序的下标
{
if(array[k]>min1)
{
m=k;
break;
}
}
for(int k=n-1;k>=j;k--)//确定右端开始排序的下标
{
if(array[k]<max1)
{
m2=k;
break;
}
}
return {m,m2};
}
};
面试题 10.01. 合并排序的数组
给定两个排序后的数组 A 和 B,其中 A 的末端有足够的缓冲空间容纳 B。 编写一个方法,将 B 合并入 A 并排序。
初始化 A 和 B 的元素数量分别为 m 和 n。
示例:
输入:
A = [1,2,3,0,0,0], m = 3
B = [2,5,6], n = 3
输出: [1,2,2,3,5,6]
说明:
A.length == n + m
题解
class Solution {
public:
void merge(vector<int>& A, int m, vector<int>& B, int n) {
int i=m-1,j=n-1;int max1=m+n-1;
int l1=0;if(m==0)while(l1<n){A[l1]=B[l1];l1++;}//当然A为空时,把B直接给A;
while(j>=0&&i>=0)
{
if(A[i]>=B[j]){ A[max1]=A[i]; max1--; i--; }//逆向插入,把最大的插入向前依次插入
else { A[max1]=B[j]; max1--; j--;}
}
if(j>=0)while(j>=0){A[max1]=B[j]; max1--; j--; }//当一个已经插完了,如果是A剩下了那么A就不需要再插了
//如果是B剩下的,就应该全部把剩下的给全部剩下的A;
}
};
快慢指针
876. 链表的中间结点
给定一个头结点为 head 的非空单链表,返回链表的中间结点。
如果有两个中间结点,则返回第二个中间结点。
示例 1:
输入:[1,2,3,4,5]
输出:此列表中的结点 3 (序列化形式:[3,4,5])
返回的结点值为 3 。 (测评系统对该结点序列化表述是 [3,4,5])。
注意,我们返回了一个 ListNode 类型的对象 ans,这样:
ans.val = 3, ans.next.val = 4, ans.next.next.val = 5, 以及 ans.next.next.next = NULL.
示例 2:
输入:[1,2,3,4,5,6]
输出:此列表中的结点 4 (序列化形式:[4,5,6])
由于该列表有两个中间结点,值分别为 3 和 4,我们返回第二个结点。
提示:
给定链表的结点数介于 1 和 100 之间。
题解
/**
* 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* middleNode(ListNode* head) {
ListNode *a=head;
ListNode *b=head;
while(b!=NULL&&b->next!=NULL)
{
a=a->next;
b=b->next->next;
}
return a;
}
};
19. 删除链表的倒数第 N 个结点
给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。
进阶:你能尝试使用一趟扫描实现吗?
示例 1:
输入:head = [1,2,3,4,5], n = 2
输出:[1,2,3,5]
示例 2:
输入:head = [1], n = 1
输出:[]
示例 3:
输入:head = [1,2], n = 1
输出:[1]
提示:
链表中结点的数目为 sz
1 <= sz <= 30
0 <= Node.val <= 100
1 <= n <= sz
题解
/**
* 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) {
ListNode* qwe=new ListNode(0);
qwe->next=head;
ListNode *p=qwe;
ListNode *q=qwe;
for(int i=0;i<n+1;i++)
{
p=p->next;
}
while(p!=NULL)
{
q=q->next;
p=p->next;
}
ListNode* delNode = q->next;
q->next = delNode->next;
delete delNode;
ListNode* retNode = qwe->next;
delete qwe;
return retNode;
}
};
面试题 02.02. 返回倒数第 k 个节点
实现一种算法,找出单向链表中倒数第 k 个节点。返回该节点的值。
注意:本题相对原题稍作改动
示例:
输入: 1->2->3->4->5 和 k = 2
输出: 4
说明:
给定的 k 保证是有效的。
题解
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
int kthToLast(ListNode* head, int k) {
ListNode * qwe = new ListNode(0);
qwe->next=head;
ListNode * q=qwe;
ListNode * w=qwe;
for(int i=0;i<k+1;i++)
{
w=w->next;
}
while(w)
{
w=w->next;
q=q->next;
}
return q->next->val;
}
};
面试题 02.04. 分割链表
给你一个链表的头节点 head 和一个特定值 x ,请你对链表进行分隔,使得所有 小于 x 的节点都出现在 大于或等于 x 的节点之前。
你不需要 保留 每个分区中各节点的初始相对位置。
示例 1:
输入:head = [1,4,3,2,5,2], x = 3
输出:[1,2,2,4,3,5]
题解
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* partition(ListNode* head, int x) {
ListNode* small=new ListNode(0);
ListNode* large=new ListNode(0);
ListNode* smallhead=small;
ListNode* largehead=large;
while(head)
{
if(head->val>=x)
{large->next=head; large=large->next;}
if(head->val<x)
{small->next=head; small=small->next;}
head=head->next;
}
large->next = nullptr;
small->next = largehead->next;
return smallhead->next;
}
};