文章目录
03 数组中重复的数字
法一:用set
class Solution {
public:
int findRepeatNumber(vector<int>& nums) {
set<int> s;
for(int i=0;i<nums.size();i++){
if(s.count(nums[i])) return nums[i];
else s.insert(nums[i]);
}
return -1;
}
};
法二:排序
class Solution {
public:
int findRepeatNumber(vector<int>& nums) {
sort(nums.begin(),nums.end());
for(int i=0;i<nums.size()-1;i++){
if(nums[i]==nums[i+1]){
return nums[i];
}
}
return -1;
}
};
法三:鸽巢原理
因为出现的元素值 < nums.size(); 所以我们可以将见到的元素 放到索引的位置,如果交换时,发现索引处已存在该元素,则重复 O(N) 空间O(1)
class Solution {
public:
int findRepeatNumber(vector<int>& nums) {
for(int i=0;i<nums.size();i++){
while(nums[i]!=i){
if(nums[nums[i]] == nums[i]) return nums[i];
int tmp = nums[i];
nums[i] = nums[tmp];
nums[tmp] = tmp;
}
}
return -1;
}
};
04查找矩阵中一个数
法一:直接搜索
class Solution {
public:
bool findNumberIn2DArray(vector<vector<int>>& matrix, int target) {
for(int i=0;i<matrix.size();i++){
for(int j=0;j<matrix[0].size();j++){
if(target==matrix[i][j])return true;
}
}
return false;
}
};
法二:线性查找,需要满足这个列递增和行递增的特点
class Solution {
public:
bool findNumberIn2DArray(vector<vector<int>>& matrix, int target) {
if(matrix.size()==0||matrix[0].size()==0) return false;
int i=0,j=matrix[0].size()-1;
while(i<matrix.size() && j>=0){//注意这里j>=0保证0也要判断
if(matrix[i][j]==target) return true;
if(matrix[i][j]<target) i++;
else j--;
}
return false;
}
};
05替换空格
class Solution {
public:
string replaceSpace(string s) {
string result;
for(int i=0;i<s.size();i++){
if(s[i]==' '){
result.push_back('%');
result.push_back('2');
result.push_back('0');
}
else{
result.push_back(s[i]);
}
}
return result;
}
};
06从尾到头打印链表
法一:弄到数组里再逆序打印
class Solution {
public:
vector<int> reversePrint(ListNode* head) {
vector<int> re;
ListNode * pnode=head;
while(pnode){
re.push_back(pnode->val);
pnode=pnode->next;
}
for(int i=0;i<re.size()/2;i++){
swap(re[i],re[re.size()-1-i]);
}
return re;
}
};
法二:递归
class Solution {
public:
vector<int> reversePrint(ListNode* head) {
if(head==nullptr) return{};
vector<int>a=reversePrint(head->next);
a.push_back(head->val);
return a;
}
};
07重建二叉树
class Solution {
public:
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
return build(preorder,inorder,0,0,inorder.size()-1);
}
TreeNode* build(vector<int>& preorder,vector<int>& inorder,int root,int start,int end)//前序root 中序start和end
{
if(start>end) return NULL;
TreeNode *tree = new TreeNode(preorder[root]);//根据前序得到根节点
int i=start;
while(i<end && preorder[root]!=inorder[i]) i++;//找到中序遍历中的根节点
tree->left=build(preorder,inorder,root+1,start,i-1);//前序遍历,每次都是一个子树的根节点
tree->right=build(preorder,inorder,root+1+i-start,i+1,end);//i-start得到左子树的数量
return tree;
}
};
09用两个栈实现队列
class CQueue {
public:
stack<int> a;
stack<int> b;
CQueue() {
}
void appendTail(int value) {
a.push(value);
}
int deleteHead() {
if(a.empty() && b.empty()) return -1;
else if(b.empty() && !a.empty()){
while(!a.empty()){
b.push(a.top());
a.pop();
}
}
int temp=b.top();
b.pop();
return temp;
}
};
10 -1斐波那契数列
简单dp 注意取模
class Solution {
public:
int fib(int n) {
if(n==0) return 0;
if(n==1) return 1;
vector<int> result;
result.push_back(0);
result.push_back(1);
for(int i=2;i<=n;i++){
int temp=(result[i-1]+result[i-2])%1000000007;
result.push_back(temp);
}
return result[result.size()-1];
}
};
递归:
class Solution {
public:
int numWays(int n) {
if(n == 0 || n == 1)
return 1;
return numWays(n-1) + numWays(n-2);
}
};
10-2青蛙跳台
class Solution {
public:
int numWays(int n) {
if(n==0) return 1;
if(n==1) return 1;
vector<int> dp;
dp.push_back(1);
dp.push_back(1);
for(int i=2;i<=n;i++){
int temp=(dp[i-1]+dp[i-2])%1000000007;
dp.push_back(temp);
}
return dp[n];
}
};
11旋转数组的最小值
class Solution {
public:
int minArray(vector<int>& numbers) {
int low=0,high=numbers.size()-1;
while(low<high){
int mid=low+(high-low)/2;
if(numbers[mid]>numbers[high]) low=mid+1;//34512mid在左,转折点在mid+1-high
else if(numbers[mid]<numbers[high]) high=mid;//mid在右边,转折点在low-mid
else high--;//无法确定左还是右,缩小范围
}
return numbers[high];//or low
}
};
12矩阵中的路径
搜索 回溯
class Solution {
public:
vector<int> dx={0,0,1,-1};
vector<int> dy={1,-1,0,0};
bool find(vector<vector<char>>& board,string &word,int x,int y,int index){
if(index==word.size()){
return true;
}
if(x>board.size()-1||y>board[0].size()-1||x<0||y<0) return false;
if(board[x][y]==word[index] && board[x][y]!='*'){
board[x][y]='*';
for(int i=0;i<4;i++){
if(find(board,word,x+dx[i],y+dy[i],index+1)) return true;
}
board[x][y]=word[index];
}
return false;
}
bool exist(vector<vector<char>>& board, string word) {
for(int i=0;i<board.size();i++){
for(int j=0;j<board[0].size();j++){
if(find(board,word,i,j,0)) return true;
}
}
return false;
}
};
注意一个点,为什么下面这样会超时?因为find那里四种情况无论如何都走了一遍,上面的写法一旦有true就可以返回了,相当于没有剪枝。
class Solution {
public:
bool hasroute=false;
vector<int> dx={0,0,1,-1};
vector<int> dy={1,-1,0,0};
void find(vector<vector<char>>& board,string word,int x,int y,int index,vector<vector<int>>& visit){
if(index==word.size()){
hasroute=true;
return;
}
if(x>board.size()-1||y>board[0].size()-1||x<0||y<0) return;
if(board[x][y]==word[index] && visit[x][y]==0){
visit[x][y]=1;
for(int i=0;i<4;i++){
find(board,word,x+dx[i],y+dy[i],index+1,visit);
}
visit[x][y]=0;
}
}
bool exist(vector<vector<char>>& board, string word) {
vector<vector<int>> visit(board.size(),vector<int>(board[0].size(),0));
for(int i=0;i<board.size();i++){
for(int j=0;j<board[0].size();j++){
find(board,word,i,j,0,visit);
}
}
return hasroute;
}
};
13机器人的运动范围
dfs 不回溯
class Solution {
public:
vector<int> dx={0,0,1,-1};
vector<int> dy={1,-1,0,0};
int result=0;
bool judge(int x,int y,int k){
int sum=0;
while(x>0){
sum+=x%10;
x/=10;
}
while(y>0){
sum+=y%10;
y/=10;
}
if(sum>k) return false;
else return true;
}
int move(int x,int y,int m,int n,int k,vector<vector<int>> & visit){
if(x>m-1 || y>n-1 || x<0 || y<0) return result;
if(!judge(x,y,k)||visit[x][y]==1) return result;
visit[x][y]=1;
result++;
int maxn=0;
for(int i=0;i<4;i++){
maxn=max(maxn,move(x+dx[i],y+dy[i],m,n,k,visit));
}
//visit[x][y]=0;
return maxn;
}
int movingCount(int m, int n, int k) {
vector<vector<int>> visit(m,vector<int>(n,0));
return move(0,0,m,n,k,visit);
}
};
注意这里为啥不把visit还原?因为求的是范围,去过的就算,是多条路线求和,不是仅仅算一条路径。
14-1割绳子(未做)
15二进制中1的个数
技巧就是n&(n-1)可以消除最低位的1
class Solution {
public:
int hammingWeight(uint32_t n) {
int re=0;
while(n>0){
re++;
n=n&(n-1);
}
return re;
}
};
18 删除链表的节点
O(n)方法 注意这里的dummynode是为了让头节点也能通过p->next得到
class Solution {
public:
ListNode* deleteNode(ListNode* head, int val) {
ListNode* dummy=new ListNode(1);
dummy->next=head;
ListNode * p=dummy;
while(p && p->next){
if(p->next->val==val){
p->next=p->next->next;
break;
}else{
p=p->next;
}
}
return dummy->next;
}
};
22链表中倒数第k个节点
用vector存放指针值,再返回
class Solution {
public:
ListNode* getKthFromEnd(ListNode* head, int k) {
vector<ListNode*> res;
while(head){
res.push_back(head);
head=head->next;
}
return res[res.size()-k];
}
};
24翻转链表
class Solution {
public:
ListNode* reverseList(ListNode* head) {
ListNode* cur = NULL, *last = head;
while (last != NULL) {
ListNode* t = last->next;//保存用于更新cur
last->next = cur;
cur = last;
last = t;//cur和last同时移动
}
return cur;
}
};
25合并两个排序链表
class Solution {
public:
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
ListNode *dummy=new ListNode(1);
ListNode* p1=l1,*p2=l2,*pr=dummy;
while(p1 && p2){
if(p1->val>p2->val){
pr->next=p2;
pr=pr->next;
p2=p2->next;
} else{
pr->next=p1;
pr=pr->next;
p1=p1->next;
}
}
if(p1==nullptr){
pr->next=p2;
}
else{
pr->next=p1;
}
return dummy->next;
}
};
26树的子结构
class Solution {
public:
bool isSubStructure(TreeNode* A, TreeNode* B) {//这一步是遍历A树,写的时候先不管跟B比较
if(A==nullptr||B==nullptr) return false;
if(isPartSame(A,B)) return true;
else return(isSubStructure(A->left,B)||isSubStructure(A->right,B));
}
bool isPartSame(TreeNode* A,TreeNode* B){
if(B==nullptr)return true;//B中节点可以为空,因为B是子部分
if(A==nullptr)return false;//B不为空但是A为空,不行
if(A->val==B->val){
return isPartSame(A->left,B->left)&&isPartSame(A->right,B->right);
}
else{
return false;
}
}
};
27二叉树的镜像
递归
class Solution {
public:
TreeNode* mirrorTree(TreeNode* root) {
if(root==nullptr) return nullptr;
swap(root->left,root->right);
mirrorTree(root->left);
mirrorTree(root->right);
return root;
}
};
栈实现二叉树先序遍历
class Solution {
public:
TreeNode* mirrorTree(TreeNode* root) {
stack<TreeNode*>s;
s.push(root);
while(!s.empty()){
TreeNode* node=s.top();
s.pop();
if(node==nullptr){
continue;
}
swap(node->left,node->right);
s.push(node->left);
s.push(node->right);
}
return root;
}
};
队列实现二叉树层次遍历
class Solution {
public:
TreeNode* mirrorTree(TreeNode* root) {
queue<TreeNode*> q;
q.push(root);
while(!q.empty()){
TreeNode* node=q.front();
q.pop();
if(node==nullptr) continue;
swap(node->left,node->right);
q.push(node->right);
q.push(node->left);
}
return root;
}
};
28 对称二叉树
class Solution {
public:
bool isSymmetric(TreeNode* root) {
if(root==NULL) return true;
return recur(root->left,root->right);
}
bool recur(TreeNode* ltree,TreeNode* rtree){
if(ltree==NULL && rtree==NULL) return true;
if(ltree==NULL || rtree==NULL || ltree->val!=rtree->val) return false;
return recur(ltree->left,rtree->right) && recur(ltree->right,rtree->left);
}
};
30包含min的栈
class MinStack {
public:
/** initialize your data structure here. */
stack <int> A;
stack<int> B;
MinStack() {
}
void push(int x) {
A.push(x);
if(B.empty()||x<=B.top()){//这里注意顺序
B.push(x);
}
}
void pop() {
if(A.top()==B.top()){
B.pop();
}
A.pop();
}
int top() {
return A.top();
}
int min() {
return B.top();
}
};
/**
* Your MinStack object will be instantiated and called as such:
* MinStack* obj = new MinStack();
* obj->push(x);
* obj->pop();
* int param_3 = obj->top();
* int param_4 = obj->min();
*/
32 从上到下打印二叉树
拓展
转:https://leetcode-cn.com/problems/cong-shang-dao-xia-da-yin-er-cha-shu-iii-lcof/solution/cli-yong-dequeshuang-duan-dui-lie-hao-shi-0ms-ji-b/
class Solution {
public:
vector<vector<int>> levelOrder(TreeNode* root) {
vector<vector<int>> res;
if (root==NULL)
return res;
bool flag = true; //从左向右打印为true,从右向左打印为false
deque<TreeNode*> q;
q.push_back(root);
while (!q.empty())
{
int n = q.size();
vector<int> out;
TreeNode* node;
while (n>0)
{
if (flag) // 前取后放:从左向右打印,所以从前边取,后边放入
{
node = q.front();
q.pop_front();
if (node->left)
q.push_back(node->left); // 下一层顺序存放至尾
if (node->right)
q.push_back(node->right);
}
else //后取前放: 从右向左,从后边取,前边放入
{
node = q.back();
q.pop_back();
if (node->right)
q.push_front(node->right); // 下一层逆序存放至首
if (node->left)
q.push_front(node->left);
}
out.push_back(node->val);
n--;
}
flag = !flag;
res.push_back(out);
}
return res;
}
};
33二叉搜索树的后序遍历序列
class Solution {
public:
bool verifyPostorder(vector<int>& postorder) {
return recur(postorder,0,postorder.size()-1);
}
bool recur(vector<int> postorder,int i,int j){
if(i>=j)return true;
int p=i;
while(postorder[p]<postorder[j]) p++;//j是根节点,左子树都小于根节点
int m=p;//找到m是右子树根节点
while(postorder[p]>postorder[j])p++;//右子树都大于根节点
return p==j&& recur(postorder,i,m-1)&&recur(postorder,m,j-1);
}
};
40最小的k个数
法一:排序取k个
class Solution {
public:
vector<int> getLeastNumbers(vector<int>& arr, int k) {
sort(arr.begin(),arr.end());
vector<int> re(k,0);
for(int i=0;i<k;i++){
re[i]=arr[i];
}
return re;
}
};
时间:O(nlogn) 空间:O(logn)
法二:大根堆
class Solution {
public:
vector<int> getLeastNumbers(vector<int>& arr, int k) {
vector<int> vec(k,0);
if(k==0)return vec;
priority_queue<int> q;
for(int i=0;i<k;i++)q.push(arr[i]);
for(int i=k;i<(int)arr.size();i++){
if(q.top()>arr[i]){
q.pop();
q.push(arr[i]);
}
}
for(int i=0;i<k;i++){
vec[i]=q.top();
q.pop();
}
return vec;
}
};
时间:O(nlogk) 空间 O(k)
法三:快速排序:
class Solution {
public:
int partition(vector<int> &nums,int low,int high){
int p= rand()%(high-low+1)+low;
swap(nums[p],nums[high]);
int i=low,j=low;
for(;j<high;j++){
if(nums[j]<nums[high]){
swap(nums[i++],nums[j]);
}
}
swap(nums[high],nums[i]);
return i;
}
void findPart(vector<int> &nums,int low,int high,int k){
if(low>high)return;
int p=partition(nums,low,high);
if(p==k)return;
if(p>k)findPart(nums,low,p-1,k);
else findPart(nums,p+1,high,k);
}
vector<int> getLeastNumbers(vector<int>& arr, int k) {
findPart(arr,0,arr.size()-1,k);
vector<int> re(k,0);
for(int i=0;i<k;i++){
re[i]=arr[i];
}
return re;
}
};
时间:O(n) 最坏情况为O(n^2) 空间:O(logn)
49 丑数
自己写的dp,,时间复杂度有点高
class Solution {
public:
int nthUglyNumber(int n) {
vector<long> dp(n,0);
dp[0]=1;
for(int i=1;i<n;i++){
long amin=dp[i-1]*2;
for(int k=0;k<i;k++){
long temp=0;
if((temp=dp[k]*2)>dp[i-1] || (temp=dp[k]*3)>dp[i-1] || (temp=dp[k]*5)>dp[i-1]){
amin=min(temp,amin);
dp[i]=amin;
}
}
}
return dp[n-1];
}
};
三指针:
https://leetcode-cn.com/problems/chou-shu-lcof/comments/250364
int nthUglyNumber(int n) {
if(!n)return 0;
vector<int>ugly(n,0);
ugly[0] = 1; //基础丑数为1
int i=0,j=0,k=0; //初始分别指向三个有序链表第一个元素,这三个有序链表是想象出来的,分别就是ugly数组元素分别乘以2,3,5得到的
for(int idx=1;idx<n;idx++)
{
int tmp = min(ugly[i]*2,min(ugly[j]*3,ugly[k]*5));
//三个链表可能有相同元素,所以只要是最小的,都要移动指针
if(tmp == ugly[i]*2)i++;
if(tmp == ugly[j]*3)j++;
if(tmp == ugly[k]*5)k++;
ugly[idx] = tmp;
}
return ugly[n-1];
}
55二叉树深度
法一:dfs递归
class Solution {
public:
int maxDepth(TreeNode* root) {
if(root){
return max(1+maxDepth(root->right),1+maxDepth(root->left));
}
else return 0;
}
};
法二:利用队列BFS
class Solution {
public:
int maxDepth(TreeNode* root) {
if(root==NULL)return 0;
queue<TreeNode*> q;
q.push(root);
int res=0;
while(!q.empty()){
queue <TreeNode*>temp;//用于存放下一层节点
while(!q.empty()){
TreeNode* cur=q.front();
q.pop();
if(cur->left) temp.push(cur->left);
if(cur->right) temp.push(cur->right);
}
res++;
q=temp;
}
return res;
}
};
时间复杂度都是O(n)要遍历所有节点