剑指offer|解析和答案(C++/Python) (一)

剑指offer|解析和答案(C++/Python) (一)

参考剑指offer(第二版),这里做一个学习汇总,包括解析及代码。代码均在牛客网进行验证(摘自自己的牛客网笔记)。

整个剑指offer解析链接:
剑指offer|解析和答案(C++/Python) (一).
剑指offer|解析和答案(C++/Python) (二).
剑指offer|解析和答案(C++/Python) (三).
剑指offer|解析和答案(C++/Python) (四).
剑指offer|解析和答案(C++/Python) (五)上.
剑指offer|解析和答案(C++/Python) (五)下.
剑指offer|解析和答案(C++/Python) (六).

习题

面试需要的基础知识
1.数组中重复的数字
2.二维数组中的查找
3.替换空格
4.从头到尾打印链表
5.重建二叉树
6.二叉树的下一个节点
7.用两个栈实现队列
8.斐波那契数列
9.旋转数组的最小数字
10.矩阵中的路径
11.机器人的运动范围
12.剪绳子
13.二进制中1的个数
14.跳台阶

1.数组中重复的数字

在一个长度为n的数组里的所有数字都在0到n-1的范围内。 数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中任意一个重复的数字。 例如,如果输入长度为7的数组{2,3,1,0,2,5,3},那么对应的输出是第一个重复的数字2。

思路:
1.使用哈希表。将哈希表初始化为0,然后从头到尾扫描数组的每个数字,每扫到一个数字的时候,可以用O(1)的时间复杂度判断哈希表里面是否包含了该数字。如果有则加1。扫描完后,从头到尾扫描哈希表,只要对应的数值大于1,则有重复的数字,返回true。遍历完没有大于1的,则返回false。
代码:
C++

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) {
        vector<int> completeNum;
        for(int i = 0; i < length; ++i){
            completeNum.push_back(0);
        }
        for(int i = 0; i < length; ++i){
            ++completeNum[numbers[i]];
        }
        for(int i = 0; i < length; ++i){
            if(completeNum[i]>1){
                *duplication = i;
                return true;
            }
        }
        return false;
    }
};

2.使用哈希表的时间复杂度是O(n),空间复杂度是O(n),还有可以优化的空间。使用数值和ID交换的思路,可以实现空间复杂度是O(1)的算法。
数组数字的范围是0~n-1。如果数组中没有重复的数字,那么当数组排序之后数字i将出现在下标为i的位置,如果数组中有重复的数字,那么必定会出现数字i不仅仅出现在下标为i的位置,还可能出现在下标为m、x…的位置,只要发现下标为m和下标为i的数字相同,则发现有重复的数字,返回ture。
代码:
C++

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) {
        for(int i = 0; i < length; ++i){
            while(i != numbers[i]){
                //判断
                if(numbers[i] == numbers[numbers[i]]){
                    //找到重复的数字
                    *duplication = numbers[i];
                    return true;
                }
                //交换顺序
                int temp = numbers[i];
                numbers[i] = numbers[temp];
                numbers[temp] = temp;
            }
        }
        return false;
    }
};

Python

# -*- coding:utf-8 -*-
class Solution:
    # 这里要特别注意~找到任意重复的一个值并赋值到duplication[0]
    # 函数返回True/False
    def duplicate(self, numbers, duplication):
        # write code here
        for i in range(len(numbers)):
            #对应位置上的元素不相等
            while numbers[i] != i:
                #判断
                if numbers[i] == numbers[numbers[i]]:
                    #找到重复的数字
                    duplication[0] = numbers[i]
                    return True
 
                #交换位置
                temp = numbers[i]
                numbers[i] = numbers[temp]
                numbers[temp] = temp
        #没有找到重复的
        return False

2.二维数组中的查找

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

思路:
从右上角元素为参考元素,每次和target进行比较。如果target等于参考元素,那么返回true,如果不是进行比较。1.target大于参考元素,则排除该行。2.如果target小于参考元素,则排除该列。一次类推,直到达到边界条件,如果还没有没有找到,则返回false。
C++

class Solution {
public:
    bool Find(int target, vector<vector<int> > array) {
 
        int cols = array[0].size();
        int rows = array.size();
        if( array.empty()==0 && cols > 0 && rows > 0){
        //查找右上角的元素
        int col = cols - 1;
        int row = 0;
        //如果target大于该元素,则排除该行,如果target小于该元素,排除该列
 
        while( col >= 0 && row < rows){
            if( target == array[row][col]){
                return true;
            }else if( target > array[row][col]){
                ++row;
            }else if( target < array[row][col]){
                --col;
            }
        }
        }
        return false;
    }
};

