剑指offer分类合集(c++语言版本)

目录

链表

//数据结构
struct ListNode{
    int val;
    struct ListNode* next;
    ListNode(int x):val(x),next(NULL){}
};

56、删除链表中重复的节点

ListNode* deleteDuplication(ListNode* pHead)
{
    if(pHead=NULL||pHead->next==NULL)
        return pHead;
    //防止头结点被删除
    ListNode* pRes=new ListNode(-1);
    pRes->next=pHead;
    //新建两个结点(链表的返回与操作过程中对链表产生的改动)
    ListNode* pPre=pRes;
    ListNode* pLast=pRes->next;
    //遍历链表
    while(pLast!=NULL){
        if(pLast->next!=NULL&&pLast->val==pLast->next->val){
            //防止出现1->1->1->3超过两个的重复节点
            while(pLast->next!=NULL&&pLast->val==pLast->next->val){
                pLast=pLast->next;
            }
            pPre->next=pLast->next;//此步很精巧
            pLast=pLast->next;
        }else{
            pPre=pPre->next;
            pLast=pLast->next;
        }
    }
    return pRes->next;
}

55、链表中环的入口结点

//快慢指针
ListNode* EntryNodeOfLoop(ListNode* pHead)
{
    if(pHead==NULL||pHead->next==NULL)
        return NULL;
    ListNode* pSlow=pHead;
    ListNode* pFast=pHead;
    while(pFast->next!=NULL&&pFast->next->next!=NULL){
        pSlow=pSlow->next;
        pFast=pFast->next->next;
        if(pSlow==pFast)
            break;
    }
    if(pFast->next==NULL)return NULL;
    //两个指针分别从链表头和相遇点出发,最后一定相遇于环入口。
    pSlow=pHead;
    while(pFast!=pSlow){
        pFast=pFast->next;
        pSlow=pSlow->next;
    }
    return pFast;
}

46、孩子们的游戏(圆圈中最后剩下的数)

int LastRemaining_Solution(int n,int m)
{
    if(n<1||m<1)
        return -1;
    ListNode* pHead=new ListNode(0);
    //开始构造循环链表
    ListNode* pPre=pHead;
    ListNode* pTmp=NULL;
    for(int i=1;i<n;i++){
        pTmp=new ListNode(i);
        pPre->next=pTmp;
        pPre=pTmp;
    }
    pTmp->next=pHead;
    //每次删除一个节点,最后剩下的即是答案
    ListNode* pTmp2=NULL;
    while(n!=1){
        pTmp2=pHead;
        for(int i=1;i<m-1;i++){
            pTmp2=pTmp2->next;
        }
        pTmp2->next=pTmp2->next->next;
        pHead=pTmp2->next;
        n--;
    }
    return pHead->val;
}

36、两个链表的第一个公共节点

ListNode* FindFirstCommonNode(ListNode* pHead1,ListNode* pHead2)
{
    int len1=FindListLen(pHead1);
    int len2=FindListLen(pHead2);
    if(len1>len2)
        pHead1=WalkStep(pHead1,len1-len2);
    else
        pHead2=WalkStep(pHead2,len2-len1);
    //pHead1!=NULL
    while(pHead1!=NULL){
        if(pHead1==pHead2)return pHead1;
        pHead1=pHead1->next;
        pHead2=pHead2->next;
    }
    return NULL;
}
int FindListLen(ListNode* pHead)
{
    int len=0;
    while(pHead!=NULL){
        pHead=pHead->next;
        len++;
    }
    return len;
}
ListNode* WalkStep(ListNode* pHead,int len)
{
    while(len>0){
        pHead=pHead->next;
        len--;
    }
    return pHead;
}

25、复杂链表的复制

/*
struct RandomListNode{
	int val;
	struct RandomListNode* next,* random;
	RandomListNode(int x):
		val(x),next(NULL),random(NULL){}
};
*/
RandomListNode* Clone(RandomListNode* pHead)
{
    if(pHead==NULL)return NULL;
    //1->2->6
    RandomListNode* pNode=pHead;
    //1->1->2->6
    while(pNode){
        RandomListNode* pTmp=new RandomListNode(pNode->val);
        pTmp->next=pNode->next;
        pNode->next=pTmp;
        pNode=pTmp->next;
    }
    //复制random
    pNode=pHead;//此处的意义不是重新将pHead赋值,而是回到已经复制后的头部结点
    while(pNode){
        RandomListNode* pTmp=pNode->next;
        if(pNode->random){
            pTmp->random=pNode->random->next;
        }
        pNode=pTmp->next;
    }
    //拆
    RandomListNode* pRes=pHead->next;
    RandomListNode* pTmp;
    pNode=pHead;
    //1->2->2->6->6
    while(pNode->next){
        pTmp=pNode->next;
        pNode->next=pTmp->next;
        pNode=pTmp;
    }
    return pRes;
}

16、合并两个排序链表

ListNode* Merge(ListNode* pHead1,ListNode* pHead2)
{
    if(pHead1==NULL)return pHead2;
    if(pHead2==NULL)return pHead1;
    ListNode* pRes=new ListNode(-1);
    ListNode* pHead=pRes;
    while(pHead1!=NULL&&pHead2!=NULL){
        if(pHead1->val<pHead2->val){
            pRes->next=new ListNode(pHead1->val);
            pHead1=pHead1->next;
        }else{
            pRes->next=new ListNode(pHead2->val);
            pHead2=pHead1->next;
        }
        pRes=pRes->next;
    }
    if(pHead1==NULL)
        pRes->next=pHead2;
    else
        pRes->next=pHead1;
    return pHead->next;
}

15、反转链表

