链表类题目

/*问题1
* 将链表的后半截逆序,然后跟前半截交叉生成新的链表。
* 要求: Time: O(N), Space: O(1),不允许递归如:1->2->3->4->5 ==>1->5->2->4->3
*/

void reserve(ListNode* &head) {

	if (!head) return;
	// 1->2->3->4->5
	// 1  2  3  4  null
	//       s     f
	if (!head->next) {
		return;
	}
	
	ListNode* slow = head;
	ListNode* fast = head;

	while (slow->next && fast->next) {
		slow = slow->next;
		fast = fast->next->next;
	}

	// 把 slow(不包含)后面的倒序 
	ListNode *r_list = nullptr;
	ListNode* node = nullptr;
	while (slow->next) { // 5 --> 4
		if (!r_list) {
			r_list = slow->next;
		}
		else {// 头插
			node = slow->next;
			node->next = r_list;
			r_list = node;
		}
		slow->next = slow->next->next;
	}
	// 1->2->3    5->4  交叉合并
	ListNode* new_list = nullptr;
	ListNode* tail = nullptr;
	ListNode* node1 = nullptr;
	ListNode* node2 = nullptr;
	while (head && r_list) {

		node1 = head;
		head = head->next;
		node1->next = NULL;

		node2 = r_list;
		r_list = r_list->next;
		node2->next = NULL;

		// 尾插法
		if (!new_list) {
			new_list = node1;
			new_list->next = node2;
			tail = new_list->next;
		}
		else {
			tail->next = node1;
			tail = tail->next;
			tail->next = node2;
			tail = tail->next;
		}
	}

	if (head) {
		if(tail) tail->next = head;
	}
	head = new_list;
}


/*问题2
反转链表的[n,m]区间
*/
void head_insert(ListNode*& head, ListNode* node) {
	if (!head) {
		head = node;
	}
	else {
		node->next = head;
		head = node;
	}
}

void reserve_n2m(ListNode* head, int n, int m) {

	if (!head || n >= m || n < 0) return;

	int dist = m - n;

	// 找到第n个节点 并记录它前面一个节点
	ListNode* pre_node = new ListNode(0);
	pre_node->next = head;
	ListNode* ptr = head; // 遍历节点

	for (int i = 0; i < n; ++i) {
		pre_node = ptr;
		ptr = ptr->next;
	}

	ListNode* reserve_list = nullptr;
	ListNode* node = nullptr;
	// 把节点从原来的链表节点删除放入reserve_list中
	for (int i = 0; i <= dist && pre_node->next; ++i) {
		node = pre_node->next;
		pre_node->next = pre_node->next->next;
		node->next = nullptr;
		head_insert(reserve_list, node);
	}
	// 在插入就行
	node = pre_node->next;
	ptr = reserve_list;
	while (ptr->next) {
		ptr = ptr->next;
	}
	pre_node->next = reserve_list;
	ptr->next = node;

}


/*问题3
单链表快速排序
*/
ListNode* patition(ListNode* start, ListNode* end) {
	ListNode* mid = start;
	ListNode* tail = start->next;
	int base = start->val;
	while (tail != end) {
		if (base > tail->val) {
			mid = mid->next;
			swap(mid->val, tail->val);
		}
		tail = tail->next;
	}

	swap(mid->val, start->val);
	return mid;
}
void list_quick_sort(ListNode* start, ListNode* end) {

	if (start != end && start->next != end) {
		ListNode* mid = patition(start, end);
		list_quick_sort(start, mid);
		list_quick_sort(mid->next, end);
	}
}

/*问题4
输入两个无环的单向链表,找出它们的第一个公共结点,如果没有公共节点则返回空。
(注意因为传入数据是链表,所以错误测试数据的提示是用其他方式显示的,保证传入数据是正确的)

数据范围: n≤1000
要求:空间复杂度 O(1),时间复杂度 O(n)
*/
struct ListNode {
	int val;
	struct ListNode* next;
	ListNode(int x) :
		val(x), next(NULL) {
	}
};
ListNode* FindFirstCommonNode(ListNode* pHead1, ListNode* pHead2) {
	if (!pHead1 || !pHead2) return NULL;
	ListNode* ptr1 = pHead1;
	ListNode* ptr2 = pHead2;
	
	uint16_t len1 = 0;
	uint16_t len2 = 0;
	while (ptr1) {
		len1++;
		ptr1 = ptr1->next;
	}

	while (ptr2) {
		len2++;
		ptr2 = ptr2->next;
	}
	ptr1 = pHead1;
	ptr2 = pHead2;
	if (len1 > len2) {// ptr1 长
		for (int i = 0; i < len1 - len2; ++i) ptr1 = ptr1->next;
	}
	else {
		for (int i = 0; i < len2 - len1; ++i) ptr2 = ptr2->next;
	}
	
	while (ptr1 && ptr2) {
		if (ptr1 == ptr2) break;
		ptr1 = ptr1->next;
		ptr2 = ptr2->next;
	}
	if (!ptr2) return NULL;

	return ptr2;
}