Python

class Solution:
    # array 二维列表
    def Find(self, target, array):
        # write code here
        cols = len(array[0])
        rows = len(array)
        if cols > 0 and rows > 0:
            col = cols - 1
            row = 0
            while row < rows and col >= 0 :
                if target == array[row][col]:
                    return 1
                elif target < array[row][col]:
                    col = col - 1
                else :
                    row = row +1    
        return 0

3.替换空格

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

思路
两个方向
1.正向查找,找到’ ‘就把后面的字符数组后移2位,同时把’ ‘替换为’%20‘。但是这样每找到’ '就后把后面的所以字符数组移动,假设这个字符数组长度为n,时间复杂度为O(n^2)。
2.反向查找,对原字符数组进行“扩容”,计算所有的’ ‘的个数,一个指针指向“扩容”数组的最后一个,一个指针指向“原”数组的最后一个,依次赋值移动。
C++

class Solution{
public:
	//length 为字符数组str的总容量
	void replaceSpace(char*str,int length) {
		//特殊情况判断
	        if( str == nullptr||length <= 0){
	        	return;
	        }
		 //记录空格数目
	        int originalLength = 0;//计算原字符串实际长度
	        int numberOfBlank = 0;//记录空格数目
	        int i = 0;
	        while(str[i] !='\0'){
	            ++originalLength;
	            if( str[i] ==' '){
	                ++numberOfBlank;
	            }
	            ++i;
	        }
	        //判断是否超出容量
	        int newLength = originalLength + 2*numberOfBlank;
	        if(newLength > length){
	            return;
	        }
	        int indexOfOriginal = originalLength;
	        int indexOfNew = newLength;//倒序排列赋值
	        while(indexOfOriginal >= 0 && indexOfNew > indexOfOriginal){
	            //注意originalLength是排除'\0'之后的长度,
	            //所以第一次进入循环时:str[originalLength] = '\0'
	            if( str[indexOfOriginal] ==' '){
	                str[indexOfNew--] ='0';
	                str[indexOfNew--] ='2';
	                str[indexOfNew--] ='%';
	            }else{
	                str[indexOfNew--] = str[indexOfOriginal];
	            }
	            --indexOfOriginal;
        	}
	}
}; 

Python

class Solution:
    # s 源字符串
    def replaceSpace(self, s):
        # write code here        
        originalLength = len(s)
        #记录空格数
        i = 0
        numberOfBlank = 0
        while i < originalLength:
            if s[i] == ' ':
                numberOfBlank = numberOfBlank + 1
            i = i + 1
        newLength = originalLength + 2*numberOfBlank 
        indexOriginal = originalLength - 1
        indexNewstr = newLength - 1       
        #python的string类型不允许改变 新建一个string
        new_string = [None for i in range(newLength)]
        while indexOriginal >= 0  :
            if s[indexOriginal] == ' ':
                new_string[indexNewstr] = '0'                
                new_string[indexNewstr - 1] = '2'                
                new_string[indexNewstr - 2] = '%'
                indexNewstr = indexNewstr - 3
            else :
                new_string[indexNewstr] = s[indexOriginal]
                indexNewstr = indexNewstr - 1             
            indexOriginal = indexOriginal -1
        return ''.join(new_string)

4.从头到尾打印链表

输入一个链表,按链表从尾到头的顺序返回一个ArrayList。

思路
在不改变原来数据内容的前提下,进行从头到尾的打印链表内容。这是一个很明显的“后进先出”,可以使用“栈”这种数据结构进行存储。分两步走:1.遍历链表,存储链表的值到栈stack中。2.弹出栈stack顶层的元素到ArrayList中,直到栈为空。代码如下:
C++:

/**
*  struct ListNode {
*        int val;
*        struct ListNode *next;
*        ListNode(int x) :
*              val(x), next(NULL) {
*        }
*  };
*/
class Solution {
public:
    vector<int> printListFromTailToHead(ListNode* head) {
        std::stack<ListNode*> nodes;//创建栈这种数据结构,后进先出。
        ListNode* pNode = head;//先指向链表头结点
        while(pNode != nullptr){//当没有指向最后的空指针时
            nodes.push(pNode);//把该指针放进栈中
            pNode = pNode->next;//指向下一个指针
        }
        vector<int> ArrayList;//存储打印的值
        while(!nodes.empty()){//当栈不为空时
            pNode = nodes.top();
            ArrayList.push_back(pNode -> val);//放进向量中
            nodes.pop();//弹出
        }
        return ArrayList;
    }
};

