剑指offer刷题

3.数组中重复的数字:
题目描述
在一个长度为n的数组里的所有数字都在0到n-1的范围内。 数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中任意一个重复的数字。 例如,如果输入长度为7的数组{2,3,1,0,2,5,3},那么对应的输出是第一个重复的数字2。
思路:a.排序后找重复的,需要排序的时间复杂度。O(nlogn)
b.利用哈希表。从头到尾按顺序扫描数组的每个数字,每扫描到一个数字,可以用O(1)的时间来判断哈希表中是否已经包含了该数字。如果哈希表中没有这个数字,把它加入哈希表。否则,找到一个重复数字。时间复杂度使遍历O(1)。

bool duplicate(int numbers[], int length, int* duplication) {
         if(numbers==NULL||length<=1) return false;
         int *hashTable=new int[length]();
         for(int i=0;i<length;i++){
             if(hashTable[numbers[i]]){
                 *duplication=numbers[i];
                 return true;
             }
             else{
                 hashTable[numbers[i]]=1;
             }
         }
         return false;
     }

回溯法:
用回溯法解决的问题的所有选项可以形象地用树状结构表示。在某一步有n个可能的选项,那么该步骤可以看出是树状结构中的一个节点,每个选项看成树中节点连接线,经过这些连接线到达该节点的n个子节点。树的叶节点对应着终结状态。如果在叶节点的状态满足题目的约束条件,那么我们找到了一个可行的解决方案。
如果在叶节点的状态不满足约束条件,那么只好回溯到它的上一个节点再尝试其它的选项。如果上一个节点所有可能的选项都已经试过,并且不能满足约束条件的终结状态,则再次回溯到上一个节点。如果所有的节点的所有选项都已经尝试过仍然不能到达满足约束条件的终结状态,则该问题无解。
通常回溯算法适合用递归实现代码。当我们到达某一个节点时,尝试所有可能的选项并在满足条件的前提下递归地抵达下一个节点。
12.矩阵中的路径
题目描述:
在这里插入图片描述
在这里插入图片描述
思路:回溯法典型题目,首先,在矩阵中任选一个格子作为路径的起点。假设矩阵中某个格子的字符为ch,并且这个格子将对应于路径上的第i个字符。如果路径上的第i个字符不是ch,那么这个格子不可能处在路径上的第i个位置。如果路径上的第i个字符正好是ch,那么到相邻的格子寻找路径上的第i+1个字符。除矩阵边界上格子之外,其它格子都有4个相邻的格子。重复这个过程,直到路径上的所有字符都在矩阵中找到相应的位置。(广度优先)。
由于回溯法的递归特性,路径可以被看成一个栈。当在矩阵中定位了路径中前n个字符的位置之后,在与第n个字符对应的格子的周围都没有找到第n+1个字符,这时候只好在路径上回到第n-1个字符,重新定位第n个字符。
由于路径不能重复进入矩阵的格子,所以还需要定义和字符矩阵大小一样的bool矩阵,用来标识路径是否已经进入了每个格子。

class Solution {
public:
    bool hasPath(char* matrix, int rows, int cols, char* str)
    {
        if(matrix==NULL||rows<1||cols<1||str==NULL)
            return false;
        bool *visited=new bool[rows*cols];//对是否已经访问进行标记
        memset(visited,0,rows*cols);
        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;
    }
    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]){
            pathLength++;
        visited[row*cols+col]=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。但它不能进入方格(25,38),因为3+5+3+8=19。请问该机器人能够到达多少个格子?
方格也可以看作一个m*n矩阵。同时,在这个矩阵中,除边界上的格子之外,其它格子都有4个相邻的格子。
机器人从坐标(0,0)开始移动。当它准备进入坐标为(i,j)的格子时,通过检查坐标的数位和来判断机器人是否能够进入。如果机器人能够进入坐标为(i,j)的格子,则再判断它能否进入4个相邻的格子(i,j-1),(i-1,j),(i,j+1),(i+1,j)。因此,可以用回溯算法来实现。

class Solution {
public:
//主函数
    int movingCount(int threshold, int rows, int cols)
    {
        if(threshold<0||rows<=0||cols<=0)
            return 0;
        bool *visited=new bool[rows*cols];
        for(int i=0;i<rows*cols;i++)//初始化
            visited[i]=false;
        int count=movingCountCore(threshold,rows,cols,0,0,visited);
        delete[] visited;
        return count;
    }
    //回溯递归代码
    int movingCountCore(int threshold,int rows,int cols,int row,int col,bool *visited){
        int count=0;
        if(check(threshold,rows,cols,row,col,visited)){
            visited[row*cols+col]=true;
            count=1+movingCountCore(threshold,rows,cols,row-1,col,visited)
                +movingCountCore(threshold,rows,cols,row,col-1,visited)
                +movingCountCore(threshold,rows,cols,row+1,col,visited)
                +movingCountCore(threshold,rows,cols,row,col+1,visited);
        }
        return count;
    }
    //判断是否满足条件
    bool check(int threshold,int rows,int cols,int row,int col,bool* visited){
        if(row>=0&&row<rows&&col>=0&&col<cols&&getDigitSum(row)+getDigitSum(col)<=threshold&&!visited[row*cols+col])
            return true;
        else return false;
    }
    //数字各位数求和。
    int getDigitSum(int number){
        int sum=0;
        while(number>0){
            sum+=number%10;
            number/=10;
        }
        return sum;
    }
};

动态规划与贪婪算法
14.减绳子
题目描述:给你一根长度为n的绳子,请把绳子剪成m段(m,n都是整数,n>1并且m>1),每段绳子的长度记为k[0],k[1]…k[m],请问k[0]k[1]…*k[m]可能的最大乘积是多少?例如,当绳子长度是8时,我们把它剪成2、3、3三段,此时得到的最大乘积是18.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值