【问题标题】:Dummy Head Node Linked List虚拟头节点链表
【发布时间】:2014-02-26 00:12:55
【问题描述】:

我正在尝试为循环双向链表的字符串值编写插入函数。我看到创建一个虚拟节点有利于这样做,因此我可以消除特殊情况,例如列表为空时。问题是我没有找到很多关于虚拟头节点的好信息。我理解他们的目的,但我不明白我是如何创建/实现它的。

感谢所有代码示例,尽管有人可以查看它,但我自己试图弄清楚它有点卡住了。

 #include <iostream>
 #include <string>
 using namespace std;
 typedef string ListItemType;

struct node {
node * next;
node * prev;
ListItemType value;

};
node * head;
node * dummyHead = new node;
void insert(const ListItemType input, node *  & within);

void main(){
     insert("bob",dummyHead);
}

void insert( const ListItemType input, node *  &ListHead){
   node *newPtr = new node;
   node *curr;
   newPtr->value = input;

curr = ListHead->next; //point to first node;
while (curr != ListHead && input < curr->value){
    curr = curr->next;
}
        //insert the new node pointed to by the newPTr before
        // the node pointed to by curr
    newPtr->next = curr;
    newPtr->prev = curr->prev;
    curr->prev = newPtr;
    newPtr->prev->next = newPtr;
}

【问题讨论】:

  • 您的插入函数不应该需要第二个参数 ListHead,因为 List 知道自己的头部。您的插入函数应该接受一个要插入的值,找到插入新节点的位置并插入该节点。看起来它是按降序插入的。
  • 而且您只需要 1 个节点(head 或 dummyHead),无论您喜欢哪个。该节点也称为哨兵节点。

标签: c++ linked-list


【解决方案1】:

对于循环双向链表,您可以设置 1 个哨兵节点,当列表为空时,“next”和“prev”都指向自身。当 list 不为空时, sentinel->next 指向第一个元素, sentinel->prev 指向最后一个元素。有了这些知识,您的插入和删除函数将如下所示。

这是非常基本的,您的 LinkedList 和 Node 类可能会以不同的方式实现。那没问题。主要的是 insert() 和 remove() 函数实现,它显示了哨兵节点如何消除检查 NULL 值的需要。

希望这会有所帮助。

class DoublyLinkedList
{
    Node *sentinel;
    int size = 0;

    public DoublyLinkedList() {
        sentinel = new Node(null);
    }

    // Insert to the end of the list
    public void insert(Node *node) {
        // being the last node, point next to sentinel
        node->next = sentinel;

        // previous would be whatever sentinel->prev is pointing previously
        node->prev = sentinel->prev;

        // setup previous node->next to point to newly inserted node
        node->prev->next = node;

        // sentinel previous points to new current last node
        sentinel->prev = node;

        size++;
    }

    public Node* remove(int index) {
        if(index<0 || index>=size) throw new NoSuchElementException();

        Node *retval = sentinel->next;
        while(index!=0) {
            retval=retval->next;
            index--;
        }

        retval->prev->next = retval->next;
        retval->next->prev = retval->prev;
        size--;
        return retval;
    }
}

class Node 
{
    friend class DoublyLinkedList;
    string *value;
    Node *next;
    Node *prev;

    public Node(string *value) {
        this->value = value;
        next = this;
        prev = this;
    }

    public string* value() { return value; }
}

【讨论】:

  • 谢谢你的例子,如果你能看一下的话,我有点想自己做。
【解决方案2】:

您为什么要尝试使用虚拟节点? 我希望您可以在没有虚拟节点的情况下处理它。 例如:

void AddNode(Node node)
{
   if(ptrHead == NULL)
   {
       ptrHead = node;
   }else
   {
       Node* itr = ptrHead;
       for(int i=1; i<listSize; i++)
       {
            itr = itr->next;
       }
       itr->next = node;
   }
   listSize++;
}