Python

# -*- coding:utf-8 -*-
#class ListNode:
#    def __init__(self, x):
#        self.val = x
#        self.next = None
 
class Solution:
    # 返回从尾部到头部的列表值序列,例如[1,2,3]
    def printListFromTailToHead(self, listNode):
        # write code here
        stack = []#创建一个栈
        pNode = listNode #创建一个结点遍历整个链表
        while pNode:#当链表不是最后一个链表时
            stack.append(pNode.val)
            pNode = pNode.next
        ArrayList = []#保存倒序输出的值
        while len(stack) != 0:#栈不为空时
            ArrayList.append(stack.pop())#弹出栈最后一个值
         
        return ArrayList

5.重建二叉树

输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。
在这里插入图片描述

根据上图可以发现根节点的规律。前序的第一个节点便是根节点,根据这个节点可以差分左右子树,然后进一步遍历重建二叉树。
思路:
先根据前序遍历的第一个数字船舰根节点,并在中序遍历中找到根节点的位置,并进一步确定左右子树及其节点。通过上述步骤划分左右子树以及根节点之后,便递归调用函数实现二叉树重建。
C++:

class Solution {
public:
    TreeNode* reConstructBinaryTree(vector<int> pre,vector<int> vin) {
 
        TreeNode* tree;
        if(pre.empty() || vin.empty()){
            return nullptr;
        }
        //把vector转换成数组
        int len = pre.size();
        int preArr[len];
        int vinArr[len];
        for( int i = 0; i < len; i++){
            preArr[i] = pre[i];
            vinArr[i] = vin[i];
        }
        //使用递归
 
        return Construct(preArr, preArr + pre.size() - 1,
                         vinArr, vinArr + vin.size() - 1);
 
    }
    TreeNode* Construct(int* startPre, int* endPre,
                        int* startVin, int* endVin){
        //前序遍历的第一个数字是根节点值
        int rootValue = startPre[0];
        //这样写会越界
        /*TreeNode* root;
        root->val = rootValue;
        root->left = nullptr;
        root->right = nullptr;
        */
        TreeNode* root = new TreeNode(rootValue);
        cout<< "rootValue:"<<rootValue<<endl;
 
        if(startPre == endPre){//只有一个节点时 前序和中序都只有一个值
            if(startVin == endVin && *startPre == *startVin){
                return root;
            }else{
                //throw std::exception("Invalid input");
                cout<<"error"<<endl;
            }
 
        }
        //在中序中找到根节点值的地址
        int* rootInorder = startVin;//指针表明地址,数组的地址是连续的
        while(rootInorder <= endVin && *rootInorder != rootValue){
            ++rootInorder;//地址+1 直到找到在中序中找到跟节点值
        }
        if(rootInorder == endVin && *rootInorder != *endVin){
            //throw std::exception("Invalid input");
            cout<<"error"<<endl;
        }
        int leftLength = rootInorder - startVin;//地址相减 得到左子树长度
        int* leftPreEnd = startPre + leftLength;//在前序中找到左子树结束的地址
        if(leftLength > 0){
            //构建左子树
            root->left = Construct(startPre + 1,  leftPreEnd, startVin, rootInorder - 1);
        }
        if(leftLength < endPre - startPre){//表明除左子树外还有右子树
            root->right = Construct(leftPreEnd + 1 , endPre, rootInorder + 1, endVin);
        }
        return root;
 
 
    }
 
};

Python

class Solution:            
    def reConstructBinaryTree(self, pre, tin):
        # write code here
        if len(pre) == 0 or len(tin) == 0:
            return None;
        startPre = 0
        endPre = len(pre) - 1
        startTin = 0
        endTin = len(tin) - 1
        rootValue = pre[0] #前序第一个数字是根节点
        root = TreeNode(rootValue)#创建根节点
        #异常处理  不写这一部分也可以通过
        if startPre == endPre:#当只剩一个节点时
            if startTin == endTin and pre[startPre] == tin[startTin]:
                return root
            else:
                print ("error")
                #print "error"
        #在中序中找到根节点对应的位置
        rootTin = startTin
        while rootTin <= endTin and tin[rootTin] != rootValue:
            rootTin = rootTin + 1
        #异常处理
        if(rootTin == endTin and tin[rootTin] != rootValue):
            print ("error")
            #print "error"
        #获取左子树的长度
        leftLength = rootTin - startTin
        #得到在前序排列中 左子树最后的地址序号
        leftPreEnd = startPre + leftLength
        #使用递归
        #有左子树
        if leftLength > 0 :
            root.left = self.reConstructBinaryTree(pre[startPre + 1: leftPreEnd + 1], 
                                              tin[: rootTin])
        #有右子树
        if leftLength < endPre - startPre :
            root.right = self.reConstructBinaryTree(pre[leftPreEnd + 1: ],
                                               tin[rootTin +1 : ])        
 
        return root

