我正在尝试为循环双向链表的字符串值编写一个插入函数。我看到创建一个虚拟节点有利于这样做,这样我就可以消除特殊情况,例如列表为空时。问题是我没有找到很多关于虚拟头节点的好信息。我了解他们的目的,但我不明白我是如何创建/实现它的。
感谢所有代码示例,试图自己弄清楚,如果有人可以查看它,我有点卡住了。
#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;
}
对于循环双向链表,您可以设置 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; }
}
你为什么要尝试使用虚拟节点?我希望您可以在没有虚拟节点的情况下处理它。例如:
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++;
}
上面的一个在没有虚拟节点的情况下处理链表的示例。
对于没有虚拟节点的循环双链表,第一个节点的前一个指针指向最后一个节点,最后一个节点下一个指针指向第一个节点。列表本身具有指向第一个节点的头部指针,以及指向最后一个节点和/或计数的尾部指针(可选)。
对于虚拟节点,第一个节点的前一个指针指向虚拟节点,最后一个节点下一个指针指向虚拟节点。虚拟节点指针可以指向虚拟节点本身,也可以为 null。
HP/Microsoft STL 列表函数使用虚拟节点作为列表头节点,下一个指针用作指向第一个实节点的头指针,前一个指针用作指向最后一个实节点的尾部指针。
#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;
}
我认为这段代码可以帮助您。当你想要实现一些数据结构时,你必须有一个清晰的蓝图。
在构造函数中执行以下操作
ptrHead = new Node();
listSize = 1;
如果你也有尾巴,
ptrHead->next = ptrTail;
上面的代码将创建虚拟节点。确保实现不应受到此虚拟节点的影响。
例如:
int getSize()
{
return listSize-1;
}