链表(linked list)是一种常见的数据结构,用于存储一系列元素。它由一系列节点组成,每个节点包含数据和指向下一个节点的指针。
在 C++ 中,可以使用结构体来表示链表节点,然后使用指针将这些节点连接起来。
----------
在常见的链表实现中,头指针(head pointer)是链表的一部分。头指针是一个指针变量,指向链表中的第一个节点。通过头指针,可以访问整个链表的节点序列。
在 C++ 中,头指针通常是一个类的私有成员变量,在链表的操作中用来标识链表的起始位置。通过头指针,可以遍历整个链表,执行插入、删除等操作。
需要注意的是,头指针只是指向链表中第一个节点的指针,它本身并不包含任何数据。链表的实际数据存储在节点中,而头指针只是用来指示链表的起始位置。
#include <iostream>
using namespace std;struct Node {int data;Node* next;
};class LinkedList {
private:Node* head; // 头指针,指向链表的第一个节点public:LinkedList() {head = nullptr; // 初始时链表为空}// 在链表头部插入一个新节点void insertAtBeginning(int newData) {Node* newNode = new Node;newNode->data = newData;newNode->next = head;head = newNode;}// 删除链表中的第一个节点void deleteAtBeginning() {if (head == nullptr) {cout << "Linked list is empty. Cannot delete from an empty list." << endl;return;}Node* temp = head;head = head->next;delete temp;}// 打印链表的所有节点数据void printList() {Node* temp = head;cout << "Linked List: ";while (temp != nullptr) {cout << temp->data << " ";temp = temp->next;}cout << endl;}
};int main() {LinkedList myList;myList.insertAtBeginning(35);myList.insertAtBeginning(75);myList.insertAtBeginning(49);myList.printList(); // 输出: Linked List: 9 7 3myList.deleteAtBeginning();myList.printList(); // 输出: Linked List: 7 3return 0;
}
让我们来解释一下插入数据的顺序和头指针之间的关系。
// 在单向链表中,头指针通常指向链表中的第一个节点。当我们要在链表的头部插入一个新节点时,我们需要进行以下步骤:
// 创建一个新节点,并设置它的数据值。
// 将新节点的 next 指针指向当前的第一个节点,使新节点成为新的第一个节点。
// 更新头指针,使它指向新的第一个节点。
// 这样做的原因是,链表的头指针需要始终指向链表的第一个节点,以便我们能够轻松地访问整个链表。
-------------------
// 这段代码定义了一个名为 insert 的函数,用于在链表中插入新节点。让我解释一下这段代码的逻辑:
// 函数接受两个参数:指向链表头节点的引用 head 和要插入的新节点的值 value。
// 首先,它创建了一个新的节点 newNode,并将新节点的数据域初始化为 value。
// 接着,它检查链表是否为空。如果链表为空(即 head 为 nullptr),则将新节点设为链表的头节点。
// 如果链表不为空,则遍历链表,直到找到最后一个节点(即指针域为 nullptr 的节点)。
// 将新节点链接到链表的末尾,即将当前最后一个节点的指针域指向新节点。
#include <iostream>// 定义链表节点结构体
struct Node {int data; // 数据域Node* next; // 指针域,指向下一个节点
};// 在链表中插入新节点
// 这段代码定义了一个名为 insert 的函数,用于在链表中插入新节点。让我解释一下这段代码的逻辑:
// 函数接受两个参数:指向链表头节点的引用 head 和要插入的新节点的值 value。
// 首先,它创建了一个新的节点 newNode,并将新节点的数据域初始化为 value。
// 接着,它检查链表是否为空。如果链表为空(即 head 为 nullptr),则将新节点设为链表的头节点。
// 如果链表不为空,则遍历链表,直到找到最后一个节点(即指针域为 nullptr 的节点)。
// 将新节点链接到链表的末尾,即将当前最后一个节点的指针域指向新节点。
// 这样,新节点就成功地插入到了链表的末尾。这个函数的时间复杂度是 O(n),其中 n 是链表中节点的数量,因为它需要遍历整个链表来找到最后一个节点。
void insert(Node*& head, int value) {Node* newNode = new Node;newNode->data = value; // 手动设置数据域的值newNode->next = nullptr; // 手动设置指针域的值if (head == nullptr) {head = newNode;} else {Node* current = head;while (current->next != nullptr) {// 如果链表不为空,则遍历链表,直到找到最后一个节点(即指针域为 nullptr 的节点)。current = current->next;}current->next = newNode;// 将新节点链接到链表的末尾,即将当前最后一个节点的指针域指向新节点。}
}// 打印链表
void printList(Node* head) {Node* current = head;while (current != nullptr) {std::cout << current->data << " -> ";current = current->next;}std::cout << "nullptr" << std::endl;
}// 主函数
int main() {Node* head = nullptr;insert(head, 1);insert(head, 2);insert(head, 3);printList(head);return 0;
}
--------------------------------------------
// 在没有头指针的情况下,我们仍然需要一个指针来引导我们访问链表的第一个节点。这个指针可以是一个指向第一个节点的指针,比如在示例中使用的 start 指针。通过使用这个指针,我们可以找到链表的第一个节点,并在其前面插入新节点,然后更新 start 指针以指向新的第一个节点。
// 因此,插入数据的顺序与头指针的关系在于,无论是否有头指针,我们都需要确保链表的第一个节点正确地指向新插入的节点,以确保我们可以轻松地访问整个链表。
#include <iostream>// 定义节点结构体
struct Node {int data;Node* next;
};// 在链表头部插入一个新节点
Node* insertAtBeginning(Node* start, int newData) {// 创建一个新节点Node* newNode = new Node;newNode->data = newData;newNode->next = start; // 新节点的下一个节点指向当前第一个节点return newNode; // 返回新节点作为新的第一个节点
}// 打印链表
void printList(Node* start) {Node* current = start;while (current != nullptr) {std::cout << current->data << " ";current = current->next;}std::cout << std::endl;
}int main() {Node* start = nullptr; // 定义一个指向第一个节点的指针,并初始化为 nullptr// 在链表头部插入节点start = insertAtBeginning(start, 3);start = insertAtBeginning(start, 7);start = insertAtBeginning(start, 9);// 打印链表std::cout << "Linked List: ";printList(start);return 0;
}