6.二叉树的下一个节点

给定一个二叉树和其中的一个结点,请找出中序遍历顺序的下一个结点并且返回。注意,树中的结点不仅包含左右子结点,同时包含指向父结点的指针。

在这里插入图片描述
思路:
中序遍历为:{d, b, h, e, i, a, f,c, g}
1.存在右子树,下一个节点就是它右子树中最左节点。如b的下一个节点是h,a的下一个节点是f。
2.不存在右子树,本节点是父节点的左子节点,那父节点为下一个结点。如d的下一个节点是b,f的下一个节点是c。
3.不存在右子树,且本节点是父节点的右子节点,那沿着父结点向上遍历,直到找到一个节点是其父节点的左子节点。如节点b的父节点a就是节点i的下一个节点。由于节点a是树的跟节点,它没有父节点,因此节点g没有下一个节点。
代码:
C++

class Solution {
public:
    TreeLinkNode* GetNext(TreeLinkNode* pNode)
    {
        if(pNode == nullptr){
            return nullptr;
        }
        TreeLinkNode* pNext;
        //1.存在右子树,下一个节点就是它右子树中最左节点
        if(pNode->right != nullptr){
            pNode = pNode->right;
            while(pNode->left != nullptr){
                pNode = pNode->left;
            }
            pNext = pNode;
        }
        //2.不存在右子树,本节点是父节点的左子节点,那父节点为下一个结点
        //3.不存在右子树,且本节点是父节点的右节
        //那沿着父结点向上遍历,直到找到一个节点是其父节点的左子节点
        else if(pNode->next != nullptr){
            TreeLinkNode* pCurrent;
            TreeLinkNode* pParent;
            pCurrent = pNode;
            pParent = pNode->next;
            while( pParent != nullptr&& pParent->left != pCurrent){
                pCurrent = pParent;
                pParent = pCurrent->next;
            }
            pNext =  pParent;
        }
        return pNext;
    }
};

Python

class Solution:
    def GetNext(self, pNode):
        # write code here
        if pNode.right != None :
            pNode = pNode.right
            while pNode.left != None :
                pNode = pNode.left
            return pNode
        elif pNode.next != None :
            while pNode.next != None and pNode.next.left != pNode:
                pNode = pNode.next
            pNode = pNode.next
            return pNode
        elif pNode.next == None :
            return None

7.用两个栈实现队列

用两个栈来实现一个队列,完成队列的Push和Pop操作。 队列中的元素为int类型。

思路:
根据题目要求要用栈(先入后出)实现队列(先入先出)的队列尾部插入和队列头部删除功能。
1.队列尾部插入。其实栈和队列是一致的,只需要输入即可。
2.队列头部删除。这是两种数据结构不一样的地方,栈是首先弹出的是栈顶(可以理解成队列的尾部)和队列首先弹出(删除)的是头部是不一样的(完全相反的)。换个思路想,如果把栈的顺寻进行颠倒,那么需要弹出的栈底,转换成另一个栈的栈顶不就好了么?
参考下列图示:
在这里插入图片描述
C++:

class Solution
{
public:
    void push(int node) {
        //放进元素
        stack1.push(node);
    }
 
    int pop() {
        //把stack1的元素放进stack2
        if(stack2.size() <= 0){
            while(stack1.size() > 0){
                int data = stack1.top();
                stack1.pop();
                stack2.push(data);
            }
        }
        int head = stack2.top();
        stack2.pop();
        return head;
    }
 
private:
    stack<int> stack1;
    stack<int> stack2;
};

Python

# -*- coding:utf-8 -*-
class Solution:
    def push(self, node):
        # write code here
        #栈1 先入
        self.stack1.append(node)
    def pop(self):
        #判断栈2是否为空
        if(len(self.stack2) == 0):
            while len(self.stack1) > 0:
                #把栈1放如栈2
                self.stack2.append(self.stack1.pop())
        #弹出栈顶
        head = self.stack2.pop()
        return head
        # return xx
    #初始化栈1 栈2
    def __init__(self):
        self.stack1 = []
        self.stack2 = []

8.斐波那契数列

大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项(从0开始,第0项为0)。 n<=39

