算法题摘录三

转载请注明原文地址:http://www.cnblogs.com/ygj0930/p/6429971.htmlhtml

1:判断给定数组是否是二叉搜索树的前/后序遍历序列算法

二叉搜索树的特色是:左子树的值<根结点值<右子树的值。而前/后序遍历序列的首/尾数是根结点,依据根结点能够把数组其他部分划分为左子树、右子树,而后根据左右子树序列又能够递归地肯定父结点并划分子树......若是能递归遍历完整个数组则说明合法,不然非法。数组

下面是我实现的判断后序遍历序列的代码:函数

public boolean isSquenceOfBST(int[] nums,int start,int end){
      if(nums==null){
          return false;
      }
      //序列的end就是当前层的根结点
      int curr_root=end;
      //由根结点划分序列获得左子树的结束位
      int leftEnd=0;
      for (int i = start; i <end; i++) {
        if(nums[i+1]>nums[curr_root]){
            leftEnd=i;
            break;
        }
    }
      //由左子树结束位+1获得右子树开始位
      int rightStart=leftEnd+1;
      
      //若是左子树序列不为空,则递归左子树
      boolean isLeftOfBST=true;
      if(leftEnd>0){
       isLeftOfBST=isSquenceOfBST(nums, start, leftEnd);}
      
      //同理,先判断右子树序列是否存在,是则递归右子树
      //这里要注意右子树是rightStart到end-1!
      boolean isRightOfBST=true;
      if(rightStart<end-1){
           isRightOfBST=isSquenceOfBST(nums, rightStart, end-1);
      }     
      return isLeftOfBST&&isRightOfBST;
  }

 

2:给定一棵二叉树,判断给的序列是否对应二叉树的前/后/中序遍历序列优化

传统解法:先按前/后/中序遍历整个树,把每一个结点的值保存到一个数组中,而后把结果数组与所给数组一一比较便可。这是由树求数组去判数组。编码

递归解法:好比咱们判断后序遍历序列的合法性:咱们从二叉树的根节点开始遍历,curr_root记录当时树的根节点。由curr_root与当前序列最后一个值比较判断当前层序列的合法性;而后由curr_root—>left值划分遍历序列获得左子树序列的最后一位leftEnd,由rightStart=leftEnd+1获得右子树序列的开始下标。而后递归左右子树,比较子树的根结点与所给序列的末尾值比较,最后返回左右子树的遍历结果便可。递归边界是:curr_root的子树为空。或者当前树的根不等于当前序列末位值。spa

 

3:打印二叉树中结点值和为给定值的全部路径指针

 从根节点开始处理每个结点,把结点入栈,并统计当前路径耗散值。若是子节点非空,则递归处理子节点。当前结点为叶结点,则比较路径耗散值与给定和是否相等,是则打印栈中元素(路径),不然不打印;而后回溯:弹出当前结点。code

Vector<MyTreeNode> path=new Vector<MyTreeNode>();//因为打印路径时须要从头打印,因此这里用Vector替代了stack。由于vector能够从头遍历,又包含了栈的抛出后入者的功能
    public void printPath(MyTreeNode currRoot,Vector<MyTreeNode> path,int currSum,int total){
        //判断第一个根结点是否为空树,是则直接返回
        if(currRoot==null){
            return;
        }
        //计算当前结点耗散值
        currSum+=currRoot.val;
        //若是当前结点是叶结点而且耗散值等于所求路径和,则打印这条路径
        if(currSum==total && currRoot.leftNode==null && currRoot.rightNode==null){
            for(int i=0;i<path.size();++i){
                System.out.print(path.get(i).val+",");
            }
        }
        //若是路径不是叶结点,而且左子结点存在,则递归左子结点
        if(currRoot.leftNode!=null){
            printPath(currRoot.leftNode, path, currSum, total);
        }
        //若是右子结点存在,递归右子结点
        if(currRoot.rightNode!=null){
            printPath(currRoot.rightNode, path, currSum, total);
        }
        //回溯:把路径上当前点删除
        path.remove(path.size()-1);
    }
 

4:复杂链表的复制htm

复杂链表是指:每一个结点不只有指向下一结点的指针,还有一个指向任意结点的指针。

