c提高5-链表

博客主要介绍了链表相关知识。链表具有非连续存储、动态生成等特点,按头节点、节点组织形式、存储结构有不同分类。还提及编程元素,如新建节点。此外,介绍了linux内核链表,它让数据域包含指针域,通用链表在此基础上优化,指针域与结构体地址重合,可编译为动态库调用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

链表

关键字:非连续存储,动态生成,指针连接,数据与和指针域。

分类

按头节点:

  • 带头链表(头节点无数据域)
  • 不带头链表

按节点组织形式:

  • 单向链表
  • 双向链表
  • 循环链表

按存储结构:

  • 静态链表,用数组实现或栈中分配内存,连续存储,容量固定。
  • 动态链表,用malloc()等内存申请函数实现。

编程元素

pHead,pCurrent,pPrev,pNext.,还有新建节点pMalloc.

#include<stdio.h>
#include<stdlib.h>
#include<conio.h>


typedef struct node{
    int data;
	struct node *next;
}linklist;


linklist *Creatlist();
linklist *Find(linklist *head,int x);
int add_to_end(linklist *p,int data);
int insert(linklist *head,int originData,int data);
int delNode(linklist *head,int data);
void linklist_output(linklist *head);
int inverse(linklist *head);
int linklist_destroy(linklist **head);


int main(void)
{
	int data;
	linklist *pHead = NULL;
	int ret;
	pHead = Creatlist();
	if(pHead == NULL){
		printf("CreatList() err\n");
		return 1;
	} 
	linklist_output(pHead);
	
	
	ret = insert(pHead,2,5);
	if(ret != 0)
	{
		return 0;
	}
	linklist_output(pHead);
	
	ret = inverse(pHead);
	if(ret != 0)
	{
		return 0;
	}
	linklist_output(pHead);
	
	ret = linklist_destroy(&pHead);
	if(ret != 0)
	{
		return 0;
	}
	return 0;
}


//initial the linklist  
linklist *Creatlist()
{
	/*
	    with a head node;
		single direction;
	    loop creation;
	    input ends with -1.
		return the address of the head node. 
	*/
	int data;
	linklist *pHead = NULL,*pMalloc = NULL,*pCurrent = NULL;
	
	pHead=(linklist*)malloc(sizeof(linklist));
	if(pHead == NULL)
	{
		return NULL;
	} 
	pHead->data = 0;
	pHead->next = NULL;
	
	pCurrent = pHead;
	
	
	while(data != -1)
	{
		pMalloc = (linklist*)malloc(sizeof(linklist));
		if(pMalloc == NULL)
		{
			linklist_destroy(&pHead); 
			return pHead;
		} 
		
		printf("input data(linklist ends with -1):");
		scanf("%d",&data);
		fflush(stdin);
		
		pMalloc->data = data;
		pMalloc->next = NULL;
		
		pCurrent->next=pMalloc;
		pCurrent=pMalloc;
	}
	pCurrent->next=NULL;
	return pHead;
}

void linklist_output(linklist *head)
{
	linklist *pCurrent = NULL;
	if(head == NULL)
	{
		printf("linklist_output() arg can't be NULL.\n");
		return ;
	}
	pCurrent = head->next;
	while(pCurrent)
	{
		printf("%d ",pCurrent->data);	
		pCurrent = pCurrent->next;
	}
	printf("\n");
	
}


int add_to_end(linklist *head,int data)
{
	linklist *pMalloc = NULL,*pRear = NULL;
	pMalloc = (linklist*)malloc(sizeof(linklist));
	if(pMalloc == NULL)
	{
		printf("InsertAfter() : malloc() error.\n");
		return -1;
	}
	pMalloc->data = data;
	pMalloc->next = NULL;
	pRear = head;
	while(pRear->next != NULL){
		pRear = pRear->next;
	}
	pRear->next = pMalloc;
	return 0;
}

