更新ing…第一遍刷LeetCode剑指offerII笔记记录。
栈与队列
剑指 Offer 09. 用两个栈实现队列
栈模拟队列:
两个栈,一个入栈只接受,另一个出栈
class CQueue {
public:
CQueue() {
}
void appendTail(int value) {
stackpush.push(value);
}
int deleteHead() {
if(stackpop.empty()){
while(!stackpush.empty()){
stackpop.push(stackpush.top());
stackpush.pop();
}
}
if(!stackpop.empty()){
int temp = stackpop.top();
stackpop.pop();
return temp;
}else{
return -1;
}
}
private:
stack<int>stackpop;
stack<int>stackpush;
};
剑指 Offer 30. 包含min函数的栈
两个栈一个维护数据,一个维护最小值的索引。
class MinStack {
public:
/** initialize your data structure here. */
MinStack() {
memset(stack,0,sizeof(int)*length);
memset(minstack,0,sizeof(int)*length);
index = minindex = -1;
}
void push(int x) {
if(index<length){
stack[++index]=x;
if(minindex>-1){
if(stack[minstack[minindex]]>=x)
minstack[++minindex]=index;
}
else minstack[++minindex]=index;
}
}
void pop() {
if(index>-1){
if(index==minstack[minindex]){
--minindex;
}
index--;
}
}
int top() {
if(index>-1)return stack[index];
else return -1;
}
int min() {
if(minindex>-1)return stack[minstack[minindex]];
else return -1;
}
private:
static const int length = 20000;
int stack[length];
int index;
int minstack[length];
int minindex;
};
链表
剑指 Offer 06. 从尾到头打印链表
class Solution {
public:
vector<int> reversePrint(ListNode* head) {
ListNode node,* p = NULL;
node.next = NULL;
while(head!=NULL){//头插法逆序链表
p = head;
head = head->next;
p->next = node.next;
node.next = p;
}
head = node.next;
vector<int> nums;
while(head!=NULL){//遍历链表装入数组
nums.push_back(head->val);
head = head->next;
}
return nums;
}
};
剑指 Offer 24. 反转链表
- 头插法逆序时间On 空间O1
class Solution {
public:
ListNode* reverseList(ListNode* head) {
ListNode node,*p=NULL;
node.next = NULL;
while(head!=NULL){
p = head;
head = head->next;
p->next = node.next;
node.next = p;
}
return node.next;
}
};
- 官方双指针法可省去方法一中的ListNode局部变量
剑指 Offer 35. 复杂链表的复制
- 思路:数组存新链表各个节点的指针,map存旧链表中指针和第几个节点的映射关系。时间On空间On(较差)
class Solution {
public:
Node* copyRandomList(Node* head) {
vector<Node *> point(1000);
map<Node*,int>mp;
Node h(-1) , *tail=&h,*temp;
int cnt = -1;
Node *temphead = head;
while(head!=NULL){//构建新链表仅维护next指针
temp = new Node(head->val);
point[++cnt] = temp;
mp[head] = cnt;
tail->next = temp;
tail = tail->next;
head = head->next;
}
head = temphead;
tail = h.next;
while(head!=NULL){//维护random指针
if(head->random!=NULL){
tail->random = point[mp[head->random]];
}
head=head->next;
tail=tail->next;
}
return h.next;
}
};
- leetcode官方哈希表映射指针进行复制,时间On空间On 优于自己写的方法一
- leetcode官方拼接 + 拆分,先拼接后拆分,时间On空间O1(最优)
第一步,copy原节点
第二步,copyrandom
第三步,剪切
字符串
剑指 Offer 05. 替换空格
class Solution {
public:
string replaceSpace(string s) {
int index =-1;
while(s.find(' ')!=string::npos){
s.replace(s.find(' '),1,"%20");
}
return s;
}
};
剑指 Offer 58 - II. 左旋转字符串
1.利用转置可以解决,时间On空间O1,(c++reverse函数默认为原地翻转)
(
A
B
)
T
=
A
T
B
T
(AB)^T = A^TB^T
(AB)T=ATBT
class Solution {
public:
string reverseLeftWords(string s, int n) {
reverse(s.begin(),s.begin()+n);
reverse(s.begin()+n,s.end());
reverse(s.begin(),s.end());
return s;
}
};
- 字符串拼接
class Solution {
public:
string reverseLeftWords(string s, int n) {
return (s+s).substr(n,s.size());
}
};
作者:yzwz
链接:https://leetcode-cn.com/problems/zuo-xuan-zhuan-zi-fu-chuan-lcof/solution/yi-xing-jiu-gou-liao-by-yzwz-2/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
查找算法
剑指 Offer 03. 数组中重复的数字
- 不打乱顺序,使用map记录,时间On空间On
class Solution {
public:
int findRepeatNumber(vector<int>& nums) {
unordered_map<int,int> mp;
int cnt = 0;
for(int i=nums.size()-1;i>=0;--i){
if(mp.find(nums[i])==mp.end())
mp.insert({nums[i],-1});
else {
cnt = nums[i];
break;
}
}
return cnt;
}
};
- 更改数组,先排序再遍历时间Onlogn空间O1
- 更改数组,原地交换,时间On空间O1
class Solution {
public:
int findRepeatNumber(vector<int>& nums) {
int N = nums.size();
for(int i=0; i<N; i++){
while(nums[i] != i){ //发现这个坑里的萝卜不是自己家的
int temp = nums[i]; //看看你是哪家的萝卜
if(nums[temp] == temp) //看看你家里有没有和你一样的萝卜
return temp; //发现你家里有了和你一样的萝卜,那你就多余了,上交国家
else //你家里那个萝卜和你不一样
swap(nums[temp], nums[i]); //把你送回你家去,然后把你家里的那个萝卜拿回来
}
}
return -1;
}
};
作者:Dean-98543
链接:https://leetcode-cn.com/problems/shu-zu-zhong-zhong-fu-de-shu-zi-lcof/solution/yuan-di-jiao-huan-yi-jiao-huan-luo-bu-bi-gh5c/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
剑指 Offer 53 - I. 在排序数组中查找数字 I
二分查找,二分上界和下界
class Solution {
public:
int search(vector<int>& nums, int target) {
return
upper_bound(nums.begin(),nums.end(),target)-lower_bound(nums.begin(),nums.end(),target);
}
};
剑指 Offer 53 - II. 0~n-1中缺失的数字
class Solution {
public:
int missingNumber(vector<int>& nums) {
int l =0,r = nums.size()-1;
while(l<=r){
int mid = (r-l)/2+l;
if(nums[mid]==mid){
l = mid+1;
}else r = mid-1;
}
return l;
}
};
剑指 Offer 04. 二维数组中的查找
- 思路:对于每一行进行二分查找,时间O(nlogm)空间O1
class Solution {
public:
bool findNumberIn2DArray(vector<vector<int>>& matrix, int target) {
for(int i=matrix.size()-1 ;i>=0;--i){
int l=0,r = matrix[0].size()-1;
while(l<=r){
int mid = (r-l)/2+l;
//printf("%d %d %d\n",i,mid,matrix[i][mid]);
if(matrix[i][mid]==target){
return true;
}else if(matrix[i][mid]>target){
r = mid -1;
}else l = mid+1;
}
}
return false;
}
};
- 旋转45度,进行查找,时间O(m+n)空间O1
class Solution {
public:
bool findNumberIn2DArray(vector<vector<int>>& matrix, int target) {
int i = matrix.size() - 1, j = 0;
while(i >= 0 && j < matrix[0].size())
{
if(matrix[i][j] > target) i--;
else if(matrix[i][j] < target) j++;
else return true;
}
return false;
}
};
作者:jyd
链接:https://leetcode-cn.com/problems/er-wei-shu-zu-zhong-de-cha-zhao-lcof/solution/mian-shi-ti-04-er-wei-shu-zu-zhong-de-cha-zhao-zuo/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
剑指 Offer 50. 第一个只出现一次的字符
- 思路,第一遍统计,第二遍判断。时间O(n)空间O(m),m为字符数。
class Solution {
public:
char firstUniqChar(string s) {
int visit[26]={0};
for(int i=0;i<s.length();i++){
visit[s[i]-'a']++;
}
for(int i=0;i<s.length();i++){
if(1 == visit[s[i]-'a'])return s[i];
}
return ' ';
}
};
剑指 Offer 11. 旋转数组的最小数字
思路:没做会,差十几个例子,参考leetcode官方解析。
class Solution {
public:
int minArray(vector<int>& numbers) {
int low = 0;
int high = numbers.size() - 1;
while (low < high) {
int pivot = low + (high - low) / 2;
if (numbers[pivot] < numbers[high]) {
high = pivot;
}
else if (numbers[pivot] > numbers[high]) {
low = pivot + 1;
}
else {
high -= 1;
}
}
return numbers[low];
}
};
作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/xuan-zhuan-shu-zu-de-zui-xiao-shu-zi-lcof/solution/xuan-zhuan-shu-zu-de-zui-xiao-shu-zi-by-leetcode-s/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
搜索与回溯算法
剑指 Offer 32 - I. 从上到下打印二叉树
思路:二叉树层次遍历,时间On空间On
class Solution {
public:
vector<int> levelOrder(TreeNode* root) {
vector<int>v;
if(root==NULL)return v;
queue<TreeNode *>q;
q.push(root);
while(!q.empty()){
TreeNode * temp = q.front();
q.pop();
v.push_back(temp->val);
if(temp->left!=NULL)q.push(temp->left);
if(temp->right!=NULL)q.push(temp->right);
}
return v;
}
};
剑指 Offer 32 - II. 从上到下打印二叉树 II
思路:广度优先搜索时对于每一层加len标记。时间On空间On
class Solution {
public:
vector<vector<int>> levelOrder(TreeNode* root) {
vector<vector<int>> v;
if(root == NULL)return v;
queue<TreeNode*>q;
q.push(root);
int cnt =0;
while(!q.empty()){
int len=q.size();//每一层长度标记
vector<int>vec;
while(len--){
TreeNode* temp = q.front();
q.pop();
vec.push_back(temp->val);
if(temp->left!=NULL)q.push(temp->left);
if(temp->right!=NULL)q.push(temp->right);
}
v.push_back(vec);
}
return v;
}
};
剑指 Offer 32 - III. 从上到下打印二叉树 III
- 思路:两个队列分别维护从左到右和从右到左的层次遍历,通过标记
isodd
判断偶数列和奇数列。时间On空间On。
class Solution {
public:
vector<vector<int>> levelOrder(TreeNode* root) {
vector<vector<int>>v;
if(root==NULL)return v;
queue<TreeNode*>oddq;
queue<TreeNode*>evenq;
oddq.push(root);
evenq.push(root);
int isodd = 1;//指示当前应该添加奇数列还是偶数列的标记
while(!oddq.empty()){
int len = oddq.size();
vector<int>vec;
while(len--){
TreeNode* tempodd = oddq.front();
TreeNode* tempeven = evenq.front();
oddq.pop();
evenq.pop();
if(tempodd->left!=NULL)oddq.push(tempodd->left);
if(tempodd->right!=NULL)oddq.push(tempodd->right);
if(tempeven->right!=NULL)evenq.push(tempeven->right);
if(tempeven->left!=NULL)evenq.push(tempeven->left);
if(isodd){
vec.push_back(tempodd->val);
}else{
vec.push_back(tempeven->val);
}
}
isodd^=1;
v.push_back(vec);
}
return v;
}
};
- 看题解有这样思路:(优化掉方法一中的一个队列)时间On空间On
采用双端队列:
当为偶数层时,子节点从尾部压入,先压入左节点,后压入右节点。并从头部取出,
当为奇数层是,子节点从头部压入,先压入右节点,后压入左节点。并从尾部取出
剑指 Offer 28. 对称的二叉树
思路:isSymmetric 处理root为空的特殊情况以及接收遍历结果。isduichen递归判断是否对称,通过flag值带出结果。时间On空间On
class Solution {
public:
void isduichen(TreeNode* root,TreeNode * roottemp,bool &flag){
if(root==NULL&&roottemp==NULL){
}else if(root!=NULL&&roottemp!=NULL){
if(roottemp->val!=root->val){
//printf("%d %d\n",root->val,roottemp->val);
flag = false;
}else{
isduichen(root->right,roottemp->left,flag);
isduichen(root->left,roottemp->right,flag);
}
}else{
flag = false;
}
}
bool isSymmetric(TreeNode* root) {
if(root==NULL)return true;
bool flag = true;
isduichen(root->left,root->right,flag);
return flag;
}
};
剑指 Offer 26. 树的子结构
思路:回溯过程中,A中有值和B的根节点值相等就传入递归函数进行判断。时间O(n*m)空间我认为是O(max(n,m))
class Solution {
public:
bool isSub(TreeNode *A,TreeNode *B){
if(B!=NULL&&A!=NULL){
printf("%d %d\n",A->val,B->val);
return B->val==A->val&& isSub(A->left,B->left) && isSub(A->right,B->right);
}else if(B!=NULL && A==NULL)return false;
return true;
}
bool isSubStructure(TreeNode* A, TreeNode* B) {
if(A!=NULL&&B!=NULL){
bool flag = isSubStructure(A->left,B) || isSubStructure(A->right,B);
if(A->val == B->val){
return isSub(A,B) || flag;
}else{
return flag;
}
}
return false;
}
};
- 更加简洁的方法一写法
class Solution { public: bool isSubStructure(TreeNode* A, TreeNode* B) { return (A && B) && (recur(A, B) || isSubStructure(A->left, B) || isSubStructure(A->right, B)); } bool recur(TreeNode* A, TreeNode* B) { if(!B) return true; if(!A || A->val != B->val) return false; return recur(A->left, B->left) && recur(A->right, B->right); } }; 作者:guizimo 链接:https://leetcode-cn.com/problems/shu-de-zi-jie-gou-lcof/solution/cshuang-di-gui-by-guizimo-ng2q/ 来源:力扣(LeetCode) 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
剑指 Offer 27. 二叉树的镜像
- 思路:递归,交换左右子树,时间On空间On
class Solution { public: TreeNode* mirrorTree(TreeNode* root) { if(root!= NULL){ swap(root-> left,root-> right); mirrorTree(root-> left); mirrorTree(root-> right); } return root; } };
- 思路:广搜,交换左右子树,时间On空间On
class Solution {
public:
TreeNode* mirrorTree(TreeNode* root) {
if (root == nullptr)
return root;
queue<TreeNode*> que;
que.push(root);
while (!que.empty()){
int size = que.size();
for (int i = 0; i < size; i ++){
TreeNode* node = que.front();
que.pop();
TreeNode* swap = node->left;
node->left = node->right;
node->right = swap;
if (node->left != nullptr)
que.push(node->left);
if (node->right != nullptr)
que.push(node->right);
}
}
return root;
}
};
作者:master_xue
链接:https://leetcode-cn.com/problems/er-cha-shu-de-jing-xiang-lcof/solution/cdai-ma-fen-xiang-yan-du-you-xian-shen-d-gl4h/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
动态规划
剑指 Offer 10- I. 斐波那契数列
时间On空间O1
class Solution {
public:
int fib(int n) {
if(n==0)return 0;
if(n==1)return 1;
int f1 = 0,f2 = 1,temp=0;
//0 1 1 2 3 5
while(--n){
//printf("%d %d %d\n",temp,f1,f2);
temp = (f1+f2)%1000000007;
f1 = f2;
f2 = temp;
}
return f2;
}
};
剑指 Offer 10- II. 青蛙跳台阶问题
思路:此题天然符合斐波那契数列。例如对于3级台阶,可以分为从2级台阶走一步到3级,也可以从1级台阶走两步到3级,恰好为f(3) = f(1)+f(2)
。只是起始数字不同。时间On空间O1。
class Solution {
public:
int numWays(int n) {
if(n==0)return 1;
if(n==1)return 1;
int f1=1,f2=1,temp=0;
while(--n){
temp = (f1+f2)%1000000007;
f1=f2;
f2=temp;
}
return f2;
}
};
剑指 Offer 63. 股票的最大利润
思路:给定区间内,1次买卖获得最大利润。即这个区间内最大差值。时间On空间O1
class Solution {
public:
int maxProfit(vector<int>& prices) {
if(prices.size()<=1)return 0;//不能卖出
int maxpro=0,minprice = 0x3f3f3f3f;
for(int i=0;i<prices.size();++i){
maxpro = max(maxpro,prices[i]-minprice);
minprice = min(minprice,prices[i]);
}
return maxpro;
}
};
剑指 Offer 42. 连续子数组的最大和
思路:对于f(n)来讲,有两个来源,一是f(n-1)与第n个元素之和,二是从第n个元素从新计算。
//f(n) = max(f(n-1)+nums[n],nums[n])
class Solution {
public:
int maxSubArray(vector<int>& nums) {
int pre=0,ans=-0x3f3f3f3f;
for(const auto&x:nums){
pre=max(pre+x,x);
//cout<<pre<<endl;
ans=max(ans,pre);
}
return ans;
}
};
剑指 Offer 47. 礼物的最大价值
思路:f(n,m)有两个来源,一是f(n-1,m)+grid[n][m],二是f(n,m-1)+grid[n][m]。
//f[n][m] = max(f[n-1][m],f[n][m-1])+grid[n][m]
class Solution {
public:
int maxValue(vector<vector<int>>& grid) {
int row=grid.size(),col=grid[0].size();
for(int i=0;i<row;++i)
for(int j=0;j<col;++j){
grid[i][j]=
max(j>0?grid[i][j-1]:0,i>0?grid[i-1][j]:0)+grid[i][j];
}
return grid[row-1][col-1];
}
};
剑指 Offer 46. 把数字翻译成字符串
没想出来,题解如下两种方法。
class Solution {
public:
int translateNum(int num) {
if (num < 10) return 1;
return (num%100 < 10 || num%100 > 25) ? translateNum(num/10) : translateNum(num/10) + translateNum(num/100);
}
};
作者:OrangeMan
链接:https://leetcode-cn.com/problems/ba-shu-zi-fan-yi-cheng-zi-fu-chuan-lcof/solution/cjian-ji-dai-ma-shuang-bai-by-orangeman/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
class Solution {
public:
int translateNum(int num) {
//记dp[i]表示从倒数第i个数位开始往右的数字对应的情况(注意个位对应i=1)
//那么dp[i]只取决于dp[i-1]和dp[i-2]
//若数字num倒数第i位与其后面一位(即倒数第i-1位)无法组合为0~25之间的数字(不能含前导0)
//那么dp[i]=dp[i-1]
//否则dp[i]=dp[i-1]+dp[i-2],因为针对倒数第i位有两种翻译方式,分别对应已求出的两种子情况
int dp0=1, dp1=1;//分别表示只考虑最右边0个和1个数位时的情况数(滚动数组形式更新)
int last_num=num%10;//记录"后面一位"
while(num/=10){//运算结果就是运算表达式的值
int n=num%10;//当前数位
int dp0_backup=dp0;//备份一下dp0
dp0=dp1;//无论时1种还是2种翻译方式,dp0都会更新为dp1
//有两种翻译方式时(注意前导0)
if(n>0&&n*10+last_num<26) dp1+=dp0_backup;
last_num=n;//更新"后面一位"
}
return dp1;
}
};
剑指 Offer 48. 最长不含重复字符的子字符串
思路:维护一个字符标记数组,判断是否出现该字符,然后利用双指针维护当前不重复字符长度,当出现重复字符时,将左指针移到上一个字符出现位置的下一个字符上。维护ans记录最大长度。时间On空间Om,m为字符种类。
class Solution {
public:
int lengthOfLongestSubstring(string s) {
vector<bool>visit(256,false);
int length = s.length();
int ans = 0;
int temp = 0;
int j=0;
for(int i=0;i<length;i++){
if(visit[s[i]]==false){
temp++;
visit[s[i]]=true;
ans = max(ans,temp);
}else{
while(s[j]!=s[i]){
visit[s[j]]=false;
temp--;
j++;
}
j++;
}
}
return ans;
}
};
双指针
剑指 Offer 22. 链表中倒数第k个节点
思路:双指针,一个指针先跑k次,然后两个指针一起走,直到第一个指针走到头。特殊情况:头指针为空,链表长度不够k。
时间On空间O1。
class Solution {
public:
ListNode* getKthFromEnd(ListNode* head, int k) {
if(head==NULL)return NULL;
ListNode node;//头结点 方便链表操作
node.next = head;
ListNode *p = node.next,*q = node.next;
while(k-- && p!=NULL){
p = p->next;
}
// if(p)
// cout<<p->val<<endl;
// cout<<k<<endl;
if(p==NULL && k>=0)return NULL;
while(p!=NULL){
p = p->next;
q = q->next;
}
return q;
}
};
剑指 Offer 18. 删除链表的节点
思路:遍历,找到第一个节点,删除。特殊情况:头指针为空,删除第一个节点。时间On空间O1
class Solution {
public:
ListNode* deleteNode(ListNode* head, int val) {
if(head ==NULL)return head;
ListNode node;
node.next = head;
ListNode * pre = &node,*q = node.next;
while(q!=NULL){
if(q->val==val){
pre->next = q->next;
break;
}
pre = q;
q = q->next;
}
return node.next;
}
};
剑指 Offer 25. 合并两个排序的链表
思路:
构造一个头结点简化对于头指针是否为空的判断。然后双指针遍历构造有序链表。时间O(n+m)空间O1
class Solution {
public:
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
ListNode node(0);
ListNode * tail = &node;
while(l1!=NULL && l2!=NULL){
if(l1->val<l2->val){
tail->next = l1;
l1 = l1->next;
}else{
tail->next = l2;
l2 = l2->next;
}
tail = tail->next; tail->next =NULL;
}
if(l1!=NULL)tail->next = l1;
if(l2!=NULL)tail->next = l2;
return node.next;
}
};
剑指 Offer 52. 两个链表的第一个公共节点
- 做出来但代码没有官方简洁,官方思路分析很好,这是双指针遍历,时间On+m 空间O1
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
if (headA == nullptr || headB == nullptr) {
return nullptr;
}
ListNode *pA = headA, *pB = headB;
while (pA != pB) {
pA = pA == nullptr ? headB : pA->next;
pB = pB == nullptr ? headA : pB->next;
}
return pA;
}
};
作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/liang-ge-lian-biao-de-di-yi-ge-gong-gong-jie-dian-lcof/solution/liang-ge-lian-biao-de-di-yi-ge-gong-gong-pzbs/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
自己写的代码:
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
ListNode * p = headA,*q = headB;
bool flagp=true,flagq = true;
while(p!=NULL&&q!=NULL){
if(p == q){
return p;
}
p = p->next;
q = q->next;
if(p == NULL&&flagp){
p = headB;flagp = false;
}
if(q == NULL){
q = headA;
flagq = false;
}
}
return NULL;
}
};
剑指 Offer 21. 调整数组顺序使奇数位于偶数前面
思路:题目没描述清楚,应是数组中的奇数在前偶数在后,并非奇数位和偶数位。
双指针从数组两端遍历,从左至右找到一个偶数和从右至左找到一个奇数进程互换。时间On空间O1。
class Solution {
public:
vector<int> exchange(vector<int>& nums) {
int l=0,r=nums.size()-1;
while(l<r){
while(l<r && (nums[l]&1))++l;
while(l<r && (nums[r]&1)==0)--r;
swap(nums[l++],nums[r--]);
}
return nums;
}
};
剑指 Offer 57. 和为s的两个数字
- 用无序map,对遍历过的元素进行映射,时间On空间On
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
unordered_map<int,bool>mp;
for(int i = nums.size()-1;i>=0;--i){
mp.emplace(nums[i],true);
}
for(int i = nums.size()-1;i>=0;--i){
int temp = nums[i];
if(mp.find(target-temp)!=mp.end()){
vector<int>ans;
ans.emplace_back(target-temp);
ans.emplace_back(temp);
return ans;
}
}
return vector<int>();
}
};
- 利用数组有序进行双指针从两端向里查找(非二分查找)时间On空间O1
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
int l = 0,r = nums.size()-1;
while(l<r){
int temp = nums[l]+nums[r];
if(temp==target)
return {nums[l],nums[r]};
else if(temp>target){
--r;
}else{
++l;
}
}
return {};
}
};
剑指 Offer 58 - I. 翻转单词顺序
- 思路:利用转置进行逆序。处理好边界条件。时间On2空间O n
( A B ) T = A T B T (AB)^T = A^TB^T (AB)T=ATBT
class Solution {
public:
void reverse(string &s,int l,int r){
while(l<r){
swap(s[l++],s[r--]);
}
}
string reverseWords(string s) {
if(s.empty())return {};
string ans;
int l = 0;
s.append(" ");//填一个空格好操作
int len = s.length()-1;
while(l<len && s[len]==' ')--len;//去除首尾空格
while(l<len && s[l]==' ')++l;
for(int r=0;r<=len;r++){
while(s[l]==' ')++l;
if(s[r]!=' ' && s[r+1]==' '){
reverse(s,l,r);
ans.append(s.begin()+l,s.begin()+r+1);
ans.append(" ");
l = r+1;
}
}
ans.pop_back();//去除最后一个ans.append(" ")多余空格。
reverse(ans,0,ans.length()-1);
return ans;
}
};
- 思路:从后往前便利字符串。
class Solution {
public:
string reverseWords(string s) {
string ans;
//i,j用于确定跳过首尾空格的下标范围
int i=0,j=s.size()-1;
//跳过s的首部空格
while(i<=j&&s[i]==' ') i++;
//跳过s的尾部空格
while(i<=j&&s[j]==' ') j--;
//k,w为用于确定每个单词范围的双指针,从非空格尾部开始往前扫描,i为前边界,j为后边界
int k=j,w=j;
//当输入全为空格时,跳过首尾空格后i>j
while(i<=j&&k>=i){
//k往前扫描直到遇到空格停下,或者超出i前边界停下
while(k>=i&&s[k]!=' ') k--;
//k+1到w为一个单词的范围,将每个字符按序加入string ans即可
for(int idx=k+1;idx<=w;idx++) ans+=s[idx];
//没超出前边界i时,k停下遇到的肯定是空格,可能是一个或多个,跳过
if(k>=i&&s[k]==' '){
while(k>=i&&s[k]==' ') k--;
//跳过一个或多个空格后,ans加一个必要的空格
ans+=' ';
}
//w跳到k位置继续扫描下一个单词范围
w=k;
}
return ans;
}
};
作者:rong-ma-ma-C1XvG4c2gy
链接:https://leetcode-cn.com/problems/fan-zhuan-dan-ci-shun-xu-lcof/solution/cshuang-zhi-zhen-mo-ni-bu-shi-yong-api-b-5dp7/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
搜索与回溯算法
剑指 Offer 12. 矩阵中的路径
思路:如果矩阵中字符与字符串首字符相等则进行dfs遍历。
时间O(n2m) 空间On2
class Solution {
public:
int dx[4]={1,-1,0,0};
int dy[4]={0,0,1,-1};
int row=0;
int col=0;
bool dfs(vector<vector<char>>& board,vector<vector<bool>>& visit,const string &word,int index,int x,int y){
if(index==word.length()-1){
//printf("****");
//printf("%c %c\n",board[x][y],word[index]);
return board[x][y]==word[index];
}
bool flag=false;
//printf("%c %c\n",board[x][y],word[index]);
for(int i=0;i<4;i++){
int xx=x+dx[i];
int yy=y+dy[i];
if(0<=xx&&0<=yy&&xx<row&&yy<col&&board[xx][yy]==word[index+1]&&visit[xx][yy]==false){
visit[xx][yy]=true;
flag=flag || dfs(board,visit,word,index+1,xx,yy);
//printf("**\t%c %c\n",board[xx][yy],word[index]);
visit[xx][yy]=false;
}
}
return flag;
}
bool exist(vector<vector<char>>& board, string word) {
row = board.size();
col= board[0].size();
vector<vector<bool>> visit(row,vector<bool>(col,false));
for(int i=0;i<row;++i){
for(int j=0;j<col;++j){
if(board[i][j]==word[0]){
visit[i][j]=true;
if(dfs(board,visit,word,0,i,j)){
return true;
}
visit[i][j]=false;
}
}
}
return false;
}
};
剑指 Offer 13. 机器人的运动范围
思路:广搜的同时,控制边界判断。时间On2空间On2
class Solution {
public:
inline bool istrue(int x,int y,int sum){
//printf("%d\n",x%10+y%10+x/10+y/10);
return (x%10+x/10+y%10+y/10)>sum?false:true;
}
int movingCount(int m, int n, int k) {
int dx[] = {1,-1,0,0};
int dy[] = {0,0,1,-1};
queue<int> qx;
queue<int> qy;
vector<vector<bool>> visit(m,vector<bool>(n,false));//模版特化节省空间
qx.push(0);
qy.push(0);
visit[0][0] = true;
int count = 1;
while(!qx.empty()){
int x=qx.front();
qx.pop();
int y=qy.front();
qy.pop();
//printf("%d %d\n",x,y);
visit[x][y] = true;
for(int i=0;i<4;i++){
int xx = x+dx[i];
int yy = y+dy[i];
if(0<=xx&&0<=yy&&xx<m&&yy<n&&istrue(xx,yy,k)&&visit[xx][yy]==false){
count++;
visit[xx][yy] = true;
qx.push(xx);
qy.push(yy);
}
}
}
return count;
}
};
剑指 Offer 34. 二叉树中和为某一值的路径
思路:维护路径和sum以及路径path,进行深度优先搜索。利用vector的push_back方法为拷贝的特性,符合要求就加入答案中。时间On 空间On。注意是根到叶子节点的路径。
class Solution {
public:
vector<vector<int>> ans;
void dfs(TreeNode* root,int sum,const int target,vector<int>&path){
if(root->left==nullptr&&root->right==nullptr && sum == target){
ans.push_back(path);
}
if(root->left!=nullptr){
int temp = root->left->val;
path.emplace_back(temp);
dfs(root->left,sum+temp,target,path);
path.pop_back();
}
if(root->right){
int temp = root->right->val;
path.emplace_back(temp);
dfs(root->right,sum+temp,target,path);
path.pop_back();
}
}
vector<vector<int>> pathSum(TreeNode* root, int target) {
if(root==nullptr)return ans;
vector<int>path;
path.emplace_back(root->val);
dfs(root,root->val,target,path);
return ans;
}
};
剑指 Offer 36. 二叉搜索树与双向链表
思路:对于一颗二叉搜索树来说,根节点的前驱为左子树最右下节点。根节点的后继是右子树的最左下节点。故深度搜索时需要返回这两个信息与根节点连接起来。如何知道本层是上层的左子树还是右子树?通过istrue标记来指示,为真则表示为右子树。时间On空间O1
class Solution {
public:
Node * leftnode(Node * root){
if(root==NULL)return root;
while(root->left!=NULL){
root = root->left;
}
return root;
}
Node * rightnode(Node * root){
if(root==NULL)return root;
while(root->right!=NULL){
root = root->right;
}
return root;
}
Node * gengrate(Node * root,bool istrue){
if(root->left!=NULL){
root->left = gengrate(root->left,false);
root->left->right = root;
}
if(root->right!=NULL){
root->right = gengrate(root->right,true);
root->right->left = root;
}
return istrue?leftnode(root):rightnode(root);
}
Node* treeToDoublyList(Node* root) {
if(root==NULL)return NULL;
Node *l = leftnode(root),*r = rightnode(root);
//printf("%d %d\n",l==NULL?-1:l->val,r==NULL?-1:r->val);
gengrate(root,false);
l->left = r;
r->right = l;
return l;
}
};
剑指 Offer 54. 二叉搜索树的第k大节点
思路:二叉搜索树在中序遍历时是一个递增序列。故镜像二叉搜索树中序遍历是递减序列。由此先镜像再在中序遍历中记录当前遍历节点为第几个即可。时间On 空间O1
class Solution {
public:
void reverse(TreeNode *root){
if(root){
swap(root->left,root->right);
reverse(root->left);
reverse(root->right);
}
}
void bianli(TreeNode * root,int &k,int &val){
if(root){
bianli(root->left,k,val);
if((--k)==0){
val = root->val;
return ;
}
bianli(root->right,k,val);
}
}
int kthLargest(TreeNode* root, int k) {
int val=0;
reverse(root);
bianli(root,k,val);
return val;
}
};
排序
剑指 Offer 61. 扑克牌中的顺子
起初不知道什么是顺子,题解如下:
class Solution {
public:
bool isStraight(vector<int>& nums) {
sort(nums.begin(),nums.end());
int minindex=0;
for(int i=0;i<4;++i){
if(nums[i]==0)minindex++;
else if(nums[i]==nums[i+1]){
return false;
}
}
return nums[4]-nums[minindex]<5;
}
};
剑指 Offer 45. 把数组排成最小的数
思路:转化为字符串,利用字符串进行比大小。
时间 O(nlogn)空间On
class Solution {
public:
static bool cmp(string &a,string &b){
return a+b<b+a;
}
string minNumber(vector<int>& nums) {
vector<string>numstring(nums.size());
for(int i=nums.size()-1;i>=0;--i){
numstring[i] = to_string(nums[i]);
}
string ans;
sort(numstring.begin(),numstring.end(),cmp);
for(string s:numstring)ans.append(s);
return ans;
}
};
剑指 Offer 40. 最小的k个数
思路:利用快速排序的特性,每轮确定一个数的位置可得,当一个数是数组第k个时,其前面的元素就为最小的k个数
class Solution {
public:
void quicksort(vector<int>&arr,int left,int right,int k,bool &isfind){
if(left>=right||isfind) return ;
int l =left,r = right;
int target = arr[l];
//printf("%d %d\n",l,r);
while(l<r){
while(l<r && arr[r]>=target)r--;
swap(arr[l],arr[r]);
while(l<r && arr[l]<=target)l++;
swap(arr[r],arr[l]);
}
arr[l] = target;
if(l+1==k)isfind=true;
quicksort(arr,left,l-1,k,isfind);
quicksort(arr,l+1,right,k,isfind);
}
vector<int> getLeastNumbers(vector<int>& arr, int k) {
bool isfind = false;
quicksort(arr,0,arr.size()-1,k,isfind);
return vector<int>(arr.begin(),arr.begin()+k);
}
};
剑指 Offer 41. 数据流中的中位数
思路1:用vector保持有序数组,但插入时时间复杂度很高,解出来复杂度很高
思路2:看题解知利用堆,
class MedianFinder {
public:
/** initialize your data structure here. */
// 创建大顶堆
priority_queue<int, vector<int>, less<int>> maxHeap;
// 创建小顶堆
priority_queue<int, vector<int>, greater<int>> minHeap;
MedianFinder() {
}
void addNum(int num) {
/*
* 注意:两个堆的元素个数之差要小于等于1
* 1、若大顶堆元素数目与小顶堆元素数目相等:
* 先将数字存入大顶堆,再将大顶堆堆顶元素存入小顶堆
* 保证小顶堆中的数永远大于等于大顶堆的堆顶元素
* 2、若大顶堆元素数目与小顶堆元素数目不相等:
* 先将数字存入小顶堆,再将小顶堆堆顶元素存入大顶堆
* 保证大顶堆中的数永远小于等于小顶堆的堆顶元素
*/
if (maxHeap.size() == minHeap.size()) {
maxHeap.push(num);
minHeap.push(maxHeap.top());
maxHeap.pop();
} else {
minHeap.push(num);
maxHeap.push(minHeap.top());
minHeap.pop();
}
}
double findMedian() {
/*
* 1、若两个堆的元素个数相等:
* 中位数就是两个堆顶元素的平均值
* 2、若两个堆的元素个数不相等:
* 由于当两个堆元素个数相等时,
* 插入元素是先插入大顶堆,再将大顶堆堆顶元素插入小顶堆
* 这就相当于多出来的这个数最终插入的是小顶堆
* 所以小顶堆的堆顶元素就是数据流的中位数
*/
return maxHeap.size() == minHeap.size() ? (maxHeap.top() + minHeap.top()) / 2.0 : minHeap.top();
}
};
/**
* Your MedianFinder object will be instantiated and called as such:
* MedianFinder* obj = new MedianFinder();
* obj->addNum(num);
* double param_2 = obj->findMedian();
*/
作者:RyanWangllng
链接:https://leetcode-cn.com/problems/shu-ju-liu-zhong-de-zhong-wei-shu-lcof/solution/zhu-shi-xing-ti-jie-da-ding-dui-xiao-din-txvk/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。