JS手撸数据结构系列(三) ——子序列、幂集与递归

这里写图片描述

穷举所有子序列

当时的情况是这样的,本来想用最蠢的方法写LCS(最长公共子序列),穷举A、B的所有子序列,然后循环套循环逐一比较……

不过…..穷举所有子序列…..貌似也不是那么一下就能解决…..

人脑遍历的结果如下所示:一共 24=16 种结果,归纳成公式就是 2str.length 。现在我们需要解决的问题就变成了如何穷举出所有的子序列。

 // 一个字符串,也就是一个序列
 str='abcd';

 subSeq0 = [' ']; // 没有元素,也就是集合为空,这和幂集的定义有关。

 subSeq1 = ['a', 'b', 'c', 'd']; // 一个元素的情况

 subSeq2 = ['ab ', 'bc', 'ac']; // 两个元素的情况

 subSeq3 = ['abc', 'bcd', 'acd']; // 三个元素的情况

 subSeq4 = ['abcd']; // 四个元素 

幂集

通过上面的分析,发现穷举子序列这个问题等价于求一个元素的幂集。

  • 幂集定义:集合A的幂集就是所有A的子集所组成的集合。比如集合{1,2,3},它的幂集B就是{{1},{2},{3},{1,2},{2,3},{1,3},{空集}}

状态二叉树

可以设想这么一个过程,幂集中的每个元素都是一个集合,它或者是空集,或含有集合A中的一个元素,或两个元素…..或者全部n个元素。
另一个角度,从 A 中每个元素来看,它只有两种状态[‘属于幂集元素集’, ‘不属于幂集元素集’],则求幂集所有元素的过程可以看成是依次对集合 A 中的元素进行 ‘取’ 或 ‘舍’ 的过程,并可用下图的二叉树来表示。最下层的所有叶子节点就是所求幂集的所有元素。

二叉树

根据这颗状态树,可以写出如下代码:

var arr = [1, 2, 3];


 function getPowerSet(i, listA) {

    let listB = [];

   function recurse(i, listA) {

        if(i > listA.length - 1){
                //输出当前B值,即幂集里的一个元素
            console.log('set: ' + listB);

        } else {
             var x = listA[i];
             listB.push(x);
             recurse(i+1, listA);
             listB.pop(x);
             recurse(i+1, listA);
        }
    }

  recurse(i, listA);

    // body...
 }

getPowerSet(0, arr)

重构一下下

上面的是数据结构书上的内容,理解起来很别扭。我试着重构了一下下让代码更简单。
把上述的树存在一个数组中

arr[0] = ’ ‘空集
arr[1] = ‘a’, 代表’取a’arr[2] = ’ ‘, 代表’舍a’
arr[3] = ‘ab’, 代表 ‘取b’, arr[4] = ‘a’, 代表 ‘舍b’arr[5] = ‘b’, 代表 ‘取b’,arr[6] = ‘a’, 代表 ‘舍b’

然后我们发现,所有表示舍弃的状态,因为你没有元素在末尾追加新元素,都和以前的重复了。
所以我们只保留 ‘取’的这一种分支。

  • 新算法思路:
    1. 首先有一个空数组list[];
    2. 遍历数组list,在list中每一个元素末尾追加新字符,然后把新生成的元素push到list的末尾。生成新的list
    3. 重复第二步,直到没有新字符需要判断。
let arr = ['a', 'b', 'c', 'd'];
let list = [''];

for(var i = 0, len = arr.length; i<len ; i++ ){
     list.forEach(x => {
            list.push(x + arr[i]);              
     });     
}

console.log(list.sort()); // 排序一下,方便查看

运行结果

powerSet

《 论被自己写的代码美哭是什么感觉 》

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值