JavaScript中的LinkedList,如何将更改附加到列表中?

时间:2018-09-02 20:51:28

标签: javascript linked-list

我遇到了JavaScript中变量的引用问题,并且我一直在努力解决这个问题。

我正准备讲授关于数据结构的课程,并且在至少看了10年之后我正在审查这些材料。

我从理论上知道链表,但是由于某种原因,我一直在努力提出实际上可以在JavaScript中工作的代码(我选择JavaScript是因为我的课程对此最了解)

这是我的代码:

let LinkedList  = {
    head: {},
    tail: {}
};

let Node = {
    data: {},
    next: {}
}

function isObjectEmpty(obj1) {
    return Object.keys(obj1).length === 0 && obj1.constructor === Object;
}

function count(node, counter) {
    if (node.next) {
        return 1 + count(node.next, counter);
    }
    return counter;
}

/**
 * Adds data to LinkedList
 * @param {LinkedList} list
 * @param {Node} data
 */
function add_node(list, data) {
    let temp = Object.assign({}, Node);
    temp.data = data;
    temp.next = {};

    if (Object.keys(list.head).length === 0) {
        list.head = temp;
        list.tail = temp;
    } else {
        list.tail.next = temp;
        list.tail = temp;
    }
    return list;
}
function insert(l, n, position) {
    if (position <= 0) {
        position = 0;
    } else {
        position = position - 1;
    }

    var list = Object.assign({}, l);
    var node = Object.assign({}, Node);
    node.data = n;

    // this only counts elements on the list.
    var elements = count(list.head, 0);

    if (position > elements) {
        return list;
    }

    var currentPosition = list.head;
    var counter = 0;

    while (!isObjectEmpty(currentPosition)) {
        if (position === counter) {
            var tmp = currentPosition;
            currentPosition = node;
            currentPosition.next = tmp.next;
            return list;
        }
        currentPosition = currentPosition.next;
        counter++;
    }

    return list;
}

// how to use the function
let songs = [
{id: '1', name: 'Kamikaze', artist: 'Eminem', releaseDate: '2018-08-31'},
{id: '2', name: 'despacito', artist: 'Luis Fonsi', releaseDate: '2018-08-31'},
{id: '3', name: 'La tortura', artist: 'Shakira', releaseDate: '2018-08-31'},
{id: '4', name: 'Roar', artist: 'Roar', releaseDate: '2018-08-31'},
 ];

let list = Object.assign({}, LinkedList);
    songs.forEach((song) => {
        add_node(list, song); // nothing special, just builds the linkedlist
     });

list = insert(list, {id: '5', name: 'Havana', artist:'who knows', releaseDate:'2018-01-01'}, 3); 
console.log(list); // new object isn't there.

该功能应该在链表中的任意位置插入一个元素。有点用。问题在于返回的列表在重新关联之前保留了对旧对象的引用。

如果在此块中放入调试器:

    if (position === counter) {
        var tmp = currentPosition;
        currentPosition = node;
        currentPosition.next = tmp.next;
        return list;
    }

您会看到我实际上已经成功地将新节点插入到想要的位置。

但是,如果您使用console.log list结构,将会发现找不到新插入的Node。

我不确定在哪里出现故障,或者为什么列表中保留了旧的引用并且不遵循新的“路径”。

任何朝着正确方向的指针都将受到赞赏。

3 个答案:

答案 0 :(得分:1)

问题夫妇。首先,您要替换节点,而不是严格插入。我认为您希望.next成为tmp节点,而不是tmp.next

currentPosition.next = tmp;

第二,您需要在插入点之前保留对节点的引用,以便可以将其next的值设置为新插入的节点:

previousNode.next = node //<- node being the one you're inserting

这就是为什么您在链接列表中看不到差异的原因。

*编辑(将它们放在一起):

var prevNode = null;
while (!isObjectEmpty(currentPosition)) {
  if (position === counter) {
    var tmp = currentPosition;
    currentPosition = node;
    currentPosition.next = tmp;
    if (prevNode === null) list.head = currentPosition;
    else prevNode.next = currentPosition;
    return list;
  }
  prevNode = currentPosition;
  currentPosition = currentPosition.next;
  counter++;
}

