链表动静构造法

对于链表的增删改查操作是数据结构的基础,但是当我们提交代码的时候经常会遇到头结点的判断出错问题,这里以leetcode的一道题为例重点记录下构建链表的动静态初始化方法,可以包含头结点的处理,不用对头结点做单独处理。

Merge Two Sorted Lists

将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 

示例:

输入:1->2->4, 1->3->4
输出:1->1->2->3->4->4

这道题很简单,只要初始化定义两个指针p1和p2,分别指向两个链表,把小的那个节点构建到新的链表中即可。

这里给出模板:

//新定义的链表头指针的前一个指针
ListNode* pre_head = new ListNode(-1);
//用来指向尾节点
ListNode* tmp = head;
while(something){
    //把tmp指向下一个节点
    tmp->next = l1;
    //把tmp更新为尾节点
    tmp = tmp->next;
}
已这道题为例,代码如下:
    ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
	if (!l1) {
		return l2;
	}
	if (!l2) {
		return l1;
	}
	ListNode* head = new ListNode(-1);
	ListNode* tmp = head;
	while (l1 && l2) {
		if (l1->val >= l2->val) {
			tmp->next = l2;
			tmp = tmp->next;
			l2 = l2->next;
		}
		else {
			tmp->next = l1;
			tmp = tmp->next;
			l1 = l1->next;
		}
	}
	if (!l1 && l2) {
		tmp->next = l2;
	}
	if (l1 && !l2) {
		tmp->next = l1;
	}
	return head->next;
    }

这种情况已经包含了头结点的情况,因为最后返回head->next,即使两个链表都为空,则head->next也为空,返回的依然为空指针。

如果是删除节点,a->b->c,我们要删除节点b,一般的做法是找到删除节点的前一个节点a,把a的下一个指针指向c,并删除b节点。但是如果是删除头结点a怎么办呢?a可是没有前驱节点的,所以如果不考虑这种情况就会报错。而用静态初始化链表法(静态声明pre_head节点),并把pre_head的下一个指针指向头结点(动态绑定旧链表),便可以解决删除头结点的问题:找到头结点head的前一个节点pre_head,把pre_head的next指向head的next,删除head,便完成了head头结点的删除,使代码具有统一性,健壮性更强。

ListNode* pre_head = new ListNode(-1);

pre_head->next=head

删除节点的代码如下(这里假设待删除节点):

void deleteNode(ListNode* head, ListNode* deleteNode) {
	ListNode* pre_head = new ListNode(-1);
	ListNode* tmp = pre_head;
	pre_head->next = head;

	ListNode* preDeleteNode = tmp;

	while (tmp != deleteNode) {
		preDeleteNode = tmp;
		tmp = tmp->next;
	}
	//preDeleteNode为待删除节点deleteNode的头一个节点
	preDeleteNode->next = tmp->next;
	delete tmp;
}
这种静动态构造链表的方法非常重要,以后测试用例如果未通过(特别是关于头结点的特殊节点报错),请考虑这种方法。
### 链表类构造函数的实现与用法 链表类的构造函数主要用于初始化链表的状态,确保链表对象在创建时具备正确的结构和初始值。以下是几种常见的链表类构造函数实现方式及其用法: #### 1. 默认构造函数 默认构造函数用于创建一个空链表,通常将头指针初始化为 `nullptr`,表示链表为空。 ```cpp LinkedList() : head(nullptr) {} ``` 这种方式确保了链表对象在创建时不会指向任何无效内存地址[^1]。 #### 2. 拷贝构造函数 拷贝构造函数用于创建一个与已有链表内容相同的链表。它需要遍历原链表,并为每个节点创建一个新的节点。 ```cpp LinkedList(const LinkedList& rhs) { head = nullptr; Node* current = rhs.head; Node** tail = &head; while (current != nullptr) { *tail = new Node(current->data); tail = &((*tail)->next); current = current->next; } } ``` 此实现通过逐个复制节点的方式,确保新链表与原链表具有相同的内容,但不共享内存地址[^3]。 #### 3. 原生数组构造函数 该构造函数允许根据一个数组初始化链表。它会遍历数组中的每个元素,并将其插入到链表中。 ```cpp LinkedList(int const a[], int n) { head = nullptr; Node** tail = &head; for (int i = 0; i < n; ++i) { *tail = new Node(a[i]); tail = &((*tail)->next); } } ``` 这种方式适用于从已有的数据源(如数组)快速构建链表。 #### 4. 填充构造函数 填充构造函数用于创建一个包含指定数量节点的链表,每个节点的值都相同。 ```cpp LinkedList(int n, int value) { head = nullptr; Node** tail = &head; for (int i = 0; i < n; ++i) { *tail = new Node(value); tail = &((*tail)->next); } } ``` 这种构造函数在需要初始化大量重复值时非常有用。 #### 5. 析构函数 析构函数用于释放链表所占用的所有动态分配的内存。 ```cpp ~LinkedList() { Node* current = head; while (current != nullptr) { Node* next = current->next; delete current; current = next; } } ``` 确保所有动态分配的内存都被正确释放,避免内存泄漏[^1]。 ### 示例代码 以下是一个完整的链表类定义,包含多种构造函数的实现: ```cpp class LinkedList { public: struct Node { int data; Node* next; Node(int a = 0, Node* b = nullptr) : data(a), next(b) {} }; private: Node* head; public: // 默认构造函数 LinkedList() : head(nullptr) {} // 拷贝构造函数 LinkedList(const LinkedList& rhs) { head = nullptr; Node* current = rhs.head; Node** tail = &head; while (current != nullptr) { *tail = new Node(current->data); tail = &((*tail)->next); current = current->next; } } // 原生数组构造函数 LinkedList(int const a[], int n) { head = nullptr; Node** tail = &head; for (int i = 0; i < n; ++i) { *tail = new Node(a[i]); tail = &((*tail)->next); } } // 填充构造函数 LinkedList(int n, int value) { head = nullptr; Node** tail = &head; for (int i = 0; i < n; ++i) { *tail = new Node(value); tail = &((*tail)->next); } } // 析构函数 ~LinkedList() { Node* current = head; while (current != nullptr) { Node* next = current->next; delete current; current = next; } } }; ``` ### 注意事项 - 构造函数可以重载,以支持不同的初始化需求[^2]。 - 在实现拷贝构造函数时,需注意深拷贝与浅拷贝的区别,避免内存共享问题[^3]。 - 析构函数必须正确释放所有动态分配的内存,防止内存泄漏。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值