思路:
1.递归求解。这种方法简单,但是效率较低。
2.循环。这种算法复杂度为O(n),这种算法是从下往上计算的,首先根据f(0)和f(1)计算出f(2),再根据f(1)和f(2)计算出f(3)…以此类推就可以算出第n项了。
代码:
C++

class Solution {
public:
    int Fibonacci(int n) {
        if(n <= 1){
            return n;
        }
        int firstFib;
        int secondFib;
        int num;
        firstFib = 0;
        secondFib = 1;
        for(int i = 2; i <= n; i++){
            num = firstFib + secondFib;
            firstFib = secondFib;
            secondFib = num;
        }
        return num;
    }
};

Python

# -*- coding:utf-8 -*-
class Solution:
    def Fibonacci(self, n):
        # write code here
        if n <= 1:
            return n
        firstFib = 0
        secondFib = 1
        num = 0
        for i in range(1, n):
            num = firstFib + secondFib
            firstFib = secondFib
            secondFib = num
        return num

9.旋转数组的最小数字

把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。
输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素。
例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。
NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。

思路:
参考图示
在这里插入图片描述
1.把P1指向数组的第一个元素,把P2指向数组的第二个元素。并求得P1、P2中间的元素。
2.在a图中P1、P2中间的元素是5,大于P1对应的元素(为3),把P1重新赋值为(P1+P2)/2。
3.重复计算,求得现在P1、P2中间的元素(为1)。
4.现在P1、P2中间的元素是1,小于P2(为2),把P2重新赋值为(P1+P2)/2。
5.现在P2 - P1 = 1,达到结束条件,P2即为翻转元素的最小值。
特殊情况:
1.翻转0个元素,返回第一个元素即可。
2.P1,P2和(P1+P2)/2对应的元素完全相等,无法判断。如数组{1,0,1,1,1}和数组{1,1,1,0,1}都可以堪称递增排序数组{0,1,1,1,1}的旋转。需要循环查找实现。
代码:
C++

class Solution {
public:
    int minNumberInRotateArray(vector<int> rotateArray) {
        if(rotateArray.empty()){
            cout<<"Error: empty"<<endl;
        }
        int length = rotateArray.size();
        int index1 = 0;//第一个指针
        int index2 = length - 1; //第二个指针
        int indexMid = index1;
        while(rotateArray[index1] >= rotateArray[index2]){//排除有0个元素翻转
            if(index2 - index1 == 1){
                indexMid = index2;
                break;
            }
            indexMid = (index1 + index2)/2;
            if(rotateArray[indexMid] >= rotateArray[index1]){
                index1 = indexMid;
            }else if(rotateArray[indexMid] <= rotateArray[index2]){
                index2 = indexMid;
            }else if(rotateArray[indexMid] == rotateArray[index1]&&
                     rotateArray[indexMid] == rotateArray[index2]){//特殊情况 3个元素一样只能循环查找
                indexMid = findMid(rotateArray, index1, index2);
                break;
            }
 
        }
        //如果翻转0个元素 直接返回第一个元素
        return rotateArray[indexMid];
 
    }
    //在index1和index2之间循环查找
    int findMid(vector<int> rotateArray, int index1, int index2){
        int index = index1;
        for(int i = index1 + 1; i < index2; ++i){
            if(rotateArray[index] > rotateArray[i]){
                index = i;
                break;
            }
        }
        return index;
    }
};

Python

# -*- coding:utf-8 -*-
class Solution:
    def minNumberInRotateArray(self, rotateArray):
        # write code here
        index1 = 0
        index2 = len(rotateArray) - 1#rotateArray是list  要用len()得到其长度
        indexMid = index1
        while rotateArray[index1] >= rotateArray[index2]:
            if index2 - index1 == 1:
                indexMid = index2
                break
            indexMid = (index1 + index2)/2
            if rotateArray[indexMid] == rotateArray[index1] and rotateArray[indexMid] == rotateArray[index2]:
 
                indexMid = self.findMid(rotateArray, index1, index2)
                break
            elif rotateArray[indexMid] >= rotateArray[index1]:
                index1 = indexMid
            elif rotateArray[indexMid] <= rotateArray[index2]:
                index2 = indexMid
        return rotateArray[indexMid]
    def findMid(self, rotateArray, index1, index2):
        index = index1
        for i in range(index1 + 1, index2):
            if rotateArray[index] > rotateArray[i]:
                index = i
                break           
        return index

10.矩阵中的路径

请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子。如果一条路径经过了矩阵中的某一个格子,则该路径不能再进入该格子。
例如
a b c e
s f c s
a d e e
矩阵中包含一条字符串"bccced"的路径,但是矩阵中不包含"abcb"路径,因为字符串的第一个字符b占据了矩阵中的第一行第二个格子之后,路径不能再次进入该格子。

