逆置单链表/求倒数第k个结点

本文介绍如何定义及创建链表,并提供逆置单链表和查找倒数第k个节点的方法。此外,还提供了一个不使用基本算术运算符实现两数相加的函数。

定义并创建链表:

#include <iostream>
using namespace std;
#include <assert.h>


typedef struct ListNode
{
	int _data;
	struct ListNode* _next;
	ListNode(int data)
		:_data(data)
		,_next(NULL)
	{}
}ListNode;

void Push(ListNode** list,int x)
{
	assert(list);
	ListNode* tmp = new ListNode(x);
	if (*list == NULL)
	{
		*list = tmp;
	}
	else
	{
		ListNode* cur = *list;
		while (cur->_next)
		{
			cur = cur->_next;
		}
		cur->_next = tmp;
	}
}
逆置单链表:

ListNode* ListReverse(ListNode* list)
{
	if (list==NULL || list->_next==NULL)
	{
		return list;
	}
	ListNode* cur = list;
	ListNode* prev = NULL;
	ListNode* tmp = NULL;

	while (cur)
	{
		tmp = cur;
		cur = cur->_next;
		tmp->_next = prev;
		prev = tmp;
	}
	return prev;
}
求倒数第k个结点:
ListNode* FindKNode(ListNode* list,int k)
{
	if (list==NULL || k==0)       //空链表或给的值不合法
	{
		return NULL;
	}
	ListNode* fast = list;
	ListNode* slow = list;
	for (int i=0; i<k; ++i)
	{
		if (fast->_next != NULL)
		{
			fast = fast->_next;
		}
		else                        //给的数值超过范围
		{
			return NULL;
		}
	}

	while (fast)
	{
		fast = fast->_next;
		slow = slow->_next;
	}
	return slow;
}
实现一个Add函数,让两个数相加,但是不能使用+、-、*、/等四则运算符。ps:也不能用++、--等等:

int Add(int a,int b)
{
	do 
	{
		int sum1 = a^b;
		int sum2 = (a&b)<<1;

		a = sum1;
		b = sum2;

	} while (b);

	return a;

}




