剑指OFFER

23 篇文章 0 订阅

找出数组中重复的数字。

在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。

位图法:

class Solution {
public:
    int findRepeatNumber(vector<int>& nums) {
        int * bitmap;
        int index,bit,tmp;
        int size=(100000/32+1)*sizeof(int);
        bitmap= (int*) malloc(size);
        memset(bitmap,0,size);
        for(auto e: nums){
            index=e/32;
            bit=e-index*32;
            tmp=bitmap[index]>>bit;
            tmp=tmp & 1;
            if (tmp){
                free(bitmap);
                return e;
            }else{
                bitmap[index]|=1<<bit;
            }
        }
        free(bitmap);
        return 0;
    }
};

就地法:
遍历nums
假设nums[i]=tmp
如果nums[tmp]=tmp,返回tmp
否则交换nums[tmp]和 nums[i]

剑指 Offer 04. 二维数组中的查找

在一个 n * m 的二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个高效的函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。

示例:

现有矩阵 matrix 如下:

[
[1, 4, 7, 11, 15],
[2, 5, 8, 12, 19],
[3, 6, 9, 16, 22],
[10, 13, 14, 17, 24],
[18, 21, 23, 26, 30]
]

给定 target = 5,返回 true。

给定 target = 20,返回 false。

思路: 从左下角开始,若小于target,列数加1,反之行数减一。

class Solution {
public:
    bool findNumberIn2DArray(vector<vector<int>>& matrix, int target) {
        int row=matrix.size();
        if (row==0){
            return false;
        }
        int col=matrix[0].size();
        int i=row-1;
        int j=0;
        while(true){
            if(i>=0 &&j<=col-1){
                if(matrix[i][j]>target){
                   i-=1;
                }else if(matrix[i][j]<target){
                    j+=1;
                }else{
                    return true;
                }
            }else{
                return false;
            }
        }
    }
};

青蛙跳台阶问题

一只青蛙一次可以跳上1级台阶,也可以跳上2级台阶。求该青蛙跳上一个 n 级的台阶总共有多少种跳法。

答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。

示例 1:

输入:n = 2
输出:2

思路 : 在循环内部取余,而不是在return 取余

class Solution {
public:
    int numWays(int n) {
        if(n == 0)  return 1;//没有台阶,直接在上面,一种
        if(n <= 2)  return n;//一个或两个台阶
        int a = 1, b = 2, c = 0;//a保存f(n-2),b保存f(n-1),c保存f(n)
        for(int i = 3; i <= n; ++i)//i:第i个台阶
        {
            c = (a + b) % 1000000007;//第i个台阶跳法:a:i-2个台阶跳发,b:i-1个台阶跳发
            a = b;//a保存b
            b = c;//b保存c
        }
        return c;
    }
};

给定一个二维网格和一个单词,找出该单词是否存在于网格中。

单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。

示例:

board =
[
[‘A’,‘B’,‘C’,‘E’],
[‘S’,‘F’,‘C’,‘S’],
[‘A’,‘D’,‘E’,‘E’]
]

给定 word = “ABCCED”, 返回 true
给定 word = “SEE”, 返回 true
给定 word = “ABCB”, 返回 false

class Solution {
public:
    bool exist(vector<vector<char>>& board, string &word) {
        int row=board.size();
        int col=board[0].size();
        vector<char> tmp;
        for (int r=0;r<row;r++){
            for (int c=0;c<col;c++){
                if(dfs(r,c,tmp, board,word)){
                    return true;
                }
            }
        }
        return false;
    }
    bool dfs(int i,int j, vector<char> &tmp,vector<vector<char>>& board,string &word){
        int len=tmp.size();
        int row=board.size();
        int col=board[0].size();
        if (i==row || j==col || i==-1 || j==-1 ||board[i][j]!=word[len]|| board[i][j]==' '){
            return false;
        }

        if (len==(word.size()-1)){
            return true;
        }
        tmp.push_back(board[i][j]);
        board[i][j]=' ';
        bool ret=dfs(i,j+1,tmp,board,word) || dfs(i+1,j,tmp,board,word) || dfs(i,j-1,tmp,board,word)||dfs(i-1,j,tmp,board,word) ;
        board[i][j]=tmp.back();
        tmp.pop_back();
        return ret;
    }
};

无重叠区间

给定一个区间的集合,找到需要移除区间的最小数量,使剩余区间互不重叠。

注意:

可以认为区间的终点总是大于它的起点。
区间 [1,2] 和 [2,3] 的边界相互“接触”,但没有相互重叠。

示例 1:

输入: [ [1,2], [2,3], [3,4], [1,3] ]

输出: 1

解释: 移除 [1,3] 后,剩下的区间没有重叠。

  • 自己的解法(没用排序,超时)
struct node{
    node *left;
    node *right;
    vector<int> *value;
}*root;
class Solution {
public:
    int max_num=-100000;
    int eraseOverlapIntervals(vector<vector<int>>& intervals) {
        if (intervals.size()==0){
            return 0;
        }
        vector<int> root_vector(2,100000);
        root=new node{NULL,NULL,&root_vector};
        int path=0;
        for (int i=0;i<intervals.size();i++){
            dfs(intervals,i,path);
        }

        return intervals.size()-max_num;
    }
// (*(cur.value))[0];
    void dfs(vector<vector<int>>& intervals,int start, int& path){
        int start_left=intervals[start][0];
        int start_right=intervals[start][1];
        int cur_left;
        int cur_right;
        node* new_node;
        node* cur=root;
        node* pre=cur;
        int flag=0;
        while(cur){
            cur_left=(*(cur->value))[0];
            cur_right=(*(cur->value))[1];
            if(start_left>=cur_right){
                pre=cur;
                cur=cur->right;
                flag=2;
            }
            else if (start_right<=cur_left){
                pre=cur;
                cur=cur->left;
                flag=1;
            }
            else{
                flag=0;
                break;
            }
        }

        if(flag>0){
            path++;
            if(path>max_num){
                max_num=path;
            }
            if(flag==1){
                new_node=new node{NULL,NULL,&(intervals[start])};
                pre->left=new_node;
            }else{
                new_node=new node{NULL,NULL,&(intervals[start])};
                pre->right=new_node;                
            }
            for(int i=start+1;i<intervals.size();i++){
                dfs(intervals,i,path);
            }
            path--;
            if(flag==1){
                pre->left=NULL;
            }else{
                pre->right=NULL;
            }
            delete new_node;
            return;
        }
        return;
    }


};