思路:
在这里插入图片描述
从矩阵中的任意一个点出发,如果这个点是寻找元素中的一个,那么就从这个点四周继续寻找。如果四周存在寻找元素中的一个,那么就从那个元素四周继续寻找,如果四周没有符合的元素,回溯到上一个点继续寻找。
代码:
C++:

class Solution {
public:
    bool hasPath(char* matrix, int rows, int cols, char* str)
    {
        if(matrix == nullptr|| rows < 1 || cols < 1 || str == nullptr){
            return nullptr;
        }
        //创建一个数组 保存矩阵路径是否被选取
        bool *visited = new bool[rows * cols];
        for(int i = 0; i< rows * cols; i++){
            visited[i] = false;
        }
        //定义一个被查找的路径的长度
        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'){//'\0'是str的结束符 表明每个字符都有匹配的路径
            return true;
        }
        bool hasPath = false;
        if(row >= 0&& row < rows && col >= 0 && col < cols &&//在矩阵范围内部
           !visited[row * cols + col]&&//这个格子没有路过
           matrix[row * cols + col] ==  str[pathLength]//字符串中的字符等于矩阵该位置对应的元素
           ){
            ++pathLength;//找下一个字符
            visited[row * cols + col] = true;//对应位置下的格子被占据
            //回溯法
            //接下来找相邻四个位置下是否存在对应的元素
            hasPath = 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)||
                      hasPathCore(matrix, rows, cols, row, col + 1, str, pathLength,visited);
            if(!hasPath){//如果没有存在的路径
                --pathLength;//回溯到上一个字符
                visited[row * cols + col] = false;
            }
        }
        return hasPath;
    }
};

Python

# -*- coding:utf-8 -*-
class Solution:
    def hasPath(self, matrix, rows, cols, path):
        # write code here
        #初始化 判断路径是否被占据        
        visited = [False] * rows * cols     
 
        #以矩阵每一个点为初始点尝试寻找路径是否存在
        for row in range(0, rows):
            for col in range(0, cols):
                if self.hasPathCore(matrix, rows, cols, path, row, col, visited):
                    return True
        return False
 
    def hasPathCore(self, matrix, rows, cols, path, row, col, visited):        
        if not path:
            return True        
        #默认没有找到
        hasPath = False
        #查找这个点是否满足条件
        if row >= 0 and row < rows and col >= 0 and col < cols and \
        visited[row * cols + col] == False and  \
        path[0] == matrix[row * cols + col]:#矩阵范围内  格子未被占据  对应元素相等
            #格子被占据
            visited[row * cols + col] = True
 
            #在四周判断是否存在该元素
            hasPath = self.hasPathCore(matrix, rows, cols, path[1:], row - 1, col, visited) or\
            self.hasPathCore(matrix, rows, cols, path[1:], row + 1, col, visited) or\
            self.hasPathCore(matrix, rows, cols, path[1:], row, col - 1, visited) or\
            self.hasPathCore(matrix, rows, cols, path[1:], row, col + 1, visited)
            #如果没有找到 回溯到上一个元素
            if hasPath == False:
                visited[row * cols + col] = False
        return hasPath

11.机器人的运动范围

地上有一个m行和n列的方格。一个机器人从坐标0,0的格子开始移动,每一次只能向左,右,上,下四个方向移动一格,但是不能进入行坐标和列坐标的数位之和大于k的格子。 例如,当k为18时,机器人能够进入方格(35,37),因为3+5+3+7 = 18。但是,它不能进入方格(35,38),因为3+5+3+8 = 19。请问该机器人能够达到多少个格子?

思路:
使用回溯法解决。方格可以看作m*n的矩阵。除边界上的格子,其他格子都有4个相邻的格子。机器人从(0,0)开始移动,移动到(i,j)时判断是否能够进入,能进入再判断四周能否进入,依此类推。
代码:
C++

