把一个规模为n的问题分解为k个规模较小的子问题,这些子问题相互独立且与原问题相同,递归的解这些子问题,而后把各个子问题的解合并获得原问题的解。算法
使用快速排序方法排列一个一维数组。数组
对于输入的子数组a[p:r],按照一下3个步骤进行排序:
1)分解divide:以a[p]为基准元素将a[p:r]划分红3段a[p:q-1],a[q]和a[q+1:r],其中a[q]不小于a[p:q-1]中的任何元素且不大于a[q+1:r]中的任何元素,下标q在划分中肯定。
2)递归求解conquer:经过递归调用排序,分别对a[p:q-1]和a[q+1:r]进行排序。
3)合并merge:合并a[p:q-1],a[q]和a[q+1:r]返回为最终结果。ide
var __array__ = [1, 3, 2, 4, 5, 57, 6, 46, 4, 6, 45]; console.log("排序前:" + __array__); function sort(x, y) { if (x < y) { var p = partition(x, y); sort(x, p - 1); sort(p + 1, y); } } function partition(p, q) { var x = p; var y = q; var r = p; var flag = __array__[r]; while (true) { while (__array__[x] <= flag && x < y) { x++; } while (__array__[y] > flag && y > x) { y--; } if (x >= y) { break; } var temp = __array__[x]; __array__[x] = __array__[y]; __array__[y] = temp; } if (__array__[x] > flag) { x--; } __array__[p] = __array__[x]; __array__[x] = flag; return x; } sort(0, __array__.length - 1); console.log("排序后:" + __array__);
和分治法基本思想有共同的地方,不一样的是子问题每每不是独立的,有事母问题要借助子问题的解来判断,所以把已经计算好的问题记录在表格中,后续若是须要查询一下,能够避免重复计算,这是动态规划的基本思想。设计
不过动态规划具体实现起来多种多样,不过都具备相同的填表格式,一般按照下面步骤设计算法:code
1)找出最优解的性质,并刻画其结构特征;排序
2)递归的定义最优值;递归
3)以自底向上的方式计算出最优值;队列
4)经过计算最优值时刻意记录的判断结果来构造最优解。it
可使用该算法思想设计算法的问题通常会具备二个决定性的性质:io
1)最优子结构性质;
2)子问题重叠性质。
和上面的算法思想差很少,不一样的是备忘录为每一个解过的子问题创建备忘录以备须要的时候查看,避免了相同的问题计算屡次。
通常来讲,当一个问题的全部子问题都至少要解一次时,用动态规划比备忘录要好,由于不会有任务暂存且没有多余的计算;当子问题空间中部分问题没必要解时,用备忘录比较好。
不过上面不是绝对的,这样说只是想区别一下二个思想的不一样,具体的时候仍是要根据业务场景来在保证可行的前提下选择更好的方法。
给定n个矩形{A1,A2,...,An},其中Ai与Ai+1是可乘的,因为矩阵知足结合律,不一样的加括号方法计算次数不同,求最优的加括号方法。
分别计算有1,2,3,...,n个矩阵的最优解,计算i个时候,所有的i-1的最优解已经记录下来了,保证计算不重复。
/** * 初始化数据 */ var P = [30, 35, 15, 5, 10, 20, 25]; //记录了矩阵的大小 var num = P.length - 1; //矩阵个数 var minNum = []; var i, j; //全局复杂循环变量 /** * 初始化数据 */ for (i = 0; i < num; i++) { minNum[i] = []; for (j = 0; j < num; j++) { if (i == j) { minNum[i][j] = 0; } else { minNum[i][j] = "#"; } } } /** * 计算最优并记录下来 */ for(i=2;i<=num;i++){//计算的矩阵个数,从二个开始到所有的状况 for(j=1;j<=num+1-i;j++){//计算矩阵第j到第i+j-1个的状况 //先初始化认为在第j分割是最优的(在第j分割的意思是j单独一个,j+1->i+j-1是一组) var splitIndex=j; var splitMin=minNum[j][i+j-2]+P[j-1]*P[j]*P[i+j-1]; minNum[j-1][i+j-2]=splitMin; for(splitIndex=j+1;splitIndex<=i+j-2;splitIndex++){ splitMin=minNum[j-1][splitIndex-1]+minNum[splitIndex][i+j-2]+P[j-1]*P[splitIndex]*P[i+j-1]; if(splitMin<minNum[j-1][i+j-2]){ minNum[j-1][i+j-2]=splitMin; } } } } console.log("最优次数:"); console.log(minNum);
算法思想很简单,和字面意思同样,每次都选择对本身最有利的,不过这是有条件的,只有在知足条件下每次选择最有利本身的才能够获取最优解。
贪心选择性质和最优子结构性质是该思想最重要的性质:
1)贪心选择性质:所求问题的总体最优解能够经过一系列局部最优的选择达到。
2)最优子结构性质:当一个问题的最优解包含其子问题的最优解时,称此问题具备此性质。
有一批集装箱要装上一艘载重为c的轮船,其中集装箱i的重量为wi,要求在装货体积不受限制的条件下尽力多装集装箱的解。
先排序,而后选择从最轻的开始装货物。
这里就不提供具体代码了,由于感受没有什么意义,最重要的是要先肯定问题知足贪心选择性质,这样在不少时候,能够更容易的解决问题,这点很重要。
说的直白点就是深度优先方式系统搜索问题的算法。
有一批共n个集装箱要装上两艘载重方别为c1和c2的轮船上,其中集装箱i的重量为wi,且所有集装箱重量不大于两艘载重之和,问是否有一个装载方案完成装载。
对第一艘船,构造一个0/1树,0表明不选择,1表明选择,而后分别去从根节点试图爬到叶节点,去一一记录下来可行的,选择最小的为解,余下的判断第二艘船是否装的下便可。
var weight1 = 30; //第一艘船载重 var weight2 = 10; //第二艘船载重 var w = [1, 9, 9, 4, 4, 9]; //集装箱 var nowW1 = 0; //当前载重 var nowBest1 = 0; //当前最优装载 var n = w.length; //集装箱个数 function Loading(deep) { if (deep > n) { //若是到达根 if (nowW1 > nowBest1) nowBest1 = nowW1; return; } if (nowW1 + w[deep - 1] <= weight1) { //若是1分支能够 nowW1 += w[deep - 1]; Loading(deep + 1); nowW1 -= w[deep - 1]; } //0分支 Loading(deep + 1); } function main() { Loading(1); var firstLoad = nowBest1; var all = 0; for (var i = 0; i < n; i++) { all += w[i]; } console.log("第一艘载重:" + firstLoad + "n"); if (all > weight2 + firstLoad) { console.log("失败n"); } else { console.log("成功n"); } } main();
对比回溯法就很容易思考,用广度优先的办法,不断扩大当前节点的孩子为当前节点,主要是求解一个最优解,算法相比回溯法要简单些。
有一批共n个集装箱要装上两艘载重方别为c1和c2的轮船上,其中集装箱i的重量为wi,且所有集装箱重量不大于两艘载重之和,问是否有一个装载方案完成装载。
借助队列,一层层来检查,找到最优解。
var weight1 = 30; //第一艘船载重 var weight2 = 10; //第二艘船载重 var w = [1, 9, 9, 4, 4, 9]; //集装箱 var nowBest1 = 0; //当前最优装载 var n = w.length; //集装箱个数 var arrayFIFO = []; arrayFIFO.push([1, 1]); //deep,此时已经载重 arrayFIFO.push([1, 0]); var nowBest1 = 1; while (arrayFIFO.length > 0) { var nowNode= arrayFIFO.shift(); currentDeep = nowNode[0]; currentWeight = nowNode[1]; if (currentDeep >= n) { if (currentWeight > nowBest1) { nowBest1 = currentWeight; } } else { arrayFIFO.push([currentDeep + 1, currentWeight]); if (currentWeight + w[currentDeep] < weight1) { arrayFIFO.push([currentDeep + 1, currentWeight + w[currentDeep]]); } } } allW = 0; for (val = 0; val < w.length; val++) { allW += w[val]; } console.log("第一艘船载重:" + nowBest1); if (allW <= nowBest1 + weight2) { console.log("成功"); } else { console.log("失败"); }