分治算法的题型以及与动态规划的区别

动态规划属于是分治算法的一种,而不同的是分治算法在大部分情况下是自顶向下进行求解,将问题分解为各个小问题再合并,而动态规划大部分是定义dp数组,求解小问题后进一步自底向上进行求解大问题。

分治算法步骤:

1>分解:将原问题分解为各个小问题。

2>求解:如果可以简单的进行求解则解出来,否则再次进行1>分解。

3>合并:将各个子问题合并为原问题最终解。

动态规划步骤:

1>状态定义2>状态转移方程3>初始状态4>返回值

动态规划的三个特征:1最优子结构(利用子问题最优解推导出原问题最优解)2无后效性(定义状态转移方程之后,只关心它前面的状态是什么,不考虑它是怎么推导出来的,并且后续的决策对当前状态不会有任何的影响)3重复子问题(不同的决策阶段,可能有重复的状态)

剑指 Offer 33. 二叉搜索树的后序遍历序列

题目会输入一个二叉搜索树的后序遍历数组。

根据二叉搜索树的性质,知道左结点一定小于根结点,右结点一定大于根结点。

根据后续遍历的性质,知道数组的最后一个结点必为根结点。

问题分解:

先取出数组最后一个值,得到根结点的值

循环遍历数组,赋值一个变量i,来寻找数组当中第一个大于根结点的值

根据上面得到的下标i,数组分割为两部分:0,i与i,len-1。由于二叉搜索树的性质,前部分必全部小于根结点的值,后部分必大于根结点的值,利用数组的every方法来判断,如果不满足则return false

递归求解各个小问题:

在递归时永远先写回溯条件,在函数第一行写上判断如果数组元素小于等于一则return true进行回溯。

在判断完左右列表满足后,用&&对左右列表进行一个递归,如果全部满足则最终会return true;

/**
 * @param {number[]} postorder
 * @return {boolean}
 */
var verifyPostorder = function(postorder) {
    var len = postorder.length;
    if (len<=1){
        return true;
    }
    var root = postorder[len-1];
    var i = 0;
    while(i<len-1){
        if(postorder[i]<root)i++;
        else break;
    }
    var left = postorder.slice(0,i);
    var right = postorder.slice(i,len-1);
    if (left.every(function(x){return x<root})&&right.every(function(x){return x>root}))return verifyPostorder(left)&&verifyPostorder(right);
    else return false;
};



剑指 Offer 16. 数值的整数次方

该题最简单的办法是利用循环来一下一下的减少n的值并且一直执行x*x。

进阶一点可以利用递归来进行求解,并且加上对n奇数偶数的判断(此处运用位运算来判断,如果n为奇数其与1做和运算则必不为0),如果为偶数则将x*x并且n/2(利用x^4 == (x^2)^2 )

其终止条件是n==0 return1;

/**
 * @param {number} x
 * @param {number} n
 * @return {number}
 */
var myPow = function(x, n) {
    if (n<0)return 1/myPow(x,-n);
    if (n==0) return 1;

    if(n&1)return myPow(x,n-1)*x;//位运算与,末尾为0则不是偶数,为1则是偶数
    else return myPow(x*x,n/2)
};

后来才知道这其实是一种思想:快速幂

其时间复杂度可以实现对数复杂度(logn)相当于二分查找  1/2 ^ k *n = 1

写成循环的方式将空间复杂度降到1:

/**
 * @param {number} x
 * @param {number} n
 * @return {number}
 */
var myPow = function(x, n) {
    if(x==1)return 1;
    var res = 1;
    if(n<0){
        x=1/x;
        n=-n;
    }
    while(n){
        if(n&1)res*=x;
        x*=x;
        n>>>=1;
    }
    return res;
};

剑指 Offer 07. 重建二叉树

分解问题:

利用二叉树前序遍历和中序遍历的规律(根左右,左根右),可以得出该图:

图片.png

 分解问题:

先获取根结点记录为root

/**
 * Definition for a binary tree node.
 * function TreeNode(val) {
 *     this.val = val;
 *     this.left = this.right = null;
 * }
 */
/**
 * @param {number[]} preorder
 * @param {number[]} inorder
 * @return {TreeNode}
 */
var buildTree = function(preorder, inorder) {
    if(!preorder.length||!inorder.length)return null;
    var root = preorder[0];
    var i=0;
    for(i;i<inorder.length;i++){
        if(inorder[i] == root) break;
    }
    var left_in_list = inorder.slice(0,i);
    var right_in_list = inorder.slice(i+1,inorder.length);
    var left_pre_list = preorder.slice(1,i+1);
    var right_pre_list = preorder.slice(i+1,preorder.length);
    var node = new TreeNode(root);
    node.left = buildTree(left_pre_list,left_in_list);
    node.right = buildTree(right_pre_list,right_in_list);
    return node;
};

寻找在中序遍历当中的root值,将两个数组划分为左右两块。

定义var node = new treenode(root);

对node.left 与right进行定义

合并问题:

在函数开始写上递归回溯条件,当数组为空时return null;

在定义node.left 与 right的时候直接传递数组的左右两块

code:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值