剑指offer(1---10)

1、二维数组中的查找

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

从题目看这个二维数组的规律是从上到下,从左到右是依次递增的,这里就可以利用二分查找的思想来实现,将数组看成一个矩阵,这里有一个特殊的参照就是矩阵右上角,如果target比它小,就可以向左找,如果target比它大,就可以向下找,如果相等就可以返回。
实现代码:

class Solution {
public:
    bool Find(int target, vector<vector<int> > array) {
        int row=array.size();
        int col=array[0].size();
        int left=0;
        int right=col-1;//以右上角的为基准
        while(left<row && right>=0)
        {
            if(target == array[left][right])
                return true;
            else if(target < array[left][right])
                right--;
            else
                left++;
        }
        return false;
    }
};
2、替换空格

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

这里可以看到%20占三个字符的位置,而原字符串每两个字符之间只有一个字符的空格位置,计算原字符串共有多少个空格,新字符串的长度应该是旧字符串长度+2*空格数,之后只需要定义两个变量,p1是旧字符串长度,p2是新字符串长度,将旧字符串的字符逐个填充到新字符串中(注意遇到空格就要实现替换),这个过程p1>=0并且p2>=p1。
实现代码:

class Solution {
public:
	void replaceSpace(char *str,int length) {
        if(str == nullptr)
            return;
        int originlen=0;
        int spacecount=0;
        for(int i=0;str[i]!='\0';++i)
        {
            originlen++;
            if(str[i] == ' ')
                spacecount++;
        }
        int newlen = originlen+2*spacecount;
        if(newlen>length+1)
            return;
        int p1=originlen;
        int p2=newlen;
        while(p1>=0 && p2>=p1)
        {
            if(str[p1] == ' ')
            {
                str[p2--]='0';
                str[p2--]='2';
                str[p2--]='%';
            }
            else
                str[p2--]=str[p1];
            p1--;
        }
	}
};
3、从尾到头打印链表

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

我们可以定义一个栈,利用其先进后出的特性让链表的值逆转,然后一一取出栈顶元素尾插到Arra中即可。

/**
*  struct ListNode {
*        int val;
*        struct ListNode *next;
*        ListNode(int x) :
*              val(x), next(NULL) {
*        }
*  };
*/
class Solution {
public:
    vector<int> printListFromTailToHead(ListNode* head) {
        vector<int> ArrayList;
        if(head == nullptr)
            return ArrayList;
        ListNode* pCur=head;
        stack<int> s;
        while(pCur)
        {
            s.push(pCur->val);
            pCur=pCur->next;
        }
        int len=s.size();
        for(int i=0;i<len;++i)
        {
            ArrayList.push_back(s.top());
            s.pop();
        }
        return ArrayList;     
    }
};
4、重建二叉树

知道前序序列(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:
    TreeNode* reConstructBinaryTree(vector<int> pre,vector<int> vin) {
        if(vin.size()==0)
            return nullptr;
        TreeNode* pRoot=new TreeNode(pre[0]);
        //构造根结点
        int flag=0;
        for(int i=0;i<vin.size();++i)
        {
            if(vin[i]==pre[0])
            {
                flag=i;
                break;
            }
        }
        vector<int> preleft,preright,vinleft,vinright;
        for(int i=0;i<flag;++i)
        {
            preleft.push_back(pre[i+1]);
            vinleft.push_back(vin[i]);
        }
        for(int i=flag+1;i<vin.size();++i)
        {
            preright.push_back(pre[i]);
            vinright.push_back(vin[i]);
        }
        pRoot->left=reConstructBinaryTree(preleft, vinleft);
        pRoot->right=reConstructBinaryTree(preright,vinright);
        return pRoot;
    }
};

知道中序序列(4 7 2 1 5 3 8 6)和后序序列(7 4 2 5 8 6 3 1),重建二叉树