<think>好的,我需要帮助用户找到与C语言链表相关的练习题或面试题。用户提到了他们想寻找练习题和示例代码,所以我需要整理一些常见的链表题目,并提供相应的思路和代码示例。同时,根据用户提供的引用内容,我需要确保引用的正确使用,并在回答末尾添加相关问题。 首先,回顾用户提供的引用资料,引用1到引用5涉及链表的基础知识、内存操作(如realloc)、链表合并、快慢指针应用以及链表旋转等问题。这些可以作为题目来源。 接下来,我需要列出常见的链表练习题型,并确保每个题目都有对应的解决思路和代码示例。例如: 1. 链表反转:这是基础题目,可以用迭代或递归方法解决。引用3中的快慢指针可能相关,但更直接的是反转指针方向。 2. 合并两个有序链表:引用4提到了这个问题,可以直接使用递归或迭代方法。 3. 检测环形链表:快慢指针法,引用3中的方法2可能适用。 4. 删除倒数第N个节点:同样可以用快慢指针,如引用3中的代码。 5. 链表中间节点:快慢指针,快指针走两步,慢指针一步。 6. 链表排序:归并排序适合链表,引用中的合并方法可用。 7. 链表元素删除:比如删除重复项,需要遍历并比较节点值。 8. 链表实现栈/队列:考察基础数据结构实现。 9. 复杂链表复制:涉及深拷贝和哈希表或节点拆分。 10. 链表相加:处理进位和大数相加问题。 每个题目需要给出思路和代码示例。例如,链表反转的迭代法需要三个指针:prev, current, next。合并有序链表可以用递归或迭代,这里可能参考引用4的方法。 需要确保代码示例用C语言编写,符合用户要求,如引用3中的C++代码需要转换为C。例如,合并链表时,创建哑节点作为新链表的头,然后逐个比较节点值,调整指针指向。 同时,注意引用文献的正确标注。例如,合并链表的问题可以引用[^4],快慢指针的应用引用[^3]和[^5]。 最后,生成相关问题,可能包括链表操作的时间复杂度、优化方法、具体应用场景等,例如如何检测环形链表、反转链表的递归实现、合并链表的迭代方法优化等。 总结步骤: 1. 分整理链表常见题目,确保覆盖用户引用中的内容。 2. 为每个题目编写简要思路和C语言代码示例。 3. 正确引用提供的文献。 4. 生成相关问题列表,便于用户进一步学习。 现在需要将这些内容组织成用户友好的格式,分点说明,代码用正确格式,并添加相关问题。</think>### C语言链表练习题与示例代码 以下是10个常见的链表练习题及解题思路,包含C语言代码示例和相关引用标注: --- #### 1. **链表反转** **问题**:反转单链表 **思路**:使用三个指针`prev`、`current`、`next`逐步反转指针方向。 **代码示例**: ```c struct ListNode* reverseList(struct ListNode* head) { struct ListNode *prev = NULL, *current = head, *next; while (current != NULL) { next = current->next; current->next = prev; prev = current; current = next; } return prev; } ``` **引用**:似快慢指针思想扩展应用。 --- #### 2. **合并两个有序链表** **问题**:将两个升序链表合并为一个新链表。 **思路**:创建哑节点作为新链表头,逐个比较节点值。 **代码示例**: ```c struct ListNode* mergeTwoLists(struct ListNode* l1, struct ListNode* l2) { struct ListNode dummy = {0, NULL}; struct ListNode *tail = &dummy; while (l1 && l2) { if (l1->val <= l2->val) { tail->next = l1; l1 = l1->next; } else { tail->next = l2; l2 = l2->next; } tail = tail->next; } tail->next = l1 ? l1 : l2; return dummy.next; } ``` --- #### 3. **检测环形链表** **问题**:判断链表是否有环。 **思路**:快慢指针法,若相遇则有环。 **代码示例**: ```c bool hasCycle(struct ListNode *head) { struct ListNode *slow = head, *fast = head; while (fast && fast->next) { slow = slow->next; fast = fast->next->next; if (slow == fast) return true; } return false; } ``` **引用**:快慢指针思想参考自。 --- #### 4. **删除倒数第N个节点** **问题**:删除链表倒数第N个节点。 **思路**:快指针先走N步,然后快慢指针同步移动。 **代码示例**: ```c struct ListNode* removeNthFromEnd(struct ListNode* head, int n) { struct ListNode dummy = {0, head}; struct ListNode *fast = &dummy, *slow = &dummy; while (n--) fast = fast->next; while (fast->next) { fast = fast->next; slow = slow->next; } slow->next = slow->next->next; return dummy.next; } ``` **引用**:快慢指针实现参考[^3]。 --- #### 5. **链表的中间节点** **问题**:返回链表的中间节点。 **思路**:快指针走两步,慢指针走一步。 **代码示例**: ```c struct ListNode* middleNode(struct ListNode* head) { struct ListNode *slow = head, *fast = head; while (fast && fast->next) { slow = slow->next; fast = fast->next->next; } return slow; } ``` --- #### 6. **链表排序(归并排序)** **问题**:对链表进行升序排序。 **思路**:利用归并排序的分治策略,先拆分后合并。 **代码示例**: ```c struct ListNode* sortList(struct ListNode* head) { if (!head || !head->next) return head; // 快慢指针找中点拆分 struct ListNode *slow = head, *fast = head->next; while (fast && fast->next) { slow = slow->next; fast = fast->next->next; } struct ListNode *mid = slow->next; slow->next = NULL; return mergeTwoLists(sortList(head), sortList(mid)); } ``` --- #### 7. **删除重复元素** **问题**:删除排序链表中重复元素。 **思路**:遍历链表,跳过重复节点。 **代码示例**: ```c struct ListNode* deleteDuplicates(struct ListNode* head) { struct ListNode *current = head; while (current && current->next) { if (current->val == current->next->val) { current->next = current->next->next; } else { current = current->next; } } return head; } ``` --- #### 8. **链表实现栈** **问题**:用链表实现栈的`push`和`pop`操作。 **代码示例**: ```c typedef struct { struct ListNode *top; } Stack; void push(Stack *s, int val) { struct ListNode *node = malloc(sizeof(struct ListNode)); node->val = val; node->next = s->top; s->top = node; } int pop(Stack *s) { if (!s->top) return -1; // 栈空 struct ListNode *temp = s->top; int val = temp->val; s->top = temp->next; free(temp); return val; } ``` --- #### 9. **复杂链表的深拷贝** **问题**:复制带随机指针的链表。 **思路**:使用哈希表或节点拆分法。 **代码示例**(节点拆分法): ```c struct Node* copyRandomList(struct Node* head) { if (!head) return NULL; // 1. 复制节点并插入原链表 struct Node *current = head; while (current) { struct Node *copy = malloc(sizeof(struct Node)); copy->val = current->val; copy->next = current->next; current->next = copy; current = copy->next; } // 2. 复制random指针 current = head; while (current) { if (current->random) current->next->random = current->random->next; current = current->next->next; } // 3. 拆分链表 struct Node *newHead = head->next; current = head; while (current) { struct Node *copy = current->next; current->next = copy->next; if (copy->next) copy->next = copy->next->next; current = current->next; } return newHead; } ``` --- #### 10. **链表相加** **问题**:两个链表表示的数相加(逆序存储)。 **思路**:模拟加法进位过程。 **代码示例**: ```c struct ListNode* addTwoNumbers(struct ListNode* l1, struct ListNode* l2) { struct ListNode dummy = {0, NULL}; struct ListNode *tail = &dummy; int carry = 0; while (l1 || l2 || carry) { int sum = carry; if (l1) sum += l1->val, l1 = l1->next; if (l2) sum += l2->val, l2 = l2->next; carry = sum / 10; tail->next = malloc(sizeof(struct ListNode)); tail->next->val = sum % 10; tail->next->next = NULL; tail = tail->next; } return dummy.next; } ``` --- §§ 相关问题 § 1. 如何检测链表中是否存在环?时间复杂度是多少? 2. 反转链表的递归实现方式是什么? 3. 合并两个有序链表时如何避免内存泄漏? 4. 快慢指针法在链表中还有哪些应用场景? 5. 链表的归并排序相比数组有哪些优势?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值