前几天把华为的简单编程题做好了,然后看见是十六题不是那样简单的字符串问题了,所以决定先练习一点关于算法的题目,然后就开始写剑指offer的编程题,下面简单的总结一下前十题。
(发现上次写的博客有牛友给我点赞了,哈哈哈哈哈哈哈哈,好高兴啊!决定从今天(2020.3.3)开始每三天都写一篇博客,记录一下菜鸟的成长!谢谢大家)

一:二维数组中的查找
1:题目
在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
2:思路
实际上这个二维数组从左到右递增,从上到下递增。不妨设有两个指针(java中无指针,用下标表示,道理相似)一个指向最后一列,一个指向第一行。判断交叉点处的值,若大于目标值,则列指针左移(该列最小的值都大于目标值,故寻找的值肯定都不在那一列),同理,若小于目标值,行指针下移(该行最大的值都小于目标值,故排除该行,行指针下移),直到二者移动到左下角为止,如此可快速找到目标值。
3:代码(java)

public class Solution {
    public boolean Find(int target, int [][] array) {
      int len=array.length-1;
      int i=0;
        while(len>=0&&i<array[0].length)
        {
            if(array[len][i]>target)
            {
                len--;//列指针左移
            }
            else if(array[len][i]<target)
            {
                i++;//行指针下移
            }
            else
            {
                return true;
            }
        }
        return false;
    }
}

二:替换空格
1:题目
请实现一个函数,将一个字符串中的每个空格替换成“%20”。例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。
2:思路
暴力解法(即从前往后遍历,遇到空格,后面的数都要后移)复杂的度n的平方,故不妥:
考虑从后往前,先一遍遍历,获得其空格的数量,记为count,可知空格占一个位置,"%20"占三个位置,所以每次出现一个空格,拓展后的字符串要多两个位置,所以替换后的字符比原字符多(2count)个位置,所以从后往前遍历时,在碰到第一个空格之前,只需要把字符串后移(2count)个位置,遇到第一个空格时,只需要将空格(2count,2count-1,2count-2)替换为"02%",剩下的视为子串,注意到此时count-1即可。
3:代码(C++)

class Solution {
public:
    void replaceSpace(char *str,int length) {
        if(str==NULL||length==0)
        {
                   return;
        }
        int count=0;
        for(int i=0;i<length;i++)//从后往前
        {
            if(str[i]==' ')
            {
                count++;
            }
        }
        for(int i=length-1;i>=0;i--)
        {
            if(str[i]!=' ')//非空格直接后移
            {
                str[i+count*2]=str[i];
            }
            else//是空格替换
            {

                str[i+2*count]='0';
                str[i+2*count-1]='2';
                str[i+2*count-2]='%';
                count--;//计数值减一
            }
        }

    }
};

三:从尾到头打印链表
1:题目
输入一个链表,按链表从尾到头的顺序返回一个ArrayList。
2:思路
利用栈,先将链表中的值全部放入栈里面,然后再将栈中内容全部输出到容器,即可得到倒序。具体操作见注释,我觉得这题就是让我们熟悉容器,栈的操作。
3:代码(C++)

/**
*  struct ListNode {
*        int val;
*        struct ListNode *next;
*        ListNode(int x) :
*              val(x), next(NULL) {
*        }
*  };
*/
#include<vector>
class Solution {
public:
    vector<int> printListFromTailToHead(ListNode* head) {
        vector<int> value;//返回的是一个容器
        ListNode*p=head;//取表头
        stack<int> stk;//初始化栈
        while(p!=NULL)//全部入栈
        {
            stk.push(p->val);
            p=p->next;
        }
        while(!stk.empty())
        {
            value.push_back(stk.top());//取栈顶元素入容器里
            stk.pop();

        }
        return value;


    }
};

(决定剑指offer的后面题目都用java写,这样看上去代码简洁,易体现算法思想)
四:重建二叉树
1:题目:
输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。
2:思路
树的题目一般用递归。根据前序遍历,我们知道根节点是1,又根据中序遍历,我们知道左子树是{4,7,2},右子树是{5,3,8,6},反过来根据前序遍历{2,4,7},我们又知道了左子树的根节点是2,而左子树只有左子树的左子树{4,7},依次递归下去即可。
3:代码(此代码是一位大佬牛友写的,简洁易懂,在此引用一下)

public class Solution {
    public TreeNode reConstructBinaryTree(int [] pre,int [] in) {
        TreeNode root=reConstructBinaryTree(pre,0,pre.length-1,in,0,in.length-1);//重写了一下函数,加上了参数数组下标
        return root;
    }

    private TreeNode reConstructBinaryTree(int [] pre,int startPre,int endPre,int [] in,int startIn,int endIn) {

        if(startPre>endPre||startIn>endIn)//数组为空,返回
            return null;
        TreeNode root=new TreeNode(pre[startPre]);/**从前序遍历数组中的第一个值作为跟节点,通过分析,我们知道了左子树元素和右子树元素,然后调用自己分别再将左子树排序右子树排序**/

        for(int i=startIn;i<=endIn;i++)
        { if(in[i]==pre[startPre]){
                root.left=reConstructBinaryTree(pre,startPre+1,startPre+i-startIn,in,startIn,i-1);
                root.right=reConstructBinaryTree(pre,i-startIn+startPre+1,endPre,in,i+1,endIn);
                      break;
            }
        }      
        return root;
    }
}

