剑指offer(刷题1-10)--c++,Python版本

目录

第一题:

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

解题思路:

这是一个查找问题,由于题目特定强调了有序的数组,所以我们如果使用直接使用顺序查找肯定是不当的。所以我们可以利用数组的性质进行查找。

  • 1.使用顺序查找,但是不从第一个数开始找,而是从二位数组的左上角的数开始找,如果正好相等,则返回,如果小于被查找的数,则行号加一,否则,列号减一。
  • 2.使用二分查找。可以遍历行或者列,先比较该行或者该列的最后一个元素与要查找的元素的大小关系,然后针对该行或者列进行二分查找

代码实现:

c++

顺序查找
#include <iostream>
#include <vector>

using namespace std;

//二维数组查找
bool Find(int target , vector<vector<int>> array){
	if(array.empty()){
		return false;
	}
	int row = array.size();
	int col = array[0].size();
	int i=0 , j = col - 1;
	while(i < row && j >= 0){
		if(target == array[i][j]){
			return true;
		}else if (target > array[i][j]){
			i ++;
		}else{
			j --;
		}
	}
	return false;
}

int main(){
	int a1[] = { 1, 1, 8, 9, };
    int a2[] = { 2, 4, 9, 12, };
    int a3[] = { 4, 7, 10, 13, };
    int a4[] = { 6, 8, 11, 15, };

	vector<vector<int>> myArry;
	myArry.push_back(vector<int>(a1, a1 + 4));
	myArry.push_back(vector<int>(a2, a2 + 4));
    myArry.push_back(vector<int>(a3, a3 + 4));
    myArry.push_back(vector<int>(a4, a4 + 4));

	cout<<"the result is : "<<Find(100,myArry)<<endl;
	return 0;
}
二分查找
#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

//二维数组查找
bool Find(int target , vector<vector<int>> array){
	if(array.empty()){
		return false;
	}
	int row = array.size();  //行的数目
	//每行进行查找
	for(int i = 0;i<row;i++){
		if(binary_search(array[i].begin(),array[i].end(),target)){
			return true;
		}
	}
	//扫描完每行后没有发现,则说明没有找到
	return false;
}

int main(){
	int a1[] = { 1, 1, 8, 9, };
    int a2[] = { 2, 4, 9, 12, };
    int a3[] = { 4, 7, 10, 13, };
    int a4[] = { 6, 8, 11, 15, };

	vector<vector<int>> myArry;
	myArry.push_back(vector<int>(a1, a1 + 4));
	myArry.push_back(vector<int>(a2, a2 + 4));
    myArry.push_back(vector<int>(a3, a3 + 4));
    myArry.push_back(vector<int>(a4, a4 + 4));

	cout<<"the result is : "<<Find(4,myArry)<<endl;
	return 0;
}

Python

def Find(target ,array):
    if array == []:
        return False
    row = len(array) - 1
    col = len(array[0])
    i = 0 
    j = col - 1
    while i < row and j >= 0:
        if target == array[i][j]:
            return True
        elif target > array[i][j]:
            i += 1
        else:
            j -= 1
    return False

Find(1,[[1, 1, 8, 9],[2, 4, 9, 12],[4, 7, 10, 13],[6, 8, 11, 15]])

第二题:

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

解题思路:

  • 一般的思维:遍历字符串,然后找到每个字符串中每个空格的位置,然后使用“%20”进行替换;但是由于字符串存储的是顺序结构,所以插入字符串会导致字符串中字符后移;所以可以先统计出字符串中空字符的个数,然后首先算出要移动的位置,从字符串后面往前逐步替换掉。
  • 借助c++中的string类的方法,首选将字符串转换为string ,然后调用find函数和replace函数将每个空格替换成“%20”,最后将string转换为c_string.

代码实现:

c++

#include <string>

