剑指offer编程题答案

(自己编写且已通过,留来后面优化,不断更新中ing)

  1. 在一个长度为n的数组里的所有数字都在0到n-1的范围内。 数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中任意一个重复的数字。 例如,如果输入长度为7的数组{2,3,1,0,2,5,3},那么对应的输出是第一个重复的数字2。
class Solution {
public:
    // Parameters:
    //        numbers:     an array of integers
    //        length:      the length of array numbers
    //        duplication: (Output) the duplicated number in the array number
    // Return value:       true if the input is valid, and there are some duplications in the array number
    //                     otherwise false
    bool duplicate(int numbers[], int length, int* duplication) 
    {
        int i=0,temp=0,num=0;
        for(i=0;i<length;)
        {
            if(numbers[i]==i)
            {
                i++;
                break;
            }                
            else
            {
                num=numbers[i];
                if(numbers[i]==numbers[num])
                {
                    *duplication=numbers[i];//将重复的值赋值给传入地址中
                    //duplication=&numbers[i];//不能对传入地址重新赋值
                    return true;
                }
                else
                {
                    temp=numbers[num];
                    numbers[num]=numbers[i];
                    numbers[i]=temp;
                }
                    
            }
        }
        return false;
    }
};
  1. 在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
class Solution {
public:
    bool Find(int target, vector<vector<int> > array) {
        int m=array[0].size();//输入数组列数
        int n=array.size();//输入数组行数
        int i=0,j=m-1;//初始行列位置,矩阵右上角
        if(m==0||n==0)
            return false;
        while(1)
        {
            if((i>n-1)||(j<0))//判断是否超出边界
            {
                    return false;
            }
            if(array[i][j]==target)//比较当前数字和目标数字
                return true;
            else if(array[i][j]<target)//小于目标数行数加1
                i++;
            else//大于目标数列减1
                j--;
        }
        
    }
};

5.请实现一个函数,将一个字符串中的每个空格替换成“%20”。例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。

class Solution {
public:
	void replaceSpace(char *str,int length)
    {
        int spacenum=0;
        int strlength=0;
         if(str==NULL||length<=0)return;   
        while(*str!='\0')//获取空格数
        {
            if(*str==' ')
                spacenum++;
            str++;
            strlength++;
        }
        int newstrlength=strlength+2*spacenum;//获取新字符串长度
        if(newstrlength>length)
            return;
        int p1=strlength-1;
        int p2=newstrlength-1;
        while(spacenum!=0)//从原字符串末尾处往新字符串末尾拷贝
        {
            if(*(str+p1)==' ')
            {
                *(str+(p2--))='0';
                *(str+(p2--))='2';
                *(str+p2)='%';
                p2--;
                spacenum--;
            }
            else{
                *(str+p2--)=*(str+p1);
            }
            p1--;
            
	    }
    }
};


  1. 输入一个链表,按链表值从尾到头的顺序返回一个ArrayList。
    (网上查了下vector用法)
    方法1(向量函数翻转):
/**
*  struct ListNode {
*        int val;
*        struct ListNode *next;
*        ListNode(int x) :
*              val(x), next(NULL) {
*        }
*  };
*/
class Solution {
public:
    vector<int> printListFromTailToHead(ListNode* head) {
        vector<int> res;
        if(head==nullptr)
            return res;       
        ListNode* pnode=head;
        while(pnode!=nullptr)
        {
            res.push_back(pnode->val);
            pnode=pnode->next;
        }
        
        reverse(res.begin(), res.end());
        return res;
    }
};

方法2(翻转链表):

class Solution {
public:
    vector<int> printListFromTailToHead(ListNode* head) {
        
        vector<int> ivec;
        if(head == NULL || head->next == NULL)
        {
            return ivec;
        }
        ListNode* phead=(ListNode*)malloc(sizeof(ListNode));
		phead->val=0;
		phead->next=head;
        //反转
        ListNode* prev = NULL;
        ListNode* cur = phead->next;
        ListNode* next = NULL;
        while(cur)
        {
            next = cur->next;
            
            cur->next = prev;
            prev = cur;
            cur = next;
            
        }
        phead->next = prev;
        
        //赋值
        ListNode *p = phead->next;
        while(p)
        {
            ivec.push_back(p->val);
            p = p->next;
        }
        return ivec;
        
    }
};
  1. 输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。
    (基础太差,感觉这题好难,做到崩溃,代码参考评论区大神的)
