如何在双向链表的第一个节点之前插入新节点?

发布于 2024-10-23 13:39:51 字数 3230 浏览 0 评论 0原文

我正在研究如何在双向链表的第一个节点之前插入新节点。我对该操作所需的辅助节点以及执行该操作的步骤序列感到困惑。我将不胜感激有关如何解决此问题的提示,即我的 insertBeforeFirst 方法有什么问题。就目前情况而言,该方法会导致 nullPointerException,我发现很难对其进行故障排除。 (注意:这是之前的 的后续内容帖子。)

public DLL()
{
    header = null ;
    tail = null ;
}

...
DLL myList = new DLL() ;
DLLNode A = new DLLNode("Hello", null, null) ;
DLLNode B = new DLLNode("Hi", null, null) ;
...

myList.addFirst(A) ;
myList.insertBeforeFirst(B)

...
public void addFirst(DLLNode v)
{
    v.pred = header ; 
    v.succ = tail ; 
    header = v ;
    tail = v ;
}
...

public void insertBeforeFirst(DLLNode v)
{
    DLLNode aux = v ;
    aux.succ = header.succ ;
    header = aux ;
    DLLNode aux2 = aux.succ ;
    aux2.pred = v ;
}

[编辑]

我听从了亚伦的建议并做了一张图,我有了一点改进,因为我不再得到 nullPointerException 但新模式是在之后插入的第一个节点而不是之前的节点。所以我认为我的绘画技巧也需要一些改进:)

public void insertBeforeFirst(DLLNode v)
{
    v.succ = header.succ ; // point new node succ to current first node
    header.succ = v ;  //point header to new node
    DLLNode aux = header.succ ; // auxiliary node for backward insertion
    aux.pred = v ; // point auxiliary's pred backward to new node
}

[EDIT2]

看看 MahlerFive 的帖子,我现在明白为什么你们中的一些人可能会对我的标题和尾部谈话感到困惑。这是我从这里得到的:“为了简化编程,在双向链表的两端添加特殊节点很方便:在列表头之前添加一个 header 节点,和列表尾部后面的 trailer 节点。这些“虚拟”节点不存储任何元素” source

因此,对于初学者来说,我似乎需要找到一种方法来正确实现这些虚拟节点,然后才能添加任何内容并进行正确的引用。这些虚拟节点似乎需要不同的节点构造函数?它们可以由 DLL 默认构造函数实例化吗?

[EDIT3]

@MahlerFive,DLL 构造函数将如下所示:

public DLL()
{
    DLLNode Header = new DLLNode(null, null, null) ;
    DLLNode Tail = new DLLNode(null, Header, null) ;
    Header.succ = Tail ;
}

我的方法类似这样,尽管我现在收到 nullPointerException:

// insert z before v
public void addBeforeFirst(DLLNode v, DLLNode z)
{
    DLLNode aux = v.pred ;
    z.pred = aux ;
    z.succ = v ;
    v.pred = z ;
    aux.succ = z ;
}

[EDIT4]

我正在取得进步。 (感觉很棒!)我同意 MahlerFive 的观点,即虚拟标头和尾节点并不是解决此问题的好方法。但正如一本已出版的关于此事的书中提到的那样,它至少值得探索。这是我的新代码(不使用虚拟节点):

...

// DLL Constructor
public DLL()
{
    first = null ;
    last = null ;
}
...
// example insert call
// B is the node in front of which i want to insert
l.insert("Ciao", B) ;
...

public void insert(String elem, DLLNode pred)
{
    // make ins a link to a newly-created node with element elem,
    // predecessor null, and successor null.
    DLLNode ins = new DLLNode(elem, null, null) ;
    // Insert ins at the insertion point in the
    // forward SLL headed by first.
    ins.pred = first ;
    ins.succ = first ;
    // let first be the the new node
    first = ins ;
}

这需要微调,因为我还没有设置任何向后链接,但这是一个很好的起点。为了确保其正常工作(至少以正向方式),我添加了打印语句以在添加节点时打印出第一个和最后一个元素。事实上,它们已正确更新:

Hi
first: hi
last: hi

Ciao Hi
first: Ciao
last: hi

Moin Ciao Hi
first: Moin
last: hi

I am looking at how to insert a new node before the first node of a doubly-linked list. I'm getting confused with the auxiliary nodes required for this operation and the sequence of steps in which to perform the operation. I would be grateful for a hint on how to solve this problem i.e. what is wrong with my insertBeforeFirst method. As it stands the method causes a nullPointerException which i find hard to troubleshoot. (note: this is a follow-on to a previous post.)

public DLL()
{
    header = null ;
    tail = null ;
}