class Solution {
public:
	void replaceSpace(char *str,int length) {
        string tempStr(str);  //将c风格的字符串转换为string
	    //遍历字符串,找到每个空格的位置,然后替换掉它
	    for(int i=0 ; i <tempStr.size(); i++){
		    int tempIndex = tempStr.find(" " , i);  //前面搜过的一定不能重复搜索
		    if(tempIndex!=-1){
			    tempStr.replace(tempIndex,1,"%20");
		    }
	    }
        strcpy(str,tempStr.c_str());
    }
        
};

python

## 字符串空格替换
def replaceSpace(s):
    s = s.replace(" ","%20")
    return s
replaceSpace("We Are Happy")

第三题:

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

解题思路:

  • 借助于栈结构,保存数据然后依次出栈即可;时间复杂度为O(N),空间复杂度为O(N);
  • 借助vector数组反转函数,先用vector保存顺序遍历链表的值,然后直接对数组进行反转,然后输出;时间复杂度为O(N),空间复杂度为O(1);
  • 先将链表反转,然后再依次遍历。

代码实现:

c++

使用栈辅助
#include <functional>
#include <string>
#include <cstring>
#include <stack>

using namespace std;

struct ListNode {
        int val;
        struct ListNode *next;
        ListNode(int x) :
              val(x), next(NULL) {
        }
  };


vector<int> printListFromTailToHead(ListNode *head){
	stack<int>  dataStack;
	vector<int> arrayList;
	if(head == NULL){
		return ;
	}
	while(head != NULL){
		dataStack.push(head->val);
		head = head->next;
	}
	while(!dataStack.empty()){
		arrayList.push_back(dataStack.top());
		dataStack.pop();
	}
	return arrayList;
}

反转链表
#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>

using namespace std;

struct ListNode {
        int val;
        struct ListNode *next;
        ListNode(int x) :
              val(x), next(NULL) {
        }
  };

vector<int> printListFromTailToHead(ListNode *head){
	ListNode *pCur , *pPre , *pNext;
	pPre = head;
	pCur = pPre->next;
	while(pCur){
		pNext = pCur->next;
		pCur->next = pPre;
		pPre = pCur;
		pCur = pNext;
	}
	head->next = NULL;
	head = pPre;
	vector<int> arrayList;
	while(head){
		arrayList.push_back(head->val);
		head = head->next;
	}
	return arrayList;	
}

python

## 链表反转输出
def printListFromTailToHead(listNode):
    tempList = []
    #顺序访问链表,并将链表的值保存
    while listNode != None:
        tempList.append(listNode.val)
        listNode = listNode.next
    return list(reversed(tempList))  #直接输出列表的反转

第四题:

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

解题思路:

  • 由于是二叉树的构建,所以我们应该想到用递归的方法进行构建,递归的终止条件就是前序遍历和中序遍历的节点个数为1,;递归的过程主要是从pre中把根节点确定,然后再从Vin中根据确定的根节点确定该节点的左右子树集合。

代码实现:

c++

#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>

using namespace std;

//二叉树节点结构定义
struct TreeNode {
	int val; //值域
	TreeNode *left;
	TreeNode *right;
	TreeNode(int x) : val(x), left(NULL), right(NULL) {}
  };
//二叉树重建
class Solution {
public:
    TreeNode* reConstructBinaryTree(vector<int> pre,vector<int> vin) {
		if(pre.size() != vin.size()){
			return NULL;
		}
		int vinLen = vin.size();
		if(vinLen == 0){
			return NULL;
		}
		vector<int> left_pre , left_vin , right_pre , right_vin;
		TreeNode * head = new TreeNode(pre[0]);
		int headIndex = 0;
		for(int i=0 ; i < vin.size() ; i++ ){
			if(vin[i] == pre[0]){
				headIndex = i;
				break;
			}
		}
		for(int i = 0; i < headIndex ; i++){
			left_vin.push_back(vin[i]);
			left_pre.push_back(pre[i+1]);//前序第一个为根节点
		}
		for(int i = headIndex + 1 ; i < vin.size() ; i++){
			right_pre.push_back(pre[i]);
			right_vin.push_back(vin[i]);
		}
		//和shell排序的思想类似,取出前序和中序遍历根节点左边和右边的子树
  
       //递归,再对其进行上述所有步骤,即再区分子树的左、右子子数,直到叶节点
		head->left = reConstructBinaryTree(left_pre,left_vin);
		head->right = reConstructBinaryTree(right_pre,right_vin);
		return head;
    }

};