为了安全地插入初始位置,我们必须能够设置list.head。否则,我们将设置上一个节点的next属性。

答案 1 :(得分:1)

您可以使用不带Object.assign的版本,并使用由相同命名函数创建的对象作为列表和节点。此功能以后可以用无形的功能代替,以实现更多的OOP样式化方法。

根据功能,此建议在列表的末尾或列表中的任何位置插入一个节点并将其插入。如果列表中没有节点,则将其插入链接列表的开头。

function linkedList() {
    return { head: null, tail: null };
}

function node(data, next) {
    return { data, next };
}

function insertTail(list, node) {
    if (list.head) {
        list.tail.next = node;
        list.tail = list.tail.next;
    } else {
        list.head = node;
        list.tail = node;
    }
    return list;
}

function insertPos(list, node, n) {
    var temp = list.head,
        previous;

    if (!temp) {
        return insertTail(list, node);
    }

    while (temp.next && n--) {
        previous = temp;
        temp = temp.next;
    }

    node.next = temp;
    previous.next = node;

    return list;
}

var songs = [{ id: '1', name: 'Kamikaze', artist: 'Eminem', releaseDate: '2018-08-31' }, { id: '2', name: 'despacito', artist: 'Luis Fonsi', releaseDate: '2018-08-31' }, { id: '3', name: 'La tortura', artist: 'Shakira', releaseDate: '2018-08-31' }, { id: '4', name: 'Roar', artist: 'Roar', releaseDate: '2018-08-31' }],
    list = linkedList();

songs.forEach(song => insertTail(list, node(song)));
console.log(list);

insertPos(list, node({ id: '5', name: 'Havana', artist: 'who knows', releaseDate: '2018-01-01' }), 3);
console.log(list);
.as-console-wrapper { max-height: 100% !important; top: 0; }

答案 2 :(得分:1)

  

如果在此块中放入调试器:

if (position === counter) {
    var tmp = currentPosition;
    currentPosition = node;
    currentPosition.next = tmp.next;
    return list;
}
     

您可以看到我实际上已经成功插入了新节点   我想去的地方

不,你不知道。如果我们将tmpcurrentPosition的分配混淆了,那段代码就等于

if (position === counter) {
    node.next = currentPosition.next;
    return list;
}

所有发生的事情是,您将列表的尾部复制到新节点上,但是您从未真正将节点作为列表中当前节点的next插入。它缺少一个

currentPosition.next = node;

其他几点:

  • 请勿使用isObjectEmpty和空对象来表示“无节点”。请改用null。如果出于某种教学原因,您不想引入null,请在对象上使用布尔值.isNode来区分带有数据的节点与空节点。
  • 避免使用Object.assign。您的用法确实很简单。在

    let temp = Object.assign({}, Node);
    temp.data = data;
    temp.next = {};
    

    您将直接覆盖刚从Node复制的值-更好地简化为使用对象文字:

    let temp = {data, next: {}};
    

    var list = Object.assign({}, l);中,您根本不需要创建新对象。您将要对传入的列表进行变异,因此您应该保留该变量。 (如果要使纯函数具有不可变的数据结构,则还必须使所有节点不可变,并插入克隆整个列表直到所需位置。)

  • 如果您打算使用Object.assign创建新对象,以后可能会涉及其他您不想覆盖的属性(或方法),请改用工厂函数。

  • 不要预先count该列表。单次插入,如果您在要插入的位置之前到达列表的末尾,请return

function makeLinkedList() {
    return { head: null, tail: null };
}

function makeNode(data, next = null) {
    return { data, next };
}

function append(list, node) {
    if (list.head) {
        list.tail.next = node;
    } else {
        list.head = node;
    }
    list.tail = node;
}

function insert(list, data, position) {
    if (position < 0) throw new Error("position must be nonnegative");

    let newNode = makeNode(data);
    let prev = null, cur = list.head;
    while (cur != null && position > 0) {
        prev = cur;
        cur = cur.next;
        position--;
    }
    if (cur == null && position > 0) throw new Error("position must be <= list length")
    if (cur == null) {
        list.tail = newNode;
    } else {
        newNode.next = cur;
    }
    if (prev == null) {
        list.head = newNode;
    } else {
        prev.next = newNode;
    }
}