ListNode* ReverseList(ListNode* pHead)
{
    if(pHead==NULL)
        return NULL;
    ListNode* pNode=pHead;
    ListNode* pReverseHead=NULL;//初始化
    ListNode* pPre=NULL;
    while(pNode!=NULL){
        ListNode* pNext=pNode->next;//链断开之前一定要保存断开位置后边的结点
        if(pNext==NULL)
            pReverseHead=pNode;
        pNode->next=pPre;//反转
        pPre=pNode;
        pNode=pNext;
    }
    return pReverseHead;
}

14、链表中倒数第k个结点

ListNode* func(ListNode* pHead,int k)
{
    if(pHead==NULL||k==0)
        return NULL;
    ListNode* pFast=pHead;
    ListNode* pSlow=pHead;
    for(int i=1;i<k;i++){
        if(pFast->next!=NULL){
        	pFast=pFast->next;
        }else{
            return NULL;
        }
    }
    while(pFast->next){
        pSlow=pSlow->next;
        pFast=pFast->next;
    }
    return pSlow;
}

3、从尾到头打印链表

vector<int> printListFromTailToHead(ListNode* pHead)
{
    vector<int> res;
    while(pHead){
        res.insert(res.begin(),pHead->val);
        pHead=pHead->next;
    }
    return res;
}

贪心

67、剪绳子

int curRope(int n)
{
    if(n==2)
        return 1;
    if(n==3)
        return 2;
    int[] dp=new int[n+1];
    dp[1]=1;
    dp[2]=2;
    dp[3]=3;
    int res=0;
    for(int i=4;i<=n;i++){
        for(int j=1;j<=i/2;j++){
            res=max(res,dp[j]*dp[i-j]);
        }
        dp[i]=res;
    }
    return dp[n];  
}

9、变态跳台阶

//一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。
int jumpStairsII(int n)
{
    if(n<=0)
        return -1;
    if(n==1)
        return 1;
    else
        return 2*jumpStairs(n-1);
}

8、跳台阶

//一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法(先后次序不同算不同的结果)。
int jumStairs(int n)
{
    if(n<=0)
        return -1;
    else if(n==1)
        return 1;
    elseif(n==2)
        return 2;
    else
    	return jumpStairs(n-1)+jumpStairs(n-2);
}

递归

7、斐波那契数列

int Fibonacci(int n)
{
    if(n<=0)
        return 0;
    if(n==1)
        return 1;
    int res=0,f1=0,f2=1;
    for(int i=2;i<=n;i++){
        res=f1+f2;
        f1=f2;
        f2=res;
    }
    return res;
}

10、矩形覆盖

int rectCover(int n)
{
    if(n<=0)
        return 0;
    if(n==1||n==2)
        return n;
    return rectCover(n-1)+rectCover(n-2);
}

27、字符串的排列

//输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则按字典序打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。
vector<string> Permutation(string str)
{
    vector<string> res;
    if(str.size()==0)
        return res;
    Permutation(str,res,0);
    sort(res.begin(),res.end());
    return res;
}
void Permutation(string str,vector<string>& res,int begin)
{
    if(begin==str.size()-1)
        res.push_back(str);
    for(int i=begin;i<str.size();i++){
        if(i!=begin&&str[i]==str[begin])
            continue;
        swap(str[i],str[begin]);
        Permutation(str,res,begin+1);
        swap(str[i],str[begin]);
    }
}

65、矩阵中的路径

//请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子。如果一条路径经过了矩阵中的某一个格子,则该路径不能再进入该格子。
bool hasPath(char* matrix,int rows,int cols,char* str)
{
    if(str==NULL||rows<=0||cols<=0)
        return false;
    bool* flag=new bool[rows*cols]();
    for(int i=0;i<rows;i++){
        for(int j=0;j<cols;j++){
            if(hasPath(matrix,rows,cols,str,flag,i,j))
                return true;
        }
    }
    return false;
}
bool hasPath(char *matrix,int rows,int cols,char *str,bool *flag,int i,int j)
{
    if(*str=='\0')
        return true;
    if(x<0||x>=rows||y<0||y>=cols)
        return false;
    if(flag[i*cols+y]||*str!=matrix[i*cols+y])
        return false;
    flag[i*cols+y]=true;
    bool sign=hasPath(matrix,rows,cols,str+1,flag,i+1,j)||
        hasPath(matrix,rows,cols,str+1,flag,i-1,j)||
        hasPath(matrix,rows,cols,str+1,flag,i,j+1)||
        hasPath(matrix,rows,cols,str+1,flag,i,j-1);
    flag[i*cols+y]=false;
    return sign;
}

//数据结构
struct TreeNode{
    int val;
    TreeNode* left;
    TreeNode* right;
    struct TreeNode(int x):val(x),left(NULL),right(NULL){}
};

62、二叉搜索树的第k个结点

//给定一棵二叉搜索树,请找出其中的第k小的结点。例如, (5,3,7,2,4,6,8)    中,按结点数值大小顺序第三小结点的值为4。
TreeNode* KthNode(TreeNode* pRoot,int k)
{
    if(pRoot==NULL||k<=0)
        return NULL;
    vector<TreeNode*> res;
    Inorder(pRoot,res);
    if(k>vec.size())//意外情况
        return NULL;
    return res[k-1];
}
void Inorder(TreeNode* pRoot,vector<TreeNode*>& res)
{
    if(pRoot==NULL)return;//递归的结束条件
    Inorder(pRoot->left,res);
    res.push_back(pRoot);
    Inorder(pRoot->right,res);
}

61、序列化二叉树