上面是一个处理没有虚拟节点的链表的例子。

【讨论】:

  • 我正在尝试使用虚拟节点所以我不需要检查特殊情况,例如 if ptrHead == NULL
  • 当您执行 what 时不必检查 null 吗?在基于 array 的循环队列中牺牲节点槽还有其他好处,但对于链表来说,没有明显的优势。它不需要具有headtail 指针的列表结构来代替使用死节点的prevnext 来实现相同目的。这就是你所得到的。
【解决方案3】:

对于没有哑节点的循环双链表,第一个节点的previous指针指向最后一个节点,最后一个节点的next指针指向第一个节点。列表本身有一个指向第一个节点的头指针和一个可选的指向最后一个节点的尾指针和/或计数。

对于虚拟节点,第一个节点的前一个指针指向虚拟节点,最后一个节点的下一个指针指向虚拟节点。虚拟节点指针可以指向虚拟节点本身,也可以为空。

HP/Microsoft STL 列表函数使用一个虚拟节点作为列表头节点,next 指针用作指向第一个真实节点的头指针,而前一个指针用作指向最后一个真实节点的尾指针。

【讨论】:

    【解决方案4】:
    #include <iostream>
    #include <string>
    using namespace std;
    typedef string ElementType;
    
    struct Node
    {
        Node(){}
        Node(ElementType element, Node* prev = NULL, Node* next = NULL):element(element){}
        ElementType element;
        Node* prev;
        Node* next;
    };
    
    class LinkList
    {
    public:
        LinkList()
        {
            head = tail = dummyHead = new Node("Dummy Head", NULL, NULL);
            dummyHead->next = dummyHead;
            dummyHead->prev = dummyHead;
    
            numberOfElement = 0;
        }
    
        void insert(ElementType element)
        {
            Node* temp = new Node(element, NULL, NULL);
    
            if (0 == numberOfElement)
            {
                head = tail = temp;
                head->prev = dummyHead;
                dummyHead->next = head;
    
                tail->next = dummyHead;
                dummyHead->prev = tail;
            }
            else
            {
                tail->next = temp;
                temp->prev = dummyHead->next;
                temp->next =  dummyHead;
                dummyHead->next = temp;
                tail = temp;
            }
    
            numberOfElement += 1;
        }
    
        int length() const { return numberOfElement; }
        bool empty() const { return head == dummyHead; }
    
        friend ostream& operator<< (ostream& out, const LinkList& List);
    private:
        Node* head;
        Node* tail;
        Node* dummyHead;
    
        int numberOfElement;
    };
    
    
    ostream& operator<< (ostream& out, const LinkList& List)
    {
        Node* current = List.head;
        while (current != List.dummyHead)
        {
            out<<current->element<<" ";
            current = current->next;
        }
        out<<endl;
        return out;
    }
    int main()
    {
        string arr[] = {"one", "two", "three", "four", "five"};
        LinkList list;
        int len = sizeof(arr) / sizeof(arr[0]);
        for (int i = 0; i < len; ++i)
        {
            list.insert(arr[i]);
        }
    
        cout<<list<<endl;
    }
    

    我认为这段代码可以帮助你。当你想实现某种数据结构时,你必须有一个清晰的蓝图。

    【讨论】:

      【解决方案5】:

      在构造函数中执行以下操作

      ptrHead = new Node();
      listSize = 1;
      

      如果你也有尾巴,

      ptrHead->next = ptrTail;
      

      以上代码将创建虚拟节点。 确保您的实现不受此虚拟节点的影响。

      例如:

      int getSize()
      {
         return listSize-1;
      }
      

      【讨论】:

      • 我已经给出了实现虚拟节点的方法,因为你要求它。但我的建议是不要使用虚拟节点。
      • 感谢您帮助尝试编写解决方案,但无法处理实际头值。发表在编辑中
      猜你喜欢
      • 2019-08-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-03-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多