什么是用于项目重新排列的良好算法和数据结构?

时间:2019-10-23 11:38:57

标签: javascript arrays algorithm data-structures

比方说,我有一些物品,每个物品都有一个序列-较少的序列表示物品在上面。一个项目可以对其他项目具有依赖性。同样,一个项目可以具有可信赖的对象-即其他一些项目可能依赖于它。下面的项目列表(我在这里使用了关联的数组)列出了项目-每个项目的属性“ dep”列出了依赖项和可靠项。


var dependencyDict = {
  item1: {dependsOn: [], dependable: ['item3']},
  item2: {dependsOn: [], dependable: []},
  item3: {dependsOn: ['item1'], dependable: ['item4']},
  item4: {dependsOn: ['item3'], dependable: []}
}

var itemList  = {
  item1: {
    name: 'item1',
    seq: 1,
    dep: dependencyDict['item1']
  },
  item2: {
    name: 'item2',
    seq: 2,
    dep: dependencyDict['item2']
  },
  item3: {
    name: 'item3',
    seq: 3,
    dep: dependencyDict['item3']
  },
  item4: {
    name: 'item4',
    seq: 4,
    dep: dependencyDict['item4']
  }
}

根据上述内容,项目按其顺序依次为:

item1
item2
item3
item4

我的目标是对项目进行重新排序,即更改顺序(如果可能的话),以使依存关系保持完整。

验证:只有在依存关系保持不变的情况下,项目才能移动:即

  • 一个项目只能依赖于其上方的项目,即其顺序小于该项目的顺序 反之亦然

  • 一个项目只有当项目低于项目时,即项目的顺序大于项目的顺序,才可以将项目作为从属项

例如,如果我说move(item3,2)-我要将item3移至位置2,以便新项目列表应为:

{
  item1: {
    name: 'item1',
    seq: 1,
    dep: dependencyDict['item1']
  },
  item2: {
    name: 'item2',
    seq: 3,
    dep: dependencyDict['item2']
  },
  item3: {
    name: 'item3',
    seq: 2,
    dep: dependencyDict['item3']
  },
  item4: {
    name: 'item4',
    seq: 4,
    dep: dependencyDict['item4']
}

请注意序列的更改

但是,如果我说move(item3,1),它将不会,因为item3依赖于item1-如果它移至位置1,则item1将移至位置2,这使该项只能依赖于项的规则无效在它上面。

我的代码处于正常工作状态,但是我使用的if-elses比合适的算法还要多

灵活性:项目列表可以放在任何数据结构中,可以使用任何算法

2 个答案:

答案 0 :(得分:1)

一些建议:

  • 如果您的数据很大且具有许多依赖性,则使用Set来注册依赖性而不是数组可能会更有效。
  • 删除dependsOndependable中的一个,因为一个可以从另一个中派生
  • 不存储seq,而仅依赖项在数组中的索引。确实,这意味着您必须使用indexOf扫描阵列,但是另一方面,您不必对移动中涉及的所有seq属性进行更新。
  • 使用从零开始的索引,而不是从1开始的位置。

这是我建议的数据结构的class实现。

class OrderedGraph {
    constructor(pairs) {
        this._dep = new Map;
        this._order = [];
        if (pairs) for (let [item, dependsOn] of pairs) this.add(item, dependsOn);
    }
    add(item, dependsOn=[]) {
        for (let ref of dependsOn) if (!this._dep.has(ref)) throw ref + " not found";
        this._dep.set(item, new Set(dependsOn));
        this._order.push(item);
    }
    move(item, toIdx) {
        let fromIdx = typeof item === "number" ? item : this._order.indexOf(item);
        if (fromIdx < 0) throw "not found: " + item
        if (typeof item === "number") item = this._order[item];
        let dep = this._dep.get(item);
        let ok = fromIdx > toIdx
            ? !this._order.slice(toIdx, fromIdx).some(it => dep.has(it))
            : !this._order.slice(fromIdx+1, toIdx+1).some(it => this._dep.get(it).has(item));
        if (ok) this._order.splice(toIdx, 0, ...this._order.splice(fromIdx, 1));
        return ok;
    }
    indexOf(item)  { return this._order.indexOf(item) }
    includes(item) { return this._dep.has(item) }
    * values()     { yield * this._order }
    [Symbol.iterator]() { return this.values() }
}

// Example use
let data = new OrderedGraph([
    ["item1", []],
    ["item2", []],
    ["item3", ["item1"]],
    ["item4", ["item3"]]
]);

// Some actions on the data object:
console.log(JSON.stringify(Array.from(data)));
console.log("success moving 'item3' to 0? ", data.move("item3", 0));
console.log(JSON.stringify(Array.from(data)));
console.log("success moving 'item3' to 1? ", data.move("item3", 1));
console.log(JSON.stringify(Array.from(data)));
console.log("success moving 'item3' to 1? ", data.move("item3", 1));
console.log(JSON.stringify(Array.from(data)));
console.log("success moving 'item3' to 2? ", data.move("item3", 2));
console.log(JSON.stringify(Array.from(data)));
console.log("index of 'item3': ", data.indexOf("item3"));

答案 1 :(得分:0)

您可以使用所需的新节点列表的索引来检查依赖项。

function move(node, position) {
    var nodes = Object.keys(itemList).sort(({ seq: a }, { seq: b }) => a - b);

    nodes.splice(position - 1, 0, ...nodes.splice(itemList[node].seq - 1, 1));

    var valid = nodes.every((n, i) =>
            dependencyDict[n].dependsOn.every(m => nodes.indexOf(m) <= i) &&
            dependencyDict[n].dependable.every(m => nodes.indexOf(m) >= i)
        );

    console.log(valid);
    if (valid) nodes.forEach((node, i) => itemList[node].seq = i + 1);
    console.log(nodes);
    console.log(itemList);
}

var dependencyDict = {
        item1: {dependsOn: [], dependable: ['item3']},
        item2: {dependsOn: [], dependable: []},
        item3: {dependsOn: ['item1'], dependable: ['item4']},
        item4: {dependsOn: ['item4'], dependable: []}
    },
    itemList  = {
        item1: { name: 'item1', seq: 1, dep: dependencyDict['item1'] },
        item2: { name: 'item2', seq: 2, dep: dependencyDict['item2'] },
        item3: { name: 'item3', seq: 3, dep: dependencyDict['item3'] },
        item4: { name: 'item4', seq: 4, dep: dependencyDict['item4'] }
    };

move('item3', 1);
move('item3', 2);
.as-console-wrapper { max-height: 100% !important; top: 0; }