/**
 * Definition for binary tree
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    TreeNode* reConstructBinaryTree(vector<int> vin,vector<int> post) {
    	int size = vin.size();
		if (size == 0)
			return nullptr;
		TreeNode* pRoot = new TreeNode(post[size - 1]);
	    int flag = 0;
	    for (int i = 0; i < size; ++i)
	    {
	   		if (vin[i] == post[size - 1])
			{
				flag = i;
				break;
			}
		}
		vector<int> vinleft, vinright, postleft, postright;
		for (int i = 0; i < flag; ++i)
		{
			vinleft.push_back(vin[i]);
			postleft.push_back(post[i]);
		}
		for (int i = flag + 1; i < size; ++i)
		{
			vinright.push_back(vin[i]);
			postright.push_back(post[i - 1]);
		}
		pRoot->_pLeft = reConstructBinaryTree(vinleft, postleft);
		pRoot->_pRight = reConstructBinaryTree(vinright, postright);
		return pRoot;
    }
};
5、用两个栈实现队列

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

栈的特性是先进后出,而队列的特性是先进先出,入队列也就是将元素压入栈1,而出栈由于两者特性不同,因此要将栈1中的元素取出压入栈2,这样就调换了顺序,就可以像队列一样pop了。

class Solution
{
public:
    void push(int node) {
        stack1.push(node);
    }

    int pop() {
        if(stack2.empty())//stack2为空,将stack1栈顶取出压入stack2
        {
            int len=stack1.size();
            for(int i=0;i<len;++i)
            {
                int t = stack1.top();
                stack2.push(t);
                stack1.pop();
            }
        }
        //stack2不为空,出栈
        int v=stack2.top();
        stack2.pop();
        return v;
    }

private:
    stack<int> stack1;
    stack<int> stack2;
};
6、旋转数组的最小数字

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

我们此时要考虑如果这个数组整体每个元素都旋转了一遍,例如1 2 3 4 5这5个元素都旋转,其实还是和原来一样,因此这种第一个元素小于最后一个元素的情况和只有一个元素的情况,就可以直接返回第一个元素,剩下的情况要考虑使用二分法来解决,例如3 4 5 1 2 这种情况,第一个元素小于mid,说明更小的一定在mid后面,又例如5 1 2 3 4这种情况,第一个元素大于mid,说明更小的要不是mid,要不是mid前面的。

class Solution {
public:
    int minNumberInRotateArray(vector<int> rotateArray) {
        if(rotateArray.size()==0)
            return 0;
        if(rotateArray.size() == 1 || rotateArray[0] < rotateArray.back())
            //如果数组只有一个元素或者数组没有旋转,即1 2 3 4 5这种情况
            return rotateArray[0];
        int left=0;
        int right=rotateArray.size();
        while(left<right)
        {
            int mid=left+((right-left)>>1);
            if(rotateArray[0] <= rotateArray[mid])//3 4 5 1 2这种情况
                left=mid+1;
            else //5 1 2 3 4这种情况
                right=mid;
        }
        return rotateArray[left];     
    }
};
7、斐波那契数列

这是经典问题了,我们可以采用动态规划的方法,也就是当前位置的值等于前两者的值,使用循环即可,也可以使用递归的方法。
动态规划:

class Solution {
public:
    int Fibonacci(int n) {
        if(n<=0)
            return 0;
        if(n==1 || n==2)
            return 1;
        int *arr = new int[n+1];
        arr[0]=0;
        arr[1]=1;
        for(int i=2;i<=n;++i)
        {
            arr[i]=arr[i-1]+arr[i-2];
        }
        return arr[n];
        delete[]arr;
    }
};

递归:

class Solution {
public:
    int Fibonacci(int n) {
        if(n<=0)
            return 0;
        if(n==1 || n==2)
            return 1;
        return Fibonacci(n-1)+Fibonacci(n-2);

    }
};
8、跳台阶问题

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

这个也是斐波那契数列问题,当为1阶台阶时,有1种跳法,为2阶台阶时,有2种跳法,为3阶台阶时,有3种跳法,为4阶台阶时,有5种跳法,等等以此类推可得。

class Solution {
public:
    int jumpFloor(int number) {
        if(number==0)
            return 0;
        if(number==1)
            return 1;
        if(number==2)
            return 2;
        int *arr=new int[number+1];
        arr[0]=0;
        arr[1]=1;
        arr[2]=2;
        for(int i=3;i<=number;++i)
        {
            arr[i]=arr[i-1]+arr[i-2];
        }
        return arr[number];
        delete[]arr;
    }
};
9、变态跳台阶

一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。

我们的状态是F(n),此时我们分析情况:
n=0时,跳法为0;
n=1时,跳法为1;
n=2时,跳法为2;
n>2时,就要分情况讨论,即:
(1)第一次跳一级时,则跳法为F(n-1);
(2)第一次跳二级时,则跳法为F(n-2);
(3)第一次跳三级时,则跳法为F(n-3);
以此类推,第一次跳n级时,则跳法为F(n-n)=F(0),则一共有F(n)=F(n-1)+F(n-2)+F(n-3)+…+F(0)种跳法,F(n-1)=F(n-2)+F(n-3)+…+F(0),此时F(n)-F(n-1)=F(n-1),即F(n)=2*F(n-1),也就是一个等比数列,此时代码可以是:

class Solution {
public:
    int jumpFloorII(int number) {
        if(number<=0)
            return 0;
        int ret=1;
        for(int i=1;i<number;++i)
        {
            ret*=2;
        }
        return ret;
    }
};

也可以为:

class Solution {
public:
    int jumpFloorII(int number) {
        if(number<=0)
            return 0;
        return 1<<(number-1);
    }
};
10、矩形覆盖

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

这也是一个斐波那契数列问题,比如此时我们使用8个2 * 1的小矩形去覆盖一个2 * 8的大矩形,此时我们的状态为F(8),如图:
在这里插入图片描述
此时在覆盖大矩形的左边时有两种选择,横着放和竖着放,如果横着放,那么左边要放两个小矩形,剩下的为2 * 6,即F(6),而如果竖着放,左边放一个小矩形,剩下的为2 * 7,即F(7),则F(8)=F(7)+F(6),即还是斐波那契数列;
实现代码:

class Solution {
public:
    int rectCover(int number) {
        if(number==0)
            return 0;
        if(number==1 || number==2)
            return number;
        return rectCover(number-1)+rectCover(number-2);
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值