思路: 采用回溯法,对于当前的子集建立一颗排序树(区间),如果新的区间能插入就加入子集,求出子集的最大元素个数。

  • 动态规划,先按照左或者右端点排序,然后状态转移:以j区间结尾的最大区间数是以1到j-1结尾的最大区间数当中,最大值加1,并且不发生重叠。

  • 贪心法,按照右节点排序,每次都从剩下的区间中选右端点最小且不发生重叠的。

正则表达式匹配

请实现一个函数用来匹配包含’. ‘和’‘的正则表达式。模式中的字符’.‘表示任意一个字符,而’'表示它前面的字符可以出现任意次(含0次)。在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串"aaa"与模式"a.a"和"abaca"匹配,但与"aa.a"和"ab*a"均不匹配。

思路模拟nfa,并且用两个栈来保存新当前到达状态,和下一轮到达状态。

struct State{
    bool isEnd;
    map<char,int> digitTrans;
    vector<int> emptyTrans;
};

class Solution {
public:
    vector<State*> stateVector;
    void buildState(string p){
        // cout<<"ori"<<stateVector.size()<<endl;
        int pre=0;
        State* startState=new State;
        startState->isEnd=false;
        stateVector.push_back(startState);
        for(int i=0;i<p.size();i++){
            char digit=p[i];
            if(i+1<p.size() && p[i+1]=='*'){
                State* e1=new State;
                e1->isEnd=false;
                State* e2=new State;
                e2->isEnd=false;
                State* e3=new State;
                e3->isEnd=false;
                stateVector.push_back(e1);
                stateVector.push_back(e2);
                stateVector.push_back(e3);
                stateVector[pre]->emptyTrans.push_back(pre+1);
                stateVector[pre]->emptyTrans.push_back(pre+3);
                e1->digitTrans[digit]=pre+2;
                e2->emptyTrans.push_back(pre+1);
                e2->emptyTrans.push_back(pre+3);
                pre=pre+3;
                // cout<<"digit"<<digit<<endl;
            }else if(p[i]!='*'){
                State* e1=new State;
                e1->isEnd=false;
                stateVector.push_back(e1);
                stateVector[pre]->digitTrans[digit]=pre+1;
                pre=pre+1;
            }

        }
        State* e1=new State;
        e1->isEnd=true;
        stateVector.push_back(e1);
        stateVector[pre]->emptyTrans.push_back(pre+1);
        return;
    }

    void addState(vector<int>& alreadyOn, stack<int>& oldState, stack<int>& newState, int stateIndex){
        alreadyOn[stateIndex]=1;
        State* s=stateVector[stateIndex];
        newState.push(stateIndex);
        for(int index : s->emptyTrans){
            if(alreadyOn[index]!=1){
                addState(alreadyOn,oldState,newState,index);
            }
        }
    }

    bool isMatch(string s, string p) {
        buildState(p);
        stack<int> oldState;
        stack<int> newState;
        vector<int> alreadyOn(100,0);
        addState(alreadyOn,oldState,newState,0);
        // while(!newState.empty()){
        //     int s=newState.top();
        //     cout<<s<<endl;
        //     newState.pop();         
        // }
        // cout<<"newsize"<<newState.size()<<endl;
        bool flag=false;
        for(int i=0;i<s.size();i++){
            char c=s[i];
            while(!newState.empty()){
                int s=newState.top();
                newState.pop();
                alreadyOn[s]=0;
                oldState.push(s);
            }
            // cout<<"oldsize"<<oldState.size()<<endl;
            while(!oldState.empty()){
                int s=oldState.top();
                map<char,int> digitTrans=stateVector[s]->digitTrans;
                // map<char, int>::iterator iter = digitTrans.begin();
                // cout<<iter->first<<endl;
                if(digitTrans.find('.')!=digitTrans.end()){
                    int t=digitTrans['.'];
                    if(alreadyOn[t]!=1){
                        addState(alreadyOn,oldState,newState,t);
                    }
                }else{
                    if(digitTrans.find(c)!=digitTrans.end()){
                        cout<<c<<endl;
                         int t=digitTrans[c];
                        if(alreadyOn[t]!=1){
                            addState(alreadyOn,oldState,newState,t);
                        }  
                    }
                }
                oldState.pop();
            }
            // cout<<"newsize"<<newState.size()<<endl;
        }
        
        while(!newState.empty()){
            int s=newState.top();
            // cout<<"final"<<s<<endl;
            if(stateVector[s]->isEnd){
                flag=true;
            }
            newState.pop();         
        }
        for(auto state : stateVector){
            delete state;
        }
        return flag;
    }
};:

剑指 Offer 20. 表示数值的字符串

请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。例如,字符串"+100"、“5e2”、"-123"、“3.1416”、"-1E-16"、“0123"都表示数值,但"12e”、“1a3.14”、“1.2.3”、"±5"及"12e+5.4"都不是。

思路: 小数点左边或者右边不能都为空,所以有小数点左边不为空和为空两种分支。数字中间不能有空格,两边可以有空格,所以把空格当成输入,初始节点和终止节点能接受空格。

struct State{
    bool isEnd;
    map<char,int> digitTrans;
    vector<int> emptyTrans;
};