...
DLL myList = new DLL() ;
DLLNode A = new DLLNode("Hello", null, null) ;
DLLNode B = new DLLNode("Hi", null, null) ;
...

myList.addFirst(A) ;
myList.insertBeforeFirst(B)

...
public void addFirst(DLLNode v)
{
    v.pred = header ; 
    v.succ = tail ; 
    header = v ;
    tail = v ;
}
...

public void insertBeforeFirst(DLLNode v)
{
    DLLNode aux = v ;
    aux.succ = header.succ ;
    header = aux ;
    DLLNode aux2 = aux.succ ;
    aux2.pred = v ;
}

[EDIT]

I've followed Aaron's advice and made a drawing and i have a slight improvement in that i don't get a nullPointerException anymore but the new mode is inserted after the first node rather than before. So my drawing skills need some polishing too i think :)

public void insertBeforeFirst(DLLNode v)
{
    v.succ = header.succ ; // point new node succ to current first node
    header.succ = v ;  //point header to new node
    DLLNode aux = header.succ ; // auxiliary node for backward insertion
    aux.pred = v ; // point auxiliary's pred backward to new node
}

[EDIT2]

Looking at the post by MahlerFive I see now why some of you might get confused by my header and tail talk. Here is where i got it from: "To simplify programming, it is convenient to add special nodes at both ends of a doubly linked list: a header node just before the head of the list, and a trailer node just after the tail of the list. These "dummy" nodes do not store any elements" source

So it seems that for a starter i need to find a way to implement these dummy nodes correctly before i can add anything and make correct references. these DUMMY nodes seem to require a different Node constructor? Could they be instantiated by the DLL default constructor?

[EDIT3]

@MahlerFive, the DLL constructor will look like this:

public DLL()
{
    DLLNode Header = new DLLNode(null, null, null) ;
    DLLNode Tail = new DLLNode(null, Header, null) ;
    Header.succ = Tail ;
}

and my method something like this, although i'm getting a nullPointerException at the moment:

// insert z before v
public void addBeforeFirst(DLLNode v, DLLNode z)
{
    DLLNode aux = v.pred ;
    z.pred = aux ;
    z.succ = v ;
    v.pred = z ;
    aux.succ = z ;
}

[EDIT4]

I'm making progress. (great feeling!) I am in agreement with MahlerFive that the DUMMY Header and Tail nodes are not a great way to approach this. But as it was mentioned in a published book on the matter it was worth at least exploring. Here goes my new code (without the use of dummy nodes):

...

// DLL Constructor
public DLL()
{
    first = null ;
    last = null ;
}
...
// example insert call
// B is the node in front of which i want to insert
l.insert("Ciao", B) ;
...

public void insert(String elem, DLLNode pred)
{
    // make ins a link to a newly-created node with element elem,
    // predecessor null, and successor null.
    DLLNode ins = new DLLNode(elem, null, null) ;
    // Insert ins at the insertion point in the
    // forward SLL headed by first.
    ins.pred = first ;
    ins.succ = first ;
    // let first be the the new node
    first = ins ;
}

this needs fine tuning as i haven't set any backwards links yet but it's a great starting point. To make sure this works correctly (at least in the forward way) i added print statements to print out the first and last element as i added nodes. Indeed they were updated correctly:

Hi
first: hi
last: hi

Ciao Hi
first: Ciao
last: hi

Moin Ciao Hi
first: Moin
last: hi

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

眼波传意 2024-10-30 13:39:51

不要在大脑中完成这一切,而是坐下来五分钟,在纸上绘制数据结构(DLL 和 2-3 个节点)。

留下一个间隙,并在其下方画出已插入节点后的外观。

用记号笔标记您需要进行的所有更改。给每个变化一个数字。

现在坐下来实施每一项改变。

这听起来很乏味,但它将帮助您深入了解正在发生的事情。这样,您只需执行一次此操作。

如果您更喜欢纸和剪刀类型,请拿几段绳子,剪掉节点并将便利贴粘在绳子的末端。现在您可以使用字符串作为模型元素之间的“引用”。

Instead of doing this all in your brain, sit down five minutes and draw the data structure (DLL and 2-3 couple of nodes) on paper.

Leave a gap and below that, draw how it would look with the node already inserted.

Mark all the changes you need to make with a marker pen. Give each change a number.

Now sit down and implement each change.

This sounds tedious but it will help you deeply understand what is going on. This way, you will have to do this only once.

If you're more the paper and scissors type, get pieces of string, cut out the nodes and glue post it notes to the end of the strings. Now you can use the strings as "references" between elements of your model.

许一世地老天荒 2024-10-30 13:39:51

看来您的大部分困惑都围绕 header 的工作原理。它只是对列表中第一个节点的引用。此外,不需要“辅助”节点。

