LeetCode 刷题汇总(第一阶段,随机游走)
Posted 陆嵩
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LeetCode 刷题汇总(第一阶段,随机游走)相关的知识,希望对你有一定的参考价值。
从 0 开始 LeetCode刷题
面临毕业,为了谋求一份工作,不得已走上了刷题之路。不追求解法的性能和代码的优雅,只追求在最短的时间内写出来。
很多人喜欢给各种各样的方法起一个奇奇怪怪的名字,比如说“回溯”、“滑动窗口”等等,不懂这些,也可以做呀。你最后自己想了一个方法做出来了,其实默默地也就用了这些所谓的方法。这些题目我做完了,别人和我聊起什么回溯,我也不懂什么意思。我只知道,暴力出奇迹,干就完了。
我们做计算数学的人的长处不在于刷题,奈何这个世界这么无奈。
其实,我一直在思考,用这些小算法题去考验一个人的才能,是不是一个合理的事情。我认为,算法题做得好不好,和个人的工作能力,没有什么相关性,用这个来考察一个人的算法能力,是有失公允的。刷题刷得好,不代表你真的就算法做得好,很多人都是面向答案做题的,每天刷题基本就做做阅读,然后复现一下。这样,即使你题目刷得再多,当给你一道没答案的题目的时候,你该不会还是不会。
入门练习
1.两数之和
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target)
{
for(int i=0;i<nums.size();i++)
{
for(int j=i+1;j<nums.size();j++)
{
if(nums[i]+nums[j]==target)
{
vector<int> result = {i,j};
return result;
}
}
}
return {};
}
};
28.实现strStr
class Solution {
public:
int strStr(string haystack, string needle) {
if(needle.length()==0)
{return 0;}
else
{
int pos = haystack.find(needle);
if(pos == haystack.npos)
{
return -1;
}
else
return pos;
}
}
};
78.子集
class Solution {
public:
vector<vector<int>> subsets(vector<int>& nums) {
sort(nums.begin(), nums.end());
int numsSize = nums.size();
vector<vector<int>> result = {{}};
for(int i=0;i<result.size();++i)
{
for(int j=0;j<numsSize;++j)
{
vector<int> tmp(result[i]);
if(tmp.size()==0||nums[j]>tmp.back())
{
int tail = nums[j];
tmp.push_back(tail);
vector<int> newSet = tmp;
result.push_back(newSet);
}
}
}
return result;
}
};
数据结构
二叉树
104.二叉树的最大深度
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
int maxD = 1;
int maxDepth(TreeNode* root) {
if(root==NULL)
{
return 0;
}
int depth = 1;
TreeNode* curRoot = root;
getDepth(curRoot,depth);
return maxD;
}
int getDepth(TreeNode* curRoot,int &depth)
{
TreeNode* tmp1 = curRoot->left;
TreeNode* tmp2 = curRoot->right;
if(tmp1!=NULL)
{
depth++;
getDepth(tmp1,depth);
}
if(tmp2!=NULL)
{
depth++;
getDepth(tmp2,depth);
}
maxD = max(maxD,depth);
depth--;
return depth;
}
};
何时用递归?每个节点拎起来都是一个崭新的开始。每个节点拎起来要做的事情就是递归要做的事情。不能多做,不能少做。多拎几个点看看就知道做多少了。
110.平衡二叉树
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
bool flag = true;
bool isBalanced(TreeNode* root) {
if(root==NULL)
{
return true;
}
root -> val = 0;
maxHeight(root);
cout<<root->val<<endl;
judge(root);
return flag;
}
int judge(TreeNode* curRoot)
{
TreeNode* left = curRoot->left;
TreeNode* right = curRoot -> right;
if(left!=NULL && right!=NULL)
{
int diffH = abs(left->val-right->val);
cout<<diffH<<endl;
if(diffH>1)
{
flag = false;
}
}
if(left==NULL&&right!=NULL)
{
int diffH = abs(right->val);
if(diffH>1)
{
flag = false;
}
cout<<"right is not NULL:"<<diffH<<endl;
}
if(right==NULL&&left!=NULL)
{
int diffH = abs(left->val);
if(diffH>1)
{
flag = false;
}
cout<<diffH<<endl;
}
if(left!=NULL)
{
judge(left);
}
if(right!=NULL)
{
judge(right);
}
return 0;
}
int maxHeight(TreeNode* curRoot)
{
TreeNode* left = curRoot -> left;
TreeNode* right = curRoot -> right;
if(left!=NULL)
{
maxHeight(left);
}
if(right!=NULL)
{
maxHeight(right);
}
if(left==NULL&&right==NULL)
{
curRoot->val=1;
}
else
{
if(left!=NULL&&right==NULL)
{
curRoot->val = left->val+1;
}
if(left==NULL&&right!=NULL)
{
curRoot->val = right->val+1;
}
if(left!=NULL&&right!=NULL)
{
curRoot->val = max(left->val,right->val)+1;
}
}
return 0;
}
};
树的深度应该从1开始比较合理。
124.二叉树中的最大路径和
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution
{
public:
void rec(TreeNode* root,int &maxSum)
{
int originVal= root->val;
int maxLeftDepth = root->val;
int maxRightDepth = root->val;
TreeNode* left = root->left;
TreeNode* right = root->right;
if(left!=NULL)
{
cout<<maxLeftDepth<<endl;
rec(left,maxSum);
maxLeftDepth = max(originVal,maxLeftDepth+left->val);
}
if(right!=NULL)
{
cout<<maxRightDepth<<endl;
rec(right,maxSum);
maxRightDepth = max(originVal,maxRightDepth+right->val);
}
root->val = max(maxLeftDepth,maxRightDepth);
root->val = max(originVal,root->val);
maxSum = max(maxSum,maxLeftDepth+maxRightDepth-originVal);
maxSum = max(maxSum,originVal);
}
int maxPathSum(TreeNode* root)
{
int maxSum = -999999999;
rec(root,maxSum);
return maxSum;
}
};
搞清楚哪些是需要递归两层以上的量,我们将其作为返回参数进行传递。两层内的变量,看看是否能用树节点结构里面自带的变量。
236.二叉树的最近公共祖先
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
TreeNode* commonAncestor;
int recursion(TreeNode*root, TreeNode* p, TreeNode* q)
{
TreeNode* left = root->left;
TreeNode* right = root->right;
int val = root->val;
int numHavePQ = 0;
int leftNumHavePQ = 0;
int rightNumHavePQ = 0;
if(left!=NULL)
{
leftNumHavePQ = recursion(left,p,q);
}
if(right!=NULL)
{
rightNumHavePQ = recursion(right,p,q);
}
if(val==p->val||val==q->val)
{
numHavePQ++;
}
numHavePQ = numHavePQ+leftNumHavePQ+rightNumHavePQ;
cout<<"node:"<<val<<",numHavePQ:"<<numHavePQ<<endl;
if(numHavePQ==2)
{
commonAncestor = root;
return 0;
}
return numHavePQ;
}
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
recursion(root,p,q);
return commonAncestor;
}
};
1、这里提供了一个二叉树递归的范式模板:
int recursion(TreeNoderoot, TreeNode p, TreeNode* q)
{
TreeNode* left = root->left;
TreeNode* right = root->right;
int val = root->val;
if(left!=NULL)
{
leftNumHavePQ = recursion(left,p,q);
}
if(right!=NULL)
{
rightNumHavePQ = recursion(right,p,q);
}
2、如有需要,可以设置每个递归层的私有变量,以及程序全局变量。但无法改变链表自带的变量个数。
102.二叉树的层序遍历
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
//vector<int> r(10,0);
vector<vector<int>> result;
int recursion(TreeNode* root,int recursionLevel)
{
if(root==NULL)
{
return 0;
}
TreeNode* left = root->left;
TreeNode* right = root->right;
int val = root->val;
int recursionLevelCur = recursionLevel+1;
if(result.size()<recursionLevelCur+1)
{
vector<int> tmp;
result.push_back(tmp);
}
result[recursionLevelCur].push_back(val);
if(left!=NULL)
{
recursion(left,recursionLevelCur);
}
if(right!=NULL)
{
recursion(right,recursionLevelCur);
}
return 0;
}
public:
vector<vector<int>> levelOrder(TreeNode* root) {
recursion(root,-1);
return result;
}
};
二叉树的遍历可以从前往后遍历,也可以从后往前遍历。
类内的成员变量无法在定义的时候就初始化。
107.二叉树的层序遍历II
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
vector<vector<int>> result;
int recursion(TreeNode* root,int level)
{
if(root==NULL)
{
return 0;
}
int currentLevel = level+1;
TreeNode* left = root->left;
TreeNode* right = root->right;
int value = root->val;
if(result.size()<currentLevel)
{
vector<int> tmp;
result.insert(result.begin(),tmp);
}
result[result.size()-currentLevel].push_back(value);
if(left!=NULL)
{
recursion(left,currentLevel);
}
if(right!=NULL)
{
recursion(right,currentLevel);
}
return 0;
}
public:
vector<vector<int>> levelOrderBottom(TreeNode* root) {
recursion(root,0);
return result;
}
};
巧用 C++ STL 容器的灵活性,前插后插都可以。
103.二叉树的锯齿形层序遍历
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
vector<vector<int>> zigzagLevelOrder(TreeNode* root) {
vector<vector<int>> result;
if(root==NULL) return result;
TreeNode* left = root->left;
TreeNode* right = root->right;
int value = root->val;
vector<TreeNode*> from;
vector<TreeNode*> to;
from.push_back(root);
int i = 0;
while(1)
{
vector<int> tmp;
for(auto &it:from)
{
tmp.push_back(it->val);
if(it->left!=NULL)
{
to.push_back(it->left);
cout<<it->left->val<<endl;
}
if(it->right!=NULL)
{
to.push_back(it->right);
cout<<it->right->val<<endl;
}
}
if(i%2==1)
{
reverse(tmp.begin(),tmp.end());
}
result.push_back(tmp);
if(to.empty()) break;
from = to;
to.clear();
i++;
//cout<<i<<endl;
}
return result;
}
};
while
循环,最好写成while(1) xxx break;
的形式,更加灵活。
写这种 while 跳出型的循环,最开始先不写 while,先按自己脑子里的逻辑写一遍这个流程,直到重复点,最后在适当的地方补上这个 while。
98.验证二叉搜索树
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
int getMax(TreeNode* root)//HACK
{
if(root==NULL)
{
return 99999999;
}
int maxValue = root->val;
if(root->left!=NULL)
{
maxValue = max(maxValue,getMax(root->left));
}
if(root->right!=NULL)
{
maxValue = max(maxValue,getMax(root->right));
}
return maxValue;
}
int getMin(TreeNode* root)//HACK
{
if(root==NULL)
{
return -99999999;
}
int minValue = root->val;
if(root->left!=NULL)
{
minValue = min(minValue,getMin(root->left));
}
if(root->right!=NULL)
{
minValue = min(minValue,getMin(root->right));
}
return minValue;
}
bool recursion(TreeNode* root)
{
TreeNode* left = root->left;
TreeNode* right = root->right;
int value = root->val;
int leftMax;
int rightMin;
leftMax = getMax(left);//todo
rightMin = getMin(right);
bool leftIsValid = true;
bool rightIsValid = true;
if(left!=NULL)
{
leftIsValid = recursion(left)&&(value>leftMax);
}
if(right!=NULL)
{
rightIsValid = recursion(right)&&(value<rightMin);
}
return leftIsValid&&rightIsValid;
}
public:
bool isValidBST(TreeNode* root) {
return recursion(root);
}
};
当你想利用节点的新的属性辅助你的程序的时候,首先可以利用递归的返回变量,如果递归的返回变量已经被使用,可以考虑在递归里面再利用一次递归来实时地求得这个值。
递归根节点总是用 root 来表示,返回总是先看看主程序的返回。
701.二叉搜索树中的插入操作
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
int recursion(TreeNode* &root,int val)
{
if(root==NULL)
{
cout<<"root is null"<<endl;
TreeNode* append = new TreeNode(val);
root = append;
cout<<root->val<<endl;
return 0;
}
TreeNode* left=root->left;
TreeNode* right=root->right;
int value = root->val;
if(val<value)
{
if(left==NULL)
{
TreeNode* append = new TreeNode(val);
root->left = append;
}
else
recursion(left,val);
}
else{
if(right==NULL)
{
TreeNode* append = new TreeNode(val);
root->right = append;
}
else
recursion(right,val);
}
return 0;
}
public:
TreeNode* insertIntoBST(TreeNode* root, int val) {
recursion(root,val);
return root;
}
};
从前往后走,看看是插入左子树还是右子树,直到叶子节点,new 一个对象,让叶子节点的左边或者右边指向它。如果 root 就是空节点,直接 new 一个对象,让 root 指向它即可。
神奇的是,官方给出的第二张图似乎不是二解吧。问了几个人,似乎没人直到什么叫搜索树的插入。
链表
83.删除排序链表中的重复元素
/**
* 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 {
int printListNode(ListNode* root)
{
ListNode* cur = root;
while(cur!=NULL)
{
cout<<cur->val<<endl;
cur = (cur->next);
}
return 0;
}
int unique(ListNode* head)
{
while(1)
{
ListNode* next = head->next;
if(next==NULL) break;
if((head->val)==(next->val))
{
head->next = next->next;
}
else
break;
}
return 0;
}
public:
ListNode* deleteDuplicates(ListNode* head) {
ListNode* cur = head;
//printListNode(head);
while(cur!=NULL)
{
//cout<<cur->val<<endl;
unique(cur);
cur = cur->next;
}
return head;
}
};
判断的时候最好以当前点为核心,而不是 next。NULL 不存在值的,这点要注意。
82.删除排序链表中的重复元素II
/**
* 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* deleteDuplicates(ListNode* head) {
ListNode* vitualNode = new ListNode;
vitualNode->next = head;
ListNode* cur = vitualNode;
while(1)
{
if( cur->next==NULL) return vitualNode->next;
cout<<"cur:"<<cur->val<<endl;
ListNode* left = cur->next;
ListNode* right = cur->next;
while(1)
{
if(right==NULL||left->val!=right->val) break;
cout<<"left:"<<left->val<<endl;
cout<<"right:"<<right->val<<endl;
right = right->next;
}
if(right!=left->next)
{
cur->next = right;
}
else
{
cur = cur->next;
}
}
cout<<"vituralNode:"<<vitualNode->next->val<<endl;
return vitualNode->next;
}
};
1、虚拟节点技术,因为 head 有可能就是重复的,所以要新增一个虚拟节点指向 head。
2、搞三个游标,一个游标在重复段前,表示要越过重复段的链。另外两个游标,用来标识重复段。即a->b->b->c
类型的,需要把第一个游标放在 a 位置,第二、第三个游标放在第一个 b 的位置,移动第三个游标,直到其挪到了 c 位置,然后让 a 指向 c 即可。
206.反转链表
/**
* 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) return head;
ListNode* last = NULL;
ListNode* cur = head;
while(1)
{
ListNode* next = cur->next;
cur->next=last;
last = cur;
cur = next;
if(cur==NULL) break;
}
return last;
}
};
1、在 head 前面添加 NULL,然后实现箭头从右边到左边的翻转。
2、翻转前,要找个中间变量存下下一个点,方便当前点到下一点的迭代。
92.反转链表II
/**
* 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* reverseBetween(ListNode* head, int left, int right) {
ListNode* cur = head;
ListNode* from = NULL;
ListNode* to = NULL;
ListNode* curr;
int count = 1;
while(1){
if(count==left)
{
ListNode* last = NULL;
curr = cur;
while(1)
{
ListNode* next = curr->next;
curr->next = last;
if(count==right) {to=next;break;}
last = curr;
curr = next;
count++;
}
break;
}
else
{
from = cur;
cur=cur->next;
count++;
}
}
if(from!=NULL)
{
from->next=curr;
}
else
{
head = curr;
}
cur->next = to;
return head;
}
};
1、注意审题:left 和 right 表示的是位置,而不是链表的值。
2、注意标记好短点的四个位置,便于最后的三段链接。
3、用 cout 计数,找到 left 对应的位置开始翻转,一直到 right 结束翻转。重调两端的链接即可。
21.合并两个有序链表
/**
* 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* mergeTwoLists(ListNode* l1, ListNode* l2)
{
if(l1==NULL) return l2;
if(l2==NULL) return l1;
ListNode* cur1;
ListNode* cur2;
ListNode* cur;
ListNode* head;
if(l1->val<=l2->val)
{
cur = l1;
cur1 = cur->next;
cur2 = l2;
}
else
{
cur = l2;
cur1 = cur->next;
cur2 = l1;
}
head = cur;
while(1)
{
if(cur1==NULL)
{
cur->next=cur2;break;
}
if(cur2==NULL)
{
cur->next=cur1;break;
}
cout<<"cur:"<<cur->val<<endl;
cout<<"cur1:"<<cur1->val<<endl;
cout<<"cur2:"<<cur2->val<<endl;
if(cur1->val<=cur2->val)
{
cur->next = cur1;
cur = cur1;
cur1 = cur1->next;
}
else
{
cur->next = cur2;
cur = cur2;
cur2 = cur2->next;
}
if(cur1==NULL&&cur2==NULL)
break;
}
return head;
};
};
宁愿多写一些代码也别把程序逻辑搞得太复杂。
动手前先想清楚,想好用几个游标。太多太乱,太少不够。
边界的判断要想清楚,最好单独写。
86.分隔链表
/**
* 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* partition(ListNode* head, int x) {
ListNode* small = new ListNode;
ListNode* large = new ListNode;
ListNode* curSmall = small;
ListNode* curLarge = large;
ListNode* cur = head;
ListNode* curNext;
while(cur!=NULL)
{
//cout<<cur->val<<endl;
if(cur->val<x)
{
curSmall->next = cur;
curSmall = cur;
//cout<<curSmall->val<<endl<<endl;
}
else
{
curLarge->next = cur;
curLarge = cur;
cout<<curLarge->val<<endl<<endl;
}
curNext = cur->next;
cur->next = NULL;
cur= curNext;
}
//cout<<endl<<curSmall->val<<endl;
(curSmall->next) = (large->next);
return small->next;
//return head;
}
};
1、报
==42==ERROR: AddressSanitizer: heap-use-after-free on address
的错误,可能是返回的链表打结了,得仔细地看看哪里有bug。
2、指针赋值是以为,指针 next 赋值,是改变指向。
3、学会添加虚拟的链表节点。
128.排序链表
/**
* 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 {
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2)
{
if(l1==NULL) return l2;
if(l2==NULL) return l1;
ListNode* cur1;
ListNode* cur2;
ListNode* cur;
ListNode* head;
if(l1->val<=l2->val)
{
cur = l1;
cur1 = cur->next;
cur2 = l2;
}
else
{
cur = l2;
cur1 = cur->next;
cur2 = l1;
}
head = cur;
while(1)
{
if(cur1==NULL)
{
cur->next=cur2;break;
}
if(cur2==NULL)
{
cur->next=cur1;break;
}
// cout<<"cur:"<<cur->val<<endl;
// cout<<"cur1:"<<cur1->val<<endl;
// cout<<"cur2:"<<cur2->val<<endl;
if(cur1->val<=cur2->val)
{
cur->next = cur1;
cur = cur1;
cur1 = cur1->next;
}
else
{
cur->next = cur2;
cur = cur2;
cur2 = cur2->next;
}
if(cur1==NULL&&cur2==NULL)
break;
}
return head;
};
ListNode* divide(ListNode* head)
{
if(head->next==NULL) return head;
ListNode* quick;
ListNode* slow;
ListNode* last = new ListNode;
quick = head;
slow = head;
last->next = slow;
while(1)//快慢指针二分链表
{
// if(quick!=NULL)
// cout<<"slow:"<<slow->val<<"quick:"<<quick->val<<endl;
if(quick==NULL||quick->next==NULL)
{
last->next = NULL;//切断
break;
}
last = slow;
slow = slow ->next;
quick = quick->next->next;
}//头小尾轻,慢指针之后的为第二部分,被分成了两部分
head = divide(head);
//if(head->next!=NULL)
//cout<<head->val<<"\\n"<<head->next->val<<endl;
slow = divide(slow);
//cout<<slow->val<<"\\n"<<slow->next->val<<endl;
head = mergeTwoLists(head,slow);
return head;
}
public:
ListNode* sortList(ListNode* head) {
if(head==NULL)
{
return head;
}
head = divide(head);
return head;
}
};
为了性能,采用了堆排序的分治思想。合并排序的程序直接抄了前面的合并两个有序链表。
一般来说,编译通过的代码有 bug,首先应该通过肉眼去阅读代码发现,而不是急着 cout。
利用快慢指针来找链表的分界点,边界条件需要想好。
143.重排链表
/**
* 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:
void reorderList(ListNode* head) {
vector<ListNode*> nodes;
ListNode* cur = head;
while(cur!=NULL)
{
nodes.push_back(cur);
cur = cur->next;
}
int size = nodes.size();
int halfSize = size/2;
int i=0,j=size-1;
ListNode* cur1 = new ListNode;
cur1->next = head;
while(1)
{
cur1->next = nodes[i];
cur1->next->next = nodes[j];
cur1 = cur1->next->next;
i++;
j--;
if(j<i)
{
cur1->next=NULL;
break;
}
}
}
};
用 vector 来辅助别的数据结构题目,是一个非常明智的选择。
万物皆可 vector,YYDS。
141.环形链表
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
bool hasCycle(ListNode *head) {
if(head==NULL) return false;
vector<ListNode*> vec;
ListNode* cur;
cur = head;
while(1)
{
//if(cur->pos==-1) return false;
if(find(vec.begin(),vec.end(),cur)!=vec.end())
{
return true;
}
vec.push_back(cur);
cur = cur->next;
if(cur==NULL) break;
}
return false;
}
};
改用 set 性能会好一些。
142.环形链表II
/**
* 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) {
vector<ListNode*> vec;
ListNode* cur = head;
while(1)
{
if(cur==NULL) break;
auto iter = find(vec.begin(),vec.end(),cur);
if(iter!=vec.end())
{
int ind = iter-vec.begin();
return vec[ind];
}
vec.push_back(cur);
cur = cur->next;
if(cur==NULL) break;
}
return NULL;
}
};
用 vector 事半功倍。
234.回文链表
/**
* 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:
bool isPalindrome(ListNode* head) {
vector<ListNode*> vec;
ListNode* cur = head;
while(1)
{
if(cur==NULL) break;
vec.push_back(cur);
cur = cur->next;
}
cout<<vec.size()<<endl;
for(int i=0;i<vec.size()/2;i++)
{
cout<<"a:"<<vec[i]<<"and b:"<<vec[vec.size()-1-i]<<endl;
if(vec[i]->val!=vec[vec.size()-1-i]->val)
return false;
}
return true;
}
};
搞清楚是值的异同判断还是地址的异同判断。
138.复制带随机指针的链表
/*
// Definition for a Node.
class Node {
public:
int val;
Node* next;
Node* random;
Node(int _val) {
val = _val;
next = NULL;
random = NULL;
}
};
*/
class Solution {
public:
Node* copyRandomList(Node* head) {
if(head==NULL) return NULL;
unordered_map<Node*,Node*> map;
Node* cur = head;
Node* headNew = new Node(0);
//Node headNewNode(0);
//Node* headNew = &headNewNode;
//cout<<"headNew:"<<headNew<<endl;
Node* temp = headNew;
while(cur!=NULL)
{
//cout<<"cur:"<<cur<<endl;
//cout<<"temp:"<<temp<<endl;
map.insert(make_pair(cur,temp));
// map.insert(pair<Node*,Node*>(cur,temp));
//cout<<"cur:"<<cur->val<<endl;
temp->val = cur->val;
//cout<<"temp:"<<temp->val<<endl;
///cout<<"temp:"<<temp->val<<endl;
if(cur->next!=NULL)
{
Node* tempNew = new Node(0);
temp-> next = tempNew;
//cout<<"to here when temp's next = "<<temp->next->val<<endl;
}
else{
temp->next = NULL;
}
cur = cur->next;
temp = temp->next;
}
cur = head;
Node* curNew = headNew;
while(cur!=NULL)
{
auto rdm = cur->random;
if(rdm==NULL)
curNew->random = NULL;
else
{
curNew->random = map.find(cur->random)->second;
}
cout<<"cur:"<<cur->val<<endl;
cout<<"curNew:"<<curNew->val<<endl;
cur = cur->next;
curNew = curNew->next;
}
return headNew;
//return head;
}
};
1、循环体里面的申请的变量都是临时的,每次循环都要经历一次申请和释放。
2、new 的时候,new 后面的类型的传入参数要符合构造函数的传入方式。Node* temp = new Node(3)
3、new 是个好东西,没东西的时候,可以 new 一个对象。
4、函数体和类方法里面定义的变量生存周期只在函数方法内,如若要求返回一个新造的东西,要用 new,可以保持周期的全局持久。
栈和队列
155.最小栈
class MinStack {
vector<int> stack;
int m;
int size;
public:
/** initialize your data structure here. */
MinStack() {
size = 0;
}
void push(int val) {
stack.push_back(val);
size++;
}
void pop() {
stack.pop_back();
size--;
}
int top() {
return stack[size-1];
}
int getMin() {
m = stack[0];
for(auto &it:stack)
{
m = min(it,m);
}
return m;
}
};
/**
* Your MinStack object will be instantiated and called as such:
* MinStack* obj = new MinStack();
* obj->push(val);
* obj->pop();
* int param_3 = obj->top();
* int param_4 = obj->getMin();
*/
大道至简,用你觉得最快的方法做,不要想太多。
尽可能选择最合适的数据结构,比如说直接用 stack。
150.逆波兰表达式求值
class Solution {
public:
int evalRPN(vector<string>& tokens) {
stack<int> v;
for(string&it:tokens)
{
if(it=="+"||it=="-"||it=="*"||it=="/")
{
int b = v.top();
v.pop();
int a = v.top();
v.pop();
switch(it[0])
{
case '+': v.push(a+b);break;
case '-': v.push(a-b);break;
case '*': v.push(a*b);break;
case '/': v.push(a/b);break;
}
}
else
{
cout<<it<<endl;
v.push(atoi(it.c_str()));
}
}
return v.top();
}
};
1、resize之后有值存在了。
2、有明显尺寸伸缩的用stack来做比较好。
3、尽可能使用熟悉但正确的东西来做。
394.字符串解码
class Solution {
public:
string decodeString(string s) {
int len = s.size();
stack<pair<string,int>> stk;
stk.push(make_pair("",0));
int number = 0;
string sTmp="";
for(int i=0;i<len;i++)
{
char c = s[i];
if(c>='0'&&c<='9')
{
number = 10*number+(c-'0');
}
else if((c>='a'&&c<='z')||(c>='A'&&c<='Z'))
{
stk.top().first = stk.top().first+c;
}
else if(c=='[')
{
stk.top().second = number;
number = 0;
stk.push(make_pair("",0));
}
else if(c==']')
{
auto cur = stk.top();
stk.pop();
for(int i=0;i<stk.top().second;i++)
stk.top().first = stk.top().first+cur.first;
}
}
return stk.top().first;
}
};
1、string 可以当成是字符串数组使用。string[i] 是 char 类型。
2、敢于猜测一些数据结构的用法,错误了再修正。
3、stack 可以直接取值修改。
4、指针类型取不同位置用->
, 一般用.
。
5、做字符问题,最好给变量取一个好一点的名字,这样不会容易混淆。
6、尽可能用简单的数据结构。
7、总结:这种题目如果之前都没有接触过,还是有一定难度的,需要想想。
94.二叉树的中序遍历
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
int recursion(TreeNode* root,vector<int> &result)
{
if(root==NULL) return 0;
TreeNode* left = root->left;
TreeNode* right = root->right;
recursion(left,result);
result.push_back(root->val);
recursion(right,result);
return 0;
}
public:
vector<int> inorderTraversal(TreeNode* root) {
vector<int> result;
recursion(root,result);
return result;
}
};
按递归的左->中->右的顺序进行遍历取值,自然就是中序遍历了,这可以思考。
用一个 vector 不断进行引用传值即可。
133.克隆图
/*
// Definition for a Node.
class Node {
public:
int val;
vector<Node*> neighbors;
Node() {
val = 0;
neighbors = vector<Node*>();
}
Node(int _val) {
val = _val;
neighbors = vector<Node*>();
}
Node(int _val, vector<Node*> _neighbors) {
val = _val;
neighbors = _neighbors;
}
};
*/
class Solution {
unordered_map<Node*,Node*> map;
int recursion(Node* node)
{
if(map.find(node)!=map.end())
{
return 0;
}
Node* newNode = new Node(node->val);
map[node] = newNode;
for(auto &it:node->neighbors)
{
recursion(it);
newNode->neighbors.push_back(map[it]);
}
return 0;
}
public:
Node* cloneGraph(Node* node) {
if(node==NULL) return NULL;
Node* nodeCopy = new Node(node->val);
map[node] = nodeCopy;
if(empty(node->neighbors))
{
return nodeCopy;
}
for(auto &it:node->neighbors)
{
recursion(it);
nodeCopy->neighbors.push_back(map[it]);
}
return nodeCopy;
}
};
1、unordered_map 支持中括号建值。
2、深拷贝的题一般可以做一个复制前复制后节点的一个 map。
3、map 有 find 方法。
4、递归容易错,主要是 neighbors.push_back 的点位要找准,一个 neighbor 递归完,就可以 push 它了。
200.岛屿数量
class Solution {
int m,n;
int recusion(vector<vector<char>>& grid,int i,int j)
{
if(i==-1||j==-1||i==m||j==n||grid[i][j]=='0')
return 0;
grid[i][j] = '0';
recusion(grid,i-1,j);
recusion(grid,i,j-1);
recusion(grid,i,j+1);
recusion(grid,i+1,j);
return 0;
}
public:
int numIslands(vector<vector<char>>& grid) {
m = grid.size();
n = grid[0].size();
int count = 0;
for(int i=0;i<m;i++)
{
for(int j=0;j<n;j++)
{
if(grid[i][j]=='1')
{
count++;
recusion(grid,i,j);
}
}
}
return count;
}
};
84.柱状图中的最大矩形
class Solution {
public:
int largestRectangleArea(vector<int>& heights) {
int maxN = 0;
for(int i=0;i<heights.size();i++)
{
int ii = i;
while(ii!=0&&heights[ii-1]>=heights[i])
{
ii--;
}
int jj = i;
while(jj!=heights.size()-1&&heights[jj+1]>=heights[i])
{
jj++;
}
maxN = max(maxN,(jj-ii+1)*heights[i]);
// if(i==4)
// {
// cout<<heights[i]<<endl;
// cout<<jj<<endl;
// cout<<ii<<endl;
// }
}
return maxN;
}
};
暴力解法超时了。下面是非暴力的栈解法。
class Solution {
public:
int largestRectangleArea(vector<int>& heights) {
stack<int> stk;
int N = heights.size();
int Max=0;
vector<int> leftBound(N,-1);
int i = 0;
while(1)
{
// if(i>0)
// { cout<<"stk.top()"<<stk.top()<<endl;
// cout<<"leftBound[stk.top()]"<<leftBound[stk.top()]<<endl;
// cout<<empty(stk)<<endl;
// cout<<"heights[i]"<<heights[i]<<endl;
// }
if(!empty(stk))//对于相等的情况的特殊处理
{
if(heights[i]!=heights[stk.top()])
{
//cout<<"i:"<<i<<endl;
以上是关于LeetCode 刷题汇总(第一阶段,随机游走)的主要内容,如果未能解决你的问题,请参考以下文章