python

##重建二叉树

#树的节点结构
class TreeNode:
    def __init__(self, x):
        self.val = x
        self.left = None
        self.right = None

#重构二叉树
def reConstructBinaryTree(pre , tin):
    if len(pre)!= len(tin):
        return None
    rootNode = TreeNode(pre[0])
    rootTinIndex = tin.index(rootNode.val)
    if rootTinIndex == None:
        return None
    rootNode.left = reConstructBinaryTree(pre[1:rootTinIndex + 1] , tin(:rootTinIndex))
    rootNode.right =reConstructBinaryTree(pre[rootTinIndex+1:] , tin(rootTinIndex:))
    return root

第五题:

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

解题思路:

  • 使用两个栈,一个用于实现队头,一个用于实现队尾操作。需要注意的是出队操作,如果用于实现队头的那个栈没有数据,则需要将实现队尾栈中的数据复制到其中。

代码实现:

c++

#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>
#include <stack>

using namespace std;

class solution{
private:
	stack<int> stack1; //队头
	stack<int> stack2; //队尾
public:
	void push(int node){
		stack2.push(node);
	}
	int pop(){
		if(stack1.empty() && stack2.empty()){  //队列为空
			return -1;
		}
		if(stack1.empty()){  //队列一半为空
			while(!stack2.empty()){
				stack1.push(stack2.top());
				stack2.pop();
			}
		}
		int tempData = stack1.top();
		stack1.pop();
		return tempData;
	}
};



int main(){
	solution s;
	s.push(1);
	s.push(2);
	s.push(3);
	cout<<s.pop()<<endl;
	cout<<s.pop()<<endl;
	cout<<s.pop()<<endl;

	return 0;
}

python

## 两个栈实现队列
class Solution:
    def __init__(self):
        self.stack1 = []
        self.stack2 = []
    
    def push(self, node):
        # write code here
        self.stack1.append(node)
        
    def pop(self):
        # return xx
        if self.stack1 == [] and self.stack2 == []:
            return None
        if self.stack2 == []:
            while(len(self.stack1)):
                self.stack2.append(self.stack1.pop())
        return self.stack2.pop()
    
if __name__ == "__main__":
    s = Solution()
    s.push(1)
    s.push(2)
    s.push(0)
    print s.pop()

第六题:

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

解题思路:

  • 首先肯定是可以通过STL中的算法,将旋转后的数组进行排序后,然后输出第一个元素的值便可;
  • 可以利用旋转素组的特性,是将一个有序的数组的前一部分搬到数组的末尾,所以数组可以分为2块,前一块和后一块都是一种升序的数组,而转折点就是最小值。

代码实现:

c++

class Solution {
public:
    int minNumberInRotateArray(vector<int> rotateArray) {
        if(rotateArray.size() == 0){
			return 0;
		}
	for(int i = 0 ; i < rotateArray.size() ; i++ ){
		if(rotateArray[i] < rotateArray[0]){  //找到后面第一个比数组第一个元素小的元素
			return rotateArray[i];  
		}
	}
	return rotateArray[0];  //如果没有找到,说明最小的元素在数组的第一个位置
    }
};

python

# -*- coding:utf-8 -*-
class Solution:
    def minNumberInRotateArray(self, rotateArray):
        # write code here
        if len(rotateArray) == 0:
            return 0
        for i in range(len(rotateArray)):
            if rotateArray[i] < rotateArray[0]:
                return rotateArray[i]
        return rotateArray[0] 

第七题:

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