<think>根据用户要,我们需要实现带头结点单链表。参考提供的引用内容,特别是引用[1]和引用[2]中关于单链表的方法,我们可以采用头插法或递归法。但是注意,带头结点单链表与不带头结点的处理略有不同。这里我们采用头插法,因为递归法在链表较长时可能导致栈溢出。 ### 头插法带头结点单链表 头插法链表的步骤: 1. 将头结点与第一个数据结点分离(保留头结点)。 2. 依次将剩余结点用头插法插入到头结点之后。 具体算法描述: - 初始化:`p = head->next;` 然后断开头结点:`head->next = NULL;` - 遍历原链表:每次取出一个结点,用头插法插入到`head`之后。 ### 程序实现 ```c #include <stdio.h> #include <stdlib.h> typedef struct ListNode { int data; struct ListNode* next; } ListNode; typedef ListNode* LinkList; // 创建带头结点单链表(用于测试) LinkList CreateList(int a[], int n) { LinkList head = (LinkList)malloc(sizeof(ListNode)); head->next = NULL; ListNode* p = head; for (int i = 0; i < n; i++) { ListNode* node = (ListNode*)malloc(sizeof(ListNode)); node->data = a[i]; node->next = NULL; p->next = node; p = p->next; } return head; } // 打印链表 void PrintList(LinkList head) { ListNode* p = head->next; while (p) { printf("%d ", p->data); p = p->next; } printf("\n"); } // 带头结点单链表(头插法) void ReverseList(LinkList head) { if (head == NULL || head->next == NULL) { return; } ListNode* p = head->next; // 指向第一个数据结点 head->next = NULL; // 断开头结点与后面的连接 ListNode* q = NULL; while (p) { q = p; // 记录当前结点 p = p->next; // p移到下一个结点 // 头插法插入q结点 q->next = head->next; head->next = q; } } int main() { int a[] = {1, 2, 3, 4, 5}; int n = sizeof(a) / sizeof(a[0]); LinkList head = CreateList(a, n); printf("原链表:"); PrintList(head); ReverseList(head); printf("后:"); PrintList(head); // 释放链表内存(简单示例,实际中应逐个释放) // 这里仅释放头结点,实际应遍历释放所有结点 // 为了简化,我们只演示,释放操作省略 return 0; } ``` ### 算法说明 1. **头插法原理**:每次将原链表中的第一个有效结点(即头结点后的第一个结点)从链表中断开,然后将其插入到头结点之后,形成序[^1]。 2. **时间复杂度**:$O(n)$,其中$n$为链表长度。 3. **空间复杂度**:$O(1)$,仅使用常数个额外指针。 ### 程序输出 ``` 原链表:1 2 3 4 5 后:5 4 3 2 1 ``` ### 注意事项 - 带头结点链表时,头结点不变,只是改变了数据结点的链接顺序。 - 头插法过程中要注意指针的保存,防止链表断裂。 §§相关问题§§ 1. 递归法如何实现带头结点单链表? 2. 如何在不使用额外空间的情况下双向链表? 3. 单链表操作的时间复杂度最低是多少?为什么? 4. 循环链表如何实现操作? </think># 带头结点单链表的C语言实现 我将为您实现带头结点单链表操作。带头结点链表与普通链表操作有所不同,因为头结点本身不存储数据,需要特殊处理。 ## 完整实现代码 ```c #include <stdio.h> #include <stdlib.h> // 链表结点结构 typedef struct ListNode { int data; struct ListNode* next; } ListNode; // 初始化带头结点链表 ListNode* InitList() { ListNode* head = (ListNode*)malloc(sizeof(ListNode)); if (head == NULL) { printf("内存分配失败!\n"); exit(1); } head->next = NULL; return head; } // 在链表尾部插入新节点 void AppendNode(ListNode* head, int value) { ListNode* newNode = (ListNode*)malloc(sizeof(ListNode)); if (newNode == NULL) { printf("内存分配失败!\n"); exit(1); } newNode->data = value; newNode->next = NULL; ListNode* current = head; while (current->next != NULL) { current = current->next; } current->next = newNode; } // 带头结点单链表(头插法) void ReverseList(ListNode* head) { if (head == NULL || head->next == NULL || head->next->next == NULL) { return; // 空链表或只有一个节点,无需 } ListNode* prev = NULL; // 前驱节点 ListNode* current = head->next; // 当前节点(第一个数据节点) ListNode* next = NULL; // 后继节点 // 先断开头结点与第一个数据节点的连接 head->next = NULL; // 遍历链表,使用头插法实现 while (current != NULL) { next = current->next; // 保存下一个节点 current->next = prev; // 当前节点指向前驱 prev = current; // 前驱节点移动到当前节点 current = next; // 当前节点移动到下一个节点 } // 将头结点连接到新的第一个节点(原来的最后一个节点) head->next = prev; } // 打印链表内容 void PrintList(ListNode* head) { if (head == NULL) return; ListNode* current = head->next; // 跳过头结点 printf("链表内容: "); while (current != NULL) { printf("%d -> ", current->data); current = current->next; } printf("NULL\n"); } // 释放链表内存 void FreeList(ListNode* head) { ListNode* current = head; while (current != NULL) { ListNode* temp = current; current = current->next; free(temp); } } int main() { // 创建并初始化带头结点链表 ListNode* head = InitList(); // 向链表中添加数据 AppendNode(head, 10); AppendNode(head, 20); AppendNode(head, 30); AppendNode(head, 40); AppendNode(head, 50); printf("原始链表:\n"); PrintList(head); // 链表 ReverseList(head); printf("\n链表:\n"); PrintList(head); // 释放内存 FreeList(head); return 0; } ``` ## 算法说明 ### 带头结点链表原理 1. **头结点处理**:带头结点链表时,头结点不变,只数据结点 2. **指针操作**: ```c while (current != NULL) { next = current->next; // 保存下一个节点 current->next = prev; // 当前节点指向前驱 prev = current; // 前驱节点移动到当前节点 current = next; // 当前节点移动到下一个节点 } head->next = prev; // 头结点连接到新的第一个节点 ``` 3. **时间复杂度**:$O(n)$,其中$n$为链表长度 4. **空间复杂度**:$O(1)$,仅使用常数个额外指针 ### 程序输出示例 ``` 原始链表: 链表内容: 10 -> 20 -> 30 -> 40 -> 50 -> NULL 链表: 链表内容: 50 -> 40 -> 30 -> 20 -> 10 -> NULL ``` ## 扩展方法:递归法实现链表 递归法是另一种实现链表的方法,适用于带头结点链表: ```c // 递归辅助函数 ListNode* ReverseRecursive(ListNode* current) { if (current == NULL || current->next == NULL) { return current; } ListNode* newHead = ReverseRecursive(current->next); current->next->next = current; current->next = NULL; return newHead; } // 使用递归法带头结点链表 void ReverseListRecursive(ListNode* head) { if (head == NULL || head->next == NULL) { return; } ListNode* newHead = ReverseRecursive(head->next); head->next = newHead; } ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值