int insert(linklist*head,int originData,int data)
{
	linklist *pPre = NULL,*pCurrent = NULL,*pMalloc = NULL; 
	
	if(head == NULL)
	{
		printf("insert() arg linklist*head can't be NULL.\n");
		return -1;
	}
	
	pPre = head;
	pCurrent = head->next;
	
	pMalloc = (linklist*)malloc(sizeof(linklist));
	if(pMalloc == NULL)
	{
		printf("InsertAfter() : malloc() error.\n");
		return -1;
	}
	pMalloc->data = data;
	pMalloc->next = NULL;
	
	/*
		if originData is found,pMalloc will replace it,
		or pMalloc will be added to the end.
	*/
	
	while(pCurrent)
	{
		if(pCurrent->data == originData)
		{
			break;
		}
		pPre = pCurrent;
		pCurrent = pCurrent->next;
	}
	
	
	
	pPre->next = pMalloc;
	pMalloc->next = pCurrent;
	
	return 0;
}

int delNode(linklist *head,int data_to_del)
{
	linklist *pPrev = NULL, *pCurrent = NULL;
	if(head == NULL)
	{
		printf("delNode() arg linklist*head can't be NULL.\n");
		return -1;
	}
	
	pPrev = head;
	pCurrent = head->next;
	
	while(pCurrent)
	{
		if(pCurrent->data == data_to_del)
		{
			break;
		}
		pPrev = pCurrent;
		pCurrent = pCurrent->next;
	}
	if(pCurrent == NULL)
	{
		printf("%d not found.\n",data_to_del);
		return -1;
	}
	pPrev->next = pCurrent->next;
	if(pCurrent != NULL)
	{
		free(pCurrent);
		pCurrent = NULL;
	}
	
	return 0;
}

int inverse(linklist *head){
	linklist *pPrev = NULL;
	linklist *pCurrent = NULL;
	linklist *pNext = NULL;
	
	if(head == NULL)
	{
		printf("delNode() arg linklist*head can't be NULL.\n");
		return -1;
	}
	
	if(head->next == NULL)
	{
		printf("the linklist has just one head node.\n");
		return -1;
	}
	
	pPrev = head;
	pCurrent = head->next;
	while(pCurrent){ 
		pNext = pCurrent->next;
		
		pCurrent->next = pPrev;
		
		pPrev = pCurrent;
		pCurrent = pNext;
		//pNext = pCurrent->next;  DON'T BE HERE锛?
	}
	
	/*
	  ---->O----->O------>NULL
			      pPrev    pCurrent/pNext
	*/
	head->next->next = NULL;
	head->next = pPrev;
	
	return 0;
}

int linklist_destroy(linklist **head)
{
	linklist *pHead,*pNext = NULL;
	if(head == NULL)
	{
		printf("linklist_destroy() arg linklist*head can't be NULL.\n");
		return -1;
	}
	pHead = *head;
	while(pHead)
	{
		pNext = pHead->next;
		pHead->next = NULL;
		free(pHead);
		pHead = pNext;
	}
	*head = NULL;
	return 0;
}

linux内核链表

上面的部分是传统链表,节点包含数据域和指针域。

Linus在28岁时发明了linux内核链表,让数据域包含指针域,进而实现不同结构体链接起来。

数据域不能包含万事万物,那就让万事万物包含链表节点。

这种链表需要手动实现计算成员偏移量。

//compute the offset of the member 
#define offsetof(TYPE,MEMBER) ((size_t) & ((TYPE *)0)->MEMBER)

struct list_head{
    struct list_head *next,*prev;
}
struct Foo{
    int id;
    struct list_head list;
}
struct Bar{
    int id;
    struct list_head list;
}

int main()
{
    struct list_head head,*plist;
    struct Foo foo;
    struct Bar bar;
    
    INIT_LIST_HEAD(&head);
    
    list_add(&foo.list,head);
    list_add(&bar.list,head);
    
    list_for_each(plist,&head){
        struct Foo *node = list_entry(plist,struct Foo,list);
       	printf("%d \n",node->id);
    }
    
    return 0;
}

通用链表

基于linux内核链表,还可以做一点优化,让指针域地址和结构体地址重合。这种链表在实际中大量应用。