class Solution {
public:
    vector<State*> stateVector;
    void buildState(){
        State* startState=new State;
        startState->isEnd=false;
        stateVector.push_back(startState);

        State* e1=new State;
        e1->isEnd=false;
        stateVector.push_back(e1);
        startState->emptyTrans.push_back(1);
        startState->digitTrans['+']=1;
        startState->digitTrans['-']=1;
        startState->digitTrans[' ']=0;

        State* e2=new State;
        e2->isEnd=false;
        stateVector.push_back(e2);
        e1->digitTrans['d']=2;
        e2->digitTrans['d']=2;

        State* e3=new State;
        e3->isEnd=false;
        stateVector.push_back(e3);
        e2->digitTrans['.']=3;

        State* e4=new State;
        e4->isEnd=false;
        stateVector.push_back(e4);
        e3->emptyTrans.push_back(4);
        e4->digitTrans['d']=4;

        State* e5=new State;
        e5->isEnd=false;
        stateVector.push_back(e5);
        e4->emptyTrans.push_back(5);
        e2->emptyTrans.push_back(5);
        e5->emptyTrans.push_back(12);

        State* e6=new State;
        e6->isEnd=false;
        stateVector.push_back(e6);
        e5->digitTrans['E']=6;
        e5->digitTrans['e']=6;

        State* e7=new State;
        e7->isEnd=false;
        stateVector.push_back(e7);
        e6->emptyTrans.push_back(7);
        e6->digitTrans['+']=7;
        e6->digitTrans['-']=7;

        State* e8=new State;
        e8->isEnd=false;
        stateVector.push_back(e8);
        e7->digitTrans['d']=8;
        e8->digitTrans['d']=8;
        

        State* e9=new State;
        e9->isEnd=false;
        stateVector.push_back(e9);
        e1->emptyTrans.push_back(9);
        e9->digitTrans['d']=9;
        e9->emptyTrans.push_back(10);       

        State* e10=new State;
        e10->isEnd=false;
        stateVector.push_back(e10);
        e9->digitTrans['.']=10;

        State* e11=new State;
        e11->isEnd=false;
        stateVector.push_back(e11);
        e10->digitTrans['d']=11;
        e11->digitTrans['d']=11;
        e11->emptyTrans.push_back(5);

        State* e12=new State;
        e12->isEnd=true;
        stateVector.push_back(e12);
        e8->emptyTrans.push_back(12);
        e12->digitTrans[' ']=12;


        return;

    }
    void addState(vector<int>& alreadyOn, stack<int>& oldState, stack<int>& newState, int stateIndex){
        alreadyOn[stateIndex]=1;
        State* s=stateVector[stateIndex];
        newState.push(stateIndex);
        for(int index : s->emptyTrans){
            if(alreadyOn[index]!=1){
                addState(alreadyOn,oldState,newState,index);
            }
        }
    }

    bool isNumber(string s) {
        buildState();
        stack<int> oldState;
        stack<int> newState;
        vector<int> alreadyOn(100,0);
        addState(alreadyOn,oldState,newState,0);
        bool flag=false;
        int count=0;
        for(int i=0;i<s.size();i++){
            char c=s[i];
            bool flag2=false;
            while(!newState.empty()){
                int s=newState.top();
                newState.pop();
                alreadyOn[s]=0;
                oldState.push(s);
            }
            while(!oldState.empty()){
                int s=oldState.top();
                map<char,int> digitTrans=stateVector[s]->digitTrans;
                if(digitTrans.find('d')!=digitTrans.end() && c>=48 && c<=57){
                    flag2=true;
                    int t=digitTrans['d'];
                    if(alreadyOn[t]!=1){
                        addState(alreadyOn,oldState,newState,t);
                    }
                }else if(digitTrans.find(c)!=digitTrans.end() && c!='d'){
                        flag2=true;
                        int t=digitTrans[c];
                        if(alreadyOn[t]!=1){
                            addState(alreadyOn,oldState,newState,t);
                        }  
                    }
            
                oldState.pop();
            }

            if(!flag2){
                return false;
            }
        }

        
        while(!newState.empty()){
            int s=newState.top();
            if(stateVector[s]->isEnd){
                flag=true;
            }
            newState.pop();         
        }
        for(auto state : stateVector){
            delete state;
        }
        return flag;

    }
};

剑指 Offer 26. 树的子结构

输入两棵二叉树A和B,判断B是不是A的子结构。(约定空树不是任意一个树的子结构)

B是A的子结构, 即 A中有出现和B相同的结构和节点值。

例如:
给定的树 A:

 3
/ \

4 5
/
1 2
给定的树 B:

4
/
1
返回 true,因为 B 与 A 的一个子树拥有相同的结构和节点值。

示例 1:

输入:A = [1,2,3], B = [3,1]
输出:false

示例 2:

输入:A = [3,4,5,1,2], B = [4,1]
输出:true

注意:==子树的先序遍历不一定是主树先序遍历的子串,所以不能用kmp算法。==例如主树[10,12,6,8,3,11]
和子树
[10,12,6,8]

class Solution {
public:
    bool isSubStructure(TreeNode* A, TreeNode* B) {
        if(!A || !B) return false;
        bool res = false;
        // 如果在 A 中匹配到了与 B 的根节点的值一样的节点
        if(A -> val == B -> val) res = doesAHaveB(A, B);
        // 如果匹配不到,A 往左
        if(!res) res = isSubStructure(A -> left, B);
        // 还匹配不到,A 往右
        if(!res) res = isSubStructure(A -> right, B);
        return res;
    }
    bool doesAHaveB(TreeNode *r1, TreeNode *r2)
    {
        // 如果 B 已经遍历完了,true
        if(!r2) return true;
        // 如果 B 还有,但 A 遍历完了,那 B 剩下的就没法匹配了,false
        if(!r1) return false;
        // 不相等,false
        if(r1 -> val != r2 -> val) return false;
        return doesAHaveB(r1 -> left, r2 -> left) && doesAHaveB(r1 -> right, r2 -> right);
    }
};

剑指 Offer 29. 顺时针打印矩阵

输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字。

示例 1:

输入:matrix = [[1,2,3],[4,5,6],[7,8,9]]
输出:[1,2,3,6,9,8,7,4,5]

示例 2:

输入:matrix = [[1,2,3,4],[5,6,7,8],[9,10,11,12]]
输出:[1,2,3,4,8,12,11,10,9,5,6,7]

限制:

0 <= matrix.length <= 100
0 <= matrix[i].length <= 100

思路:按照有下做上四个方向循环,如果碰到边界或者已经遍历过的点就改变方向。可以就地保存已遍历的flag.

class Solution {
public:
    enum direction {RIGHT,DOWN,LEFT,UP};
    int dx[4]={1,0,-1,0};
    int dy[4]={0,1,0,-1};
    vector<int> spiralOrder(vector<vector<int>>& matrix) {
        vector<int> ret;
        int place=-9999;
        direction cur_dir=RIGHT;
        int rows=matrix.size();
        if(rows==0){
            return ret;
        }
        int cols=matrix[0].size();
        int cur_row=0;
        int cur_col=0;
        int tmp_row=0;
        int tmp_col=0;
        

        while(true){
            ret.push_back(matrix[cur_row][cur_col]);
            matrix[cur_row][cur_col]=place;
            tmp_row=cur_row+dy[cur_dir];
            tmp_col=cur_col+dx[cur_dir];
            // cout<<"---1"<<endl;
            if(tmp_row>-1 && tmp_col>-1 && tmp_row<rows && tmp_col<cols && matrix[tmp_row][tmp_col]!=place){
                cur_row=tmp_row;
                cur_col=tmp_col;
            }else{
                cur_dir=direction((cur_dir+1)%4);
                tmp_row=cur_row+dy[cur_dir];
                tmp_col=cur_col+dx[cur_dir];
                if(tmp_row>-1 && tmp_col>-1 && tmp_row<rows && tmp_col<cols && matrix[tmp_row][tmp_col]!=place){
                    cur_row=tmp_row;
                    cur_col=tmp_col;
                    continue;
                }else{
                    return ret;
                }
            }
        }
        return ret;
    }
};

剑指 Offer 28. 对称的二叉树

请实现一个函数,用来判断一棵二叉树是不是对称的。如果一棵二叉树和它的镜像一样,那么它是对称的。

例如,二叉树 [1,2,2,3,4,4,3] 是对称的。

    1
   / \
  2   2
 / \ / \
3  4 4  3

但是 [1,2,2,null,3,null,3] 则不是镜像对称的

思路:和树的子结构这一题有点类似,对比子树1和子树2的时候先比较根节点然后再对比子树1的右子树和子树2的左子树,以及子树1的做子树和子树2的右子树