class Solution {
public:
    int movingCount(int threshold, int rows, int cols)
    {
        //创建一个数组 判断路径是否走过
        bool *visited = new bool[rows * cols];
        for(int i = 0; i< rows * cols; i++){
            visited[i] = false;
        }
        int countNum = 0;
        //从(0,0)开始走
        countNum = movingCountCore(threshold, rows, cols, 0, 0, visited);
        delete[] visited;
        return countNum;
    }
    int movingCountCore(int threshold, int rows, int cols, int row, int col, bool* visited){
        int countNum = 0;
        if(row >= 0 && row < rows && col >= 0 && col < cols &&//区域之内
           visited[row * cols + col]== false && //没有走过这个格子
           getNum(row) + getNum(col) <=threshold){//小于阈值
            visited[row * cols + col] = true;
            countNum = 1 +  movingCountCore(threshold, rows, cols, row + 1, col, visited)
                        + movingCountCore(threshold, rows, cols, row - 1, col, visited)
                        + movingCountCore(threshold, rows, cols, row, col + 1, visited)
                        + movingCountCore(threshold, rows, cols, row, col - 1, visited);
           }
        return countNum;
    }
    int getNum(int num){
        int sum = 0;
        while(num > 0){
            sum += num%10;
            num = num/10;
        }
        return sum;
    } 
};

Python

# -*- coding:utf-8 -*-
class Solution:
    def movingCount(self, threshold, rows, cols):
        # write code here
        #建立数组保存是否经过路径
        visited = [False] * rows * cols
        count = 0
        count = self.movingCountCore(threshold, rows, cols, 0, 0, visited)
        return count
    def movingCountCore(self,threshold, rows, cols, row, col, visited):
        count = 0
        if row >= 0 and row < rows and col >= 0 and col < cols and \
        visited[row * cols + col] == False and \
        self.getNum(row) + self.getNum(col) <= threshold:
            visited[row * cols + col] = True
            count = 1 + self.movingCountCore(threshold, rows, cols, row - 1, col, visited) \
                    + self.movingCountCore(threshold, rows, cols, row + 1, col, visited) \
                    + self.movingCountCore(threshold, rows, cols, row, col - 1, visited) \
                    + self.movingCountCore(threshold, rows, cols, row, col + 1, visited)
        return count
    def getNum(self,num):
        sumNum = 0
        while num > 0:
            sumNum = sumNum + num % 10
            num = num /10
        return sumNum

12.剪绳子

给你一根长度为n的绳子,请把绳子剪成m段(m、n都是整数,n>1并且m>1,每根绳子的长度记为k[0],k[1],…,k[m])。请问k[0]xk[1]x…xk[m]可能的最大乘积是多少?例如,当绳子的长度是8时,我们把它剪成长度分别为2、3、3的三段,此时得到的最大乘积是18.

思路:
1.动态规划。O(n2)时间复杂度,O(n)空间复杂度。
在剪一刀的时候,有n-1种选择。剪出的长度可能分别为1,2,…,n - 1。因此f(n) = max(f(i) x f(n - i)),0<i<n。
从下往上,先得到f(2)、f(3),再得到f(4)、f(5),直到f(n)。
子问题的最优解存储在数组products中。数组中第i个元素表示把长度为i的绳子剪成若干段之后各长度乘积的最大值,即 f ( i ) f(i) f(i)。在求 f ( i ) f(i) f(i)之前,对于每一个j( 0 < i < j), f ( i ) f(i) f(i)都解出来,并放在projects[j]里。为求解 f ( i ) f(i) f(i),需要求解所有 f ( j ) × f ( i − j ) f(j) \times f(i-j) f(j)×f(ij)并比较它们的最大值。
参考代码:

class Solution {
public:
	int cutRope(int number){ 
    if(number < 2)
        return 0;
    if(number == 2)
        return 1;
    if(number == 3)
        return 2;
    int* products = new int[number + 1];
    products[0] = 0;
    products[1] = 1;
    products[2] = 2;
    products[3] = 3;
    int maxNum = 0;
    for(int i = 4; i <= number; i++){
        maxNum = 0;
        for(int j = 1; j <= i/2; ++j){
            int product = products[j] * products[i - j];
            if(maxNum < product){
                maxNum = product;
            }
            products[i] = maxNum;
        }
    }
    maxNum = products[number];
    delete[] products;
    return maxNum;
	};
};
# -*- coding:utf-8 -*-
class Solution:
    def cutRope(self, number):
        # write code here
        if number < 2:
            return 0
        if number == 2:
            return 1
        if number == 3:
            return 2
        
        products = []
        
        for i in range(4):
            products.append(i)
        for i in range(4, number + 1):
            products.append(0)
        
        for i in range(4, number + 1):
            maxNum = 0
            for j in range(1, i/2 + 1):
                product = products[j] * products[i - j]
                if maxNum < product:
                    maxNum = product
                products[i] = maxNum
        
        maxNum = products[number]
        
        return maxNum