五:用两个栈实现队列
1:题目
用两个栈来实现一个队列,完成队列的Push和Pop操作。 队列中的元素为int类型。
2:思路
队列的性质是怎样输入某序列,就怎样输出某序列,而栈先进后出,将其逆序,但是有两个栈,倒序的倒序就是正序,正好符合队列的性质。(注意到java中的取栈顶元素函数为peek()而不是C++中的pop())
3:代码

import java.util.Stack;

public class Solution {
    Stack<Integer> stack1 = new Stack<Integer>();
    Stack<Integer> stack2 = new Stack<Integer>();

    public void push(int node)
    {
        stack1.push(node);//进队将其保存在栈1中,如果全部进栈,此时已经逆序了

    }

    public int pop()//逆序的逆序
    {
        int s=0;
        while(stack2.isEmpty())//栈2为空
        {
            while(!stack1.isEmpty())//栈1非空
            {
                s=stack1.peek();
                stack2.push(s);
                stack1.pop();
            }
        }
        s=stack2.peek();
        stack2.pop();
        return s;

    }
}

六:旋转数组的最小数字
1:题目
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。
输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素。
例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。
2:思路
有人提倡用二分法,可将复杂度降低,但是考虑情况会变得复杂。在此一次遍历,算法较简单。
譬如:3 4 5 1 2
从前往后遍历,遇到5比1大,故最小值就是1
譬如3 3 3 3 3
找不到比后面元素大的,此时最小值就是第一个元素
3:代码

import java.util.ArrayList;
public class Solution {
    public int minNumberInRotateArray(int [] array) {
    if(array.length==0)
    {
        return 0;
    }
    if(array.length==1)
    {
        return array[0];
    }
    for(int i=0;i<array.length;i++)
    {
        if(array[i]>array[i+1])
        {
            return array[i+1];
        }
        else if(i==array.length-2)
        {
            return array[0];
        }

    }
        return 0;
}
}

七:斐波拉契数列(注意此题非常重要,相当于母体对后面的编程也十分重要)
1:题目
大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项(从0开始,第0项为0)。
n<=39
2:思路
第一种:递归
如果用递归,最好是先写出递归表达式:
n==0,f(n)=0
n==1,f(n)=1
n>=2,f(n)=f(n-1)+f(n-2)
如果用递归也行,思路非常明确,参考代码如下:

if(n<=1) return n;
else return Fibonacci(n-1)+Fibonacci(n-2);

3:代码(此代码用的迭代法)

public class Solution {
    public int Fibonacci(int n) {
        if(n==0)
        {
           return 0;
        }
        if(n==1)
        {
            return 1;
        }
        int a=0;
        int b=1;
        int c=1;
        for(int i=0;i<n-1;i++)
        {
            c=a+b;
            b=c;
            a=b-a;

        }
        return c;
    }
}

八:跳台阶
1:题目
一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法(先后次序不同算不同的结果)。
2:思路
很显然,一级台阶,1个跳法;
二级台阶,2个跳法
三级台阶呢?
当青蛙走三级台阶时,第一步,不是跳一级,就是跳两级;当他跳一级时,还剩两级,前面有结果了,当他跳两级时,还剩一步,前面也有结果。
综上:f(3)=f(2)+f(1)
同理:f(4)=f(3)+f(2)
故写出递归表达式
n==1,f(1)=1;
n==2,f(2)=2;
n>=3,f(n)=f(n-1)+f(n-2)
3:代码

public class Solution {
    public int JumpFloor(int target) {
          if(target<1)
          {
              return 0;
          }
          else if(target==1)
          {
              return 1;
          }
          else if(target==2)
          {
              return 2;
          }
          else
          {
              return JumpFloor(target-1)+JumpFloor(target-2);
          }
    }
}

九:变态跳台阶
1:题目
一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。
2:思路
模仿第八题我们可以得出以下递归式
f(n)=f(n-1)+f(n-2)+f(n-3)+...+f(1)
又因为
f(n-1)=f(n-2)+f(n-3)+...+f(1)
所以
f(n)=f(n-1)+f(n-1)=2*f(n-1)
当然我们要先把f(1)和f(2)求出来,口算得知f(2)=2,f(1)=1
3:代码

public class Solution {
    public int JumpFloorII(int target) {
        if(target<1)
        {
            return 0;
        }
        else if(target==1)
        {
            return 1;
        }
        else if(target==2)
        {
            return 2;
        }
        else
        {
            return 2*JumpFloorII(target-1);
        }

    }
}

十:矩形覆盖
1:题目
我们可以用21的小矩形横着或者竖着去覆盖更大的矩形。请问用n个21的小矩形无重叠地覆盖一个2n的大矩形,总共有多少种方法?
2:思路
首先当n足够大时,将其分为两个阶段,模仿跳台阶,首先第一个12的地方是横着放还是竖着放,详见下图:
图片说明
我们容易看出其实这就是一个斐波拉契数列,既然我们得到了递推公式,那么写程序就很简单了
3:代码

public class Solution {
    public int RectCover(int target) {
       if(target<1)
       {
           return 0;
       }
       else if(target==1)
       {
           return 1;
       }  
       else if(target==2)
       {
           return 2;
       }
       else
       {
           return RectCover(target-1)+RectCover(target-2);
       }
    }
}

好的,以上就是剑指offer的前十题,在接下来的三天我会去写华为的编程题,争取三天后更新“关于华为编程的十六到三十题”,朋友们,三天后再见啦!一起加油冲冲冲!
图片说明