解题思路:

  • 使用for循环遍历,使用斐波那契数列推到公式,得出每个数,然后由一个数组进行保存。
  • 由于不需要整个数列,只是数列的最后一项想,所以可以将存储空间直接取3,每次求解迭代的时候更新进行。

代码实现:

c++

第一种:
class Solution {
public:
    int Fibonacci(int n) {
        vector<int> fibonacciArray(n+1);
	    if(n<=0){
		    return 0; 
	    }
	    fibonacciArray[0] = 0;
	    fibonacciArray[1] = 1;
	    for(int i = 2 ; i < n+1 ; i++){
		    fibonacciArray[i] = fibonacciArray[i-2] + fibonacciArray[i-1];
	    }
	    return fibonacciArray[n];
    }
};
第二种:
class Solution {
public:
    int Fibonacci(int n) {
        vector<int> fibonacciArray(3);
	    if(n<=0){
		    return 0; 
	    }
        if(n < 2){
            return 1;
        }
        fibonacciArray[0] = 0;
        fibonacciArray[1] = 1;
        for(int i = 2 ; i < n+1 ; i++){
            fibonacciArray[2] = fibonacciArray[0] + fibonacciArray[1];
            fibonacciArray[0] = fibonacciArray[1];
            fibonacciArray[1] = fibonacciArray[2];
        }
        return fibonacciArray[2];
        }
};

python

#输出斐波那契数列的第N项
def Fibonacci(n):
    if n < 0 :
        return -1
    elif n==0:
        return 0
    elif n < 2:
        return 1
    else:
        pre = 0
        cur = 1
        for i in range(2,n+1):
            last = pre + cur
            pre = cur
            cur = last
        return last
    

Fibonacci(3)

第八题:

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

解题思路:

  • 归纳:
    把台阶都看成是木板,即有n块木板,其中最后一块木板是青蛙的目的地,是必须存在的,所以总共是n-1块木板可以选择。由于青蛙一次可以跳一级也可以一次跳两级,所以对于当前这个木板,它可以被跳也可以不被跳,那么久总共存在2^(n-1)种可能。
  • 递归
    记跳 n 级台阶有 f(n) 种方法
    如果第一次跳 1 级,那么之后的 n-1 级有 f(n-1) 种跳法
    如果第一次跳 2 级,那么之后的 n-2 级有 f(n-2) 种跳法
    实际上就是首两项为 1 和 2 的斐波那契数列

代码实现:

c++

递归的方法(该方法的通过率比较低):
#include <iostream>
#include <vector>
using namespace std;
int jumpFloorII(int number) {
	if(number <=0){
		return -1;
	}else if(number == 1){
		return 1;
	}else if(number == 2){
		return 2;
	}else{
		return jumpFloorII(number - 1) + jumpFloorII(number - 2);
	}

  }

int main(){
	cout<<jumpFloorII(5)<<endl;
	return 0;
}
归纳法(100%通过):
#include <iostream>
#include <vector>
#include <cmath>


using namespace std;

int jumpFloorII(int number) {
	if(number <= 0){
		return -1;
	}else{
		return pow(2,number-1);
	}
	
}

int main(){
	cout<<jumpFloorII(6)<<endl;
	return 0;
}
动态规划的方法(100%通过)
class Solution {
public:
    int jumpFloorII(int number) {
        vector<int> dp(number+1, 1);  //创建动态U规划列表
        for (int i=2; i<=number; i++)  
            for(int j=1; j<i; j++)
                dp[i] += dp[j];
        return dp[number];
    }
};

python

##青蛙跳台阶问题
#归纳方法
def jumpFloorII(number):
    if number <= 0:
        return -1
    else:
        return 2 ** (number - 1)
    
jumpFloorII(5)

第九题:

我们可以用21的小矩形横着或者竖着去覆盖更大的矩形。请问用n个21的小矩形无重叠地覆盖一个2*n的大矩形,总共有多少种方法?