让我们按顺序查看需要执行的步骤:

第 1 步:设置要插入的节点的 pred

由于该节点将成为第一个节点,因此它后面将没有节点,因此您可以说 v.pred = null;

第 2 步: 设置 succ您要插入的节点的

我们的新节点需要指向旧的第一个节点。这就是混乱的地方。您执行以下操作:

v.succ = header.succ ; // 将新节点 succ 指向当前第一个节点

但是 header 是什么?它是对第一个节点的引用。通过说 header.succ 您是在说您想要第一个节点的后继节点,即第二个节点。当您看到 header 时,只需想到“第一个节点”,您就会想到:

v.succ = header;

第 3 步: 指向旧节点第一个节点回到新节点

您已经正确执行了此步骤(记住,认为 header 是“第一个节点”):

header.succ = v ; //将标头指向新节点

第4步:现在将新节点设为标头

header = v;

编辑(响应您的编辑# 3):
关于整个方法存在一些问题,我将在最后提出,但现在先把这些问题放在一边,并假设您需要以这种方式设置列表......

假设您正在传递列表的第一个节点(Header.succ) 为 v,我们采取相同的步骤:

第 1 步:设置要插入的节点的 pred

您希望新节点指向 Header。
v.pred = Header;

第 2 步: 设置要插入的节点的 succ

您希望新节点指向旧的第一个节点,即 Header.succ

v.succ = Header.succ;

第 3 步:将旧的第一个节点指向新节点

确保您首先检查第一个节点是否存在(在我的第一篇文章中忘记了这一点)!

if (Header.succ != null) {
    Header.succ.pred = v; // Header.succ is the first node, and you want to set its pred reference
}

第 4 步: 现在将新节点设为第一个节点

Header.succ = v;

请注意,您甚至不需要 z,因为您可以使用 Header.succ< 到达第一个节点/代码>。这意味着您不需要它作为参数。

现在,除了这些之外,您还应该考虑一些问题:

具有 pred 引用和数据字段的标头有什么意义? Tail 具有 succ 引用和数据字段有什么意义?

如果您想使用此链表并插入项目,那么只需为每个项目调用 add() 而不是 addFirst() 和 addBeforeFirst() 不是更好吗?如果你可以使用remove()方法,那么你必须跟踪列表是否为空,或者不知道要调用哪个方法,addFirst()或addBeforeFirst(),这有点难看。最好编写一个更通用的 add() 来为将要使用列表的用户处理这个问题。

It looks like most of your confusion is around how header works. It is just a reference to the first node in the list. Also, there is no need for an "auxiliary" node.

Lets look at the steps you need to take in order:

Step 1: Set the pred of the node you are inserting.

Since the node will become the first node, it will have no nodes behind it, so you can say v.pred = null;

Step 2: Set the succ of the node you are inserting.

Our new node needs to point forward to the old first node. Here's where the confusion comes. You do the following:

v.succ = header.succ ; // point new node succ to current first node

But what is header? It is a reference to the first node. By saying header.succ you are saying you want the first nodes successor, which is the second node. When you see header just think "first node" and you will come up with:

v.succ = header;

Step 3: Point the old first node back to the new node

You already are doing this step correctly (remember, think header is "first node"):

header.succ = v ; //point header to new node

Step 4: Now make the new node the header

header = v;

EDIT (in response to your edit #3):
There are some issues about this whole approach which I'll bring up at the end, but those aside for now and assuming you are required to set up your list this way...

Assuming you're passing in the first node of the list (Header.succ) as v, let's take the same steps:

Step 1: Set the pred of the node you are inserting.

You want your new node to point back toward Header.
v.pred = Header;

Step 2: Set the succ of the node you are inserting.

You want your new node to point to the old first node, which was Header.succ

v.succ = Header.succ;

Step 3: Point the old first node back to the new node

Make sure you check that a first node exists first (forgot this in my first post)!

if (Header.succ != null) {
    Header.succ.pred = v; // Header.succ is the first node, and you want to set its pred reference
}

Step 4: Now make the new node the first node

Header.succ = v;

Note how you don't even need z since you can get to the first node using Header.succ. This means you shouldn't need it as a parameter.

Now, all that aside there are some issues you should think about:

What is the point of a Header having a pred reference and a data field? What is the point of a Tail having a succ reference and a data field?

If you wanted to use this linked list and insert items, wouldn't it be better if you just had to call add() for every item instead of addFirst() and addBeforeFirst()? What if you could a remove() method, then you'd have to keep track of if the list is empty or not to figure out which method to call, addFirst() or addBeforeFirst(), which is kind of ugly. It's better to write a more generalized add() which takes care of this for the user who is going to use the list.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文