struct Foo{
    struct list_head list;
    int id;
}

可以把这种链表编译为动态库随时调用。

### C语言 PTA 2-5 题重排链表解决方案 为了实现给定单链表的重新排列,可以采用双指针法来完成这一操作。具体来说,在遍历过程中将原链表分为两部分,并反转第二部分,最后交替连接这两部分节点。 #### 定义结构体与辅助函数 定义一个简单的链表节点结构体以及一些必要的辅助函数用于创建新节点、打印链表等: ```c #include <stdio.h> #include <stdlib.h> typedef struct ListNode { int val; struct ListNode *next; } ListNode; // 创建新的列表节点 ListNode* createNode(int value) { ListNode *new_node = (ListNode *)malloc(sizeof(ListNode)); new_node->val = value; new_node->next = NULL; return new_node; } // 打印整个列表的内容 void printList(ListNode *head) { while(head != NULL){ printf("%d", head->val); if(head->next!=NULL) printf(" -> "); head = head->next; } printf("\n"); } ``` #### 主要逻辑处理过程 接下来是核心算法部分,这里会先找到中间位置并断开前后半段,再翻转后半段链表,最终按照题目要求的方式拼接起来[^1]。 ```c // 反转链表 ListNode* reverseList(ListNode *head) { ListNode *prev = NULL, *curr = head, *next; while(curr){ next = curr->next; // 记录下一个节点 curr->next = prev; // 当前节点指向前一个节点 prev = curr; // 更新前驱为当前节点 curr = next; // 移动到下一位继续循环 } return prev; } // 寻找链表中点并将之后的部分截取出来作为另一条链表返回 ListNode* splitAndReverseSecondHalf(ListNode **headRef) { ListNode *slowPtr = *headRef, *fastPtr = (*headRef)->next; while(fastPtr && fastPtr->next){ // 使用快慢指针定位至中心处 slowPtr = slowPtr->next; fastPtr = fastPtr->next->next; } ListNode *second_half_start = slowPtr->next; // 获取右半边起始点 slowPtr->next = NULL; // 断开左半边 return reverseList(second_half_start); // 返回已逆序后的右边链表头 } // 合并两个交错链接的链表 void mergeAlternately(ListNode **leftHead, ListNode *rightReversedTail) { ListNode *currentLeft = *leftHead; ListNode *tempNextRight; while(currentLeft && rightReversedTail){ tempNextRight = rightReversedTail->next; // 存储右侧剩余未处理过的节点 rightReversedTail->next = currentLeft->next;// 将右侧节点插入左侧对应位置之前 currentLeft->next = rightReversedTail; // 插入完成后更新左侧指向 currentLeft = currentLeft->next->next; // 跳过刚插入的那个元素移动到下一组待处理的位置 rightReversedTail = tempNextRight; // 继续处理剩下的右侧节点 } } ``` #### 测试案例构建与调用上述功能组合解决问题 现在有了这些工具函数的支持,就可以轻松解决这个问题了。下面是一个完整的例子展示如何读取输入数据建立初始链表,执行重组动作后再输出结果[^2]。 ```c int main() { // 假设已经获取到了首地址和长度信息... int n = 6; // 示例中的数量 char firstAddrStr[] = "0"; // 示例第一个结点地址字符串形式 long addrFirst = atol(firstAddrStr); // 构建测试用例链表 {1,2,3,4,5,6} ListNode *test_head = createNode(1), *p=test_head; for(int i=2;i<=n;++i){ p->next=createNode(i); p=p->next; } // 开始处理:分割+反转+合并 ListNode *reversed_second_part = splitAndReverseSecondHalf(&test_head); mergeAlternately(&test_head, reversed_second_part); // 输出调整后的链表序列 printList(test_head); return 0; } ``` 此代码片段展示了如何利用C语言在PTA平台上解答“7-2 重排链表”这道编程题目的方法。通过合理的模块化设计使得整体思路清晰易懂,同时也提高了代码可维护性和扩展性[^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值