/**
 * Definition for binary tree
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
  class Solution {
 
    public:
 
        struct TreeNode* reConstructBinaryTree(vector<int> pre,vector<int> in) {
 
            int inlen=in.size();
 
            if(inlen==0)//判别结束条件
 
                return NULL;
 
            vector<int> left_pre,right_pre,left_in,right_in;
 
            //创建根节点,根节点肯定是前序遍历的第一个数
 
            TreeNode* head=new TreeNode(pre[0]);
 
            //找到中序遍历根节点所在位置,存放于变量gen中
 
            int gen=0;
 
            for(int i=0;i<inlen;i++)
 
            {
 
                if (in[i]==pre[0])
 
                {
 
                    gen=i;
 
                    break;
 
                }
 
            }
 
            //对于中序遍历,根节点左边的节点位于二叉树的左边,根节点右边的节点位于二叉树的右边
 
            //利用上述这点,对二叉树节点进行归并
 
            for(int i=0;i<gen;i++)
 
            {
 
                left_in.push_back(in[i]);
 
                left_pre.push_back(pre[i+1]);//前序第一个为根节点
 
            }
 
            for(int i=gen+1;i<inlen;i++)
 
            {
 
                right_in.push_back(in[i]);
 
                right_pre.push_back(pre[i]);
 
            }
 
            //和shell排序的思想类似,取出前序和中序遍历根节点左边和右边的子树
 
            //递归,再对其进行上述所有步骤,即再区分子树的左、右子子数,直到叶节点
 
           head->left=reConstructBinaryTree(left_pre,left_in);
 
           head->right=reConstructBinaryTree(right_pre,right_in);
 
           return head;
 
        }
 
    };
 
  1. 给定一个二叉树和其中的一个结点,请找出中序遍历顺序的下一个结点并且返回。注意,树中的结点不仅包含左右子结点,同时包含指向父结点的指针。
    (这题看了offer上的思路后,自己写死活只能通过37%,还是参考大神做法)
class Solution {
public:
    TreeLinkNode* GetNext(TreeLinkNode* pNode)
    {
        if(pNode==NULL)
            return NULL;
        if(pNode->right!=NULL)
        {
            pNode=pNode->right;
            while(pNode->left!=NULL)
                pNode=pNode->left;
            return pNode;
        }  
        while(pNode->next!=NULL)
        {
            TreeLinkNode *proot=pNode->next;
            if(proot->left==pNode)
                return proot;
            pNode=pNode->next;
        }
        return NULL;
    }
};

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

    int pop() {
        int popnum=0;
         while(stack1.empty()!=true){
             stack2.push(stack1.top());
             stack1.pop();
         }
        popnum=stack2.top();
        stack2.pop();
        return popnum;
    }

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

上面解法繁琐了<分析>:
入栈:将元素进队列A
出栈:判断队列A中元素的个数是否为1,如果等于1,则出队列,否则将队列A中的元素 以此出队列并放入队列B,直到队列A中的元素留下一个,然后队列A出队列,再把 队列B中的元素出队列以此放入队列A中。正解如下:

class Solution
{
public:
    void push(int node) {
       stack1.push(node);
    }
    int pop() {
        int a;
        if(stack2.empty()){
            while(!stack1.empty()){
                a=stack1.top();
                stack2.push(a);
                stack1.pop();
            }
        }
        a=stack2.top();
        stack2.pop();
        return a;
         
    }
private:
    stack<int> stack1;
    stack<int> stack2;
};
  1. 把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。 输入一个非减排序的数组的一个旋转,输出旋转数组的最小元素。 例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。 NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。
    (思路是对的但程序只通过了百分之35)
class Solution {
public:
    int minNumberInRotateArray(vector<int> rotateArray) {
        
        int vsize=rotateArray.size();
        if(vsize==1)return rotateArray[0];
        
        if(rotateArray[0]<=rotateArray[vsize-1]&&rotateArray[0]<=rotateArray[1])
            return rotateArray[0];
        
        int L=0,R=vsize-1,M=0;
        while(L!=(R-1))
        {
            M=(R+L)/2;
            if(rotateArray[M]<=rotateArray[R])
                R=M;
            else
                L=M;
        }
        if(rotateArray[L]>rotateArray[R])
            return rotateArray[R];
        else
            return rotateArray[L];
        
    }
};

正解:

#include <iostream>
#include <vector>
#include <string>
#include <stack>
#include <algorithm>
using namespace std;
 
class Solution {
public:
    int minNumberInRotateArray(vector<int> rotateArray) {
        int size = rotateArray.size();
        if(size == 0){
            return 0;
        }//if
        int left = 0,right = size - 1;
        int mid = 0;
        // rotateArray[left] >= rotateArray[right] 确保旋转
        while(rotateArray[left] >= rotateArray[right]){
            // 分界点
            if(right - left == 1){
                mid = right;
                break;
            }//if
            mid = left + (right - left) / 2;
            // rotateArray[left] rotateArray[right] rotateArray[mid]三者相等
            // 无法确定中间元素是属于前面还是后面的递增子数组
            // 只能顺序查找
            if(rotateArray[left] == rotateArray[right] && rotateArray[left] == rotateArray[mid]){
                return MinOrder(rotateArray,left,right);
            }//if
            // 中间元素位于前面的递增子数组
            // 此时最小元素位于中间元素的后面
            if(rotateArray[mid] >= rotateArray[left]){
                left = mid;
            }//if
            // 中间元素位于后面的递增子数组
            // 此时最小元素位于中间元素的前面
            else{
                right = mid;
            }//else
        }//while
        return rotateArray[mid];
    }
private:
    // 顺序寻找最小值
    int MinOrder(vector<int> &num,int left,int right){
        int result = num[left];
        for(int i = left + 1;i < right;++i){
            if(num[i] < result){
                result = num[i];
            }//if
        }//for
        return result;
    }
};

面试题12:请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子。如果一条路径经过了矩阵中的某一个格子,则之后不能再次进入这个格子。 例如 a b c e s f c s a d e e 这样的3 X 4 矩阵中包含一条字符串"bcced"的路径,但是矩阵中不包含"abcb"路径,因为字符串的第一个字符b占据了矩阵中的第一行第二个格子之后,路径不能再次进入该格子。
思路:这个题其实理解了就觉得不难,只是写代码可能会遇到一些问题。程序我就参考的剑指offer的例程了,程序大概流程为
1.检查边界值和初始化
2.用两层for循环从二维矩阵中每个元素开始尝试路径
3.进入一个格子,判断当前格子是否满足路径条件,如果满足则做好标记并递归判断其周围格子是否满足路径条件;否则返回上一层。
4.结束条件是当进入某一个格子后发现路径已经到str的末尾’\0’。
class Solution {
public:
bool hasPath(char* matrix, int rows, int cols, char* str)
{
if(matrixnullptr||rows<1||cols<1||strnullptr)//检查输入矩阵是否合法
return false;
bool visited=new bool[rowscols];//是否占用标志矩阵
memset(visited,0,rowscols);
int pathLength=0;
for(int row=0;row<rows;++row)//从矩阵中每个元素开始尝试
{
for(int col=0;col<cols;++col)
{
if(hasPathCore(matrix,rows,cols,row,col,str,pathLength,visited))
{
return true;
}
}
}
delete[] visited;
return false;
}
private:
bool hasPathCore(const char
matrix,int rows,int cols,int row,int col,const char* str,int& pathLength,bool* visited)
{
if(str[pathLength]==’\0’)//结束条件
return true;
bool hasPath=false;
if(row>=0&&row<rows&&col>=0&&col<cols&&matrix[row* cols+col]==str[pathLength]
&&!visited[row* cols+col])//判断行列是否在数组内,str[pathLength]是否和当前元素相同,该元素是否已经在路径中
{
++pathLength;
visited[row* cols+col]=true;//满足条件则标记为true
hasPath=hasPathCore(matrix,rows,cols,row,col-1,str,pathLength,visited)||//递归地遍历上下左右的格子
hasPathCore(matrix,rows,cols,row-1,col,str,pathLength,visited)||
hasPathCore(matrix,rows,cols,row,col+1,str,pathLength,visited)||
hasPathCore(matrix,rows,cols,row+1,col,str,pathLength,visited);
if(!hasPath)//判断周围的格子是否存在路径
{
–pathLength;//没有合适的路径返回上一层
visited[row*cols+col]=false;
}
}
return hasPath;
}

};
面试题13:地上有一个m行和n列的方格。一个机器人从坐标0,0的格子开始移动,每一次只能向左,右,上,下四个方向移动一格,但是不能进入行坐标和列坐标的数位之和大于k的格子。 例如,当k为18时,机器人能够进入方格(35,37),因为3+5+3+7 = 18。但是,它不能进入方格(35,38),因为3+5+3+8 = 19。请问该机器人能够达到多少个格子?
思路:思路和矩阵中找字符串相似,也是采用递归遍历的方法,其实还比上一题简单一些,自己做了一遍发现始终找到的格子比答案少几个,最后终于发现是rcSum<threshold这一句应该是rcSum<=threshold,因为刚好坐标位数和等于阈值也可以:

  1. 判断边界值
  2. 进入一个方格判断该方格是否满足条件
    (1) 若满足条件,做好标记,并递归遍历周围的格子,返回可进入的格子数。
    (2) 不满足则返回0,退回上一层。
    (3) 结束条件是当周围没有可进入的格子(已标记的格子也不能再进入)。

class Solution {
public:
int movingCount(int threshold, int rows, int cols)
{
int num=0;
if(threshold<0||rows<1||cols<1)
return 0;
bool visited=new bool[rows * cols];
memset(visited,0,rows
cols);
num=Counter(threshold,rows,cols,0,0,visited);
delete [] visited;
return num;

}

private:
int Counter(int threshold,int rows, int cols, int row, int col,bool visited)
{
int num = 0;
if(checkout(threshold,rows,cols,row,col,visited))//判断当前格子是否可进
{
visited[row
cols+col]=true;
num=1+Counter(threshold,rows,cols,row+1,col,visited)+Counter(threshold,rows,cols,row-1,col,visited)+
Counter(threshold,rows,cols,row,col-1,visited)+Counter(threshold,rows,cols,row,col+1,visited);
}
return num;
}
bool checkout(int threshold,int rows, int cols,int row, int col,bool visited)
{
if(visited[row
cols+col]!=true&&row>=0&&row<rows&&col>=0&&col<cols)//判断能否进入以及是否已被访问
{
int rcSum=0;
while(row>0)//取行列的每一位,并求和
{
rcSum+=row%10;
row=row/10;
}
while(col>0)
{
rcSum+=col%10;
col=col/10;
}
if(rcSum<=threshold)//判断能否进入以及是否已被访问
return true;
}
return false;

}

};
面试题15:输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示。
这个题看似简单,但其实做题时你会发现,如果对二进制不是太熟悉,做起来还是不简单。
常规解法:
把n和1做与运算,再判断该位是否为1,接着将1左移1位得到2,再判断该位是否为1,然后一直左移直到整数二进制的最左边,也就是,循环的次数为整数二进制的位数。

class Solution {
public:
     int  NumberOf1(int n) {
        int count = 0;
         unsigned int flag = 1;
         while(flag)
         {
             if(n&flag)count++;
             flag=flag<<1;
         }
         return count;
     }
};

高级解法:
把一个整数减去1,再和原来的整数相与,可将原整数最左边的1变为0,一个二进制整数中有多少个1,就能进行多少次这样的操作。

class Solution {
public:
     int  NumberOf1(int n) {
        int count = 0;
         while(n)
         {
             ++count;
             n=(n-1)&n;
         }
         return count;
     }
};

猜你喜欢

转载自blog.csdn.net/HuYingJie_1995/article/details/87880918