//请实现两个函数,分别用来序列化和反序列化二叉树
二叉树的序列化是指:把一棵二叉树按照某种遍历方式的结果以某种格式保存为字符串,从而使得内存中建立起来的二叉树可以持久保存。序列化可以基于先序、中序、后序、层序的二叉树遍历方式来进行修改,序列化的结果是一个字符串,序列化时通过 某种符号表示空节点(#),以 ! 表示一个结点值的结束(value!)。
二叉树的反序列化是指:根据某种遍历顺序得到的序列化字符串结果str,重构二叉树。

    
string Serialize(TreeNode* pRoot)
{
    if(pRoot==NULL)
        return NULL;
    string str;
    Serialize(pRoot,str);
    return str;
}
void Serialize(TreeNode* pRoot,string& res)
{
    if(pRoot==NULL){
        res+='#';
        return;
    }
    string r=to_string(pRoot->val);
    res+=r;
    res+=',';
    Serialize(pRoot->left,res);
    Serialize(pRoot->right,res);
}
TreeNode* Deserialize(char* str)
{
    if(str==NULL)
        return NULL;
    TreeNode* res=Deserialize(&str);
    return res;
}
TreeNode* Deserialize(char** str)
{
    
}

60、把二叉树打印成多行

//从上到下按层打印二叉树,同一层结点从左至右输出。每一层输出一行。
vector<vector<int>> Print(TreeNode* pRoot)
{
    vector<vector<int> res;
    if(pRoot==NULL)
        return res;//常规操作,此时res是?
    queue<TreeNode*> q;
    q.push(pRoot);
    while(!q.empty()){
        int lo=0,hi=q.size();
        vector<int> tmp;
        while(lo++<hi){//保证一行全部打印完成
            TreeNode* node=q.front();//不是top
            q.pop();
            tmp.push_back(node->val);
            if(node->left)q.push(node->left);
            if(node->right)q.push(node->right);
        }
        res.push_back(tmp);
    }
    return res;
}

59、按之字形顺序打印二叉树

//请实现一个函数按照之字形打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右至左的顺序打印,第三行按照从左到右的顺序打印,其他行以此类推。
vector<vector<int>> Print(TreeNode* pRoot)
{
    vector<vector<int>> res;
    if(pRoot==NULL)
        return res;
    queue<TreeNode*> q;
    q.push(pRoot);
    bool flag=false;
    while(!q.empty()){
        int lo=0,hi=q.size();
        vector<int> tmp;
        while(lo++<hi){
            TreeNode* node=q.front();
            q.pop();
            tmp.push_back(node->val);
            if(node->left)q.push(node->left);
            if(node->right)q.push(node->right);
        }
        if(flag)reverse(tmp.begin(),tmp.end());
        res.push_back(tmp);
        flag=!flag;
    }
    return res;
}

58、对称的二叉树

//请实现一个函数,用来判断一棵二叉树是不是对称的。注意,如果一个二叉树同此二叉树的镜像是同样的,定义其为对称的。
bool isSymmetrical(TreeNode* pRoot)
{
    return isSymmetrical(pRoot,pRoot);
}
bool isSymmetrical(TreeNode* pRoot1,TreeNode* pRoot2)
{
    if(pRoot1==NULL&&pRoot2==NULL)
        return true;
    if(pRoot1==NULL||pRoot2==NULL)
        return false;
    if(pRoot1->val!=pRoot2->val)
        return false;
    return isSymmetrical(pRoot1->left,pRoot2->right)&&isSymmetrical(pRoot1->right,pRoot2->left);
}

57、二叉树的下一个结点

//给定一个二叉树和其中的一个结点,请找出中序遍历顺序的下一个结点并且返回。注意,树中的结点不仅包含左右子结点,同时包含指向父结点的指针。
/*
struct TreeLinkNode {
    int val;
    struct TreeLinkNode *left;
    struct TreeLinkNode *right;
    struct TreeLinkNode *next;
    TreeLinkNode(int x) :val(x), left(NULL), right(NULL), next(NULL) {
        
    }
};
*/
TreeLinkNode* findNext(TreeLinkNode* pNode)
{
    if(pNode==NULL)
        return NULL;//此步存疑,如果父结点不为空呢?
    if(pNode->right){
        pNode=pNode->right;
        while(pNode->left){//此步思考的时候遗漏了,应该不停向左子树递归
            pNode=pNode->left;
        }
        return pNode;
    }
    while(pNode->next!=NULL){//此处是while先前没写出来
        TreeLinkNode* pHead=pNode->next;
        if(pHead->left==pNode)//本身是左结点直接返回即可
            return pNode;
        pNode=pNode->next;//本身是右结点不断向上回溯
    }
    
}
    

39、平衡二叉树

//输入一棵二叉树,判断该二叉树是否是平衡二叉树。在这里,我们只需要考虑其平衡性,不需要考虑其是不是排序二叉树(平衡二叉树:任意节点的子树的高度差都小于等于1。常见的符合平衡树的有,B树(多路平衡搜索树)、AVL树(二叉平衡搜索树)等)
bool isBanlanced(TreeNode* pRoot)
{
    if(pRoot==NULL)
        return true;
    if(abs(getDepth(pRoot->left)-getDepth(pRoot->right))>1)
       return false;
    else
       return true;
}
int getDepth(TreeNode* pRoot)
{
    if(pRoot==NULL)
        return 0;
    return 1+max(getDepth(pRoot->left),getDepth(pRoot->right));
}

38、二叉树的深度

//输入一棵二叉树,求该树的深度。从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径的长度为树的深度。(层序遍历也可用于求二叉树深度)
int getDepth(TreeNode* pRoot)
{
    if(pRoot==NULL)
        return 0;
    return 1+max(getDepth(pRoot->left),getDepth(pRoot->right));
}

?26、二叉搜索树与双向链表

//输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向。
TreeNode* Convert(TreeNode* pRoot)
{
    
}

24、二叉树中和为某一值的路径

//输入一颗二叉树的根节点和一个整数,按字典序打印出二叉树中结点值的和为输入整数的所有路径。路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。
vector<vector<int>> findPath(TreeNode* pRoot,int sum)
{
    vector<vector<int>> res;
    vector<int> tmp;
    dfs(pRoot,sum,res,tmp);
    return res;
}
void dfs(TreeNode* pRoot,int sum,vector<vector<int>>& res,vector<int>& tmp)
{
    if(pRoot==NULL)
        return;
    tmp.push_back(pRoot->val);
    if(!pRoot->left&&pRoot->right&&pRoot->val==sum){
        res.push_back(tmp);
    }else{
        if(pRoot->left){
            dfs(pRoot->left,sum-pRoot->val,res,tmp);
        }
        if(pRoot->right){
            dfs(pRoot->right,sum-pRoot->val,res,tmp);
        }
    }
    tmp.pop_back();
}

23、二叉树的后序遍历序列

//输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。如果是则返回true,否则返回false。假设输入的数组的任意两个数字都互不相同。
bool verifyBST(vector<int> arr)
{
    if(arr.size()==0)
        return false;
    return verifyBST(arr,0,arr.size()-1);
}
bool verifyBST(vector<int>& arr,int left,int right)
{
    //结束条件,此处很重要
    if(left>=right)
        return true;
    int i=right;
    //根据BST的特点将树分为两侧,最后i落在右子树的第一个
    while(i>left&&arr[i-1]>arr[right])
        --i;
    //左子树的结点必然比根结点小
    for(int j=i-1;j>=left;j--){
        if(arr[j]>arr[right])
            return false;
    }
    return judge(arr,left,i-1)&&judge(arr,i,right-1);
}

22、从上往下打印二叉树

//从上往下打印出二叉树的每个节点,同层节点从左至右打印。
vector<int> print(TreeNode* pRoot)
{
    vector<int> res;
    if(pRoot==NULL)
        return res;
    queue<TreeNode*> q;
    q.push(pRoot);
    while(!q.empty()){
        res.push_back(q.front()->val);//front()不是top()
        if(q.front()->left)//此处为q.front()不是pRoot
        	q.push(q.front()->left);
        if(q.front()->right)
        	q.push(q.front()->right);
        q,pop();
    }
    return res;
}

18、二叉树的镜像

//操作给定的二叉树,将其变换为源二叉树的镜像。
//递归
void Mirror(TreeNode* pRoot)
{
    if(pRoot=NULL)
        return;
    //判断如果是叶子结点则直接返回
    if(pRoot->left==NULL&&pRoot->right==NULL)
        return;
    //左右子结点交换
    TreeNode* tmp=pRoot->left;
    pRoot->left=pRoot->right;
    pRoot->right=tmp;
    //多一个存在性判断
    if(pRoot->left)
        Mirror(pRoot->left);
    if(pRoot->right)
        Mirror(pRoot->right);
}

17、树的子结构

//输入两棵二叉树A,B,判断B是不是A的子结构。(ps:我们约定空树不是任意一个树的子结构)
//递归
bool hasSubTree(TreeNode* pRootA,TreeNode* pRootB)
{
    if(pRootA==NULL||pRootB==NULL)
        return false;
    return isSubTree(pRootA,pRootB)||
        hasSubTree(pRootA->left,pRootB)||
        hasSubTree(pRootA->right,pRootB);
    
}
bool isSubTree(TreeNode* pRootA,TreeNode* pRootB)
{
    if(pRootB==NULL)
        return true;
    if(pRootA==NULL)
        return false;
    if(pRootA->val==pRootB->val){
        return isSubTree(pRootA->left,pRootB->left)&&
        isSubTree(pRootA->right,pRootB->right);
    }else
        return false;
}

4、重建二叉树

//输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。
//递归
TreeNode* construct(vector<int> pre,vector<int> in)
{
    int len=in.size();
    if(len==0)
        return NULL;
    vector<int> pre_left,pre_right,in_left,in_right;
    TreeNode* pRoot=new TreeNode(pre[0]);
    int gen=0;
    for(int i=0;i<len;i++){
        if(in[i]==pre[0]){
            gen=i;
            break;
        }
    }
    for(int i=0;i<gen;i++){
        pre_left.push_back(pre[i]);//此处使用的是push_back,不是赋值
        in_left.push_back(in[i]);
    }
    for(int i=gen+1;i<len;i++){
        pre_right.push_back(pre[i]);
        in_right.push_back(in[i]);
    }
    pRoot->left=construct(pre_left,in_left);
    pRoot->right=construct(pre_right,in_right);
    return pRoot;
}

字符串

54、字符流中第一个不重复的字符

//请实现一个函数用来找出字符流中第一个只出现一次的字符。例如,当从字符流中只读出前两个字符"go"时,第一个只出现一次的字符是"g"。当从该字符流中读出前六个字符“google"时,第一个只出现一次的字符是"l",如果当前字符流没有存在出现一次的字符,返回#字符。
class Solution
{
public:
    string s;
    char hash[256]={0};//为啥是256个字符,为啥这般初始化
  //Insert one char from stringstream,字符流
    void Insert(char ch)
    {
         s+=ch;
         hash[ch]++;
    }
  //return the first appearence once char in current stringstream
    char FirstAppearingOnce()
    {
    	int len=s.size();
        for(int i=0;i<len;i++){
            if(hash[s[i]==1])
                return s[i];
        }
        return '#';
    }

};

53、表示数值的字符串

//请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。例如,字符串"+100","5e2","-123","3.1416"和"-1E-16"都表示数值。 但是"12e","1a3.14","1.2.3","+-5"和"12e+4.3"都不是。
//穷举
bool isNumber(char* str)
{
    bool sign=false,hasE=false,decimal=false;
    for(int i=0;i<strlen(str);i++){
        if(str[i]=='e'||str[i]=='E'){
            if(i==strlen(str)-1)return false;//e/E位于字符串末尾
            if(hasE)return false;//e/E已经存在
            hasE=true;
        }else if(str[i]=='+'||str[i]=='-'){
            if(sign&&str[i-1]!='e'&&str[i-1]!='E')return false;//第二次出现+-号,不在e/E后面
            if(!sign&&i>0&&str[i-1]!='e'&&str[i-1]!='E')return false;//第一次出现+-号不在开头
            sign=true;
        }else if(str[i]=='.'){
            if(hasE||decimal)return false;//e后面不能接小数点且小数点不出现两次
            decimal=true;
        }else if(str[i]<'0'||str[i]>'9'){
            return false;
        }
    }
    return true;
}

52、正则表达式匹配

//请实现一个函数用来匹配包括'.'和'*'的正则表达式。模式中的字符'.'表示任意一个字符,而'*'表示它前面的字符可以出现任意次(包含0次)。 在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串"aaa"与模式"a.a"和"ab*ac*a"匹配,但是与"aa.a"和"ab*a"均不匹配
//递归
bool match(char* str,char* pattern)
{
    if(*str=='/0'&&*pattern=='/0')
        return true;
    if(*str!='/0'&&pattern=='/0')
        return false;
    //这个if判断不明白
    if(*(pattern+1)!='*'){
        if(*str==*pattern || (*str!='/0')&&*pattern=='.')
            return match(str+1,pattern+1);
        else
            return false;
    }else{
        if(*str==*pattern||(*str!='\0' && *pattern=='.')
           return match(str,pattern+2)||match(str+1,pattern);
        else
           return match(str,pattern+2);
    }
}

49、把字符串转换成整数

//将一个字符串(包括数字字母符号,可以为空)转换成一个整数,要求不能使用字符串转换整数的库函数。 数值为0或者字符串不是一个合法的数值则返回0
int strToNum(string str)
{
    int len=str.size(),s=1;
    long long res;
    if(!len)return 0;
    if(str[0]=='-')s=-1;
    for(int i=(str[0]=='+'||str[0]=='-')?1:0;i<len;i++){
        if(str[i]<'0'||str[i]>'9')
            return 0;
        res=res*10+str[i]-'0';
    }
    //此处乘以符号
    long int sum=res*s;
    if(sum>INT_MAX||sum<INT_MIN)
        return 0;
    else
        return sum;
}

45、扑克牌顺子

//LL今天心情特别好,因为他去买了一副扑克牌,发现里面居然有2个大王,2个小王(一副牌原本是54张^_^)...他随机从中抽出了5张牌,想测测自己的手气,看看能不能抽到顺子,如果抽到的话,他决定去买体育彩票,嘿嘿!!“红心A,黑桃3,小王,大王,方片5”,“Oh My God!”不是顺子.....LL不高兴了,他想了想,决定大\小 王可以看成任何数字,并且A看作1,J为11,Q为12,K为13。上面的5张牌就可以变成“1,2,3,4,5”(大小王分别看作2和4),“So Lucky!”。LL决定去买体育彩票啦。 现在,要求你使用这幅牌模拟上面的过程,然后告诉我们LL的运气如何, 如果牌能组成顺子就输出true,否则就输出false。为了方便起见,你可以认为大小王是0。
bool isContinuous(vector<int> num)
{
    int len=num.size();
    if(len!=5)return false;
    sort(num.begin(),num.end());
    int jokers=0;
    for(int i=0;i<num.size();i++){
        jokers++;
    }
    if(jokers>4)
        return false;
    //排前面的都是0
    for(int i=jokers+1;i<5;i++){
        if(num[i]==num[i-1])
            return false;
    }
    //最大值与最小值的差值
    int dis=num[4]-num[jokers];
	if(dis<=4)
        return true;
    return false;
}

44、翻转单词顺序列

//牛客最近来了一个新员工Fish,每天早晨总是会拿着一本英文杂志,写些句子在本子上。同事Cat对Fish写的内容颇感兴趣,有一天他向Fish借来翻看,但却读不懂它的意思。例如,“student. a am I”。后来才意识到,这家伙原来把句子单词的顺序翻转了,正确的句子应该是“I am a student.”。Cat对一一的翻转这些单词顺序可不在行,你能帮助他么?
string reverseSentence(string str)
{
    reverseWord(str,0,str.size()-1);
    int begin=0,end=0,i=0;
    while(i<str.size()){
        while(i<str.size()&&str[i]==' ')
            i++;
        begin=end=i;
        while(i<str.size()&&str[i]!=' '){
            end++;
            i++;
        }
        reverseWord(str,begin,end-1);
    }
    return str;
}
void reverseWord(string& str,int begin,int end)
{
    while(begin<end)
        swap(str[begin++],str[end--]);
}

43、左旋转字符串

//汇编语言中有一种移位指令叫做循环左移(ROL),现在有个简单的任务,就是用字符串模拟这个指令的运算结果。对于一个给定的字符序列S,请你把其循环左移K位后的序列输出。例如,字符序列S=”abcXYZdef”,要求输出循环左移3位后的结果,即“XYZdefabc”。是不是很简单?OK,搞定它!
string leftRotate(string str,int k)
{
    int len=str.length();
    if(0==len || 0==n)
        return str;
    reverseWord(str,0,k-1);
    reverseWord(str,k,len-1);
    reverseWord(str,0,len-1);
    return str;
}
void reverseWord(string& str,int begin,int end)
{
    while(begin<end)
        swap(str[begin++],str[end--]);
}

34、第一个只出现一次的字符位置

//在一个字符串(0<=字符串长度<=10000,全部由字母组成)中找到第一个只出现一次的字符,并返回它的位置, 如果没有则返回 -1(需要区分大小写).(从0开始计数)
int findFirst(string str)
{
    map<char,int> m;
    for(char ch:str){
        m[ch]++;
    }
    for(int i=0;i<str.size();i++){
        if(m[str[i]]==1)
            return i;
    }
    return -1;
}

2、替换空格

//请实现一个函数,将一个字符串中的每个空格替换成“%20”。例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。(此处的length是允许扩展的总长度,不是字符串的长度)
void replaceSpace(char* str,int length)
{
    if(str==NULL||length<0)
        return;
    //计算字符指针的长度
    int oldLen=0,i=0,space=0;
    //注意此处结束标志方向\
    while(str[i]!='\0'){
        oldLen++;
        if(str[i]==' ')
            space++;
        i++;
    }
    int newLen=space*2+oldLen;
    //多一个判断
    if(newLen>length)
        return;
    //开始放字符
    while(oldLen>=0&&newLen>oldLen){
        if(str[oldLen]==' '){
            str[newLen--]='0';
            str[newLen--]='2';
            str[newLen--]='%';
        }else{
            str[newLen--]=str[oldLen];
        }
        oldLen--;
    }
}

64、滑动窗口的最大值

//给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。例如,如果输入数组{2,3,4,2,6,2,5,1}及滑动窗口的大小3,那么一共存在6个滑动窗口,他们的最大值分别为{4,4,6,6,6,5}; 针对数组{2,3,4,2,6,2,5,1}的滑动窗口有以下6个: {[2,3,4],2,6,2,5,1}, {2,[3,4,2],6,2,5,1}, {2,3,[4,2,6],2,5,1}, {2,3,4,[2,6,2],5,1}, {2,3,4,2,[6,2,5],1}, {2,3,4,2,6,[2,5,1]}。窗口大于数组长度的时候,返回空
//双端队列
vector<int> maxInWindows(vector<int> num,int size)
{
    vector<int> res;
    deque<int> s;
    for(int i=0;i<nums.size();i++){
        
        while(s.size()&&num[s.back()]<=num[i]){
            s.pop_back();
        }
        while(s.size()&&i-s.front()+1>size){
            s.pop_front();
        }
        s.push_back(i);
        if(size&&i+1>=size){
            res.push_back(num[s.front()]);
        }
    }
    return res;
}

63、数据流中的中位数

//如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。我们使用Insert()方法读取数据流,使用GetMedian()方法获取当前读取数据的中位数。
//大小堆
class Solution {
public:
    priority_queue<int,vector<int>,less<int>> min;//最大堆,第一个元素为最大值
    priority_queue<int,vector<int>,greater<int>> max;//最小堆,第一个元素为最小值
    void Insert(int num)
    {
        if(min.empty()||num<=min.pop())
            min.push(num);
        else
            max.push(num);
        if(min.size()==max.size()+2){
            max.push(min.top());
            min.pop();
        }
        if(min.size()+1==max.size()){
            min.push(max.top());
            max.pop();
        }
    }

    double GetMedian()
    { 
    	return min.size()==max.size()?(min.top()+max.top())/2.0:min.top();
    }
};

21、栈的压入、弹出序列

//输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈序列对应的一个弹出序列,但4,3,5,1,2就不可能是该压栈序列的弹出序列。(注意:这两个序列的长度是相等的)
bool IsPopOrder(vector<int> pushV,vector<int> popV)
{
	if(pushV.size()==0)
        return false;
    vector<int> num;
    for(int i=0,j=0;i<pushV.size();){
        num.push_back(pushV[i++]);
        //back函数
        while(j<popV.size()&&num.back()==popV[j]){
            num.pop_back();
            j++;
        }
    }
    return num.empty();
}

20、包含min函数的栈

//定义栈的数据结构,请在该类型中实现一个能够得到栈中所含最小元素的min函数(时间复杂度应为O(1))。
class Solution {
public:
    void push(int value) {
        s1.push(value);
        if(s2.empty()){
            s2.push(value);
        }
        if(value<=s2.top()){
            s2.push(value);
        }
    }
    void pop() {
        if(s1.top()==s2.top())
            s2.pop();
        s1.pop();
    }
    int top() {
        return s1.top();
    }
    int min() {
        return s2.top();
    }
private:
    stack<int> s1;
    stack<int> s2;
};

5、用两个栈实现队列

//用两个栈来实现一个队列,完成队列的Push和Pop操作。 队列中的元素为int类型。
class Solution
{
public:
    void push(int node) {
        stack1.push(node);
    }

    int pop() {
        if(stack2.size()==0){	//使用size
            while(stack1.size()!=0){
                stack2.push(stack1.top());
                stack1.pop();
            }
        }
        int a=stack2.top();
        stack2.pop();
        return a;
    }

private:
    stack<int> stack1;
    stack<int> stack2;
};

数学

48、不用加减乘除做加法

//写一个函数,求两个整数之和,要求在函数体内不得使用+、-、*、/四则运算符号。
//异或
int add(int num1,int num2)
{
    while(num2!=0){
        int temp=num1^num2;
        num2=(num1&num2)<<1;
        num1=temp;
    }
    return num1;
}

47、求1+2+3+……+n

//求1+2+3+...+n,要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)。
//递归
int sumSolution(int n)
{
    int sum=n;
    sum&&sum+=sumSolution(n-1);//短路求值原理
    return sum;
}

23、丑数

//把只包含质因子2、3和5的数称作丑数(Ugly Number)。例如6、8都是丑数,但14不是,因为它包含质因子7。 习惯上我们把1当做是第一个丑数。求按从小到大的顺序的第N个丑数。
int GetUglyNumber_Solution(int index) 
{
    if(index<7)
        return index;
    vector<int> res(index);
    res[0]=1;
    int t2=0,t3=0,t5=0,i;
    for(i=1;i<index;i++){
        res[i]=min(res[t2]*2,min(res[t3]*3,res[t5]*5));
        if(res[i]==res[t2]*2)t2++;
        if(res[i]==res[t3]*3)t3++;
        if(res[i]==res[t5]*5)t5++;
    }
    return res[index-1];
}

31、整数1出现的次数(从1到n整数中1出现的次数)

//求出1~13的整数中1出现的次数,并算出100~1300的整数中1出现的次数?为此他特别数了一下1~13中包含1的数字有1、10、11、12、13因此共出现6次,但是对于后面问题他就没辙了。ACMer希望你们帮帮他,并把问题更加普遍化,可以很快的求出任意非负整数区间中1出现的次数(从1 到 n 中1出现的次数)。
int NumberOf1Between1AndN_Solution(int n)
{
    int sum=0;
    for(int i=1;i<=n;i++){
        sum+=numOf1(i);
    }
    return sum;
}
int numOf1(int num)
{
    int res=0;
    while(n){
        if(n%10==1)res++;
        n=n/10;
    }
    return res;
}

12、数值的整数次方

//给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方。保证base和exponent不同时为0
double Power(double base, int exponent) 
{
    double sum=absPower(base,abs(exponent));
    if(exponet<0)
       sum=1.0/sum;
    return sum;
}
double absPower(double base,int exponent)
{
    double res=1.0;
    for(int i=0;i<exponent-1;i++){
       res=res*base;
    }
    return res;
}

11、二进制中1的个数

//输入一个整数,输出该数32位二进制表示中1的个数。其中负数用补码表示。
int  NumberOf1(int n) 
{
    int count=0;
    while(n!=0){
        count++;
        n=n&(n-1);
    }
    return count;
}

数组

66、机器人的运动范围

//地上有一个m行和n列的方格。一个机器人从坐标0,0的格子开始移动,每一次只能向左,右,上,下四个方向移动一格,但是不能进入行坐标和列坐标的数位之和大于k的格子。 例如,当k为18时,机器人能够进入方格(35,37),因为3+5+3+7 = 18。但是,它不能进入方格(35,38),因为3+5+3+8 = 19。请问该机器人能够达到多少个格子?
int movingCount(int threshold, int rows, int cols)
{
	bool* flag=new bool[rows*cols];
    for(int i=0;i<rows*cols;i++){
        flag[i]=false;
    }
    int count=moving(threshold,rows,cols,0,0,flag);
    delete[] flag;
    return count;
}
int moving(int threshold,int rows,int cols,int i,int j,bool* flag)
{
    int count=0;
    if(check(threshold,rows,cols,i,j,flag)){
        count=1+moving(threshold,rows,cols,i-1,j,flag)
            +moving(threshold,rows,cols,i+1,j,flag)
            +moving(threshold,rows,cols,i,j-1,flag)
            +moving(threshold,rows,cols,i,j+1,flag)
    }
    return count;
}
bool check(int threshold,int rows,int cols,int i,int j,bool* flag)
{
    if(i>=0&&i<rows&&j>=0&&j<cols
       &&getSum(i)+getSum(j)<threshold
       &&flag[i*cols+j]==false)
        return true;
    return false;
}
int getSum(int number)
{
    int res=0;
    while(number>0){
        res+=number%10;
        number/=10;
    }
    return res;
}

51、构建乘积数组

//给定一个数组A[0,1,...,n-1],请构建一个数组B[0,1,...,n-1],其中B中的元素B[i]=A[0]*A[1]*...*A[i-1]*A[i+1]*...*A[n-1]。不能使用除法。(注意:规定B[0] = A[1] * A[2] * ... * A[n-1],B[n-1] = A[0] * A[1] * ... * A[n-2];)对于A长度为1的情况,B无意义,故而无法构建,因此该情况不会存在。
vector<int> multiply(const vector<int>& A)
{
    vector<int> res;
    int len=A.size();
    if(len==0)
        return res;
    res.push_back(1);
    for(int i=0;i<len-1;i++){
        res.push_back(res.back()*A[i]);
    }
    int tmp=1;
    for(int i=len-1;i>=0;i--){
        res[i]=res[i]*tmp;
        tmp=tmp*A[i];
    }
    return res;
}

50、数组中重复的数字

//在一个长度为n的数组里的所有数字都在0到n-1的范围内。 数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中任意一个重复的数字。 例如,如果输入长度为7的数组{2,3,1,0,2,5,3},那么对应的输出是第一个重复的数字2。
bool duplicate(int numbers[], int length, int* duplication) 
{
	for(int i=0;i<length;i++){
        int index=numbers[i]%length;
        if(numbers[index]>=length){
            *duplication=index;
            return true;
        }
        numbers[index]+=length;
    } 
    return false;
}

42、和为S的两个数字

//输入一个递增排序的数组和一个数字S,在数组中查找两个数,使得他们的和正好是S,如果有多对数字的和等于S,输出两个数的乘积最小的。
vector<int> FindNumbersWithSum(vector<int> array,int sum) 
{
	vector<int> res;
    int begin=0;
    int end=array.size()-1;
    while(begin<end){
        if(array[begin]+array[end]==sum){
            res.push_back(array[begin]);
            res.push_back(array[end]);
            return res;
        }else if(array[begin]+array[end]<sum)
            begin++;
        else
            end--;
    }
    return res;
}

41、和为S的连续正数序列

//输出所有和为S的连续正数序列。序列内按照从小至大的顺序,序列间按照开始数字从小到大的顺序,连续正数和为100的序列:9,10,11,12,13,14,15,16/18,19,20,21,22
//滑动窗口
vector<vector<int> > FindContinuousSequence(int sum) 
{
	vector<vector<int>> res;
    int plow=1,phigh=2;
    while(plow<phigh){
        int cur=(plow+phigh)*(phigh-plow+1)/2;
        if(cur==sum){
            vector<int> tmp;
            for(int i=plow;i<phigh;i++){
                tmp.push_back(i);
            }
            res.push_back(tmp);
            plow++;
        }if(cur<sum)
            phigh++;
        if(cur>sum)
            plow++;
    }
    return res;
}

40、数组中只出现一次的数字

//一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。
void FindNumsAppearOnce(vector<int> data,int* num1,int *num2) 
{
    if(data.size()<2)
        return;
    int tmp=data[0];
    for(int i=1;i<data.size();i++){
        tmp=tmp^data[i];
    }
    if(tmp==0)
        return;
    int index=0;
    while((tmp&1)==0){
        tmp=tmp>>1;
        ++index;
    }
    *num1=*num2=0;
    for(int i=0;i<data.size();i++){
        if(isBit(data[i],index))
            *num1^=data[i];
        else
            *num2^=data[i];
    }
}
bool isBit(int num,int index)
{
    num=num>>index;
    return (num&1);
}

37、数字在排序数组中出现的次数

//统计一个数字在升序数组中出现的次数。
//二分查找
int GetNumberOfK(vector<int> data ,int k) 
{
	return biSearch(data,k+0.5)-biSearch(data,k-0.5);
}
int biSearch(vector<int> data,double num)
{
	int s=0,e=data.size()-1;
    while(s<=e){
        int mid=(e-s)/2+s;
        if(data[mid]<num)
            s=mid+1;
        else if(data[mid]>num)
            e=mid-1;
    }
    return s;
}

35、数组中的逆序对

//在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数P。并将P对1000000007取模的结果输出。 即输出P%1000000007
int InversePairs(vector<int> data) 
{
	
}

32、把数组排成最小的数

//输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。例如输入数组{3,32,321},则打印出这三个数字能排成的最小数字为321323。
string PrintMinNumber(vector<int> numbers) 
{
	string ans="";
    sort(numbers.begin(),numbers.end(),cmp);
    for(int i=0;i<numbers.size();i++){
        ans+=to_string(numbers[i]);
    }
    return ans;
}
bool cmp(int a,int b){
    string A="";
    string B="";
    A+=to_string(a);
    A+=to_string(b);
    B+=to_string(b);
    B+=to_string(a);
    return A<B;
}

30、连续子数组的最大和

//{6,-3,-2,7,-15,1,2,2},连续子向量的最大和为8(从第0个开始,到第3个为止)。给一个数组,返回它的最大连续子序列的和(子向量的长度至少是1)
int FindGreatestSumOfSubArray(vector<int> array) 
{
    int currentSum=array[0];
    int maxSum=array[0];
    for(int i=1;i<array.size();i++){
        if(currentSum<=0)
            currentSum=array[i];
        else
            currentSum+=array[i];
        if(currentSum>maxSum)
            maxSum=currentSum;
    }
    return maxSum;
}

29、最小的K个数

//输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4。
vector<int> GetLeastNumbers_Solution(vector<int> input, int k) 
{
    vector<int> res;
    if(input.empty()||k>input.size())return res;
    sort(input.begin(),input.end());
    for(int i=0;i<k;i++){
        res.push_back(input[i]);
    }
    return res;
}

28、数组中出现次数超过一半的数字

//数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。如果不存在则输出0。
int MoreThanHalfNum_Solution(vector<int> numbers) 
{
    if(numbers.empty())return 0;
    int res=numbers[0];
    int times=1;//这是1次不是0次
    for(int i=1;i<numbers.size();i++){
        if(times==0){
            res=numbers[i];
            time=1;
        }
        else if(numbers[i]==res)
            ++times;
        else
            times--;
    }
    times=0;
    for(int i=0;i<numbers.size();i++){
        if(numbers[i]==res)++times;
    }
    return (times>numbers.size()/2)?res:0;
}

19、顺时针打印矩阵

//输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下4 X 4矩阵: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 则依次打印出数字1,2,3,4,8,12,16,15,14,13,9,5,6,7,11,10.
vector<int> printMatrix(vector<vector<int> > matrix) 
{
	int rows=matrix.size();
    int cols=matrix[0].size();
    vector<int> vec;
    if(rows==0||cols==0)return vec;
    int left=0,top=0,right=cols-1,bottom=rows-1;
    while(left<=right&&top<=bottom){
        //
        for(int i=left;i<=right;i++){
            vec.push_back(matrix[top][i]);
        }
        for(int i=top+1;i<=bottom;i++){
            vec.push_back(matrix[i][right]);
        }
        if(top!=bottom)
        for(int i=right-1;i>=left;i--){
            vec.push_back(matrix[bottom][i]);
        }
        if(left!=right)
        for(int i=bottom-1;i>top;i--){
            vec.push_back(matrix[i][left]);
        }
        left++,top++,right--,bottom--;
    }
    return vec;
    
}

13、调整数组顺序使奇数位于偶数前面

//输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。
void reOrderArray(vector<int> &array)
{
    for(int i=0;i<array.size();i++){
        for(int j=array.size()-1;j>i;j--){
            if(array[j-1]%2==0&&array[j]%2==1){
                swap(array[j],array[j-1]);
            }
        }
    }
}

6、旋转数组的最小数字

//把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素。例如数组[3,4,5,1,2]为[1,2,3,4,5]的一个旋转,该数组的最小值为1。NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。
//二分查找
int minNumberInRotateArray(vector<int> rotateArray) 
{
    if(rotateArray.size()==0)
        return 0;
    int low=0,high=rotateArray.size()-1;
    while(low<high){
        int mid=(high-low)/2+low;
        if(rotateArray[mid]>rotateArray[high]){
            low=mid+1;
        }else if(rotateArray[mid]==rotateArray[high]){
            high=high-1;
        }else
            high=mid;
    }
    return rotateArray[low];
}

1、二维数组中的查找

//在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
 bool Find(int target, vector<vector<int> > array) 
 {
     int rows=array.size();
     int cols=array[0].size();
     int i=0;j=cols-1;
     while(i<rows&&j>=0){
         if(array[i][j]==target)
             return true;
         if(array[i][j]>target)
             j--;
         else
             i++;
     }
     return false;
 }

posted @ 2020-09-06 14:55  代码生财  阅读(196)  评论(0)    收藏  举报