我遇到了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。
我不确定在哪里出现故障,或者为什么列表中保留了旧的引用并且不遵循新的“路径”。
任何朝着正确方向的指针都将受到赞赏。
答案 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; }
您可以看到我实际上已经成功插入了新节点 我想去的地方
不,你不知道。如果我们将tmp
和currentPosition
的分配混淆了,那段代码就等于
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;
}
}