传统解法:首先遍历一次原链表,用next指针把新链表创建起来;而后再从头至尾遍历原链表和新链表,把指向任意结点的指针一一赋值给新链表对应结点。复杂度O(n^2)

优化解法:既然一个结点对应一个任意指针,那么咱们在第一次遍历原链表创建新链表时把每一个结点对应的任意指针存到一个map中,而后再遍历新链表为每一个结点经过map.get(当前结点)获取对应的任意指针进行赋值。

5:把一颗二叉搜索树转换成排好序的双向链表,要求只能改变树的指针,不能新建任何结点

由二叉搜索树的中序遍历是递增序列可知,要把BST转换成排好序的双向链表其实就是中序遍历BST并修改指针的过程。

观察可得:若是一个结点n的左右子树非空,那么n的前指针指向左子树排序后序列的尾结点,n的后指针执行右子树排列后序列的头结点。而对于n的左右子树序列的得出,毫无疑问就是递归。

6:求一个字符串全部字符的全排列

首先,求出第一个字符的全部可能状况:其实就是把字符串的第一个字符依次和后面的字符交换;

而后,在第一个字符肯定的状况下,把第二个字符依次和后面的字符交换;其实就是递归:把从第二个字符起的字符串传进递归函数进行处理;

......直到递归到了每种状况下的字符串的最后一个字符,结束递归,输出此时的字符串。回溯:把当前步的交换复位。

public void Permutation(char[] chars,int begin){
        if(chars==null){
            return;
        }
        //若是以及递归到最后一个字符了,则获得一个排列状况,进行输出
        if(begin==chars.length-1){
            System.out.println(chars);
        }else{//不然,与后面的字符逐个交换,并在交换后修改begin位置进行递归
            for (int i = begin; i < chars.length; i++) {
                char temp=chars[i];
                chars[i]=chars[begin];
                chars[begin]=temp;
                Permutation(chars, begin+1);
                //回溯
                temp=chars[i];
                chars[i]=chars[begin];
                chars[begin]=temp;
            }
        }        
    }

7:找出数组中出现次数超过数组长度一半的元素

map解法:这种映射关系的题,第一时间想到map——遍历整个数组,把数字与其出现次数关联起来。而后把map中value值最大的key输出便可。

优化解法:咱们能够设定一个活动开关:每当当前数与保存数相同,则开关值+1;不然开关值-1;检查开关值,若是开关值为0,则把当前数设置为保存数,并把开关值置1,统计新保存数的出现状况。因为出现次数超过数组一半,那么遍历完后开关值大于等于1时对应的保存数就是数组中出现次数大于一半的元素。

8:输入一个数组,找出最小的K个数

传统解法:排序,输出前K个数。复杂度O(nlogn)

优化解法:最大堆的使用。首先用输入的数组的前K个数创建其一个有K个结点的最大堆。而后每输入一个数,先与堆头比较,若小于堆头,则把堆头的值替换成新输入的值,而后对堆头进行下沉操做使结点处于合适位置从而更新堆头最大值。在数组输入完后,获得的K结点最大堆就是数组最小的K个数。

9:求连续子数组最大和

这种问题的解与先后步状况相关的问题,用动态规划来作:f(i)=Max(nums[i],f(i-1)+nums[i]),f(i)表示以第i个数字结尾的子数组最大和。

虽然动态规划经常使用递归的形式去描述,可是最终咱们都会用循环来编码。

public int Max_Son(int[] nums,int n){
        //用一个结果数组存放以i结尾的子数组的最大连续和
        int[] res=new int[n];
        //初始化
        res[0]=nums[0];
        //用currMax保存最大连续和数组中的最大值
        int currMax=res[0];
        //用循环求以i结尾的最大连续和
        for(int i=1;i<n;++i){
            if(res[i-1]<=0){
                res[i]=nums[i];
            }else{
                res[i]=res[i-1]+nums[i];
            }
            //更新最大连续和数组中的最大值
            currMax=currMax>res[i]?currMax:res[i];
        }
        //返回最大的连续和
        return currMax;
    }

10:从1数到n,求数字1出现的次数(在任意位上)

传统解法:定义一个函数专门用于统计一个数n的各位上1的个数。而后循环遍历 i:1~n ,count+=SumOfOne(i),最后count就是所求。缺点是容易超时。

优化:找规律来求。

相关文章
相关标签/搜索