解题思路:

  • 递归:
    f(1) = 1;
    f(2) = 2;
    当n>2时,画图可知,第一块小矩形可横放和竖放。横放后剩余的长度为n-2,竖放后剩余的长度为n-1。
    所以:f(n) = f(n-1) + f(n-2); (n > 2)
  • 类比:
    我们对算法模型做些简化,我们知道,只可以放置两种类型的小矩形,一种是竖着放的21矩形,另一种是两块横着放的21矩形上下放置形成的22正方形,而题目要放置的是2n的大矩形。
    我们将上面模型映射到一维,即是我们有一条长度为n的线段,现在要么放置长度为1,要么放置长度为2的线段,请将该线段填满。
    这让我想起了走阶梯的题目,一个n级阶梯,每次要么走一级要么两级,请问有多少种方法。
    综上分析,可知,
    n = 1时, f(n) = 1;
    n = 2时, f(n) = 2;
    n > 2时,f(n) = f(n - 1) + f(n - 2);

代码实现:

递归方法

c++

#include <iostream>
#include <vector>
#include <cmath>

using namespace std;

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

int main(){
	cout<<rectCover(6)<<endl;
	return 0;
}
非递归方法:
#include <iostream>
#include <vector>
#include <cmath>

using namespace std;

int rectCover(int number) {
	if(number < 0 ){
		return -1;
	}else if(number ==0){
		return 0;
	}else if(number == 1){
		return 1;
	}else if(number == 2){
		return 2;
	}
	vector<int> tempArry(3);
	tempArry[0] = 1;
	tempArry[1] = 2;
	for(int i = 3 ; i <= number;i++){
		tempArry[2] = tempArry[0] + tempArry[1];
		tempArry[0] = tempArry[1];
		tempArry[1] = tempArry[2];
	}
	return tempArry[2];
}

int main(){
	cout<<rectCover(6)<<endl;
	return 0;
}

python

#矩形覆盖问题
def rectCover(number):
    if number<0:
        return -1
    elif number==0:
        return 0
    elif number==1:
        return 1
    elif number == 2:
        return 2
    else:
        return rectCover(number -1) + rectCover(number - 2)
    
rectCover(6)

第十题:

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

解题思路:

  • 通过移位操作,每次与1做与,求出其中1的个数,一定要注意负数的情况
  • 比较巧的做法
    如果一个整数不为0,那么这个整数至少有一位是1。如果我们把这个整数减1,那么原来处在整数最右边的1就会变为0,原来在1后面的所有的0都会变成1(如果最右边的1后面还有0的话)。其余所有位将不会受到影响。
    举个例子:一个二进制数1100,从右边数起第三位是处于最右边的一个1。减去1后,第三位变成0,它后面的两位0变成了1,而前面的1保持不变,因此得到的结果是1011.我们发现减1的结果是把最右边的一个1开始的所有位都取反了。这个时候如果我们再把原来的整数和减去1之后的结果做与运算,从原来整数最右边一个1那一位开始所有位都会变成0。如1100&1011=1000.也就是说,把一个整数减去1,再和原整数做与运算,会把该整数最右边一个1变成0.那么一个整数的二进制有多少个1,就可以进行多少次这样的操作。

代码实现:

c++

class Solution {
public:
     int  NumberOf1(int n) {
        int count = 0;
        while (n != 0) {
            ++count;
            n = (n - 1) & n;
        }
        return count; 
     }
};
class Solution {
public:
     int  NumberOf1(int n) {
         int count = 0;
         if(n < 0){
             n = n & 0x7fffffff;  //当n为负数的时候,只需要将最高位的0置位为1
             ++count;  
         }
         while( n!=0 ){
             if(n & 1 == 1){
                 count++;
             }
             n = n >> 1;
         }

         return count; 
     }
};

python

#查找一个数二进制表示中1的个数
def NumberOf1(n):
    count = 0
    if n < 0 :
        n = n & 0xfffffff
        count += 1
    while n != 0:
        if n & 1 == 1:
            count += 1
        n = n >> 1
    return count

NumberOf1(3)

猜你喜欢

转载自blog.csdn.net/wangzi11111111/article/details/88601584