/**
 * 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 {
public:
    bool isSymmetric(TreeNode* root) {
        if(root==NULL){
            return true;
        }
        return compare(root->left,root->right);
    }
    bool compare(TreeNode* cur_left,TreeNode* cur_right){
        if(cur_left==NULL || cur_right==NULL){
            if(cur_left==cur_right){
                return true;
            }else{
                return false;
            }
        }
        if(cur_left->val!=cur_right->val){
            return false;
        }
        return compare(cur_left->left,cur_right->right) && compare(cur_left->right,cur_right->left);

    }
};

剑指 Offer 31. 栈的压入、弹出序列

输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如,序列 {1,2,3,4,5} 是某栈的压栈序列,序列 {4,5,3,2,1} 是该压栈序列对应的一个弹出序列,但 {4,3,5,1,2} 就不可能是该压栈序列的弹出序列。

示例 1:

输入:pushed = [1,2,3,4,5], popped = [4,5,3,2,1]
输出:true
解释:我们可以按以下顺序执行:
push(1), push(2), push(3), push(4), pop() -> 4,
push(5), pop() -> 5, pop() -> 3, pop() -> 2, pop() -> 1

思路:维护一个栈,开始时把pushed的第一个元素压入栈。每次从poped队首取一个元素x和栈顶比较,如果相等则弹出。不相等就继续把pushed中元素压入栈再比较。pushed内元素都压完后还是找不到和x相等的,就直接返回错误。

class Solution {
public:
    bool validateStackSequences(vector<int>& pushed, vector<int>& popped) {
        stack<int> stack;
        if(pushed.size()==0){
            return true;
        }
        int cur_pop=0;
        int cur_push=0;
        int cur;
        bool flag;
        stack.push(pushed[cur_push]);
        cur_push+=1;
        
        for(cur_pop=0;cur_pop<popped.size();cur_pop++){
           cur=popped[cur_pop];
           flag=false;
            if(!stack.empty() && stack.top()==popped[cur_pop]){
                stack.pop();
                flag=true;
            }else{
                while(cur_push<pushed.size()){ 
                    stack.push(pushed[cur_push]);
                    cur_push+=1;
                    if(stack.top()==popped[cur_pop]){
                        stack.pop();
                        flag=true;
                        break;
                    }
            }
            }

           if(!flag){
               return false;
           }
        }
        return true;
    }
};

LCS no.583

动态规划思路:如果string[i]==string[j]
则dp[i][j]=dp[i-1][j-1]
否则为max(dp[][j-1],dp[i-1][j])

class Solution {
public:
    
    int minDistance(string word1, string word2) {
        vector<vector<int> > dp(510,vector<int> (510));
        int len1=word1.size();
        int len2=word2.size();
        if(len1==0 || len2==0){
            return len1+len2;
        }
        if(word1[0]==word2[0]){
            dp[0][0]=1;
        }else{
            dp[0][0]=0;
        }
        for(int i=1;i<len1;i++){
            if(dp[i-1][0]==1 || word1[i]==word2[0]){
                dp[i][0]=1;
            }else{
                dp[i][0]=0;
            }
        }
        for(int i=1;i<len2;i++){
            if(dp[0][i-1]==1 || word2[i]==word1[0]){
                dp[0][i]=1;
            }else{
                dp[0][i]=0;
            }
        }
        for(int i=1;i<len1;i++){
            for(int j=1;j<len2;j++){
                if(word1[i]==word2[j]){
                    dp[i][j]=dp[i-1][j-1]+1;
                }else if(dp[i-1][j]>dp[i][j-1]){
                    dp[i][j]=dp[i-1][j];
                }else{
                    dp[i][j]=dp[i][j-1];
                }
            }
        }

        return len1+len2-2*dp[len1-1][len2-1];
    }
};

剑指 Offer 33. 二叉搜索树的后序遍历序列

输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历结果。如果是则返回 true,否则返回 false。假设输入的数组的任意两个数字都互不相同。

参考以下这颗二叉搜索树:

     5
    / \
   2   6
  / \
 1   3

示例 1:

输入: [1,6,3,2,5]
输出: false

示例 2:

输入: [1,3,2,6,5]
输出: true

思路利用后续遍历序列中最后一个节点是根节点的性质,又因为是搜索树,所以根据节点的大小确定左子树和右子树范围,递归调用find(int left,int index,vector& postorder,int s,int l) 其中left和index时子树序号范围,s和l是子树取值的范围

class Solution {
public:
    bool verifyPostorder(vector<int>& postorder) {
        int len=postorder.size();
        if(len==0){
            return true;
        }
        return find(0,len-1,postorder,-999999,999999);
    }
    bool find(int left,int index,vector<int>& postorder,int s,int l){
        int pivot=postorder[index];
        if(pivot>l || pivot<s){
            return false;
        }
        if(index==left){
            return true;
        }
        bool find_right=false;
        int new_right=index-1;
        bool find_left=false;
        int new_left=left-1;
        bool right_flag=true;
        bool left_flag=true;
        for(int i=index-1;i>=left;i--){
            if (postorder[i]>pivot){
                if(!find_right){
                    find_right=true;
                    new_right=i;
                }
            }
            else{
                find_left=true;
                new_left=i;
                break;
            }
        }
        if(find_right){
            if(pivot>s){
                right_flag=find(new_left+1,new_right,postorder,pivot,l);
            }else{
                right_flag=find(new_left+1,new_right,postorder,s,l);
            }
        }
         if(find_left){
            if(pivot<l){
                left_flag=find(left,new_left,postorder,s,pivot);
            }else{
                left_flag=find(left,new_left,postorder,s,l);
            }
         }
        return left_flag&&right_flag;
    }
};

剑指 Offer 39. 数组中出现次数超过一半的数字

数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。

你可以假设数组是非空的,并且给定的数组总是存在多数元素。

思路: 先假设第一个元素为target,count=0.如果当前元素等于target,count+1,否则-1 。如果count=0则令当前元素为target。
假设众数为x,如果当前target为y,那么最坏的情况就是:到下一个target之前,遍历的元素中一半为y一半为x。但是剩下元素中的众数还是x。继续遍历下去target肯定还是x。

示例 1:

输入: [1, 2, 3, 2, 2, 2, 5, 4, 2]
输出: 2

思路:

限制:

1 <= 数组长度 <= 50000

class Solution {
public:
    int majorityElement(vector<int>& nums) {
        int x = 0, votes = 0, count = 0;
        for(int num : nums){
            if(votes == 0) x = num;
            votes += num == x ? 1 : -1;
        }
        // 验证 x 是否为众数
        for(int num : nums)
            if(num == x) count++;
        return count > nums.size() / 2 ? x : 0; // 当无众数时返回 0
    }
};

剑指 Offer 41. 数据流中的中位数

如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。

例如,

[2,3,4] 的中位数是 3

[2,3] 的中位数是 (2 + 3) / 2 = 2.5

设计一个支持以下两种操作的数据结构:

void addNum(int num) - 从数据流中添加一个整数到数据结构中。
double findMedian() - 返回目前所有元素的中位数。

示例 1:

输入:
[“MedianFinder”,“addNum”,“addNum”,“findMedian”,“addNum”,“findMedian”]
[[],[1],[2],[],[3],[]]
输出:[null,null,null,1.50000,null,2.00000]

示例 2:

输入:
[“MedianFinder”,“addNum”,“findMedian”,“addNum”,“findMedian”]
[[],[2],[],[3],[]]
输出:[null,null,2.00000,null,2.50000]

/*
// Definition for a Node.
class Node {
public:
    int val;
    Node* left;
    Node* right;

    Node() {}

    Node(int _val) {
        val = _val;
        left = NULL;
        right = NULL;
    }

    Node(int _val, Node* _left, Node* _right) {
        val = _val;
        left = _left;
        right = _right;
    }
};
*/
class Solution {
public:
    Node* first;
    Node* pre;
    Node* last;
    Node* treeToDoublyList(Node* root) {
        if(root==NULL){
            return NULL;
        }
        pre=NULL;
        last=NULL;
        first=NULL;
        connect(root);
        first->left=last;
        last->right=first;
        return first;
    }
    void connect(Node* cur){
       
        if(cur->left){
            connect(cur->left);
        }
        
        if(pre){
            pre->right=cur;
            cur->left=pre;
            pre=cur;
        }else{
            first=cur;
            pre=cur;
        }
        last=cur;
        if(cur->right){
            connect(cur->right);
        }
    }
};

剑指 Offer 43. 1~n 整数中 1 出现的次数

输入一个整数 n ,求1~n这n个整数的十进制表示中1出现的次数。

例如,输入12,1~12这些整数中包含1 的数字有1、10、11和12,1一共出现了5次。

示例 1:

输入:n = 12
输出:5

示例 2:

输入:n = 13
输出:6

思路:递归处理首位和后缀,事先计算999…9的情况

限制:

1 <= n < 2^31
class Solution {
public:
    vector<long> base;
    vector<long> record;
    int countDigitOne(int n) {
        string s=to_string(n);
        return cal(s,0);
    }
    Solution(){
        base=vector(10,long(0));
        record=vector(10,long(0));
        fill(10);
        base.clear();
    }
    void fill(int size){
        base[0]=1;
        for(int i=1;i<size;i++){
            for(int j=i;j>=0;j--){
                if(j==0){
                    base[0]=9*base[0];
                }else{
                    base[j]=9*base[j]+base[j-1];
                }
                record[i]+=j*base[j];
            }

        }
    }
    long cal(string& number, int left){
        int size=number.size();
        istringstream is(number.substr(left,1));
        int first;
        is >> first;
        int sub_size=size-1-left;
        long result=0;
        if(left==size){
            return long(0);
        }else if(left==size-1){
            for(int i=first;i>=0;i--){
                if(i==1){
                    result+=1;
                }
            }
            return result;
        }
        
        if(first==1){
            
            int sub=atoi(number.substr(left+1).c_str());
            result+=sub+1+cal(number,left+1);
        }else{
            result+=cal(number,left+1);
        }
        for(int i=first-1;i>=0;i--){
            if(i==1){
                result+=record[sub_size]+long(pow(10,sub_size));
            }else{
                result+=record[sub_size];
            }
        }
        return result;
    }
};

剑指 Offer 42. 连续子数组的最大和

输入一个整型数组,数组中的一个或连续多个整数组成一个子数组。求所有子数组的和的最大值。

要求时间复杂度为O(n)。

示例1:

输入: nums = [-2,1,-3,4,-1,2,1,-5,4]
输出: 6
解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。

提示:

1 <= arr.length <= 10^5
-100 <= arr[i] <= 100
class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        // int maxL,maxR,posL,posR,negL,negR;
        int maxNeg,maxPos,curPos,curNeg;
        maxNeg=-99999;
        maxPos=-1;
        bool posFlag=false;
        for(int i=0;i<nums.size();i++){
            int cur=nums[i];
            if(cur>=0){
                if(posFlag){
                    curPos+=cur;
                }else{
                    posFlag=true;
                    if((curNeg+curPos)>=0){
                        curPos=curNeg+curPos+cur;
                    }else{
                        curPos=cur;
                    }
                }
            }else{
                if(posFlag){
                    posFlag=false;
                    curNeg=cur;
                    if(curPos>maxPos){
                        maxPos=curPos;
                    }
                }else{
                    curNeg+=cur;
                }
                if(cur>maxNeg){
                    maxNeg=cur;
                }
            }
        }
        if(posFlag){
            if(curPos>maxPos){
                maxPos=curPos;
            }
        }
        if(maxPos>-1){
            return maxPos;
        }else{
            return maxNeg;
        }
    }
};

剑指 Offer 44. 数字序列中某一位的数字

数字以0123456789101112131415…的格式序列化到一个字符序列中。在这个序列中,第5位(从下标0开始计数)是5,第13位是1,第19位是4,等等。

请写一个函数,求任意第n位对应的数字。

示例 1:

输入:n = 3
输出:3

示例 2:

输入:n = 11
输出:0

限制:

0 <= n < 2^31

注意:本题与主站 400 题相同:https://leetcode-cn.com/problems/nth-digit/


class Solution {
public:
    vector<long> base;
    Solution(){
        base=vector(10,long(0));
        base[1]=9;
        for(int i=2;i<base.size();i++){
            base[i]+=9*i*pow(10,i-1)+base[i-1];
        }
    }
    int findNthDigit(int n) {
        int b;
        int real;
        for(int i=1;i<base.size();i++){
            if(n<=base[i]){
                b=i;
                break;
            }
        }
        int qu=(n-base[b-1])/b;
        int re=n-base[b-1]-qu*b;
        if(re==0){
            real=pow(10,b-1)-1+qu;
            string number=to_string(real);
            istringstream is(number.substr(b-1,1));
            int result;
            is >> result;
            return result;
        }else{
            real=pow(10,b-1)+qu;
            string number=to_string(real);
            istringstream is(number.substr(re-1,1));
            int result;
            is >> result;
            return result;
        }
    }
};

剑指 Offer 46. 把数字翻译成字符串

给定一个数字,我们按照如下规则把它翻译为字符串:0 翻译成 “a” ,1 翻译成 “b”,……,11 翻译成 “l”,……,25 翻译成 “z”。一个数字可能有多个翻译。请编程实现一个函数,用来计算一个数字有多少种不同的翻译方法。

示例 1:

输入: 12258
输出: 5
解释: 12258有5种不同的翻译,分别是"bccfi", “bwfi”, “bczi”, “mcfi"和"mzi”

提示:

0 <= num < 231
class Solution {
public:
    int result;
    int len;
    string s;
    int translateNum(int num) {
        s=to_string(num);
        len=s.size();
        result=0;
        fun(0);
        return result;
    }
    void fun(int left){
        int cur_len=len-left;
        if(cur_len==1 || cur_len==0){
            result+=1;
            return;
        }
        int nt=atoi(s.substr(left,2).c_str());
        int ns=atoi(s.substr(left,1).c_str());
        fun(left+1);
        if(nt<=25 && ns!=0){
            fun(left+2);
        }
        return;

    }

};

剑指 Offer 48. 最长不含重复字符的子字符串

请从字符串中找出一个最长的不包含重复字符的子字符串,计算该最长子字符串的长度。

示例 1:

输入: “abcabcbb”
输出: 3
解释: 因为无重复字符的最长子串是 “abc”,所以其长度为 3。

示例 2:

输入: “bbbbb”
输出: 1
解释: 因为无重复字符的最长子串是 “b”,所以其长度为 1。

示例 3:

输入: “pwwkew”
输出: 3
解释: 因为无重复字符的最长子串是 “wke”,所以其长度为 3。
请注意,你的答案必须是 子串 的长度,“pwke” 是一个子序列,不是子串。

提示:

s.length <= 40000
class Solution {
public:
    int nthUglyNumber(int n) {
        int a,b,c;
        vector<int> record;
        a=b=c=0;
        record.push_back(1);
        for(int i=1;i<=n-1;i++){
            while(record[a]*2 <=record[i-1]){
                a++;
            }
            while(record[b]*3 <=record[i-1]){
                b++;
            }
            while(record[c]*5 <=record[i-1]){
                c++;
            }
            int m=min(min(record[a]*2,record[b]*3),record[c]*5);
            record.push_back(m);
        }
        return record[n-1];

    }
};

剑指 Offer 53 - I. 在排序数组中查找数字 I

统计一个数字在排序数组中出现的次数。

示例 1:

输入: nums = [5,7,7,8,8,10], target = 8
输出: 2

示例 2:

输入: nums = [5,7,7,8,8,10], target = 6
输出: 0

限制:

0 <= 数组长度 <= 50000

思路: 两趟二分查找,一个找做边界,一个找有边界。找左右边界时,中点的求法不同。

class Solution {
public:
    int search(vector<int>& nums, int target) {
        int len=nums.size();
        if(len==0){
            return 0;
        }
        int left=findLeft(nums,target,0,len-1);
        int right=findRight(nums,target,0,len-1);
        if(nums[left]!=target){
            return 0;
        }
        return right-left+1;
    }
    int findLeft(vector<int>& nums,int target,int left,int right){
        if(left==right){
            if(nums[left]==target){
                return left;
            }else{
                return 0;
            }
        }
        int mid=(left+right)/2;
        if(nums[mid]>=target){
            return findLeft(nums,target,left,mid);
        }else{
            return findLeft(nums,target,mid+1,right);
        }
    }
    int findRight(vector<int>& nums,int target,int left,int right){
        if(left==right){
            if(nums[left]==target){
                return left;
            }else{
                return -1;
            }
        }
        int mid=(left+right-1)/2+1;
        if(nums[mid]<=target){
            return findRight(nums,target,mid,right);
        }else{
            return findRight(nums,target,left,mid-1);
        }
    }
};

剑指 Offer 53 - I. 在排序数组中查找数字 I

统计一个数字在排序数组中出现的次数。

示例 1:

输入: nums = [5,7,7,8,8,10], target = 8
输出: 2

示例 2:

输入: nums = [5,7,7,8,8,10], target = 6
输出: 0

限制:

0 <= 数组长度 <= 50000
思路: 只要是有序就要想到二分法

class Solution {
public:
    int missingNumber(vector<int>& nums) {
        int len=nums.size();
        
        if((nums[len-1]-nums[0])==(len-1)){
                if(nums[0]==0){
                    return nums[len-1]+1;
                }else{
                    return 0;
                }
            }else{
                return find(nums,0,len-1);
            }
        
    }
    int find(vector<int>& nums,int left,int right){
        cout<<left<<" "<<right<<endl;
        if(right==left+1){
            return nums[left]+1;
        }

        int mid=(left+right)/2;
        if((nums[mid]-nums[left])!=(mid-left)){
            return find(nums,left,mid);
        }else{
            return find(nums,mid,right);
        }
    }
};

剑指 Offer 57 - II. 和为s的连续正数序列

输入一个正整数 target ,输出所有和为 target 的连续正整数序列(至少含有两个数)。

序列内的数字由小到大排列,不同序列按照首个数字从小到大排列。

示例 1:

输入:target = 9
输出:[[2,3,4],[4,5]]

示例 2:

输入:target = 15
输出:[[1,2,3,4,5],[4,5,6],[7,8]]

限制:

1 <= target <= 10^5

思路:分成奇数个和偶数个的情况。

class Solution {
public:
    static bool sortVector(vector<int> &ad1, vector<int> &ad2)
    {
        return ad1[0] < ad2[0];
    }
    vector<vector<int>> findContinuousSequence(int target) {
        int a;
        vector<vector<int>> result;
        for(a=target;a>1;a--){
            int b=target/a;
            if(b>a){
                break;
            }else{
                if((target%a)==0){
                    if(b%2){
                        auto tmp1=ifEven(a,b);
                        auto tmp2=ifOdd(a,b);
                        if(tmp1.size()>0){
                            result.push_back(tmp1);
                        }
                        if(tmp2.size()>0){
                            result.push_back(tmp2);
                        }
                    }
                    if(a!=b && a%2){
                        auto tmp1=ifEven(b,a);
                        auto tmp2=ifOdd(b,a);
                        if(tmp1.size()>0){
                            result.push_back(tmp1);
                        }
                        if(tmp2.size()>0){
                            result.push_back(tmp2);
                        }
                    }
                }
            }
        }
        sort(result.begin(), result.end(), sortVector);
        return result;
    }
    vector<int> ifEven(int a,int b){
        
        int center=b/2;
        vector<int> result;
        if(center-a+1>0){
            for(int i=center-a+1;i<=center+a;i++){
                result.push_back(i);
            }
            return result;
        }else{
            return result;
        }
    }
    vector<int> ifOdd(int a, int b){
        int count =b/2;
        vector<int> result;
        if(count>0 && a-count>0){
            for(int i=a-count;i<=a+count;i++){
                result.push_back(i);
            }
            return result;
        }else{
            return result;
        }
    }

};

剑指 Offer 59 - I. 滑动窗口的最大值

给定一个数组 nums 和滑动窗口的大小 k,请找出所有滑动窗口里的最大值。

示例:

输入: nums = [1,3,-1,-3,5,3,6,7], 和 k = 3
输出: [3,3,5,5,6,7]
解释:

滑动窗口的位置 最大值


[1 3 -1] -3 5 3 6 7 3
1 [3 -1 -3] 5 3 6 7 3
1 3 [-1 -3 5] 3 6 7 5
1 3 -1 [-3 5 3] 6 7 5
1 3 -1 -3 [5 3 6] 7 6
1 3 -1 -3 5 [3 6 7] 7

提示:

你可以假设 k 总是有效的,在输入数组不为空的情况下,1 ≤ k ≤ 输入数组的大小。
思路:在窗口内设置一个左端点,保持左端点一直是窗口内最大值,如果从右边新加入的节点比左端点大,则令这个端点为新的左端点,当旧的左端点被pop掉的时候且新节点比左端点小时,从左到右寻找最大的点作为新左端点。
7 6 7 5
第一个窗口 【767】
第二个窗口 【75】

class Solution {
public:
    vector<int> maxSlidingWindow(vector<int>& nums, int k) {
        int len=nums.size();

        int left=0;
        vector<int> result;
        int right;
        if(len==0){
            return result;
        }
        if(k==1){
            return nums;
        }
        for(right=1;right<k;right++){
            if(nums[right]>=nums[left]){
                left=right;
            }
        }
        result.push_back(nums[left]);
        for(right=k;right<len;right++){
            if(nums[right]>=nums[left]){
                left=right;
            }
            if(left>right-k){
                result.push_back(nums[left]);
            }else{
                int max=-9999;
                int index;
                for(left=right-k+1;left<=right;left++){
                    if(nums[left]>max){
                        max=nums[left];
                        index=left;
                    }
                }
                left=index;
                result.push_back(nums[left]);
            }
            // cout<<left<<endl;
        }
        return result;
    }
};

剑指 Offer 62. 圆圈中最后剩下的数字

0,1,···,n-1这n个数字排成一个圆圈,从数字0开始,每次从这个圆圈里删除第m个数字(删除后从下一个数字开始计数)。求出这个圆圈里剩下的最后一个数字。

例如,0、1、2、3、4这5个数字组成一个圆圈,从数字0开始每次删除第3个数字,则删除的前4个数字依次是2、0、4、1,因此最后剩下的数字是3。

示例 1:

输入: n = 5, m = 3
输出: 3

示例 2:

输入: n = 10, m = 17
输出: 2

限制:

1 <= n <= 10^5
1 <= m <= 10^6

思路:利用递归思想,

class Solution {
public:
    int lastRemaining(int n, int m) {
        if(n==1){
            return 0;
        }else{
            int r=(m-1)%n;
            int c=lastRemaining(n-1,m);
            return (r+1+c)%n;
        }
    }
};

剑指 Offer 63. 股票的最大利润

假设把某股票的价格按照时间先后顺序存储在数组中,请问买卖该股票一次可能获得的最大利润是多少?

示例 1:

输入: [7,1,5,3,6,4]
输出: 5
解释: 在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。
注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格。

示例 2:

输入: [7,6,4,3,1]
输出: 0
解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。

限制:

0 <= 数组长度 <= 10^5

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int max=0;
        int t_l=0,t_r=0;
        int len=prices.size();
        for(int i=1;i<len;i++){
            if(prices[i]>prices[t_r]){
                int t_m=prices[i]-prices[t_l];
                if(t_m>max){
                    max=t_m;
                }
            }else if(prices[i]<prices[t_l]){
                t_l=t_r=i;
            }
        }
        return max;

    }
};

剑指 Offer 66. 构建乘积数组

给定一个数组 A[0,1,…,n-1],请构建一个数组 B[0,1,…,n-1],其中 B[i] 的值是数组 A 中除了下标 i 以外的元素的积, 即 B[i]=A[0]×A[1]×…×A[i-1]×A[i+1]×…×A[n-1]。不能使用除法。

示例:

输入: [1,2,3,4,5]
输出: [120,60,40,30,24]

提示:

所有元素乘积之和不会溢出 32 位整数
a.length <= 100000

思路: 按表格排列,分别按照上三角和下三角进行计算。

class Solution {
public:
    vector<int> constructArr(vector<int>& a) {
        vector<int> up;
        up.push_back(1);
        vector<int> down;
        down.push_back(1);
        vector<int> result;
        int len=a.size();
        int t_up=1;
        int t_d=1;
        for(int i=1;i<len;i++){
            t_up*=a[i-1];
            up.push_back(t_up);
        }
        for(int i=len-2;i>=0;i--){
            t_d*=a[i+1];
            down.push_back(t_d);
        }
        for(int i=0;i<len;i++){
            result.push_back(up[i]*down[len-1-i]);
        }
        return result;

    }
};

剑指 Offer 67. 把字符串转换成整数

写一个函数 StrToInt,实现把字符串转换成整数这个功能。不能使用 atoi 或者其他类似的库函数。

首先,该函数会根据需要丢弃无用的开头空格字符,直到寻找到第一个非空格的字符为止。

当我们寻找到的第一个非空字符为正或者负号时,则将该符号与之后面尽可能多的连续数字组合起来,作为该整数的正负号;假如第一个非空字符是数字,则直接将其与之后连续的数字字符组合起来,形成整数。

该字符串除了有效的整数部分之后也可能会存在多余的字符,这些字符可以被忽略,它们对于函数不应该造成影响。

注意:假如该字符串中的第一个非空格字符不是一个有效整数字符、字符串为空或字符串仅包含空白字符时,则你的函数不需要进行转换。

在任何情况下,若函数不能进行有效的转换时,请返回 0。

说明:

假设我们的环境只能存储 32 位大小的有符号整数,那么其数值范围为 [−231, 231 − 1]。如果数值超过这个范围,请返回 INT_MAX (231 − 1) 或 INT_MIN (−231) 。

示例 1:

输入: “42”
输出: 42

示例 2:

输入: " -42"
输出: -42
解释: 第一个非空白字符为 ‘-’, 它是一个负号。
我们尽可能将负号与后面所有连续出现的数字组合起来,最后得到 -42 。

示例 3:

输入: “4193 with words”
输出: 4193
解释: 转换截止于数字 ‘3’ ,因为它的下一个字符不为数字。

示例 4:

输入: “words and 987”
输出: 0
解释: 第一个非空字符是 ‘w’, 但它不是数字或正、负号。
因此无法执行有效的转换。

示例 5:

输入: “-91283472332”
输出: -2147483648
解释: 数字 “-91283472332” 超过 32 位有符号整数范围。
因此返回 INT_MIN (−231) 。

判断溢出的方法:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值