2.贪婪算法
当n >= 5 时,尽可能多地剪长度为3的绳子;当剩下的绳子长度为4时,把绳子剪成2段长度为2的绳子。
贪婪算法需要数学证明思路的正确性。
当n >= 5 时,可证2(n - 2)>n,并且3(n - 3)>n。即当n >= 5 时,尽可能多地剪长度为3或2的绳子。又由于当n >= 5 时,3(n - 3) > 2(n - 2)。因此尽可能选择剪长度为3的绳子。当长度为4时,13 < 22。
参考代码:

class Solution {
public:
	int cutRope(int length){
		if(length < 2)
			return 0;
		if(length == 2)
			return 1;
		if(length == 3)
			return 2;
		//尽可能多地减去长度为3的绳子段
		int timesOf3 = length/3;
		//当绳子最后剩下的长度为4的时候,不能减去长度为3的绳子段。
		//此时更好的办法是将绳子剪成长度为2的两端,因为2x2 > 1x3
		if(length - timesOf3 * 3 == 1){
			timesOf3 = timesOf3 - 1;
		}
		int timesOf2 = (length - timesOf3 * 3)/2;
		
		return (int)(pow(3, timesOf3)) * (int)(pow(2, timesOf2));
	}
};

Python

# -*- coding:utf-8 -*-
class Solution:
    def cutRope(self, number):
        # write code here
        if number < 2:
            return 0
        if number == 2:
            return 1
        if number == 3:
            return 2
        timesOf3 = number//3
        
        if number - timesOf3*3 == 1:
            timesOf3 -= 1
        timesOf2 =(number - timesOf3*3)//2
        
        return 3**timesOf3 * 2**timesOf2

13.二进制中1的个数

输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示。

思路:
1.常见思路。
避免死循环,不右移输入数字n。首先把n和1做与运算,进行判断最低位是不是1。接着把1左移成2,进行判断,依此类推…
代码
C++

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

Python
**使用Python的时候需要注意,和C++通过移位来使得数值溢出是不可行的,会造成无限死循环。因此需要直接对其判断。**选择0xffffffff是因为在C++中int占4个字节共32位。

class Solution:
    def NumberOf1(self, n):
        # write code here
        numFlag = 1
        count = 0
        while numFlag & 0xffffffff != 0 :
            if numFlag & n :
                count = count + 1
            numFlag = numFlag << 1
 
        return count

2.技巧性方法。**把一个整数减去1,再和原整数做与操作,会把该整数最右边的1变成0。**如:1100,减去1后成1011,再和1100做与操作,变成1000,即最右边的1变成0。能做多少次这个操作就说明有多少个1。
代码:
C++

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

Python
在 Python 程序中,当对一个负整数与其减 1 后的值按位求与,若结果为 0 退出,循环执行此过程。由于整型数可以有无限的数值精度,其结果永远不会是 0,如此编程,在 Python 中,只会造成死循环。同样使用0xffffffff限定范围。

class Solution:
    def NumberOf1(self, n):
        # write code here        
        count = 0
        while n & 0xffffffff != 0 :
            count = count + 1
            n = (n - 1) & n             
        return count

14.跳台阶

一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法(先后次序不同算不同的结果)。

思路:
先考虑最简单的情况。如果只有1级台阶,那显然只有一种跳法。如果有2级台阶,有两种跳法:1.分两次跳,每次跳一级。2.一次跳,每次次跳两级。
一般情况:把n级台阶时的跳法看成n的函数,记为 f ( n ) f(n) f(n)。当n>2时,第一次跳的时候就两种不同选择:
1.第一次只跳1级,此时跳法数目等于剩下的n - 1级台阶的跳法数目,即为 f ( n − 1 ) f(n-1) f(n1)
2.第一次只跳2级,此时跳法数目等于剩下的n - 2级台阶的跳法数目,即为 f ( n − 2 ) f(n-2) f(n2)
此时n级台阶的不同跳法的总数为 f ( n ) = f ( n − 1 ) + f ( n − 2 ) f(n)=f(n-1)+f(n-2) f(n)=f(n1)+f(n2)
代码:
C++

class Solution {
public:
    int jumpFloor(int number) {
        if(number < 3)
			return number ;
		int floorOne = 2;
		int floorTwo = 1;
		int floorN = 0;
		for(int i = 3; i <= number; ++i){
			floorN = floorOne + floorTwo;
			floorTwo = floorOne;
			floorOne = floorN;			
		}
		return floorN;
    }
};

Python

# -*- coding:utf-8 -*-
class Solution:
    def jumpFloor(self, number):
        # write code here
        if number < 3:
            return number
        floorOne = 2
        floorTwo = 1
        for i in range(3, number + 1):
            floorN = floorOne + floorTwo
            floorTwo = floorOne
            floorOne = floorN
        
        return floorN        
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值